YAY!
authorMax <Infinity2573@gmail.com>
Fri, 10 Dec 2021 15:08:04 +0000 (18:08 +0300)
committerMax <Infinity2573@gmail.com>
Fri, 10 Dec 2021 15:08:04 +0000 (18:08 +0300)
1189 files changed:
CMakeLists.txt
cmake/FindGROMACS.cmake
src/CMakeLists.txt
src/include/gromacs/analysisdata/abstractdata.h [new file with mode: 0644]
src/include/gromacs/analysisdata/analysisdata.h [new file with mode: 0644]
src/include/gromacs/analysisdata/arraydata.h [new file with mode: 0644]
src/include/gromacs/analysisdata/dataframe.h [new file with mode: 0644]
src/include/gromacs/analysisdata/datamodule.h [new file with mode: 0644]
src/include/gromacs/analysisdata/datamodulemanager.h [new file with mode: 0644]
src/include/gromacs/analysisdata/dataproxy.h [new file with mode: 0644]
src/include/gromacs/analysisdata/datastorage.h [new file with mode: 0644]
src/include/gromacs/analysisdata/framelocaldata.h [new file with mode: 0644]
src/include/gromacs/analysisdata/modules/average.h [new file with mode: 0644]
src/include/gromacs/analysisdata/modules/displacement.h [new file with mode: 0644]
src/include/gromacs/analysisdata/modules/frameaverager.h [new file with mode: 0644]
src/include/gromacs/analysisdata/modules/histogram.h [new file with mode: 0644]
src/include/gromacs/analysisdata/modules/lifetime.h [new file with mode: 0644]
src/include/gromacs/analysisdata/modules/plot.h [new file with mode: 0644]
src/include/gromacs/analysisdata/paralleloptions.h [new file with mode: 0644]
src/include/gromacs/analysisdata/tests/datatest.h [new file with mode: 0644]
src/include/gromacs/analysisdata/tests/mock_datamodule.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/awh.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/bias.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/biasgrid.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/biasparams.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/biassharing.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/biasstate.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/biaswriter.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/coordstate.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/correlationgrid.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/correlationhistory.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/correlationtensor.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/dimparams.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/histogramsize.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/pointstate.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/read_params.h [new file with mode: 0644]
src/include/gromacs/applied_forces/awh/tests/awh_setup.h [new file with mode: 0644]
src/include/gromacs/applied_forces/densityfitting/densityfitting.h [new file with mode: 0644]
src/include/gromacs/applied_forces/densityfitting/densityfittingamplitudelookup.h [new file with mode: 0644]
src/include/gromacs/applied_forces/densityfitting/densityfittingforceprovider.h [new file with mode: 0644]
src/include/gromacs/applied_forces/densityfitting/densityfittingoptions.h [new file with mode: 0644]
src/include/gromacs/applied_forces/densityfitting/densityfittingoutputprovider.h [new file with mode: 0644]
src/include/gromacs/applied_forces/densityfitting/densityfittingparameters.h [new file with mode: 0644]
src/include/gromacs/applied_forces/electricfield.h [new file with mode: 0644]
src/include/gromacs/applied_forces/qmmm/qmmm.h [new file with mode: 0644]
src/include/gromacs/applied_forces/qmmm/qmmmforceprovider.h [new file with mode: 0644]
src/include/gromacs/applied_forces/qmmm/qmmminputgenerator.h [new file with mode: 0644]
src/include/gromacs/applied_forces/qmmm/qmmmoptions.h [new file with mode: 0644]
src/include/gromacs/applied_forces/qmmm/qmmmtopologypreprocessor.h [new file with mode: 0644]
src/include/gromacs/applied_forces/qmmm/qmmmtypes.h [new file with mode: 0644]
src/include/gromacs/commandline/cmdlinehelpcontext.h [new file with mode: 0644]
src/include/gromacs/commandline/cmdlinehelpmodule.h [new file with mode: 0644]
src/include/gromacs/commandline/cmdlinehelpwriter.h [new file with mode: 0644]
src/include/gromacs/commandline/cmdlineinit.h [new file with mode: 0644]
src/include/gromacs/commandline/cmdlinemodule.h [new file with mode: 0644]
src/include/gromacs/commandline/cmdlinemodulemanager.h [new file with mode: 0644]
src/include/gromacs/commandline/cmdlinemodulemanager_impl.h [new file with mode: 0644]
src/include/gromacs/commandline/cmdlineoptionsmodule.h [new file with mode: 0644]
src/include/gromacs/commandline/cmdlineparser.h [new file with mode: 0644]
src/include/gromacs/commandline/cmdlineprogramcontext.h [new file with mode: 0644]
src/include/gromacs/commandline/filenm.h [new file with mode: 0644]
src/include/gromacs/commandline/pargs.h [new file with mode: 0644]
src/include/gromacs/commandline/shellcompletions.h [new file with mode: 0644]
src/include/gromacs/commandline/tests/cmdlinemodulemanagertest.h [new file with mode: 0644]
src/include/gromacs/commandline/viewit.h [new file with mode: 0644]
src/include/gromacs/compat/mp11.h [new file with mode: 0644]
src/include/gromacs/compat/pointers.h [new file with mode: 0644]
src/include/gromacs/compat/utility.h [new file with mode: 0644]
src/include/gromacs/coordinateio/coordinatefile.h [new file with mode: 0644]
src/include/gromacs/coordinateio/enums.h [new file with mode: 0644]
src/include/gromacs/coordinateio/ioutputadapter.h [new file with mode: 0644]
src/include/gromacs/coordinateio/outputadaptercontainer.h [new file with mode: 0644]
src/include/gromacs/coordinateio/outputadapters.h [new file with mode: 0644]
src/include/gromacs/coordinateio/outputadapters/outputselector.h [new file with mode: 0644]
src/include/gromacs/coordinateio/outputadapters/setatoms.h [new file with mode: 0644]
src/include/gromacs/coordinateio/outputadapters/setbox.h [new file with mode: 0644]
src/include/gromacs/coordinateio/outputadapters/setforces.h [new file with mode: 0644]
src/include/gromacs/coordinateio/outputadapters/setprecision.h [new file with mode: 0644]
src/include/gromacs/coordinateio/outputadapters/setstarttime.h [new file with mode: 0644]
src/include/gromacs/coordinateio/outputadapters/settimestep.h [new file with mode: 0644]
src/include/gromacs/coordinateio/outputadapters/setvelocities.h [new file with mode: 0644]
src/include/gromacs/coordinateio/requirements.h [new file with mode: 0644]
src/include/gromacs/coordinateio/tests/coordinate_test.h [new file with mode: 0644]
src/include/gromacs/coordinateio/tests/outputadapters.h [new file with mode: 0644]
src/include/gromacs/coordinateio/tests/requirements.h [new file with mode: 0644]
src/include/gromacs/coordinateio/tests/testmodule.h [new file with mode: 0644]
src/include/gromacs/correlationfunctions/autocorr.h [new file with mode: 0644]
src/include/gromacs/correlationfunctions/crosscorr.h [new file with mode: 0644]
src/include/gromacs/correlationfunctions/expfit.h [new file with mode: 0644]
src/include/gromacs/correlationfunctions/gmx_lmcurve.h [new file with mode: 0644]
src/include/gromacs/correlationfunctions/integrate.h [new file with mode: 0644]
src/include/gromacs/correlationfunctions/manyautocorrelation.h [new file with mode: 0644]
src/include/gromacs/correlationfunctions/polynomials.h [new file with mode: 0644]
src/include/gromacs/correlationfunctions/tests/correlationdataset.h [new file with mode: 0644]
src/include/gromacs/domdec/atomdistribution.h [new file with mode: 0644]
src/include/gromacs/domdec/box.h [new file with mode: 0644]
src/include/gromacs/domdec/cellsizes.h [new file with mode: 0644]
src/include/gromacs/domdec/collect.h [new file with mode: 0644]
src/include/gromacs/domdec/computemultibodycutoffs.h [new file with mode: 0644]
src/include/gromacs/domdec/distribute.h [new file with mode: 0644]
src/include/gromacs/domdec/dlb.h [new file with mode: 0644]
src/include/gromacs/domdec/dlbtiming.h [new file with mode: 0644]
src/include/gromacs/domdec/domdec.h [new file with mode: 0644]
src/include/gromacs/domdec/domdec_constraints.h [new file with mode: 0644]
src/include/gromacs/domdec/domdec_internal.h [new file with mode: 0644]
src/include/gromacs/domdec/domdec_network.h [new file with mode: 0644]
src/include/gromacs/domdec/domdec_setup.h [new file with mode: 0644]
src/include/gromacs/domdec/domdec_specatomcomm.h [new file with mode: 0644]
src/include/gromacs/domdec/domdec_struct.h [new file with mode: 0644]
src/include/gromacs/domdec/domdec_vsite.h [new file with mode: 0644]
src/include/gromacs/domdec/dump.h [new file with mode: 0644]
src/include/gromacs/domdec/ga2la.h [new file with mode: 0644]
src/include/gromacs/domdec/gpuhaloexchange.h [new file with mode: 0644]
src/include/gromacs/domdec/gpuhaloexchange_impl.cuh [new file with mode: 0644]
src/include/gromacs/domdec/hashedmap.h [new file with mode: 0644]
src/include/gromacs/domdec/localatomset.h [new file with mode: 0644]
src/include/gromacs/domdec/localatomsetdata.h [new file with mode: 0644]
src/include/gromacs/domdec/localatomsetmanager.h [new file with mode: 0644]
src/include/gromacs/domdec/localtopology.h [new file with mode: 0644]
src/include/gromacs/domdec/localtopologychecker.h [new file with mode: 0644]
src/include/gromacs/domdec/makebondedlinks.h [new file with mode: 0644]
src/include/gromacs/domdec/mdsetup.h [new file with mode: 0644]
src/include/gromacs/domdec/nsgrid.h [new file with mode: 0644]
src/include/gromacs/domdec/options.h [new file with mode: 0644]
src/include/gromacs/domdec/partition.h [new file with mode: 0644]
src/include/gromacs/domdec/redistribute.h [new file with mode: 0644]
src/include/gromacs/domdec/reversetopology.h [new file with mode: 0644]
src/include/gromacs/domdec/utility.h [new file with mode: 0644]
src/include/gromacs/essentialdynamics/edsam.h [new file with mode: 0644]
src/include/gromacs/ewald/calculate_spline_moduli.h [new file with mode: 0644]
src/include/gromacs/ewald/ewald.h [new file with mode: 0644]
src/include/gromacs/ewald/ewald_utils.h [new file with mode: 0644]
src/include/gromacs/ewald/long_range_correction.h [new file with mode: 0644]
src/include/gromacs/ewald/pme.cuh [new file with mode: 0644]
src/include/gromacs/ewald/pme.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_coordinate_receiver_gpu.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_coordinate_receiver_gpu_impl.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_force_sender_gpu.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_force_sender_gpu_impl.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gather.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gather_sycl.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_calculate_splines.clh [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_calculate_splines.cuh [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_calculate_splines.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_calculate_splines_sycl.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_constants.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_internal.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_program.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_program_impl.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_settings.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_staging.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_timings.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_types.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_types_host.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_gpu_types_host_impl.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_grid.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_internal.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_load_balancing.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_only.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_output.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_pp.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_pp_comm_gpu.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_pp_comm_gpu_impl.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_pp_communication.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_redistribute.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_simd.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_simd4.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_solve.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_spline_work.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_spread.h [new file with mode: 0644]
src/include/gromacs/ewald/pme_spread_sycl.h [new file with mode: 0644]
src/include/gromacs/ewald/spline_vectors.h [new file with mode: 0644]
src/include/gromacs/ewald/tests/pmetestcommon.h [new file with mode: 0644]
src/include/gromacs/external/boost/stl_interfaces/fwd.hpp [new file with mode: 0644]
src/include/gromacs/external/boost/stl_interfaces/iterator_interface.hpp [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/include/clAmdFft.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/include/clAmdFft.version.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/include/clFFT.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/include/clFFT.version.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/include/convenienceFunctions.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/include/sharedLibrary.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/include/stdafx.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/include/targetver.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/include/unicode.compatibility.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/action.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/action.transpose.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/fft_binary_lookup.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/generator.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/generator.stockham.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/generator.transpose.gcn.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/generator.transpose.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/lock.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/mainpage.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/md5sum.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/plan.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/private.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/library/repo.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/statTimer/statisticalTimer.CPU.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/statTimer/statisticalTimer.GPU.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/statTimer/statisticalTimer.extern.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/statTimer/statisticalTimer.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/statTimer/stdafx.h [new file with mode: 0644]
src/include/gromacs/external/clFFT/src/statTimer/targetver.h [new file with mode: 0644]
src/include/gromacs/external/fftpack/fftpack.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-actions.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-cardinalities.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-function-mocker.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-matchers.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-more-actions.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-more-matchers.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-nice-strict.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-spec-builders.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/gmock.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/internal/custom/gmock-matchers.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/internal/custom/gmock-port.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/internal/gmock-internal-utils.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/internal/gmock-port.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/include/gmock/internal/gmock-pp.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googlemock/test/gmock_link_test.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/gtest-death-test.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/gtest-matchers.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/gtest-message.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/gtest-param-test.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/gtest-printers.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/gtest-spi.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/gtest-test-part.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/gtest-typed-test.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/gtest.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/gtest_pred_impl.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/gtest_prod.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/internal/custom/gtest-port.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/internal/custom/gtest-printers.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/internal/custom/gtest.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-death-test-internal.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-filepath.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-internal.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-param-util.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-port-arch.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-port.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-string.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-type-util.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/samples/prime_tables.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/samples/sample1.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/samples/sample2.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/samples/sample3-inl.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/samples/sample4.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/src/gtest-internal-inl.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/test/googletest-param-test-test.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/test/gtest-typed-test_test.h [new file with mode: 0644]
src/include/gromacs/external/googletest/googletest/test/production.h [new file with mode: 0644]
src/include/gromacs/external/lmfit/lmmin.h [new file with mode: 0644]
src/include/gromacs/external/lmfit/lmstruct.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParser.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParserBase.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParserBytecode.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParserCallback.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParserDLL.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParserDef.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParserError.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParserFixes.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParserInt.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParserTemplateMagic.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParserTest.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParserToken.h [new file with mode: 0644]
src/include/gromacs/external/muparser/include/muParserTokenReader.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/cce.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/cce_intrinsics.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/cce_spinlock.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/cycles.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/derived.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/fujitsu_sparc64.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_ia64.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_intrinsics.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_ppc.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_spinlock.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_x86.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/msvc.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/suncc-sparc.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/xlc_ppc.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/barrier.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/collective.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/event.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/hwinfo.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/list.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/lock.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/mpi_bindings.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/numa_malloc.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/threads.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/tmpi.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/visibility.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/thread_mpi/wait.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/include/tmpi.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/src/collective.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/src/impl.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/src/p2p.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/src/profile.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/src/pthreads.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/src/settings.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/src/tmpi_ops.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/src/unused.h [new file with mode: 0644]
src/include/gromacs/external/thread_mpi/src/winthreads.h [new file with mode: 0644]
src/include/gromacs/external/tinyxml2/tinyxml2.h [new file with mode: 0755]
src/include/gromacs/external/tng_io/external/zlib/crc32.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/external/zlib/deflate.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/external/zlib/inffast.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/external/zlib/inffixed.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/external/zlib/inflate.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/external/zlib/inftrees.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/external/zlib/trees.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/external/zlib/zconf.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/external/zlib/zlib.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/external/zlib/zutil.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/bwlzh.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/bwt.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/coder.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/dict.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/fixpoint.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/huffman.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/lz77.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/merge_sort.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/mtf.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/my64bit.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/rle.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/tng_compress.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/vals16.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/warnmalloc.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/compression/widemuldiv.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/tng/md5.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/tng/tng_io.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/tng/tng_io.hpp [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/tng/tng_io_fwd.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/include/tng/version.h.in [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test1.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test10.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test11.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test12.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test13.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test14.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test15.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test16.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test17.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test18.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test19.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test2.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test20.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test21.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test22.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test23.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test24.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test25.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test26.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test27.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test28.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test29.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test3.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test30.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test31.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test32.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test33.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test34.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test35.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test36.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test37.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test38.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test39.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test4.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test40.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test41.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test42.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test43.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test44.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test45.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test46.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test47.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test48.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test49.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test5.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test50.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test51.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test52.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test53.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test54.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test55.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test56.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test57.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test58.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test59.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test6.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test60.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test61.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test62.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test63.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test64.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test65.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test66.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test67.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test68.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test69.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test7.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test70.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test71.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test72.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test73.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test74.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test75.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test76.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test77.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test78.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test8.h [new file with mode: 0644]
src/include/gromacs/external/tng_io/src/tests/compression/test9.h [new file with mode: 0644]
src/include/gromacs/external/vmd_molfile/molfile_plugin.h [new file with mode: 0644]
src/include/gromacs/external/vmd_molfile/vmddlopen.h [new file with mode: 0644]
src/include/gromacs/external/vmd_molfile/vmdplugin.h [new file with mode: 0644]
src/include/gromacs/fft/calcgrid.h [new file with mode: 0644]
src/include/gromacs/fft/fft.h [new file with mode: 0644]
src/include/gromacs/fft/fft5d.h [new file with mode: 0644]
src/include/gromacs/fft/gpu_3dfft.h [new file with mode: 0644]
src/include/gromacs/fft/gpu_3dfft_cufft.h [new file with mode: 0644]
src/include/gromacs/fft/gpu_3dfft_heffte.h [new file with mode: 0644]
src/include/gromacs/fft/gpu_3dfft_impl.h [new file with mode: 0644]
src/include/gromacs/fft/gpu_3dfft_ocl.h [new file with mode: 0644]
src/include/gromacs/fft/gpu_3dfft_sycl.h [new file with mode: 0644]
src/include/gromacs/fft/gpu_3dfft_sycl_mkl.h [new file with mode: 0644]
src/include/gromacs/fft/gpu_3dfft_sycl_rocfft.h [new file with mode: 0644]
src/include/gromacs/fft/parallel_3dfft.h [new file with mode: 0644]
src/include/gromacs/fileio/checkpoint.h [new file with mode: 0644]
src/include/gromacs/fileio/enxio.h [new file with mode: 0644]
src/include/gromacs/fileio/espio.h [new file with mode: 0644]
src/include/gromacs/fileio/g96io.h [new file with mode: 0644]
src/include/gromacs/fileio/gmx_internal_xdr.h [new file with mode: 0644]
src/include/gromacs/fileio/gmxfio.h [new file with mode: 0644]
src/include/gromacs/fileio/gmxfio_impl.h [new file with mode: 0644]
src/include/gromacs/fileio/gmxfio_xdr.h [new file with mode: 0644]
src/include/gromacs/fileio/groio.h [new file with mode: 0644]
src/include/gromacs/fileio/matio.h [new file with mode: 0644]
src/include/gromacs/fileio/md5.h [new file with mode: 0644]
src/include/gromacs/fileio/mrcdensitymap.h [new file with mode: 0644]
src/include/gromacs/fileio/mrcdensitymapheader.h [new file with mode: 0644]
src/include/gromacs/fileio/mrcserializer.h [new file with mode: 0644]
src/include/gromacs/fileio/mtxio.h [new file with mode: 0644]
src/include/gromacs/fileio/readinp.h [new file with mode: 0644]
src/include/gromacs/fileio/rgb.h [new file with mode: 0644]
src/include/gromacs/fileio/timecontrol.h [new file with mode: 0644]
src/include/gromacs/fileio/tngio.h [new file with mode: 0644]
src/include/gromacs/fileio/trrio.h [new file with mode: 0644]
src/include/gromacs/fileio/vmdio.h [new file with mode: 0644]
src/include/gromacs/fileio/warninp.h [new file with mode: 0644]
src/include/gromacs/fileio/writeps.h [new file with mode: 0644]
src/include/gromacs/fileio/xdr_datatype.h [new file with mode: 0644]
src/include/gromacs/fileio/xdrf.h [new file with mode: 0644]
src/include/gromacs/fileio/xtcio.h [new file with mode: 0644]
src/include/gromacs/fileio/xvgr.h [new file with mode: 0644]
src/include/gromacs/gmxana/angle_correction.h [new file with mode: 0644]
src/include/gromacs/gmxana/binsearch.h [new file with mode: 0644]
src/include/gromacs/gmxana/cluster_methods.h [new file with mode: 0644]
src/include/gromacs/gmxana/cmat.h [new file with mode: 0644]
src/include/gromacs/gmxana/dens_filter.h [new file with mode: 0644]
src/include/gromacs/gmxana/eigio.h [new file with mode: 0644]
src/include/gromacs/gmxana/fitahx.h [new file with mode: 0644]
src/include/gromacs/gmxana/gmx_ana.h [new file with mode: 0644]
src/include/gromacs/gmxana/gstat.h [new file with mode: 0644]
src/include/gromacs/gmxana/hxprops.h [new file with mode: 0644]
src/include/gromacs/gmxana/interf.h [new file with mode: 0644]
src/include/gromacs/gmxana/nrama.h [new file with mode: 0644]
src/include/gromacs/gmxana/nsfactor.h [new file with mode: 0644]
src/include/gromacs/gmxana/powerspect.h [new file with mode: 0644]
src/include/gromacs/gmxana/princ.h [new file with mode: 0644]
src/include/gromacs/gmxana/sfactor.h [new file with mode: 0644]
src/include/gromacs/gmxana/thermochemistry.h [new file with mode: 0644]
src/include/gromacs/gmxlib/conformation_utilities.h [new file with mode: 0644]
src/include/gromacs/gmxlib/network.h [new file with mode: 0644]
src/include/gromacs/gmxlib/nonbonded/nb_free_energy.h [new file with mode: 0644]
src/include/gromacs/gmxlib/nonbonded/nb_softcore.h [new file with mode: 0644]
src/include/gromacs/gmxlib/nonbonded/nonbonded.h [new file with mode: 0644]
src/include/gromacs/gmxlib/nrnb.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/add_par.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/calch.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/convparm.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/editconf.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/fflibutil.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/gen_ad.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/gen_maxwell_velocities.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/gen_vsite.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/genconf.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/genhydro.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/genion.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/genrestr.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/gmxcpp.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/gpp_atomtype.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/gpp_bond_atomtype.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/gpp_nextnb.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/grompp.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/grompp_impl.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/h_db.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/hackblock.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/hizzie.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/insert_molecules.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/makeexclusiondistances.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/nm2type.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/notset.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/pdb2gmx.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/pdb2top.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/pgutil.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/readir.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/resall.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/solvate.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/specbond.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/ter_db.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/tomorse.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/topdirs.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/topio.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/toppush.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/topshake.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/toputil.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/vsite_parm.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/x2top.h [new file with mode: 0644]
src/include/gromacs/gmxpreprocess/xlate.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/clfftinitializer.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/cuda_arch_utils.cuh [new file with mode: 0644]
src/include/gromacs/gpu_utils/cuda_kernel_utils.cuh [new file with mode: 0644]
src/include/gromacs/gpu_utils/cudautils.cuh [new file with mode: 0644]
src/include/gromacs/gpu_utils/device_context.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/device_event.cuh [new file with mode: 0644]
src/include/gromacs/gpu_utils/device_event.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/device_event_ocl.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/device_event_sycl.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/device_stream.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/device_stream_manager.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/device_utils.clh [new file with mode: 0644]
src/include/gromacs/gpu_utils/devicebuffer.cuh [new file with mode: 0644]
src/include/gromacs/gpu_utils/devicebuffer.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/devicebuffer_datatype.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/devicebuffer_ocl.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/devicebuffer_sycl.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/gmxopencl.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/gmxsycl.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/gpu_macros.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/gpu_utils.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/gpueventsynchronizer.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/gpuregiontimer.cuh [new file with mode: 0644]
src/include/gromacs/gpu_utils/gpuregiontimer.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/gpuregiontimer_ocl.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/gpuregiontimer_sycl.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/gputraits.cuh [new file with mode: 0644]
src/include/gromacs/gpu_utils/gputraits.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/gputraits_ocl.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/gputraits_sycl.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/hostallocator.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/ocl_caching.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/ocl_compiler.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/oclraii.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/oclutils.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/pinning.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/pmalloc.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/sycl_kernel_utils.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/syclutils.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/tests/devicetransfers.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/tests/typecasts_runner.h [new file with mode: 0644]
src/include/gromacs/gpu_utils/typecasts.cuh [new file with mode: 0644]
src/include/gromacs/gpu_utils/vectype_ops.clh [new file with mode: 0644]
src/include/gromacs/gpu_utils/vectype_ops.cuh [new file with mode: 0644]
src/include/gromacs/hardware/architecture.h [new file with mode: 0644]
src/include/gromacs/hardware/cpuinfo.h [new file with mode: 0644]
src/include/gromacs/hardware/detecthardware.h [new file with mode: 0644]
src/include/gromacs/hardware/device_information.h [new file with mode: 0644]
src/include/gromacs/hardware/device_management.h [new file with mode: 0644]
src/include/gromacs/hardware/hardwaretopology.h [new file with mode: 0644]
src/include/gromacs/hardware/hw_info.h [new file with mode: 0644]
src/include/gromacs/hardware/identifyavx512fmaunits.h [new file with mode: 0644]
src/include/gromacs/hardware/prepare_detection.h [new file with mode: 0644]
src/include/gromacs/hardware/printhardware.h [new file with mode: 0644]
src/include/gromacs/imd/imd.h [new file with mode: 0644]
src/include/gromacs/imd/imdsocket.h [new file with mode: 0644]
src/include/gromacs/linearalgebra/eigensolver.h [new file with mode: 0644]
src/include/gromacs/linearalgebra/gmx_arpack.h [new file with mode: 0644]
src/include/gromacs/linearalgebra/gmx_blas.h [new file with mode: 0644]
src/include/gromacs/linearalgebra/gmx_lapack.h [new file with mode: 0644]
src/include/gromacs/linearalgebra/gmx_lapack/lapack_limits.h [new file with mode: 0644]
src/include/gromacs/linearalgebra/matrix.h [new file with mode: 0644]
src/include/gromacs/linearalgebra/nrjac.h [new file with mode: 0644]
src/include/gromacs/linearalgebra/sparsematrix.h [new file with mode: 0644]
src/include/gromacs/listed_forces/bonded.h [new file with mode: 0644]
src/include/gromacs/listed_forces/disre.h [new file with mode: 0644]
src/include/gromacs/listed_forces/listed_forces.h [new file with mode: 0644]
src/include/gromacs/listed_forces/listed_forces_gpu.h [new file with mode: 0644]
src/include/gromacs/listed_forces/listed_forces_gpu_impl.h [new file with mode: 0644]
src/include/gromacs/listed_forces/listed_internal.h [new file with mode: 0644]
src/include/gromacs/listed_forces/manage_threading.h [new file with mode: 0644]
src/include/gromacs/listed_forces/orires.h [new file with mode: 0644]
src/include/gromacs/listed_forces/pairs.h [new file with mode: 0644]
src/include/gromacs/listed_forces/position_restraints.h [new file with mode: 0644]
src/include/gromacs/listed_forces/restcbt.h [new file with mode: 0644]
src/include/gromacs/listed_forces/utilities.h [new file with mode: 0644]
src/include/gromacs/math/3dtransforms.h [new file with mode: 0644]
src/include/gromacs/math/arrayrefwithpadding.h [new file with mode: 0644]
src/include/gromacs/math/coordinatetransformation.h [new file with mode: 0644]
src/include/gromacs/math/densityfit.h [new file with mode: 0644]
src/include/gromacs/math/densityfittingforce.h [new file with mode: 0644]
src/include/gromacs/math/exponentialmovingaverage.h [new file with mode: 0644]
src/include/gromacs/math/gausstransform.h [new file with mode: 0644]
src/include/gromacs/math/gmxcomplex.h [new file with mode: 0644]
src/include/gromacs/math/invertmatrix.h [new file with mode: 0644]
src/include/gromacs/math/matrix.h [new file with mode: 0644]
src/include/gromacs/math/multidimarray.h [new file with mode: 0644]
src/include/gromacs/math/neldermead.h [new file with mode: 0644]
src/include/gromacs/math/optimization.h [new file with mode: 0644]
src/include/gromacs/math/paddedvector.h [new file with mode: 0644]
src/include/gromacs/math/tests/testarrayrefs.h [new file with mode: 0644]
src/include/gromacs/math/veccompare.h [new file with mode: 0644]
src/include/gromacs/math/vecdump.h [new file with mode: 0644]
src/include/gromacs/mdlib/boxdeformation.h [new file with mode: 0644]
src/include/gromacs/mdlib/broadcaststructs.h [new file with mode: 0644]
src/include/gromacs/mdlib/calc_verletbuf.h [new file with mode: 0644]
src/include/gromacs/mdlib/calcmu.h [new file with mode: 0644]
src/include/gromacs/mdlib/calcvir.h [new file with mode: 0644]
src/include/gromacs/mdlib/checkpointhandler.h [new file with mode: 0644]
src/include/gromacs/mdlib/compute_io.h [new file with mode: 0644]
src/include/gromacs/mdlib/constr.h [new file with mode: 0644]
src/include/gromacs/mdlib/constraintrange.h [new file with mode: 0644]
src/include/gromacs/mdlib/coupling.h [new file with mode: 0644]
src/include/gromacs/mdlib/dispersioncorrection.h [new file with mode: 0644]
src/include/gromacs/mdlib/ebin.h [new file with mode: 0644]
src/include/gromacs/mdlib/enerdata_utils.h [new file with mode: 0644]
src/include/gromacs/mdlib/energydrifttracker.h [new file with mode: 0644]
src/include/gromacs/mdlib/energyoutput.h [new file with mode: 0644]
src/include/gromacs/mdlib/expanded.h [new file with mode: 0644]
src/include/gromacs/mdlib/expanded_internal.h [new file with mode: 0644]
src/include/gromacs/mdlib/force.h [new file with mode: 0644]
src/include/gromacs/mdlib/force_flags.h [new file with mode: 0644]
src/include/gromacs/mdlib/forcerec.h [new file with mode: 0644]
src/include/gromacs/mdlib/freeenergyparameters.h [new file with mode: 0644]
src/include/gromacs/mdlib/gmx_omp_nthreads.h [new file with mode: 0644]
src/include/gromacs/mdlib/gpuforcereduction.h [new file with mode: 0644]
src/include/gromacs/mdlib/gpuforcereduction_impl.h [new file with mode: 0644]
src/include/gromacs/mdlib/gpuforcereduction_impl_internal.h [new file with mode: 0644]
src/include/gromacs/mdlib/groupcoord.h [new file with mode: 0644]
src/include/gromacs/mdlib/leapfrog_gpu.h [new file with mode: 0644]
src/include/gromacs/mdlib/leapfrog_gpu_internal.h [new file with mode: 0644]
src/include/gromacs/mdlib/lincs.h [new file with mode: 0644]
src/include/gromacs/mdlib/lincs_gpu.h [new file with mode: 0644]
src/include/gromacs/mdlib/lincs_gpu_internal.h [new file with mode: 0644]
src/include/gromacs/mdlib/makeconstraints.h [new file with mode: 0644]
src/include/gromacs/mdlib/md_support.h [new file with mode: 0644]
src/include/gromacs/mdlib/mdatoms.h [new file with mode: 0644]
src/include/gromacs/mdlib/mdebin_bar.h [new file with mode: 0644]
src/include/gromacs/mdlib/mdoutf.h [new file with mode: 0644]
src/include/gromacs/mdlib/membed.h [new file with mode: 0644]
src/include/gromacs/mdlib/perf_est.h [new file with mode: 0644]
src/include/gromacs/mdlib/rbin.h [new file with mode: 0644]
src/include/gromacs/mdlib/resethandler.h [new file with mode: 0644]
src/include/gromacs/mdlib/rf_util.h [new file with mode: 0644]
src/include/gromacs/mdlib/settle.h [new file with mode: 0644]
src/include/gromacs/mdlib/settle_gpu.h [new file with mode: 0644]
src/include/gromacs/mdlib/settle_gpu_internal.h [new file with mode: 0644]
src/include/gromacs/mdlib/shake.h [new file with mode: 0644]
src/include/gromacs/mdlib/sighandler.h [new file with mode: 0644]
src/include/gromacs/mdlib/simulationsignal.h [new file with mode: 0644]
src/include/gromacs/mdlib/splitter.h [new file with mode: 0644]
src/include/gromacs/mdlib/stat.h [new file with mode: 0644]
src/include/gromacs/mdlib/stophandler.h [new file with mode: 0644]
src/include/gromacs/mdlib/tests/constrtestdata.h [new file with mode: 0644]
src/include/gromacs/mdlib/tests/constrtestrunners.h [new file with mode: 0644]
src/include/gromacs/mdlib/tests/leapfrogtestdata.h [new file with mode: 0644]
src/include/gromacs/mdlib/tests/leapfrogtestrunners.h [new file with mode: 0644]
src/include/gromacs/mdlib/tests/settletestdata.h [new file with mode: 0644]
src/include/gromacs/mdlib/tests/settletestrunners.h [new file with mode: 0644]
src/include/gromacs/mdlib/tests/watersystem.h [new file with mode: 0644]
src/include/gromacs/mdlib/tgroup.h [new file with mode: 0644]
src/include/gromacs/mdlib/trajectory_writing.h [new file with mode: 0644]
src/include/gromacs/mdlib/update.h [new file with mode: 0644]
src/include/gromacs/mdlib/update_constrain_gpu.h [new file with mode: 0644]
src/include/gromacs/mdlib/update_constrain_gpu_impl.h [new file with mode: 0644]
src/include/gromacs/mdlib/update_constrain_gpu_internal.h [new file with mode: 0644]
src/include/gromacs/mdlib/update_vv.h [new file with mode: 0644]
src/include/gromacs/mdlib/updategroups.h [new file with mode: 0644]
src/include/gromacs/mdlib/updategroupscog.h [new file with mode: 0644]
src/include/gromacs/mdlib/vcm.h [new file with mode: 0644]
src/include/gromacs/mdlib/vsite.h [new file with mode: 0644]
src/include/gromacs/mdlib/wall.h [new file with mode: 0644]
src/include/gromacs/mdlib/wholemoleculetransform.h [new file with mode: 0644]
src/include/gromacs/mdrun/isimulator.h [new file with mode: 0644]
src/include/gromacs/mdrun/legacymdrunoptions.h [new file with mode: 0644]
src/include/gromacs/mdrun/legacysimulator.h [new file with mode: 0644]
src/include/gromacs/mdrun/mdmodules.h [new file with mode: 0644]
src/include/gromacs/mdrun/membedholder.h [new file with mode: 0644]
src/include/gromacs/mdrun/minimize.h [new file with mode: 0644]
src/include/gromacs/mdrun/replicaexchange.h [new file with mode: 0644]
src/include/gromacs/mdrun/runner.h [new file with mode: 0644]
src/include/gromacs/mdrun/shellfc.h [new file with mode: 0644]
src/include/gromacs/mdrun/simulationcontext.h [new file with mode: 0644]
src/include/gromacs/mdrun/simulationinput.h [new file with mode: 0644]
src/include/gromacs/mdrun/simulationinputhandle.h [new file with mode: 0644]
src/include/gromacs/mdrun/simulatorbuilder.h [new file with mode: 0644]
src/include/gromacs/mdrunutility/freeenergy.h [new file with mode: 0644]
src/include/gromacs/mdrunutility/handlerestart.h [new file with mode: 0644]
src/include/gromacs/mdrunutility/logging.h [new file with mode: 0644]
src/include/gromacs/mdrunutility/multisim.h [new file with mode: 0644]
src/include/gromacs/mdrunutility/printtime.h [new file with mode: 0644]
src/include/gromacs/mdrunutility/tests/threadaffinitytest.h [new file with mode: 0644]
src/include/gromacs/mdrunutility/threadaffinity.h [new file with mode: 0644]
src/include/gromacs/mdspan/accessor_policy.h [new file with mode: 0644]
src/include/gromacs/mdspan/extensions.h [new file with mode: 0644]
src/include/gromacs/mdspan/extents.h [new file with mode: 0644]
src/include/gromacs/mdspan/layouts.h [new file with mode: 0644]
src/include/gromacs/mdspan/mdspan.h [new file with mode: 0644]
src/include/gromacs/mdtypes/atominfo.h [new file with mode: 0644]
src/include/gromacs/mdtypes/awh_correlation_history.h [new file with mode: 0644]
src/include/gromacs/mdtypes/awh_history.h [new file with mode: 0644]
src/include/gromacs/mdtypes/awh_params.h [new file with mode: 0644]
src/include/gromacs/mdtypes/checkpointdata.h [new file with mode: 0644]
src/include/gromacs/mdtypes/commrec.h [new file with mode: 0644]
src/include/gromacs/mdtypes/df_history.h [new file with mode: 0644]
src/include/gromacs/mdtypes/edsamhistory.h [new file with mode: 0644]
src/include/gromacs/mdtypes/enerdata.h [new file with mode: 0644]
src/include/gromacs/mdtypes/energyhistory.h [new file with mode: 0644]
src/include/gromacs/mdtypes/fcdata.h [new file with mode: 0644]
src/include/gromacs/mdtypes/forcebuffers.h [new file with mode: 0644]
src/include/gromacs/mdtypes/forceoutput.h [new file with mode: 0644]
src/include/gromacs/mdtypes/forcerec.h [new file with mode: 0644]
src/include/gromacs/mdtypes/group.h [new file with mode: 0644]
src/include/gromacs/mdtypes/iforceprovider.h [new file with mode: 0644]
src/include/gromacs/mdtypes/imdmodule.h [new file with mode: 0644]
src/include/gromacs/mdtypes/imdoutputprovider.h [new file with mode: 0644]
src/include/gromacs/mdtypes/imdpoptionprovider.h [new file with mode: 0644]
src/include/gromacs/mdtypes/inputrec.h [new file with mode: 0644]
src/include/gromacs/mdtypes/interaction_const.h [new file with mode: 0644]
src/include/gromacs/mdtypes/locality.h [new file with mode: 0644]
src/include/gromacs/mdtypes/md_enums.h [new file with mode: 0644]
src/include/gromacs/mdtypes/mdatom.h [new file with mode: 0644]
src/include/gromacs/mdtypes/mdrunoptions.h [new file with mode: 0644]
src/include/gromacs/mdtypes/multipletimestepping.h [new file with mode: 0644]
src/include/gromacs/mdtypes/nblist.h [new file with mode: 0644]
src/include/gromacs/mdtypes/observableshistory.h [new file with mode: 0644]
src/include/gromacs/mdtypes/observablesreducer.h [new file with mode: 0644]
src/include/gromacs/mdtypes/pull_params.h [new file with mode: 0644]
src/include/gromacs/mdtypes/pullhistory.h [new file with mode: 0644]
src/include/gromacs/mdtypes/simulation_workload.h [new file with mode: 0644]
src/include/gromacs/mdtypes/state.h [new file with mode: 0644]
src/include/gromacs/mdtypes/state_propagator_data_gpu.h [new file with mode: 0644]
src/include/gromacs/mdtypes/state_propagator_data_gpu_impl.h [new file with mode: 0644]
src/include/gromacs/mdtypes/swaphistory.h [new file with mode: 0644]
src/include/gromacs/mdtypes/threaded_force_buffer.h [new file with mode: 0644]
src/include/gromacs/mimic/communicator.h [new file with mode: 0644]
src/include/gromacs/mimic/utilities.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/andersentemperaturecoupling.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/checkpointhelper.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/compositesimulatorelement.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/computeglobalselement.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/constraintelement.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/domdechelper.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/energydata.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/expandedensembleelement.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/firstorderpressurecoupling.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/forceelement.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/freeenergyperturbationdata.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/modularsimulator.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/modularsimulatorinterfaces.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/mttk.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/nosehooverchains.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/parrinellorahmanbarostat.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/pmeloadbalancehelper.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/propagator.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/pullelement.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/referencetemperaturemanager.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/signallers.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/simulatoralgorithm.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/statepropagatordata.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/topologyholder.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/trajectoryelement.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/trotterhelperfunctions.h [new file with mode: 0644]
src/include/gromacs/modularsimulator/velocityscalingtemperaturecoupling.h [new file with mode: 0644]
src/include/gromacs/nbnxm/atomdata.h [new file with mode: 0644]
src/include/gromacs/nbnxm/benchmark/bench_coords.h [new file with mode: 0644]
src/include/gromacs/nbnxm/benchmark/bench_setup.h [new file with mode: 0644]
src/include/gromacs/nbnxm/benchmark/bench_system.h [new file with mode: 0644]
src/include/gromacs/nbnxm/boundingboxes.h [new file with mode: 0644]
src/include/gromacs/nbnxm/clusterdistancekerneltype.h [new file with mode: 0644]
src/include/gromacs/nbnxm/cuda/nbnxm_cuda.h [new file with mode: 0644]
src/include/gromacs/nbnxm/cuda/nbnxm_cuda_kernels.cuh [new file with mode: 0644]
src/include/gromacs/nbnxm/cuda/nbnxm_cuda_types.h [new file with mode: 0644]
src/include/gromacs/nbnxm/freeenergydispatch.h [new file with mode: 0644]
src/include/gromacs/nbnxm/gpu_common.h [new file with mode: 0644]
src/include/gromacs/nbnxm/gpu_common_utils.h [new file with mode: 0644]
src/include/gromacs/nbnxm/gpu_data_mgmt.h [new file with mode: 0644]
src/include/gromacs/nbnxm/gpu_jit_support.h [new file with mode: 0644]
src/include/gromacs/nbnxm/gpu_types_common.h [new file with mode: 0644]
src/include/gromacs/nbnxm/grid.h [new file with mode: 0644]
src/include/gromacs/nbnxm/gridset.h [new file with mode: 0644]
src/include/gromacs/nbnxm/gridsetdata.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernel_common.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernel_file_generator/kernel_simd_template.h.pre [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_reference/kernel_gpu_ref.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_reference/kernel_ref.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_reference/kernel_ref_includes.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_reference/kernel_ref_inner.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_reference/kernel_ref_outer.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_reference/kernel_ref_prune.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_simd_2xmm/kernel_common.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_simd_2xmm/kernel_inner.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_simd_2xmm/kernel_outer.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_simd_2xmm/kernel_prune.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_simd_2xmm/kernels.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_simd_4xm/kernel_common.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_simd_4xm/kernel_inner.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_simd_4xm/kernel_outer.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_simd_4xm/kernel_prune.h [new file with mode: 0644]
src/include/gromacs/nbnxm/kernels_simd_4xm/kernels.h [new file with mode: 0644]
src/include/gromacs/nbnxm/nbnxm.h [new file with mode: 0644]
src/include/gromacs/nbnxm/nbnxm_geometry.h [new file with mode: 0644]
src/include/gromacs/nbnxm/nbnxm_gpu.h [new file with mode: 0644]
src/include/gromacs/nbnxm/nbnxm_gpu_buffer_ops_internal.h [new file with mode: 0644]
src/include/gromacs/nbnxm/nbnxm_gpu_data_mgmt.h [new file with mode: 0644]
src/include/gromacs/nbnxm/nbnxm_simd.h [new file with mode: 0644]
src/include/gromacs/nbnxm/opencl/nbnxm_ocl_consts.h [new file with mode: 0644]
src/include/gromacs/nbnxm/opencl/nbnxm_ocl_kernel_pruneonly.clh [new file with mode: 0644]
src/include/gromacs/nbnxm/opencl/nbnxm_ocl_types.h [new file with mode: 0644]
src/include/gromacs/nbnxm/pairlist.h [new file with mode: 0644]
src/include/gromacs/nbnxm/pairlist_simd_2xmm.h [new file with mode: 0644]
src/include/gromacs/nbnxm/pairlist_simd_4xm.h [new file with mode: 0644]
src/include/gromacs/nbnxm/pairlist_tuning.h [new file with mode: 0644]
src/include/gromacs/nbnxm/pairlistparams.h [new file with mode: 0644]
src/include/gromacs/nbnxm/pairlistset.h [new file with mode: 0644]
src/include/gromacs/nbnxm/pairlistsets.h [new file with mode: 0644]
src/include/gromacs/nbnxm/pairlistwork.h [new file with mode: 0644]
src/include/gromacs/nbnxm/pairsearch.h [new file with mode: 0644]
src/include/gromacs/nbnxm/sycl/nbnxm_sycl_kernel.h [new file with mode: 0644]
src/include/gromacs/nbnxm/sycl/nbnxm_sycl_kernel_pruneonly.h [new file with mode: 0644]
src/include/gromacs/nbnxm/sycl/nbnxm_sycl_kernel_utils.h [new file with mode: 0644]
src/include/gromacs/nbnxm/sycl/nbnxm_sycl_types.h [new file with mode: 0644]
src/include/gromacs/onlinehelp/helpformat.h [new file with mode: 0644]
src/include/gromacs/onlinehelp/helpmanager.h [new file with mode: 0644]
src/include/gromacs/onlinehelp/helptopic.h [new file with mode: 0644]
src/include/gromacs/onlinehelp/helpwritercontext.h [new file with mode: 0644]
src/include/gromacs/onlinehelp/ihelptopic.h [new file with mode: 0644]
src/include/gromacs/onlinehelp/rstparser.h [new file with mode: 0644]
src/include/gromacs/onlinehelp/tests/mock_helptopic.h [new file with mode: 0644]
src/include/gromacs/options/abstractoption.h [new file with mode: 0644]
src/include/gromacs/options/abstractoptionstorage.h [new file with mode: 0644]
src/include/gromacs/options/abstractsection.h [new file with mode: 0644]
src/include/gromacs/options/basicoptions.h [new file with mode: 0644]
src/include/gromacs/options/basicoptionstorage.h [new file with mode: 0644]
src/include/gromacs/options/behaviorcollection.h [new file with mode: 0644]
src/include/gromacs/options/filenameoption.h [new file with mode: 0644]
src/include/gromacs/options/filenameoptionmanager.h [new file with mode: 0644]
src/include/gromacs/options/filenameoptionstorage.h [new file with mode: 0644]
src/include/gromacs/options/ioptionsbehavior.h [new file with mode: 0644]
src/include/gromacs/options/ioptionscontainer.h [new file with mode: 0644]
src/include/gromacs/options/ioptionscontainerwithsections.h [new file with mode: 0644]
src/include/gromacs/options/isectionstorage.h [new file with mode: 0644]
src/include/gromacs/options/ivaluestore.h [new file with mode: 0644]
src/include/gromacs/options/optionfiletype.h [new file with mode: 0644]
src/include/gromacs/options/optionflags.h [new file with mode: 0644]
src/include/gromacs/options/optionmanagercontainer.h [new file with mode: 0644]
src/include/gromacs/options/options.h [new file with mode: 0644]
src/include/gromacs/options/options_impl.h [new file with mode: 0644]
src/include/gromacs/options/optionsassigner.h [new file with mode: 0644]
src/include/gromacs/options/optionsection.h [new file with mode: 0644]
src/include/gromacs/options/optionstoragetemplate.h [new file with mode: 0644]
src/include/gromacs/options/optionsvisitor.h [new file with mode: 0644]
src/include/gromacs/options/repeatingsection.h [new file with mode: 0644]
src/include/gromacs/options/timeunitmanager.h [new file with mode: 0644]
src/include/gromacs/options/treesupport.h [new file with mode: 0644]
src/include/gromacs/options/valueconverter.h [new file with mode: 0644]
src/include/gromacs/options/valuestore.h [new file with mode: 0644]
src/include/gromacs/pbcutil/boxutilities.h [new file with mode: 0644]
src/include/gromacs/pbcutil/com.h [new file with mode: 0644]
src/include/gromacs/pbcutil/ishift.h [new file with mode: 0644]
src/include/gromacs/pbcutil/mshift.h [new file with mode: 0644]
src/include/gromacs/pbcutil/pbc.h [new file with mode: 0644]
src/include/gromacs/pbcutil/pbc_aiuc.h [new file with mode: 0644]
src/include/gromacs/pbcutil/pbc_aiuc_sycl.h [new file with mode: 0644]
src/include/gromacs/pbcutil/pbc_simd.h [new file with mode: 0644]
src/include/gromacs/pbcutil/pbcenums.h [new file with mode: 0644]
src/include/gromacs/pbcutil/pbcmethods.h [new file with mode: 0644]
src/include/gromacs/pbcutil/rmpbc.h [new file with mode: 0644]
src/include/gromacs/pulling/output.h [new file with mode: 0644]
src/include/gromacs/pulling/pull.h [new file with mode: 0644]
src/include/gromacs/pulling/pull_internal.h [new file with mode: 0644]
src/include/gromacs/pulling/pull_rotation.h [new file with mode: 0644]
src/include/gromacs/pulling/pullcoordexpressionparser.h [new file with mode: 0644]
src/include/gromacs/pulling/transformationcoordinate.h [new file with mode: 0644]
src/include/gromacs/random/exponentialdistribution.h [new file with mode: 0644]
src/include/gromacs/random/gammadistribution.h [new file with mode: 0644]
src/include/gromacs/random/normaldistribution.h [new file with mode: 0644]
src/include/gromacs/random/seed.h [new file with mode: 0644]
src/include/gromacs/random/tabulatednormaldistribution.h [new file with mode: 0644]
src/include/gromacs/random/threefry.h [new file with mode: 0644]
src/include/gromacs/random/uniformintdistribution.h [new file with mode: 0644]
src/include/gromacs/random/uniformrealdistribution.h [new file with mode: 0644]
src/include/gromacs/restraint/manager.h [new file with mode: 0644]
src/include/gromacs/restraint/restraintmdmodule.h [new file with mode: 0644]
src/include/gromacs/restraint/restraintmdmodule_impl.h [new file with mode: 0644]
src/include/gromacs/selection/centerofmass.h [new file with mode: 0644]
src/include/gromacs/selection/compiler.h [new file with mode: 0644]
src/include/gromacs/selection/evaluate.h [new file with mode: 0644]
src/include/gromacs/selection/indexutil.h [new file with mode: 0644]
src/include/gromacs/selection/keywords.h [new file with mode: 0644]
src/include/gromacs/selection/mempool.h [new file with mode: 0644]
src/include/gromacs/selection/nbsearch.h [new file with mode: 0644]
src/include/gromacs/selection/parser.h [new file with mode: 0644]
src/include/gromacs/selection/parser_internal.h [new file with mode: 0644]
src/include/gromacs/selection/parsetree.h [new file with mode: 0644]
src/include/gromacs/selection/poscalc.h [new file with mode: 0644]
src/include/gromacs/selection/position.h [new file with mode: 0644]
src/include/gromacs/selection/scanner.h [new file with mode: 0644]
src/include/gromacs/selection/scanner_flex.h [new file with mode: 0644]
src/include/gromacs/selection/scanner_internal.h [new file with mode: 0644]
src/include/gromacs/selection/selection.h [new file with mode: 0644]
src/include/gromacs/selection/selectioncollection.h [new file with mode: 0644]
src/include/gromacs/selection/selectioncollection_impl.h [new file with mode: 0644]
src/include/gromacs/selection/selectionenums.h [new file with mode: 0644]
src/include/gromacs/selection/selectionfileoption.h [new file with mode: 0644]
src/include/gromacs/selection/selectionfileoptionstorage.h [new file with mode: 0644]
src/include/gromacs/selection/selectionoption.h [new file with mode: 0644]
src/include/gromacs/selection/selectionoptionbehavior.h [new file with mode: 0644]
src/include/gromacs/selection/selectionoptionmanager.h [new file with mode: 0644]
src/include/gromacs/selection/selectionoptionstorage.h [new file with mode: 0644]
src/include/gromacs/selection/selelem.h [new file with mode: 0644]
src/include/gromacs/selection/selhelp.h [new file with mode: 0644]
src/include/gromacs/selection/selmethod.h [new file with mode: 0644]
src/include/gromacs/selection/selmethod_impl.h [new file with mode: 0644]
src/include/gromacs/selection/selparam.h [new file with mode: 0644]
src/include/gromacs/selection/selvalue.h [new file with mode: 0644]
src/include/gromacs/selection/symrec.h [new file with mode: 0644]
src/include/gromacs/selection/tests/toputils.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_definitions.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_general.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_simd4_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_simd_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_simd_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_util_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_util_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_sve/impl_arm_sve.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_definitions.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_general.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_simd4_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_simd4_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_simd_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_simd_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_util_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_util_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx.h [new file with mode: 0644]
src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_definitions.h [new file with mode: 0644]
src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_general.h [new file with mode: 0644]
src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd4_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_util_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_util_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_none/impl_none.h [new file with mode: 0644]
src/include/gromacs/simd/impl_reference/impl_reference.h [new file with mode: 0644]
src/include/gromacs/simd/impl_reference/impl_reference_definitions.h [new file with mode: 0644]
src/include/gromacs/simd/impl_reference/impl_reference_general.h [new file with mode: 0644]
src/include/gromacs/simd/impl_reference/impl_reference_simd4_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_reference/impl_reference_simd4_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_reference/impl_reference_simd_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_reference/impl_reference_simd_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_reference/impl_reference_util_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_reference/impl_reference_util_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_definitions.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_general.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_simd4_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_simd4_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_simd_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_simd_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_util_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_util_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_definitions.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_general.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd4_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd4_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_util_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_util_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_definitions.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_general.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_simd4_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_simd4_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_simd_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_simd_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_util_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_util_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_definitions.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_general.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd4_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd4_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_util_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_util_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_definitions.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_general.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd4_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd4_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_util_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_util_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_definitions.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_general.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_simd4_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_simd4_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_simd_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_simd_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_util_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_util_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_definitions.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_general.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd4_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_util_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_util_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_definitions.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_general.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd4_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd_float.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_util_double.h [new file with mode: 0644]
src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_util_float.h [new file with mode: 0644]
src/include/gromacs/simd/scalar/scalar.h [new file with mode: 0644]
src/include/gromacs/simd/scalar/scalar_math.h [new file with mode: 0644]
src/include/gromacs/simd/scalar/scalar_util.h [new file with mode: 0644]
src/include/gromacs/simd/simd.h [new file with mode: 0644]
src/include/gromacs/simd/simd_math.h [new file with mode: 0644]
src/include/gromacs/simd/simd_memory.h [new file with mode: 0644]
src/include/gromacs/simd/support.h [new file with mode: 0644]
src/include/gromacs/simd/tests/base.h [new file with mode: 0644]
src/include/gromacs/simd/tests/data.h [new file with mode: 0644]
src/include/gromacs/simd/tests/simd.h [new file with mode: 0644]
src/include/gromacs/simd/tests/simd4.h [new file with mode: 0644]
src/include/gromacs/simd/vector_operations.h [new file with mode: 0644]
src/include/gromacs/statistics/statistics.h [new file with mode: 0644]
src/include/gromacs/swap/swapcoords.h [new file with mode: 0644]
src/include/gromacs/tables/cubicsplinetable.h [new file with mode: 0644]
src/include/gromacs/tables/forcetable.h [new file with mode: 0644]
src/include/gromacs/tables/quadraticsplinetable.h [new file with mode: 0644]
src/include/gromacs/tables/splineutil.h [new file with mode: 0644]
src/include/gromacs/tables/tableinput.h [new file with mode: 0644]
src/include/gromacs/taskassignment/decidegpuusage.h [new file with mode: 0644]
src/include/gromacs/taskassignment/decidesimulationworkload.h [new file with mode: 0644]
src/include/gromacs/taskassignment/findallgputasks.h [new file with mode: 0644]
src/include/gromacs/taskassignment/reportgpuusage.h [new file with mode: 0644]
src/include/gromacs/taskassignment/resourcedivision.h [new file with mode: 0644]
src/include/gromacs/taskassignment/taskassignment.h [new file with mode: 0644]
src/include/gromacs/taskassignment/usergpuids.h [new file with mode: 0644]
src/include/gromacs/timing/cyclecounter.h [new file with mode: 0644]
src/include/gromacs/timing/gpu_timing.h [new file with mode: 0644]
src/include/gromacs/timing/wallcycle.h [new file with mode: 0644]
src/include/gromacs/timing/wallcyclereporting.h [new file with mode: 0644]
src/include/gromacs/timing/walltime_accounting.h [new file with mode: 0644]
src/include/gromacs/tools/check.h [new file with mode: 0644]
src/include/gromacs/tools/convert_tpr.h [new file with mode: 0644]
src/include/gromacs/tools/dump.h [new file with mode: 0644]
src/include/gromacs/tools/eneconv.h [new file with mode: 0644]
src/include/gromacs/tools/make_ndx.h [new file with mode: 0644]
src/include/gromacs/tools/mk_angndx.h [new file with mode: 0644]
src/include/gromacs/tools/pme_error.h [new file with mode: 0644]
src/include/gromacs/tools/report_methods.h [new file with mode: 0644]
src/include/gromacs/tools/trjcat.h [new file with mode: 0644]
src/include/gromacs/tools/trjconv.h [new file with mode: 0644]
src/include/gromacs/tools/tune_pme.h [new file with mode: 0644]
src/include/gromacs/topology/atomprop.h [new file with mode: 0644]
src/include/gromacs/topology/atoms.h [new file with mode: 0644]
src/include/gromacs/topology/atomsbuilder.h [new file with mode: 0644]
src/include/gromacs/topology/block.h [new file with mode: 0644]
src/include/gromacs/topology/exclusionblocks.h [new file with mode: 0644]
src/include/gromacs/topology/forcefieldparameters.h [new file with mode: 0644]
src/include/gromacs/topology/idef.h [new file with mode: 0644]
src/include/gromacs/topology/ifunc.h [new file with mode: 0644]
src/include/gromacs/topology/index.h [new file with mode: 0644]
src/include/gromacs/topology/invblock.h [new file with mode: 0644]
src/include/gromacs/topology/mtop_lookup.h [new file with mode: 0644]
src/include/gromacs/topology/mtop_util.h [new file with mode: 0644]
src/include/gromacs/topology/residuetypes.h [new file with mode: 0644]
src/include/gromacs/topology/symtab.h [new file with mode: 0644]
src/include/gromacs/topology/topology.h [new file with mode: 0644]
src/include/gromacs/topology/topsort.h [new file with mode: 0644]
src/include/gromacs/trajectory/energyframe.h [new file with mode: 0644]
src/include/gromacs/trajectory/trajectoryframe.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/analysismodule.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/analysissettings.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/analysissettings_impl.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/cmdlinerunner.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/angle.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/convert_trj.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/distance.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/extract_cluster.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/freevolume.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/msd.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/pairdist.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/rdf.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/sasa.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/select.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/surfacearea.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/trajectory.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/modules/unionfind.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/runnercommon.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/tests/lysozyme.top [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/tests/moduletest.h [new file with mode: 0644]
src/include/gromacs/trajectoryanalysis/topologyinformation.h [new file with mode: 0644]
src/include/gromacs/utility/alignedallocator.h [new file with mode: 0644]
src/include/gromacs/utility/allocator.h [new file with mode: 0644]
src/include/gromacs/utility/any.h [new file with mode: 0644]
src/include/gromacs/utility/arraysize.h [new file with mode: 0644]
src/include/gromacs/utility/basenetwork.h [new file with mode: 0644]
src/include/gromacs/utility/baseversion-gen.cpp.cmakein [new file with mode: 0644]
src/include/gromacs/utility/baseversion.h [new file with mode: 0644]
src/include/gromacs/utility/baseversion_gen.h [new file with mode: 0644]
src/include/gromacs/utility/binaryinformation.h [new file with mode: 0644]
src/include/gromacs/utility/bitmask.h [new file with mode: 0644]
src/include/gromacs/utility/classhelpers.h [new file with mode: 0644]
src/include/gromacs/utility/compare.h [new file with mode: 0644]
src/include/gromacs/utility/coolstuff.h [new file with mode: 0644]
src/include/gromacs/utility/cstringutil.h [new file with mode: 0644]
src/include/gromacs/utility/cuda_version_information.h [new file with mode: 0644]
src/include/gromacs/utility/datafilefinder.h [new file with mode: 0644]
src/include/gromacs/utility/defaultinitializationallocator.h [new file with mode: 0644]
src/include/gromacs/utility/dir_separator.h [new file with mode: 0644]
src/include/gromacs/utility/directoryenumerator.h [new file with mode: 0644]
src/include/gromacs/utility/enumerationhelpers.h [new file with mode: 0644]
src/include/gromacs/utility/errorcodes.h [new file with mode: 0644]
src/include/gromacs/utility/errorformat.h [new file with mode: 0644]
src/include/gromacs/utility/fatalerror.h [new file with mode: 0644]
src/include/gromacs/utility/fileptr.h [new file with mode: 0644]
src/include/gromacs/utility/fileredirector.h [new file with mode: 0644]
src/include/gromacs/utility/filestream.h [new file with mode: 0644]
src/include/gromacs/utility/fixedcapacityvector.h [new file with mode: 0644]
src/include/gromacs/utility/flags.h [new file with mode: 0644]
src/include/gromacs/utility/futil.h [new file with mode: 0644]
src/include/gromacs/utility/gmxmpi.h [new file with mode: 0644]
src/include/gromacs/utility/gmxomp.h [new file with mode: 0644]
src/include/gromacs/utility/ikeyvaluetreeerror.h [new file with mode: 0644]
src/include/gromacs/utility/init.h [new file with mode: 0644]
src/include/gromacs/utility/inmemoryserializer.h [new file with mode: 0644]
src/include/gromacs/utility/int64_to_int.h [new file with mode: 0644]
src/include/gromacs/utility/iserializer.h [new file with mode: 0644]
src/include/gromacs/utility/keyvaluetree.h [new file with mode: 0644]
src/include/gromacs/utility/keyvaluetreebuilder.h [new file with mode: 0644]
src/include/gromacs/utility/keyvaluetreemdpwriter.h [new file with mode: 0644]
src/include/gromacs/utility/keyvaluetreeserializer.h [new file with mode: 0644]
src/include/gromacs/utility/keyvaluetreetransform.h [new file with mode: 0644]
src/include/gromacs/utility/logger.h [new file with mode: 0644]
src/include/gromacs/utility/loggerbuilder.h [new file with mode: 0644]
src/include/gromacs/utility/mdmodulesnotifier.h [new file with mode: 0644]
src/include/gromacs/utility/mdmodulesnotifiers.h [new file with mode: 0644]
src/include/gromacs/utility/message_string_collector.h [new file with mode: 0644]
src/include/gromacs/utility/mpiinfo.h [new file with mode: 0644]
src/include/gromacs/utility/niceheader.h [new file with mode: 0644]
src/include/gromacs/utility/nodelete.h [new file with mode: 0644]
src/include/gromacs/utility/path.h [new file with mode: 0644]
src/include/gromacs/utility/physicalnodecommunicator.h [new file with mode: 0644]
src/include/gromacs/utility/pleasecite.h [new file with mode: 0644]
src/include/gromacs/utility/programcontext.h [new file with mode: 0644]
src/include/gromacs/utility/range.h [new file with mode: 0644]
src/include/gromacs/utility/smalloc.h [new file with mode: 0644]
src/include/gromacs/utility/snprintf.h [new file with mode: 0644]
src/include/gromacs/utility/strconvert.h [new file with mode: 0644]
src/include/gromacs/utility/strdb.h [new file with mode: 0644]
src/include/gromacs/utility/stringcompare.h [new file with mode: 0644]
src/include/gromacs/utility/stringstream.h [new file with mode: 0644]
src/include/gromacs/utility/stringtoenumvalueconverter.h [new file with mode: 0644]
src/include/gromacs/utility/stringutil.h [new file with mode: 0644]
src/include/gromacs/utility/sycl_version_information.h [new file with mode: 0644]
src/include/gromacs/utility/sysinfo.h [new file with mode: 0644]
src/include/gromacs/utility/template_mp.h [new file with mode: 0644]
src/include/gromacs/utility/tests/alignedallocator_impl.h [new file with mode: 0644]
src/include/gromacs/utility/tests/bitmask.h [new file with mode: 0644]
src/include/gromacs/utility/textreader.h [new file with mode: 0644]
src/include/gromacs/utility/textstream.h [new file with mode: 0644]
src/include/gromacs/utility/textwriter.h [new file with mode: 0644]
src/include/gromacs/utility/txtdump.h [new file with mode: 0644]
src/include/gromacs/utility/typetraits.h [new file with mode: 0644]
src/include/gromacs/utility/unique_cptr.h [new file with mode: 0644]

index e80ee1bb807e1a2db26155a268a189a4a26aa14e..33536f62c2ec4bb981aea7ca7b77692d1a93fb09 100644 (file)
@@ -28,7 +28,7 @@ else()
     set(GROMACS_SUFFIX ${GMX_SUFFIX})
 endif()
 
-find_package(GROMACS 2021 REQUIRED)
+find_package(GROMACS 2022 REQUIRED)
 gromacs_check_double(GMX_DOUBLE)
 gromacs_check_compiler(CXX)
 
index d8959ade49582d8e280ecb8bcf9a91ee59168bc9..530a09c71550ab2a716cf8026c0eafc85bcd2a7f 100644 (file)
 
 # Propagate all flags passed to parent find_package() to the config call below.
 
+#set(GROMACS_DIR "/usr/local/gromacs/share/cmake/gromacs/")
+set(GROMACS_DIR "/home/max/GROMACS/gromacs-2022-beta1/build/src/gromacs/")
+#set(GROMACS_DIR "/home/max/GROMACS/gromacs libs/share/cmake/gromacs/")
+
 #set(GROMACS_DIR "/usr/local/gromacs/share/cmake")
 #set(GROMACS_DIR "/home/max/gromacs-2021.3/build/src/gromacs/")
 #set(GROMACS_DIR "/home/max/gromacs-2022-beta1/build/src/gromacs/")
index 57f91ef4d6f6f892407d7c64672238ba6a77f00f..736244f0122cb2e04593f7e21243984939fa6dfb 100644 (file)
@@ -1,7 +1,7 @@
 add_executable(dssp  dssp.cpp)
 include_directories(
         ${GROMACS_INCLUDE_DIRS}
-        ${CMAKE_SOURCE_DIR}/include)
+        ${CMAKE_SOURCE_DIR}/src/include)
 set_target_properties(dssp PROPERTIES
                       COMPILE_FLAGS "${GROMACS_CXX_FLAGS}")
 target_link_libraries(dssp ${GROMACS_LIBRARIES})
diff --git a/src/include/gromacs/analysisdata/abstractdata.h b/src/include/gromacs/analysisdata/abstractdata.h
new file mode 100644 (file)
index 0000000..c8ed784
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AbstractAnalysisData.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_ABSTRACTDATA_H
+#define GMX_ANALYSISDATA_ABSTRACTDATA_H
+
+#include <memory>
+
+namespace gmx
+{
+
+class AnalysisDataModuleManager;
+class AnalysisDataFrameHeader;
+class AnalysisDataFrameRef;
+class AnalysisDataPointSetRef;
+class IAnalysisDataModule;
+
+//! Smart pointer for managing a generic analysis data module.
+typedef std::shared_ptr<IAnalysisDataModule> AnalysisDataModulePointer;
+
+/*! \brief
+ * Abstract base class for all objects that provide data.
+ *
+ * The public interface includes methods for querying the data (isMultipoint(),
+ * dataSetCount(), columnCount(), frameCount(), tryGetDataFrame(),
+ * getDataFrame(), requestStorage()) and methods for using modules for
+ * processing the data (addModule(), addColumnModule(), applyModule()).
+ *
+ * Notice that even for non-const objects, the interface does not provide any
+ * means of altering the data.  It is only possible to add modules, making it
+ * relatively safe to return a non-const pointer of this type pointing to an
+ * internal data structure without worrying about possible modifications of the
+ * data.
+ *
+ * \if libapi
+ * This class also provides protected methods for use in derived classes.
+ * The properties returned by isMultipoint(), dataSetCount(), and columnCount()
+ * must be set using setMultipoint(), setDataSetCount(), and setColumnCount().
+ * notify*() methods in the AnalysisDataModuleManager returned by
+ * moduleManager() must be used to report when data becomes available for
+ * modules to process it.
+ * There are also three pure virtual methods that need to be implemented to
+ * provide access to stored data: one public (frameCount()) and two protected
+ * ones (requestStorageInternal() and tryGetDataFrameInternal()).
+ *
+ * It is up to subclasses to ensure that the virtual methods and the
+ * notifications in AnalysisDataModuleManager are called in a
+ * correct sequence (the methods will assert in most incorrect use cases), and
+ * that the data provided through the public interface matches that passed to
+ * the modules with the notify methods.
+ * Helper class AnalysisDataStorage provides a default implementation for
+ * storing data (calls to the pure virtual methods can simply be forwarded to
+ * appropriate methods in the helper class), and takes care of correctly
+ * calling the notification methods when new data is added to the storage.
+ * In most cases, it should be used to implement the derived classes.
+ * \endif
+ *
+ * Currently, it is not possible to continue using the data object if an
+ * attached module throws an exception during data processing; it is only safe
+ * to destroy such data object.
+ *
+ * \todo
+ * Improve the exception-handling semantics.  In most cases, it doesn't make
+ * much sense to continue data processing after one module fails, but having
+ * the alternative would not hurt.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AbstractAnalysisData
+{
+public:
+    virtual ~AbstractAnalysisData();
+
+    /*! \brief
+     * Whether the data can have multiple points in the same column
+     * in the same frame.
+     *
+     * \returns \c true if multiple points in the same column are
+     *     allowed within a single frame.
+     *
+     * This kind of data can appear in many histogramming applications
+     * (e.g., RDFs), where each trajectory frame has several data points
+     * (possibly a different number for each frame). The current interface
+     * doesn't support storing such data, but this should rarely be
+     * necessary.
+     *
+     * The returned value does not change after modules have been notified
+     * of data start.
+     * \if libapi
+     * Derived classes can change the type by calling setMultipoint()
+     * subject to the above restriction.
+     * If this is not done, the function always returns false.
+     * \endif
+     *
+     * Does not throw.
+     */
+    bool isMultipoint() const;
+    /*! \brief
+     * Returns the number of data sets in the data object.
+     *
+     * \returns The number of data sets in the data.
+     *
+     * If the number is not yet known, returns 0.
+     * The returned value does not change after modules have been notified
+     * of data start, but may change multiple times before that, depending
+     * on the actual data class.
+     * \if libapi
+     * Derived classes should set the number of columns with
+     * setDataSetCount(), within the above limitations.
+     * \endif
+     *
+     * Does not throw.
+     */
+    int dataSetCount() const;
+    /*! \brief
+     * Returns the number of columns in a data set.
+     *
+     * \param[in] dataSet Zero-based index of the data set to query.
+     * \returns The number of columns in the data.
+     *
+     * If the number of columns is not yet known, returns 0.
+     * The returned value does not change after modules have been notified
+     * of data start, but may change multiple times before that, depending
+     * on the actual data class.
+     * \if libapi
+     * Derived classes should set the number of columns with
+     * setColumnCount(), within the above limitations.
+     * \endif
+     *
+     * Does not throw.
+     */
+    int columnCount(int dataSet) const;
+    /*! \brief
+     * Returns the number of columns in the data.
+     *
+     * \returns The number of columns in the data.
+     *
+     * This is a convenience method for data objects with a single data set.
+     * Can only be called if dataSetCount() == 1.
+     *
+     * Does not throw.
+     *
+     * \see columnCount(int)
+     */
+    int columnCount() const;
+    /*! \brief
+     * Returns the total number of frames in the data.
+     *
+     * \returns The total number of frames in the data.
+     *
+     * This function returns the number of frames that the object has
+     * produced.  If requestStorage() has been successfully called,
+     * tryGetDataframe() or getDataFrame() can be used to access some or
+     * all of these frames.
+     *
+     * Does not throw.
+     *
+     * \if libapi
+     * Derived classes should implement this to return the number of
+     * frames.  The frame count should not be incremented before
+     * tryGetDataFrameInternal() can return the new frame.
+     * The frame count must be incremented before
+     * AnalysisDataModuleManager::notifyFrameFinish() is called.
+     * \endif
+     */
+    virtual int frameCount() const = 0;
+    /*! \brief
+     * Access stored data.
+     *
+     * \param[in] index  Zero-based frame index to access.
+     * \returns   Frame reference to frame \p index, or an invalid
+     *      reference if no such frame is available.
+     *
+     * Does not throw.  Failure to access a frame with the given index is
+     * indicated through the return value.  Negative \p index is allowed,
+     * and will always result in an invalid reference being returned.
+     *
+     * \see requestStorage()
+     * \see getDataFrame()
+     */
+    AnalysisDataFrameRef tryGetDataFrame(int index) const;
+    /*! \brief
+     * Access stored data.
+     *
+     * \param[in] index  Zero-based frame index to access.
+     * \returns   Frame reference to frame \p index.
+     * \throws    APIError if the requested frame is not accessible.
+     *
+     * If the data is not certainly available, use tryGetDataFrame().
+     *
+     * \see requestStorage()
+     * \see tryGetDataFrame()
+     */
+    AnalysisDataFrameRef getDataFrame(int index) const;
+    /*! \brief
+     * Request storage of frames.
+     *
+     * \param[in] nframes  Request storing at least \c nframes previous
+     *     frames (-1 = request storing all). Must be >= -1.
+     * \returns true if the request could be satisfied.
+     *
+     * If called multiple times, the largest request is honored.
+     *
+     * Does not throw.  Failure to honor the request is indicated through
+     * the return value.
+     *
+     * \see getDataFrame()
+     * \see tryGetDataFrame()
+     */
+    bool requestStorage(int nframes);
+
+    /*! \brief
+     * Adds a module to process the data.
+     *
+     * \param     module  Module to add.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    APIError if
+     *      - \p module is not compatible with the data object
+     *      - data has already been added to the data object and everything
+     *        is not available through getDataFrame().
+     * \throws    unspecified Any exception thrown by \p module in its
+     *      notification methods (if data has been added).
+     *
+     * If data has already been added to the data, the new module
+     * immediately processes all existing data.  APIError is thrown
+     * if all data is not available through getDataFrame().
+     *
+     * The caller can keep a copy of the module pointer if it requires
+     * later access to the module.
+     *
+     * If the method throws, the state of the data object is not changed.
+     * The state of the data module is indeterminate.
+     */
+    void addModule(const AnalysisDataModulePointer& module);
+    /*! \brief
+     * Adds a module that processes only a subset of the columns.
+     *
+     * \param[in] col     First column.
+     * \param[in] span    Number of columns.
+     * \param     module  Module to add.
+     *
+     * Throws in the same situations as addModule().
+     *
+     * Currently, all data sets are filtered using the same column mask.
+     *
+     * \todo
+     * This method doesn't currently work in all cases with multipoint
+     * data or with multiple data sets.  In particular, if the added module
+     * requests storage and uses getDataFrame(), it will behave
+     * unpredictably (most likely asserts).
+     *
+     * \todo
+     * Generalize this method to multiple data sets (e.g., for adding
+     * modules that only process a single data set).
+     *
+     * \see addModule()
+     */
+    void addColumnModule(int col, int span, const AnalysisDataModulePointer& module);
+    /*! \brief
+     * Applies a module to process data that is ready.
+     *
+     * \param     module  Module to apply.
+     * \throws    APIError in same situations as addModule().
+     * \throws    unspecified Any exception thrown by \p module in its
+     *      notification methods.
+     *
+     * This function works as addModule(), except that it does not keep a
+     * reference to \p module within the data object after it returns.
+     * Also, it can only be called after the data is ready, and only if
+     * getDataFrame() gives access to all of the data.
+     * It is provided for additional flexibility in postprocessing
+     * in-memory data.
+     *
+     * \todo
+     * Currently, this method may not work correctly if \p module requests
+     * storage (addModule() has the same problem if called after data is
+     * started).
+     */
+    void applyModule(IAnalysisDataModule* module);
+
+protected:
+    /*! \cond libapi */
+    /*! \brief
+     * Initializes a new analysis data object.
+     *
+     * \throws std::bad_alloc if out of memory.
+     */
+    AbstractAnalysisData();
+
+    /*! \brief
+     * Sets the number of data sets.
+     *
+     * \param[in] dataSetCount  Number of data sets (must be > 0).
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    APIError if modules have been added that are not
+     *      compatible with the new data set count.
+     *
+     * It not called, the data object has a single data set.  Can be called
+     * only before AnalysisDataModuleManager::notifyDataStart().
+     * Multiple calls are allowed before that point; the last call takes
+     * effect.
+     *
+     * Strong exception safety.
+     *
+     * \see dataSetCount()
+     */
+    void setDataSetCount(int dataSetCount);
+    /*! \brief
+     * Sets the number of columns for a data set.
+     *
+     * \param[in] dataSet      Zero-based index of the data set.
+     * \param[in] columnCount  Number of columns in \p dataSet (must be > 0).
+     * \throws    APIError if modules have been added that are not
+     *      compatible with the new column count.
+     *
+     * Must be called at least once for each data set before
+     * AnalysisDataModuleManager::notifyDataStart().  Can be called only
+     * before AnalysisDataModuleManager::notifyDataStart().
+     * Multiple calls are allowed before that point; the last call takes
+     * effect.
+     *
+     * Strong exception safety.
+     *
+     * \see columnCount()
+     */
+    void setColumnCount(int dataSet, int columnCount);
+    /*! \brief
+     * Sets whether the data has multiple points per column in a frame.
+     *
+     * \param[in] bMultipoint  Whether multiple points per column are
+     *     possible.
+     * \throws    APIError if modules have been added that are not
+     *      compatible with the new setting.
+     *
+     * If not called, only a single point per column is allowed.  Can be
+     * called only before AnalysisDataModuleManager::notifyDataStart().
+     * Multiple calls are allowed before that point; the last call takes
+     * effect.
+     *
+     * Strong exception safety.
+     *
+     * \see isMultipoint()
+     */
+    void setMultipoint(bool bMultipoint);
+
+    /*! \brief
+     * Implements access to data frames.
+     *
+     * \param[in] index  Zero-based frame index to access.
+     * \returns   Frame reference to frame \p index, or an invalid
+     *      reference if no such frame is available.
+     *
+     * Must not throw.  Failure to access a frame with the given index is
+     * indicated through the return value.
+     *
+     * Code in derived classes can assume that \p index is non-negative and
+     * less than frameCount().
+     *
+     * Derived classes can choose to return an invalid reference if
+     * requestStorageInternal() has not been called at all, or if the frame
+     * is too old (compared to the value given to requestStorageInternal()).
+     *
+     * This method is called internally by tryGetDataFrame() and
+     * getDataFrame().
+     *
+     * \see AnalysisDataStorage
+     */
+    virtual AnalysisDataFrameRef tryGetDataFrameInternal(int index) const = 0;
+    /*! \brief
+     * Implements storage requests.
+     *
+     * \param[in] nframes  Request storing at least \c nframes previous
+     *     frames (-1 = request storing all). Will be either -1 or >0.
+     * \returns   true if the request could be satisfied.
+     *
+     * Must not throw.  Failure to access a frame with the given index is
+     * indicated through the return value.
+     *
+     * Derived classes should be prepared for any number of calls to this
+     * method before notifyDataStart() is called (and during that call).
+     *
+     * This method is called internally by requestStorage().
+     *
+     * \see AnalysisDataStorage
+     */
+    virtual bool requestStorageInternal(int nframes) = 0;
+
+    //! Returns the module manager to use for calling notification methods.
+    AnalysisDataModuleManager& moduleManager();
+    //! Returns the module manager to use for calling notification methods.
+    const AnalysisDataModuleManager& moduleManager() const;
+    //! \endcond
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/analysisdata.h b/src/include/gromacs/analysisdata/analysisdata.h
new file mode 100644 (file)
index 0000000..5b4d12a
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010-2018, The GROMACS development team.
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AnalysisData and gmx::AnalysisDataHandle.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_ANALYSISDATA_H
+#define GMX_ANALYSISDATA_ANALYSISDATA_H
+
+#include "gromacs/analysisdata/abstractdata.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+class AnalysisDataHandle;
+class AnalysisDataParallelOptions;
+
+/*! \brief
+ * Parallelizable data container for raw data.
+ *
+ * This is the main class used to implement parallelizable data processing in
+ * analysis tools.  It is used by first creating an object and setting its
+ * properties using setDataSetCount(), setColumnCount() and setMultipoint(),
+ * and attaching necessary modules using addModule() etc.  Then one or more
+ * AnalysisDataHandle objects can be created using startData().  Each data
+ * handle can then be independently used to provide data frames (each frame
+ * must be provided by a single handle, but different frames can be freely
+ * mixed between the handles).  The finishFrameSerial() method must be called
+ * in serial for each frame, after one of the handles has been used to provide
+ * the data for that frame.  When all data has been provided, the handles
+ * are destroyed using finishData() (or AnalysisDataHandle::finishData()).
+ *
+ * When used through the trajectory analysis framework, calls to startData(),
+ * finishFrameSerial(), and finishData() are handled by the framework.
+ *
+ * \todo
+ * Parallel implementation is not complete.
+ *
+ * \if internal
+ * Special note for MPI implementation: assuming that the initialization of
+ * data objects is identical in all processes, associating the data objects
+ * in different MPI processes should be possible without changes in the
+ * interface.
+ * Alternative, more robust implementation could get a unique ID as parameter
+ * to the constructor or a separate function, but would require all tools to
+ * provide it.  With the current registration mechanism in
+ * TrajectoryAnalysisModule, this should be straightforward.
+ * \endif
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisData : public AbstractAnalysisData
+{
+public:
+    /*! \brief
+     * Creates an empty analysis data object.
+     *
+     * \throws std::bad_alloc if out of memory.
+     */
+    AnalysisData();
+    ~AnalysisData() override;
+
+    /*! \brief
+     * Sets the number of data sets.
+     *
+     * \param[in] dataSetCount  Number of data sets (must be > 0).
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    APIError if modules have been added that are not
+     *      compatible with the new data set count.
+     *
+     * Must not be called after startData() has been called.
+     * If not called, a single data set is assumed.
+     * If called multiple times, the last call takes effect.
+     */
+    void setDataSetCount(int dataSetCount);
+    /*! \brief
+     * Sets the number of columns in a data set.
+     *
+     * \param[in] dataSet      Zero-based data set index.
+     * \param[in] columnCount  Number of columns in the data (must be > 0).
+     * \throws    APIError if modules have been added that are not
+     *      compatible with the new column count.
+     *
+     * Must be called before startData() for each data set.
+     * Must not be called after startData() has been called.
+     * If called multiple times for a data set, the last call takes effect.
+     */
+    void setColumnCount(int dataSet, int columnCount);
+    /*! \brief
+     * Sets whether the data contains multiple points per column per frame.
+     *
+     * \param[in] bMultipoint  Whether the data will allow multiple points
+     *      per column within a single frame.
+     * \throws    APIError if modules have been added that are not
+     *      compatible with the new setting.
+     *
+     * If this method is not called, the data is not multipoint.
+     *
+     * Must not be called after startData() has been called.
+     *
+     * \see isMultipoint()
+     */
+    void setMultipoint(bool bMultipoint);
+
+    int frameCount() const override;
+
+    /*! \brief
+     * Creates a handle for adding data.
+     *
+     * \param[in]  opt     Options for setting how this handle will be
+     *     used.
+     * \returns The created handle.
+     * \throws  std::bad_alloc if out of memory.
+     * \throws  APIError if any attached data module is not compatible.
+     * \throws  unspecified  Any exception thrown by attached data modules
+     *      in IAnalysisDataModule::dataStarted().
+     *
+     * The caller should retain the returned handle (or a copy of it), and
+     * pass it to finishData() after successfully adding all data.
+     * The caller should discard the returned handle if an error occurs;
+     * memory allocated for the handle will be freed when the AnalysisData
+     * object is destroyed.
+     *
+     * The \p opt options should be the same for all calls to this method,
+     * and the number of calls should match the parallelization factor
+     * defined in \p opt.
+     */
+    AnalysisDataHandle startData(const AnalysisDataParallelOptions& opt);
+    /*! \brief
+     * Performs in-order sequential processing for the next frame.
+     *
+     * \param[in]  frameIndex Index of the frame that has been finished.
+     * \throws  unspecified  Any exception thrown by attached data modules
+     *      in IAnalysisDataModule::frameFinishedSerial().
+     *
+     * This method should be called sequentially for each frame, after data
+     * for that frame has been produced.  It is not necessary to call this
+     * method if there is no parallelism, i.e., if only a single data
+     * handle is created and the parallelization options provided at that
+     * time do not indicate parallelism.
+     */
+    void finishFrameSerial(int frameIndex);
+    /*! \brief
+     * Destroys a handle after all data has been added.
+     *
+     * \param[in]  handle  Handle to destroy.
+     * \throws  unspecified  Any exception thrown by attached data modules
+     *      in IAnalysisDataModule::dataFinished().
+     *
+     * \p handle must have been obtained from startData() of this object.
+     * The order of the calls with respect to the corresponding startData()
+     * calls is not important.
+     *
+     * The \p handle (and any copies) are invalid after the call.
+     */
+    void finishData(AnalysisDataHandle handle);
+
+private:
+    AnalysisDataFrameRef tryGetDataFrameInternal(int index) const override;
+    bool                 requestStorageInternal(int nframes) override;
+
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+
+    friend class AnalysisDataHandle;
+};
+
+namespace internal
+{
+class AnalysisDataHandleImpl;
+} // namespace internal
+
+/*! \brief
+ * Handle for inserting data into AnalysisData.
+ *
+ * This class provides an interface for adding data frames into an AnalysisData
+ * object.  After a handle is obtained from AnalysisData::startData(), new
+ * frames can be added using startFrame().  Then values for that frame are set
+ * using provided methods (see below), and finishFrame() is called.  After all
+ * frames have been added, finishData() (or AnalysisData::finishData()) must be
+ * called.
+ *
+ * For simple (non-multipoint) data, within a frame values can be set using
+ * selectDataSet(), setPoint() and setPoints().  Setting the same column in the
+ * same data set multiple times overrides previously set values.
+ * When the frame is finished, attached modules are notified.
+ *
+ * Multipoint data works otherwise similarly, but requires finishPointSet() to
+ * be called for each set of points for which the modules need to be notified.
+ * Each point set starts empty (after startFrame() or finishPointSet()), and
+ * values can be set using setPoint()/setPoints().
+ * A single point set can contain values only for a single data set, which must
+ * be selected with selectDataSet() before setting any values.
+ * finishPointSet() must also be called for the last point set just before
+ * finishFrame().
+ *
+ * This class works like a pointer type: copying and assignment is lightweight,
+ * and all copies work interchangeably, accessing the same internal handle.
+ * However, normally you should only keep one copy of a handle, i.e., treat
+ * this type as movable.
+ * Several handles created from the same AnalysisData object can exist
+ * concurrently, but must currently operate on separate frames.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataHandle
+{
+public:
+    /*! \brief
+     * Constructs an invalid data handle.
+     *
+     * This constructor is provided for convenience in cases where it is
+     * easiest to declare an AnalysisDataHandle without immediately
+     * assigning a value to it.  Any attempt to call methods without first
+     * assigning a value from AnalysisData::startData() to the handle
+     * causes an assert.
+     *
+     * Does not throw.
+     */
+    AnalysisDataHandle();
+
+    //! Returns whether this data handle is valid.
+    bool isValid() const { return impl_ != nullptr; }
+
+    /*! \brief
+     * Start data for a new frame.
+     *
+     * \param[in] index  Zero-based index for the frame to start.
+     * \param[in] x      x value for the frame.
+     * \param[in] dx     Error in x for the frame if applicable.
+     *
+     * \throws    unspecified  Any exception thrown by attached data
+     *      modules in IAnalysisDataModule::frameStarted().
+     *
+     * Each \p index value 0, 1, ..., N (where N is the total number of
+     * frames) should be started exactly once by exactly one handle of an
+     * AnalysisData object.  The frames may be started out of order, but
+     * currently the implementation places some limitations on how far
+     * the index can be in the future (as counted from the first frame that
+     * is not finished).
+     */
+    void startFrame(int index, real x, real dx = 0.0);
+    /*! \brief
+     * Selects a data set for subsequent setPoint()/setPoints() calls.
+     *
+     * \param[in] index  Zero-based data set index.
+     *
+     * After startFrame(), the first data set is always selected.
+     * The set value is remembered until the end of the current frame, also
+     * across finishPointSet() calls.
+     *
+     * Does not throw.
+     */
+    void selectDataSet(int index);
+    /*! \brief
+     * Set a value for a single column for the current frame.
+     *
+     * \param[in] column  Zero-based column index.
+     * \param[in] value   Value to set for the column.
+     * \param[in] bPresent Present flag to set for the column.
+     *
+     * If called multiple times for a column (within one point set for
+     * multipoint data), old values are overwritten.
+     *
+     * Does not throw.
+     */
+    void setPoint(int column, real value, bool bPresent = true);
+    /*! \brief
+     * Set a value and its error estimate for a single column for the
+     * current frame.
+     *
+     * \param[in] column  Zero-based column index.
+     * \param[in] value   Value to set for the column.
+     * \param[in] error   Error estimate to set for the column.
+     * \param[in] bPresent Present flag to set for the column.
+     *
+     * If called multiple times for a column (within one point set for
+     * multipoint data), old values are overwritten.
+     *
+     * Does not throw.
+     */
+    void setPoint(int column, real value, real error, bool bPresent = true);
+    /*! \brief
+     * Set values for consecutive columns for the current frame.
+     *
+     * \param[in] firstColumn  Zero-based column index.
+     * \param[in] count        Number of columns to set.
+     * \param[in] values       Value array of \p column items.
+     * \param[in] bPresent     Present flag to set for the column.
+     *
+     * Equivalent to calling setPoint(firstColumn + i, values[i], bPresent) for
+     * i from 0 to count.
+     *
+     * Does not throw.
+     */
+    void setPoints(int firstColumn, int count, const real* values, bool bPresent = true);
+    /*! \brief
+     * Finish data for the current point set.
+     *
+     * \throws    APIError if any attached data module is not compatible.
+     * \throws    unspecified  Any exception thrown by attached data
+     *      modules in IAnalysisDataModule::pointsAdded().
+     *
+     * Must be called after each point set for multipoint data, including
+     * the last (i.e., no values must be set between the last call to this
+     * method and AnalysisDataStorage::finishFrame()).
+     * Must not be called for non-multipoint data.
+     */
+    void finishPointSet();
+    /*! \brief
+     * Finish data for the current frame.
+     *
+     * \throws    APIError if any attached data module is not compatible.
+     * \throws    unspecified  Any exception thrown by attached data
+     *      modules in frame notification methods.
+     */
+    void finishFrame();
+    //! Calls AnalysisData::finishData() for this handle.
+    void finishData();
+
+private:
+    /*! \brief
+     * Creates a new data handle associated with \p data.
+     *
+     * \param  impl Data to associate the handle with.
+     *
+     * The constructor is private because data handles should only be
+     * constructed through AnalysisData::startData().
+     *
+     * Does not throw.
+     */
+    explicit AnalysisDataHandle(internal::AnalysisDataHandleImpl* impl);
+
+    /*! \brief
+     * Pointer to the internal implementation class.
+     *
+     * The memory for this object is managed by the AnalysisData object,
+     * and AnalysisDataHandle simply provides a public interface for
+     * accessing the implementation.
+     */
+    internal::AnalysisDataHandleImpl* impl_;
+
+    /*! \brief
+     * Needed to access the non-public implementation.
+     */
+    friend class AnalysisData;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/arraydata.h b/src/include/gromacs/analysisdata/arraydata.h
new file mode 100644 (file)
index 0000000..71de890
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AbstractAnalysisArrayData and gmx::AnalysisArrayData.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_ARRAYDATA_H
+#define GMX_ANALYSISDATA_ARRAYDATA_H
+
+#include <vector>
+
+#include "gromacs/analysisdata/abstractdata.h"
+#include "gromacs/analysisdata/dataframe.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+/*! \brief
+ * Abstract base class for data objects that present in-memory data.
+ *
+ * This class implements a subclass of AbstractAnalysisData that presents an
+ * in-memory array through the AbstractAnalysisData interface.  Subclasses
+ * should initialize the in-memory array through the provided protected member
+ * functions.  This class provides public accessor methods for read access to
+ * the data.
+ *
+ * Public accessor methods in this class do not throw, but assert if data is
+ * accessed before it is available.
+ *
+ * \todo
+ * Add support for multiple data sets.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AbstractAnalysisArrayData : public AbstractAnalysisData
+{
+public:
+    ~AbstractAnalysisArrayData() override;
+
+    int frameCount() const override { return bReady_ ? rowCount_ : 0; }
+
+    /*! \brief
+     * Returns the number of rows in the data array.
+     *
+     * This function is identical to frameCount(), except that frameCount()
+     * returns 0 before valuesReady() has been called.
+     */
+    int rowCount() const { return rowCount_; }
+    //! Returns true if values have been allocated.
+    bool isAllocated() const { return !value_.empty(); }
+    //! Returns the x value of the first frame.
+    real xstart() const { return xvalue_[0]; }
+    //! Returns the step between frame x values.
+    real xstep() const
+    {
+        GMX_ASSERT(bUniformX_, "Accessing x step for non-uniform data");
+        return xstep_;
+    }
+    //! Returns the x value of a row.
+    real xvalue(int row) const
+    {
+        GMX_ASSERT(row >= 0 && row < rowCount(), "Row index out of range");
+        return xvalue_[row];
+    }
+    //! Returns a given array element.
+    const AnalysisDataValue& value(int row, int col) const
+    {
+        GMX_ASSERT(row >= 0 && row < rowCount(), "Row index out of range");
+        GMX_ASSERT(col >= 0 && col < columnCount(), "Column index out of range");
+        GMX_ASSERT(isAllocated(), "Data array not allocated");
+        return value_[row * columnCount() + col];
+    }
+
+protected:
+    /*! \brief
+     * Initializes an empty array data object.
+     *
+     * \throws std::bad_alloc if out of memory.
+     */
+    AbstractAnalysisArrayData();
+
+    /*! \brief
+     * Sets the number of columns in the data array.
+     *
+     * \param[in] ncols  Number of columns in the data.
+     *
+     * Cannot be called after allocateValues().
+     *
+     * See AbstractAnalysisData::setColumnCount() for exception behavior.
+     */
+    void setColumnCount(int ncols);
+    /*! \brief
+     * Sets the number of rows in the data array.
+     *
+     * \param[in] rowCount  Number of rows in the data.
+     *
+     * Cannot be called after allocateValues().
+     *
+     * Does not throw.
+     */
+    void setRowCount(int rowCount);
+    /*! \brief
+     * Allocates memory for the values.
+     *
+     * \throws std::bad_alloc if memory allocation fails.
+     *
+     * setColumnCount() and setRowCount() must have been called.
+     *
+     * Strong exception safety guarantee.
+     */
+    void allocateValues();
+    /*! \brief
+     * Sets the values reported as x values for frames.
+     *
+     * \param[in] start  x value for the first frame.
+     * \param[in] step   Step between x values of successive frames.
+     *
+     * Must not be called after valuesReady().
+     * Any values set with setXAxisValue() are overwritten.
+     *
+     * Does not throw.
+     */
+    void setXAxis(real start, real step);
+    /*! \brief
+     * Sets a single value reported as x value for frames.
+     *
+     * \param[in] row    Row/frame for which to set the value.
+     * \param[in] value  x value for the frame specified by \p row.
+     *
+     * Must not be called after valuesReady().
+     *
+     * Does not throw.
+     */
+    void setXAxisValue(int row, real value);
+    //! Returns a reference to a given array element.
+    AnalysisDataValue& value(int row, int col)
+    {
+        GMX_ASSERT(row >= 0 && row < rowCount(), "Row index out of range");
+        GMX_ASSERT(col >= 0 && col < columnCount(), "Column index out of range");
+        GMX_ASSERT(isAllocated(), "Data array not allocated");
+        return value_[row * columnCount() + col];
+    }
+    /*! \brief
+     * Notifies modules of the data.
+     *
+     * \throws    unspecified Any exception thrown by attached data modules
+     *      in data notification methods.
+     *
+     * This function should be called once the values in the array
+     * have been initialized.  The values should not be changed after this
+     * function has been called.
+     */
+    void valuesReady();
+
+    /*! \brief
+     * Copies the contents into a new object.
+     *
+     * \param[in]     src  Object to copy data from.
+     * \param[in,out] dest Empty array data object to copy data to.
+     * \throws std::bad_alloc if memory allocation for \p dest fails.
+     *
+     * \p dest should not have previous contents.
+     */
+    static void copyContents(const AbstractAnalysisArrayData* src, AbstractAnalysisArrayData* dest);
+
+private:
+    AnalysisDataFrameRef tryGetDataFrameInternal(int index) const override;
+    bool                 requestStorageInternal(int nframes) override;
+
+    int                            rowCount_;
+    AnalysisDataPointSetInfo       pointSetInfo_;
+    std::vector<AnalysisDataValue> value_;
+    std::vector<real>              xvalue_;
+    real                           xstep_;
+    bool                           bUniformX_;
+    bool                           bReady_;
+
+    // Copy and assign disallowed by base.
+};
+
+/*! \brief
+ * Simple in-memory data array.
+ *
+ * This class is a simple alternative to AnalysisData for in-memory data arrays
+ * that are constructed in-place.
+ *
+ * Public accessor methods in this class do not throw, but assert if data is
+ * accessed before it is available.
+ *
+ * \if libapi
+ * This class exposes the protected functions of AbstractAnalysisArrayData for
+ * users.
+ * \endif
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisArrayData : public AbstractAnalysisArrayData
+{
+public:
+    /*! \brief
+     * Initializes an empty array data object.
+     *
+     * \throws std::bad_alloc if out of memory.
+     */
+    AnalysisArrayData() {}
+
+    // TODO: These statements cause Doxygen to generate confusing
+    // documentation.
+    using AbstractAnalysisArrayData::allocateValues;
+    using AbstractAnalysisArrayData::setColumnCount;
+    using AbstractAnalysisArrayData::setRowCount;
+    using AbstractAnalysisArrayData::setXAxis;
+    using AbstractAnalysisArrayData::setXAxisValue;
+    using AbstractAnalysisArrayData::value;
+    using AbstractAnalysisArrayData::valuesReady;
+
+    // Copy and assign disallowed by base.
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/dataframe.h b/src/include/gromacs/analysisdata/dataframe.h
new file mode 100644 (file)
index 0000000..20db90e
--- /dev/null
@@ -0,0 +1,604 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares classes for accessing data frame information.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_DATAFRAME_H
+#define GMX_ANALYSISDATA_DATAFRAME_H
+
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/flags.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*! \brief
+ * Value type for representing a single value in analysis data objects.
+ *
+ * Default copy constructor and assignment operator are used and work as
+ * intended.
+ *
+ * Methods in this class do not throw.
+ *
+ * Non-const methods are provided for use within the library only; currently
+ * it is not possible to access a non-const AnalysisDataValue through the
+ * public interface.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataValue
+{
+public:
+    /*! \brief
+     * Constructs an unset value.
+     */
+    AnalysisDataValue() : value_(0.0), error_(0.0) {}
+    /*! \brief
+     * Constructs a value object with the given value.
+     *
+     * The constructed object is marked as set and present.
+     */
+    explicit AnalysisDataValue(real value) : value_(value), error_(0.0)
+    {
+        flags_.set(efSet);
+        flags_.set(efPresent);
+    }
+
+    /*! \brief
+     * Direct access to the value.
+     *
+     * Assigning a value to this does not mark the value as set; setValue()
+     * must be used for this.
+     */
+    real& value() { return value_; }
+    /*! \brief
+     * Direct access to the error estimate.
+     *
+     * Assigning a value to this does not mark the error estimate as set;
+     * setValue() must be used for this.
+     */
+    real& error() { return error_; }
+    //! Returns the value for this value.
+    real value() const { return value_; }
+    //! Returns the error estimate for this value, or zero if not set.
+    real error() const { return error_; }
+    /*! \brief
+     * Returns whether this value has been set.
+     *
+     * If this method returns false, the return value of value() and
+     * error() are undefined.
+     */
+    bool isSet() const { return flags_.test(efSet); }
+    /*! \brief
+     * Returns whether the error estimate for this value has been set.
+     *
+     * If this method returns false, but isSet() returns true, error()
+     * returns zero.
+     */
+    bool hasError() const { return flags_.test(efErrorSet); }
+    /*! \brief
+     * Returns whether this value has been marked as present.
+     *
+     * If this method returns false, it is up to the source data to define
+     * whether isSet() may return true.
+     */
+    bool isPresent() const { return flags_.test(efPresent); }
+
+    //! Clears and unsets this value.
+    void clear() { *this = AnalysisDataValue(); }
+    //! Sets this value.
+    void setValue(real value, bool bPresent = true)
+    {
+        value_ = value;
+        flags_.set(efSet);
+        flags_.set(efPresent, bPresent);
+    }
+    //! Sets this value and its error estimate.
+    void setValue(real value, real error, bool bPresent = true)
+    {
+        value_ = value;
+        error_ = error;
+        flags_.set(efSet);
+        flags_.set(efErrorSet);
+        flags_.set(efPresent, bPresent);
+    }
+    //! Set only error estimate for this value.
+    void setError(real error)
+    {
+        error_ = error;
+        flags_.set(efErrorSet);
+    }
+
+private:
+    //! Possible flags for \a flags_.
+    enum Flag : uint64_t
+    {
+        efSet      = 1 << 0, //!< Value has been set.
+        efErrorSet = 1 << 1, //!< Error estimate has been set.
+        efPresent  = 1 << 2  //!< Value is set as present.
+    };
+
+    //! Value for this value.
+    real value_;
+    //! Error estimate for this value, zero if not set.
+    real error_;
+    //! Status flags for thise value.
+    FlagsTemplate<Flag> flags_;
+};
+
+//! Shorthand for reference to an array of data values.
+typedef ArrayRef<const AnalysisDataValue> AnalysisDataValuesRef;
+
+
+/*! \brief
+ * Value type for storing frame-level information for analysis data.
+ *
+ * Default copy constructor and assignment operator are used and work as
+ * intended.
+ * Typically new objects of this type are only constructed internally by the
+ * library and in classes that are derived from AbstractAnalysisData.
+ *
+ * Methods in this class do not throw, but may contain asserts for incorrect
+ * usage.
+ *
+ * Note that it is not possible to change the contents of an initialized
+ * object, except by assigning a new object to replace it completely.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataFrameHeader
+{
+public:
+    /*! \brief
+     * Constructs an invalid frame header.
+     *
+     * Return values of other methods than isValid() are unspecified for
+     * the constructed object.
+     */
+    AnalysisDataFrameHeader();
+    /*! \brief
+     * Constructs a frame header from given values.
+     *
+     * \param[in] index  Index of the frame. Must be >= 0.
+     * \param[in] x      x coordinate for the frame.
+     * \param[in] dx     Error estimate for x.
+     */
+    AnalysisDataFrameHeader(int index, real x, real dx);
+
+    /*! \brief
+     * Returns whether the frame header corresponds to a valid frame.
+     *
+     * If returns false, return values of other methods are not specified.
+     */
+    bool isValid() const { return index_ >= 0; }
+    /*! \brief
+     * Returns zero-based index of the frame.
+     *
+     * The return value is >= 0 for valid frames.
+     * Should not be called for invalid frames.
+     */
+    int index() const
+    {
+        GMX_ASSERT(isValid(), "Tried to access invalid frame header");
+        return index_;
+    }
+    /*! \brief
+     * Returns the x coordinate for the frame.
+     *
+     * Should not be called for invalid frames.
+     */
+    real x() const
+    {
+        GMX_ASSERT(isValid(), "Tried to access invalid frame header");
+        return x_;
+    }
+    /*! \brief
+     * Returns error in the x coordinate for the frame (if applicable).
+     *
+     * All data do not provide error estimates.
+     * Typically returns zero in those cases.
+     *
+     * Should not be called for invalid frames.
+     */
+    real dx() const
+    {
+        GMX_ASSERT(isValid(), "Tried to access invalid frame header");
+        return dx_;
+    }
+
+private:
+    int  index_;
+    real x_;
+    real dx_;
+};
+
+
+/*! \cond libinternal */
+/*! \libinternal \brief
+ * Value type for internal indexing of point sets.
+ *
+ * This class contains the necessary data to split an array of
+ * AnalysisDataValue objects into point sets.  It is always specified in the
+ * context of an array of AnalysisDataValues: the point set specified by this
+ * class contains valueCount() values, starting from the array index
+ * valueOffset().
+ * The value at location valueOffset() corresponds to column firstColumn().
+ * It is not necessary for code using the analysis data framework to know of
+ * this class, but it is declared in a public header to allow using it in other
+ * types.
+ *
+ * Default copy constructor and assignment operator are used and work as
+ * intended.
+ * Typically new objects of this type are only constructed internally by the
+ * library and in classes that are derived from AbstractAnalysisData.
+ *
+ * Methods in this class do not throw, but may contain asserts for incorrect
+ * usage.
+ *
+ * Note that it is not possible to change the contents of an initialized
+ * object, except by assigning a new object to replace it completely.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataPointSetInfo
+{
+public:
+    //! Construct point set data object with the given values.
+    AnalysisDataPointSetInfo(int valueOffset, int valueCount, int dataSetIndex, int firstColumn) :
+        valueOffset_(valueOffset), valueCount_(valueCount), dataSetIndex_(dataSetIndex), firstColumn_(firstColumn)
+    {
+        GMX_ASSERT(valueOffset >= 0, "Negative value offsets are invalid");
+        GMX_ASSERT(valueCount >= 0, "Negative value counts are invalid");
+        GMX_ASSERT(dataSetIndex >= 0, "Negative data set indices are invalid");
+        GMX_ASSERT(firstColumn >= 0, "Negative column indices are invalid");
+    }
+
+    //! Returns the offset of the first value in the referenced value array.
+    int valueOffset() const { return valueOffset_; }
+    //! Returns the number of values in this point set.
+    int valueCount() const { return valueCount_; }
+    //! Returns the data set index for this point set.
+    int dataSetIndex() const { return dataSetIndex_; }
+    //! Returns the index of the first column in this point set.
+    int firstColumn() const { return firstColumn_; }
+
+private:
+    int valueOffset_;
+    int valueCount_;
+    int dataSetIndex_;
+    int firstColumn_;
+};
+
+//! Shorthand for reference to an array of point set data objects.
+typedef ArrayRef<const AnalysisDataPointSetInfo> AnalysisDataPointSetInfosRef;
+
+//! \endcond
+
+
+/*! \brief
+ * Value type wrapper for non-mutable access to a set of data column values.
+ *
+ * Default copy constructor and assignment operator are used and work as
+ * intended.
+ * Typically new objects of this type are only constructed internally by the
+ * library and in classes that are derived from AbstractAnalysisData.
+ *
+ * Methods in this class do not throw, but may contain asserts for incorrect
+ * usage.
+ *
+ * The design of the interfaces is such that all objects of this type should be
+ * valid, i.e., header().isValid() should always return true.
+ *
+ * Note that it is not possible to change the contents of an initialized
+ * object, except by assigning a new object to replace it completely.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataPointSetRef
+{
+public:
+    /*! \brief
+     * Constructs a point set reference from given values.
+     *
+     * \param[in] header       Header for the frame.
+     * \param[in] pointSetInfo Information about the point set.
+     * \param[in] values       Values for each column.
+     *
+     * The first element of the point set should be found from \p values
+     * using the offset in \p pointSetInfo.
+     */
+    AnalysisDataPointSetRef(const AnalysisDataFrameHeader&  header,
+                            const AnalysisDataPointSetInfo& pointSetInfo,
+                            const AnalysisDataValuesRef&    values);
+    /*! \brief
+     * Constructs a point set reference from given values.
+     *
+     * \param[in] header      Header for the frame.
+     * \param[in] values      Values for each column.
+     *
+     * The first element in \p values should correspond to the first
+     * column.
+     */
+    AnalysisDataPointSetRef(const AnalysisDataFrameHeader&        header,
+                            const std::vector<AnalysisDataValue>& values);
+    /*! \brief
+     * Constructs a point set reference to a subset of columns.
+     *
+     * \param[in] points      Point set to use as source.
+     * \param[in] firstColumn First column index to include.
+     * \param[in] columnCount Number of columns to include.
+     *
+     * Creates a point set that contains \p columnCount columns starting
+     * from \p firstColumn from \p points, or a subset if all requested
+     * columns are not present in \p points.  If the requested column range
+     * and the range in \p points do not intersect, the result has
+     * columnCount() == 0.
+     *
+     * \p firstColumn is relative to the whole data set, i.e., not relative
+     * to points.firstColumn().
+     *
+     * Mainly intended for internal use.
+     */
+    AnalysisDataPointSetRef(const AnalysisDataPointSetRef& points, int firstColumn, int columnCount);
+
+    /*! \brief
+     * Returns the frame header for the frame of this point set.
+     */
+    const AnalysisDataFrameHeader& header() const { return header_; }
+    //! \copydoc AnalysisDataFrameHeader::index()
+    int frameIndex() const { return header_.index(); }
+    //! \copydoc AnalysisDataFrameHeader::x()
+    real x() const { return header_.x(); }
+    //! \copydoc AnalysisDataFrameHeader::dx()
+    real dx() const { return header_.dx(); }
+    //! Returns zero-based index of the dataset that this set is part of.
+    int dataSetIndex() const { return dataSetIndex_; }
+    //! Returns zero-based index of the first column included in this set.
+    int firstColumn() const { return firstColumn_; }
+    //! Returns the number of columns included in this set.
+    int columnCount() const { return ssize(values()); }
+    //! Returns zero-based index of the last column included in this set (inclusive).
+    int lastColumn() const { return firstColumn_ + columnCount() - 1; }
+    /*! \brief
+     * Returns reference container for all values.
+     *
+     * First value in the returned container corresponds to firstColumn().
+     */
+    const AnalysisDataValuesRef& values() const { return values_; }
+    /*! \brief
+     * Returns data value for a column in this set.
+     *
+     * \param[in] i  Zero-based column index relative to firstColumn().
+     *     Should be >= 0 and < columnCount().
+     */
+    real y(int i) const
+    {
+        GMX_ASSERT(i >= 0 && i < columnCount(), "Out of range data access");
+        return values()[i].value();
+    }
+    /*! \brief
+     * Returns error estimate for a column in this set if applicable.
+     *
+     * \param[in] i  Zero-based column index relative to firstColumn().
+     *     Should be >= 0 and < columnCount().
+     *
+     * Currently, this method returns zero if the source data does not
+     * specify errors.
+     */
+    real dy(int i) const
+    {
+        GMX_ASSERT(i >= 0 && i < columnCount(), "Out of range data access");
+        return values()[i].error();
+    }
+    /*! \brief
+     * Returns whether a column is present in this set.
+     *
+     * \param[in] i  Zero-based column index relative to firstColumn().
+     *     Should be >= 0 and < columnCount().
+     *
+     * If present(i) returns false, it is depends on the source data
+     * whether y(i) and/or dy(i) are defined.
+     */
+    bool present(int i) const
+    {
+        GMX_ASSERT(i >= 0 && i < columnCount(), "Out of range data access");
+        return values()[i].isPresent();
+    }
+    /*! \brief
+     * Returns true if all points in this point set are present.
+     *
+     * That is, if present() would return true for all points.
+     */
+    bool allPresent() const;
+
+private:
+    AnalysisDataFrameHeader header_;
+    int                     dataSetIndex_;
+    int                     firstColumn_;
+    AnalysisDataValuesRef   values_;
+};
+
+
+/*! \brief
+ * Value type wrapper for non-mutable access to a data frame.
+ *
+ * Default copy constructor and assignment operator are used and work as
+ * intended.
+ * Typically new objects of this type are only constructed internally by the
+ * library and in classes that are derived from AbstractAnalysisData.
+ *
+ * Methods in this class do not throw, but may contain asserts for incorrect
+ * usage.
+ *
+ * Note that it is not possible to change the contents of an initialized
+ * object, except by assigning a new object to replace it completely.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataFrameRef
+{
+public:
+    /*! \brief
+     * Constructs an invalid frame reference.
+     *
+     * Return values of other methods than isValid() are unspecified for
+     * the constructed object.
+     */
+    AnalysisDataFrameRef();
+    /*! \brief
+     * Constructs a frame reference from given values.
+     *
+     * \param[in] header      Header for the frame.
+     * \param[in] values      Values for each column.
+     * \param[in] pointSets   Point set data.
+     */
+    AnalysisDataFrameRef(const AnalysisDataFrameHeader&      header,
+                         const AnalysisDataValuesRef&        values,
+                         const AnalysisDataPointSetInfosRef& pointSets);
+    /*! \brief
+     * Constructs a frame reference from given values.
+     *
+     * \param[in] header      Header for the frame.
+     * \param[in] values      Values for each column.
+     * \param[in] pointSets   Point set data.
+     */
+    AnalysisDataFrameRef(const AnalysisDataFrameHeader&               header,
+                         const std::vector<AnalysisDataValue>&        values,
+                         const std::vector<AnalysisDataPointSetInfo>& pointSets);
+    /*! \brief
+     * Constructs a frame reference to a subset of columns.
+     *
+     * \param[in] frame       Frame to use as source.
+     * \param[in] firstColumn First column index to include.
+     * \param[in] columnCount Number of columns to include.
+     *
+     * Creates a frame reference that contains \p columnCount columns
+     * starting from \p firstColumn from \p frame, or a subset if all
+     * requested columns are not present in \p frame.
+     *
+     * Mainly intended for internal use.
+     */
+    AnalysisDataFrameRef(const AnalysisDataFrameRef& frame, int firstColumn, int columnCount);
+
+    /*! \brief
+     * Returns whether the object refers to a valid frame.
+     *
+     * If returns false, return values of other methods are not specified.
+     */
+    bool isValid() const { return header().isValid(); }
+    //! Returns the header for this frame.
+    const AnalysisDataFrameHeader& header() const { return header_; }
+    //! \copydoc AnalysisDataFrameHeader::index()
+    int frameIndex() const { return header().index(); }
+    //! \copydoc AnalysisDataFrameHeader::x()
+    real x() const { return header().x(); }
+    //! \copydoc AnalysisDataFrameHeader::dx()
+    real dx() const { return header().dx(); }
+    /*! \brief
+     * Returns the number of point sets for this frame.
+     *
+     * Returns zero for an invalid frame.
+     */
+    int pointSetCount() const { return ssize(pointSets_); }
+    /*! \brief
+     * Returns point set reference for a given point set.
+     *
+     * Should not be called for invalid frames.
+     */
+    AnalysisDataPointSetRef pointSet(int index) const
+    {
+        GMX_ASSERT(isValid(), "Invalid data frame accessed");
+        GMX_ASSERT(index >= 0 && index < pointSetCount(), "Out of range data access");
+        return AnalysisDataPointSetRef(header_, pointSets_[index], values_);
+    }
+    /*! \brief
+     * Convenience method for accessing a column value in simple data.
+     *
+     * \copydetails AnalysisDataPointSetRef::y()
+     */
+    real y(int i) const { return singleColumnValue(i).value(); }
+    /*! \brief
+     * Convenience method for accessing error for a column value in simple
+     * data.
+     *
+     * \copydetails AnalysisDataPointSetRef::dy()
+     */
+    real dy(int i) const { return singleColumnValue(i).error(); }
+    /*! \brief
+     * Convenience method for accessing present status for a column in
+     * simple data.
+     *
+     * \copydetails AnalysisDataPointSetRef::present()
+     */
+    bool present(int i) const { return singleColumnValue(i).isPresent(); }
+    /*! \brief
+     * Returns true if all points in this frame are present.
+     */
+    bool allPresent() const;
+
+private:
+    //! Helper method for accessing single columns in simple data.
+    const AnalysisDataValue& singleColumnValue(int i) const
+    {
+        GMX_ASSERT(isValid(), "Invalid data frame accessed");
+        GMX_ASSERT(pointSets_.size() == 1U && pointSets_[0].firstColumn() == 0,
+                   "Convenience method not available for multiple point sets");
+        GMX_ASSERT(i >= 0 && i < ssize(values_), "Out of range data access");
+        return values_[i];
+    }
+
+    AnalysisDataFrameHeader      header_;
+    AnalysisDataValuesRef        values_;
+    AnalysisDataPointSetInfosRef pointSets_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/datamodule.h b/src/include/gromacs/analysisdata/datamodule.h
new file mode 100644 (file)
index 0000000..220be7d
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::IAnalysisDataModule and related convenience classes.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_DATAMODULE_H
+#define GMX_ANALYSISDATA_DATAMODULE_H
+
+namespace gmx
+{
+
+class AbstractAnalysisData;
+class AnalysisDataFrameHeader;
+class AnalysisDataParallelOptions;
+class AnalysisDataPointSetRef;
+
+/*! \brief
+ * Interface for a module that gets notified whenever data is added.
+ *
+ * The interface provides one method (flags()) that describes features of
+ * data objects the module supports.  Only most common features are included
+ * in the flags; custom checks can be implemented in the dataStarted() and/or
+ * parallelDataStarted() methods (see below).
+ * All other methods in the interface are callbacks that are called by the
+ * data object to which the module is attached to describe the data.
+ * See \ref module_analysisdata for an overview of the notifications the
+ * modules receive, and \ref page_analysisdata for overview of the terminology.
+ *
+ * Concrete modules typically do not directly derive from this interface, but
+ * from either AnalysisDataModuleSerial or AnalysisDataModuleParallel.
+ * Both these classes implement one of dataStarted()/parallelDataStarted() by
+ * forwarding the calls to the other method of this pair.  This allows the
+ * module to only implement the initialization once, without needing to worry
+ * how to correctly handle both cases.
+ *
+ * Currently, if the module throws an exception, it requires the analysis tool
+ * to terminate, since AbstractAnalysisData will be left in a state where it
+ * is not possible to continue processing.  See a related todo item in
+ * AbstractAnalysisData.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class IAnalysisDataModule
+{
+public:
+    /*! \brief
+     * Possible flags for flags().
+     */
+    enum Flag
+    {
+        //! The module can process multipoint data.
+        efAllowMultipoint = 1 << 0,
+        //! The module does not make sense for non-multipoint data.
+        efOnlyMultipoint = 1 << 1,
+        //! The module can process data with more than one column.
+        efAllowMulticolumn = 1 << 2,
+        //! The module can process data with missing points.
+        efAllowMissing = 1 << 3,
+        //! The module can process data with multiple data sets.
+        efAllowMultipleDataSets = 1 << 4
+    };
+
+    virtual ~IAnalysisDataModule() {}
+
+    /*! \brief
+     * Returns properties supported by the module.
+     *
+     * The return value of this method should not change after the module
+     * has been added to a data (this responsibility can, and in most cases
+     * must, be delegated to the user of the module).
+     *
+     * The purpose of this method is to remove the need for common checks
+     * for data compatibility in the classes that implement the interface.
+     * Instead, AbstractAnalysisData performs these checks based on the
+     * flags provided.
+     *
+     * Does not throw.
+     */
+    virtual int flags() const = 0;
+
+    /*! \brief
+     * Called (once) when the data has been set up properly.
+     *
+     * \param[in] data  Data object to which the module is added.
+     * \throws    APIError if the provided data is not compatible.
+     * \throws    unspecified  Can throw any exception required by the
+     *      implementing class to report errors.
+     *
+     * When the data is ready, either this method or parallelDataStarted()
+     * is called, depending on the nature of the input data.  If this
+     * method is called, the input data will always present the frames in
+     * sequential order.
+     *
+     * The data to which the module is attached is passed as an argument
+     * to provide access to properties of the data for initialization
+     * and/or validation.  The module can also call
+     * AbstractAnalysisData::requestStorage() if needed.
+     *
+     * This is the only place where the module gets access to the data;
+     * if properties of the data are required later, the module should
+     * store them internally.  It is guaranteed that the data properties
+     * (column count, whether it's multipoint) do not change once this
+     * method has been called.
+     *
+     * Notice that \p data will be a proxy object if the module is added as
+     * a column module, not the data object for which
+     * AbstractAnalysisData::addColumnModule() was called.
+     */
+    virtual void dataStarted(AbstractAnalysisData* data) = 0;
+    /*! \brief
+     * Called (once) for parallel data when the data has been set up.
+     *
+     * \param[in] data     Data object to which the module is added.
+     * \param[in] options  Parallelization properties of the input data.
+     * \returns   true if the module can process the input in
+     *      non-sequential order.
+     * \throws    APIError if the provided data is not compatible.
+     * \throws    unspecified  Can throw any exception required by the
+     *      implementing class to report errors.
+     *
+     * This method is called instead of dataStarted() if the input data has
+     * the capability to present data in non-sequential order.
+     * If the method returns true, then the module accepts this and frame
+     * notification methods may be called in that non-sequential order.
+     * If the method returns false, then the frame notification methods are
+     * called in sequential order, as if dataStarted() had been called.
+     *
+     * See dataStarted() for general information on initializing the data.
+     * That applies to this method as well, with the exception that calling
+     * AbstractAnalysisData::requestStorage() is currently not very well
+     * supported (or rather, accessing the requested storage doesn't work).
+     */
+    virtual bool parallelDataStarted(AbstractAnalysisData*              data,
+                                     const AnalysisDataParallelOptions& options) = 0;
+    /*! \brief
+     * Called at the start of each data frame.
+     *
+     * \param[in] frame  Header information for the frame that is starting.
+     * \throws    unspecified  Can throw any exception required by the
+     *      implementing class to report errors.
+     */
+    virtual void frameStarted(const AnalysisDataFrameHeader& frame) = 0;
+    /*! \brief
+     * Called one or more times during each data frame.
+     *
+     * \param[in] points  Set of points added (also provides access to
+     *      frame-level data).
+     * \throws    APIError if the provided data is not compatible.
+     * \throws    unspecified  Can throw any exception required by the
+     *      implementing class to report errors.
+     *
+     * Can be called once or multiple times for a frame.  For all data
+     * objects currently implemented in the library (and all objects that
+     * will use AnalysisDataStorage for internal implementation), it is
+     * called exactly once for each frame if the data is not multipoint,
+     * but currently this restriction is not enforced.
+     */
+    virtual void pointsAdded(const AnalysisDataPointSetRef& points) = 0;
+    /*! \brief
+     * Called when a data frame is finished.
+     *
+     * \param[in] header  Header information for the frame that is ending.
+     * \throws    unspecified  Can throw any exception required by the
+     *      implementing class to report errors.
+     */
+    virtual void frameFinished(const AnalysisDataFrameHeader& header) = 0;
+    /*! \brief
+     * Called in sequential order for each frame after they are finished.
+     *
+     * \param[in] frameIndex   Index of the next finished frame.
+     * \throws    unspecified  Can throw any exception required by the
+     *      implementing class to report errors.
+     *
+     * This method is called after frameFinished(), but with an additional
+     * constraint that it is always called in serial and with an increasing
+     * \p frameIndex.  Parallel data modules need this to serialize their
+     * data for downstream serial modules; AnalysisDataModuleSerial provides
+     * an empty implementation, as there frameFinished() can be used for
+     * the same purpose.
+     */
+    virtual void frameFinishedSerial(int frameIndex) = 0;
+    /*! \brief
+     * Called (once) when no more data is available.
+     *
+     * \throws    unspecified  Can throw any exception required by the
+     *      implementing class to report errors.
+     */
+    virtual void dataFinished() = 0;
+};
+
+/*! \brief
+ * Convenience base class for serial analysis data modules.
+ *
+ * Implements the parallelDataStarted() method such that initialization is
+ * always forwarded to dataStarted(), and the module always behaves as serial
+ * (parallelDataStarted() returns false).
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataModuleSerial : public IAnalysisDataModule
+{
+public:
+    ~AnalysisDataModuleSerial() override {}
+
+    int flags() const override = 0;
+
+    void dataStarted(AbstractAnalysisData* data) override              = 0;
+    void frameStarted(const AnalysisDataFrameHeader& frame) override   = 0;
+    void pointsAdded(const AnalysisDataPointSetRef& points) override   = 0;
+    void frameFinished(const AnalysisDataFrameHeader& header) override = 0;
+    void dataFinished() override                                       = 0;
+
+private:
+    bool parallelDataStarted(AbstractAnalysisData* data, const AnalysisDataParallelOptions& options) override;
+    void frameFinishedSerial(int /*frameIndex*/) override {}
+};
+
+/*! \brief
+ * Convenience base class for parallel analysis data modules.
+ *
+ * Implements the dataStarted() method such that initialization is always done
+ * in parallelDataStarted().  dataStarted() calls are forwarded to
+ * parallelDataStarted() using a dummy serial AnalysisDataParallelOptions.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataModuleParallel : public IAnalysisDataModule
+{
+public:
+    ~AnalysisDataModuleParallel() override {}
+
+    int flags() const override = 0;
+
+    bool parallelDataStarted(AbstractAnalysisData*              data,
+                             const AnalysisDataParallelOptions& options) override = 0;
+    void frameStarted(const AnalysisDataFrameHeader& frame) override              = 0;
+    void pointsAdded(const AnalysisDataPointSetRef& points) override              = 0;
+    void frameFinished(const AnalysisDataFrameHeader& header) override            = 0;
+    void frameFinishedSerial(int index) override                                  = 0;
+    void dataFinished() override                                                  = 0;
+
+private:
+    void dataStarted(AbstractAnalysisData* data) override;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/datamodulemanager.h b/src/include/gromacs/analysisdata/datamodulemanager.h
new file mode 100644 (file)
index 0000000..44c844b
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::AnalysisDataModuleManager.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_DATAMODULEMANAGER_H
+#define GMX_ANALYSISDATA_DATAMODULEMANAGER_H
+
+#include <memory>
+
+#include "gromacs/analysisdata/abstractdata.h"
+
+namespace gmx
+{
+
+class AnalysisDataParallelOptions;
+
+/*! \libinternal \brief
+ * Encapsulates handling of data modules attached to AbstractAnalysisData.
+ *
+ * See IAnalysisDataModule and \ref module_analysisdata for more
+ * details on the notifications and the order in which they should be raised.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataModuleManager
+{
+public:
+    /*! \brief
+     * Identifies data properties to check with data modules.
+     *
+     * \see IAnalysisDataModule::Flag
+     */
+    enum DataProperty
+    {
+        eMultipleDataSets, //!< Data has multiple data sets.
+        eMultipleColumns,  //!< Data has multiple columns.
+        eMultipoint,       //!< Data is multipoint.
+        eDataPropertyNR    //!< Number of properties; for internal use only.
+    };
+
+    AnalysisDataModuleManager();
+    ~AnalysisDataModuleManager();
+
+    /*! \brief
+     * Allows the manager to check modules for compatibility with the data.
+     *
+     * \throws  APIError if any data module already added is not compatible
+     *      with the new setting.
+     *
+     * Does two things: checks any modules already attached to the data and
+     * throws if any of them is not compatible, and stores the property
+     * to check modules attached in the future.
+     *
+     * Strong exception safety.
+     */
+    void dataPropertyAboutToChange(DataProperty property, bool bSet);
+
+    /*! \brief
+     * Whether there are modules that do not support parallel processing.
+     *
+     * Must not be called before notifyDataStart()/notifyParallelDataStart().
+     * If notifyDataStart() has been called, returns true if there are any
+     * modules (all modules are treated as serial).
+     *
+     * Does not throw.
+     */
+    bool hasSerialModules() const;
+
+    /*! \brief
+     * Adds a module to process the data.
+     *
+     * \param     data    Data object to add the module to.
+     * \param     module  Module to add.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    APIError if
+     *      - \p module is not compatible with the data object
+     *      - data has already been added to the data object and everything
+     *        is not available through getDataFrame().
+     * \throws    unspecified Any exception thrown by \p module in its
+     *      notification methods (if data has been added).
+     *
+     * \see AbstractAnalysisData::addModule()
+     */
+    void addModule(AbstractAnalysisData* data, const AnalysisDataModulePointer& module);
+    /*! \brief
+     * Applies a module to process data that is ready.
+     *
+     * \param     data    Data object to apply the module to.
+     * \param     module  Module to apply.
+     * \throws    APIError in same situations as addModule().
+     * \throws    unspecified Any exception thrown by \p module in its
+     *      notification methods.
+     *
+     * \see AbstractAnalysisData::applyModule()
+     */
+    void applyModule(AbstractAnalysisData* data, IAnalysisDataModule* module);
+
+    /*! \brief
+     * Notifies attached modules of the start of serial data.
+     *
+     * \param   data  Data object that is starting.
+     * \throws  APIError if any attached data module is not compatible.
+     * \throws  unspecified Any exception thrown by attached data modules
+     *      in IAnalysisDataModule::dataStarted().
+     *
+     * Should be called once, after data properties have been set with
+     * the methods in AbstractAnalysisData, and before any other
+     * notification methods.
+     * The caller should be prepared for requestStorage() calls to \p data
+     * from the attached modules.
+     *
+     * \p data should typically be \c this when calling from a class
+     * derived from AbstractAnalysisData.
+     *
+     * This method initializes all modules for serial processing by calling
+     * IAnalysisDataModule::dataStarted().
+     */
+    void notifyDataStart(AbstractAnalysisData* data);
+    /*! \brief
+     * Notifies attached modules of the start of parallel data.
+     *
+     * \param     data    Data object that is starting.
+     * \param[in] options Parallelization properties of the input data.
+     * \throws  APIError if any attached data module is not compatible.
+     * \throws  unspecified Any exception thrown by attached data modules
+     *      in IAnalysisDataModule::parallelDataStarted().
+     *
+     * Can be called instead of notifyDataStart() if \p data supports
+     * non-sequential creation of frames.  Works as notifyDataStart(),
+     * but instead calls IAnalysisDataModule::parallelDataStarted()
+     * and records whether the module supports the parallel mode.
+     * Subsequent notification calls then notify the modules according to
+     * the mode they accept.
+     *
+     * See notifyDataStart() for general constraints.
+     */
+    void notifyParallelDataStart(AbstractAnalysisData* data, const AnalysisDataParallelOptions& options);
+    /*! \brief
+     * Notifies attached serial modules of the start of a frame.
+     *
+     * \param[in] header  Header information for the frame that is starting.
+     * \throws    unspecified Any exception thrown by attached data modules
+     *      in IAnalysisDataModule::frameStarted().
+     *
+     * Should be called once for each frame, before notifyPointsAdd() calls
+     * for that frame.
+     */
+    void notifyFrameStart(const AnalysisDataFrameHeader& header) const;
+    /*! \brief
+     * Notifies attached parallel modules of the start of a frame.
+     *
+     * \param[in] header  Header information for the frame that is starting.
+     * \throws    unspecified Any exception thrown by attached data modules
+     *      in IAnalysisDataModule::frameStarted().
+     *
+     * If notifyParallelDataStart() has been called, should be called once
+     * for each frame, before notifyParallelPointsAdd() calls for that
+     * frame.
+     * It is allowed to call this method in any order for the frames, but
+     * should be called exactly once for each frame.
+     */
+    void notifyParallelFrameStart(const AnalysisDataFrameHeader& header) const;
+    /*! \brief
+     * Notifies attached serial modules of the addition of points to the
+     * current frame.
+     *
+     * \param[in] points  Set of points added (also provides access to
+     *      frame-level data).
+     * \throws    APIError if any attached data module is not compatible.
+     * \throws    unspecified Any exception thrown by attached data modules
+     *      in IAnalysisDataModule::pointsAdded().
+     *
+     * Can be called zero or more times for each frame.
+     * The caller should ensure that any column occurs at most once in the
+     * calls, unless the data is multipoint.
+     * For efficiency reasons, calls to this method should be aggregated
+     * whenever possible, i.e., it's better to handle multiple columns or
+     * even the whole frame in a single call rather than calling the method
+     * for each column separately.
+     */
+    void notifyPointsAdd(const AnalysisDataPointSetRef& points) const;
+    /*! \brief
+     * Notifies attached parallel modules of the addition of points to a frame.
+     *
+     * \param[in] points  Set of points added (also provides access to
+     *      frame-level data).
+     * \throws    APIError if any attached data module is not compatible.
+     * \throws    unspecified Any exception thrown by attached data modules
+     *      in IAnalysisDataModule::pointsAdded().
+     *
+     * See notifyPointsAdd() for information on the structure of the point
+     * sets.
+     */
+    void notifyParallelPointsAdd(const AnalysisDataPointSetRef& points) const;
+    /*! \brief
+     * Notifies attached serial modules of the end of a frame.
+     *
+     * \param[in] header  Header information for the frame that is ending.
+     * \throws    unspecified Any exception thrown by attached data modules
+     *      in IAnalysisDataModule::frameFinished().
+     *
+     * Should be called once for each call of notifyFrameStart(), after any
+     * notifyPointsAdd() calls for the frame.
+     * \p header should be identical to that used in the corresponding
+     * notifyFrameStart() call.
+     *
+     * This method also notifies parallel modules about serial end of frame.
+     */
+    void notifyFrameFinish(const AnalysisDataFrameHeader& header) const;
+    /*! \brief
+     * Notifies attached parallel modules of the end of a frame.
+     *
+     * \param[in] header  Header information for the frame that is ending.
+     * \throws    unspecified Any exception thrown by attached data modules
+     *      in IAnalysisDataModule::frameFinished().
+     *
+     * Should be called once for each call of notifyParallelFrameStart(),
+     * after any notifyParallelPointsAdd() calls for the frame.
+     * \p header should be identical to that used in the corresponding
+     * notifyParallelFrameStart() call.
+     */
+    void notifyParallelFrameFinish(const AnalysisDataFrameHeader& header) const;
+    /*! \brief
+     * Notifies attached modules of the end of data.
+     *
+     * \throws    unspecified Any exception thrown by attached data modules
+     *      in IAnalysisDataModule::dataFinished().
+     *
+     * Should be called once, after all the other notification calls.
+     */
+    void notifyDataFinish() const;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/dataproxy.h b/src/include/gromacs/analysisdata/dataproxy.h
new file mode 100644 (file)
index 0000000..7c672e0
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares gmx::AnalysisDataProxy.
+ *
+ * This header is only meant for internal use to implement
+ * gmx::AbstractAnalysisData::setColumnModule().
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_DATAPROXY_H
+#define GMX_ANALYSISDATA_DATAPROXY_H
+
+#include "gromacs/analysisdata/abstractdata.h"
+#include "gromacs/analysisdata/datamodule.h"
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief
+ * Internal implementation class used to implement column modules.
+ *
+ * This class serves as a proxy between AbstractAnalysisData and the attached
+ * IAnalysisDataModule object.  For each notification that
+ * AbstractAnalysisData sends, it maps it such that only the relevant columns
+ * are visible to the IAnalysisDataModule.  Similarly, it implements
+ * the frame access methods of AbstractAnalysisData such that only the relevant
+ * columns are returned.
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataProxy : public AbstractAnalysisData, public IAnalysisDataModule
+{
+public:
+    /*! \brief
+     * Creates a proxy object that only presents certain columns.
+     *
+     * \param[in] firstColumn  First column to present.
+     * \param[in] columnSpan   Number of columns to present.
+     * \param[in] data         Data object that should be wrapped.
+     *
+     * Does not throw.
+     */
+    AnalysisDataProxy(int firstColumn, int columnSpan, AbstractAnalysisData* data);
+
+    int frameCount() const override;
+
+    int flags() const override;
+
+    void dataStarted(AbstractAnalysisData* data) override;
+    bool parallelDataStarted(AbstractAnalysisData* data, const AnalysisDataParallelOptions& options) override;
+    void frameStarted(const AnalysisDataFrameHeader& frame) override;
+    void pointsAdded(const AnalysisDataPointSetRef& points) override;
+    void frameFinished(const AnalysisDataFrameHeader& header) override;
+    void frameFinishedSerial(int frameIndex) override;
+    void dataFinished() override;
+
+private:
+    AnalysisDataFrameRef tryGetDataFrameInternal(int index) const override;
+    bool                 requestStorageInternal(int nframes) override;
+
+    AbstractAnalysisData& source_;
+    int                   firstColumn_;
+    int                   columnSpan_;
+    bool                  bParallel_;
+
+    // Copy and assign disallowed by base.
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/datastorage.h b/src/include/gromacs/analysisdata/datastorage.h
new file mode 100644 (file)
index 0000000..fad12ae
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::AnalysisDataStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_DATASTORAGE_H
+#define GMX_ANALYSISDATA_DATASTORAGE_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/analysisdata/dataframe.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+class AbstractAnalysisData;
+class AnalysisDataFrameHeader;
+class AnalysisDataFrameRef;
+class AnalysisDataModuleManager;
+class AnalysisDataParallelOptions;
+
+class AnalysisDataStorage;
+
+namespace internal
+{
+class AnalysisDataStorageImpl;
+class AnalysisDataStorageFrameData;
+} // namespace internal
+
+/*! \libinternal \brief
+ * Allows assigning values for a data frame in AnalysisDataStorage.
+ *
+ * This class implements the necessary methods to add new data into the
+ * storage.  AnalysisDataStorage::startFrame() returns an object of this type,
+ * which can be used to add one or more point sets to that data frame.
+ * When all data has been added, finishFrame() needs to be called.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataStorageFrame
+{
+public:
+    /*! \brief Frees the frame object.
+     *
+     * Should not be called outside AnalysisDataStorage.
+     */
+    ~AnalysisDataStorageFrame();
+
+    /*! \brief
+     * Select data set that all other methods operate on.
+     *
+     * \param[in] index  Zero-based data set index to select.
+     *
+     * With multipoint data, a single point set can only contain values in
+     * a single data set.
+     * With non-multipoint data, arbitrary sequences of selectDataSet() and
+     * setValue() are supported.  The full frame is notified to the modules
+     * once it is finished.
+     *
+     * Does not throw.
+     */
+    void selectDataSet(int index);
+
+    //! Returns number of columns for the frame.
+    int columnCount() const { return columnCount_; }
+
+    /*! \brief
+     * Sets value for a column.
+     *
+     * \param[in] column  Zero-based column index.
+     * \param[in] value   Value to set for the column.
+     * \param[in] bPresent Present flag to set for the column.
+     *
+     * If called multiple times for a column (within one point set for
+     * multipoint data), old values are overwritten.
+     *
+     * Does not throw.
+     */
+    void setValue(int column, real value, bool bPresent = true)
+    {
+        GMX_ASSERT(column >= 0 && column < columnCount(), "Invalid column index");
+        values_[currentOffset_ + column].setValue(value, bPresent);
+        bPointSetInProgress_ = true;
+    }
+    /*! \brief
+     * Sets value for a column.
+     *
+     * \param[in] column  Zero-based column index.
+     * \param[in] value   Value to set for the column.
+     * \param[in] error   Error estimate to set for the column.
+     * \param[in] bPresent Present flag to set for the column.
+     *
+     * If called multiple times for a column (within one point set for
+     * multipoint data), old values are overwritten.
+     *
+     * Does not throw.
+     */
+    void setValue(int column, real value, real error, bool bPresent = true)
+    {
+        GMX_ASSERT(column >= 0 && column < columnCount(), "Invalid column index");
+        values_[currentOffset_ + column].setValue(value, error, bPresent);
+        bPointSetInProgress_ = true;
+    }
+    /*! \brief
+     * Access value for a column.
+     *
+     * \param[in] column  Zero-based column index.
+     *
+     * Should only be called after the column value has been set using
+     * setValue(); assigning a value to \c value(i) does not mark the
+     * column as set.
+     *
+     * Does not throw.
+     */
+    real& value(int column)
+    {
+        GMX_ASSERT(column >= 0 && column < columnCount(), "Invalid column index");
+        return values_[currentOffset_ + column].value();
+    }
+    /*! \brief
+     * Access value for a column.
+     *
+     * \param[in] column  Zero-based column index.
+     *
+     * Should only be called after the column value has been set using
+     * setValue().
+     *
+     * Does not throw.
+     */
+    real value(int column) const
+    {
+        GMX_ASSERT(column >= 0 && column < columnCount(), "Invalid column index");
+        return values_[currentOffset_ + column].value();
+    }
+    /*! \brief
+     * Mark point set as finished for multipoint data.
+     *
+     * Must be called after each point set for multipoint data, including
+     * the last (i.e., no values must be set between the last call to this
+     * method and AnalysisDataStorage::finishFrame()).
+     * Must not be called for non-multipoint data.
+     *
+     * After this method has been called, all values appear as not set.
+     *
+     * May call AnalysisDataModuleManager::notifyPointsAdd() and
+     * AnalysisDataModuleManager::notifyParallelPointsAdd(), and may throw
+     * any exception these methods throw.
+     */
+    void finishPointSet();
+    /*! \brief
+     * Finish storing a frame.
+     *
+     * Must be called exactly once for each frame returned by startFrame(),
+     * after the corresponding call.
+     * The frame object must not be accessed after the call.
+     *
+     * Calls notification methods in AnalysisDataModuleManager, and may
+     * throw any exceptions these methods throw.
+     */
+    void finishFrame();
+
+private:
+    /*! \brief
+     * Create a new storage frame.
+     *
+     * \param[in] data  Data object for which the frame is for
+     *      (used for data set and column counts).
+     */
+    explicit AnalysisDataStorageFrame(const AbstractAnalysisData& data);
+
+    //! Clear all column values from the frame.
+    void clearValues();
+
+    //! Implementation data.
+    internal::AnalysisDataStorageFrameData* data_;
+    //! Values for the currently in-progress point set.
+    std::vector<AnalysisDataValue> values_;
+
+    //! Index of the currently active dataset.
+    int currentDataSet_;
+    //! Offset of the first value in \a values_ for the current data set.
+    int currentOffset_;
+    //! Number of columns in the current data set.
+    int columnCount_;
+
+    //! Whether any values have been set in the current point set.
+    bool bPointSetInProgress_;
+
+    //! Needed for access to the constructor.
+    friend class internal::AnalysisDataStorageImpl;
+    //! Needed for managing the frame the object points to.
+    friend class internal::AnalysisDataStorageFrameData;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(AnalysisDataStorageFrame);
+};
+
+/*! \libinternal \brief
+ * Helper class that implements storage of data.
+ *
+ * This class implements a standard way of storing data to avoid implementing
+ * storage in each class derived from AbstractAnalysisData separately.
+ * To use this class in a class derived from AbstractAnalysisData, a member
+ * variable of this type should be declared and the pure virtual methods
+ * forwarded to frameCount(), tryGetDataFrame() and requestStorage().
+ * Storage properties should be set up, and then startDataStorage() or
+ * startParallelDataStorage() called.
+ * New frames can then be added using startFrame(), currentFrame(),
+ * finishFrame(), and finishFrameSerial() methods (the last is only necessary
+ * if startParallelDataStorage() is used).  When all frames are ready,
+ * finishDataStorage() must be called.  These methods (and
+ * AnalysisDataStorageFrame::finishPointSet()) take the responsibility of
+ * calling all the notification methods in AnalysisDataModuleManager,
+ *
+ * \todo
+ * Proper multi-threaded implementation.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataStorage
+{
+public:
+    //! Constructs a storage object.
+    AnalysisDataStorage();
+    ~AnalysisDataStorage();
+
+    /*! \brief
+     * Returns the number of ready frames.
+     *
+     * This method is designed such that calls to
+     * AbstractAnalysisData::frameCount() can be directly forwarded to this
+     * method.  See that method for more documentation.
+     *
+     * If this method returns N, this means that the first N frames have
+     * all been finished.
+     *
+     * \see AbstractAnalysisData::frameCount()
+     */
+    int frameCount() const;
+    /*! \brief
+     * Implements access to data frames.
+     *
+     * This method is designed such that calls to
+     * AbstractAnalysisData::tryGetDataFrameInternal() can be directly
+     * forwarded to this method.  See that method for more documentation.
+     *
+     * A valid reference for a frame will be returned after finishFrame()
+     * has been called for that frame.
+     *
+     * \see AbstractAnalysisData::tryGetDataFrameInternal()
+     */
+    AnalysisDataFrameRef tryGetDataFrame(int index) const;
+    /*! \brief
+     * Implements storage requests.
+     *
+     * This method is designed such that calls to
+     * AbstractAnalysisData::requestStorageInternal() can be directly
+     * forwarded to this method.  See that method for more documentation.
+     *
+     * \see AbstractAnalysisData::requestStorageInternal()
+     */
+    bool requestStorage(int nframes);
+
+    /*! \brief
+     * Start storing data.
+     *
+     * \param[in] data    AbstractAnalysisData object containing this
+     *      storage.
+     * \param     modules Module manager for \p data.
+     * \exception std::bad_alloc if storage allocation fails.
+     *
+     * Typically called as \c startDataStorage(this, &moduleManager())
+     * from a member of \p data when the data is ready to be started.
+     * The storage object will take responsibility of calling all
+     * module notification methods in AnalysisDataModuleManager using
+     * \p modules.
+     *
+     * Lifetime of \p data and \p modules must exceed the lifetime of the
+     * storage object
+     * (typically, the storage object will be a member in \p data).
+     *
+     * Calls AnalysisDataModuleManager::notifyDataStart(), and throws any
+     * exceptions this method throws.
+     */
+    void startDataStorage(AbstractAnalysisData* data, AnalysisDataModuleManager* modules);
+    /*! \brief
+     * Start storing data in parallel.
+     *
+     * \param[in] data    AbstractAnalysisData object containing this
+     *      storage.
+     * \param[in] options Parallelization options to use.
+     * \param     modules Module manager for \p data.
+     * \exception std::bad_alloc if storage allocation fails.
+     *
+     * Should be called instead of startDataStorage() if the data will be
+     * produced in parallel.  Works as startDataStorage(), but additionally
+     * initializes the storage and the attached modules to prepare for
+     * out-of-order data frames.
+     *
+     * Calls AnalysisDataModuleManager::notifyParallelDataStart(), and
+     * throws any exceptions this method throws.
+     */
+    void startParallelDataStorage(AbstractAnalysisData*              data,
+                                  AnalysisDataModuleManager*         modules,
+                                  const AnalysisDataParallelOptions& options);
+    /*! \brief
+     * Starts storing a new frame.
+     *
+     * \param[in] header  Header for the new frame.
+     * \retval  Frame object corresponding to the started frame.
+     * \exception std::bad_alloc if storage reallocation fails
+     *      (only possible if storage of all frames has been requested).
+     * \exception APIError if frame is too far in the future.
+     *
+     * The returned object will be valid until the corresponding
+     * finishFrame() call.
+     *
+     * Must be called exactly once for each frame index.
+     *
+     * Currently, the implementation only works if the new frame is not too
+     * far in the future:
+     * If \c i is the index of the last frame such that all frames from
+     * 0, ..., \c i have been finished, then \p header().index() should be
+     * at most \c parallelizationFactor larger than \c i, where
+     * parallelizationFactor is the parallelization factor passed to
+     * setParallelOptions().
+     * Throws APIError if this constraint is violated.
+     *
+     * Calls AnalysisDataModuleManager::notifyFrameStart() (in certain
+     * cases) and AnalysisDataModuleManager::notifyParallelFrameStart(),
+     * and throws any exceptions these methods throw.
+     */
+    AnalysisDataStorageFrame& startFrame(const AnalysisDataFrameHeader& header);
+    /*! \brief
+     * Convenience method to start storing a new frame.
+     *
+     * Identical to \c startFrame(AnalysisDataFrameHeader(index, x, dx));
+     */
+    AnalysisDataStorageFrame& startFrame(int index, real x, real dx);
+    /*! \brief
+     * Obtains a frame object for an in-progress frame.
+     *
+     * \param[in] index  Frame index.
+     * \retval  Frame object corresponding to \p index.
+     *
+     * startFrame() should have been called for the frame with index
+     * \p index, and finishFrame() should not yet have been called.
+     * Returns the same object as returned by the original startFrame()
+     * call for the same index.
+     *
+     * Does not throw.
+     */
+    AnalysisDataStorageFrame& currentFrame(int index);
+    /*! \brief
+     * Convenience method for finishing a data frame.
+     *
+     * \param[in] index  Frame index.
+     *
+     * Identical to \c currentFrame(index).finishFrame().
+     *
+     * \see AnalysisDataStorageFrame::finishFrame()
+     */
+    void finishFrame(int index);
+    /*! \brief
+     * Performs in-order sequential processing for a data frame.
+     *
+     * \param[in] index  Frame index.
+     *
+     * If startParallelDataStorage() has been called with options that
+     * indicate parallelism, this method must be called after
+     * `finishFrame(index)` (or the equivalent call in
+     * AnalysisDataStorageFrame), such that it is called in the correct
+     * order sequentially for each frame.
+     *
+     * If there is no parallelism, this method does nothing; the equivalent
+     * processing is done already during finishFrame().
+     */
+    void finishFrameSerial(int index);
+    /*! \brief
+     * Finishes storing data.
+     *
+     * Calls AnalysisDataModuleManager::notifyDataFinish(), and throws any
+     * exceptions this method throws.
+     */
+    void finishDataStorage();
+
+private:
+    typedef internal::AnalysisDataStorageImpl Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/framelocaldata.h b/src/include/gromacs/analysisdata/framelocaldata.h
new file mode 100644 (file)
index 0000000..6c5fe01
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Defines gmx::AnalysisDataFrameLocalData and supporting types.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_FRAMELOCALDATA_H
+#define GMX_ANALYSISDATA_FRAMELOCALDATA_H
+
+#include <algorithm>
+#include <numeric>
+#include <vector>
+
+#include "gromacs/analysisdata/paralleloptions.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+
+//! \addtogroup module_analysisdata
+//! \{
+
+/*! \internal
+ * \brief
+ * Handle to a single data set within frame-local data array.
+ *
+ * Methods in this class do not throw.
+ *
+ * \see AnalysisDataFrameLocalData
+ */
+template<typename ValueType>
+class AnalysisDataFrameLocalDataSetHandle
+{
+public:
+    //! Constructs a handle from an array of values.
+    explicit AnalysisDataFrameLocalDataSetHandle(ArrayRef<ValueType> values) : values_(values) {}
+
+    //! Clears all values in the data set.
+    void clear() { std::fill(values_.begin(), values_.end(), ValueType()); }
+
+    //! Accesses a single value in the data set.
+    ValueType& value(int column)
+    {
+        GMX_ASSERT(column >= 0 && column < ssize(values_), "Invalid column index");
+        return values_[column];
+    }
+
+private:
+    ArrayRef<ValueType> values_;
+};
+
+/*! \internal
+ * \brief
+ * Handle to a single frame data within frame-local data array.
+ *
+ * Methods in this class do not throw.
+ *
+ * \see AnalysisDataFrameLocalData
+ */
+template<typename ValueType>
+class AnalysisDataFrameLocalDataHandle
+{
+public:
+    //! Shorthand for the internal array of values.
+    typedef std::vector<ValueType> ValueArray;
+    //! Shorthand for a handle to a single data set.
+    typedef AnalysisDataFrameLocalDataSetHandle<ValueType> DataSetHandle;
+
+    //! Constructs a handle from specified frame data.
+    AnalysisDataFrameLocalDataHandle(const std::vector<int>* dataSetIndices, ValueArray* values) :
+        dataSetIndices_(dataSetIndices), values_(values)
+    {
+    }
+
+    //! Returns the number of data sets in the array.
+    int dataSetCount() const { return dataSetIndices_->size() - 1; }
+    //! Clears all values in the frame.
+    void clear() { std::fill(values_->begin(), values_->end(), ValueType()); }
+
+    //! Returns a handle for a single data set.
+    DataSetHandle dataSet(int dataSet)
+    {
+        GMX_ASSERT(dataSet >= 0 && dataSet < dataSetCount(), "Invalid data set index");
+        const int firstIndex = (*dataSetIndices_)[dataSet];
+        const int lastIndex  = (*dataSetIndices_)[dataSet + 1];
+        return DataSetHandle(makeArrayRef(*values_).subArray(firstIndex, lastIndex - firstIndex));
+    }
+    //! Accesses a single value in the frame.
+    ValueType& value(int dataSet, int column)
+    {
+        GMX_ASSERT(dataSet >= 0 && dataSet < dataSetCount(), "Invalid data set index");
+        const int firstIndex = (*dataSetIndices_)[dataSet];
+        GMX_ASSERT(column >= 0 && column < (*dataSetIndices_)[dataSet + 1] - firstIndex,
+                   "Invalid column index");
+        return (*values_)[firstIndex + column];
+    }
+
+private:
+    const std::vector<int>* dataSetIndices_;
+    ValueArray*             values_;
+};
+
+/*! \internal \brief
+ * Container for an array of frame-local values that supports parallel data
+ * processing.
+ *
+ * \tparam ValueType Type of values to store.
+ *
+ * This class provides a convenient interface to create an array of frame-local
+ * data for use in analysis data modules that support parallel processing.
+ * The object is initialized by setting the desired dimensionality with
+ * setDataSetCount() and setColumnCount(), followed by a call to init(),
+ * typically in IAnalysisDataModule::parallelDataStarted(),
+ *
+ * After initialization, frameData() can be used to access the data for a given
+ * frame, independently from other frames.  This works if the assumptions about
+ * parallelism hold: if `N` is the parallelization factor given for init() with
+ * AnalysisDataParallelOptions::parallelizationFactor(), then frame `i+N` must
+ * not be accessed before all processing for frame `i` is finished.
+ * Technically, the data for different frames is kept in a ring buffer of size
+ * `N`.
+ *
+ * The data for a frame is not cleared after it is reused for a new frame (but
+ * is initially cleared).  This allows using the data for accumulating values
+ * over all frames in a lock-free manner.
+ *
+ * frameDataSet() is provided for convenience when only a single data set
+ * needs to be accessed (typically in IAnalysisDataModule::pointsAdded()).
+ *
+ * Methods in this class do not throw except where indicated.
+ *
+ * \see AnalysisDataFrameLocalData
+ */
+template<typename ValueType>
+class AnalysisDataFrameLocalData
+{
+public:
+    //! Shorthand for the internal array of values for a frame.
+    typedef std::vector<ValueType> ValueArray;
+    //! Shorthand for a handle to a single frame.
+    typedef AnalysisDataFrameLocalDataHandle<ValueType> FrameHandle;
+    //! Shorthand for a handle to a single data set.
+    typedef AnalysisDataFrameLocalDataSetHandle<ValueType> DataSetHandle;
+
+    //! Constructs an empty container with a single data set.
+    AnalysisDataFrameLocalData() { dataSetColumns_.resize(2); }
+
+    //! Whether init() has been called.
+    bool isInitialized() const { return !values_.empty(); }
+    /*! \brief
+     * Returns number of independent data frames in this object.
+     *
+     * This supports looping over all the frame arrays to, e.g., sum them
+     * up at the end in accumulation scenarios.
+     */
+    int frameCount() const { return values_.size(); }
+
+    /*! \brief
+     * Sets the number of data sets stored for each frame.
+     *
+     * \throws std::bad_alloc if out of memory.
+     *
+     * If not called, there is a single data set in the object.
+     * Cannot be called after init().
+     */
+    void setDataSetCount(int dataSetCount)
+    {
+        GMX_RELEASE_ASSERT(!isInitialized(), "Cannot change value count after init()");
+        GMX_RELEASE_ASSERT(dataSetCount >= 0, "Invalid data set count");
+        dataSetColumns_.resize(dataSetCount + 1);
+    }
+    /*! \brief
+     * Sets the number of columns stored for a data set.
+     *
+     * Must be called for each data set that needs to have values,
+     * otherwise there will be zero columns for that data set.
+     * Cannot be called after init().
+     */
+    void setColumnCount(int dataSet, int columnCount)
+    {
+        GMX_RELEASE_ASSERT(!isInitialized(), "Cannot change value count after init()");
+        GMX_RELEASE_ASSERT(dataSet >= 0 && dataSet < ssize(dataSetColumns_) - 1,
+                           "Invalid data set index");
+        GMX_RELEASE_ASSERT(columnCount >= 0, "Invalid column count");
+        dataSetColumns_[dataSet + 1] = columnCount;
+    }
+
+    /*! \brief
+     * Initializes the storage to support specified parallelism.
+     *
+     * \throws std::bad_alloc if out of memory.
+     */
+    void init(const AnalysisDataParallelOptions& opt)
+    {
+        GMX_RELEASE_ASSERT(!isInitialized(), "init() called multiple times");
+        std::partial_sum(dataSetColumns_.begin(), dataSetColumns_.end(), dataSetColumns_.begin());
+        values_.resize(opt.parallelizationFactor());
+        typename std::vector<ValueArray>::iterator i;
+        for (i = values_.begin(); i != values_.end(); ++i)
+        {
+            i->resize(dataSetColumns_.back());
+        }
+    }
+
+    //! Returns a handle to access data for a frame.
+    FrameHandle frameData(int frameIndex)
+    {
+        GMX_ASSERT(frameIndex >= 0, "Invalid frame index");
+        GMX_ASSERT(isInitialized(), "Cannot access data before init()");
+        return FrameHandle(&dataSetColumns_, &values_[frameIndex % values_.size()]);
+    }
+    //! Returns a handle to access a single data set within a frame.
+    DataSetHandle frameDataSet(int frameIndex, int dataSet)
+    {
+        return frameData(frameIndex).dataSet(dataSet);
+    }
+
+private:
+    /*! \brief
+     * Index to find data sets within a per-frame array in `values_`.
+     *
+     * The first entry is always zero, followed by one entry for each data
+     * set.  Before init(), the data set entries hold the numbers set with
+     * setColumnCount().  After init(), the data set entries hold the
+     * indices of the first column for that data set in the per-frame
+     * arrays in `values_`.
+     */
+    std::vector<int> dataSetColumns_;
+    /*! \brief
+     * Data array for each frame.
+     *
+     * This is a ring buffer whose size is specified by the desired
+     * parallelism level.  For each frame, there is a single array of
+     * values, where the individual data sets are indexed with
+     * `dataSetColumns_`.
+     */
+    std::vector<ValueArray> values_;
+};
+
+//! \}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/modules/average.h b/src/include/gromacs/analysisdata/modules/average.h
new file mode 100644 (file)
index 0000000..63abd9c
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AnalysisDataAverageModule.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_MODULES_AVERAGE_H
+#define GMX_ANALYSISDATA_MODULES_AVERAGE_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/analysisdata/abstractdata.h"
+#include "gromacs/analysisdata/arraydata.h"
+#include "gromacs/analysisdata/datamodule.h"
+
+namespace gmx
+{
+
+/*! \brief
+ * Data module for independently averaging each column in input data.
+ *
+ * Computes the average and standard deviation independently for each column in
+ * the input data.  Multipoint data, multiple data sets, and missing data
+ * points are all supported.
+ * The average is always calculated over all frames and data points for a
+ * column.
+ *
+ * Output data contains a column for each data set in the input data, and a
+ * frame for each column in the input data.  If different data sets have
+ * different number of columns, the frame count accommodates the largest data
+ * set.  Other columns are padded with zero values that are additionally marked
+ * as missing.
+ * Each value in the output data is the average of the corresponding
+ * input column in the corresponding input data set.  The error value for each
+ * value provides the standard deviation of the corresponding input column.
+ * average(), standardDeviation(), and sampleCount() methods are also
+ * provided for convenient access to these properties.
+ *
+ * The output data becomes available only after the input data has been
+ * finished.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataAverageModule : public AbstractAnalysisArrayData, public AnalysisDataModuleSerial
+{
+public:
+    AnalysisDataAverageModule();
+    ~AnalysisDataAverageModule() override;
+
+    using AbstractAnalysisArrayData::setXAxis;
+    using AbstractAnalysisArrayData::setXAxisValue;
+
+    /*! \brief
+     * Sets the averaging to happen over entire data sets.
+     *
+     * If \p bDataSets is false (the default), the module averages each
+     * column separately.  The output will have a column for each data set,
+     * and a row for each column.
+     *
+     * If \p bDataSets is true, the module averages all values within
+     * a single data set into a single average/standard deviation.
+     * The output will have only one column, with one row for each data
+     * set.
+     */
+    void setAverageDataSets(bool bDataSets);
+
+    int flags() const override;
+
+    void dataStarted(AbstractAnalysisData* data) override;
+    void frameStarted(const AnalysisDataFrameHeader& header) override;
+    void pointsAdded(const AnalysisDataPointSetRef& points) override;
+    void frameFinished(const AnalysisDataFrameHeader& header) override;
+    void dataFinished() override;
+
+    /*! \brief
+     * Convenience access to the average of a data column.
+     *
+     * Note that the interpretation of the parameters follows their naming:
+     * with \c setAverageDataSets(false), \p dataSet corresponds to a
+     * column in the output, but with \c setAverageDataSets(false) it
+     * corresponds to an output row.  In both cases, it selects the data
+     * set; with \c setAverageDataSets(false), \p column should always be
+     * zero as there is only one value per data set.
+     */
+    real average(int dataSet, int column) const;
+    /*! \brief
+     * Convenience access to the standard deviation of a data column.
+     *
+     * See average() for the interpretation of the parameters.
+     */
+    real standardDeviation(int dataSet, int column) const;
+    /*! \brief
+     * Access the number of samples for a data column.
+     *
+     * See average() for the interpretation of the parameters.
+     */
+    int sampleCount(int dataSet, int column) const;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+//! Smart pointer to manage an AnalysisDataAverageModule object.
+typedef std::shared_ptr<AnalysisDataAverageModule> AnalysisDataAverageModulePointer;
+
+/*! \brief
+ * Data module for averaging of columns for each frame.
+ *
+ * Output data has the same number of frames as the input data.
+ * The number of columns in the output data is the same as the number of data
+ * sets in the input data.
+ * Each frame in the output contains the average of the column values for each
+ * data set in the corresponding frame of the input data.
+ *
+ * Multipoint data and missing data points are both supported.  The average
+ * is always calculated over all data points present in a column for a data
+ * set.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataFrameAverageModule : public AbstractAnalysisData, public AnalysisDataModuleSerial
+{
+public:
+    AnalysisDataFrameAverageModule();
+    ~AnalysisDataFrameAverageModule() override;
+
+    int frameCount() const override;
+
+    int flags() const override;
+
+    void dataStarted(AbstractAnalysisData* data) override;
+    void frameStarted(const AnalysisDataFrameHeader& header) override;
+    void pointsAdded(const AnalysisDataPointSetRef& points) override;
+    void frameFinished(const AnalysisDataFrameHeader& header) override;
+    void dataFinished() override;
+
+private:
+    AnalysisDataFrameRef tryGetDataFrameInternal(int index) const override;
+    bool                 requestStorageInternal(int nframes) override;
+
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+//! Smart pointer to manage an AnalysisDataFrameAverageModule object.
+typedef std::shared_ptr<AnalysisDataFrameAverageModule> AnalysisDataFrameAverageModulePointer;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/modules/displacement.h b/src/include/gromacs/analysisdata/modules/displacement.h
new file mode 100644 (file)
index 0000000..12916d7
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AnalysisDataDisplacementModule.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_MODULES_DISPLACEMENT_H
+#define GMX_ANALYSISDATA_MODULES_DISPLACEMENT_H
+
+#include "gromacs/analysisdata/abstractdata.h"
+#include "gromacs/analysisdata/datamodule.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+class AnalysisDataBinAverageModule;
+
+/*! \brief
+ * Data module for calculating displacements.
+ *
+ * Output data contains a frame for each frame in the input data except the
+ * first one.  For each frame, there can be multiple points, each of which
+ * describes displacement for a certain time difference ending that that frame.
+ * The first column contains the time difference (backwards from the current
+ * frame), and the remaining columns the sizes of the displacements.
+ *
+ * Current implementation is not very generic, but should be easy to extend.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataDisplacementModule : public AbstractAnalysisData, public AnalysisDataModuleSerial
+{
+public:
+    AnalysisDataDisplacementModule();
+    ~AnalysisDataDisplacementModule() override;
+
+    /*! \brief
+     * Sets the largest displacement time to be calculated.
+     */
+    void setMaxTime(real tmax);
+    /*! \brief
+     * Sets an histogram module that will receive a MSD histogram.
+     *
+     * If this function is not called, no histogram is calculated.
+     */
+    void setMSDHistogram(const std::shared_ptr<AnalysisDataBinAverageModule>& histm);
+
+    int flags() const override;
+
+    void dataStarted(AbstractAnalysisData* data) override;
+    void frameStarted(const AnalysisDataFrameHeader& header) override;
+    void pointsAdded(const AnalysisDataPointSetRef& points) override;
+    void frameFinished(const AnalysisDataFrameHeader& header) override;
+    void dataFinished() override;
+
+private:
+    AnalysisDataFrameRef tryGetDataFrameInternal(int index) const override;
+    bool                 requestStorageInternal(int nframes) override;
+
+    class Impl;
+
+    std::unique_ptr<Impl> _impl;
+};
+
+//! Smart pointer to manage an AnalysisDataDisplacementModule object.
+typedef std::shared_ptr<AnalysisDataDisplacementModule> AnalysisDataDisplacementModulePointer;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/modules/frameaverager.h b/src/include/gromacs/analysisdata/modules/frameaverager.h
new file mode 100644 (file)
index 0000000..45e4def
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares gmx::AnalysisDataFrameAverager.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_MODULES_FRAMEAVERAGER_H
+#define GMX_ANALYSISDATA_MODULES_FRAMEAVERAGER_H
+
+#include <vector>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+class AnalysisDataPointSetRef;
+
+/*! \internal
+ * \brief
+ * Helper class for modules that average values over frames.
+ *
+ * This class implements common functionality for analysis data modules that
+ * need to average a set of values over frames.  Currently, it is designed for
+ * computing averages for each input column independently, but should be
+ * relatively easy to make more general if required.
+ *
+ * This class takes care of accumulating the values and computing their
+ * variance.  It allows different number of samples for each input column.
+ * Accumulation is always in double precision and uses a formula that is
+ * relatively stable numerically.  For now, does nothing fancy,
+ * but provides ground for other implementation (e.g., related to
+ * parallelization) that would benefit all such modules.
+ *
+ * Methods in this class do not throw unless otherwise indicated.
+ *
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataFrameAverager
+{
+public:
+    AnalysisDataFrameAverager() : bFinished_(false) {}
+
+    /*! \brief
+     * Returns the number of columns in this averager.
+     */
+    int columnCount() const { return values_.size(); }
+
+    /*! \brief
+     * Sets the number of columns in the input data.
+     *
+     * \throws std::bad_alloc if out of memory.
+     *
+     * Typically called from IAnalysisDataModule::dataStarted().
+     *
+     * Must be called exactly once, before setting calling any other method
+     * in the class.
+     */
+    void setColumnCount(int columnCount);
+    /*! \brief
+     * Adds a single value to the average for a given column.
+     *
+     * \param[in] index  Index of the column to add the value to.
+     * \param[in] value  Value to add to the sample.
+     */
+    void addValue(int index, real value);
+    /*! \brief
+     * Accumulates data from a given point set into the average.
+     *
+     * Typically called from IAnalysisDataModule::pointsAdded().
+     *
+     * Each call accumulates the values for those columns that are present
+     * in the point set.  Can be called multiple times for a frame, and
+     * does not need to be called for every frame.
+     */
+    void addPoints(const AnalysisDataPointSetRef& points);
+    /*! \brief
+     * Finalizes the calculation of the averages and variances.
+     *
+     * Does any computation that is not done during the accumulation in
+     * addPoints().  Currently, does nothing, but provided as a placeholder
+     * for more complex implementation.
+     *
+     * Typically called from IAnalysisDataModule::dataFinished().
+     */
+    void finish();
+
+    /*! \brief
+     * Returns the computed average for a given column.
+     *
+     * If called before finish(), the results are undefined.
+     */
+    real average(int index) const
+    {
+        GMX_ASSERT(index >= 0 && index < columnCount(), "Invalid column index");
+        GMX_ASSERT(bFinished_, "Values available only after finished() has been called");
+        return values_[index].average;
+    }
+    /*! \brief
+     * Returns the computed (sample) variance for a given column.
+     *
+     * If called before finish(), the results are undefined.
+     */
+    real variance(int index) const
+    {
+        GMX_ASSERT(index >= 0 && index < columnCount(), "Invalid column index");
+        GMX_ASSERT(bFinished_, "Values available only after finished() has been called");
+        const AverageItem& item = values_[index];
+        return item.samples > 1 ? item.squaredSum / (item.samples - 1) : 0.0;
+    }
+    /*! \brief
+     * Returns the number of samples for a given column.
+     *
+     * If called before finish(), the results are undefined.
+     */
+    int sampleCount(int index) const
+    {
+        GMX_ASSERT(index >= 0 && index <= ssize(values_), "Invalid column index");
+        GMX_ASSERT(bFinished_, "Values available only after finished() has been called");
+        return values_[index].samples;
+    }
+
+private:
+    struct AverageItem
+    {
+        AverageItem() : average(0.0), squaredSum(0.0), samples(0) {}
+
+        //! Average of the values so far.
+        double average;
+        //! Sum of squared deviations from the average for values so far.
+        double squaredSum;
+        //! Number of values so far.
+        int samples;
+    };
+
+    std::vector<AverageItem> values_;
+    bool                     bFinished_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/modules/histogram.h b/src/include/gromacs/analysisdata/modules/histogram.h
new file mode 100644 (file)
index 0000000..f71a1e0
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares analysis data modules for calculating histograms.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_MODULES_HISTOGRAM_H
+#define GMX_ANALYSISDATA_MODULES_HISTOGRAM_H
+
+#include <memory>
+
+#include "gromacs/analysisdata/abstractdata.h"
+#include "gromacs/analysisdata/arraydata.h"
+#include "gromacs/analysisdata/datamodule.h"
+
+namespace gmx
+{
+
+class AnalysisHistogramSettings;
+
+/*! \brief
+ * Provides "named parameter" idiom for constructing histograms.
+ *
+ * \see histogramFromBins()
+ * \see histogramFromRange()
+ *
+ * Methods in this class do not throw.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisHistogramSettingsInitializer
+{
+public:
+    /*! \brief
+     * Creates an empty initializer.
+     *
+     * Should not be called directly, but histogramFromRange() or
+     * histogramFromBins() should be used instead.
+     */
+    AnalysisHistogramSettingsInitializer();
+
+    /*! \brief
+     * Sets the first bin location.
+     *
+     * Typically should not be called directly, but through
+     * histogramFromBins().
+     */
+    AnalysisHistogramSettingsInitializer& start(real min)
+    {
+        min_ = min;
+        return *this;
+    }
+    /*! \brief
+     * Sets the number of bins in the histogram.
+     *
+     * If only the first bin location is specified, this value is required
+     * (and automatically provided if histogramFromBins() is used).
+     * If both the first and last bins are specified, either this value or
+     * binWidth() is required.
+     */
+    AnalysisHistogramSettingsInitializer& binCount(int binCount)
+    {
+        binCount_ = binCount;
+        return *this;
+    }
+    /*! \brief
+     * Sets the first and last bin locations.
+     *
+     * Typically should not be called directly, but through
+     * histogramFromRange().
+     */
+    AnalysisHistogramSettingsInitializer& range(real min, real max)
+    {
+        min_ = min;
+        max_ = max;
+        return *this;
+    }
+    /*! \brief
+     * Sets the bin width of the histogram.
+     *
+     * If only the first bin location is specified, this value is required
+     * (and automatically provided if histogramFromBins() is used).
+     * If both the first and last bins are specified, either this value or
+     * binCount() is required.
+     * If a bin width is provided with both first and last bin locations,
+     * and the given bin width does not divide the range exactly, the last
+     * bin location is adjusted to match.
+     */
+    AnalysisHistogramSettingsInitializer& binWidth(real binWidth)
+    {
+        binWidth_ = binWidth;
+        return *this;
+    }
+    /*! \brief
+     * Indicate that first and last bin locations to specify bin centers.
+     *
+     * If set, the first and last bin locations are interpreted as bin
+     * centers.
+     * If not set (the default), the first and last bin locations are
+     * interpreted as the edges of the whole histogram.
+     *
+     * Cannot be specified together with roundRange().
+     */
+    AnalysisHistogramSettingsInitializer& integerBins(bool enabled = true)
+    {
+        bIntegerBins_ = enabled;
+        return *this;
+    }
+    /*! \brief
+     * Round first and last bin locations.
+     *
+     * If set, the resulting histogram will cover the range specified, but
+     * the actual bin locations will be rounded such that the edges fall
+     * on multiples of the bin width.
+     * Only implemented when both first and last bin location and bin width
+     * are defined.
+     * Cannot be specified together with integerBins() or with binCount().
+     */
+    AnalysisHistogramSettingsInitializer& roundRange(bool enabled = true)
+    {
+        bRoundRange_ = enabled;
+        return *this;
+    }
+    /*! \brief
+     * Sets the histogram to match all values.
+     *
+     * If set, the histogram behaves as if the bins at the ends extended to
+     * +-infinity.
+     */
+    AnalysisHistogramSettingsInitializer& includeAll(bool enabled = true)
+    {
+        bIncludeAll_ = enabled;
+        return *this;
+    }
+
+private:
+    real min_;
+    real max_;
+    real binWidth_;
+    int  binCount_;
+    bool bIntegerBins_;
+    bool bRoundRange_;
+    bool bIncludeAll_;
+
+    friend class AnalysisHistogramSettings;
+};
+
+/*! \brief
+ * Initializes a histogram using a range and a bin width.
+ *
+ * Does not throw.
+ *
+ * \inpublicapi
+ */
+inline AnalysisHistogramSettingsInitializer histogramFromRange(real min, real max)
+{
+    return AnalysisHistogramSettingsInitializer().range(min, max);
+}
+
+/*! \brief
+ * Initializes a histogram using bin width and the number of bins.
+ *
+ * Does not throw.
+ *
+ * \inpublicapi
+ */
+inline AnalysisHistogramSettingsInitializer histogramFromBins(real start, int nbins, real binwidth)
+{
+    return AnalysisHistogramSettingsInitializer().start(start).binCount(nbins).binWidth(binwidth);
+}
+
+
+/*! \brief
+ * Contains parameters that specify histogram bin locations.
+ *
+ * Methods in this class do not throw.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisHistogramSettings
+{
+public:
+    //! Initializes undefined parameters.
+    AnalysisHistogramSettings();
+    /*! \brief
+     * Initializes parameters based on a named parameter object.
+     *
+     * This constructor is not explicit to allow initialization of
+     * histograms directly from AnalysisHistogramSettingsInitializer:
+     * \code
+       gmx::AnalysisDataSimpleHistogramModule *hist =
+               new gmx::AnalysisDataSimpleHistogramModule(
+                       histogramFromRange(0.0, 5.0).binWidth(0.5));
+     * \endcode
+     */
+    AnalysisHistogramSettings(const AnalysisHistogramSettingsInitializer& settings);
+
+    //! Returns the left edge of the first bin.
+    real firstEdge() const { return firstEdge_; }
+    //! Returns the right edge of the first bin.
+    real lastEdge() const { return lastEdge_; }
+    //! Returns the number of bins in the histogram.
+    int binCount() const { return binCount_; }
+    //! Returns the width of a bin in the histogram.
+    real binWidth() const { return binWidth_; }
+    //! Whether values beyond the edges are mapped to the edge bins.
+    bool includeAll() const { return bAll_; }
+    //! Returns a zero-based bin index for a value, or -1 if not in range.
+    int findBin(real y) const;
+
+private:
+    real firstEdge_;
+    real lastEdge_;
+    real binWidth_;
+    real inverseBinWidth_;
+    int  binCount_;
+    bool bAll_;
+};
+
+
+class AbstractAverageHistogram;
+
+//! Smart pointer to manage an AbstractAverageHistogram object.
+typedef std::unique_ptr<AbstractAverageHistogram> AverageHistogramPointer;
+
+/*! \brief
+ * Base class for representing histograms averaged over frames.
+ *
+ * The averaging module for a per-frame histogram is always created by the
+ * histogram module class (e.g., AnalysisDataSimpleHistogramModule), and can be
+ * accessed using, e.g., AnalysisDataSimpleHistogramModule::averager().
+ * The user can alter some properties of the average histogram directly, but
+ * the main use of the object is to postprocess the histogram once the
+ * calculation is finished.
+ *
+ * This class can represent multiple histograms in one object: each column in
+ * the data is an independent histogram.
+ * The X values correspond to center of the bins, except for a cumulative
+ * histogram made with makeCumulative().
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AbstractAverageHistogram : public AbstractAnalysisArrayData
+{
+public:
+    ~AbstractAverageHistogram() override;
+
+    //! Returns bin properties for the histogram.
+    const AnalysisHistogramSettings& settings() const { return settings_; }
+
+    /*! \brief
+     * Creates a copy of the histogram with double the bin width.
+     *
+     * \param[in] bIntegerBins If `true`, the first bin in the result will
+     *     cover the first bin from the source. Otherwise, the first bin
+     *     will cover first two bins from the source.
+     * \throws std::bad_alloc if out of memory.
+     *
+     * The caller is responsible of deleting the returned object.
+     */
+    AverageHistogramPointer resampleDoubleBinWidth(bool bIntegerBins) const;
+    /*! \brief
+     * Creates a deep copy of the histogram.
+     *
+     * \throws std::bad_alloc if out of memory.
+     *
+     * The returned histogram is not necessarily of the same dynamic type
+     * as the original object, but contains the same data from the point of
+     * view of the AbstractAverageHistogram interface.
+     *
+     * The caller is responsible of deleting the returned object.
+     */
+    AverageHistogramPointer clone() const;
+    //! Normalizes the histogram such that the integral over it is one.
+    void normalizeProbability();
+    /*! \brief
+     * Makes the histograms cumulative by summing up each bin to all bins
+     * after it.
+     *
+     * The X values in the data are adjusted such that they match the right
+     * edges of bins instead of bin centers.
+     */
+    void makeCumulative();
+    //! Scales a single histogram by a uniform scaling factor.
+    void scaleSingle(int index, real factor);
+    //! Scales all histograms by a uniform scaling factor.
+    void scaleAll(real factor);
+    //! Scales the value of each bin by a different scaling factor.
+    void scaleAllByVector(const real factor[]);
+    /*! \brief
+     * Notifies attached modules of the histogram data.
+     *
+     * After this function has been called, it is no longer possible to
+     * alter the histogram.
+     */
+    void done() { AbstractAnalysisArrayData::valuesReady(); }
+
+protected:
+    /*! \brief
+     * Creates a histogram module with undefined bins.
+     *
+     * Bin parameters must be defined with init() before data input is
+     * started.
+     */
+    AbstractAverageHistogram();
+    //! Creates a histogram module with defined bin parameters.
+    explicit AbstractAverageHistogram(const AnalysisHistogramSettings& settings);
+
+    /*! \brief
+     * (Re)initializes the histogram from settings.
+     */
+    void init(const AnalysisHistogramSettings& settings);
+
+private:
+    AnalysisHistogramSettings settings_;
+
+    // Copy and assign disallowed by base.
+};
+
+
+/*! \brief
+ * Data module for per-frame histograms.
+ *
+ * Output data contains the same number of frames and data sets as the input
+ * data.  Each frame contains the histogram(s) for the points in that frame.
+ * Each input data set is processed independently into the corresponding output
+ * data set.  Missing values are ignored.
+ * All input columns for a data set are averaged into the same histogram.
+ * The number of columns for all data sets equals the number of bins in the
+ * histogram.
+ *
+ * The histograms are accumulated as 64-bit integers within a frame and summed
+ * in double precision across frames, even if the output data is in single
+ * precision.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataSimpleHistogramModule : public AbstractAnalysisData, public AnalysisDataModuleParallel
+{
+public:
+    /*! \brief
+     * Creates a histogram module with undefined bins.
+     *
+     * Bin parameters must be defined with init() before data input is
+     * started.
+     */
+    AnalysisDataSimpleHistogramModule();
+    //! Creates a histogram module with defined bin parameters.
+    explicit AnalysisDataSimpleHistogramModule(const AnalysisHistogramSettings& settings);
+    ~AnalysisDataSimpleHistogramModule() override;
+
+    /*! \brief
+     * (Re)initializes the histogram from settings.
+     */
+    void init(const AnalysisHistogramSettings& settings);
+
+    /*! \brief
+     * Returns the average histogram over all frames.
+     *
+     * Can be called already before the histogram is calculated to
+     * customize the way the average histogram is calculated.
+     *
+     * \see AbstractAverageHistogram
+     */
+    AbstractAverageHistogram& averager();
+
+    //! Returns bin properties for the histogram.
+    const AnalysisHistogramSettings& settings() const;
+
+    int frameCount() const override;
+
+    int flags() const override;
+
+    bool parallelDataStarted(AbstractAnalysisData* data, const AnalysisDataParallelOptions& options) override;
+    void frameStarted(const AnalysisDataFrameHeader& header) override;
+    void pointsAdded(const AnalysisDataPointSetRef& points) override;
+    void frameFinished(const AnalysisDataFrameHeader& header) override;
+    void frameFinishedSerial(int frameIndex) override;
+    void dataFinished() override;
+
+private:
+    AnalysisDataFrameRef tryGetDataFrameInternal(int index) const override;
+    bool                 requestStorageInternal(int nframes) override;
+
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+
+    // Copy and assign disallowed by base.
+};
+
+
+/*! \brief
+ * Data module for per-frame weighted histograms.
+ *
+ * Output data contains the same number of frames and data sets as the input
+ * data.  Each frame contains the histogram(s) for the points in that frame,
+ * interpreted such that the first column passed to pointsAdded() determines
+ * the bin and the rest give weights to be added to that bin (input data should
+ * have at least two columns, and at least two columns should be added at the
+ * same time).
+ * Each input data set is processed independently into the corresponding output
+ * data set.
+ * All input columns for a data set are averaged into the same histogram.
+ * The number of columns for all data sets equals the number of bins in the
+ * histogram.
+ *
+ * The histograms are accumulated in double precision, even if the output data
+ * is in single precision.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataWeightedHistogramModule : public AbstractAnalysisData, public AnalysisDataModuleParallel
+{
+public:
+    //! \copydoc AnalysisDataSimpleHistogramModule::AnalysisDataSimpleHistogramModule()
+    AnalysisDataWeightedHistogramModule();
+    //! \copydoc AnalysisDataSimpleHistogramModule::AnalysisDataSimpleHistogramModule(const AnalysisHistogramSettings &)
+    explicit AnalysisDataWeightedHistogramModule(const AnalysisHistogramSettings& settings);
+    ~AnalysisDataWeightedHistogramModule() override;
+
+    //! \copydoc AnalysisDataSimpleHistogramModule::init()
+    void init(const AnalysisHistogramSettings& settings);
+
+    //! \copydoc AnalysisDataSimpleHistogramModule::averager()
+    AbstractAverageHistogram& averager();
+
+    //! \copydoc AnalysisDataSimpleHistogramModule::settings()
+    const AnalysisHistogramSettings& settings() const;
+
+    int frameCount() const override;
+
+    int flags() const override;
+
+    bool parallelDataStarted(AbstractAnalysisData* data, const AnalysisDataParallelOptions& options) override;
+    void frameStarted(const AnalysisDataFrameHeader& header) override;
+    void pointsAdded(const AnalysisDataPointSetRef& points) override;
+    void frameFinished(const AnalysisDataFrameHeader& header) override;
+    void frameFinishedSerial(int frameIndex) override;
+    void dataFinished() override;
+
+private:
+    AnalysisDataFrameRef tryGetDataFrameInternal(int index) const override;
+    bool                 requestStorageInternal(int nframes) override;
+
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+
+    // Copy and assign disallowed by base.
+};
+
+
+/*! \brief
+ * Data module for bin averages.
+ *
+ * Output data contains one row for each bin; see AbstractAverageHistogram.
+ * Output data contains one column for each input data set.
+ * The value in a column is the average over all frames of that data set for
+ * that bin.
+ * The input data is interpreted such that the first column passed to
+ * pointsAdded() determines the bin and the rest give values to be added to
+ * that bin (input data should have at least two columns, and at least two
+ * columns should be added at the same time).
+ * All input columns for a data set are averaged into the same histogram.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataBinAverageModule : public AbstractAnalysisArrayData, public AnalysisDataModuleSerial
+{
+public:
+    //! \copydoc AnalysisDataSimpleHistogramModule::AnalysisDataSimpleHistogramModule()
+    AnalysisDataBinAverageModule();
+    //! \copydoc AnalysisDataSimpleHistogramModule::AnalysisDataSimpleHistogramModule(const AnalysisHistogramSettings &)
+    explicit AnalysisDataBinAverageModule(const AnalysisHistogramSettings& settings);
+    ~AnalysisDataBinAverageModule() override;
+
+    //! \copydoc AnalysisDataSimpleHistogramModule::init()
+    void init(const AnalysisHistogramSettings& settings);
+
+    //! \copydoc AnalysisDataSimpleHistogramModule::settings()
+    const AnalysisHistogramSettings& settings() const;
+
+    int flags() const override;
+
+    void dataStarted(AbstractAnalysisData* data) override;
+    void frameStarted(const AnalysisDataFrameHeader& header) override;
+    void pointsAdded(const AnalysisDataPointSetRef& points) override;
+    void frameFinished(const AnalysisDataFrameHeader& header) override;
+    void dataFinished() override;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+
+    // Copy and assign disallowed by base.
+};
+
+//! Smart pointer to manage an AnalysisDataSimpleHistogramModule object.
+typedef std::shared_ptr<AnalysisDataSimpleHistogramModule> AnalysisDataSimpleHistogramModulePointer;
+//! Smart pointer to manage an AnalysisDataWeightedHistogramModule object.
+typedef std::shared_ptr<AnalysisDataWeightedHistogramModule> AnalysisDataWeightedHistogramModulePointer;
+//! Smart pointer to manage an AnalysisDataBinAverageModule object.
+typedef std::shared_ptr<AnalysisDataBinAverageModule> AnalysisDataBinAverageModulePointer;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/modules/lifetime.h b/src/include/gromacs/analysisdata/modules/lifetime.h
new file mode 100644 (file)
index 0000000..d77eb48
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AnalysisDataLifetimeModule.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_MODULES_LIFETIME_H
+#define GMX_ANALYSISDATA_MODULES_LIFETIME_H
+
+#include <memory>
+
+#include "gromacs/analysisdata/arraydata.h"
+#include "gromacs/analysisdata/datamodule.h"
+
+namespace gmx
+{
+
+/*! \brief
+ * Data module for computing lifetime histograms for columns in input data.
+ *
+ * The input data set is treated as a boolean array: each value that is present
+ * (AnalysisDataValue::isPresent() returns true) and is >0 is treated as
+ * present, other values are treated as absent.
+ * For each input data set, analyzes the columns to identify the intervals
+ * where a column is continuously present.
+ * Produces a histogram from the lengths of these intervals.
+ * Input data should have frames with evenly spaced x values.
+ *
+ * Output data contains one column for each data set in the input data.
+ * This column gives the lifetime histogram for the corresponding data set.
+ * x axis in the output is spaced the same as in the input data, and extends
+ * as long as required to cover all the histograms.
+ * Histograms are padded with zeros as required to be of the same length.
+ * setCumulative() can be used to alter the handling of subintervals in the
+ * output histogram.
+ *
+ * The output data becomes available only after the input data has been
+ * finished.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataLifetimeModule : public AbstractAnalysisArrayData, public AnalysisDataModuleSerial
+{
+public:
+    AnalysisDataLifetimeModule();
+    ~AnalysisDataLifetimeModule() override;
+
+    /*! \brief
+     * Sets a cumulative histogram mode.
+     *
+     * \param[in] bCumulative If true, all subintervals of a long
+     *   interval are also explicitly added into the histogram.
+     *
+     * Does not throw.
+     */
+    void setCumulative(bool bCumulative);
+
+    int flags() const override;
+
+    void dataStarted(AbstractAnalysisData* data) override;
+    void frameStarted(const AnalysisDataFrameHeader& header) override;
+    void pointsAdded(const AnalysisDataPointSetRef& points) override;
+    void frameFinished(const AnalysisDataFrameHeader& header) override;
+    void dataFinished() override;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+//! Smart pointer to manage an AnalysisDataLifetimeModule object.
+typedef std::shared_ptr<AnalysisDataLifetimeModule> AnalysisDataLifetimeModulePointer;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/modules/plot.h b/src/include/gromacs/analysisdata/modules/plot.h
new file mode 100644 (file)
index 0000000..371fb8f
--- /dev/null
@@ -0,0 +1,332 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::AnalysisDataPlotModule for plotting data (into a file).
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ */
+#ifndef GMX_ANALYSISDATA_MODULES_PLOT_H
+#define GMX_ANALYSISDATA_MODULES_PLOT_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/analysisdata/datamodule.h"
+#include "gromacs/options/timeunitmanager.h"
+
+enum class XvgFormat : int;
+
+namespace gmx
+{
+
+class AnalysisDataValue;
+class IOptionsContainer;
+class SelectionCollection;
+
+/*! \brief
+ * Common settings for data plots.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataPlotSettings
+{
+public:
+    //! Constructs default analysis plot settings.
+    AnalysisDataPlotSettings();
+
+    //! Returns the selection collection set with setSelectionCollection().
+    const SelectionCollection* selectionCollection() const { return selections_; }
+    //! Returns the time unit set with setTimeUnit().
+    TimeUnit timeUnit() const { return timeUnit_; }
+    /*! \brief
+     * Returns the plot format.
+     */
+    XvgFormat plotFormat() const { return plotFormat_; }
+
+    /*! \brief
+     * Set selection collection to print as comments into the output.
+     *
+     * Formatted selection text from all selections in \p selections is
+     * printed as comments in the output file.
+     * If this method is not called, no selection information is written
+     * to the output.
+     */
+    void setSelectionCollection(const SelectionCollection* selections);
+    /*! \brief
+     * Sets the time unit for the plot.
+     *
+     * The value is used only if AbstractPlotModule::setXAxisIsTime() is
+     * called, in which case it is used to print the appropriate axis label
+     * and to scale the values.
+     * If not called, the default time unit is ps.
+     */
+    void setTimeUnit(TimeUnit timeUnit) { timeUnit_ = timeUnit; }
+
+
+    /*! \brief
+     * Adds common options for setting plot options.
+     *
+     * \param[in,out] options Options object to which options are added.
+     */
+    void initOptions(IOptionsContainer* options);
+
+private:
+    const SelectionCollection* selections_;
+    TimeUnit                   timeUnit_;
+    XvgFormat                  plotFormat_;
+};
+
+/*! \brief
+ * Abstract data module for writing data into a file.
+ *
+ * Implements features common to all plotting modules.  Subclasses implement
+ * features specific to certain applications (AnalysisDataPlotModule implements
+ * straightforward plotting).
+ *
+ * By default, the data is written into an xvgr file, according to the
+ * options read from the AnalysisDataPlotSettings object given to the
+ * constructor.
+ * For non-xvgr data, it's possible to skip all headers by calling
+ * setPlainOutput().
+ *
+ * A single output line corresponds to a single frame.  In most cases with
+ * multipoint data, setPlainOutput() should be called since the output does not
+ * make sense as an xvgr file, but this is not enforced.
+ *
+ * Multipoint data and multiple data sets are both supported, in which case all
+ * the points are written to the output, in the order in which they are added
+ * to the data.
+ *
+ * \ingroup module_analysisdata
+ */
+class AbstractPlotModule : public AnalysisDataModuleSerial
+{
+public:
+    ~AbstractPlotModule() override;
+
+    /*! \brief
+     * Set common settings for the plotting.
+     */
+    void setSettings(const AnalysisDataPlotSettings& settings);
+    /*! \brief
+     * Set the output file name.
+     *
+     * If no file name is set (or if \p filename is empty), no output occurs.
+     */
+    void setFileName(const std::string& filename);
+    /*! \brief
+     * Set plain output.
+     *
+     * If \p bPlain is true, no xvgr headers are written to the file.
+     * In this case, only setOmitX(), setXFormat(), and setYFormat()
+     * methods have any effect on the output.
+     */
+    void setPlainOutput(bool bPlain);
+    /*! \brief
+     * Plot errors as a separate output column after each value column.
+     */
+    void setErrorsAsSeparateColumn(bool bSeparate);
+    /*! \brief
+     * Omit the X coordinates from the output.
+     *
+     * This method only makes sense when combined with setPlainOutput().
+     */
+    void setOmitX(bool bOmitX);
+    /*! \brief
+     * Set plot title.
+     */
+    void setTitle(const char* title);
+    //! \copydoc setTitle(const char *)
+    void setTitle(const std::string& title);
+    /*! \brief
+     * Set plot subtitle.
+     */
+    void setSubtitle(const char* subtitle);
+    //! \copydoc setSubtitle(const char *)
+    void setSubtitle(const std::string& subtitle);
+    /*! \brief
+     * Set X axis label.
+     */
+    void setXLabel(const char* label);
+    /*! \brief
+     * Treat X axis as time.
+     *
+     * Sets the label for the axis accordingly and also scales output to
+     * take into account the correct time unit.
+     */
+    void setXAxisIsTime();
+    /*! \brief
+     * Set Y axis label.
+     */
+    void setYLabel(const char* label);
+    /*! \brief
+     * Add legend from an array of strings.
+     *
+     * Multiple calls to setLegend() and/or appendLegend() are added
+     * together.
+     */
+    void setLegend(int nsets, const char* const* setname);
+    /*! \brief
+     * Add a legend string for the next data set.
+     *
+     * Multiple calls to setLegend() and/or appendLegend() are added
+     * together.
+     */
+    void appendLegend(const char* setname);
+    //! \copydoc appendLegend(const char *)
+    void appendLegend(const std::string& setname);
+    /*! \brief
+     * Set field width and precision for X value output.
+     */
+    void setXFormat(int width, int precision, char format = 'f');
+    /*! \brief
+     * Set field width and precision for Y value output.
+     */
+    void setYFormat(int width, int precision, char format = 'f');
+
+    int flags() const override;
+
+    void dataStarted(AbstractAnalysisData* data) override;
+    void frameStarted(const AnalysisDataFrameHeader& header) override;
+    void pointsAdded(const AnalysisDataPointSetRef& points) override = 0;
+    void frameFinished(const AnalysisDataFrameHeader& header) override;
+    void dataFinished() override;
+
+protected:
+    /*! \cond libapi */
+    AbstractPlotModule();
+    //! Creates AbstractPlotModule and assign common settings.
+    explicit AbstractPlotModule(const AnalysisDataPlotSettings& settings);
+
+    //! Whether an output file has been opened.
+    bool isFileOpen() const;
+    /*! \brief
+     * Appends a single value to the current output line.
+     *
+     * \param[in] value  Value to append.
+     *
+     * Should be used from pointsAdded() implementations in derived classes
+     * to write out individual y values to the output.
+     *
+     * Must not be called if isFileOpen() returns false.
+     */
+    void writeValue(const AnalysisDataValue& value) const;
+    //! \endcond
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+
+/*! \brief
+ * Plotting module for straightforward plotting of data.
+ *
+ * See AbstractPlotModule for common plotting options.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataPlotModule : public AbstractPlotModule
+{
+public:
+    AnalysisDataPlotModule();
+    //! Creates AnalysisDataPlotModule and assign common settings.
+    explicit AnalysisDataPlotModule(const AnalysisDataPlotSettings& settings);
+
+    void pointsAdded(const AnalysisDataPointSetRef& points) override;
+
+    // Copy and assign disallowed by base.
+};
+
+
+/*! \brief
+ * Plotting module specifically for data consisting of vectors.
+ *
+ * See AbstractPlotModule for common plotting options.
+ *
+ * \inpublicapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataVectorPlotModule : public AbstractPlotModule
+{
+public:
+    AnalysisDataVectorPlotModule();
+    //! Creates AnalysisDataVectorPlotModule and assign common settings.
+    explicit AnalysisDataVectorPlotModule(const AnalysisDataPlotSettings& settings);
+
+    /*! \brief
+     * Set whether to write X component.
+     */
+    void setWriteX(bool bWrite);
+    /*! \brief
+     * Set whether to write Y component.
+     */
+    void setWriteY(bool bWrite);
+    /*! \brief
+     * Set whether to write Z component.
+     */
+    void setWriteZ(bool bWrite);
+    /*! \brief
+     * Set whether to write norm of the vector.
+     */
+    void setWriteNorm(bool bWrite);
+    /*! \brief
+     * Set mask for what to write.
+     */
+    void setWriteMask(const bool bWrite[4]);
+
+    void pointsAdded(const AnalysisDataPointSetRef& points) override;
+
+private:
+    bool bWrite_[4];
+
+    // Copy and assign disallowed by base.
+};
+
+//! Smart pointer to manage an AnalysisDataPlotModule object.
+typedef std::shared_ptr<AnalysisDataPlotModule> AnalysisDataPlotModulePointer;
+//! Smart pointer to manage an AnalysisDataVectorPlotModule object.
+typedef std::shared_ptr<AnalysisDataVectorPlotModule> AnalysisDataVectorPlotModulePointer;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/paralleloptions.h b/src/include/gromacs/analysisdata/paralleloptions.h
new file mode 100644 (file)
index 0000000..83db181
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::AnalysisDataParallelOptions.
+ *
+ * \if internal
+ * Implementation of this class is currently in datastorage.cpp.
+ * \endif
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_PARALLELOPTIONS_H
+#define GMX_ANALYSISDATA_PARALLELOPTIONS_H
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Parallelization options for analysis data objects.
+ *
+ * Methods in this class do not throw.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataParallelOptions
+{
+public:
+    //! Constructs options for serial execution.
+    AnalysisDataParallelOptions();
+    /*! \brief
+     * Constructs options for parallel execution with given number of
+     * concurrent frames.
+     *
+     * \param[in] parallelizationFactor
+     *      Number of frames that may be constructed concurrently.
+     *      Must be >= 1.
+     */
+    explicit AnalysisDataParallelOptions(int parallelizationFactor);
+
+    //! Returns the number of frames that may be constructed concurrently.
+    int parallelizationFactor() const { return parallelizationFactor_; }
+
+private:
+    int parallelizationFactor_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/tests/datatest.h b/src/include/gromacs/analysisdata/tests/datatest.h
new file mode 100644 (file)
index 0000000..1e1766b
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Helper classes for testing classes that derive from AbstractAnalysisData.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_TESTS_DATATEST_H
+#define GMX_ANALYSISDATA_TESTS_DATATEST_H
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/analysisdata/dataframe.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+#include "testutils/refdata.h"
+
+// currently the bug manifests itself only in AbstractAnalysisData testing
+#if defined __PATHSCALE__
+#    define STATIC_ANON_NAMESPACE_BUG // see #1558 for details
+#endif
+
+namespace gmx
+{
+
+class AbstractAnalysisData;
+class AnalysisData;
+class AnalysisDataHandle;
+
+namespace test
+{
+
+class FloatingPointTolerance;
+
+/*! \libinternal \brief
+ * Represents a single set of points in AnalysisDataTestInputFrame structure.
+ *
+ * If the data is not multipoint, each frame contains exactly one set of
+ * points.  If there is more than one set of points, each of these sets is
+ * passed separately and AnalysisDataHandle::finishPointSet() called in
+ * between.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataTestInputPointSet
+{
+public:
+    //! Returns zero-based index of this point set in its frame.
+    int index() const { return index_; }
+    //! Returns zero-based index of the data set of this point set.
+    int dataSetIndex() const { return dataSetIndex_; }
+    //! Returns zero-based index of the first column in this point set.
+    int firstColumn() const { return firstColumn_; }
+    //! Returns zero-based index of the last column in this point set.
+    int lastColumn() const { return firstColumn_ + size() - 1; }
+    //! Returns the number of columns in the point set.
+    int size() const { return values_.size(); }
+    //! Returns the value in column \p i.
+    real y(int i) const { return values_[i].y; }
+    //! Returns whether the error is present for column \p i.
+    bool hasError(int i) const { return values_[i].bError; }
+    //! Returns the error in column \p i.
+    real error(int i) const { return values_[i].error; }
+    //! Returns whether the value in column \p i is present.
+    static bool present(int /*i*/) { return true; }
+    //! Returns an AnalysisDataValue for column \p i.
+    AnalysisDataValue value(int i) const
+    {
+        AnalysisDataValue result;
+        result.setValue(values_[i].y);
+        if (values_[i].bError)
+        {
+            result.setError(values_[i].error);
+        }
+        return result;
+    }
+
+    //! Appends a value to this point set.
+    void addValue(real y) { values_.emplace_back(y); }
+    //! Appends a value with an error estimate to this point set.
+    void addValueWithError(real y, real error) { values_.emplace_back(y, error); }
+
+private:
+    //! Creates an empty point set.
+    AnalysisDataTestInputPointSet(int index, int dataSetIndex, int firstColumn);
+
+    struct Value
+    {
+        Value() : y(0.0), error(0.0), bError(false) {}
+        explicit Value(real y) : y(y), error(0.0), bError(false) {}
+        Value(real y, real error) : y(y), error(error), bError(true) {}
+
+        real y;
+        real error;
+        bool bError;
+    };
+
+    int                index_;
+    int                dataSetIndex_;
+    int                firstColumn_;
+    std::vector<Value> values_;
+
+    //! For constructing new point sets.
+    friend class AnalysisDataTestInputFrame;
+};
+
+/*! \libinternal \brief
+ * Represents a single frame in AnalysisDataTestInput structure.
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataTestInputFrame
+{
+public:
+    //! Returns zero-based index for the frame.
+    int index() const { return index_; }
+    //! Returns x coordinate for the frame.
+    real x() const { return x_; }
+    //! Returns error in the x coordinate for the frame.
+    static real dx() { return 0.0; }
+
+    //! Number of individual point sets in the frame.
+    int pointSetCount() const { return pointSets_.size(); }
+    //! Returns a point set object for a given point set.
+    const AnalysisDataTestInputPointSet& pointSet(int index) const
+    {
+        GMX_ASSERT(index >= 0 && static_cast<size_t>(index) < pointSets_.size(),
+                   "Point set index out of range");
+        return pointSets_[index];
+    }
+
+    //! Appends an empty point set to this frame.
+    AnalysisDataTestInputPointSet& addPointSet(int dataSet, int firstColumn);
+    //! Adds a point set with given values to this frame.
+    void addPointSetWithValues(int dataSet, int firstColumn, real y1);
+    //! Adds a point set with given values to this frame.
+    void addPointSetWithValues(int dataSet, int firstColumn, real y1, real y2);
+    //! Adds a point set with given values to this frame.
+    void addPointSetWithValues(int dataSet, int firstColumn, real y1, real y2, real y3);
+    //! Adds a point set with given values to this frame.
+    void addPointSetWithValueAndError(int dataSet, int firstColumn, real y1, real e1);
+
+private:
+    //! Constructs a new frame object with the given values.
+    AnalysisDataTestInputFrame(int index, real x);
+
+    int                                        index_;
+    real                                       x_;
+    std::vector<AnalysisDataTestInputPointSet> pointSets_;
+
+    //! For constructing new frames.
+    friend class AnalysisDataTestInput;
+};
+
+/*! \libinternal \brief
+ * Represents static input data for AbstractAnalysisData tests.
+ *
+ * Used to construct structured test input data for analysis data unit tests.
+ * Typically used as input to methods in AnalysisDataTestFixture.
+ *
+ * \see AnalysisDataTestFixture
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataTestInput
+{
+public:
+    /*! \brief
+     * Constructs empty input data.
+     *
+     * \param[in] dataSetCount Number of data sets in the data.
+     * \param[in] bMultipoint  Whether the data will be multipoint.
+     *
+     * The column count for each data set must be set with
+     * setColumnCount().
+     */
+    AnalysisDataTestInput(int dataSetCount, bool bMultipoint);
+    ~AnalysisDataTestInput();
+
+    //! Whether the input data is multipoint.
+    bool isMultipoint() const { return bMultipoint_; }
+    //! Returns the number of data sets in the input data.
+    int dataSetCount() const { return columnCounts_.size(); }
+    //! Returns the number of columns in a given data set.
+    int columnCount(int dataSet) const { return columnCounts_[dataSet]; }
+    //! Returns the number of frames in the input data.
+    int frameCount() const { return frames_.size(); }
+    //! Returns a frame object for the given input frame.
+    const AnalysisDataTestInputFrame& frame(int index) const;
+
+    //! Sets the number of columns in a data set.
+    void setColumnCount(int dataSet, int columnCount);
+    //! Appends an empty frame to this data.
+    AnalysisDataTestInputFrame& addFrame(real x);
+    //! Adds a frame with a single point set and the given values.
+    void addFrameWithValues(real x, real y1);
+    //! Adds a frame with a single point set and the given values.
+    void addFrameWithValues(real x, real y1, real y2);
+    //! Adds a frame with a single point set and the given values.
+    void addFrameWithValues(real x, real y1, real y2, real y3);
+    //! Adds a frame with a single point set and the given values.
+    void addFrameWithValueAndError(real x, real y1, real e1);
+
+private:
+    std::vector<int>                        columnCounts_;
+    bool                                    bMultipoint_;
+    std::vector<AnalysisDataTestInputFrame> frames_;
+};
+
+/*! \libinternal \brief
+ * Test fixture for AbstractAnalysisData testing.
+ *
+ * This test fixture is designed to help writing tests for objects that
+ * derive from the AbstractAnalysisData class.  Typical flow in such tests is
+ * that first the test creates the required data objects, then call static
+ * methods in this class to add mock modules (using
+ * AbstractAnalysisData::addModule()) to the data objects to check that they
+ * produce the correct data, and then invokes methods in the data object to
+ * produce the data to be checked.  Static methods are also provided for
+ * pushing data from an AnalysisDataTestInput object to some generic types
+ * derived from AbstractAnalysisData.
+ *
+ * Methods addStaticCheckerModule(), addStaticColumnCheckerModule() and
+ * addStaticStorageCheckerModule() create and add mock modules that check the
+ * data against a given AnalysisDataTestInput instance.
+ *
+ * Method addReferenceCheckerModule() creates and adds a mock module that
+ * checks the output against reference data produced by a previous test
+ * execution (see TestReferenceData).  Two versions are provided, a static
+ * method to be used with any TestReferenceChecker, and a non-static method
+ * that uses the protected \p data_ member.
+ *
+ * presentAllData() and presentDataFrame() are provided to push data from an
+ * AnalysisDataTestInput into an AnalysisData object.  In typical tests, most
+ * checks are done during these methods, by the added mock modules.
+ * setupArrayData() performs the same function for classes derived from
+ * AbstractAnalysisArrayData.  In that case, the test should separately ensure
+ * that AbstractAnalysisArrayData::valuesReady() gets called.
+ *
+ * \todo
+ * Support for arbitrary AnalysisDataValues (errors and missing values).
+ *
+ * \see AnalysisDataTestInput
+ *
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+class AnalysisDataTestFixture : public ::testing::Test
+{
+public:
+    AnalysisDataTestFixture();
+
+    /*! \brief
+     * Initializes an AnalysisData object from input data.
+     *
+     * Sets the column count and other properties based on the input data.
+     */
+    static void setupDataObject(const AnalysisDataTestInput& input, AnalysisData* data);
+
+    /*! \brief
+     * Adds all data from AnalysisDataTestInput into an AnalysisData.
+     */
+    static void presentAllData(const AnalysisDataTestInput& input, AnalysisData* data);
+    /*! \brief
+     * Adds a single frame from AnalysisDataTestInput into an AnalysisData.
+     */
+    static void presentDataFrame(const AnalysisDataTestInput& input, int row, AnalysisDataHandle handle);
+    /*! \brief
+     * Initializes an array data object from AnalysisDataTestInput.
+     *
+     * \tparam ArrayData  Class derived from AbstractAnalysisArrayData.
+     *
+     * The ArrayData class should expose the setter methods
+     * (setColumnCount(), setRowCount(), allocateValues(), setValue())
+     * publicly or declare the fixture class as a friend.
+     * The X axis in \p data must be configured to match \p input before
+     * calling this method.
+     *
+     * Does not call AbstractAnalysisArrayData::valuesReady().
+     * The test must ensure that this method gets called, otherwise the
+     * mock modules never get called.
+     */
+    template<class ArrayData>
+    static void setupArrayData(const AnalysisDataTestInput& input, ArrayData* data);
+
+    /*! \brief
+     * Adds a mock module that verifies output against
+     * AnalysisDataTestInput.
+     *
+     * \param[in]  data     Data to compare against.
+     * \param      source   Data object to verify.
+     *
+     * Creates a mock module that verifies that the
+     * IAnalysisDataModule methods are called correctly by
+     * \p source.  Parameters for the calls are verified against \p data.
+     * Adds the created module to \p source using \p data->addModule().
+     * Any exceptions from the called functions should be caught by the
+     * caller.
+     *
+     * \see AbstractAnalysisData::addModule()
+     */
+    static void addStaticCheckerModule(const AnalysisDataTestInput& data, AbstractAnalysisData* source);
+    /*! \brief
+     * Adds a mock module that verifies parallel output against
+     * AnalysisDataTestInput.
+     *
+     * \param[in]  data     Data to compare against.
+     * \param      source   Data object to verify.
+     *
+     * Creates a parallel mock module that verifies that the
+     * IAnalysisDataModule methods are called correctly by
+     * \p source.  Parameters for the calls are verified against \p data.
+     * Adds the created module to \p source using \p data->addModule().
+     * Any exceptions from the called functions should be caught by the
+     * caller.
+     *
+     * Differs from addStaticCheckerModule() in that the created mock
+     * module reports that it accepts parallel input data, and accepts and
+     * verifies notification calls following the parallel pattern.
+     *
+     * \see AbstractAnalysisData::addModule()
+     */
+    static void addStaticParallelCheckerModule(const AnalysisDataTestInput& data,
+                                               AbstractAnalysisData*        source);
+    /*! \brief
+     * Adds a column mock module that verifies output against
+     * AnalysisDataTestInput.
+     *
+     * \param[in]  data     Data to compare against.
+     * \param[in]  firstcol First column to check.
+     * \param[in]  n        Number of columns to check.
+     * \param      source   Data object to verify.
+     *
+     * Creates a mock module that verifies that the
+     * IAnalysisDataModule methods are called correctly by
+     * \p source.  Parameters for the calls are verified against \p data.
+     * Adds the created module to \p source using
+     * \p data->addColumnModule().
+     * Any exceptions from the called functions should be caught by the
+     * caller.
+     *
+     * \see AbstractAnalysisData::addColumnModule()
+     */
+    static void addStaticColumnCheckerModule(const AnalysisDataTestInput& data,
+                                             int                          firstcol,
+                                             int                          n,
+                                             AbstractAnalysisData*        source);
+    /*! \brief
+     * Adds a mock module that verifies output and storage against
+     * AnalysisDataTestInput.
+     *
+     * \param[in]  data     Data to compare against.
+     * \param[in]  storageCount  Number of previous frames to check
+     *                      (-1 = all).
+     * \param      source   Data object to verify.
+     *
+     * Works like addStaticCheckerModule(), except that in addition, for
+     * each frame, the mock module also checks that previous frames can be
+     * accessed using AbstractAnalysisData::getDataFrame().  In the
+     * IAnalysisDataModule::dataStarted() callback, the mock module
+     * calls AbstractAnalysisData::requestStorage() with \p storageCount as
+     * the parameter.
+     */
+    static void addStaticStorageCheckerModule(const AnalysisDataTestInput& data,
+                                              int                          storageCount,
+                                              AbstractAnalysisData*        source);
+    /*! \brief
+     * Adds a mock module that verifies output against reference data.
+     *
+     * \param[in]  checker   Reference data checker to use for comparison.
+     * \param[in]  id        Identifier for reference data compound to use.
+     * \param      source    Data object to verify.
+     * \param[in]  tolerance Tolerance to use for comparison.
+     *
+     * Creates a mock module that verifies that the
+     * IAnalysisDataModule methods are called correctly by
+     * \p source.  Parameters for the calls are verified against reference
+     * data using a child compound \p id of \p checker.
+     * Adds the created module to \p source using \p data->addModule().
+     * Any exceptions from the called functions should be caught by the
+     * caller.
+     *
+     * \see TestReferenceData
+     */
+    static void addReferenceCheckerModule(const TestReferenceChecker&   checker,
+                                          const char*                   id,
+                                          AbstractAnalysisData*         source,
+                                          const FloatingPointTolerance& tolerance);
+
+    /*! \brief
+     * Adds a mock module that verifies output against reference data.
+     *
+     * \param[in]  id       Identifier for reference data compound to use.
+     * \param      source   Data object to verify.
+     *
+     * Creates a reference checker module using a compound checker with id
+     * \p id at the root level of \p data_.
+     *
+     * See the static overload for other details.
+     */
+    void addReferenceCheckerModule(const char* id, AbstractAnalysisData* source);
+
+private:
+    /*! \brief
+     * Reference data object used for the reference checker modules.
+     *
+     * Tests can use the data object also for their own purposes if needed.
+     */
+    gmx::test::TestReferenceData data_;
+};
+
+
+template<class ArrayData>
+void AnalysisDataTestFixture::setupArrayData(const AnalysisDataTestInput& input, ArrayData* data)
+{
+    GMX_RELEASE_ASSERT(!input.isMultipoint(),
+                       "Array data cannot be initialized from multipoint data");
+    GMX_RELEASE_ASSERT(input.dataSetCount() == 1,
+                       "Array data cannot be initialized from multiple data sets");
+    GMX_RELEASE_ASSERT(data->columnCount() == 0 || data->columnCount() == input.columnCount(0),
+                       "Mismatching input and target data");
+    GMX_RELEASE_ASSERT(data->rowCount() == 0 || data->rowCount() == input.frameCount(),
+                       "Mismatching input and target data");
+    data->setColumnCount(input.columnCount(0));
+    data->setRowCount(input.frameCount());
+    data->allocateValues();
+    for (int row = 0; row < input.frameCount(); ++row)
+    {
+        const AnalysisDataTestInputFrame& frame = input.frame(row);
+        EXPECT_FLOAT_EQ(frame.x(), data->xvalue(row));
+        GMX_RELEASE_ASSERT(frame.pointSetCount() == 1,
+                           "Multiple point sets not supported by array data");
+        const AnalysisDataTestInputPointSet& points = frame.pointSet(0);
+        for (int column = 0; column < points.size(); ++column)
+        {
+            data->value(row, column + points.firstColumn()) = points.value(column);
+        }
+    }
+}
+
+} // namespace test
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/analysisdata/tests/mock_datamodule.h b/src/include/gromacs/analysisdata/tests/mock_datamodule.h
new file mode 100644 (file)
index 0000000..1ce1025
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares mock implementation of gmx::IAnalysisDataModule.
+ *
+ * Requires Google Mock.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_analysisdata
+ */
+#ifndef GMX_ANALYSISDATA_TESTS_MOCK_DATAMODULE_H
+#define GMX_ANALYSISDATA_TESTS_MOCK_DATAMODULE_H
+
+#include <memory>
+
+#include <gmock/gmock.h>
+
+#include "gromacs/analysisdata/dataframe.h"
+#include "gromacs/analysisdata/datamodule.h"
+#include "gromacs/analysisdata/paralleloptions.h"
+
+namespace gmx
+{
+namespace test
+{
+
+class AnalysisDataTestInput;
+class TestReferenceChecker;
+
+class MockAnalysisDataModule : public IAnalysisDataModule
+{
+public:
+    explicit MockAnalysisDataModule(int flags);
+    ~MockAnalysisDataModule() override;
+
+    int flags() const override;
+
+    MOCK_METHOD2(parallelDataStarted,
+                 bool(AbstractAnalysisData* data, const AnalysisDataParallelOptions& options));
+    MOCK_METHOD1(dataStarted, void(AbstractAnalysisData* data));
+    MOCK_METHOD1(frameStarted, void(const AnalysisDataFrameHeader& header));
+    MOCK_METHOD1(pointsAdded, void(const AnalysisDataPointSetRef& points));
+    MOCK_METHOD1(frameFinished, void(const AnalysisDataFrameHeader& header));
+    MOCK_METHOD1(frameFinishedSerial, void(int frameIndex));
+    MOCK_METHOD0(dataFinished, void());
+
+    void setupStaticCheck(const AnalysisDataTestInput& data, AbstractAnalysisData* source, bool bParallel);
+    void setupStaticColumnCheck(const AnalysisDataTestInput& data, int firstcol, int n, AbstractAnalysisData* source);
+    void setupStaticStorageCheck(const AnalysisDataTestInput& data,
+                                 int                          storageCount,
+                                 AbstractAnalysisData*        source);
+    void setupReferenceCheck(const TestReferenceChecker& checker, AbstractAnalysisData* source);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+//! Smart pointer to manage an MockAnalysisDataModule object.
+typedef std::shared_ptr<MockAnalysisDataModule> MockAnalysisDataModulePointer;
+
+} // namespace test
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/applied_forces/awh/awh.h b/src/include/gromacs/applied_forces/awh/awh.h
new file mode 100644 (file)
index 0000000..7940118
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal
+ * \defgroup module_awh Accelerated weight histogram (AWH) method
+ * \ingroup module_applied_forces
+ * \brief
+ * Implements the "accelerated weight histogram" sampling method.
+ *
+ * This class provides the interface between the AWH module and
+ * other modules using it. Currently AWH can only act on COM pull
+ * reaction coordinates, but this can easily be extended to other
+ * types of reaction coordinates.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \author Magnus Lundborg
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * Declares the Awh class.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_H
+#define GMX_AWH_H
+
+#include <cstdio>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_multisim_t;
+struct gmx_wallcycle;
+struct pull_work_t;
+struct pull_t;
+class t_state;
+struct t_commrec;
+struct t_enxframe;
+struct t_inputrec;
+enum class PbcType : int;
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+struct AwhHistory;
+class AwhParams;
+class Bias;
+struct BiasCoupledToSystem;
+class BiasSharing;
+class ForceWithVirial;
+
+/*! \libinternal
+ * \brief Coupling of the accelerated weight histogram method (AWH) with the system.
+ *
+ * AWH calculates the free energy along order parameters of the system.
+ * Free energy barriers are overcome by adaptively tuning a bias potential along
+ * the order parameter such that the biased distribution along the parameter
+ * converges toward a chosen target distribution.
+ *
+ * The Awh class takes care of the coupling between the system and the AWH
+ * bias(es). The Awh class contains one or more BiasCoupledToSystem objects.
+ * The BiasCoupledToSystem class takes care of the reaction coordinate input
+ * and force output for the single Bias object it containts.
+ *
+ * \todo Update parameter reading and checkpointing, when general C++ framework is ready.
+ */
+class Awh
+{
+public:
+    /*! \brief Construct an AWH at the start of a simulation.
+     *
+     * AWH will here also register itself with the pull struct as the
+     * potential provider for the pull coordinates given as AWH coordinates
+     * in the user input. This allows AWH to later apply the bias force to
+     * these coordinate in \ref Awh::applyBiasForcesAndUpdateBias.
+     *
+     * \param[in,out] fplog              General output file, normally md.log, can be nullptr.
+     * \param[in]     inputRecord        General input parameters (as set up by grompp).
+     * \param[in]     commRecord         Struct for communication, can be nullptr.
+     * \param[in]     multiSimRecord     Multi-sim handler
+     * \param[in]     awhParams          AWH input parameters, consistent with the relevant
+     * parts of \p inputRecord (as set up by grompp).
+     * \param[in]     biasInitFilename   Name of file to read PMF and target from.
+     * \param[in,out] pull_work          Pointer to a pull struct which AWH will
+     * couple to, has to be initialized, is assumed not to change during the
+     * lifetime of the Awh object.
+     * \param[in] numLambdaStates        The number of free energy lambda states.
+     * \param[in] lambdaState            The initial free energy lambda state of the system.
+     * \throws    InvalidInputError      If there is user input (or combinations thereof) that is not allowed.
+     */
+    Awh(FILE*                 fplog,
+        const t_inputrec&     inputRecord,
+        const t_commrec*      commRecord,
+        const gmx_multisim_t* multiSimRecord,
+        const AwhParams&      awhParams,
+        const std::string&    biasInitFilename,
+        pull_t*               pull_work,
+        int                   numLambdaStates,
+        int                   lambdaState);
+
+    ~Awh();
+
+    /*! \brief Peform an AWH update, to be called every MD step.
+     *
+     * An update has two tasks: apply the bias force and improve
+     * the bias and the free energy estimate that AWH keeps internally.
+     *
+     * For the first task, AWH retrieves the pull coordinate values from the pull struct.
+     * With these, the bias potential and forces are calculated.
+     * The bias force together with the atom forces and virial
+     * are passed on to pull which applies the bias force to the atoms.
+     * This is done at every step.
+     *
+     * Secondly, coordinate values are regularly sampled and kept by AWH.
+     * Convergence of the bias and free energy estimate is achieved by
+     * updating the AWH bias state after a certain number of samples has been collected.
+     *
+     * \note Requires that pull_potential from pull.h has been called first
+     * since AWH needs the current coordinate values (the pull code checks
+     * for this).
+     *
+     * \param[in]     pbcType          Type of periodic boundary conditions.
+     * \param[in]     masses           Atoms masses.
+     * \param[in]     neighborLambdaEnergies An array containing the energy of the system
+     * in neighboring lambdas. The array is of length numLambdas+1, where numLambdas is
+     * the number of free energy lambda states. Element 0 in the array is the energy
+     * of the current state and elements 1..numLambdas contain the energy of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[in]     neighborLambdaDhdl     An array containing the dHdL at the neighboring lambda
+     * points. The array is of length numLambdas+1, where numLambdas is the number of free
+     * energy lambda states. Element 0 in the array is the dHdL
+     * of the current state and elements 1..numLambdas contain the dHdL of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[in]     box              Box vectors.
+     * \param[in,out] forceWithVirial  Force and virial buffers, should cover at least the local atoms.
+     * \param[in]     t                Time.
+     * \param[in]     step             The current MD step.
+     * \param[in,out] wallcycle        Wallcycle counter, can be nullptr.
+     * \param[in,out] fplog            General output file, normally md.log, can be nullptr.
+     * \returns the potential energy for the bias.
+     */
+    real applyBiasForcesAndUpdateBias(PbcType                pbcType,
+                                      ArrayRef<const real>   masses,
+                                      ArrayRef<const double> neighborLambdaEnergies,
+                                      ArrayRef<const double> neighborLambdaDhdl,
+                                      const matrix           box,
+                                      gmx::ForceWithVirial*  forceWithVirial,
+                                      double                 t,
+                                      int64_t                step,
+                                      gmx_wallcycle*         wallcycle,
+                                      FILE*                  fplog);
+
+    /*! \brief
+     * Update the AWH history in preparation for writing to checkpoint file.
+     *
+     * Should be called at least on the master rank at checkpoint steps.
+     *
+     * Should be called with a valid \p awhHistory (is checked).
+     *
+     * \param[in,out] awhHistory  AWH history to set.
+     */
+    void updateHistory(AwhHistory* awhHistory) const;
+
+    /*! \brief
+     * Allocate and initialize an AWH history with the given AWH state.
+     *
+     * This function should be called at the start of a new simulation
+     * at least on the master rank.
+     * Note that only constant data will be initialized here.
+     * History data is set by \ref Awh::updateHistory.
+     *
+     * \returns a shared pointer to the AWH history on the rank that does I/O, nullptr otherwise.
+     */
+    std::shared_ptr<AwhHistory> initHistoryFromState() const;
+
+    /*! \brief Restore the AWH state from the given history.
+     *
+     * Should be called on all ranks (for internal MPI broadcast).
+     * Should pass a point to an AwhHistory on the master rank that
+     * is compatible with the AWH setup in this simulation. Will throw
+     * an exception if it is not compatible.
+     *
+     * \param[in] awhHistory  AWH history to restore from.
+     */
+    void restoreStateFromHistory(const AwhHistory* awhHistory);
+
+    /*! \brief Fills the AWH data block of an energy frame with data at certain steps.
+     *
+     * \param[in]     step  The current MD step.
+     * \param[in,out] fr    Energy data frame.
+     */
+    void writeToEnergyFrame(int64_t step, t_enxframe* fr) const;
+
+    /*! \brief Returns string "AWH" for registering AWH as an external potential provider with the pull module.
+     */
+    static const char* externalPotentialString();
+
+    /*! \brief Register the AWH biased coordinates with pull.
+     *
+     * This function is public because it needs to be called by grompp
+     * (and is otherwise only called by Awh()).
+     * Pull requires all external potentials to register themselves
+     * before the end of pre-processing and before the first MD step.
+     * If this has not happened, pull with throw an error.
+     *
+     * \param[in]     awhParams  The AWH parameters.
+     * \param[in,out] pull_work  Pull struct which AWH will register the bias into.
+     */
+    static void registerAwhWithPull(const AwhParams& awhParams, pull_t* pull_work);
+
+    /*! \brief Get the current free energy lambda state.
+     * \returns The value of lambdaState_.
+     */
+    int fepLambdaState() const { return fepLambdaState_; }
+
+    /*! \brief Returns if foreign energy differences are required during this step.
+     * \param[in]     step             The current MD step.
+     */
+    bool needForeignEnergyDifferences(int64_t step) const;
+
+    /*! \brief Returns true if AWH has a bias with a free energy lambda state dimension at all.
+     */
+    bool hasFepLambdaDimension() const;
+
+private:
+    /*! \brief Returns whether we need to write output at the current step.
+     *
+     * \param[in]     step             The current MD step.
+     */
+    bool isOutputStep(int64_t step) const;
+
+    std::vector<BiasCoupledToSystem> biasCoupledToSystem_; /**< AWH biases and definitions of their coupling to the system. */
+    const int64_t    seed_;   /**< Random seed for MC jumping with umbrella type bias potential. */
+    const int        nstout_; /**< Interval in steps for writing to energy file. */
+    const t_commrec* commRecord_; /**< Pointer to the communication record. */
+    //! Object for sharing bias between simulations, only set when needed
+    std::unique_ptr<BiasSharing> biasSharing_;
+    pull_t*                      pull_; /**< Pointer to the pull working data. */
+    double potentialOffset_; /**< The offset of the bias potential which changes due to bias updates. */
+    const int numFepLambdaStates_; /**< The number of free energy lambda states of the system. */
+    int       fepLambdaState_;     /**< The current free energy lambda state. */
+};
+
+/*! \brief Makes an Awh and prepares to use it if the user input
+ * requests that
+ *
+ * Restores state from history in checkpoint if needed.
+ *
+ * \param[in,out] fplog                   General output file, normally md.log, can be nullptr.
+ * \param[in]     inputRecord             General input parameters (as set up by grompp).
+ * \param[in]     stateGlobal             A pointer to the global state structure.
+ * \param[in]     commRecord              Struct for communication, can be nullptr.
+ * \param[in]     multiSimRecord          Multi-sim handler
+ * \param[in]     startingFromCheckpoint  Whether the simulation is starting from a checkpoint
+ * \param[in]     usingShellParticles     Whether the user requested shell particles (which is unsupported)
+ * \param[in]     biasInitFilename        Name of file to read PMF and target from.
+ * \param[in,out] pull_work               Pointer to a pull struct which AWH will couple to, has to be initialized,
+ *                                        is assumed not to change during the lifetime of the Awh object.
+ * \returns       An initialized Awh module, or nullptr if none was requested.
+ * \throws        InvalidInputError       If another active module is not supported.
+ */
+std::unique_ptr<Awh> prepareAwhModule(FILE*                 fplog,
+                                      const t_inputrec&     inputRecord,
+                                      t_state*              stateGlobal,
+                                      const t_commrec*      commRecord,
+                                      const gmx_multisim_t* multiSimRecord,
+                                      bool                  startingFromCheckpoint,
+                                      bool                  usingShellParticles,
+                                      const std::string&    biasInitFilename,
+                                      pull_t*               pull_work);
+
+} // namespace gmx
+
+#endif /* GMX_AWH_H */
diff --git a/src/include/gromacs/applied_forces/awh/bias.h b/src/include/gromacs/applied_forces/awh/bias.h
new file mode 100644 (file)
index 0000000..7b9545d
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the Bias class.
+ *
+ * This class is essentially a wrapper around the BiasState class.
+ * In addition to BiasState, it holds all data that BiasState needs
+ * to update the bias. Interaction of the outside world, such as updating
+ * BiasState or extracting bias data all happen through Bias.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_BIAS_H
+#define GMX_AWH_BIAS_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "biasgrid.h"
+#include "biasparams.h"
+#include "biasstate.h"
+#include "biaswriter.h"
+#include "dimparams.h"
+
+struct t_commrec;
+struct t_enxsubblock;
+
+namespace gmx
+{
+
+struct AwhBiasHistory;
+class AwhBiasParams;
+struct AwhHistory;
+class AwhParams;
+struct AwhPointStateHistory;
+class CorrelationGrid;
+class BiasGrid;
+class BiasSharing;
+class GridAxis;
+class PointState;
+
+/*! \internal
+ * \brief A bias acting on a multidimensional coordinate.
+ *
+ * At each step AWH should provide its biases with updated
+ * values of their coordinates. Each bias provides AWH with an updated
+ * bias forces and the corresponding potential.
+ *
+ * See the user manual for details on the algorithm and equations.
+ *
+ * The bias is responsible for keeping and updating a free energy estimate
+ * along the coordinate. The bias potential is basically a function of the
+ * free energy estimate and so also changes by the update.
+ * The free energy update is based on information from coordinate samples
+ * collected at a constant bias potential, between updates.
+ *
+ * The bias keeps a grid with coordinate points that organizes spatial
+ * information about the coordinate. The grid has the same geometry
+ * as the coordinate, i.e. they have the same dimensionality and periodicity
+ * (if any). The number of points in the grid sets the resolution of
+ * the collected data and its extent defines the sampling region of interest.
+ *
+ * Each coordinate point has further statistical properties and function values
+ * which a grid point does not know about. E.g., for the bias each coordinate point
+ * is associated with values of the bias, free energy and target distribution,
+ * accumulated sampling weight, etc. For this the bias attaches to each grid
+ * point a state. The grid + vector of point states are the bias coordinate points.
+ *
+ * The bias has a fairly complex global state keeping track of where
+ * the system (coordinate) currently is (CoordState), where it has
+ * sampled since the last update (BiasState) and controlling the free energy
+ * convergence rate (HistogramSize).
+ *
+ * Partly, the complexity comes from the bias having two convergence stages:
+ * an initial stage which in an heuristic, non-deterministic way restricts
+ * the early convergence rate for sake of robustness; and a final stage
+ * where the convergence rate is constant. The length of the initial stage
+ * depends on the sampling and is unknown beforehand.
+ *
+ * Another complexity comes from the fact that coordinate points,
+ * for sake of efficiency in the case of many grid points, are typically
+ * only accessed in recently sampled regions even though the free energy
+ * update is inherently global and affects all points.
+ * The bias allows points thay are non-local at the time the update
+ * was issued to postpone ("skip", as it is called in the code) the update.
+ * A non-local point is defined as a point which has not been sampled since
+ * the last update. Local points are points that have been sampled since
+ * the last update. The (current) set of local points are kept track of by
+ * the bias state and reset after every update. An update is called local
+ * if it only updates local points. Non-local points will temporarily "skip"
+ * the update until next time they are local (or when a global update
+ * is issued). For this to work, the bias keeps a global "clock"
+ * (in HistogramSize) of the number of issued updates. Each PointState
+ * also has its own local "clock" with the counting the number of updates
+ * it has pulled through. When a point updates its state it asserts
+ * that its local clock is synchronized with the global clock.
+ */
+class Bias
+{
+public:
+    //! Enum for requesting Bias set up with(out) I/O on this rank.
+    enum class ThisRankWillDoIO
+    {
+        No, //!< This rank will not do I/O.
+        Yes //!< This rank will do I/O.
+    };
+
+    /*! \brief
+     * Constructor.
+     *
+     * \param[in] biasIndexInCollection  Index of the bias in collection.
+     * \param[in] awhParams              AWH parameters.
+     * \param[in] awhBiasParams          Bias parameters.
+     * \param[in] dimParams              Bias dimension parameters.
+     * \param[in] beta                   1/(k_B T).
+     * \param[in] mdTimeStep             The MD time step.
+     * \param[in] biasSharing            Pointer to object for sharing bias over simulations, can be nullptr
+     * \param[in] biasInitFilename       Name of file to read PMF and target from.
+     * \param[in] thisRankWillDoIO       Tells whether this MPI rank will do I/O (checkpointing, AWH output),
+     * normally (only) the master rank does I/O.
+     * \param[in] disableUpdateSkips     If to disable update skips, useful for testing.
+     */
+    Bias(int                            biasIndexInCollection,
+         const AwhParams&               awhParams,
+         const AwhBiasParams&           awhBiasParams,
+         ArrayRef<const DimParams>      dimParams,
+         double                         beta,
+         double                         mdTimeStep,
+         const BiasSharing*             biasSharing,
+         const std::string&             biasInitFilename,
+         ThisRankWillDoIO               thisRankWillDoIO,
+         BiasParams::DisableUpdateSkips disableUpdateSkips = BiasParams::DisableUpdateSkips::no);
+
+    /*! \brief
+     * Print information about initialization to log file.
+     *
+     * Prints information about AWH variables that are set internally
+     * but might be of interest to the user.
+     *
+     * \param[in,out] fplog  Log file, can be nullptr.
+     */
+    void printInitializationToLog(FILE* fplog) const;
+
+    /*! \brief
+     * Evolves the bias at every step.
+     *
+     * At each step the bias step needs to:
+     * - set the bias force and potential;
+     * - update the free energy and bias if needed;
+     * - reweight samples to extract the PMF.
+     *
+     * \param[in]     coordValue     The current coordinate value(s).
+     * \param[in]     neighborLambdaEnergies An array containing the energy of the system
+     * in neighboring lambdas. The array is of length numLambdas+1, where numLambdas is
+     * the number of free energy lambda states. Element 0 in the array is the energy
+     * of the current state and elements 1..numLambdas contain the energy of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[in]     neighborLambdaDhdl     An array containing the dHdL at the neighboring lambda
+     * points. The array is of length numLambdas+1, where numLambdas is the number of free
+     * energy lambda states. Element 0 in the array is the dHdL
+     * of the current state and elements 1..numLambdas contain the dHdL of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[out]    awhPotential   Bias potential.
+     * \param[out]    potentialJump  Change in bias potential for this bias.
+     * \param[in]     t              Time.
+     * \param[in]     step           Time step.
+     * \param[in]     seed           Random seed.
+     * \param[in,out] fplog          Log file.
+     * \returns a reference to the bias force, size \ref ndim(), valid until the next call of this method or destruction of Bias, whichever comes first.
+     */
+    gmx::ArrayRef<const double> calcForceAndUpdateBias(const awh_dvec         coordValue,
+                                                       ArrayRef<const double> neighborLambdaEnergies,
+                                                       ArrayRef<const double> neighborLambdaDhdl,
+                                                       double*                awhPotential,
+                                                       double*                potentialJump,
+                                                       double                 t,
+                                                       int64_t                step,
+                                                       int64_t                seed,
+                                                       FILE*                  fplog);
+
+    /*! \brief
+     * Calculates the convolved bias for a given coordinate value.
+     *
+     * The convolved bias is the effective bias acting on the coordinate.
+     * Since the bias here has arbitrary normalization, this only makes
+     * sense as a relative, to other coordinate values, measure of the bias.
+     *
+     * \param[in] coordValue  The coordinate value.
+     * \returns the convolved bias >= -GMX_FLOAT_MAX.
+     */
+    double calcConvolvedBias(const awh_dvec& coordValue) const
+    {
+        return state_.calcConvolvedBias(dimParams_, grid_, coordValue);
+    }
+
+    /*! \brief
+     * Restore the bias state from history on the master rank and broadcast it.
+     *
+     * \param[in] biasHistory  Bias history struct, only allowed to be nullptr on non-master ranks.
+     * \param[in] cr           The communication record.
+     */
+    void restoreStateFromHistory(const AwhBiasHistory* biasHistory, const t_commrec* cr);
+
+    /*! \brief
+     * Allocate and initialize a bias history with the given bias state.
+     *
+     * This function will be called at the start of a new simulation.
+     * Note that only constant data will be initialized here.
+     * History data is set by \ref updateHistory.
+     *
+     * \param[in,out] biasHistory  AWH history to initialize.
+     */
+    void initHistoryFromState(AwhBiasHistory* biasHistory) const;
+
+    /*! \brief
+     * Update the bias history with the current state.
+     *
+     * \param[out] biasHistory  Bias history struct.
+     */
+    void updateHistory(AwhBiasHistory* biasHistory) const;
+
+    /*! \brief
+     * Do all previously skipped updates.
+     * Public for use by tests.
+     */
+    void doSkippedUpdatesForAllPoints();
+
+    //! Returns the dimensionality of the bias.
+    inline int ndim() const { return dimParams_.size(); }
+
+    /*! \brief Returns the dimension parameters.
+     */
+    inline ArrayRef<const DimParams> dimParams() const { return dimParams_; }
+
+    //! Returns the bias parameters
+    inline const BiasParams& params() const { return params_; }
+
+    //! Returns the global state of the bias.
+    inline const BiasState& state() const { return state_; }
+
+    //! Returns the index of the bias.
+    inline int biasIndex() const { return params_.biasIndex; }
+
+    /*! \brief Return the coordinate value for a grid point.
+     *
+     * \param[in] gridPointIndex  The index of the grid point.
+     */
+    inline const awh_dvec& getGridCoordValue(size_t gridPointIndex) const
+    {
+        GMX_ASSERT(gridPointIndex < grid_.numPoints(),
+                   "gridPointIndex should be in the range of the grid");
+
+        return grid_.point(gridPointIndex).coordValue;
+    }
+
+private:
+    /*! \brief
+     * Performs statistical checks on the collected histograms and warns if issues are detected.
+     *
+     * \param[in]     t        Time.
+     * \param[in]     step     Time step.
+     * \param[in,out] fplog    Output file for warnings.
+     */
+    void warnForHistogramAnomalies(double t, int64_t step, FILE* fplog);
+
+    /*! \brief
+     * Collect samples for the force correlation analysis on the grid.
+     *
+     * \param[in] probWeightNeighbor  Probability weight of the neighboring points.
+     * \param[in] neighborLambdaDhdl  An array containing the dHdL at the neighboring lambda
+     * points. The array is of length numLambdas+1, where numLambdas is the number of free
+     * energy lambda states. Element 0 in the array is the dHdL
+     * of the current state and elements 1..numLambdas contain the dHdL of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[in] t                   The time.
+     */
+    void updateForceCorrelationGrid(gmx::ArrayRef<const double> probWeightNeighbor,
+                                    ArrayRef<const double>      neighborLambdaDhdl,
+                                    double                      t);
+
+public:
+    /*! \brief Return a const reference to the force correlation grid.
+     */
+    const CorrelationGrid& forceCorrelationGrid() const
+    {
+        GMX_RELEASE_ASSERT(forceCorrelationGrid_ != nullptr,
+                           "forceCorrelationGrid() should only be called with a valid force "
+                           "correlation object");
+
+        return *forceCorrelationGrid_;
+    }
+
+    /*! \brief Return the number of data blocks that have been prepared for writing.
+     */
+    int numEnergySubblocksToWrite() const;
+
+    /*! \brief Write bias data blocks to energy subblocks.
+     *
+     * \param[in,out] subblock  Energy subblocks to write to.
+     * \returns the number of subblocks written.
+     */
+    int writeToEnergySubblocks(t_enxsubblock* subblock) const;
+
+    /*! \brief Returns true if the bias has a free energy lambda state dimension at all.
+     */
+    bool hasFepLambdaDimension() const
+    {
+        return std::any_of(std::begin(dimParams_), std::end(dimParams_), [](const auto& dimParam) {
+            return dimParam.isFepLambdaDimension();
+        });
+    }
+
+    /*! \brief
+     * Returns whether we should sample the coordinate.
+     *
+     * \param[in] step  The MD step number.
+     */
+    bool isSampleCoordStep(int64_t step) const;
+
+    /* Data members. */
+private:
+    const std::vector<DimParams> dimParams_; /**< Parameters for each dimension. */
+    const BiasGrid grid_; /**< The multidimensional grid organizing the coordinate point locations. */
+
+    const BiasParams params_; /**< Constant parameters for the method. */
+
+    BiasState        state_; /**< The state, both global and of the grid points */
+    std::vector<int> updateList_; /**< List of points for update for temporary use (could be made another tempWorkSpace) */
+
+    const bool thisRankDoesIO_; /**< Tells whether this MPI rank will do I/O (checkpointing, AWH output) */
+
+    std::vector<double> biasForce_; /**< Vector for returning the force to the caller. */
+
+    /* Force correlation grid */
+    std::unique_ptr<CorrelationGrid> forceCorrelationGrid_; /**< Takes care of force correlation statistics for every grid point. */
+
+    /* I/O */
+    std::unique_ptr<BiasWriter> writer_; /**< Takes care of AWH data output. */
+
+    /* Temporary working vectors used during the update.
+     * These are only here to avoid allocation at every MD step.
+     */
+    std::vector<double, AlignedAllocator<double>> alignedTempWorkSpace_; /**< Working vector of doubles. */
+    std::vector<double>                           tempForce_; /**< Bias force work buffer. */
+
+    /* Run-local counter to avoid flooding log with warnings. */
+    int numWarningsIssued_; /**< The number of warning issued in the current run. */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_BIAS_H */
diff --git a/src/include/gromacs/applied_forces/awh/biasgrid.h b/src/include/gromacs/applied_forces/awh/biasgrid.h
new file mode 100644 (file)
index 0000000..2c6777d
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * This file contains datatypes and function declarations necessary
+ * for AWH to interface with the grid code.
+ *
+ * The grid organizes spatial properties of the AWH coordinate points.
+ * This includes traversing points in a specific order, locating
+ * neighboring points and calculating distances. Multiple dimensions
+ * as well as periodic dimensions are supported.
+ *
+ * \todo: Replace this by a more generic grid class once that is available.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_BIASGRID_H
+#define GMX_AWH_BIASGRID_H
+
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "gromacs/utility/arrayref.h"
+
+#include "dimparams.h" /* This is needed for awh_dvec */
+
+namespace gmx
+{
+
+class AwhDimParams;
+
+/*! \internal
+ * \brief An axis, i.e. dimension, of the grid.
+ */
+class GridAxis
+{
+public:
+    /*! \brief Constructor.
+     *
+     * The spacing and number of points are set such that we have
+     * at least the requested point density.
+     * Requesting 0 point density results in the minimum number
+     * of points (2).
+     *
+     * \param[in] origin           Starting value.
+     * \param[in] end              End value.
+     * \param[in] period           Period, pass 0 if not periodic.
+     * \param[in] pointDensity     Requested number of point per unit of axis length.
+     */
+    GridAxis(double origin, double end, double period, double pointDensity);
+
+    /*! \brief Constructor.
+     *
+     * \param[in] origin           Starting value.
+     * \param[in] end              End value.
+     * \param[in] period           Period, pass 0 if not periodic.
+     * \param[in] numPoints        The number of points.
+     * \param[in] isFepLambdaAxis     If this axis is controlling lambda.
+     */
+    GridAxis(double origin, double end, double period, int numPoints, bool isFepLambdaAxis);
+
+    /*! \brief Returns whether the axis has periodic boundaries.
+     */
+    bool isPeriodic() const { return period_ > 0; }
+
+    /*! \brief Returns the period of the grid along the axis.
+     */
+    double period() const { return period_; }
+
+    /*! \brief Returns the grid origin along the axis.
+     */
+    double origin() const { return origin_; }
+
+    /*! \brief Returns the grid point spacing along the axis.
+     */
+    double spacing() const { return spacing_; }
+
+    /*! \brief Returns the number of grid points along the axis.
+     */
+    int numPoints() const { return numPoints_; }
+
+    /*! \brief Returns the period of the grid in points along the axis.
+     *
+     * Returns 0 if the axis is not periodic.
+     */
+    int numPointsInPeriod() const { return numPointsInPeriod_; }
+
+    /*! \brief Returns the length of the interval.
+     *
+     * This returns the distance obtained by connecting the origin point to
+     * the end point in the positive direction. Note that this is generally
+     * not the shortest distance. For period > 0, both origin and
+     * end are expected to take values in the same periodic interval.
+     */
+    double length() const { return length_; }
+
+    /*! \brief Map a value to the nearest point index along an axis.
+     *
+     * \param[in] value  Value along the axis.
+     * \returns the index nearest to the value.
+     */
+    int nearestIndex(double value) const;
+
+    /*! \brief Returns whether this axis is coupled to the free energy lambda state.
+     */
+    bool isFepLambdaAxis() const { return isFepLambdaAxis_; }
+
+private:
+    double origin_;            /**< Interval start value */
+    double length_;            /**< Interval length */
+    double period_;            /**< The period, 0 if not periodic */
+    double spacing_;           /**< Point spacing */
+    int    numPoints_;         /**< Number of points in the interval */
+    int    numPointsInPeriod_; /**< Number of points in a period (0 if no periodicity) */
+    bool isFepLambdaAxis_; /**< If this axis is coupled to the system's free energy lambda state */
+};
+
+/*! \internal
+ * \brief A point in the grid.
+ *
+ * A grid point has a coordinate value and a coordinate index of the same dimensionality as the
+ * grid. It knows the linear indices of its neighboring point (which are useful only when handed up
+ * to the grid).
+ */
+struct GridPoint
+{
+    awh_dvec         coordValue; /**< Multidimensional coordinate value of this point */
+    awh_ivec         index;      /**< Multidimensional point indices */
+    std::vector<int> neighbor;   /**< Linear point indices of the neighboring points */
+};
+
+/*! \internal
+ * \brief The grid for a single bias, generally multidimensional and periodic.
+ *
+ * The grid discretizes a multidimensional space with some given resolution.
+ * Each dimension is represented by an axis which sets the spatial extent,
+ * point spacing and periodicity of the grid in that direction.
+ */
+class BiasGrid
+{
+private:
+    /*! \brief Initializes the grid points.
+     */
+    void initPoints();
+
+public:
+    /*! \brief
+     * The point density per sigma of the Gaussian distribution in an umbrella.
+     *
+     * This value should be at least 1 to uniformly cover the reaction coordinate
+     * range with density and having it larger than 1 does not add information.
+     */
+    static constexpr double c_numPointsPerSigma = 1.0;
+
+    //! Cut-off in sigma for considering points, neglects 4e-8 of the density.
+    static constexpr double c_scopeCutoff = 5.5;
+
+    /*! \brief Construct a grid using AWH input parameters.
+     *
+     * \param[in] dimParams     Dimension parameters including the expected inverse variance of the
+     * coordinate living on the grid (determines the grid spacing).
+     * \param[in] awhDimParams  Dimension params from inputrec.
+     */
+    BiasGrid(ArrayRef<const DimParams> dimParams, ArrayRef<const AwhDimParams> awhDimParams);
+
+    /*! \brief Returns the number of points in the grid.
+     *
+     * \returns the number of points in the grid.
+     */
+    size_t numPoints() const { return point_.size(); }
+
+    /*! \brief Returns a reference to a point on the grid.
+     *
+     * \returns a constant reference to a point on the grid.
+     */
+    const GridPoint& point(size_t pointIndex) const { return point_[pointIndex]; }
+
+    /*! \brief Returns the dimensionality of the grid.
+     *
+     * \returns the dimensionality of the grid.
+     */
+    int numDimensions() const { return axis_.size(); }
+
+    /*! \brief Returns the grid axes.
+     *
+     * \returns a constant reference to the grid axes.
+     */
+    ArrayRef<const GridAxis> axis() const { return axis_; }
+
+    /*! \brief Returns a grid axis.
+     *
+     * param[in] dim  Dimension to return the grid axis for.
+     * \returns a constant reference to the grid axis.
+     */
+    const GridAxis& axis(int dim) const { return axis_[dim]; }
+
+    /*! \brief Find the grid point with value nearest to the given value.
+     *
+     * \param[in] value  Value vector.
+     * \returns the grid point index.
+     */
+    int nearestIndex(const awh_dvec value) const;
+
+    /*! \brief Query if the value is in the grid.
+     *
+     * \param[in] value  Value vector.
+     * \returns true if the value is in the grid.
+     * \note It is assumed that any periodicity of value has already been taken care of.
+     */
+    bool covers(const awh_dvec value) const;
+
+    /*! \brief Returns true if the grid has a free energy lambda state axis at all.
+     */
+    bool hasLambdaAxis() const
+    {
+        return std::any_of(std::begin(axis_), std::end(axis_), [](const auto& axis) {
+            return axis.isFepLambdaAxis();
+        });
+    }
+
+    /*! \brief
+     * Returns the index of a free energy lambda state axis (there can be
+     * no more than one) if there is one.
+     */
+    std::optional<int> lambdaAxisIndex() const;
+
+    /*! \brief
+     * Returns the number of free energy lambda states in the grid (the number
+     * of points along a free energy lambda state axis) or 0 if there are no free energy
+     * lambda state axes.
+     */
+    int numFepLambdaStates() const;
+
+private:
+    std::vector<GridPoint> point_; /**< Points on the grid */
+    std::vector<GridAxis>  axis_;  /**< Axes, one for each dimension. */
+};
+
+/*! \endcond */
+
+/*! \brief Convert a multidimensional grid point index to a linear one.
+ *
+ * \param[in] grid        The grid.
+ * \param[in] indexMulti  Multidimensional grid point index to convert to a linear one.
+ * \returns the linear index.
+ */
+int multiDimGridIndexToLinear(const BiasGrid& grid, const awh_ivec indexMulti);
+
+/*! \brief Convert multidimensional array index to a linear one.
+ *
+ * \param[in] indexMulti    Multidimensional index to convert to a linear one.
+ * \param[in] numDim        Number of dimensions of the array.
+ * \param[in] numPointsDim  Number of points of the array.
+ * \returns the linear index.
+ * \note This function can be used without having an initialized grid.
+ */
+int multiDimArrayIndexToLinear(const awh_ivec indexMulti, int numDim, const awh_ivec numPointsDim);
+
+/*! \brief Convert a linear grid point index to a multidimensional one.
+ *
+ * \param[in]  grid         The grid.
+ * \param[in]  indexLinear  Linear grid point index to convert to a multidimensional one.
+ * \param[out] indexMulti   The multidimensional index.
+ */
+void linearGridindexToMultiDim(const BiasGrid& grid, int indexLinear, awh_ivec indexMulti);
+
+/*! \brief Convert a linear array index to a multidimensional one.
+ *
+ * \param[in]  indexLinear   Linear array index
+ * \param[in]  ndim          Number of dimensions of the array.
+ * \param[in]  numPointsDim  Number of points for each dimension.
+ * \param[out] indexMulti    The multidimensional index.
+ */
+void linearArrayIndexToMultiDim(int indexLinear, int ndim, const awh_ivec numPointsDim, awh_ivec indexMulti);
+
+/*! \brief
+ * Find the next grid point in the sub-part of the grid given a starting point.
+ *
+ * The given grid point index is updated to the next valid grid point index
+ * by traversing the sub-part of the grid, here termed the subgrid.
+ * Since the subgrid range might extend beyond the actual size of the grid,
+ * the subgrid is traversed until a point both in the subgrid and grid is
+ * found. If no point is found, the function returns false and the index is
+ * not modified. The starting point needs to be inside of the subgrid. However,
+ * if this index is not given, meaning < 0, then the search is initialized at
+ * the subgrid origin, i.e. in this case the "next" grid point index is
+ * defined to be the first common grid/subgrid point.
+ *
+ * \param[in]     grid            The grid.
+ * \param[in]     subgridOrigin   Vector locating the subgrid origin relative to the grid origin.
+ * \param[in]     subgridNpoints  Number of points along each subgrid dimension.
+ * \param[in,out] gridPointIndex  Pointer to the starting/next grid point index.
+ * \returns true if the grid point was updated.
+ */
+bool advancePointInSubgrid(const BiasGrid& grid,
+                           const awh_ivec  subgridOrigin,
+                           const awh_ivec  subgridNpoints,
+                           int*            gridPointIndex);
+
+/*! \brief Maps each point in the grid to a point in the data grid.
+ *
+ * This functions maps an AWH bias grid to a user provided input data grid.
+ * The value of data grid point i along dimension d is given by data[d][i].
+ * The number of dimensions of the data should equal that of the grid.
+ * A fatal error is thrown if extracting the data fails or the data does not cover the whole grid.
+ *
+ * \param[out] gridpointToDatapoint  Array mapping each grid point to a data point index.
+ * \param[in]  data                  2D array in format ndim x ndatapoints with data grid point values.
+ * \param[in]  numDataPoints         Number of data points.
+ * \param[in]  dataFilename          The data filename.
+ * \param[in]  grid                  The grid.
+ * \param[in]  correctFormatMessage  String to include in error message if extracting the data fails.
+ */
+void mapGridToDataGrid(std::vector<int>*    gridpointToDatapoint,
+                       const double* const* data,
+                       int                  numDataPoints,
+                       const std::string&   dataFilename,
+                       const BiasGrid&      grid,
+                       const std::string&   correctFormatMessage);
+
+/*! \brief
+ * Get the deviation along one dimension from the given value to a point in the grid.
+ *
+ * \param[in] grid        The grid.
+ * \param[in] dimIndex    Dimensional index in [0, ndim -1].
+ * \param[in] pointIndex  BiasGrid point index.
+ * \param[in] value       Value along the given dimension.
+ * \returns the deviation of the given value to the given point.
+ */
+double getDeviationFromPointAlongGridAxis(const BiasGrid& grid, int dimIndex, int pointIndex, double value);
+
+/*! \brief
+ * Get the deviation from one point to another along one dimension in the grid.
+ *
+ * \param[in] grid        The grid.
+ * \param[in] dimIndex    Dimensional index in [0, ndim -1].
+ * \param[in] pointIndex1 Grid point index of the first point.
+ * \param[in] pointIndex2 Grid point index of the second point.
+ * \returns the deviation of the two points along the given axis.
+ */
+double getDeviationFromPointAlongGridAxis(const BiasGrid& grid, int dimIndex, int pointIndex1, int pointIndex2);
+
+/*! \brief
+ * Checks whether two points are along a free energy lambda state axis.
+ *
+ * \param[in] grid        The grid.
+ * \param[in] pointIndex1 Grid point index of the first point.
+ * \param[in] pointIndex2 Grid point index of the second point.
+ * \returns true if the two points are along a free energy lambda state axis.
+ */
+bool pointsAlongLambdaAxis(const BiasGrid& grid, int pointIndex1, int pointIndex2);
+
+/*! \brief
+ * Checks whether two points are different in the free energy lambda state
+ * dimension (if any).
+ *
+ * \param[in] grid        The grid.
+ * \param[in] pointIndex1 Grid point index of the first point.
+ * \param[in] pointIndex2 Grid point index of the second point.
+ * \returns true if the two points have different lambda values.
+ */
+bool pointsHaveDifferentLambda(const BiasGrid& grid, int pointIndex1, int pointIndex2);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/applied_forces/awh/biasparams.h b/src/include/gromacs/applied_forces/awh/biasparams.h
new file mode 100644 (file)
index 0000000..119f91d
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the BiasParams class.
+ *
+ * This class holds the parameters for the bias. Most are direct copies
+ * of the input that the user provided. Some are a combination of user
+ * input and properties of the simulated system.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_BIASPARAMS_H
+#define GMX_AWH_BIASPARAMS_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+#include "dimparams.h"
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+class AwhBiasParams;
+class AwhParams;
+struct DimParams;
+class GridAxis;
+enum class AwhTargetType : int;
+
+/*! \internal \brief Constant parameters for the bias.
+ */
+class BiasParams
+{
+public:
+    /*! \brief Switch to turn off update skips, useful for testing.
+     */
+    enum class DisableUpdateSkips
+    {
+        no, /**< Allow update skips (when supported by the method) */
+        yes /**< Disable update skips */
+    };
+
+    /*! \brief
+     * Check if the parameters permit skipping updates.
+     *
+     * Generally, we can skip updates of points that are non-local
+     * at the time of the update if we for later times, when the points
+     * with skipped updates have become local, know exactly how to apply
+     * the previous updates. The free energy updates only depend
+     * on local sampling, but the histogram rescaling factors
+     * generally depend on the histogram size (all samples).
+     * If the histogram size is kept constant or the scaling factors
+     * are trivial, this is not a problem. However, if the histogram growth
+     * is scaled down by some factor the size at the time of the update
+     * needs to be known. It would be fairly simple to, for a deterministically
+     * growing histogram, backtrack and calculate this value, but currently
+     * we just disallow this case. This is not a restriction because it
+     * only affects the local Boltzmann target type for which every update
+     * is currently anyway global because the target is always updated globally.
+     *
+     * \returns true when we can skip updates.
+     */
+    inline bool skipUpdates() const { return (!disableUpdateSkips_ && localWeightScaling == 1); }
+
+    /*! \brief
+     * Returns the radius that needs to be sampled around a point before it is considered covered.
+     */
+    inline const awh_ivec& coverRadius() const { return coverRadius_; }
+
+    /*! \brief
+     * Returns whether we should sample the coordinate.
+     *
+     * \param[in] step  The MD step number.
+     */
+    inline bool isSampleCoordStep(int64_t step) const
+    {
+        return (step > 0 && step % numStepsSampleCoord_ == 0);
+    }
+
+    /*! \brief
+     * Returns whether we should update the free energy.
+     *
+     * \param[in] step  The MD step number.
+     */
+    inline bool isUpdateFreeEnergyStep(int64_t step) const
+    {
+        int stepIntervalUpdateFreeEnergy = numSamplesUpdateFreeEnergy_ * numStepsSampleCoord_;
+        return (step > 0 && step % stepIntervalUpdateFreeEnergy == 0);
+    }
+
+    /*! \brief
+     * Returns whether we should update the target distribution.
+     *
+     * \param[in] step  The MD step number.
+     */
+    inline bool isUpdateTargetStep(int64_t step) const { return step % numStepsUpdateTarget_ == 0; }
+
+    /*! \brief
+     * Returns if to do checks for covering in the initial stage.
+     *
+     * To avoid overhead due to expensive checks, we do not check
+     * at every free energy update. However, if checks are
+     * performed too rarely the detection of coverings will be
+     * delayed, ultimately affecting free energy convergence.
+     *
+     * \param[in] step                  Time step.
+     * \returns true at steps where checks should be performed.
+     * \note  Only returns true at free energy update steps.
+     */
+    bool isCheckCoveringStep(int64_t step) const
+    {
+        return step > 0 && (step % numStepsCheckCovering_ == 0);
+    }
+
+    /*! \brief
+     * Returns if to perform checks for anomalies in the histogram.
+     *
+     * To avoid overhead due to expensive checks, we do not check
+     * at every free energy update. These checks are only used for
+     * warning the user and can be made as infrequently as
+     * neccessary without affecting the algorithm itself.
+     *
+     * \param[in] step                  Time step.
+     * \returns true at steps where checks should be performed.
+     * \note Only returns true at free energy update steps.
+     * \todo Currently this function just calls isCheckCoveringStep but the checks could be done less frequently.
+     */
+    bool isCheckHistogramForAnomaliesStep(int64_t step) const { return isCheckCoveringStep(step); }
+
+    /*! \brief Constructor.
+     *
+     * The local Boltzmann target distibution is defined by
+     * 1) Adding the sampled weights instead of the target weights to the reference weight histogram.
+     * 2) Scaling the weights of these samples by the beta scaling factor.
+     * 3) Setting the target distribution equal the reference weight histogram.
+     * This requires the following special update settings:
+     *   localWeightScaling      = targetParam
+     *   idealWeighthistUpdate   = false
+     * Note: these variables could in principle be set to something else also for other target distribution types.
+     * However, localWeightScaling < 1  is in general expected to give lower efficiency and, except for local Boltzmann,
+     * idealWeightHistUpdate = false gives (in my experience) unstable, non-converging results.
+     *
+     * \param[in] awhParams              AWH parameters.
+     * \param[in] awhBiasParams          Bias parameters.
+     * \param[in] dimParams              Bias dimension parameters.
+     * \param[in] beta                   1/(k_B T) in units of 1/(kJ/mol), should be > 0.
+     * \param[in] mdTimeStep             The MD time step.
+     * \param[in] numSharingSimulations  The number of simulations to share the bias across.
+     * \param[in] gridAxis               The grid axes.
+     * \param[in] disableUpdateSkips     If to disable update skips, useful for testing.
+     * \param[in] biasIndex              Index of the bias.
+     */
+    BiasParams(const AwhParams&          awhParams,
+               const AwhBiasParams&      awhBiasParams,
+               ArrayRef<const DimParams> dimParams,
+               double                    beta,
+               double                    mdTimeStep,
+               DisableUpdateSkips        disableUpdateSkips,
+               int                       numSharingSimulations,
+               ArrayRef<const GridAxis>  gridAxis,
+               int                       biasIndex);
+
+    /* Data members */
+    const double invBeta; /**< 1/beta = kT in kJ/mol */
+private:
+    const int64_t numStepsSampleCoord_; /**< Number of steps per coordinate value sample. */
+public:
+    const int numSamplesUpdateFreeEnergy_; /**< Number of samples per free energy update. */
+private:
+    const int64_t numStepsUpdateTarget_; /**< Number of steps per updating the target distribution. */
+    const int64_t numStepsCheckCovering_; /**< Number of steps per checking for covering. */
+public:
+    const AwhTargetType eTarget;       /**< Type of target distribution. */
+    const double freeEnergyCutoffInKT; /**< Free energy cut-off in kT for cut-off target distribution. */
+    const double temperatureScaleFactor; /**< Temperature scaling factor for temperature scaled targed distributions. */
+    const bool   idealWeighthistUpdate; /**< Update reference weighthistogram using the target distribution? Otherwise use the realized distribution. */
+    const int    numSharedUpdate; /**< The number of (multi-)simulations sharing the bias update */
+    const double updateWeight;    /**< The probability weight accumulated for each update. */
+    const double localWeightScaling; /**< Scaling factor applied to a sample before adding it to the reference weight histogram (= 1, usually). */
+    const double initialErrorInKT;   /**< Estimated initial free energy error in kT. */
+    const double initialHistogramSize; /**< Initial reference weight histogram size. */
+private:
+    awh_ivec coverRadius_; /**< The radius (in points) that needs to be sampled around a point before it is considered covered. */
+public:
+    const bool convolveForce; /**< True if we convolve the force, false means use MC between umbrellas. */
+    const int biasIndex; /**< Index of the bias, used as a second random seed and for priting. */
+private:
+    const bool disableUpdateSkips_; /**< If true, we disallow update skips, even when the method supports it. */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_BIASPARAMS_H */
diff --git a/src/include/gromacs/applied_forces/awh/biassharing.h b/src/include/gromacs/applied_forces/awh/biassharing.h
new file mode 100644 (file)
index 0000000..e73ec51
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares functions to check bias sharing properties.
+ *
+ * This actual sharing of biases is currently implemeted in BiasState.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_BIASSHARING_H
+#define GMX_AWH_BIASSHARING_H
+
+#include <cstddef>
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/gmxmpi.h"
+
+struct gmx_multisim_t;
+struct t_commrec;
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+class AwhParams;
+
+class BiasSharing
+{
+public:
+    /*! \brief Constructor
+     *
+     * \param[in] awhParams              Parameters for all biases in this simulation
+     * \param[in] commRecord             Intra-simulation communication record
+     * \param[in] simulationMastersComm  MPI communicator for all master ranks of all simulations that share this bias
+     */
+    BiasSharing(const AwhParams& awhParams, const t_commrec& commRecord, MPI_Comm simulationMastersComm);
+
+    ~BiasSharing();
+
+    //! Returns the number of simulations sharing bias \p biasIndex
+    int numSharingSimulations(int biasIndex) const { return numSharingSimulations_[biasIndex]; }
+
+    //! Returns the index of our simulation in the simulations sharing bias \p biasIndex
+    int sharingSimulationIndex(int biasIndex) const { return sharingSimulationIndices_[biasIndex]; }
+
+    //! Sums data over the master ranks of all simulations sharing bias \p biasIndex
+    void sumOverMasterRanks(ArrayRef<int> data, int biasIndex) const;
+
+    //! Sums data over the master ranks of all simulations sharing bias \p biasIndex
+    void sumOverMasterRanks(ArrayRef<long> data, int biasIndex) const;
+
+    //! Sums data over all simulations sharing bias \p biasIndex and broadcasts to all ranks within the simulation
+    void sum(ArrayRef<int> data, int biasIndex) const;
+
+    //! Sums data over all simulations sharing bias \p biasIndex and broadcasts to all ranks within the simulation
+    void sum(ArrayRef<double> data, int biasIndex) const;
+
+private:
+    //! The number of simulations sharing for each bias
+    std::vector<int> numSharingSimulations_;
+    //! The index of our simulations in the simulations for each bias
+    std::vector<int> sharingSimulationIndices_;
+    //! Reference to the intra-simulation communication record
+    const t_commrec& commRecord_;
+
+    //! Communicator between master ranks sharing a bias, for each bias
+    std::vector<MPI_Comm> multiSimCommPerBias_;
+    //! List of MPI communicators created by this object so we can destroy them on distruction
+    std::vector<MPI_Comm> createdCommList_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(BiasSharing);
+};
+
+/*! \brief Returns if any bias is sharing within a simulation.
+ *
+ * \param[in] awhParams  The AWH parameters.
+ */
+bool haveBiasSharingWithinSimulation(const AwhParams& awhParams);
+
+/*! \brief Checks whether biases are compatible for sharing between simulations, throws when not.
+ *
+ * Should be called simultaneously on the master rank of every simulation.
+ * Note that this only checks for technical compatibility. It is up to
+ * the user to check that the sharing physically makes sense.
+ * Throws an exception when shared biases are not compatible.
+ *
+ * \param[in] awhParams     The AWH parameters.
+ * \param[in] pointSize     Vector of grid-point sizes for each bias.
+ * \param[in] biasSharing   Object for communication for sharing bias data over simulations.
+ */
+void biasesAreCompatibleForSharingBetweenSimulations(const AwhParams&       awhParams,
+                                                     ArrayRef<const size_t> pointSize,
+                                                     const BiasSharing&     biasSharing);
+
+} // namespace gmx
+
+#endif /* GMX_AWH_BIASSHARING_H */
diff --git a/src/include/gromacs/applied_forces/awh/biasstate.h b/src/include/gromacs/applied_forces/awh/biasstate.h
new file mode 100644 (file)
index 0000000..87a6b03
--- /dev/null
@@ -0,0 +1,555 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the BiasState class.
+ *
+ * The data members of this class are the state variables of the bias.
+ * All interaction from the outside happens through the Bias class, which
+ * holds important helper classes such as DimParams and BiasGrid.
+ * This class holds many methods, but more are const methods that compute
+ * properties of the state.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_BIASSTATE_H
+#define GMX_AWH_BIASSTATE_H
+
+#include <cstdio>
+
+#include <string>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "coordstate.h"
+#include "dimparams.h"
+#include "histogramsize.h"
+
+struct t_commrec;
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+struct AwhBiasHistory;
+class BiasParams;
+class BiasGrid;
+class BiasSharing;
+class GridAxis;
+class PointState;
+
+/*! \internal
+ * \brief The state of a bias.
+ *
+ * The bias state has the current coordinate state: its value and the grid point
+ * it maps to (the grid point of the umbrella potential if needed). It contains
+ * a vector with the state for each point on the grid. It also
+ * counts the number of updates issued and tracks which points have been sampled
+ * since last update. Finally, the convergence state is a global property set
+ * ultimately by the histogram size histogramSize in the sub-class HistogramSize,
+ * since the update sizes are ~ 1/histogramSize.
+ */
+class BiasState
+{
+public:
+    /*! \brief Constructor.
+     *
+     * Constructs the global state and the point states on a provided
+     * geometric grid passed in \p grid.
+     *
+     * \param[in] awhBiasParams         The Bias parameters from inputrec.
+     * \param[in] histogramSizeInitial  The estimated initial histogram size.
+     *                                  This is floating-point, since histograms use weighted
+     *                                  entries and grow by a floating-point scaling factor.
+     * \param[in] dimParams             The dimension parameters.
+     * \param[in] grid                  The bias grid.
+     * \param[in] biasSharing           Multisim bias sharing object, can be nullptrx
+     */
+    BiasState(const AwhBiasParams&      awhBiasParams,
+              double                    histogramSizeInitial,
+              ArrayRef<const DimParams> dimParams,
+              const BiasGrid&           grid,
+              const BiasSharing*        biasSharing);
+
+    /*! \brief
+     * Restore the bias state from history.
+     *
+     * \param[in] biasHistory  Bias history struct.
+     * \param[in] grid         The bias grid.
+     */
+    void restoreFromHistory(const AwhBiasHistory& biasHistory, const BiasGrid& grid);
+
+    /*! \brief
+     * Broadcast the bias state over the MPI ranks in this simulation.
+     *
+     * \param[in] commRecord  Struct for communication.
+     */
+    void broadcast(const t_commrec* commRecord);
+
+    /*! \brief
+     * Allocate and initialize a bias history with the given bias state.
+     *
+     * This function will be called at the start of a new simulation.
+     * Note that this only sets the correct size and does produce
+     * a valid history object, but with all data set to zero.
+     * Actual history data is set by \ref updateHistory.
+     *
+     * \param[in,out] biasHistory  AWH history to initialize.
+     */
+    void initHistoryFromState(AwhBiasHistory* biasHistory) const;
+
+    /*! \brief
+     * Update the bias state history with the current state.
+     *
+     * \param[out] biasHistory  Bias history struct.
+     * \param[in]  grid         The bias grid.
+     */
+    void updateHistory(AwhBiasHistory* biasHistory, const BiasGrid& grid) const;
+
+private:
+    /*! \brief Convolves the given PMF using the given AWH bias.
+     *
+     * \note: The PMF is in single precision, because it is a statistical
+     *        quantity and therefore never reaches full float precision.
+     *
+     * \param[in] dimParams     The bias dimensions parameters
+     * \param[in] grid          The grid.
+     * \param[in,out] convolvedPmf  Array returned will be of the same length as the AWH grid to store the convolved PMF in.
+     */
+    void calcConvolvedPmf(ArrayRef<const DimParams> dimParams,
+                          const BiasGrid&           grid,
+                          std::vector<float>*       convolvedPmf) const;
+
+    /*! \brief
+     * Convolves the PMF and sets the initial free energy to its convolution.
+     *
+     * \param[in] dimParams  The bias dimensions parameters
+     * \param[in] grid       The bias grid.
+     */
+    void setFreeEnergyToConvolvedPmf(ArrayRef<const DimParams> dimParams, const BiasGrid& grid);
+
+    /*! \brief
+     * Normalize the PMF histogram.
+     *
+     * \param[in] numSharingSims  The number of simulations sharing the bias.
+     */
+    void normalizePmf(int numSharingSims);
+
+public:
+    /*! \brief
+     * Initialize the state of grid coordinate points.
+     *
+     * \param[in] awhBiasParams   Bias parameters from inputrec.
+     * \param[in] dimParams       The dimension parameters.
+     * \param[in] grid            The grid.
+     * \param[in] params          The bias parameters.
+     * \param[in] filename        Name of file to read PMF and target from.
+     * \param[in] numBias         The number of biases.
+     */
+    void initGridPointState(const AwhBiasParams&      awhBiasParams,
+                            ArrayRef<const DimParams> dimParams,
+                            const BiasGrid&           grid,
+                            const BiasParams&         params,
+                            const std::string&        filename,
+                            int                       numBias);
+
+    /*! \brief
+     * Performs statistical checks on the collected histograms and warns if issues are detected.
+     *
+     * \param[in]     grid            The grid.
+     * \param[in]     biasIndex       The index of the bias we are checking for.
+     * \param[in]     t               Time.
+     * \param[in,out] fplog           Output file for warnings.
+     * \param[in]     maxNumWarnings  Don't issue more than this number of warnings.
+     * \returns the number of warnings issued.
+     */
+    int warnForHistogramAnomalies(const BiasGrid& grid, int biasIndex, double t, FILE* fplog, int maxNumWarnings) const;
+
+    /*! \brief
+     * Calculates and sets the force the coordinate experiences from an umbrella centered at the given point.
+     *
+     * The umbrella potential is an harmonic potential given by 0.5k(coord value - point value)^2. This
+     * value is also returned.
+     *
+     * \param[in]     dimParams  The bias dimensions parameters.
+     * \param[in]     grid       The grid.
+     * \param[in]     point      Point for umbrella center.
+     * \param[in]     neighborLambdaDhdl     An array containing the dHdL at the neighboring lambda
+     * points. The array is of length numLambdas+1, where numLambdas is the number of free
+     * energy lambda states. Element 0 in the array is the dHdL
+     * of the current state and elements 1..numLambdas contain the dHdL of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[in,out] force      Force vector to set.
+     * Returns the umbrella potential.
+     */
+    double calcUmbrellaForceAndPotential(ArrayRef<const DimParams> dimParams,
+                                         const BiasGrid&           grid,
+                                         int                       point,
+                                         ArrayRef<const double>    neighborLambdaDhdl,
+                                         ArrayRef<double>          force) const;
+
+    /*! \brief
+     * Calculates and sets the convolved force acting on the coordinate.
+     *
+     * The convolved force is the weighted sum of forces from umbrellas
+     * located at each point in the grid.
+     *
+     * \param[in]     dimParams           The bias dimensions parameters.
+     * \param[in]     grid                The grid.
+     * \param[in]     probWeightNeighbor  Probability weights of the neighbors.
+     * \param[in]     neighborLambdaDhdl     An array containing the dHdL at the neighboring lambda
+     * points. The array is of length numLambdas+1, where numLambdas is the number of free
+     * energy lambda states. Element 0 in the array is the dHdL
+     * of the current state and elements 1..numLambdas contain the dHdL of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[in]     forceWorkBuffer     Force work buffer, values only used internally.
+     * \param[in,out] force               Bias force vector to set.
+     */
+    void calcConvolvedForce(ArrayRef<const DimParams> dimParams,
+                            const BiasGrid&           grid,
+                            ArrayRef<const double>    probWeightNeighbor,
+                            ArrayRef<const double>    neighborLambdaDhdl,
+                            ArrayRef<double>          forceWorkBuffer,
+                            ArrayRef<double>          force) const;
+
+    /*! \brief
+     * Move the center point of the umbrella potential.
+     *
+     * A new umbrella center is sampled from the biased distibution. Also, the bias
+     * force is updated and the new potential is return.
+     *
+     * This function should only be called when the bias force is not being convolved.
+     * It is assumed that the probability distribution has been updated.
+     *
+     * \param[in] dimParams                   Bias dimension parameters.
+     * \param[in] grid                        The grid.
+     * \param[in] probWeightNeighbor          Probability weights of the neighbors.
+     * \param[in] neighborLambdaDhdl          An array containing the dHdL at the neighboring lambda
+     * points. The array is of length numLambdas+1, where numLambdas is the number of free
+     * energy lambda states. Element 0 in the array is the dHdL
+     * of the current state and elements 1..numLambdas contain the dHdL of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[in,out] biasForce               The AWH bias force.
+     * \param[in] step                        Step number, needed for the random number generator.
+     * \param[in] seed                        Random seed.
+     * \param[in] indexSeed                   Second random seed, should be the bias Index.
+     * \param[in] onlySampleUmbrellaGridpoint Only sample the umbrella gridpoint without calculating
+     * force and potential.
+     * \returns the new potential value.
+     */
+    double moveUmbrella(ArrayRef<const DimParams> dimParams,
+                        const BiasGrid&           grid,
+                        ArrayRef<const double>    probWeightNeighbor,
+                        ArrayRef<const double>    neighborLambdaDhdl,
+                        ArrayRef<double>          biasForce,
+                        int64_t                   step,
+                        int64_t                   seed,
+                        int                       indexSeed,
+                        bool                      onlySampleUmbrellaGridpoint);
+
+private:
+    /*! \brief
+     * Gets the histogram rescaling factors needed for skipped updates.
+     *
+     * \param[in]  params             The bias parameters.
+     * \param[out] weighthistScaling  Scaling factor for the reference weight histogram.
+     * \param[out] logPmfsumScaling   Log of the scaling factor for the PMF histogram.
+     */
+    void getSkippedUpdateHistogramScaleFactors(const BiasParams& params,
+                                               double*           weighthistScaling,
+                                               double*           logPmfsumScaling) const;
+
+public:
+    /*! \brief
+     * Do all previously skipped updates.
+     * Public for use by tests.
+     *
+     * \param[in] params  The bias parameters.
+     */
+    void doSkippedUpdatesForAllPoints(const BiasParams& params);
+
+    /*! \brief
+     * Do previously skipped updates in this neighborhood.
+     *
+     * \param[in] params  The bias parameters.
+     * \param[in] grid    The grid.
+     */
+    void doSkippedUpdatesInNeighborhood(const BiasParams& params, const BiasGrid& grid);
+
+private:
+    /*! \brief
+     * Reset the range used to make the local update list.
+     *
+     * \param[in] grid  The grid.
+     */
+    void resetLocalUpdateRange(const BiasGrid& grid);
+
+    /*! \brief
+     * Returns the new size of the reference weight histogram in the initial stage.
+     *
+     * This function also takes care resetting the histogram used for covering checks
+     * and for exiting the initial stage.
+     *
+     * \param[in]     params            The bias parameters.
+     * \param[in]     t                 Time.
+     * \param[in]     detectedCovering  True if we detected that the sampling interval has been sufficiently covered.
+     * \param[in,out] fplog             Log file.
+     * \returns the new histogram size.
+     */
+    double newHistogramSizeInitialStage(const BiasParams& params, double t, bool detectedCovering, FILE* fplog);
+
+    /*! \brief
+     * Check if the sampling region has been covered "enough" or not.
+     *
+     * A one-dimensional interval is defined as covered if each point has
+     * accumulated the same weight as is in the peak of a discretized normal
+     * distribution. For multiple dimensions, the weights are simply projected
+     * onto each dimension and the multidimensional space is covered if each
+     * dimension is.
+     *
+     * \note The covering criterion for multiple dimensions could improved, e.g.
+     * by using a path finding algorithm.
+     *
+     * \param[in] params        The bias parameters.
+     * \param[in] dimParams     Bias dimension parameters.
+     * \param[in] grid          The grid.
+     * \returns true if covered.
+     */
+    bool isSamplingRegionCovered(const BiasParams&         params,
+                                 ArrayRef<const DimParams> dimParams,
+                                 const BiasGrid&           grid) const;
+
+    /*! \brief
+     * Return the new reference weight histogram size for the current update.
+     *
+     * This function also takes care of checking for covering in the initial stage.
+     *
+     * \param[in]     params   The bias parameters.
+     * \param[in]     t        Time.
+     * \param[in]     covered  True if the sampling interval has been covered enough.
+     * \param[in,out] fplog    Log file.
+     * \returns the new histogram size.
+     */
+    double newHistogramSize(const BiasParams& params, double t, bool covered, FILE* fplog);
+
+public:
+    /*! \brief
+     * Update the reaction coordinate value.
+     *
+     * \param[in] grid        The bias grid.
+     * \param[in] coordValue  The current reaction coordinate value (there are no limits on allowed values).
+     */
+    void setCoordValue(const BiasGrid& grid, const awh_dvec coordValue)
+    {
+        coordState_.setCoordValue(grid, coordValue);
+    }
+
+    /*! \brief
+     * Performs an update of the bias.
+     *
+     * The objective of the update is to use collected samples (probability weights)
+     * to improve the free energy estimate. For sake of efficiency, the update is
+     * local whenever possible, meaning that only points that have actually been sampled
+     * are accessed and updated here. For certain AWH settings or at certain steps
+     * however, global need to be performed. Besides the actual free energy update, this
+     * function takes care of ensuring future convergence of the free energy. Convergence
+     * is obtained by increasing the size of the reference weight histogram in a controlled
+     * (sometimes dynamic) manner. Also, there are AWH variables that are direct functions
+     * of the free energy or sampling history that need to be updated here, namely the target
+     * distribution and the bias function.
+     *
+     * \param[in]     dimParams   The dimension parameters.
+     * \param[in]     grid        The grid.
+     * \param[in]     params      The bias parameters.
+     * \param[in]     t           Time.
+     * \param[in]     step        Time step.
+     * \param[in,out] fplog       Log file.
+     * \param[in,out] updateList  Work space to store a temporary list.
+     */
+    void updateFreeEnergyAndAddSamplesToHistogram(ArrayRef<const DimParams> dimParams,
+                                                  const BiasGrid&           grid,
+                                                  const BiasParams&         params,
+                                                  double                    t,
+                                                  int64_t                   step,
+                                                  FILE*                     fplog,
+                                                  std::vector<int>*         updateList);
+
+    /*! \brief
+     * Update the probability weights and the convolved bias.
+     *
+     * Given a coordinate value, each grid point is assigned a probability
+     * weight, w(point|value), that depends on the current bias function. The sum
+     * of these weights is needed for normalizing the probability sum to 1 but
+     * also equals the effective, or convolved, biasing weight for this coordinate
+     * value. The convolved bias is needed e.g. for extracting the PMF, so we save
+     * it here since this saves us from doing extra exponential function evaluations
+     * later on.
+     *
+     * \param[in]  dimParams              The bias dimensions parameters
+     * \param[in]  grid                   The grid.
+     * \param[in]  neighborLambdaEnergies An array containing the energy of the system
+     * in neighboring lambdas. The array is of length numLambdas+1, where numLambdas is
+     * the number of free energy lambda states. Element 0 in the array is the energy
+     * of the current state and elements 1..numLambdas contain the energy of the system in the
+     * neighboring lambda states (also including the current state). When there are no free
+     * energy lambda state dimensions this can be empty.
+     * \param[out] weight                 Probability weights of the neighbors, SIMD aligned.
+     * \returns the convolved bias.
+     */
+
+    double updateProbabilityWeightsAndConvolvedBias(ArrayRef<const DimParams> dimParams,
+                                                    const BiasGrid&           grid,
+                                                    ArrayRef<const double> neighborLambdaEnergies,
+                                                    std::vector<double, AlignedAllocator<double>>* weight) const;
+
+    /*! \brief
+     * Take samples of the current probability weights for future updates and analysis.
+     *
+     * Points in the current neighborhood will now have data meaning they
+     * need to be included in the local update list of the next update.
+     * Therefore, the local update range is also update here.
+     *
+     * \param[in] grid                The grid.
+     * \param[in] probWeightNeighbor  Probability weights of the neighbors.
+     */
+    void sampleProbabilityWeights(const BiasGrid& grid, ArrayRef<const double> probWeightNeighbor);
+
+    /*! \brief
+     * Sample the reaction coordinate and PMF for future updates or analysis.
+     *
+     * These samples do not affect the (future) sampling and are thus
+     * pure observables. Statisics of these are stored in the energy file.
+     *
+     * \param[in] dimParams           The bias dimensions parameters
+     * \param[in] grid                The grid.
+     * \param[in] probWeightNeighbor  Probability weights of the neighbors.
+     * \param[in] convolvedBias       The convolved bias.
+     */
+    void sampleCoordAndPmf(const std::vector<DimParams>& dimParams,
+                           const BiasGrid&               grid,
+                           ArrayRef<const double>        probWeightNeighbor,
+                           double                        convolvedBias);
+    /*! \brief
+     * Calculates the convolved bias for a given coordinate value.
+     *
+     * The convolved bias is the effective bias acting on the coordinate.
+     * Since the bias here has arbitrary normalization, this only makes
+     * sense as a relative, to other coordinate values, measure of the bias.
+     *
+     * \note If it turns out to be costly to calculate this pointwise
+     * the convolved bias for the whole grid could be returned instead.
+     *
+     * \param[in] dimParams   The bias dimensions parameters
+     * \param[in] grid        The grid.
+     * \param[in] coordValue  Coordinate value.
+     * \returns the convolved bias >= -GMX_FLOAT_MAX.
+     */
+    double calcConvolvedBias(ArrayRef<const DimParams> dimParams,
+                             const BiasGrid&           grid,
+                             const awh_dvec&           coordValue) const;
+
+    /*! \brief
+     * Fills the given array with PMF values.
+     *
+     * Points outside of the biasing target region will get PMF = GMX_FLOAT_MAX.
+     * \note: The PMF is in single precision, because it is a statistical
+     *        quantity and therefore never reaches full float precision.
+     *
+     * \param[out] pmf  Array(ref) to be filled with the PMF values, should have the same size as the bias grid.
+     */
+    void getPmf(ArrayRef<float> /*pmf*/) const;
+
+    /*! \brief Returns the current coordinate state.
+     */
+    const CoordState& coordState() const { return coordState_; }
+
+    /*! \brief Returns a const reference to the point state.
+     */
+    const std::vector<PointState>& points() const { return points_; }
+
+    /*! \brief Returns true if we are in the initial stage.
+     */
+    bool inInitialStage() const { return histogramSize_.inInitialStage(); }
+
+    /*! \brief Returns the current histogram size.
+     */
+    inline HistogramSize histogramSize() const { return histogramSize_; }
+
+    /*! \brief Sets the umbrella grid point to the current grid point
+     */
+    void setUmbrellaGridpointToGridpoint() { coordState_.setUmbrellaGridpointToGridpoint(); }
+
+    /* Data members */
+private:
+    CoordState coordState_; /**< The Current coordinate state */
+
+    /* The grid point state */
+    std::vector<PointState> points_; /**< Vector of state of the grid points */
+
+    /* Covering values for each point on the grid */
+    std::vector<double> weightSumCovering_; /**< Accumulated weights for covering checks */
+
+    HistogramSize histogramSize_; /**< Global histogram size related values. */
+
+    /* Track the part of the grid sampled since the last update. */
+    awh_ivec originUpdatelist_; /**< The origin of the rectangular region that has been sampled since last update. */
+    awh_ivec endUpdatelist_; /**< The end of the rectangular region that has been sampled since last update. */
+
+    //! Object for sharing biases over multiple simulations, can be nullptr
+    const BiasSharing* biasSharing_;
+};
+
+//! Linewidth used for warning output
+static const int c_linewidth = 80 - 2;
+
+//! Indent used for warning output
+static const int c_indent = 0;
+
+} // namespace gmx
+
+#endif /* GMX_AWH_BIASSTATE_H */
diff --git a/src/include/gromacs/applied_forces/awh/biaswriter.h b/src/include/gromacs/applied_forces/awh/biaswriter.h
new file mode 100644 (file)
index 0000000..20451d9
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * This file contains the BiasWriter class that prepares and writes data of a Bias to an energy file.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_BIASWRITER_H
+#define GMX_AWH_BIASWRITER_H
+
+#include <map>
+#include <vector>
+
+#include "gromacs/fileio/enxio.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_multisim_t;
+struct t_enxsubblock;
+
+namespace gmx
+{
+class Bias;
+
+/* TODO: the post-simulations AWH reader and this AWH writer are totally
+ * disconnected although they read/write the same data. I'm not sure how
+ * to handle that or if it should be left as it is until the writing is done
+ * in a differen format (i.e. TNG) than the current energy file.
+ */
+
+//! Enum with the AWH variables to write.
+enum class AwhOutputEntryType
+{
+    MetaData,               //!< Meta data.
+    CoordValue,             //!< Coordinate value.
+    Pmf,                    //!< The pmf.
+    Bias,                   //!< The bias.
+    Visits,                 //!< The number of visits.
+    Weights,                //!< The weights.
+    Target,                 //!< The target distribition.
+    ForceCorrelationVolume, //!< The volume of the force correlation tensor.
+    FrictionTensor          //!< The full friction tensor.
+};
+
+//! Enum with the types of metadata to write.
+enum class AwhOutputMetaData
+{
+    NumBlock,    //!< The number of blocks.
+    TargetError, //!< The target error.
+    ScaledSampleWeight, //!< The logarithm of the sample weight relative to a sample weight of 1 at the initial time.
+    Count //!< The number of enum values, not including Count.
+};
+
+//! Enum with different ways of normalizing the output.
+enum class Normalization
+{
+    None,        //!< No normalization.
+    Coordinate,  //!< Scale using the internal/user input coordinate scaling factor.
+    FreeEnergy,  //!< Normalize free energy values by subtracting the minimum value.
+    Distribution //!< Normalize the distribution to 1.
+};
+
+/*! \internal \brief AWH output data block that can be written to an energy file block.
+ */
+class AwhEnergyBlock
+{
+public:
+    /*! \brief Constructor
+     *
+     * \param[in] numPoints           Number of points in block.
+     * \param[in] normalizationType   Type of normalization.
+     * \param[in] normalizationValue  Value to normalization with.
+     */
+    AwhEnergyBlock(int numPoints, Normalization normalizationType, float normalizationValue);
+
+    /*! \brief Returns an ArrarRef to the data in the block.
+     */
+    gmx::ArrayRef<float> data() { return data_; }
+
+    const Normalization normalizationType;  /**< How to normalize the output data */
+    const float         normalizationValue; /**< The normalization value */
+private:
+    std::vector<float> data_; /**< The data, always float which is enough since this is statistical data */
+};
+
+/*! \internal \brief Class organizing the output data storing and writing of an AWH bias.
+ */
+class BiasWriter
+{
+public:
+    /*! \brief Constructor.
+     *
+     * \param[in] bias  The AWH bias.
+     */
+    BiasWriter(const Bias& bias);
+
+    /*! \brief Returns the number of data blocks.
+     *
+     * \returns the number of data blocks.
+     */
+    int numBlocks() const { return block_.size(); }
+
+    /*! \brief Collect AWH bias data in blocks and write to energy subblocks.
+     *
+     * \param[in]     bias      The AWH Bias.
+     * \param[in,out] subblock  Energy subblocks to write to.
+     * \returns the number of blocks written.
+     */
+    int writeToEnergySubblocks(const Bias& bias, t_enxsubblock* subblock);
+
+private:
+    /*! \brief Query if the writer has a block for the given variable.
+     *
+     * \param[in] outputType  Output entry type.
+     */
+    bool hasVarBlock(AwhOutputEntryType outputType) const
+    {
+        return outputTypeToBlock_.find(outputType)->second >= 0;
+    }
+
+    /*! \brief* Find the first block containing the given variable.
+     *
+     * \param[in] outputType  Output entry type.
+     * \returns the first block index for the variable, or -1 there is no block.
+     */
+    int getVarStartBlock(AwhOutputEntryType outputType) const
+    {
+        return outputTypeToBlock_.find(outputType)->second;
+    }
+
+    /*! \brief Transfer AWH point data to writer data blocks.
+     *
+     * \param[in] metaDataIndex  Meta data block index.
+     * \param[in] metaDataType   The type of meta data to write.
+     * \param[in] bias           The AWH Bias.
+     */
+    void transferMetaDataToWriter(gmx::index metaDataIndex, AwhOutputMetaData metaDataType, const Bias& bias);
+
+    /*! \brief Transfer AWH point data to writer data blocks.
+     *
+     * \param[in] outputType  Output entry type.
+     * \param[in] pointIndex  The point index.
+     * \param[in] bias        The AWH Bias.
+     * \param[in] pmf         PMF values.
+     */
+    void transferPointDataToWriter(AwhOutputEntryType         outputType,
+                                   int                        pointIndex,
+                                   const Bias&                bias,
+                                   gmx::ArrayRef<const float> pmf);
+
+    /*! \brief
+     * Prepare the bias output data.
+     *
+     * \param[in] bias  The AWH Bias.
+     */
+    void prepareBiasOutput(const Bias& bias);
+
+    std::vector<AwhEnergyBlock>       block_; /**< The data blocks */
+    std::map<AwhOutputEntryType, int> outputTypeToBlock_; /**< Start block index for each variable, -1 when variable should not be written */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_BIASWRITER_H */
diff --git a/src/include/gromacs/applied_forces/awh/coordstate.h b/src/include/gromacs/applied_forces/awh/coordstate.h
new file mode 100644 (file)
index 0000000..d9dff8f
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the CoordState class.
+ *
+ * It sets and holds the current coordinate value and corresponding closest
+ * grid point index. These are (re)set at every step.
+ * With umbrella potential type, this class also holds and updates the umbrella
+ * potential reference location, which is a state variable that presists over
+ * the duration of an AWH sampling interval.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_COORDSTATE_H
+#define GMX_AWH_COORDSTATE_H
+
+#include <vector>
+
+#include "dimparams.h"
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+class AwhBiasParams;
+struct AwhBiasStateHistory;
+class BiasParams;
+class BiasGrid;
+
+/*! \internal \brief Keeps track of the current coordinate value, grid index and umbrella location.
+ */
+class CoordState
+{
+public:
+    /*! \brief Constructor.
+     *
+     * \param[in] awhBiasParams  The Bias parameters from inputrec.
+     * \param[in] dimParams      The dimension Parameters.
+     * \param[in] grid           The grid.
+     */
+    CoordState(const AwhBiasParams& awhBiasParams, ArrayRef<const DimParams> dimParams, const BiasGrid& grid);
+
+    /*! \brief
+     * Sample a new umbrella reference point given the current coordinate value.
+     *
+     * It is assumed that the probability distribution has been updated.
+     *
+     * \param[in] grid                The grid.
+     * \param[in] gridpointIndex      The grid point, sets the neighborhood.
+     * \param[in] probWeightNeighbor  Probability weights of the neighbors.
+     * \param[in] step                Step number, needed for the random number generator.
+     * \param[in] seed                Random seed.
+     * \param[in] indexSeed           Second random seed, should be the bias Index.
+     * \returns the index of the sampled point.
+     */
+    void sampleUmbrellaGridpoint(const BiasGrid&             grid,
+                                 int                         gridpointIndex,
+                                 gmx::ArrayRef<const double> probWeightNeighbor,
+                                 int64_t                     step,
+                                 int64_t                     seed,
+                                 int                         indexSeed);
+
+    /*! \brief Update the coordinate value with coordValue.
+     *
+     * \param[in] grid        The grid.
+     * \param[in] coordValue  The new coordinate value.
+     */
+    void setCoordValue(const BiasGrid& grid, const awh_dvec coordValue);
+
+    /*! \brief Restores the coordinate state from history.
+     *
+     * \param[in] stateHistory  The AWH bias state history.
+     */
+    void restoreFromHistory(const AwhBiasStateHistory& stateHistory);
+
+    /*! \brief Returns the current coordinate value.
+     */
+    const awh_dvec& coordValue() const { return coordValue_; }
+
+    /*! \brief Returns the grid point index for the current coordinate value.
+     */
+    int gridpointIndex() const { return gridpointIndex_; }
+
+    /*! \brief Returns the index for the current reference grid point.
+     */
+    int umbrellaGridpoint() const { return umbrellaGridpoint_; }
+
+    /*! \brief Sets the umbrella grid point to the current grid point
+     */
+    void setUmbrellaGridpointToGridpoint();
+
+private:
+    awh_dvec coordValue_;        /**< Current coordinate value in (nm or rad) */
+    int      gridpointIndex_;    /**< The grid point index for the current coordinate value */
+    int      umbrellaGridpoint_; /**< Index for the current reference grid point for the umbrella, only used with umbrella potential type */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_COORDSTATE_H */
diff --git a/src/include/gromacs/applied_forces/awh/correlationgrid.h b/src/include/gromacs/applied_forces/awh/correlationgrid.h
new file mode 100644 (file)
index 0000000..eaaed1c
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the CorrelationGrid class to collect correlation statistics on a grid, using several block lengths.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_CORRELATIONGRID_H
+#define GMX_AWH_CORRELATIONGRID_H
+
+#include <cstddef>
+
+#include <vector>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "correlationtensor.h"
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+struct CorrelationGridHistory;
+
+/*! \internal
+ * \brief BiasGrid of local correlation tensors.
+ *
+ * This class provides the means for a bias to interaction with the grid
+ * of correlation tensors. The grid should have the same number of points
+ * and the same dimensionality as the bias grid.
+ */
+class CorrelationGrid
+{
+public:
+    //! Enum that sets how we measure block length.
+    enum class BlockLengthMeasure
+    {
+        Time,  //!< Measure block length in time.
+        Weight //!< Measure block length in sampled weight.
+    };
+
+    /*! \brief Constructor.
+     *
+     * \param[in] numPoints           Number of points in the grid.
+     * \param[in] numDims             Number of dimensions of the grid.
+     * \param[in] blockLengthInit     Initial length of the blocks used for block averaging.
+     * \param[in] blockLengthMeasure  Sets how we measure block length.
+     * \param[in] dtSample            Time step for sampling correlations.
+     */
+    CorrelationGrid(int                numPoints,
+                    int                numDims,
+                    double             blockLengthInit,
+                    BlockLengthMeasure blockLengthMeasure,
+                    double             dtSample);
+
+    /*! \brief Adds a weighted data vector to one point in the correlation grid.
+     *
+     * \param[in] pointIndex  Index of the point to add data to.
+     * \param[in] weight      Weight to assign to the data.
+     * \param[in] data        One data point for each grid dimension.
+     * \param[in] t           The time when the data was sampled.
+     */
+    void addData(int pointIndex, double weight, gmx::ArrayRef<const double> data, double t)
+    {
+        tensors_[pointIndex].addData(weight, data, blockLengthMeasure == BlockLengthMeasure::Weight, t);
+    }
+
+    /*! \brief Restores the correlation grid state from the correlation grid history.
+     *
+     * The setup in the history should match that of this simulation.
+     * If this is not the case, an exception is thrown.
+     *
+     * \param[in] correlationGridHistory  The correlation grid state history.
+     */
+    void restoreStateFromHistory(const CorrelationGridHistory& correlationGridHistory);
+
+    /*! \brief Returns the number of elements in the tensor: dim*(dim+1)/2.
+     */
+    int tensorSize() const
+    {
+        GMX_RELEASE_ASSERT(!tensors_.empty(), "Should only call tensorSize on a valid grid");
+
+        return tensors_[0].blockDataList()[0].correlationIntegral().size();
+    }
+
+    /*! \brief Returns the size of the block data list.
+     */
+    int blockDataListSize() const
+    {
+        GMX_RELEASE_ASSERT(!tensors_.empty(), "Should only call tensorSize on a valid grid");
+
+        return tensors_[0].blockDataList().size();
+    }
+
+    /*! \brief Get a const reference to the correlation grid data.
+     */
+    const std::vector<CorrelationTensor>& tensors() const { return tensors_; }
+
+    /* Right now the below functions are only used for an initial log printing. */
+
+    /*! \brief Get the current blocklength.
+     */
+    double getBlockLength() const;
+
+    /*! \brief Get the current number of blocks.
+     *
+     * If we have a finite block span we have a constant number of blocks,
+     * otherwise we are always adding more blocks (and we don't keep
+     * track of the number), so we return -1.
+     */
+    int getNumBlocks() const;
+
+    const double             dtSample;           /**< Time in between samples. */
+    const BlockLengthMeasure blockLengthMeasure; /**< The measure for the block length. */
+private:
+    std::vector<CorrelationTensor> tensors_; /**< Correlation tensor grid */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_CORRELATIONGRID_H */
diff --git a/src/include/gromacs/applied_forces/awh/correlationhistory.h b/src/include/gromacs/applied_forces/awh/correlationhistory.h
new file mode 100644 (file)
index 0000000..05ac528
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Contains datatypes and function declarations needed by AWH to
+ * have its force correlation data checkpointed.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_CORRELATIONHISTORY_H
+#define GMX_AWH_CORRELATIONHISTORY_H
+
+struct t_commrec;
+
+namespace gmx
+{
+class CorrelationGrid;
+struct CorrelationGridHistory;
+
+/*! \brief
+ * Allocate a correlation grid history with the same structure as the given correlation grid.
+ *
+ * This function would be called at the start of a new simulation.
+ * Note that only sizes and memory are initialized here.
+ * History data is set by \ref updateCorrelationGridHistory.
+ *
+ * \param[in,out] corrGrid      Correlation grid state to initialize with.
+ * \returns the correlation grid history struct.
+ */
+CorrelationGridHistory initCorrelationGridHistoryFromState(const CorrelationGrid& corrGrid);
+
+/*! \brief
+ * Restores the correlation grid state from the correlation grid history.
+ *
+ * \param[in] corrGridHist  Correlation grid history to read.
+ * \param[in,out] corrGrid  Correlation grid state to set.
+ */
+void restoreCorrelationGridStateFromHistory(const CorrelationGridHistory& corrGridHist,
+                                            CorrelationGrid*              corrGrid);
+
+/*! \brief
+ * Update the correlation grid history for checkpointing.
+ *
+ * \param[in,out] corrGridHist  Correlation grid history to set.
+ * \param[in]     corrGrid      Correlation grid state to read.
+ */
+void updateCorrelationGridHistory(CorrelationGridHistory* corrGridHist, const CorrelationGrid& corrGrid);
+
+} // namespace gmx
+
+#endif /* GMX_AWH_CORRELATIONHISTORY_H */
diff --git a/src/include/gromacs/applied_forces/awh/correlationtensor.h b/src/include/gromacs/applied_forces/awh/correlationtensor.h
new file mode 100644 (file)
index 0000000..9da3928
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the CorrelationTensor class for correlation tensor statistics.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_CORRELATIONTENSOR_H
+#define GMX_AWH_CORRELATIONTENSOR_H
+
+#include <cstddef>
+
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+struct CorrelationBlockDataHistory;
+
+/*! \internal \brief Correlation block averaging weight-only data.
+ */
+class CorrelationBlockData
+{
+public:
+    /*! \internal \brief Correlation block averaging data.
+     */
+    struct CoordData
+    {
+        /*! \brief Constructor.
+         */
+        CoordData() : blockSumWeightX(0), sumOverBlocksBlockWeightBlockWeightX(0) {}
+
+        double blockSumWeightX; /**< Weighted sum of x for current block. */
+        double sumOverBlocksBlockWeightBlockWeightX; /**< Sum over all blocks in the simulation of block weight times sum_wx. */
+    };
+
+    /*! \brief Constructor.
+     *
+     * \param[in] numDim           The dimensionality.
+     * \param[in] blockLengthInit  The initial block length.
+     */
+    CorrelationBlockData(int numDim, double blockLengthInit) :
+        blockSumWeight_(0),
+        blockSumSquareWeight_(0),
+        sumOverBlocksSquareBlockWeight_(0),
+        sumOverBlocksBlockSquareWeight_(0),
+        blockLength_(blockLengthInit),
+        previousBlockIndex_(-1),
+        coordData_(numDim),
+        correlationIntegral_(numDim * (numDim + 1) / 2)
+    {
+    }
+
+    /*! \brief Restore the state from history.
+     *
+     * \param[in] blockHistory         The block data history containing the weight sums.
+     * \param[in] coordData            The coordinate data.
+     * \param[in] correlationIntegral  The correlation integral for all tensor elements.
+     */
+    void restoreFromHistory(const CorrelationBlockDataHistory& blockHistory,
+                            const std::vector<CoordData>&      coordData,
+                            const std::vector<double>&         correlationIntegral);
+
+    /*! \brief Adds a weighted data vector to one point in the correlation grid.
+     *
+     * \param[in] weight  The weight of the data.
+     * \param[in] data    One data point for each grid dimension.
+     */
+    void addData(double weight, gmx::ArrayRef<const double> data)
+    {
+        GMX_ASSERT(data.size() == coordData_.size(),
+                   "Size of data should match the size of coordData");
+
+        blockSumWeight_ += weight;
+        blockSumSquareWeight_ += weight * weight;
+
+        for (size_t d = 0; d < coordData_.size(); d++)
+        {
+            coordData_[d].blockSumWeightX += weight * data[d];
+        }
+    }
+
+    /*! \brief Adds a filled data block to correlation time integral.
+     */
+    void addBlockToCorrelationIntegral();
+
+    /*! \brief Returns the sum weights for current block. */
+    double blockSumWeight() const { return blockSumWeight_; }
+
+    /*! \brief Returns the sum weights^2 for current block. */
+    double blockSumSquareWeight() const { return blockSumSquareWeight_; }
+
+    /*! \brief Returns the sum over blocks of block weight^2. */
+    double sumOverBlocksSquareBlockWeight() const { return sumOverBlocksSquareBlockWeight_; }
+
+    /*! \brief Returns the sum over blocks of weight^2. */
+    double sumOverBlocksBlockSquareWeight() const { return sumOverBlocksBlockSquareWeight_; }
+
+    /*! \brief Returns the length of each block used for block averaging. */
+    double blockLength() const { return blockLength_; }
+
+    /*! \brief Double the length of each block used for block averaging. */
+    void doubleBlockLength() { blockLength_ *= 2; }
+
+    /*! \brief Return the last block index data was added to (needed only for block length in terms of time). */
+    int previousBlockIndex() const { return previousBlockIndex_; }
+
+    /*! \brief Set the last block index data was added to.
+     *
+     * \param[in] blockIndex  The block index.
+     */
+    void setPreviousBlockIndex(int blockIndex) { previousBlockIndex_ = blockIndex; }
+
+    /*! \brief Return sums for each coordinate dimension. */
+    const std::vector<CoordData>& coordData() const { return coordData_; }
+
+    /*! \brief Return the correlation integral tensor. */
+    const std::vector<double>& correlationIntegral() const { return correlationIntegral_; }
+
+private:
+    /* Weight sum data, indentical for all dimensions */
+    double blockSumWeight_;                 /**< Sum weights for current block. */
+    double blockSumSquareWeight_;           /**< Sum weights^2 for current block. */
+    double sumOverBlocksSquareBlockWeight_; /**< Sum over all blocks in the simulation of block weight^2. */
+    double sumOverBlocksBlockSquareWeight_; /**< Sum over all blocks in the simulation of weight^2. */
+    double blockLength_; /**< The length of each block used for block averaging */
+    int    previousBlockIndex_; /**< The last block index data was added to (needed only for block length in terms of time). */
+
+    /* Sums for each coordinate dimension. */
+    std::vector<CoordData> coordData_; /**< Array with sums for each coordinate dimension. */
+
+    /* Correlation tensor. */
+    std::vector<double> correlationIntegral_; /**< Array with the correlation elements corr(x, y) in the tensor, where x, y are vector components. */
+};
+
+/*! \internal
+ * \brief Correlation data for computing the correlation tensor of one grid point.
+ *
+ * The time integrated autocorrelation of the desired quantity is computed using
+ * block averages, which is a computationally efficient and low memory method.
+ * Most of the work here goes into computing the block averages for weights
+ * and the coordinate quantity. This is done for a number of blocks in
+ * the range of \p c_numCorrelationBlocks/2 + 1 to \p c_numCorrelationBlocks,
+ * depending on the current simulation length. As the simulation time
+ * progresses, the blocks get longer. This is implemented in an efficient
+ * way by keeping track of log2(\p c_numCorrelationBlocks) \p BlockData
+ * data blocks with block length increasing progressively by a factor of 2.
+ * Once \p c_numCorrelationBlocks are reached, all block lengths are doubled.
+ */
+class CorrelationTensor
+{
+public:
+    /*! \brief 64 blocks is a good trade-off between signal and noise */
+    static constexpr int c_numCorrelationBlocks = 64;
+
+    /*! \brief Constructor.
+     *
+     * \param[in] numDim          The dimensionality.
+     * \param[in] numBlockData     The number of data block data structs.
+     * \param[in] blockLengthInit  The initial block length.
+     */
+    CorrelationTensor(int numDim, int numBlockData, double blockLengthInit);
+
+    /*! \brief Get a const reference to the list of block data.
+     */
+    const std::vector<CorrelationBlockData>& blockDataList() const { return blockDataList_; }
+
+    /*! \brief
+     * Get the total weight of the data in the correlation matrix.
+     *
+     * \returns the weight of the added data.
+     */
+    double getWeight() const
+    {
+        /* The last blockdata has only 1 block containing all data */
+        return blockDataList().back().blockSumWeight();
+    }
+
+    /*! \brief Restore a correlation element from history.
+     *
+     * \param[in]     blockDataBuffer  The linear correlation grid data history buffer.
+     * \param[in,out] bufferIndex      The index in \p blockDataBuffer to start reading, is increased with the number of blocks read.
+     */
+    void restoreFromHistory(const std::vector<CorrelationBlockDataHistory>& blockDataBuffer,
+                            size_t*                                         bufferIndex);
+
+private:
+    /*! \brief Updates the block length by doubling.
+     *
+     * The length of all blocks is doubled. This is achieved by removing
+     * the shortest block, moving all other blocks and duplicating
+     * the data of longest block to a nw block of double length (but
+     * currenly only half filled with data).
+     */
+    void doubleBlockLengths();
+
+    /*! \brief Updates the block length such that data fits.
+     *
+     * \param[in] samplingLength  Sampling length of all data, in time or weight.
+     */
+    void updateBlockLengths(double samplingLength);
+
+public:
+    /*! \brief Adds a weighted data vector to one point in the correlation grid.
+     *
+     * \note To avoid rounding noise, data with weight smaller than 1e-6
+     *       is ignored.
+     *
+     * \param[in] weight               The weight of the data.
+     * \param[in] data                 One data point for each grid dimension.
+     * \param[in] blockLengthInWeight  If true, a block is measured in probability weight, otherwise
+     * in time. \param[in] t                    The simulation time.
+     */
+    void addData(double weight, gmx::ArrayRef<const double> data, bool blockLengthInWeight, double t);
+
+    /*! \brief Returns an element of the time integrated correlation tensor at a given point in the grid.
+     *
+     * The units of the integral are time*(units of data)^2. This will be friction units time/length^2
+     * if the data unit is 1/length.
+     *
+     * The correlation index lists the elements of the upper-triangular
+     * correlation matrix row-wise, so e.g. in 3D:
+     * 0 (0,0), 1 (1,0), 2 (1,1), 3 (2,0), 4 (2,1), 5 (2,2).
+     * (TODO: this should ideally not have to be known by the caller.)
+     *
+     * \param[in] tensorIndex  Index in the tensor.
+     * \param[in] dtSample     The sampling interval length.
+     * \returns the integral.
+     */
+    double getTimeIntegral(int tensorIndex, double dtSample) const;
+
+    /*! \brief
+     * Returns the volume element of the correlation metric.
+     *
+     * The matrix of the metric equals the time-integrated correlation matrix. The volume element of
+     * the metric therefore equals the square-root of the absolute value of its determinant
+     * according to the standard formula for a volume element in a metric space.
+     *
+     * Since the units of the metric matrix elements are time*(units of data)^2, the volume element
+     * has units of (sqrt(time)*(units of data))^(ndim of data).
+     *
+     * \param[in] dtSample  The sampling interval length.
+     * \returns the volume element.
+     */
+    double getVolumeElement(double dtSample) const;
+
+private:
+    std::vector<CorrelationBlockData> blockDataList_; /**< The block data for different, consecutively doubling block lengths. */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_CORRELATIONTENSOR_H */
diff --git a/src/include/gromacs/applied_forces/awh/dimparams.h b/src/include/gromacs/applied_forces/awh/dimparams.h
new file mode 100644 (file)
index 0000000..cf536b8
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the DimParams struct and AWH vector types.
+ *
+ * This class holds the physical information for a dimension
+ * of the bias reaction-coordinate grid.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_DIMPARAMS_H
+#define GMX_AWH_DIMPARAMS_H
+
+#include <variant>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+
+//! The maximum dimensionality of the AWH coordinate.
+static const int c_biasMaxNumDim = 4;
+
+//! A real vector in AWH coordinate space.
+typedef double awh_dvec[c_biasMaxNumDim];
+
+//! An integer vector in AWH coordinate space.
+typedef int awh_ivec[c_biasMaxNumDim];
+
+/*! \internal \brief Constant parameters for each dimension of the coordinate.
+ */
+struct DimParams
+{
+    /*! \internal \brief Type for storing dimension parameters for pull type dimensions
+     */
+    struct PullDimParams
+    {
+        const double k;     /**< Force constant (kJ/mol/nm^2) for each coordinate dimension. */
+        const double betak; /**< Inverse variance (1/nm^2) for each coordinate dimension. */
+        const double userCoordUnitsToInternal; /**< Conversion factor coordinate units. */
+    };
+
+    /*! \internal \brief Type for storing dimension parameters for free-energy lamdba type dimensions
+     */
+    struct FepDimParams
+    {
+        const double beta;               /**< 1/(k_B T). */
+        const int    numFepLambdaStates; /**< Number of lambda points in this dimension. */
+    };
+
+private:
+    /*! \brief
+     * Private constructor called by public builder functions for PullDimParams and FepLambdaDimParams.
+     */
+    DimParams(double conversionFactor, std::variant<PullDimParams, FepDimParams> dimParams) :
+        dimParams(std::move(dimParams)), userCoordUnitsToInternal(conversionFactor)
+    {
+    }
+
+public:
+    /*! \brief
+     * Builder function for pull dimension parameters.
+     *
+     * \param[in] conversionFactor  Conversion factor from user coordinate units to bias internal
+     * units (=c_deg2Rad for angles).
+     * \param[in] forceConstant     The harmonic force constant.
+     * \param[in] beta              1/(k_B T).
+     */
+    static DimParams pullDimParams(double conversionFactor, double forceConstant, double beta)
+    {
+        PullDimParams pullDimParams = { forceConstant, forceConstant * beta };
+
+        return DimParams(conversionFactor, pullDimParams);
+    }
+
+    /*! \brief
+     * Builder function for FEP lambda dimension parameters.
+     *
+     * \param[in] numFepLambdaStates  Number of lambda states in the system.
+     * \param[in] beta                1/(k_B T).
+     */
+    static DimParams fepLambdaDimParams(int numFepLambdaStates, double beta)
+    {
+        FepDimParams fepDimParams = { beta, numFepLambdaStates };
+
+        return DimParams(1.0, fepDimParams);
+    }
+
+    //! Returns whether this dimension is coupled to a pull coordinate.
+    bool isPullDimension() const { return std::holds_alternative<PullDimParams>(dimParams); }
+
+    //! Returns whether this dimension has lambda states and thereby is a dimension coupled to lambda.
+    bool isFepLambdaDimension() const { return std::holds_alternative<FepDimParams>(dimParams); }
+
+    //! Returns pull dimension parameters, only call for pull dimensions
+    const PullDimParams& pullDimParams() const { return std::get<PullDimParams>(dimParams); }
+
+    //! Returns FEP dimension parameters, only call for FEP dimensions
+    const FepDimParams& fepDimParams() const { return std::get<FepDimParams>(dimParams); }
+
+    /*! \brief Convert internal coordinate units to external, user coordinate units.
+     *
+     * \param[in] value               Value to convert.
+     * \returns the converted value.
+     */
+    double scaleInternalToUserInput(double value) const { return value / userCoordUnitsToInternal; }
+
+    /*! \brief Convert external, user coordinate units to internal coordinate units.
+     *
+     * \param[in] value               Value to convert.
+     * \returns the converted value.
+     */
+    double scaleUserInputToInternal(double value) const { return value * userCoordUnitsToInternal; }
+
+    //! Parameters for pull dimensions, either type pull or free-energy lambda
+    const std::variant<PullDimParams, FepDimParams> dimParams;
+    //! Conversion factor for ordinate units
+    const double userCoordUnitsToInternal;
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_DIMPARAMS_H */
diff --git a/src/include/gromacs/applied_forces/awh/histogramsize.h b/src/include/gromacs/applied_forces/awh/histogramsize.h
new file mode 100644 (file)
index 0000000..f0c5725
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the HistogramSize class.
+ *
+ * The data members of this class keep track of global size and update related
+ * properties of the bias histogram and the evolution of the histogram size.
+ * Initially histogramSize_ (and thus the convergence rate) is controlled
+ * heuristically to get good initial estimates,  i.e. increase the robustness
+ * of the method.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_HISTOGRAMSIZE_H
+#define GMX_AWH_HISTOGRAMSIZE_H
+
+#include <cstdio>
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+struct AwhBiasStateHistory;
+class AwhBiasParams;
+class BiasParams;
+class PointState;
+
+/*! \internal
+ * \brief Tracks global size related properties of the bias histogram.
+ *
+ * Tracks the number of updates and the histogram size.
+ * Also keep track of the stage (initial/final of the AWH method
+ * and printing warnings about covering.
+ *
+ * \note Histogram sizes are floating-point values, since the histogram uses weighted
+ *        entries and we can assign a floating-point scaling factor when changing it.
+ */
+class HistogramSize
+{
+public:
+    /*! \brief Constructor.
+     *
+     * \param[in] awhBiasParams         The Bias parameters from inputrec.
+     * \param[in] histogramSizeInitial  The initial histogram size.
+     */
+    HistogramSize(const AwhBiasParams& awhBiasParams, double histogramSizeInitial);
+
+private:
+    /*! \brief
+     * Returns the new size of the reference weight histogram in the initial stage.
+     *
+     * This function also takes care resetting the histogram used for covering checks
+     * and for exiting the initial stage.
+     *
+     * \param[in]     params             The bias parameters.
+     * \param[in]     t                  Time.
+     * \param[in]     detectedCovering   True if we detected that the sampling interval has been
+     * sufficiently covered. \param[in,out] weightsumCovering  The weight sum for checking covering.
+     * \param[in,out] fplog              Log file.
+     * \returns the new histogram size.
+     */
+    double newHistogramSizeInitialStage(const BiasParams& params,
+                                        double            t,
+                                        bool              detectedCovering,
+                                        ArrayRef<double>  weightsumCovering,
+                                        FILE*             fplog);
+
+public:
+    /*! \brief
+     * Return the new reference weight histogram size for the current update.
+     *
+     * This function also takes care of checking for covering in the initial stage.
+     *
+     * \param[in]     params             The bias parameters.
+     * \param[in]     t                  Time.
+     * \param[in]     covered            True if the sampling interval has been covered enough.
+     * \param[in]     pointStates        The state of the grid points.
+     * \param[in,out] weightsumCovering  The weight sum for checking covering.
+     * \param[in,out] fplog              Log file.
+     * \returns the new histogram size.
+     */
+    double newHistogramSize(const BiasParams&          params,
+                            double                     t,
+                            bool                       covered,
+                            ArrayRef<const PointState> pointStates,
+                            ArrayRef<double>           weightsumCovering,
+                            FILE*                      fplog);
+
+    /*! \brief Restores the histogram size from history.
+     *
+     * \param[in] stateHistory  The AWH bias state history.
+     */
+    void restoreFromHistory(const AwhBiasStateHistory& stateHistory);
+
+    /*! \brief Store the histogram size state in a history struct.
+     *
+     * \param[in,out] stateHistory  The AWH bias state history.
+     */
+    void storeState(AwhBiasStateHistory* stateHistory) const;
+
+    /*! \brief Returns the number of updates since the start of the simulation.
+     */
+    int numUpdates() const { return numUpdates_; }
+
+    /*! \brief Increments the number of updates by 1.
+     */
+    void incrementNumUpdates() { numUpdates_ += 1; }
+
+    /*! \brief Returns the histogram size.
+     */
+    double histogramSize() const { return histogramSize_; }
+
+    /*! \brief Sets the histogram size.
+     *
+     * \param[in] histogramSize                 The new histogram size.
+     * \param[in] weightHistogramScalingFactor  The factor to scale the weight by.
+     */
+    void setHistogramSize(double histogramSize, double weightHistogramScalingFactor);
+
+    /*! \brief Returns true if we are in the initial stage of the AWH method.
+     */
+    bool inInitialStage() const { return inInitialStage_; }
+
+    /*! \brief Returns The log of the current sample weight, scaled because of the histogram rescaling.
+     */
+    double logScaledSampleWeight() const { return logScaledSampleWeight_; }
+
+private:
+    int64_t numUpdates_; /**< The number of updates performed since the start of the simulation. */
+
+    /* The histogram size sets the update size and so controls the convergence rate of the free energy and bias. */
+    double histogramSize_; /**< Size of reference weight histogram. */
+
+    /* Values that control the evolution of the histogram size. */
+    bool   inInitialStage_;       /**< True if in the intial stage. */
+    bool   equilibrateHistogram_; /**< True if samples are kept from accumulating until the sampled distribution is close enough to the target. */
+    double logScaledSampleWeight_; /**< The log of the current sample weight, scaled because of the histogram rescaling. */
+    double maxLogScaledSampleWeight_; /**< Maximum sample weight obtained for previous (smaller) histogram sizes. */
+
+    /* Bool to avoid printing multiple, not so useful, messages to log */
+    bool havePrintedAboutCovering_; /**< True if we have printed about covering to the log while equilibrateHistogram==true */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_HISTOGRAMSIZE_H */
diff --git a/src/include/gromacs/applied_forces/awh/pointstate.h b/src/include/gromacs/applied_forces/awh/pointstate.h
new file mode 100644 (file)
index 0000000..fd4b5e9
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares and defines the PointState class.
+ *
+ * Since nearly all operations on PointState objects occur in loops over
+ * (parts of) the grid of an AWH bias, all these methods should be inlined.
+ * Only samplePmf() is called only once per step and is thus not inlined.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_POINTSTATE_H
+#define GMX_AWH_POINTSTATE_H
+
+#include <cmath>
+
+#include "gromacs/mdtypes/awh_history.h"
+#include "gromacs/mdtypes/awh_params.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "biasparams.h"
+
+namespace gmx
+{
+
+namespace detail
+{
+
+//! A value that can be passed to exp() with result 0, also with SIMD
+constexpr double c_largeNegativeExponent = -10000.0;
+
+//! The largest acceptable positive exponent for variables that are passed to exp().
+constexpr double c_largePositiveExponent = 700.0;
+
+} // namespace detail
+
+/*! \internal
+ * \brief The state of a coordinate point.
+ *
+ * This class contains all the state variables of a coordinate point
+ * (on the bias grid) and methods to update the state of a point.
+ */
+class PointState
+{
+public:
+    /*! \brief Constructs a point state with default values. */
+    PointState() :
+        bias_(0),
+        freeEnergy_(0),
+        target_(1),
+        targetConstantWeight_(1),
+        weightSumIteration_(0),
+        weightSumTot_(0),
+        weightSumRef_(1),
+        lastUpdateIndex_(0),
+        logPmfSum_(0),
+        numVisitsIteration_(0),
+        numVisitsTot_(0)
+    {
+    }
+
+    /*! \brief
+     * Set all values in the state to those from a history.
+     *
+     * \param[in] psh  Coordinate point history to copy from.
+     */
+    void setFromHistory(const AwhPointStateHistory& psh)
+    {
+        target_             = psh.target;
+        freeEnergy_         = psh.free_energy;
+        bias_               = psh.bias;
+        weightSumIteration_ = psh.weightsum_iteration;
+        weightSumTot_       = psh.weightsum_tot;
+        weightSumRef_       = psh.weightsum_ref;
+        lastUpdateIndex_    = psh.last_update_index;
+        logPmfSum_          = psh.log_pmfsum;
+        numVisitsIteration_ = psh.visits_iteration;
+        numVisitsTot_       = psh.visits_tot;
+    }
+
+    /*! \brief
+     * Store the state of a point in a history struct.
+     *
+     * \param[in,out] psh  Coordinate point history to copy to.
+     */
+    void storeState(AwhPointStateHistory* psh) const
+    {
+        psh->target              = target_;
+        psh->free_energy         = freeEnergy_;
+        psh->bias                = bias_;
+        psh->weightsum_iteration = weightSumIteration_;
+        psh->weightsum_tot       = weightSumTot_;
+        psh->weightsum_ref       = weightSumRef_;
+        psh->last_update_index   = lastUpdateIndex_;
+        psh->log_pmfsum          = logPmfSum_;
+        psh->visits_iteration    = numVisitsIteration_;
+        psh->visits_tot          = numVisitsTot_;
+    }
+
+    /*! \brief
+     * Query if the point is in the target region.
+     *
+     * \returns true if the point is in the target region.
+     */
+    bool inTargetRegion() const { return target_ > 0; }
+
+    /*! \brief Return the bias function estimate. */
+    double bias() const { return bias_; }
+
+    /*! \brief Set the target to zero and the bias to minus infinity. */
+    void setTargetToZero()
+    {
+        target_ = 0;
+        /* the bias = log(target) + const = -infty */
+        bias_ = detail::c_largeNegativeExponent;
+    }
+
+    /*! \brief Return the free energy. */
+    double freeEnergy() const { return freeEnergy_; }
+
+    /*! \brief Set the free energy, only to be used at initialization.
+     *
+     * \param[in] freeEnergy  The free energy.
+     */
+    void setFreeEnergy(double freeEnergy) { freeEnergy_ = freeEnergy; }
+
+    /*! \brief Return the target distribution value. */
+    double target() const { return target_; }
+
+    /*! \brief Return the weight accumulated since the last update. */
+    double weightSumIteration() const { return weightSumIteration_; }
+
+    /*! \brief Increases the weight accumulated since the last update.
+     *
+     * \param[in] weight  The amount to add to the weight
+     */
+    void increaseWeightSumIteration(double weight) { weightSumIteration_ += weight; }
+
+    /*! \brief Returns the accumulated weight */
+    double weightSumTot() const { return weightSumTot_; }
+
+    /*! \brief Return the reference weight histogram. */
+    double weightSumRef() const { return weightSumRef_; }
+
+    /*! \brief Return log(PmfSum). */
+    double logPmfSum() const { return logPmfSum_; }
+
+    /*! \brief Set log(PmfSum).
+     *
+     * TODO: Replace this setter function with a more elegant solution.
+     *
+     * \param[in] logPmfSum  The log(PmfSum).
+     */
+    void setLogPmfSum(double logPmfSum) { logPmfSum_ = logPmfSum; }
+
+    /*! \brief Return the number of visits since the last update */
+    double numVisitsIteration() const { return numVisitsIteration_; }
+
+    /*! \brief Return the total number of visits */
+    double numVisitsTot() const { return numVisitsTot_; }
+
+    /*! \brief Set the constant target weight factor.
+     *
+     * \param[in] targetConstantWeight  The target weight factor.
+     */
+    void setTargetConstantWeight(double targetConstantWeight)
+    {
+        targetConstantWeight_ = targetConstantWeight;
+    }
+
+    /*! \brief Updates the bias of a point. */
+    void updateBias()
+    {
+        GMX_ASSERT(target_ > 0, "AWH target distribution must be > 0 to calculate the point bias.");
+
+        bias_ = freeEnergy() + std::log(target_);
+    }
+
+    /*! \brief Set the initial reference weighthistogram.
+     *
+     * \param[in] histogramSize  The weight histogram size.
+     */
+    void setInitialReferenceWeightHistogram(double histogramSize)
+    {
+        weightSumRef_ = histogramSize * target_;
+    }
+
+    /*! \brief Correct free energy and PMF sum for the change in minimum.
+     *
+     * \param[in] minimumFreeEnergy  The free energy at the minimum;
+     */
+    void normalizeFreeEnergyAndPmfSum(double minimumFreeEnergy)
+    {
+        if (inTargetRegion())
+        {
+            /* The sign of the free energy and PMF constants are opposite
+             * because the PMF samples are reweighted with the negative
+             * bias e^(-bias) ~ e^(-free energy).
+             */
+            freeEnergy_ -= minimumFreeEnergy;
+            logPmfSum_ += minimumFreeEnergy;
+        }
+    }
+
+    /*! \brief Apply previous updates that were skipped.
+     *
+     * An update can only be skipped if the parameters needed for the update are constant or
+     * deterministic so that the same update can be performed at a later time.
+     * Here, the necessary parameters are the sampled weight and scaling factors for the
+     * histograms. The scaling factors are provided as arguments only to avoid recalculating
+     * them for each point
+     *
+     * The last update index is also updated here.
+     *
+     * \param[in] params             The AWH bias parameters.
+     * \param[in] numUpdates         The global number of updates.
+     * \param[in] weighthistScaling  Scale factor for the reference weight histogram.
+     * \param[in] logPmfSumScaling   Scale factor for the reference PMF histogram.
+     * \returns true if at least one update was applied.
+     */
+    bool performPreviouslySkippedUpdates(const BiasParams& params,
+                                         int64_t           numUpdates,
+                                         double            weighthistScaling,
+                                         double            logPmfSumScaling)
+    {
+        GMX_ASSERT(params.skipUpdates(),
+                   "Calling function for skipped updates when skipping updates is not allowed");
+
+        if (!inTargetRegion())
+        {
+            return false;
+        }
+
+        /* The most current past update */
+        int64_t lastUpdateIndex   = numUpdates;
+        int64_t numUpdatesSkipped = lastUpdateIndex - lastUpdateIndex_;
+
+        if (numUpdatesSkipped == 0)
+        {
+            /* Was not updated */
+            return false;
+        }
+
+        for (int64_t i = 0; i < numUpdatesSkipped; i++)
+        {
+            /* This point was non-local at the time of the update meaning no weight */
+            updateFreeEnergyAndWeight(params, 0, weighthistScaling, logPmfSumScaling);
+        }
+
+        /* Only past updates are applied here. */
+        lastUpdateIndex_ = lastUpdateIndex;
+
+        return true;
+    }
+
+    /*! \brief Apply a point update with new sampling.
+     *
+     * \note The last update index is also updated here.
+     * \note The new sampling containers are cleared here.
+     *
+     * \param[in] params              The AWH bias parameters.
+     * \param[in] numUpdates          The global number of updates.
+     * \param[in] weighthistScaling   Scaling factor for the reference weight histogram.
+     * \param[in] logPmfSumScaling    Log of the scaling factor for the PMF histogram.
+     */
+    void updateWithNewSampling(const BiasParams& params, int64_t numUpdates, double weighthistScaling, double logPmfSumScaling)
+    {
+        GMX_RELEASE_ASSERT(lastUpdateIndex_ == numUpdates,
+                           "When doing a normal update, the point update index should match the "
+                           "global index, otherwise we lost (skipped?) updates.");
+
+        updateFreeEnergyAndWeight(params, weightSumIteration_, weighthistScaling, logPmfSumScaling);
+        lastUpdateIndex_ += 1;
+
+        /* Clear the iteration collection data */
+        weightSumIteration_ = 0;
+        numVisitsIteration_ = 0;
+    }
+
+
+    /*! \brief Update the PMF histogram with the current coordinate value.
+     *
+     * \param[in] convolvedBias  The convolved bias.
+     */
+    void samplePmf(double convolvedBias);
+
+    /*! \brief Update the PMF histogram of unvisited coordinate values
+     * (along a lambda axis)
+     *
+     * \param[in] bias  The bias to update with.
+     */
+    void updatePmfUnvisited(double bias);
+
+private:
+    /*! \brief Update the free energy estimate of a point.
+     *
+     * The free energy update here is inherently local, i.e. it just depends on local sampling and
+     * on constant AWH parameters. This assumes that the variables used here are kept constant, at
+     * least in between global updates.
+     *
+     * \param[in] params          The AWH bias parameters.
+     * \param[in] weightAtPoint   Sampled probability weight at this point.
+     */
+    void updateFreeEnergy(const BiasParams& params, double weightAtPoint)
+    {
+        double weighthistSampled = weightSumRef() + weightAtPoint;
+        double weighthistTarget  = weightSumRef() + params.updateWeight * target_;
+
+        double df = -std::log(weighthistSampled / weighthistTarget);
+        freeEnergy_ += df;
+
+        GMX_RELEASE_ASSERT(std::abs(freeEnergy_) < detail::c_largePositiveExponent,
+                           "Very large free energy differences or badly normalized free energy in "
+                           "AWH update.");
+    }
+
+    /*! \brief Update the reference weight histogram of a point.
+     *
+     * \param[in] params         The AWH bias parameters.
+     * \param[in] weightAtPoint  Sampled probability weight at this point.
+     * \param[in] scaleFactor    Factor to rescale the histogram with.
+     */
+    void updateWeightHistogram(const BiasParams& params, double weightAtPoint, double scaleFactor)
+    {
+        if (params.idealWeighthistUpdate)
+        {
+            /* Grow histogram using the target distribution. */
+            weightSumRef_ += target_ * params.updateWeight * params.localWeightScaling;
+        }
+        else
+        {
+            /* Grow using the actual samples (which are distributed ~ as target). */
+            weightSumRef_ += weightAtPoint * params.localWeightScaling;
+        }
+
+        weightSumRef_ *= scaleFactor;
+    }
+
+    /*! \brief Apply a point update.
+     *
+     * This updates local properties that can be updated without
+     * accessing or affecting all points.
+     * This excludes updating the size of reference weight histogram and
+     * the target distribution. The bias update is excluded only because
+     * if updates have been skipped this function will be called multiple
+     * times, while the bias only needs to be updated once (last).
+     *
+     * Since this function only performs the update with the given
+     * arguments and does not know anything about the time of the update,
+     * the last update index is not updated here. The caller should take
+     * care of updating the update index.
+     *
+     * \param[in] params             The AWH bias parameters.
+     * \param[in] weightAtPoint      Sampled probability weight at this point.
+     * \param[in] weighthistScaling  Scaling factor for the reference weight histogram.
+     * \param[in] logPmfSumScaling   Log of the scaling factor for the PMF histogram.
+     */
+    void updateFreeEnergyAndWeight(const BiasParams& params,
+                                   double            weightAtPoint,
+                                   double            weighthistScaling,
+                                   double            logPmfSumScaling)
+    {
+        updateFreeEnergy(params, weightAtPoint);
+        updateWeightHistogram(params, weightAtPoint, weighthistScaling);
+        logPmfSum_ += logPmfSumScaling;
+    }
+
+
+public:
+    /*! \brief Update the target weight of a point.
+     *
+     * Note that renormalization over all points is needed after the update.
+     *
+     * \param[in] params            The AWH bias parameters.
+     * \param[in] freeEnergyCutoff  The cut-off for the free energy for target type "cutoff".
+     * \returns the updated value of the target.
+     */
+    double updateTargetWeight(const BiasParams& params, double freeEnergyCutoff)
+    {
+        switch (params.eTarget)
+        {
+            case AwhTargetType::Constant: target_ = 1; break;
+            case AwhTargetType::Cutoff:
+            {
+                double df = freeEnergy_ - freeEnergyCutoff;
+                target_   = 1 / (1 + std::exp(df));
+                break;
+            }
+            case AwhTargetType::Boltzmann:
+                target_ = std::exp(-params.temperatureScaleFactor * freeEnergy_);
+                break;
+            case AwhTargetType::LocalBoltzmann: target_ = weightSumRef_; break;
+            default: GMX_RELEASE_ASSERT(false, "Unhandled enum");
+        }
+
+        /* All target types can be modulated by a constant factor. */
+        target_ *= targetConstantWeight_;
+
+        return target_;
+    }
+
+    /*! \brief Set the weight and count accumulated since the last update.
+     *
+     * \param[in] weightSum  The weight-sum value
+     * \param[in] numVisits  The number of visits
+     */
+    void setPartialWeightAndCount(double weightSum, double numVisits)
+    {
+        weightSumIteration_ = weightSum;
+        numVisitsIteration_ = numVisits;
+    }
+
+    /*! \brief Add the weights and counts accumulated between updates. */
+    void addPartialWeightAndCount()
+    {
+        weightSumTot_ += weightSumIteration_;
+        numVisitsTot_ += numVisitsIteration_;
+    }
+
+    /*! \brief Scale the target weight of the point.
+     *
+     * \param[in] scaleFactor  Factor to scale with.
+     */
+    void scaleTarget(double scaleFactor) { target_ *= scaleFactor; }
+
+private:
+    double bias_;                 /**< Current biasing function estimate */
+    double freeEnergy_;           /**< Current estimate of the convolved free energy/PMF. */
+    double target_;               /**< Current target distribution, normalized to 1 */
+    double targetConstantWeight_; /**< Constant target weight, from user data. */
+    double weightSumIteration_; /**< Accumulated weight this iteration; note: only contains data for this Bias, even when sharing biases. */
+    double weightSumTot_;       /**< Accumulated weights, never reset */
+    double weightSumRef_; /**< The reference weight histogram determining the free energy updates */
+    int64_t lastUpdateIndex_; /**< The last update that was performed at this point, in units of number of updates. */
+    double  logPmfSum_;          /**< Logarithm of the PMF histogram */
+    double  numVisitsIteration_; /**< Visits to this bin this iteration; note: only contains data for this Bias, even when sharing biases. */
+    double  numVisitsTot_;       /**< Accumulated visits to this bin */
+};
+
+} // namespace gmx
+
+#endif /* GMX_AWH_POINTSTATE_H */
diff --git a/src/include/gromacs/applied_forces/awh/read_params.h b/src/include/gromacs/applied_forces/awh/read_params.h
new file mode 100644 (file)
index 0000000..22247ae
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * Declares functions needed for reading, initializing and setting the AWH parameter data types.
+ *
+ * \author Viveca Lindahl
+ * \inlibraryapi
+ * \ingroup module_awh
+ */
+
+#ifndef GMX_AWH_READPARAMS_H
+#define GMX_AWH_READPARAMS_H
+
+#include "gromacs/fileio/readinp.h"
+#include "gromacs/math/vectypes.h"
+
+struct t_grpopts;
+struct t_inputrec;
+struct gmx_mtop_t;
+struct pull_params_t;
+struct pull_t;
+enum class PbcType : int;
+
+namespace gmx
+{
+
+class AwhParams;
+class ISerializer;
+
+/*! \brief Check the AWH parameters.
+ *
+ * \param[in]     awhParams    The AWH parameters.
+ * \param[in]     inputrec     Input parameter struct.
+ * \param[in,out] wi           Struct for bookeeping warnings.
+ */
+void checkAwhParams(const AwhParams& awhParams, const t_inputrec& inputrec, warninp_t wi);
+
+
+/*! \brief
+ * Sets AWH parameters that need state parameters such as the box vectors.
+ *
+ * \param[in,out] awhParams             AWH parameters.
+ * \param[in]     pull_params           Pull parameters.
+ * \param[in,out] pull_work             Pull working struct to register AWH bias in.
+ * \param[in]     box                   Box vectors.
+ * \param[in]     pbcType               Periodic boundary conditions enum.
+ * \param[in]     compressibility       Compressibility matrix for pressure coupling, pass all 0 without pressure coupling
+ * \param[in]     inputrecGroupOptions  Parameters for atom groups.
+ * \param[in]     initLambda            The starting lambda, to allow using free energy lambda as reaction coordinate
+ * provider in any dimension.
+ * \param[in]     mtop                  The system topology.
+ * \param[in,out] wi                    Struct for bookeeping warnings.
+ *
+ * \note This function currently relies on the function set_pull_init to have been called.
+ */
+void setStateDependentAwhParams(AwhParams*           awhParams,
+                                const pull_params_t& pull_params,
+                                pull_t*              pull_work,
+                                const matrix         box,
+                                PbcType              pbcType,
+                                const tensor&        compressibility,
+                                const t_grpopts*     inputrecGroupOptions,
+                                real                 initLambda,
+                                const gmx_mtop_t&    mtop,
+                                warninp_t            wi);
+
+} // namespace gmx
+
+#endif /* GMX_AWH_READPARAMS_H */
diff --git a/src/include/gromacs/applied_forces/awh/tests/awh_setup.h b/src/include/gromacs/applied_forces/awh/tests/awh_setup.h
new file mode 100644 (file)
index 0000000..ee6cb61
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_AWH_TEST_SETUP_H
+#define GMX_AWH_TEST_SETUP_H
+
+#include "gmxpre.h"
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/applied_forces/awh/bias.h"
+#include "gromacs/mdtypes/awh_params.h"
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+
+namespace test
+{
+
+/*! \internal \brief
+ * Prepare a memory buffer with serialized AwhDimParams.
+ */
+std::vector<char> awhDimParamSerialized(
+        AwhCoordinateProviderType inputCoordinateProvider = AwhCoordinateProviderType::Pull,
+        int                       inputCoordIndex         = 0,
+        double                    inputOrigin             = 0.5,
+        double                    inputEnd                = 1.5,
+        double                    inputPeriod             = 0,
+        // Correction for removal of GaussianGeometryFactor/2 in histogram size
+        double inputDiffusion = 0.1 / (0.144129616073222 * 2));
+
+/*! \internal \brief
+ * Struct that gathers all input for setting up and using a Bias
+ */
+struct AwhTestParameters
+{
+    explicit AwhTestParameters(ISerializer* serializer);
+    //! Move constructor
+    AwhTestParameters(AwhTestParameters&& o) noexcept :
+        beta(o.beta), awhParams(std::move(o.awhParams)), dimParams(std::move(o.dimParams))
+    {
+    }
+    //! 1/(kB*T).
+    double beta;
+
+    //! AWH parameters, this is the struct to actually use.
+    AwhParams awhParams;
+    //! Dimension parameters for setting up Bias.
+    std::vector<DimParams> dimParams;
+};
+
+/*! \brief
+ * Helper function to set up the C-style AWH parameters for the test.
+ *
+ * Builds the test input data from serialized data.
+ */
+AwhTestParameters getAwhTestParameters(AwhHistogramGrowthType            eawhgrowth,
+                                       AwhPotentialType                  eawhpotential,
+                                       ArrayRef<const std::vector<char>> dimensionParameterBuffers,
+                                       bool                              inputUserData,
+                                       double                            beta,
+                                       bool                              useAwhFep,
+                                       double                            inputErrorScaling,
+                                       int                               numFepLambdaStates,
+                                       int                               biasShareGroup = 0);
+
+} // namespace test
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/applied_forces/densityfitting/densityfitting.h b/src/include/gromacs/applied_forces/densityfitting/densityfitting.h
new file mode 100644 (file)
index 0000000..a5d09de
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_APPLIED_FORCES_DENSITYFITTING_H
+#define GMX_APPLIED_FORCES_DENSITYFITTING_H
+
+#include <memory>
+#include <string>
+
+namespace gmx
+{
+
+class IMDModule;
+
+/*! \libinternal \brief Information about the density fitting module.
+ *
+ * Provides name and method to create a density fitting module.
+ */
+struct DensityFittingModuleInfo
+{
+    /*! \brief
+     * Creates a module for applying forces to fit a given density.
+     *
+     * Fitting an all-atom structure into an experimental cryo-EM density map is a
+     * typical application.
+     */
+    static std::unique_ptr<IMDModule> create();
+    //! The name of the module
+    static const std::string name_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/applied_forces/densityfitting/densityfittingamplitudelookup.h b/src/include/gromacs/applied_forces/densityfitting/densityfittingamplitudelookup.h
new file mode 100644 (file)
index 0000000..92b21f1
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares amplitude lookup for density fitting
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_applied_forces
+ */
+#ifndef GMX_APPLIED_FORCES_DENSITYFITTINGAMPLITUDELOOKUP_H
+#define GMX_APPLIED_FORCES_DENSITYFITTINGAMPLITUDELOOKUP_H
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+
+/*! \brief
+ * The methods that determine how amplitudes are spread on a grid in density guided simulations.
+ */
+enum class DensityFittingAmplitudeMethod : int
+{
+    Unity,  //!< same spread amplitude, unity, for all atoms
+    Mass,   //!< atom mass is the spread amplitude
+    Charge, //!< partial charge determines the spread amplitude
+    Count
+};
+
+class DensityFittingAmplitudeLookupImpl;
+
+/*! \internal \brief Class that translates atom properties into amplitudes.
+ *
+ */
+class DensityFittingAmplitudeLookup
+{
+public:
+    //! Construct force provider for density fitting from its parameters
+    explicit DensityFittingAmplitudeLookup(const DensityFittingAmplitudeMethod& method);
+    ~DensityFittingAmplitudeLookup();
+    //! Copy constructor
+    DensityFittingAmplitudeLookup(const DensityFittingAmplitudeLookup& other);
+    //! Copy assignment
+    DensityFittingAmplitudeLookup& operator=(const DensityFittingAmplitudeLookup& other);
+    //! Move constructor
+    DensityFittingAmplitudeLookup(DensityFittingAmplitudeLookup&& other) noexcept;
+    //! Move assignment
+    DensityFittingAmplitudeLookup& operator=(DensityFittingAmplitudeLookup&& other) noexcept;
+    /*! \brief Return the amplitudes for spreading atoms of a given local index.
+     * \param[in] chargeA Atom charges
+     * \param[in] massT   Atom masses.
+     * \param[in] localIndex the local atom indices
+     * \returns amplitudes
+     */
+    const std::vector<real>& operator()(ArrayRef<const real> chargeA,
+                                        ArrayRef<const real> massT,
+                                        ArrayRef<const int>  localIndex);
+
+private:
+    std::unique_ptr<DensityFittingAmplitudeLookupImpl> impl_;
+};
+
+} // namespace gmx
+
+#endif // GMX_APPLIED_FORCES_DENSITYFITTINGAMPLITUDELOOKUP_H
diff --git a/src/include/gromacs/applied_forces/densityfitting/densityfittingforceprovider.h b/src/include/gromacs/applied_forces/densityfitting/densityfittingforceprovider.h
new file mode 100644 (file)
index 0000000..c31d1a6
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares force provider for density fitting
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_applied_forces
+ */
+#ifndef GMX_APPLIED_FORCES_DENSITYFITTINGFORCEPROVIDER_H
+#define GMX_APPLIED_FORCES_DENSITYFITTINGFORCEPROVIDER_H
+
+#include <memory>
+
+#include "gromacs/fileio/checkpoint.h"
+#include "gromacs/math/exponentialmovingaverage.h"
+#include "gromacs/mdspan/extensions.h"
+#include "gromacs/mdtypes/iforceprovider.h"
+
+enum class PbcType : int;
+
+namespace gmx
+{
+
+class LocalAtomSet;
+class TranslateAndScale;
+struct DensityFittingParameters;
+
+/*! \internal
+ * \brief Parameters defining the internal density fitting force provider state.
+ */
+struct DensityFittingForceProviderState
+{
+    /*! \brief The steps since the last force calculation.
+     *  Used if density fitting is to be calculated every N steps.
+     */
+    std::int64_t stepsSinceLastCalculation_ = 0;
+    /*! \brief String naming variable holding the steps since last calculation.
+     * \note Changing this name will break backwards compability for checkpoint file writing.
+     */
+    static const std::string stepsSinceLastCalculationName_;
+    //! The state of the exponential moving average of the similarity measure
+    ExponentialMovingAverageState exponentialMovingAverageState_ = {};
+    /*! \brief String naming variable holding the exponential moving average.
+     * \note Changing this name will break backwards compability for checkpoint file writing.
+     */
+    static const std::string exponentialMovingAverageStateName_;
+
+    //! An additional factor scaling the force for adaptive force scaling
+    real adaptiveForceConstantScale_ = 1.0_real;
+    /*! \brief String naming variable holding the adaptive force constant scale.
+     * \note Changing this name will break backwards compability for checkpoint file writing.
+     */
+    static const std::string adaptiveForceConstantScaleName_;
+
+    /*! \brief Write internal density fitting data into a key value tree.
+     * The entries to the kvt are identified with identifier, so that a variable
+     * is indentified with the key "identifier-variablename"
+     *
+     * \param[in] kvtBuilder enables writing to the Key-Value-Tree
+     *                              the state is written to
+     *
+     * \param[in] identifier denotes the module that is checkpointing the data
+     */
+    void writeState(KeyValueTreeObjectBuilder kvtBuilder, const std::string& identifier) const;
+
+    /*! \brief Read the internal parameters from the checkpoint file on master
+     * \param[in] kvtData holding the checkpoint information
+     * \param[in] identifier identifies the data in a key-value-tree
+     */
+    void readState(const KeyValueTreeObject& kvtData, const std::string& identifier);
+
+    /*! \brief Broadcast the internal parameters.
+     *
+     * \param[in] communicator to broadcast the state information
+     * \param[in] isParallelRun to determine if anything has to be broadcast at all
+     *
+     */
+    void broadcastState(MPI_Comm communicator, bool isParallelRun);
+};
+
+/*! \internal \brief
+ * Implements IForceProvider for density-fitting forces.
+ */
+class DensityFittingForceProvider final : public IForceProvider
+{
+public:
+    //! Construct force provider for density fitting from its parameters
+    DensityFittingForceProvider(const DensityFittingParameters&             parameters,
+                                basic_mdspan<const float, dynamicExtents3D> referenceDensity,
+                                const TranslateAndScale& transformationToDensityLattice,
+                                const LocalAtomSet&      localAtomSet,
+                                PbcType                  pbcType,
+                                double                   simulationTimeStep,
+                                const DensityFittingForceProviderState& state);
+    ~DensityFittingForceProvider();
+    /*!\brief Calculate forces that maximise goodness-of-fit with a reference density map.
+     * \param[in] forceProviderInput input for force provider
+     * \param[out] forceProviderOutput output for force provider
+     */
+    void calculateForces(const ForceProviderInput& forceProviderInput,
+                         ForceProviderOutput*      forceProviderOutput) override;
+
+    /*! \brief Write internal density fitting data to checkpoint file.
+     * \param[in] checkpointWriting enables writing to the Key-Value-Tree
+     *                              that is used for storing the checkpoint
+     *                              information
+     * \param[in] moduleName names the module that is checkpointing this force-provider
+     *
+     * \note The provided state to checkpoint has to change if checkpointing
+     *       is moved before the force provider call in the MD-loop.
+     */
+    void writeCheckpointData(MDModulesWriteCheckpointData checkpointWriting, const std::string& moduleName);
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif // GMX_APPLIED_FORCES_DENSITYFITTINGFORCEPROVIDER_H
diff --git a/src/include/gromacs/applied_forces/densityfitting/densityfittingoptions.h b/src/include/gromacs/applied_forces/densityfitting/densityfittingoptions.h
new file mode 100644 (file)
index 0000000..bbdf317
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares options for density fitting
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_applied_forces
+ */
+#ifndef GMX_APPLIED_FORCES_DENSITYFITTINGOPTIONS_H
+#define GMX_APPLIED_FORCES_DENSITYFITTINGOPTIONS_H
+
+#include <string>
+
+#include "gromacs/mdtypes/imdpoptionprovider.h"
+
+#include "densityfittingparameters.h"
+
+namespace gmx
+{
+
+class EnergyCalculationFrequencyErrors;
+class IndexGroupsAndNames;
+class KeyValueTreeObject;
+class KeyValueTreeBuilder;
+
+/*! \internal
+ * \brief Input data storage for density fitting
+ */
+class DensityFittingOptions final : public IMdpOptionProvider
+{
+public:
+    //! From IMdpOptionProvider
+    void initMdpTransform(IKeyValueTreeTransformRules* rules) override;
+
+    /*! \brief
+     * Build mdp parameters for density fitting to be output after pre-processing.
+     * \param[in, out] builder the builder for the mdp options output KV-tree.
+     * \note This should be symmetrical to option initialization without
+     *       employing manual prefixing with the section name string once
+     *       the legacy code blocking this design is removed.
+     */
+    void buildMdpOutput(KeyValueTreeObjectBuilder* builder) const override;
+
+    /*! \brief
+     * Connect option name and data.
+     */
+    void initMdpOptions(IOptionsContainerWithSections* options) override;
+
+    //! Report if this set of options is active
+    bool active() const;
+
+    //! Process input options to parameters, including input file reading.
+    const DensityFittingParameters& buildParameters();
+
+    /*! \brief Evaluate and store atom indices.
+     *
+     * During pre-processing, use the group string from the options to
+     * evaluate the indices of the atoms to be subject to forces from this
+     * module.
+     */
+    void setFitGroupIndices(const IndexGroupsAndNames& indexGroupsAndNames);
+
+    //! Store the paramers that are not mdp options in the tpr file
+    void writeInternalParametersToKvt(KeyValueTreeObjectBuilder treeBuilder);
+
+    //! Set the internal parameters that are stored in the tpr file
+    void readInternalParametersFromKvt(const KeyValueTreeObject& tree);
+
+    //! Return the file name of the reference density
+    const std::string& referenceDensityFileName() const;
+
+    //! Check if input parameters are consistent with other simulation parameters
+    void checkEnergyCaluclationFrequency(EnergyCalculationFrequencyErrors* energyCalculationFrequencyErrors) const;
+
+private:
+    const std::string c_activeTag_ = "active";
+
+    /*! \brief Denote the .mdp option that defines the group of fit atoms.
+     * \note Changing this string will break .tpr backwards compability
+     */
+    const std::string c_groupTag_  = "group";
+    std::string       groupString_ = "protein";
+
+    const std::string c_similarityMeasureTag_ = "similarity-measure";
+
+    const std::string c_amplitudeMethodTag_ = "atom-spreading-weight";
+
+    const std::string c_forceConstantTag_ = "force-constant";
+
+    const std::string c_gaussianTransformSpreadingWidthTag_ = "gaussian-transform-spreading-width";
+    const std::string c_gaussianTransformSpreadingRangeInMultiplesOfWidthTag_ =
+            "gaussian-transform-spreading-range-in-multiples-of-width";
+
+    const std::string c_referenceDensityFileNameTag_ = "reference-density-filename";
+    std::string       referenceDensityFileName_      = "reference.mrc";
+
+    const std::string c_everyNStepsTag_ = "nst";
+
+    const std::string c_normalizeDensitiesTag_ = "normalize-densities";
+
+    const std::string c_adaptiveForceScalingTag_ = "adaptive-force-scaling";
+
+    const std::string c_adaptiveForceScalingTimeConstantTag_ =
+            "adaptive-force-scaling-time-constant";
+
+    const std::string c_translationTag_ = "shift-vector";
+
+    const std::string c_transformationMatrixTag_ = "transformation-matrix";
+
+    DensityFittingParameters parameters_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/applied_forces/densityfitting/densityfittingoutputprovider.h b/src/include/gromacs/applied_forces/densityfitting/densityfittingoutputprovider.h
new file mode 100644 (file)
index 0000000..9bd165d
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares output to file for density fitting
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_applied_forces
+ */
+#ifndef GMX_APPLIED_FORCES_DENSITYFITTINGOUTPUTPROVIDER_H
+#define GMX_APPLIED_FORCES_DENSITYFITTINGOUTPUTPROVIDER_H
+
+#include "gromacs/mdtypes/imdoutputprovider.h"
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief Handle file output for density guided simulations.
+ */
+class DensityFittingOutputProvider final : public IMDOutputProvider
+{
+public:
+    //! Initialize output
+    void initOutput(FILE* /*fplog*/,
+                    int /*nfile*/,
+                    const t_filenm /*fnm*/[],
+                    bool /*bAppendFiles*/,
+                    const gmx_output_env_t* /*oenv*/) override;
+    //! Finalizes output from a simulation run.
+    void finishOutput() override;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/applied_forces/densityfitting/densityfittingparameters.h b/src/include/gromacs/applied_forces/densityfitting/densityfittingparameters.h
new file mode 100644 (file)
index 0000000..f6dc43a
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares parameters needed to evaluate forces and energies for density fitting
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_applied_forces
+ */
+#ifndef GMX_APPLIED_FORCES_DENSITYFITTINGPARAMETERS_H
+#define GMX_APPLIED_FORCES_DENSITYFITTINGPARAMETERS_H
+
+#include <string>
+#include <vector>
+
+#include "gromacs/math/densityfit.h"
+#include "gromacs/utility/basedefinitions.h"
+
+#include "densityfittingamplitudelookup.h"
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief Holding all directly user-provided parameters for density fitting.
+ *
+ * Also used for setting all default parameters.
+ */
+struct DensityFittingParameters
+{
+    //! Indicate if density fitting is active
+    bool active_ = false;
+    //! Indices of the atoms that shall be fit to the density
+    std::vector<index> indices_;
+    //! Determines how to measure similarity between simulated and reference density
+    DensitySimilarityMeasureMethod similarityMeasureMethod_ = DensitySimilarityMeasureMethod::innerProduct;
+    //! Determines with what weight atoms are spread
+    DensityFittingAmplitudeMethod amplitudeLookupMethod_ = DensityFittingAmplitudeMethod::Unity;
+    //! The force constant to be used for the density fitting
+    real forceConstant_ = 1e9;
+    //! The spreading width used for the gauss transform of atoms onto the density grid
+    real gaussianTransformSpreadingWidth_ = 0.2;
+    //! The spreading range for spreading atoms onto the grid in multiples of the spreading width
+    real gaussianTransformSpreadingRangeInMultiplesOfWidth_ = 4.0;
+    //! Apply density fitting forces only every n-steps
+    std::int64_t calculationIntervalInSteps_ = 1;
+    //! Normalize reference and simulated densities
+    bool normalizeDensities_ = true;
+    //! Perform adaptive force scaling during the simulation
+    bool adaptiveForceScaling_ = false;
+    //! The time constant for the adaptive force scaling in ps
+    real adaptiveForceScalingTimeConstant_ = 4;
+    //! Translation of the structure, so that the coordinates that are fitted are x+translation
+    std::string translationString_;
+    //! Linear transformation of the structure, so that the coordinates that are fitted are Matrix * x
+    std::string transformationMatrixString_;
+};
+
+/*!\brief Check if two structs holding density fitting parameters are equal.
+ *
+ * \param[in] lhs left hand side to be compared
+ * \param[in] rhs right hand side to be compared
+ * \returns true if all elements in DensityFittingParameters are equal, else false
+ */
+bool operator==(const DensityFittingParameters& lhs, const DensityFittingParameters& rhs);
+
+/*!\brief Check if two structs holding density fitting parameters are not equal.
+ *
+ * \param[in] lhs left hand side to be compared
+ * \param[in] rhs right hand side to be compared
+ * \returns true if lhs is not equal rhs
+ */
+bool operator!=(const DensityFittingParameters& lhs, const DensityFittingParameters& rhs);
+
+} // namespace gmx
+
+#endif // GMX_APPLIED_FORCES_DENSITYFITTINGPARAMETERS_H
diff --git a/src/include/gromacs/applied_forces/electricfield.h b/src/include/gromacs/applied_forces/electricfield.h
new file mode 100644 (file)
index 0000000..175c5bc
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares module creation function for applied electric fields
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \ingroup module_applied_forces
+ * \inlibraryapi
+ */
+#ifndef GMX_APPLIED_FORCES_ELECTRICFIELD_H
+#define GMX_APPLIED_FORCES_ELECTRICFIELD_H
+
+#include <memory>
+
+namespace gmx
+{
+
+class IMDModule;
+
+/*! \brief
+ * Creates a module for an external electric field.
+ *
+ * The returned class describes the time dependent electric field that can
+ * be applied to all charges in a simulation. The field is described
+ * by the following:
+ *     E(t) = A cos(omega*(t-t0))*exp(-sqr(t-t0)/(2.0*sqr(sigma)));
+ * If sigma = 0 there is no pulse and we have instead
+ *     E(t) = A cos(omega*t)
+ *
+ * force is kJ mol^-1 nm^-1 = e * kJ mol^-1 nm^-1 / e
+ *
+ * WARNING:
+ * There can be problems with the virial.
+ * Since the field is not self-consistent this is unavoidable.
+ * For neutral molecules the virial is correct within this approximation.
+ * For neutral systems with many charged molecules the error is small.
+ * But for systems with a net charge or a few charged molecules
+ * the error can be significant when the field is high.
+ * Solution: implement a self-consistent electric field into PME.
+ */
+std::unique_ptr<IMDModule> createElectricFieldModule();
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/applied_forces/qmmm/qmmm.h b/src/include/gromacs/applied_forces/qmmm/qmmm.h
new file mode 100644 (file)
index 0000000..b983ce3
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares fabrique structure for QMMM MDModule class
+ *
+ * \author Dmitry Morozov <dmitry.morozov@jyu.fi>
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_applied_forces
+ */
+#ifndef GMX_APPLIED_FORCES_QMMM_H
+#define GMX_APPLIED_FORCES_QMMM_H
+
+#include <memory>
+#include <string>
+
+namespace gmx
+{
+
+class IMDModule;
+struct MdModulesNotifier;
+
+/*! \libinternal \brief Information about the QM/MM module.
+ *
+ * Provides name and method to create a QM/MM module.
+ */
+struct QMMMModuleInfo
+{
+    /*! \brief
+     * Creates a module for applying forces according to a QM/MM.
+     *
+     */
+    static std::unique_ptr<IMDModule> create();
+    //! The name of the module
+    static const std::string name_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/applied_forces/qmmm/qmmmforceprovider.h b/src/include/gromacs/applied_forces/qmmm/qmmmforceprovider.h
new file mode 100644 (file)
index 0000000..e90565c
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares force provider for QMMM
+ *
+ * \author Dmitry Morozov <dmitry.morozov@jyu.fi>
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_applied_forces
+ */
+#ifndef GMX_APPLIED_FORCES_QMMMFORCEPROVIDER_H
+#define GMX_APPLIED_FORCES_QMMMFORCEPROVIDER_H
+
+#include "gromacs/domdec/localatomset.h"
+#include "gromacs/mdtypes/forceoutput.h"
+#include "gromacs/mdtypes/iforceprovider.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/logger.h"
+
+#include "qmmmtypes.h"
+
+namespace gmx
+{
+
+#ifdef __clang__
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wunused-private-field"
+#endif
+
+//! Type for CP2K force environment handle
+typedef int force_env_t;
+
+/*! \internal \brief
+ * Implements IForceProvider for QM/MM.
+ */
+class QMMMForceProvider final : public IForceProvider
+{
+public:
+    QMMMForceProvider(const QMMMParameters& parameters,
+                      const LocalAtomSet&   localQMAtomSet,
+                      const LocalAtomSet&   localMMAtomSet,
+                      PbcType               pbcType,
+                      const MDLogger&       logger);
+
+    //! Destruct force provider for QMMM and finalize libcp2k
+    ~QMMMForceProvider();
+
+    /*!\brief Calculate forces of QMMM.
+     * \param[in] fInput input for force provider
+     * \param[out] fOutput output for force provider
+     */
+    void calculateForces(const ForceProviderInput& fInput, ForceProviderOutput* fOutput) override;
+
+private:
+    //! Write message to the log
+    void appendLog(const std::string& msg);
+
+    /*!\brief Check if atom belongs to the global index of qmAtoms_
+     * \param[in] globalAtomIndex global index of the atom to check
+     */
+    bool isQMAtom(index globalAtomIndex);
+
+    /*!\brief Initialization of QM program.
+     * \param[in] cr connection record structure
+     */
+    void initCP2KForceEnvironment(const t_commrec& cr);
+
+    const QMMMParameters& parameters_;
+    const LocalAtomSet&   qmAtoms_;
+    const LocalAtomSet&   mmAtoms_;
+    const PbcType         pbcType_;
+    const MDLogger&       logger_;
+
+    //! Internal copy of PBC box
+    matrix box_;
+
+    //! Flag wether initCP2KForceEnvironment() has been called already
+    bool isCp2kLibraryInitialized_ = false;
+
+    //! CP2K force environment handle
+    force_env_t force_env_ = -1;
+};
+
+#ifdef __clang__
+#    pragma clang diagnostic pop
+#endif
+
+} // namespace gmx
+
+#endif // GMX_APPLIED_FORCES_QMMMFORCEPROVIDER_H
diff --git a/src/include/gromacs/applied_forces/qmmm/qmmminputgenerator.h b/src/include/gromacs/applied_forces/qmmm/qmmminputgenerator.h
new file mode 100644 (file)
index 0000000..a0c9c91
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares input generator class for CP2K QMMM
+ *
+ * \author Dmitry Morozov <dmitry.morozov@jyu.fi>
+ * \ingroup module_applied_forces
+ */
+#ifndef GMX_APPLIED_FORCES_QMMMINPUTGENERATOR_H
+#define GMX_APPLIED_FORCES_QMMMINPUTGENERATOR_H
+
+#include <set>
+
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/utility/arrayref.h"
+
+#include "qmmmtypes.h"
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * Class that takes QMMMParameters, Coordinates, Point charges, Box dimensions, pbcType.
+ * Generates QM/MM sample input parameters and pdb-style coordinates for CP2K.
+ * Input are generated as std::string objects which can be stored in tpr KVT
+ * and/or flushed into the files.
+ */
+class QMMMInputGenerator
+{
+public:
+    /*! \brief Construct QMMMInputGenerator from its parameters
+     *
+     * \param[in] parameters Structure with QMMM parameters
+     * \param[in] pbcType Periodic boundary conditions
+     * \param[in] box Matrix with full box of the system
+     * \param[in] q Point charges of each atom in the system
+     * \param[in] x Coordinates of each atom in the system
+     */
+    QMMMInputGenerator(const QMMMParameters& parameters,
+                       PbcType               pbcType,
+                       const matrix          box,
+                       ArrayRef<const real>  q,
+                       ArrayRef<const RVec>  x);
+
+    /*! \brief Generates sample CP2K input file
+     *
+     */
+    std::string generateCP2KInput() const;
+
+    /*! \brief Generates PDB file suitable for usage with CP2K.
+     *  In that PDB file Point Charges of MM atoms are provided with Extended Beta field
+     */
+    std::string generateCP2KPdb() const;
+
+    //! \brief Returns computed QM box dimensions
+    const matrix& qmBox() const;
+
+    //! \brief Returns computed translation vector in order to center QM atoms inside QM box
+    const RVec& qmTrans() const;
+
+private:
+    //! \brief Check if atom belongs to the global index of qmAtoms_
+    bool isQMAtom(index globalAtomIndex) const;
+
+    /*!\brief Calculates dimensions and center of the QM box.
+     *  Also evaluates translation for the system in order to center QM atoms inside QM box
+     *
+     *  \param[in] scale Factor of how much QM box would be bigger than the radius of QM system
+     *  \param[in] minNorm Minimum norm of the QM box vector
+     */
+    void computeQMBox(real scale, real minNorm);
+
+    //! \brief Generates &GLOBAL section of CP2K Input
+    static std::string generateGlobalSection();
+
+    //! \brief Generates &DFT section of CP2K Input
+    std::string generateDFTSection() const;
+
+    //! \brief Generates &QMMM section of CP2K Input
+    std::string generateQMMMSection() const;
+
+    //! \brief Generates &MM section of CP2K Input
+    static std::string generateMMSection();
+
+    //! \brief Generates &SUBSYS section of CP2K Input
+    std::string generateSubsysSection() const;
+
+    //! QMMM Parameters structure
+    const QMMMParameters& parameters_;
+    //! Simulation PbcType
+    PbcType pbc_;
+    //! Simulation Box
+    matrix box_;
+    //! QM box
+    matrix qmBox_;
+    //! PBC-aware center of QM subsystem
+    RVec qmCenter_;
+    //! Translation that shifts qmCenter_ to the center of qmBox_
+    RVec qmTrans_;
+    //! Set containing indexes of all QM atoms
+    std::set<index> qmAtoms_;
+    //! Atoms point charges
+    ArrayRef<const real> q_;
+    //! Atoms coordinates
+    ArrayRef<const RVec> x_;
+
+    //! Scale of the generated QM box with respect to the QM-subsystem size
+    static constexpr real sc_qmBoxScale = 1.5;
+    //! Minimum length of the generated QM box vectors in nm
+    static constexpr real sc_qmBoxMinLength = 1.0;
+};
+
+/*! \brief Transforms vector a such as distance from it to the plane defined by vectors b and c
+ * will be h minimum length will be milL and maximum length maxL
+ *
+ * \param[in] a Vector which should be scaled
+ * \param[in] b First vector that forms the plane
+ * \param[in] c Second vector that forms the plane
+ * \param[in] h Distance from the end of a to the plane of (b,c)
+ * \param[in] minNorm Minimum norm of vector
+ * \param[in] maxNorm Maximum norm of vector
+ */
+RVec computeQMBoxVec(const RVec& a, const RVec& b, const RVec& c, real h, real minNorm, real maxNorm);
+
+} // namespace gmx
+
+#endif // GMX_APPLIED_FORCES_QMMMINPUTGENERATOR_H
diff --git a/src/include/gromacs/applied_forces/qmmm/qmmmoptions.h b/src/include/gromacs/applied_forces/qmmm/qmmmoptions.h
new file mode 100644 (file)
index 0000000..ce5dc74
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares options for QM/MM
+ * QMMMOptions class responsible for all parameters set up during pre-processing
+ * also modificatios of topology would be done here
+ *
+ * \author Dmitry Morozov <dmitry.morozov@jyu.fi>
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_applied_forces
+ */
+#ifndef GMX_APPLIED_FORCES_QMMMOPTIONS_H
+#define GMX_APPLIED_FORCES_QMMMOPTIONS_H
+
+#include "gromacs/mdtypes/imdpoptionprovider.h"
+
+#include "qmmmtypes.h"
+
+struct gmx_mtop_t;
+struct warninp;
+
+namespace gmx
+{
+
+class IndexGroupsAndNames;
+class KeyValueTreeObject;
+class KeyValueTreeBuilder;
+class MDLogger;
+struct MdRunInputFilename;
+struct CoordinatesAndBoxPreprocessed;
+struct QMInputFileName;
+
+//! Tag with name of the QMMM with CP2K MDModule
+static const std::string c_qmmmCP2KModuleName = "qmmm-cp2k";
+
+/*! \internal
+ * \brief Input data storage for QM/MM
+ */
+class QMMMOptions final : public IMdpOptionProvider
+{
+public:
+    //! Implementation of IMdpOptionProvider method
+    void initMdpTransform(IKeyValueTreeTransformRules* rules) override;
+
+    /*! \brief
+     * Build mdp parameters for QMMM to be output after pre-processing.
+     * \param[in, out] builder the builder for the mdp options output KVT.
+     */
+    void buildMdpOutput(KeyValueTreeObjectBuilder* builder) const override;
+
+    /*! \brief
+     * Connects options names and data.
+     */
+    void initMdpOptions(IOptionsContainerWithSections* options) override;
+
+    //! Report if this set of MDP options is active (i.e. QMMM MdModule is active)
+    bool active() const;
+
+    //! Get parameters_ instance
+    const QMMMParameters& parameters();
+
+    /*! \brief Evaluate and store atom indices.
+     * During pre-processing, use the group string from the options to
+     * evaluate the indices of both QM atoms and MM atoms, also stores them
+     * as vectors into the parameters_
+     * \param[in] indexGroupsAndNames object containing data about index groups and names
+     */
+    void setQMMMGroupIndices(const IndexGroupsAndNames& indexGroupsAndNames);
+
+    /*! \brief Process external QM input file in case it is provided with -qmi option of grompp.
+     * Produces parameters_.qmInput in case parameters_.qmMethod_ = INPUT
+     * \param[in] qmExternalInputFileName structure with information about external QM input
+     */
+    void setQMExternalInputFile(const QMInputFileName& qmExternalInputFileName);
+
+    /*! \brief Process coordinates, PbcType and Box in order to produce CP2K sample input.
+     * Produces qmPdb_ in all cases. Produces parameters_.qmInput_, parameters_.qmTrans_
+     * and parameters_.qmBox_ in case parameters_.qmMethod_ != INPUT.
+     * \param[in] coord structure with coordinates and box dimensions
+     */
+    void processCoordinates(const CoordinatesAndBoxPreprocessed& coord);
+
+    /*! \brief Modifies topology in case of active QMMM module using QMMMTopologyPreprocessor
+     * \param[in,out] mtop topology to modify for QMMM
+     */
+    void modifyQMMMTopology(gmx_mtop_t* mtop);
+
+    //! Store the paramers that are not mdp options in the tpr file
+    void writeInternalParametersToKvt(KeyValueTreeObjectBuilder treeBuilder);
+
+    //! Set the internal parameters that are stored in the tpr file
+    void readInternalParametersFromKvt(const KeyValueTreeObject& tree);
+
+    /*! \brief Process MdRunInputFilename notification during mdrun.
+     * In case parameters_.qmFileNameBase_ is empty sets it to tpr name with _cp2k suffix
+     * \param[in] tprFilename name of the *.tpr file that mdrun simulates
+     */
+    void processTprFilename(const MdRunInputFilename& tprFilename);
+
+    //! Set the MDLogger instance
+    void setLogger(const MDLogger& logger);
+
+    //! Set the warninp instance
+    void setWarninp(warninp* wi);
+
+private:
+    //! Write message to the log
+    void appendLog(const std::string& msg);
+
+    //! Write grompp warning
+    void appendWarning(const std::string& msg);
+
+    /*! \brief Processes external CP2K input file with the name qmExternalInputFileName_
+     * 1) Extracts parameters_.qmCharge_ and parameters_.qmMult_ from it
+     * 2) Replaces COORD_FILE_NAME parameter in it with a placeholder
+     * 3) Saves it into the parameters_.qmInput_
+     * \throws FileIOError if input file could not be read
+     * \throws InvalidInputError if MULTIPLICITY, CHARGE or COORD_FILE_NAME not found
+     */
+    void processExternalInputFile();
+
+    /*! \brief Following Tags denotes names of parameters from .mdp file
+     * \note Changing this strings will break .tpr backwards compability
+     */
+    //! \{
+    const std::string c_activeTag_              = "active";
+    const std::string c_qmGroupTag_             = "qmgroup";
+    const std::string c_qmChargeTag_            = "qmcharge";
+    const std::string c_qmMultTag_              = "qmmultiplicity";
+    const std::string c_qmMethodTag_            = "qmmethod";
+    const std::string c_qmUserInputFileNameTag_ = "qmfilenames";
+    //! \}
+
+    /*! \brief This tags for parameters which will be generated during grompp
+     * and stored into *.tpr file via KVT
+     */
+    //! \{
+    const std::string c_atomNumbersTag_ = "atomnumbers";
+    const std::string c_mmGroupTag_     = "mmgroup";
+    const std::string c_qmLinkTag_      = "qmlink";
+    const std::string c_mmLinkTag_      = "mmlink";
+    const std::string c_qmInputTag_     = "qminput";
+    const std::string c_qmPdbTag_       = "qmpdb";
+    const std::string c_qmBoxTag_       = "qmbox";
+    const std::string c_qmTransTag_     = "qmtrans";
+    //! \}
+
+    //! Logger instance
+    const MDLogger* logger_ = nullptr;
+
+    //! Instance of warning bookkeeper
+    warninp* wi_ = nullptr;
+
+    //! QM index group name, Default whole System
+    std::string groupString_ = "System";
+
+    //! QMMM parameters built from mdp input
+    QMMMParameters parameters_;
+
+    //! Name of the external input file provided with -qmi option of grompp
+    std::string qmExternalInputFileName_;
+
+    //! Vector with atoms point charges
+    std::vector<real> atomCharges_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/applied_forces/qmmm/qmmmtopologypreprocessor.h b/src/include/gromacs/applied_forces/qmmm/qmmmtopologypreprocessor.h
new file mode 100644 (file)
index 0000000..64a93ff
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * QMMMTopologyPrepocessor class responsible for
+ * all modificatios of the topology during input pre-processing
+ *
+ * \author Dmitry Morozov <dmitry.morozov@jyu.fi>
+ * \ingroup module_applied_forces
+ */
+#ifndef GMX_APPLIED_FORCES_QMMMTOPOLOGYPREPROCESSOR_H
+#define GMX_APPLIED_FORCES_QMMMTOPOLOGYPREPROCESSOR_H
+
+#include <set>
+#include <string>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/arrayref.h"
+
+#include "qmmmtypes.h"
+
+struct gmx_mtop_t;
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief Contains various information about topology modifications
+ * Used for statistics during topology pre-processing within QMMMTopologyPreprocessor class
+ */
+struct QMMMTopologyInfo
+{
+    //! Total number of MM atoms
+    int numMMAtoms = 0;
+    //! Total number of QM atoms
+    int numQMAtoms = 0;
+    //! Total remaining charge of MM part
+    real remainingMMCharge = 0.0;
+    //! Total classical charge removed from QM atoms
+    real totalClassicalChargeOfQMAtoms = 0.0;
+    //! Total number of Non-bonded (LJ) exclusions made for QM-QM interactions
+    int numExclusionsMade = 0;
+    //! Total number of removed classical Bonds between QM-QM atoms
+    int numBondsRemoved = 0;
+    //! Total number of removed classical Angles between QM-QM atoms
+    int numAnglesRemoved = 0;
+    //! Total number of removed classical Dihedrals between QM-QM atoms
+    int numDihedralsRemoved = 0;
+    //! Total number of removed F_SETTLE between QM-QM atoms
+    int numSettleRemoved = 0;
+    //! Total number of empty chemical bonds (F_CONNBONDS) added between QM-QM atoms
+    int numConnBondsAdded = 0;
+    //! Total number of virtual sites, that consisting of QM atoms only, which charge has been removed
+    int numVirtualSitesModified = 0;
+    //! Total number of constrained bonds within QM subsystem
+    int numConstrainedBondsInQMSubsystem = 0;
+    //! Total number of broken bonds between QM and MM atoms (Link Frontier)
+    int numLinkBonds = 0;
+};
+
+/*! \internal
+ * \brief Class implementing gmx_mtop_t QMMM modifications during preprocessing
+ * 1) Split QM-containing molecules from other molecules in blocks
+ * 2) Nullify charges on all virtual sites consisting of QM only atoms
+ * 3) Nullifies charges on all QM atoms
+ * 4) Excludes LJ interactions between QM atoms
+ * 5) Builds vector with atomic numbers of all atoms
+ * 6) Makes F_CONNBOND between atoms within QM region
+ * 7) Removes angles and settles containing 2 or more QM atoms
+ * 8) Removes dihedrals containing 3 or more QM atoms
+ * 9) Builds vector containing pairs of bonded QM - MM atoms (Link frontier)
+ */
+class QMMMTopologyPreprocessor
+{
+public:
+    /*! \brief Constructor for QMMMTopologyPreprocessor from its parameters
+     *
+     * \param[in] qmIndices Array with global indicies of QM atoms
+     */
+    QMMMTopologyPreprocessor(ArrayRef<const index> qmIndices);
+
+    /*! \brief Pocesses mtop topology and prepares atomNumbers_ and linkFrontier_ vectors
+     * Builds topInfo_ containing information about topology modifications
+     *
+     * \param[in,out] mtop Topology that needs to be modified
+     */
+    void preprocess(gmx_mtop_t* mtop);
+
+    //! \brief Returns data about modifications made via QMMMTopologyInfo
+    const QMMMTopologyInfo& topInfo() const;
+
+    //! \brief Returns view of atomic numbers for all atoms in the processed topology
+    ArrayRef<const int> atomNumbers() const;
+
+    //! \brief Returns view of point charges for all atoms in the processed topology
+    ArrayRef<const real> atomCharges() const;
+
+    //! \brief Returns view of the whole Link Frontier for the processed topology
+    ArrayRef<const LinkFrontier> linkFrontier() const;
+
+private:
+    //! Retruns true if globalAtomIndex belongs to QM region
+    bool isQMAtom(index globalAtomIndex);
+
+    /*! \brief Splits QM containing molecules out of MM blocks in topology
+     * Modifies blocks in topology
+     * Updates bQMBlock vector containing QM flags of all blocks in modified mtop
+     */
+    void splitQMblocks(gmx_mtop_t* mtop);
+
+    /*! \brief Removes classical charges from QM atoms
+     * Provides data about removed charge via topInfo_
+     */
+    void removeQMClassicalCharges(gmx_mtop_t* mtop);
+
+    //! \brief Build exlusion list for LJ interactions between QM atoms
+    void addQMLJExclusions(gmx_mtop_t* mtop);
+
+    /*! \brief Builds atomNumbers_ vector
+     * Provides data about total number of QM and MM atoms via topInfo_
+     */
+    void buildQMMMAtomNumbers(gmx_mtop_t* mtop);
+
+    /*! \brief Modifies pairwise bonded interactions
+     * Removes any other pairwise bonded interactions between QM-QM atoms
+     * Creates F_CONNBOND between QM atoms
+     * Any restraints and constraints will be kept
+     * Provides data about modifications via topInfo_
+     */
+    void modifyQMMMTwoCenterInteractions(gmx_mtop_t* mtop);
+
+    /*! \brief Builds link_ vector with pairs of atoms indicting broken QM - MM chemical bonds.
+     * Also performs search of constrained bonds within QM subsystem.
+     */
+    void buildQMMMLink(gmx_mtop_t* mtop);
+
+    /*! \brief Modifies three-centers interactions (i.e. Angles, Settles)
+     * Removes any other three-centers bonded interactions including 2 or more QM atoms
+     * Any restraints and constraints will be kept
+     * Any F_SETTLE containing QM atoms will be converted to the pair of F_CONNBONDS
+     * Provides data about modifications via topInfo_
+     */
+    void modifyQMMMThreeCenterInteractions(gmx_mtop_t* mtop);
+
+    /*! \brief Modifies four-centers interactions
+     * Removes any other four-centers bonded interactions including 3 or more QM atoms
+     * Any restraints and constraints will be kept
+     * Provides data about modifications via topInfo_
+     */
+    void modifyQMMMFourCenterInteractions(gmx_mtop_t* mtop);
+
+    //! \brief Removes charge from all virtual sites which are consists of only QM atoms
+    void modifyQMMMVirtualSites(gmx_mtop_t* mtop);
+
+    //! Vector indicating which molblocks have QM atoms
+    std::vector<bool> bQMBlock_;
+    /*! \brief Global indices of QM atoms;
+     * The dominant operation is search and we also expect the set of qm atoms to be very small
+     * relative to the rest, so set should outperform unordered set, i.e. unsorted std::vector.
+     */
+    std::set<int> qmIndices_;
+    //! Vector with atom numbers for the whole system
+    std::vector<int> atomNumbers_;
+    //! Vector with atom point charges for the whole system
+    std::vector<real> atomCharges_;
+    //! Vector with pairs of indices defining broken bonds in QMMM
+    std::vector<LinkFrontier> linkFrontier_;
+    //! Structure with information about modifications made
+    QMMMTopologyInfo topInfo_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/applied_forces/qmmm/qmmmtypes.h b/src/include/gromacs/applied_forces/qmmm/qmmmtypes.h
new file mode 100644 (file)
index 0000000..2977fca
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares structers and types needed to evaluate forces and energies for QM/MM
+ *
+ * \author Dmitry Morozov <dmitry.morozov@jyu.fi>
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_applied_forces
+ */
+#ifndef GMX_APPLIED_FORCES_QMMMTYPES_H
+#define GMX_APPLIED_FORCES_QMMMTYPES_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief Helper structure with indexes of broken bonds between QM and MM
+ * Used to determine and store pair of QM and MM atoms between which chemical bond is broken
+ */
+struct LinkFrontier
+{
+    //! Global index of QM atom at Frontier
+    index qm;
+    //! Global index of MM atom at Frontier
+    index mm;
+};
+
+/*! \brief Enumerator for supported QM methods
+ * Also could be INPUT which means external input file provided
+ * with the name determined by QMMMParameters::qminputfilename_
+ */
+enum class QMMMQMMethod
+{
+    PBE,   //!< DFT with PBE functional
+    BLYP,  //!< DFT with BLYP functional
+    INPUT, //!< User provides suitable input file for QM package
+    Count
+};
+
+//! The names of the supported QM methods
+static const EnumerationArray<QMMMQMMethod, const char*> c_qmmmQMMethodNames = {
+    { "PBE", "BLYP", "INPUT" }
+};
+
+//! symbols of the elements in periodic table
+const std::vector<std::string> periodic_system = {
+    "X  ", "H  ", "He ", "Li ", "Be ", "B  ", "C  ", "N  ", "O  ", "F  ", "Ne ", "Na ",
+    "Mg ", "Al ", "Si ", "P  ", "S  ", "Cl ", "Ar ", "K  ", "Ca ", "Sc ", "Ti ", "V  ",
+    "Cr ", "Mn ", "Fe ", "Co ", "Ni ", "Cu ", "Zn ", "Ga ", "Ge ", "As ", "Se ", "Br ",
+    "Kr ", "Rb ", "Sr ", "Y  ", "Zr ", "Nb ", "Mo ", "Tc ", "Ru ", "Rh ", "Pd ", "Ag ",
+    "Cd ", "In ", "Sn ", "Sb ", "Te ", "I  ", "Xe ", "Cs ", "Ba ", "La ", "Ce ", "Pr ",
+    "Nd ", "Pm ", "Sm ", "Eu ", "Gd ", "Tb ", "Dy ", "Ho ", "Er ", "Tm ", "Yb ", "Lu ",
+    "Hf ", "Ta ", "W  ", "Re ", "Os ", "Ir ", "Pt ", "Au ", "Hg ", "Tl ", "Pb ", "Bi ",
+    "Po ", "At ", "Rn ", "Fr ", "Ra ", "Ac ", "Th ", "Pa ", "U  ", "Np ", "Pu ", "Am ",
+    "Cm ", "Bk ", "Cf ", "Es ", "Fm ", "Md ", "No ", "Lr ", "Rf ", "Db ", "Sg ", "Bh ",
+    "Hs ", "Mt ", "Ds ", "Rg ", "Cn ", "Nh ", "Fl ", "Mc ", "Lv ", "Ts ", "Og "
+};
+
+/*! \internal
+ * \brief Holding all parameters needed for QM/MM simulation.
+ * Also used for setting all default parameter values.
+ */
+struct QMMMParameters
+{
+    //! Indicate if QM/MM is active (default false)
+    bool active_ = false;
+    //! Indices of the atoms that are part of the QM region (default whole System)
+    std::vector<index> qmIndices_;
+    //! Indices of the atoms that are part of the MM region (default no MM atoms)
+    std::vector<index> mmIndices_;
+    //! Vector with pairs of indicies defining broken bonds in QMMM (default determined from topology)
+    std::vector<LinkFrontier> link_;
+    //! Vector with atomic numbers of all atoms in the system (default determined from topology)
+    std::vector<int> atomNumbers_;
+    //! Total charge of QM system (default 0)
+    int qmCharge_ = 0;
+    //! Total multiplicity of QM system (default 1)
+    int qmMultiplicity_ = 1;
+    //! Method used for QM calculation (default DFT with PBE functional)
+    QMMMQMMethod qmMethod_ = QMMMQMMethod::PBE;
+    /*! \brief String containing name of the CP2K files (*.inp, *.out, *.pdb)
+     * default value empty, means will be deduced from *.tpr name during mdrun
+     */
+    std::string qmFileNameBase_;
+    //! String containing whole CP2K input which can be stored inside *.tpr
+    std::string qmInput_;
+    //! String containing PDB file for CP2K input which can be stored inside *.tpr
+    std::string qmPdb_;
+    //! Matrix that contains vectors defining QM box
+    matrix qmBox_;
+    //! Translation vector to center QM subsystem inside the QM Box
+    RVec qmTrans_;
+
+    //! Constructor with default initializers for arrays
+    QMMMParameters() :
+        qmBox_{ { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 }, { 0.0, 0.0, 0.0 } }, qmTrans_{ 0.0, 0.0, 0.0 }
+    {
+    }
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(QMMMParameters);
+};
+
+} // namespace gmx
+
+#endif // GMX_APPLIED_FORCES_QMMMTYPES_H
diff --git a/src/include/gromacs/commandline/cmdlinehelpcontext.h b/src/include/gromacs/commandline/cmdlinehelpcontext.h
new file mode 100644 (file)
index 0000000..59d82be
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::CommandLineHelpContext.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_CMDLINEHELPCONTEXT_H
+#define GMX_COMMANDLINE_CMDLINEHELPCONTEXT_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/onlinehelp/helpwritercontext.h"
+#include "gromacs/utility/classhelpers.h"
+
+namespace gmx
+{
+
+class ShellCompletionWriter;
+
+/*! \libinternal \brief
+ * Context information for writing out command-line help.
+ *
+ * This class wraps a HelpWriterContext, extending it with information specific
+ * for command-line help export.  This way, code using only the routines in the
+ * onlinehelp module is not exposed to extra features of the command-line help
+ * export.
+ *
+ * Copying a context works like with HelpWriterContext: the output file and
+ * most state is shared.  However, setModuleDisplayName() and setShowHidden()
+ * can be set independently for the child context.  Defaults for these options
+ * are inherited from the parent.
+ *
+ * \ingroup module_commandline
+ */
+class CommandLineHelpContext
+{
+public:
+    /*! \brief
+     * Creates a context for help export.
+     *
+     * Wraps the constructor of HelpWriterContext.
+     */
+    CommandLineHelpContext(TextWriter*        writer,
+                           HelpOutputFormat   format,
+                           const HelpLinks*   links,
+                           const std::string& programName);
+    //! Creates a context for a particular HelpWriterContext.
+    explicit CommandLineHelpContext(const HelpWriterContext& writerContext);
+    /*! \brief
+     * Creates a context for shell completion.
+     */
+    explicit CommandLineHelpContext(ShellCompletionWriter* writer);
+    //! Creates a copy of the context.
+    explicit CommandLineHelpContext(const CommandLineHelpContext& other);
+    //! Moves the context.
+    CommandLineHelpContext(CommandLineHelpContext&& other) noexcept;
+    //! Move-assigns the context.
+    CommandLineHelpContext& operator=(CommandLineHelpContext&& other) noexcept;
+    ~CommandLineHelpContext();
+
+    /*! \brief
+     * Sets a display name for the module for which help is being written.
+     *
+     * \throws std::bad_alloc if out of memory.
+     */
+    void setModuleDisplayName(const std::string& name);
+    //! Sets whether hidden options should be shown in help output.
+    void setShowHidden(bool bHidden);
+    //! \copydoc HelpWriterContext::enterSubSection()
+    void enterSubSection(const std::string& title);
+
+    //! Returns the lower-level context for writing the help.
+    const HelpWriterContext& writerContext() const;
+    /*! \brief
+     * Returns a display name for the module for which help is being written.
+     *
+     * Does not throw.
+     */
+    const char* moduleDisplayName() const;
+    //! Returns whether hidden options should be shown in help output.
+    bool showHidden() const;
+    //! Returns whether this context is for exporting shell completions.
+    bool isCompletionExport() const;
+    /*! \brief
+     * Returns the shell completion writer for this context.
+     *
+     * Can only be called if isCompletionExport() returns `true`.
+     */
+    ShellCompletionWriter& shellCompletionWriter() const;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+
+    GMX_DISALLOW_ASSIGN(CommandLineHelpContext);
+};
+
+/*! \libinternal \brief
+ * Helper for passing CommandLineHelpContext into parse_common_args().
+ *
+ * This class provides a mechanism to set and retrieve a global
+ * CommandLineHelpContext object.  It is used to pass this object into
+ * parse_common_args() from CommandLineModuleManager::runAsMainCMain() through
+ * the main() function that is not aware of the wrapper binary mechanism.
+ * It is not thread-safe because in this limited use case, it is always called
+ * from a single-threaded context.
+ *
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+class GlobalCommandLineHelpContext
+{
+public:
+    //! Returns the global context, or NULL if not set.
+    static const CommandLineHelpContext* get();
+
+    /*! \brief
+     * Sets the global context for the scope.
+     *
+     * The global context is cleared when this object goes out of scope.
+     *
+     * It is an error to have more than one GlobalCommandLineHelpContext
+     * object in existence at the same time.
+     */
+    explicit GlobalCommandLineHelpContext(const CommandLineHelpContext& context);
+    //! Clears the global context.
+    ~GlobalCommandLineHelpContext();
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/commandline/cmdlinehelpmodule.h b/src/include/gromacs/commandline/cmdlinehelpmodule.h
new file mode 100644 (file)
index 0000000..9b32961
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares gmx::CommandLineHelpModule.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_CMDLINEHELPMODULE_H
+#define GMX_COMMANDLINE_CMDLINEHELPMODULE_H
+
+#include <memory>
+
+#include "gromacs/commandline/cmdlinemodule.h"
+#include "gromacs/onlinehelp/ihelptopic.h"
+
+#include "cmdlinemodulemanager_impl.h"
+
+namespace gmx
+{
+
+class CommandLineHelpContext;
+class IFileOutputRedirector;
+class IProgramContext;
+
+class CommandLineHelpModuleImpl;
+
+/*! \internal
+ * \brief
+ * Command-line module for producing help.
+ *
+ * This module implements the 'help' subcommand that is automatically added by
+ * CommandLineModuleManager.
+ *
+ * \ingroup module_commandline
+ */
+class CommandLineHelpModule : public ICommandLineModule
+{
+public:
+    /*! \brief
+     * Creates a command-line help module.
+     *
+     * \param[in] programContext Information about the running binary.
+     * \param[in] binaryName     Name of the running binary
+     *     (without Gromacs binary suffix or .exe on Windows).
+     * \param[in] modules  List of modules for to use for module listings.
+     * \param[in] groups   List of module groups.
+     * \throws    std::bad_alloc if out of memory.
+     */
+    CommandLineHelpModule(const IProgramContext&            programContext,
+                          const std::string&                binaryName,
+                          const CommandLineModuleMap&       modules,
+                          const CommandLineModuleGroupList& groups);
+    ~CommandLineHelpModule() override;
+
+    /*! \brief
+     * Creates a help topic for a command-line module.
+     *
+     * \param[in] module  Module the create the help topic for.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * The caller should add the topic using addTopic() if that is desired.
+     * This method is provided separately to allow for strong exception
+     * safety in CommandLineModuleManager::addModule().
+     */
+    HelpTopicPointer createModuleHelpTopic(const ICommandLineModule& module) const;
+    /*! \brief
+     * Adds a top-level help topic.
+     *
+     * \param[in] topic     Help topic to add.
+     * \param[in] bExported Whether this topic will be directly exported to
+     *     the user guide.
+     * \throws    std::bad_alloc if out of memory.
+     */
+    void addTopic(HelpTopicPointer topic, bool bExported);
+    //! Sets whether hidden options will be shown in help.
+    void setShowHidden(bool bHidden);
+    /*! \brief
+     * Sets an override to show the help for the given module.
+     *
+     * If called, the help module directly prints the help for the given
+     * module when called, skipping any other processing.
+     */
+    void setModuleOverride(const ICommandLineModule& module);
+
+    /*! \brief
+     * Sets a file redirector for writing help output.
+     *
+     * Used for unit testing; see
+     * CommandLineModuleManager::setOutputRedirector() for more details.
+     */
+    void setOutputRedirector(IFileOutputRedirector* output);
+
+    const char* name() const override { return "help"; }
+    const char* shortDescription() const override { return "Print help information"; }
+
+    void init(CommandLineModuleSettings* settings) override { settings->setDefaultNiceLevel(0); }
+    int  run(int argc, char* argv[]) override;
+    void writeHelp(const CommandLineHelpContext& context) const override;
+
+private:
+    typedef CommandLineHelpModuleImpl Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/commandline/cmdlinehelpwriter.h b/src/include/gromacs/commandline/cmdlinehelpwriter.h
new file mode 100644 (file)
index 0000000..8f6ac37
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::CommandLineHelpWriter.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_CMDLINEHELPWRITER_H
+#define GMX_COMMANDLINE_CMDLINEHELPWRITER_H
+
+#include <memory>
+#include <string>
+
+namespace gmx
+{
+
+class CommandLineHelpContext;
+class Options;
+
+template<typename T>
+class ArrayRef;
+
+/*! \brief
+ * Writes help information for Options.
+ *
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+class CommandLineHelpWriter
+{
+public:
+    /*! \brief
+     * Creates an object that writer ascii-formatted help for Options.
+     *
+     * \param[in] options  Options for which help should be printed.
+     */
+    explicit CommandLineHelpWriter(const Options& options);
+    ~CommandLineHelpWriter();
+
+    /*! \brief
+     * Sets the help text to print as description.
+     *
+     * \param[in] help  Help text to show.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * If `help` is empty, or this method is not called, only a list of
+     * options is printed.
+     * Formatting for the help text is described on \ref page_onlinehelp.
+     */
+    CommandLineHelpWriter& setHelpText(const std::string& help);
+    //! \copydoc setHelpText(const std::string &)
+    CommandLineHelpWriter& setHelpText(const ArrayRef<const char* const>& help);
+    /*! \brief
+     * Sets the list of known bugs/limitations.
+     *
+     * \param[in] bugs  Array of bugs/limitations.
+     *
+     * Each entry in the input array identifies a separate issue.
+     * The array passed should remain valid for the lifetime of the writer
+     * object.
+     */
+    CommandLineHelpWriter& setKnownIssues(const ArrayRef<const char* const>& bugs);
+
+    /*! \brief
+     * Sets text for known bugs.
+     *
+     * \param[in] bug  Text for bugs to show.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * Formatting for the text is described on \ref page_onlinehelp.
+     */
+    CommandLineHelpWriter& setKnownIssues(ArrayRef<const std::string> bug);
+
+    /*! \brief
+     * Writes the help.
+     *
+     * \param[in] context  Context object for writing the help.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    FileIOError on any I/O error.
+     */
+    void writeHelp(const CommandLineHelpContext& context);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/commandline/cmdlineinit.h b/src/include/gromacs/commandline/cmdlineinit.h
new file mode 100644 (file)
index 0000000..ab7cbfa
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares functions for initializing the \Gromacs library for command line use.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_CMDLINEINIT_H
+#define GMX_COMMANDLINE_CMDLINEINIT_H
+
+#include <functional>
+#include <memory>
+
+// Forward declaration of class CommandLineProgramContext is not sufficient for
+// MSVC if the return value of initForCommandLine() is ignored(!)
+#include "gromacs/commandline/cmdlineprogramcontext.h"
+
+namespace gmx
+{
+
+class ICommandLineModule;
+class ICommandLineOptionsModule;
+
+/*! \brief
+ * Initializes the \Gromacs library for command-line use.
+ *
+ * \param[in] argc  argc value passed to main().
+ * \param[in] argv  argv array passed to main().
+ * \returns   Reference to initialized program context object.
+ *
+ * This function is tailored for use in command line applications.
+ * For other usage, combination of gmx::init() and gmx::setProgramContext()
+ * provides more flexible initialization alternatives.
+ * Unlike gmx::init(), calls to this method cannot be nested.
+ *
+ * The command line arguments are communicated so that they can be
+ * parsed on each processor.
+ * \p argc and \p argv are passed to gmx::init(); see there for additional
+ * discussion.  This method does not place any additional limitations, but
+ * generally there should be no need to pass NULL values.
+ *
+ * Does not throw. Terminates the program on out-of-memory error.
+ *
+ * This method is not thread-safe, since it is intended to be the first method
+ * called.  See setProgramContext() for additional discussion.
+ *
+ * \see gmx::init()
+ * \see setProgramContext()
+ * \ingroup module_commandline
+ */
+CommandLineProgramContext& initForCommandLine(int* argc, char*** argv);
+/*! \brief
+ * Deinitializes the \Gromacs library after initForCommandLine().
+ *
+ * Calls gmx::finalize() and additionally undoes the work done by
+ * initForCommandLine().
+ *
+ * \see gmx::finalize()
+ * \ingroup module_commandline
+ */
+void finalizeForCommandLine();
+/*! \brief
+ * Handles an exception and deinitializes after initForCommandLine.
+ *
+ * \param[in] ex  Exception that is the cause for terminating the program.
+ * \returns   Return code to return from main().
+ *
+ * This method should be called as the last thing before terminating the
+ * program because of an exception. See processExceptionAtExit() for details.
+ * Additionally this method undoes the work done by initForCommandLine.
+ *
+ * Does not throw.
+ */
+int processExceptionAtExitForCommandLine(const std::exception& ex);
+/*! \brief
+ * Implements a main() method that runs a single module.
+ *
+ * \param argc   \c argc passed to main().
+ * \param argv   \c argv passed to main().
+ * \param module Module to run.
+ *
+ * This method allows for uniform behavior for binaries that only
+ * contain a single module without duplicating any of the
+ * implementation from CommandLineModuleManager (startup headers,
+ * common options etc.).
+ *
+ * The signature assumes that \p module construction does not throw
+ * (because otherwise the caller would need to duplicate all the
+ * exception handling code).  It is possible to move the construction
+ * inside the try/catch in this method using an indirection similar to
+ * TrajectoryAnalysisCommandLineRunner::runAsMain(), but until that is
+ * necessary, the current approach leads to simpler code.
+ *
+ * Usage:
+ * \code
+   int main(int argc, char *argv[])
+   {
+       CustomCommandLineModule module;
+       return gmx::runCommandLineModule(argc, argv, &module);
+   }
+   \endcode
+ *
+ * Does not throw.  All exceptions are caught and handled internally.
+ */
+int runCommandLineModule(int argc, char* argv[], ICommandLineModule* module);
+/*! \brief
+ * Implements a main() method that runs a single module.
+ *
+ * \param     argc        \c argc passed to main().
+ * \param     argv        \c argv passed to main().
+ * \param[in] name        Name for the module.
+ * \param[in] description Short description for the module.
+ * \param     factory Factory method that creates the module to run.
+ *
+ * This method allows for uniform behavior for binaries that only
+ * contain a single module without duplicating any of the
+ * implementation from CommandLineModuleManager (startup headers,
+ * common options etc.).
+ *
+ * Usage:
+ * \code
+   class CustomCommandLineOptionsModule : public ICommandLineOptionsModule
+   {
+       // <...>
+   };
+
+   static ICommandLineOptionsModule *create()
+   {
+       return new CustomCommandLineOptionsModule();
+   }
+
+   int main(int argc, char *argv[])
+   {
+       return gmx::runCommandLineModule(
+               argc, argv, "mymodule", "short description", &create);
+   }
+   \endcode
+ *
+ * Does not throw.  All exceptions are caught and handled internally.
+ */
+int runCommandLineModule(int                                                         argc,
+                         char*                                                       argv[],
+                         const char*                                                 name,
+                         const char*                                                 description,
+                         std::function<std::unique_ptr<ICommandLineOptionsModule>()> factory);
+
+} // namespace gmx
+
+/*! \brief
+ * Implements a main() method that runs a given C main function.
+ *
+ * \param argc         \c argc passed to main().
+ * \param argv         \c argv passed to main().
+ * \param mainFunction The main()-like method to wrap.
+ *
+ * This method creates a dummy command line module that does its
+ * processing by calling \p mainFunction.  It then runs this module as with
+ * gmx::runCommandLineModule().
+ * This allows the resulting executable to handle common options and do
+ * other common actions (e.g., startup headers) without duplicate code
+ * in the main methods.
+ *
+ * \p mainFunction should call parse_common_args() to process its command-line
+ * arguments.
+ *
+ * Usage:
+ * \code
+   int my_main(int argc, char *argv[])
+   {
+       // <...>
+   }
+
+   int main(int argc, char *argv[])
+   {
+       return gmx_run_cmain(argc, argv, &my_main);
+   }
+   \endcode
+ *
+ * Does not throw.  All exceptions are caught and handled internally.
+ */
+int gmx_run_cmain(int argc, char* argv[], int (*mainFunction)(int, char*[]));
+
+#endif
diff --git a/src/include/gromacs/commandline/cmdlinemodule.h b/src/include/gromacs/commandline/cmdlinemodule.h
new file mode 100644 (file)
index 0000000..509993f
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::ICommandLineModule and supporting classes.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_CMDLINEMODULE_H
+#define GMX_COMMANDLINE_CMDLINEMODULE_H
+
+#include <memory>
+
+namespace gmx
+{
+
+class CommandLineHelpContext;
+
+/*! \brief
+ * Settings to pass information between a module and the general runner.
+ *
+ * Methods in this class do not throw, except that construction may throw
+ * std::bad_alloc.
+ *
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+class CommandLineModuleSettings
+{
+public:
+    CommandLineModuleSettings();
+    ~CommandLineModuleSettings();
+
+    //! Returns the default nice level for this module.
+    int defaultNiceLevel() const;
+
+    /*! \brief
+     * Sets the default nice level for this module.
+     *
+     * If not called, the module will be niced.
+     */
+    void setDefaultNiceLevel(int niceLevel);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \brief
+ * Module that can be run from command line using CommandLineModuleManager.
+ *
+ * \see CommandLineModuleManager
+ * \see CommandLineOptionsModule
+ *
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+class ICommandLineModule
+{
+public:
+    virtual ~ICommandLineModule() {}
+
+    //! Returns the name of the module.
+    virtual const char* name() const = 0;
+    //! Returns a one-line description of the module.
+    virtual const char* shortDescription() const = 0;
+
+    /*! \brief
+     * Initializes the module and provides settings for the runner.
+     *
+     * This will be called before run(), and can be used to adjust
+     * initialization that the runner does.
+     *
+     * This method is currently not called when writing the help.
+     */
+    virtual void init(CommandLineModuleSettings* settings) = 0;
+    /*! \brief
+     * Runs the module with the given arguments.
+     *
+     * \param[in] argc  Number of elements in \p argv.
+     * \param[in] argv  Command-line arguments.
+     * \throws   unspecified  May throw exceptions to indicate errors.
+     * \returns  Exit code for the program.
+     * \retval   0 on successful termination.
+     *
+     * \p argv[0] is the name of the module, i.e., the arguments are as if
+     * the module was run as a standalone executable.
+     */
+    virtual int run(int argc, char* argv[]) = 0;
+    /*! \brief
+     * Prints help for the module.
+     *
+     * \param[in] context  Context object for writing the help.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    FileIOError on any I/O error.
+     *
+     * Note that for MPI-enabled builds, this is called only on the master
+     * rank.
+     */
+    virtual void writeHelp(const CommandLineHelpContext& context) const = 0;
+};
+
+//! \cond libapi
+/*! \libinternal \brief
+ * Helper to implement ICommandLineModule::writeHelp() with a C-like
+ * main() function that calls parse_common_args().
+ *
+ * \param[in] context      Context object for writing the help.
+ * \param[in] name         Name of the module.
+ * \param[in] mainFunction C-like main() function that calls parse_common_args().
+ *
+ * \ingroup module_commandline
+ */
+void writeCommandLineHelpCMain(const CommandLineHelpContext& context,
+                               const char*                   name,
+                               int (*mainFunction)(int argc, char* argv[]));
+//! \endcond
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/commandline/cmdlinemodulemanager.h b/src/include/gromacs/commandline/cmdlinemodulemanager.h
new file mode 100644 (file)
index 0000000..c1bca86
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::CommandLineModuleManager.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_CMDLINEMODULEMANAGER_H
+#define GMX_COMMANDLINE_CMDLINEMODULEMANAGER_H
+
+#include <memory>
+
+#include "gromacs/onlinehelp/ihelptopic.h"
+
+namespace gmx
+{
+
+class CommandLineModuleGroup;
+class CommandLineModuleGroupData;
+class CommandLineModuleSettings;
+class CommandLineProgramContext;
+class ICommandLineModule;
+class IFileOutputRedirector;
+
+//! \addtogroup module_commandline
+//! \{
+
+//! Smart pointer type for managing a ICommandLineModule.
+typedef std::unique_ptr<ICommandLineModule> CommandLineModulePointer;
+
+/*! \libinternal \brief
+ * Implements a wrapper command-line interface for multiple modules.
+ *
+ * Typical usage:
+ * \code
+   int main(int argc, char *argv[])
+   {
+       gmx::CommandLineProgramContext &programContext = gmx::initForCommandLine(&argc, &argv);
+       try
+       {
+           gmx::CommandLineModuleManager manager("gmx", &programContext);
+           // <register all necessary modules>
+           int rc = manager.run(argc, argv);
+           gmx::finalizeForCommandLine();
+           return rc;
+       }
+       catch (const std::exception &ex)
+       {
+           gmx::printFatalErrorMessage(stderr, ex);
+           return gmx::processExceptionAtExitForCommandLine(ex);
+       }
+   }
+ * \endcode
+ *
+ * \see page_wrapperbinary
+ * \inlibraryapi
+ */
+class CommandLineModuleManager
+{
+public:
+    //! Function pointer type for a C main function.
+    typedef int (*CMainFunction)(int argc, char* argv[]);
+    //! Function pointer to a settings provider.
+    typedef void (*InitSettingsFunction)(CommandLineModuleSettings* settings);
+
+    /*! \brief
+     * Implements a main() method that runs a single module.
+     *
+     * \param argc   \c argc passed to main().
+     * \param argv   \c argv passed to main().
+     * \param module Module to run.
+     *
+     * This method allows for uniform behavior for binaries that only
+     * contain a single module without duplicating any of the
+     * implementation from CommandLineModuleManager (startup headers,
+     * common options etc.).
+     *
+     * The signature assumes that \p module construction does not throw
+     * (because otherwise the caller would need to duplicate all the
+     * exception handling code).  It is possible to move the construction
+     * inside the try/catch in this method using an indirection similar to
+     * TrajectoryAnalysisCommandLineRunner::runAsMain(), but until that is
+     * necessary, the current approach leads to simpler code.
+     *
+     * Usage:
+     * \code
+       int main(int argc, char *argv[])
+       {
+           CustomCommandLineModule module;
+           return gmx::CommandLineModuleManager::runAsMainSingleModule(argc, argv, &module);
+       }
+     * \endcode
+     *
+     * Does not throw.  All exceptions are caught and handled internally.
+     */
+    static int runAsMainSingleModule(int argc, char* argv[], ICommandLineModule* module);
+    /*! \brief
+     * Implements a main() method that runs a given function.
+     *
+     * \param argc         \c argc passed to main().
+     * \param argv         \c argv passed to main().
+     * \param mainFunction The main()-like method to wrap.
+     *
+     * This method creates a dummy command-line module that does its
+     * processing by calling \p mainFunction; see addModuleCMain() for
+     * details.  It then runs this module with runAsMainSingleModule().
+     * This allows the resulting executable to handle common options and do
+     * other common actions (e.g., startup headers) without duplicate code
+     * in the main methods.
+     *
+     * Usage:
+     * \code
+       int my_main(int argc, char *argv[])
+       {
+           // <...>
+       }
+
+       int main(int argc, char *argv[])
+       {
+           return gmx::CommandLineModuleManager::runAsMainCMain(argc, argv, &my_main);
+       }
+     * \endcode
+     *
+     * Does not throw.  All exceptions are caught and handled internally.
+     */
+    static int runAsMainCMain(int argc, char* argv[], CMainFunction mainFunction);
+    /*! \brief
+     * Implements a main() method that runs a given function with custom
+     * settings.
+     *
+     * This method does the same as runAsMainCMain(), but additionally
+     * calls \p settingsFunction to initialize CommandLineModuleSettings.
+     * This allows specifying, e.g., a different default nice level.
+     */
+    static int runAsMainCMainWithSettings(int                  argc,
+                                          char*                argv[],
+                                          CMainFunction        mainFunction,
+                                          InitSettingsFunction settingsFunction);
+
+    /*! \brief
+     * Initializes a command-line module manager.
+     *
+     * \param[in] binaryName     Name of the running binary
+     *     (without Gromacs binary suffix or .exe on Windows).
+     * \param     programContext Program information for the running binary.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * \p binaryName is used to detect when the binary is run through a
+     * symlink, and automatically invoke a matching module in such a case.
+     *
+     * \p programInfo is non-const to allow the manager to amend it based
+     * on the actual module that is getting executed.
+     */
+    CommandLineModuleManager(const char* binaryName, CommandLineProgramContext* programContext);
+    ~CommandLineModuleManager();
+
+    /*! \brief
+     * Sets the module manager to quiet mode: don't print anything.
+     *
+     * \param[in] bQuiet  Whether the module manager should remain silent.
+     *
+     * Normally, the module manager prints out some information to `stderr`
+     * before it starts the module and after it finishes.  This removes
+     * that output, which is useful in particular for unit tests so that
+     * they don't spam `stderr`.
+     */
+    void setQuiet(bool bQuiet);
+    /*! \brief
+     * Redirects the output of the module manager to a file.
+     *
+     * \param[in] output  File redirector to use for output.
+     *
+     * Normally, the module manager prints explicitly requested text such
+     * as help output to `stdout`, but this method can be used to redirect
+     * that output to a file.  For exporting help from the module manager,
+     * several files are written, and can be redirected with this method as
+     * well.
+     *
+     * This is used for unit tests, either to keep them quiet or to verify
+     * that output.  To keep implementation options open, behavior with
+     * `output == NULL` is undefined and should not be relied on.
+     * For tests, there should only be need to call this a single time,
+     * right after creating the manager.
+     */
+    void setOutputRedirector(IFileOutputRedirector* output);
+
+    /*! \brief
+     * Makes the manager always run a single module.
+     *
+     * \param     module  Module to run.
+     *
+     * This method disables all mechanisms for selecting a module, and
+     * directly passes all command-line arguments to \p module.
+     * Help arguments are an exception: these are still recognized by the
+     * manager and translated into a call to
+     * ICommandLineModule::writeHelp().
+     *
+     * This is public mainly for unit testing purposes; for other code,
+     * runAsMainSingleModule() typically provides the desired
+     * functionality.
+     *
+     * Does not throw.
+     */
+    void setSingleModule(ICommandLineModule* module);
+    /*! \brief
+     * Adds a given module to this manager.
+     *
+     * \param   module  Module to add.
+     * \throws  std::bad_alloc if out of memory.
+     *
+     * The manager takes ownership of the object.
+     *
+     * This method is public mostly for testing purposes; for typical uses,
+     * registerModule() is a more convenient way of adding modules.
+     *
+     * \see registerModule()
+     */
+    void addModule(CommandLineModulePointer module);
+    /*! \brief
+     * Adds a module that runs a given main()-like function.
+     *
+     * \param[in] name             Name for the module.
+     * \param[in] shortDescription One-line description for the module.
+     * \param[in] mainFunction     Main function to wrap.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * There is normally no need to call this method outside the Gromacs
+     * library.  User code usually wants to use runAsMainCMain().
+     *
+     * \p name and \p shortDescription should be string constants, or the
+     * caller should otherwise ensure that they stay in scope for the
+     * duration the CommandLineModuleManager object exists.
+     * \p mainFunction should call parse_common_args() to process its
+     * command-line arguments.
+     */
+    void addModuleCMain(const char* name, const char* shortDescription, CMainFunction mainFunction);
+    /*! \brief
+     * Adds a module that runs a given main()-like function with custom
+     * settings.
+     *
+     * This method does the same as runAsMainCMain(), but additionally
+     * calls \p settingsFunction to initialize CommandLineModuleSettings.
+     * This allows specifying, e.g., a different default nice level.
+     */
+    void addModuleCMainWithSettings(const char*          name,
+                                    const char*          shortDescription,
+                                    CMainFunction        mainFunction,
+                                    InitSettingsFunction settingsFunction);
+    /*! \brief
+     * Registers a module of a certain type to this manager.
+     *
+     * \tparam  Module  Type of module to register.
+     * \throws  std::bad_alloc if out of memory.
+     *
+     * \p Module must be default-constructible and implement
+     * ICommandLineModule.
+     *
+     * This method is provided as a convenient alternative to addModule()
+     * for cases where each module is implemented by a different type
+     * (which should be the case for typical situations outside unit
+     * tests).
+     */
+    template<class Module>
+    void registerModule()
+    {
+        addModule(CommandLineModulePointer(new Module));
+    }
+
+    /*! \brief
+     * Adds a group for modules to use in help output.
+     *
+     * \param[in] title  Short title for the group.
+     * \returns   Handle that can be used to add modules to the group.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * Creates a group that is used to structure the list of all modules in
+     * help output.  Modules are added to the group using the returned
+     * object.
+     */
+    CommandLineModuleGroup addModuleGroup(const char* title);
+
+    /*! \brief
+     * Makes given help topic available through the manager's help module.
+     *
+     * \param[in]  topic  Help topic to add.
+     * \throws     std::bad_alloc if out of memory.
+     *
+     * The manager takes ownership of the help topic.
+     */
+    void addHelpTopic(HelpTopicPointer topic);
+
+    /*! \brief
+     * Runs a module based on given command line.
+     *
+     * \param[in] argc  Number of elements in \p argv.
+     * \param[in] argv  Command-line arguments.
+     * \throws   unspecified  Throws any exception that the selected module
+     *      throws.
+     * \returns  Exit code for the program.
+     * \retval   0 on successful termination.
+     * \retval   2 if no module is specified, or if the module is not found.
+     *
+     * Runs the module whose name matches \p argv[1].
+     */
+    int run(int argc, char* argv[]);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \libinternal \brief
+ * Handle to add content to a group added with
+ * CommandLineModuleManager::addModuleGroup().
+ *
+ * This class only provides a public interface to construct a module group for
+ * CommandLineModuleManager, and has semantics similar to a pointer: copies all
+ * point to the same group.  The actual state of the group is maintained in an
+ * internal implementation class.
+ *
+ * \inlibraryapi
+ */
+class CommandLineModuleGroup
+{
+public:
+    /*! \cond internal */
+    //! Shorthand for the implementation type that holds all the data.
+    typedef CommandLineModuleGroupData Impl;
+
+    //! Creates a new group (only called by CommandLineModuleManager).
+    explicit CommandLineModuleGroup(Impl* impl) : impl_(impl) {}
+    //! \endcond
+
+    /*! \brief
+     * Adds a module to this group.
+     *
+     * \param[in] name  Name of the module.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * This works as addModuleWithDescription(), but uses the short
+     * description of the module itself as the description.
+     *
+     * \see addModuleWithDescription()
+     */
+    void addModule(const char* name);
+    /*! \brief
+     * Adds a module to this group with a custom description.
+     *
+     * \param[in] name        Name of the module.
+     * \param[in] description Description of the module in this group.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * \p name must name a module added into the CommandLineModuleManager.
+     * It is possible to add the same module into multiple groups.
+     */
+    void addModuleWithDescription(const char* name, const char* description);
+
+private:
+    //! Pointer to the data owned by CommandLineModuleManager.
+    Impl* impl_;
+};
+
+//! \}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/commandline/cmdlinemodulemanager_impl.h b/src/include/gromacs/commandline/cmdlinemodulemanager_impl.h
new file mode 100644 (file)
index 0000000..1af5f01
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares implementation types for gmx::CommandLineModuleManager.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_CMDLINEMODULEMANAGER_IMPL_H
+#define GMX_COMMANDLINE_CMDLINEMODULEMANAGER_IMPL_H
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/commandline/cmdlinemodule.h"
+#include "gromacs/commandline/cmdlinemodulemanager.h"
+#include "gromacs/options/options.h"
+#include "gromacs/utility/binaryinformation.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+//! \addtogroup module_commandline
+//! \{
+
+//! Container type for mapping module names to module objects.
+typedef std::map<std::string, CommandLineModulePointer> CommandLineModuleMap;
+
+/*! \internal
+ * \brief
+ * Internal data for a CommandLineModuleManager module group.
+ *
+ * This class contains the state of a module group.  CommandLineModuleGroup
+ * provides the public interface to construct/alter the state, and
+ * CommandLineModuleManager and its associated classes use it for help output.
+ */
+class CommandLineModuleGroupData
+{
+public:
+    /*! \brief
+     * Shorthand for a list of modules contained in the group.
+     *
+     * The first element in the contained pair contains the tag
+     * (gmx-something) of the module, and the second element contains the
+     * description.  The second element is never NULL.
+     */
+    typedef std::vector<std::pair<std::string, const char*>> ModuleList;
+
+    /*! \brief
+     * Constructs an empty module group.
+     *
+     * \param[in] modules     List of all modules
+     *     (used for checking and default descriptions).
+     * \param[in] binaryName  Name of the binary containing the modules.
+     * \param[in] title       Title of the group.
+     *
+     * Does not throw.
+     */
+    CommandLineModuleGroupData(const CommandLineModuleMap& modules, const char* binaryName, const char* title) :
+        allModules_(modules), binaryName_(binaryName), title_(title)
+    {
+    }
+
+    //! Returns the title for the group.
+    const char* title() const { return title_; }
+    //! Returns the list of modules in the group.
+    const ModuleList& modules() const { return modules_; }
+
+    /*! \brief
+     * Adds a module to the group.
+     *
+     * \param[in] name        Name of the module.
+     * \param[in] description Description of the module in this group.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * If \p description is NULL, the description returned by the module is
+     * used.
+     */
+    void addModule(const char* name, const char* description);
+
+private:
+    const CommandLineModuleMap& allModules_;
+    const char*                 binaryName_;
+    const char*                 title_;
+    ModuleList                  modules_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineModuleGroupData);
+};
+
+//! Smart pointer type for managing a CommandLineModuleGroup.
+typedef std::unique_ptr<CommandLineModuleGroupData> CommandLineModuleGroupDataPointer;
+//! Container type for keeping a list of module groups.
+typedef std::vector<CommandLineModuleGroupDataPointer> CommandLineModuleGroupList;
+
+/*! \internal
+ * \brief
+ * Encapsulates some handling of common options to the wrapper binary.
+ */
+class CommandLineCommonOptionsHolder
+{
+public:
+    CommandLineCommonOptionsHolder();
+    ~CommandLineCommonOptionsHolder();
+
+    //! Initializes the common options.
+    void initOptions();
+    /*! \brief
+     * Finishes option parsing.
+     *
+     * \returns `false` if the wrapper binary should quit without executing
+     *     any module.
+     */
+    bool finishOptions();
+
+    //! Adjust defaults based on module settings.
+    void adjustFromSettings(const CommandLineModuleSettings& settings);
+
+    //! Returns the internal Options object.
+    Options* options() { return &options_; }
+    //! Returns the settings for printing startup information.
+    const BinaryInformationSettings& binaryInfoSettings() const { return binaryInfoSettings_; }
+
+    /*! \brief
+     * Returns `true` if common options are set such that the wrapper
+     * binary should quit, without running the actual module.
+     */
+    bool shouldIgnoreActualModule() const { return bHelp_ || bVersion_; }
+    //! Returns whether common options specify showing help.
+    bool shouldShowHelp() const { return bHelp_; }
+    //! Returns whether common options specify showing hidden options in help.
+    bool shouldShowHidden() const { return bHidden_; }
+    //! Returns whether common options specify quiet execution.
+    bool shouldBeQuiet() const { return bQuiet_ && !bVersion_; }
+    //! Returns whether backups should be made.
+    bool shouldBackup() const { return bBackup_; }
+
+    //! Returns the nice level.
+    int niceLevel() const { return niceLevel_; }
+    //! Returns whether floating-point exception should be enabled
+    bool enableFPExceptions() const { return bFpexcept_; }
+    //! Returns the debug level.
+    int debugLevel() const { return debugLevel_; }
+
+    //! Returns the file to which startup information should be printed.
+    FILE* startupInfoFile() const { return (bVersion_ ? stdout : stderr); }
+
+private:
+    Options options_;
+    //! Settings for what to write in the startup header.
+    BinaryInformationSettings binaryInfoSettings_;
+    bool                      bHelp_;
+    bool                      bHidden_;
+    bool                      bQuiet_;
+    bool                      bVersion_;
+    bool                      bCopyright_;
+    int                       niceLevel_;
+    bool                      bNiceSet_;
+    bool                      bBackup_;
+    bool                      bFpexcept_;
+    int                       debugLevel_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(CommandLineCommonOptionsHolder);
+};
+
+//! \}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/commandline/cmdlineoptionsmodule.h b/src/include/gromacs/commandline/cmdlineoptionsmodule.h
new file mode 100644 (file)
index 0000000..e22f6ca
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::ICommandLineOptionsModule and supporting routines.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_CMDLINEOPTIONSMODULE_H
+#define GMX_COMMANDLINE_CMDLINEOPTIONSMODULE_H
+
+#include <functional>
+#include <memory>
+
+#include "gromacs/commandline/cmdlinemodule.h"
+
+namespace gmx
+{
+
+template<typename T>
+class ArrayRef;
+
+class CommandLineModuleManager;
+class ICommandLineModule;
+class ICommandLineOptionsModule;
+class IOptionsBehavior;
+class IOptionsContainer;
+
+//! Smart pointer to manage an ICommandLineOptionsModule.
+typedef std::unique_ptr<ICommandLineOptionsModule> ICommandLineOptionsModulePointer;
+
+/*! \brief
+ * Settings to pass information between a CommandLineOptionsModule and generic
+ * code that runs it.
+ *
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+class ICommandLineOptionsModuleSettings
+{
+public:
+    /*! \brief
+     * Sets the help text for the module from string array.
+     *
+     * \param[in] help  String array to set as the description.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * Formatting for the help text is described on \ref page_onlinehelp.
+     *
+     * Example usage:
+     * \code
+       const char *const desc[] = {
+           "This is the description",
+           "for the options"
+       };
+
+       settings->setHelpText(desc);
+       \endcode
+     */
+    virtual void setHelpText(const ArrayRef<const char* const>& help) = 0;
+    /*! \brief
+     * Set text indicating buggy behaviour of a module from string array.
+     *
+     * \param[in] bug String array to set as the bug text.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * Formatting for the text is described on \ref page_onlinehelp.
+     */
+    virtual void setBugText(const ArrayRef<const char* const>& bug) = 0;
+    /*! \brief
+     * Adds an option behavior that performs actions before
+     * ICommandLineOptionsModule::run() is called.
+     *
+     * For now, this takes a shared_ptr to make it easier for the caller to
+     * keep a reference to the behavior, but the behavior should be treated
+     * as owned by the options module after this call.
+     */
+    virtual void addOptionsBehavior(const std::shared_ptr<IOptionsBehavior>& behavior) = 0;
+
+protected:
+    // Disallow deletion through the interface.
+    // (no need for the virtual, but some compilers warn otherwise)
+    virtual ~ICommandLineOptionsModuleSettings();
+};
+
+/*! \brief
+ * Module that can be run from a command line and uses gmx::Options for
+ * argument processing.
+ *
+ * This class provides a higher-level interface on top of
+ * gmx::ICommandLineModule for cases where gmx::Options will be used
+ * for declaring the command-line arguments.  The module only needs to declare
+ * the options it uses, and the framework takes care of command-line parsing
+ * and help output.  The module typically consists of the following parts:
+ *  - init() allows for some interaction between the module and the framework
+ *    when running the module; see ICommandLineModule::init().  If no
+ *    such customization is necessary, an empty implementation is sufficient.
+ *  - initOptions() is called both for running the module and for printing help
+ *    for the module, and it should add the options that the module
+ *    understands.  Values provided for the options are typically stored in
+ *    member variables.
+ *  - optionsFinished() can be implemented in case additional processing is
+ *    needed (e.g., checking whether an option was set by the user).
+ *  - run() is called when running the module, after command-line options have
+ *    been parsed and their values stored in the corresponding member
+ *    variables.
+ *
+ * registerModule(), runAsMain(), or createModule() can be used to use modules
+ * of this type in all contexts where a gmx::ICommandLineModule is
+ * expected.  These methods create a gmx::ICommandLineModule
+ * implementation that contains the common code needed to parse command-line
+ * options and write help, based on the information provided from the methods
+ * in this class.
+ *
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+class ICommandLineOptionsModule
+{
+public:
+    /*! \brief
+     * Function pointer to a factory method that returns an interface of
+     * this type.
+     *
+     * \returns Module to run.
+     * \throws  std::bad_alloc if out of memory.
+     */
+    typedef std::function<ICommandLineOptionsModulePointer()> FactoryMethod;
+
+    /*! \brief
+     * Creates a ICommandLineModule to run the specified module.
+     *
+     * \param[in] name        Name for the module.
+     * \param[in] description Short description for the module.
+     * \param[in] module      Module to run.
+     * \returns ICommandLineModule object that runs \p module module.
+     * \throws  std::bad_alloc if out of memory.
+     */
+    static std::unique_ptr<ICommandLineModule> createModule(const char* name,
+                                                            const char* description,
+                                                            ICommandLineOptionsModulePointer module);
+    /*! \brief
+     * Implements a main() method that runs a single module.
+     *
+     * \param     argc    \c argc passed to main().
+     * \param     argv    \c argv passed to main().
+     * \param[in] name        Name for the module.
+     * \param[in] description Short description for the module.
+     * \param[in] factory     Factory that returns the module to run.
+     *
+     * This method allows for uniform behavior for binaries that only
+     * contain a single module without duplicating any of the
+     * implementation from CommandLineModuleManager (startup headers,
+     * common options etc.).
+     *
+     * \see runCommandLineModule()
+     */
+    static int runAsMain(int argc, char* argv[], const char* name, const char* description, FactoryMethod factory);
+    /*! \brief
+     * Registers a module of a certain type to this manager.
+     *
+     * \param     manager     Manager to register to.
+     * \param[in] name        Name for the module.
+     * \param[in] description Short description for the module.
+     * \param[in] factory     Factory that returns the module to register.
+     * \throws  std::bad_alloc if out of memory.
+     *
+     * This method internally creates a ICommandLineModule module
+     * with the given \p name and \p description, and adds that to
+     * \p manager.  When run or asked to write the help, the module calls
+     * \p factory to get the actual module, and forwards the necessary
+     * calls.
+     */
+    static void registerModuleFactory(CommandLineModuleManager* manager,
+                                      const char*               name,
+                                      const char*               description,
+                                      FactoryMethod             factory);
+    /*! \brief
+     * Registers a module to this manager.
+     *
+     * \param     manager     Manager to register to.
+     * \param[in] name        Name for the module.
+     * \param[in] description Short description for the module.
+     * \param[in] module      Module to register.
+     * \throws  std::bad_alloc if out of memory.
+     *
+     * This method internally creates a ICommandLineModule module
+     * with the given \p name and \p description, and adds that to
+     * \p manager.
+     *
+     * This method is mainly used by tests that need to have a reference to
+     * the ICommandLineOptionsModule instance (e.g., for mocking).
+     */
+    static void registerModuleDirect(CommandLineModuleManager*        manager,
+                                     const char*                      name,
+                                     const char*                      description,
+                                     ICommandLineOptionsModulePointer module);
+
+    virtual ~ICommandLineOptionsModule();
+
+    //! \copydoc gmx::ICommandLineModule::init()
+    virtual void init(CommandLineModuleSettings* settings) = 0;
+    /*! \brief
+     * Initializes command-line arguments understood by the module.
+     *
+     * \param[in,out] options  Options object to add the options to.
+     * \param[in,out] settings Settings to communicate information
+     *     to/from generic code running the module.
+     *
+     * When running the module, this method is called after init().
+     * When printing help, there is no call to init(), and this is the only
+     * method called.
+     * In both cases, the implementation should add options understood by
+     * the module to \p options.  Output values from options should be
+     * stored in member variables.
+     */
+    virtual void initOptions(IOptionsContainer* options, ICommandLineOptionsModuleSettings* settings) = 0;
+    /*! \brief
+     * Called after all option values have been set.
+     *
+     * When running the module, this method is called after all
+     * command-line arguments have been parsed.
+     */
+    virtual void optionsFinished() = 0;
+
+    /*! \brief
+     * Runs the module.
+     *
+     * \throws   unspecified  May throw exceptions to indicate errors.
+     * \returns  Exit code for the program.
+     * \retval   0 on successful termination.
+     *
+     * This method is called after optionsFinished() when running the
+     * module, and should do all the processing for the module.
+     */
+    virtual int run() = 0;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/commandline/cmdlineparser.h b/src/include/gromacs/commandline/cmdlineparser.h
new file mode 100644 (file)
index 0000000..ef49a13
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::CommandLineParser.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_CMDLINEPARSER_H
+#define GMX_COMMANDLINE_CMDLINEPARSER_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace gmx
+{
+
+class Options;
+
+/*! \brief
+ * Implements command-line parsing for Options objects.
+ *
+ * Typical usage (without error checking):
+ * \code
+   gmx::Options options("name", "description");
+   // Fill up options
+
+   gmx::CommandLineParser(&options).parse(&argc, argv);
+   options.finish();
+ * \endcode
+ *
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+class CommandLineParser
+{
+public:
+    /*! \brief
+     * Creates a command-line parser that sets values for options.
+     *
+     * \param[in] options  Options object whose options should be set.
+     * \throws  std::bad_alloc if out of memory.
+     */
+    explicit CommandLineParser(Options* options);
+    ~CommandLineParser();
+
+    /*! \brief
+     * Makes the parser skip unknown options and keep them in \c argv.
+     *
+     * \param[in] bEnabled  Whether to skip and keep unknown options.
+     * \returns   *this
+     *
+     * Setting this option to true has dual effect: unknown options are
+     * silently skipped, and all recognized options are removed from
+     * \c argc and \c argv in parse().  These effects should be easy to
+     * separate into different flags if there is need for it.
+     *
+     * The default is false: unknown options result in exceptions and
+     * \c argc and \c argv are not modified.
+     *
+     * Does not throw.
+     */
+    CommandLineParser& skipUnknown(bool bEnabled);
+
+    /*! \brief
+     * Makes the parser accept positional arguments
+     *
+     * \param[in] bEnabled  Whether to skip and keep positional arguments.
+     * \returns   *this
+     *
+     * Arguments that are not options (ie. no leading hyphen), and
+     * which come before all options are acceptable if this has
+     * been enabled. If so, these arguments are left in \c argc
+     * and \c argv in parse().
+     *
+     * The default is false: unknown leading arguments result in
+     * exceptions and \c argc and \c argv are not modified.
+     *
+     * Does not throw.
+     */
+    CommandLineParser& allowPositionalArguments(bool bEnabled);
+
+    /*! \brief
+     * Parses the command line.
+     *
+     * \throws  std::bad_alloc if out of memory.
+     * \throws  InvalidInputError if any errors were detected in the input.
+     *
+     * All command-line arguments are parsed, and an aggregate
+     * exception with all the detected errors (including unknown
+     * options, where applicable) is thrown in the end.
+     *
+     * If skipUnknown() was not called, or last called with a
+     * false value, the input arguments are not modified. If
+     * skipUnknown() was last called with a true value, only
+     * unknown options will be retained in \c argc and \c argv.
+     *
+     * All positional arguments are retained in the argument list,
+     * but such arguments must precede all options.
+     *
+     * \c argv[0] is never modified.
+     *
+     */
+    void parse(int* argc, char* argv[]);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/commandline/cmdlineprogramcontext.h b/src/include/gromacs/commandline/cmdlineprogramcontext.h
new file mode 100644 (file)
index 0000000..70d06df
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::CommandLineProgramContext.
+ *
+ * This header is installed to support cmdlineinit.h because some compilers
+ * don't allow returning a reference to an incomplete type from a function.
+ * It should not be necessary to use gmx::CommandLineProgramContext outside the
+ * \Gromacs library.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_CMDLINEPROGRAMCONTEXT_H
+#define GMX_COMMANDLINE_CMDLINEPROGRAMCONTEXT_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/programcontext.h"
+
+namespace gmx
+{
+
+//! \addtogroup module_commandline
+//! \{
+
+/*! \libinternal \brief
+ * Allows customization of the way various directories are found by
+ * CommandLineProgramContext.
+ *
+ * For the CommandLineProgramContext constructors that do not take this
+ * interface as a parameter, a default implementation is used that forwards
+ * the calls to the corresponding methods in gmx::Path.
+ *
+ * \inlibraryapi
+ */
+class IExecutableEnvironment
+{
+public:
+    virtual ~IExecutableEnvironment() {}
+
+    /*! \brief
+     * Returns the working directory when the program was launched.
+     */
+    virtual std::string getWorkingDirectory() const = 0;
+    /*! \brief
+     * Returns list of paths where executables are searched for.
+     *
+     * The returned list should be in priority order.  An empty string in
+     * the returned list corresponds to getWorkindDirectory().
+     */
+    virtual std::vector<std::string> getExecutablePaths() const = 0;
+};
+
+//! Shorthand for a smart pointer to IExecutableEnvironment.
+typedef std::unique_ptr<IExecutableEnvironment> ExecutableEnvironmentPointer;
+
+/*! \libinternal \brief
+ * Program context implementation for command line programs.
+ *
+ * Constructors are provided mostly for unit testing purposes; in normal usage,
+ * a single CommandLineProgramContext object is constructed with
+ * initForCommandLine() in the beginning of the program.
+ * The returned object can be explicitly passed to other methods, or accessed
+ * through getProgramContext().
+ *
+ * Unless explicitly noted otherwise, methods in this class may throw
+ * std::bad_alloc on out-of-memory conditions, but do not throw other
+ * exceptions.
+ *
+ * \inlibraryapi
+ */
+class CommandLineProgramContext : public IProgramContext
+{
+public:
+    /*! \brief
+     * Constructs an empty context object.
+     *
+     * All methods in the constructed object return dummy values.
+     */
+    CommandLineProgramContext();
+    /*! \brief
+     * Initializes a program context object with binary name only.
+     *
+     * \param[in] binaryName  Name of the binary.
+     *
+     * This is needed for unit testing purposes.
+     * The constructed object works as if the command line consisted of
+     * only of the binary name.
+     */
+    explicit CommandLineProgramContext(const char* binaryName);
+    /*! \brief
+     * Initializes a program context object based on command line.
+     *
+     * \param[in] argc  argc value passed to main().
+     * \param[in] argv  argv array passed to main().
+     */
+    CommandLineProgramContext(int argc, const char* const argv[]);
+    /*! \brief
+     * Initializes a program context object based on command line.
+     *
+     * \param[in] argc  argc value passed to main().
+     * \param[in] argv  argv array passed to main().
+     * \param[in] env   Customizes the way the binary name is handled.
+     *
+     * This overload allows one to customize the way the binary is located
+     * by providing a custom IExecutableEnvironment implementation.
+     * This is mainly useful for testing purposes to make it possible to
+     * test different paths without setting environment variables, changing
+     * the working directory or doing other process-wide operations.
+     * It may also be useful for making Gromacs behave better when linked
+     * into a non-Gromacs executable (with possible extensions in
+     * IExecutableEnvironment).
+     */
+    CommandLineProgramContext(int argc, const char* const argv[], ExecutableEnvironmentPointer env);
+    ~CommandLineProgramContext() override;
+
+    /*! \brief
+     * Sets a display name for the binary.
+     *
+     * \throws std::bad_alloc if out of memory.
+     *
+     * This is used with the wrapper binary to add the name of the invoked
+     * module to the name of the binary shown.
+     *
+     * It is not threadsafe if there are concurrent calls to displayName()
+     * before this method has returned.  Thread safety is not required for
+     * the normal initialization sequence of command line programs; it is
+     * called in the wrapper binary before the control passes to the actual
+     * module which may create threads.
+     */
+    void setDisplayName(const std::string& name);
+
+    /*! \brief
+     * Returns the name of the binary as it was invoked without any path.
+     *
+     * Does not throw.
+     */
+    const char* programName() const override;
+    /*! \brief
+     * Returns a display name of the current module.
+     *
+     * The returned value equals programName(), unless a separate display
+     * name has been set with setDisplayName().
+     *
+     * Does not throw.
+     */
+    const char* displayName() const override;
+    /*! \brief
+     * Returns the full path of the running binary.
+     *
+     * \throws std::bad_alloc if out of memory.
+     *
+     * Returns argv[0] if there was an error in finding the absolute path.
+     */
+    const char* fullBinaryPath() const override;
+    /*! \brief
+     * Returns the installation prefix (for finding \Gromacs data files).
+     *
+     * \throws std::bad_alloc if out of memory.
+     *
+     * Returns a hardcoded path set during configuration time if there is
+     * an error in finding the library data files.
+     */
+    InstallationPrefixInfo installationPrefix() const override;
+    /*! \brief
+     * Returns the full command line used to invoke the binary.
+     *
+     * Does not throw.
+     */
+    const char* commandLine() const override;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/commandline/filenm.h b/src/include/gromacs/commandline/filenm.h
new file mode 100644 (file)
index 0000000..1a3c8a8
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares t_filenm for old-style command-line parsing of file name options.
+ *
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_FILENM_H
+#define GMX_COMMANDLINE_FILENM_H
+
+#include <string>
+#include <string_view>
+#include <vector>
+
+#include "gromacs/fileio/filetypes.h"
+
+
+//! \addtogroup module_commandline
+//! \{
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+} // namespace gmx
+
+/*! \brief
+ * File name option definition for C code.
+ *
+ * \inpublicapi
+ */
+struct t_filenm
+{
+    int                      ftp; //!< File type, see enum in filetypes.h
+    const char*              opt; //!< Command line option, can be nullptr in which case the commandline module, including all opt2??? functions below, will use the default option for the file type
+    const char*              fn; //!< File name (as set in source code), can be nullptr in which case the commandline module will use the default file name for the file type
+    unsigned long            flag;      //!< Flag for all kinds of info (see defs)
+    std::vector<std::string> filenames; //!< File names
+};
+
+//! Whether a file name option is set.
+#define ffSET 1 << 0
+//! Whether a file name option specifies an input file.
+#define ffREAD 1 << 1
+//! Whether a file name option specifies an output file.
+#define ffWRITE 1 << 2
+//! Whether a file name option specifies an optional file.
+#define ffOPT 1 << 3
+//! Whether a file name option specifies a library file.
+#define ffLIB 1 << 4
+//! Whether a file name option accepts multiple file names.
+#define ffMULT 1 << 5
+//! Whether an input file name option accepts non-existent files.
+#define ffALLOW_MISSING 1 << 6
+//! Convenience flag for an input/output file.
+#define ffRW (ffREAD | ffWRITE)
+//! Convenience flag for an optional input file.
+#define ffOPTRD (ffREAD | ffOPT)
+//! Convenience flag for an optional output file.
+#define ffOPTWR (ffWRITE | ffOPT)
+//! Convenience flag for an optional input/output file.
+#define ffOPTRW (ffRW | ffOPT)
+//! Convenience flag for a library input file.
+#define ffLIBRD (ffREAD | ffLIB)
+//! Convenience flag for an optional library input file.
+#define ffLIBOPTRD (ffOPTRD | ffLIB)
+//! Convenience flag for an input file that accepts multiple files.
+#define ffRDMULT (ffREAD | ffMULT)
+//! Convenience flag for an optional input file that accepts multiple files.
+#define ffOPTRDMULT (ffRDMULT | ffOPT)
+//! Convenience flag for an output file that accepts multiple files.
+#define ffWRMULT (ffWRITE | ffMULT)
+//! Convenience flag for an optional output file that accepts multiple files.
+#define ffOPTWRMULT (ffWRMULT | ffOPT)
+
+/*! \brief
+ * Returns the filename belonging to cmd-line option opt, or NULL when
+ * no such option.
+ */
+const char* opt2fn(const char* opt, int nfile, const t_filenm fnm[]);
+
+/*! \brief
+ * Returns the filenames belonging to cmd-line option opt.
+ *
+ * An assertion will fail when the option does not exist.
+ */
+gmx::ArrayRef<const std::string> opt2fns(const char* opt, int nfile, const t_filenm fnm[]);
+
+/*! \brief
+ * Returns the filenames belonging to cmd-line option opt when set,
+ * returns an empty vector when the option is not set.
+ *
+ * An assertion will fail when the option does not exist.
+ */
+gmx::ArrayRef<const std::string> opt2fnsIfOptionSet(const char* opt, int nfile, const t_filenm fnm[]);
+
+//! Returns a file pointer from the filename.
+#define opt2FILE(opt, nfile, fnm, mode) gmx_ffopen(opt2fn(opt, nfile, fnm), mode)
+
+//! Returns the first file name with type ftp, or NULL when none found.
+const char* ftp2fn(int ftp, int nfile, const t_filenm fnm[]);
+
+/*! \brief
+ * Returns the filenames for the first option with type ftp.
+ *
+ * An assertion will fail when when none found.
+ */
+gmx::ArrayRef<const std::string> ftp2fns(int ftp, int nfile, const t_filenm fnm[]);
+
+//! Returns a file pointer from the file type.
+#define ftp2FILE(ftp, nfile, fnm, mode) gmx_ffopen(ftp2fn(ftp, nfile, fnm), mode)
+
+//! Returns TRUE when this file type has been found on the cmd-line.
+bool ftp2bSet(int ftp, int nfile, const t_filenm fnm[]);
+
+//! Returns TRUE when this option has been found on the cmd-line.
+bool opt2bSet(const char* opt, int nfile, const t_filenm fnm[]);
+
+/*! \brief
+ * Returns the file name belonging top cmd-line option opt, or NULL when
+ * no such option.
+ *
+ * Also return NULL when opt is optional and option is not set.
+ */
+const char* opt2fn_null(const char* opt, int nfile, const t_filenm fnm[]);
+
+/*! \brief
+ * Returns the first file name with type ftp, or NULL when none found.
+ *
+ * Also return NULL when ftp is optional and option is not set.
+ */
+const char* ftp2fn_null(int ftp, int nfile, const t_filenm fnm[]);
+
+//! Returns whether or not this filenm is optional.
+bool is_optional(const t_filenm* fnm);
+
+//! Returns whether or not this filenm is output.
+bool is_output(const t_filenm* fnm);
+
+//! Returns whether or not this filenm is set.
+bool is_set(const t_filenm* fnm);
+
+/*! \brief Return whether \c filename might have been produced by mdrun -noappend.
+ *
+ * If so, it must match "prefix.partNNNN.extension", for four decimal
+ * digits N and non-empty prefix and extension. */
+bool hasSuffixFromNoAppend(std::string_view filename);
+
+/*! \brief
+ * When we do checkpointing, this routine is called to check for previous
+ * output files and append a '.partNNNN' suffix before the (output) file extensions.
+ * If there was already a '.partNNNN' suffix before the file extension, that
+ * is removed before the new suffix is added.
+ */
+int add_suffix_to_output_names(t_filenm* fnm, int nfile, const char* suffix);
+
+//! \}
+
+#endif
diff --git a/src/include/gromacs/commandline/pargs.h b/src/include/gromacs/commandline/pargs.h
new file mode 100644 (file)
index 0000000..08b1999
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares \c t_pargs, parse_common_args() and related methods.
+ *
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_PARGS_H
+#define GMX_COMMANDLINE_PARGS_H
+
+#include "gromacs/commandline/filenm.h"
+#include "gromacs/fileio/oenv.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_output_env_t;
+
+/*! \addtogroup module_commandline
+ * \{
+ */
+
+/** Command line argument type. */
+enum
+{
+    etINT,
+    etINT64,
+    etREAL,
+    etTIME,
+    etSTR,
+    etBOOL,
+    etRVEC,
+    etENUM,
+    etNR
+};
+
+/*! \brief
+ * Command-line argument definition for C code.
+ *
+ * \inpublicapi
+ */
+typedef struct
+{
+    /** Name of the argument (with leading dash included). */
+    const char* option;
+    /** Whether the argument is set (should be initialized to `FALSE`). */
+    gmx_bool bSet;
+    /** Type of the argument (one of the enums in pargs.h). */
+    int type;
+    /*! \brief
+     * Pointer to variable that is to receive the value.
+     *
+     * The expected type depends on the value of \a type.
+     * If an argument is not set by the user, then the pointed value is not
+     * changed.  In other words, the initial value for the variable defines the
+     * default value.
+     */
+    union
+    {
+        /*! \brief
+         * Generic pointer for operations that do not need type information.
+         *
+         * Needs to be the first member to use initialized arrays.
+         */
+        void* v;
+        /** Integer value for etINT. */
+        int* i;
+        /** Integer value for etINT64. */
+        int64_t* is;
+        /** Real value for etREAL and etTIME. */
+        real* r;
+        /*! \brief
+         * String value for etSTR and etENUM.
+         *
+         * For etSTR, this should point to a `const char *` variable, which
+         * will receive a pointer to the string value (caller should not free
+         * the string).
+         *
+         * For etENUM, this should be an array of `const char *` values, where
+         * the first and last values are `NULL`, and the other values define
+         * the allowed enum values.  The first non-NULL value is the default
+         * value.  After the arguments are parsed, the first element in the array
+         * points to the selected enum value (pointers will be equal).
+         */
+        const char** c;
+        /** Boolean value for etBOOL. */
+        bool* b;
+        /** Vector value for etRVEC. */
+        rvec* rv;
+    } u;
+    /*! \brief
+     * Description for the argument.
+     *
+     * If the string starts with `HIDDEN`, then the argument is hidden from
+     * normal help listing and shell completions.
+     */
+    const char* desc;
+} t_pargs;
+
+/*! \brief
+ * Returns ordinal number for an etENUM argument.
+ *
+ * \param[in] enumc  Array passed to `t_pargs` for an etENUM argument.
+ * \returns   Index of selected enum value in the array.
+ *
+ * See t_pargs::u::c for the expected format of the array, including how the
+ * first element should be initialized.
+ * Note that the return value starts at one instead of zero: if the first enum
+ * value is selected, this returns 1.
+ */
+int nenum(const char* const enumc[]);
+
+/*! \brief
+ * Returns value of an etINT option.
+ *
+ * \param[in] option Name of etINT argument to query.
+ * \param[in] nparg  Number of elements in \p pa.
+ * \param[in] pa     Array of arguments.
+ * \returns   Value of \p option.
+ *
+ * \p option must specify a valid argument in \p pa of the correct type.
+ */
+int opt2parg_int(const char* option, int nparg, t_pargs pa[]);
+
+/*! \brief
+ * Returns value of an etBOOL option.
+ *
+ * \param[in] option Name of etBOOL argument to query.
+ * \param[in] nparg  Number of elements in \p pa.
+ * \param[in] pa     Array of arguments.
+ * \returns   Value of \p option.
+ *
+ * \p option must specify a valid argument in \p pa of the correct type.
+ */
+bool opt2parg_bool(const char* option, int nparg, t_pargs pa[]);
+
+/*! \brief
+ * Returns value of an etREAL/etTIME option.
+ *
+ * \param[in] option Name of etREAL/etTIME argument to query.
+ * \param[in] nparg  Number of elements in \p pa.
+ * \param[in] pa     Array of arguments.
+ * \returns   Value of \p option.
+ *
+ * \p option must specify a valid argument in \p pa of the correct type.
+ */
+real opt2parg_real(const char* option, int nparg, t_pargs pa[]);
+
+/*! \brief
+ * Returns value of an etSTR option.
+ *
+ * \param[in] option Name of etSTR argument to query.
+ * \param[in] nparg  Number of elements in \p pa.
+ * \param[in] pa     Array of arguments.
+ * \returns   Value of \p option.
+ *
+ * \p option must specify a valid argument in \p pa of the correct type.
+ */
+const char* opt2parg_str(const char* option, int nparg, t_pargs pa[]);
+
+/*! \brief
+ * Returns value of an etENUM option.
+ *
+ * \param[in] option Name of etENUM argument to query.
+ * \param[in] nparg  Number of elements in \p pa.
+ * \param[in] pa     Array of arguments.
+ * \returns   Value of \p option.
+ *
+ * \p option must specify a valid argument in \p pa of the correct type.
+ */
+const char* opt2parg_enum(const char* option, int nparg, t_pargs pa[]);
+
+/*! \brief
+ * Returns whether an argument has been set.
+ *
+ * \param[in] option Name of argument to check.
+ * \param[in] nparg  Number of elements in \p pa.
+ * \param[in] pa     Array of arguments.
+ * \returns   `true` if \p option has been set.
+ *
+ * \p option must specify a valid argument in \p pa.
+ */
+bool opt2parg_bSet(const char* option, int nparg, const t_pargs* pa);
+
+
+/** Add option -w to view output files (must be implemented in program). */
+#define PCA_CAN_VIEW (1 << 5)
+/** Add option to set begin time for trajectory reading. */
+#define PCA_CAN_BEGIN (1 << 6)
+/** Add option to set end time for trajectory reading. */
+#define PCA_CAN_END (1 << 7)
+/** Add option to set time step for trajectory reading. */
+#define PCA_CAN_DT (1 << 14)
+/** Add all options for trajectory time control. */
+#define PCA_CAN_TIME (PCA_CAN_BEGIN | PCA_CAN_END | PCA_CAN_DT)
+/** Add option -tu to set time unit for output. */
+#define PCA_TIME_UNIT (1 << 15)
+/** Add option -deffnm to set default for all file options. */
+#define PCA_CAN_SET_DEFFNM (1 << 10)
+/** Do not raise a fatal error when invalid options are encountered. */
+#define PCA_NOEXIT_ON_ARGS (1 << 11)
+/** Don't do any special processing for ffREAD files */
+#define PCA_DISABLE_INPUT_FILE_CHECKING (1 << 17)
+
+/*! \brief
+ * Parse command-line arguments.
+ *
+ * Some common default arguments are also recognized in addition to those
+ * provided through \p pa.  The set of recognized default arguments is affected
+ * by \p Flags.
+ *
+ * Recognized arguments are removed from the list.
+ *
+ * For full functionality, this function needs to be used within a function
+ * that is passed to gmx_run_cmain().  It should be called as the first thing in
+ * that function.  Initialization code can be executed before it, but you need
+ * to be aware that if the program is executed with -h and MPI, the code before
+ * parse_common_args() only executes on the master node.
+ *
+ * If the return value is `FALSE`, the program should return immediately (this
+ * is necessary for -h and a few other cases).
+ *
+ * \see gmx_run_cmain().
+ */
+bool parse_common_args(int*               argc,
+                       char*              argv[],
+                       unsigned long      Flags,
+                       int                nfile,
+                       t_filenm           fnm[],
+                       int                npargs,
+                       t_pargs*           pa,
+                       int                ndesc,
+                       const char**       desc,
+                       int                nbugs,
+                       const char**       bugs,
+                       gmx_output_env_t** oenv);
+
+/*! \} */
+
+#endif
diff --git a/src/include/gromacs/commandline/shellcompletions.h b/src/include/gromacs/commandline/shellcompletions.h
new file mode 100644 (file)
index 0000000..ef7d17d
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares gmx::ShellCompletionWriter.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_SHELLCOMPLETIONS_H
+#define GMX_COMMANDLINE_SHELLCOMPLETIONS_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace gmx
+{
+
+class CommandLineHelpContext;
+class Options;
+class TextWriter;
+
+//! \cond internal
+//! \addtogroup module_commandline
+//! \{
+
+//! Output format for ShellCompletionWriter.
+enum ShellCompletionFormat
+{
+    eShellCompletionFormat_Bash //!< Shell completions for bash.
+};
+
+//! \}
+//! \endcond
+
+class ShellCompletionWriter
+{
+public:
+    typedef std::vector<std::string> ModuleNameList;
+
+    ShellCompletionWriter(const std::string& binaryName, ShellCompletionFormat format);
+    ~ShellCompletionWriter();
+
+    TextWriter& outputWriter();
+
+    void startCompletions();
+    void writeModuleCompletions(const char* moduleName, const Options& options);
+    void writeWrapperCompletions(const ModuleNameList& modules, const Options& options);
+    void finishCompletions();
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/commandline/tests/cmdlinemodulemanagertest.h b/src/include/gromacs/commandline/tests/cmdlinemodulemanagertest.h
new file mode 100644 (file)
index 0000000..e560eab
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Test fixture and helper classes for tests using gmx::CommandLineModuleManager.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_CMDLINEMODULEMANAGERTEST_H
+#define GMX_COMMANDLINE_CMDLINEMODULEMANAGERTEST_H
+
+#include <memory>
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "gromacs/commandline/cmdlinehelpcontext.h"
+#include "gromacs/commandline/cmdlinemodule.h"
+#include "gromacs/commandline/cmdlineoptionsmodule.h"
+
+#include "testutils/stringtest.h"
+
+namespace gmx
+{
+namespace test
+{
+
+class CommandLine;
+class MockHelpTopic;
+class TestFileOutputRedirector;
+
+/*! \internal \brief
+ * Mock implementation of gmx::ICommandLineModule.
+ *
+ * \ingroup module_commandline
+ */
+class MockModule : public gmx::ICommandLineModule
+{
+public:
+    //! Creates a mock module with the given name and description.
+    MockModule(const char* name, const char* description);
+    ~MockModule() override;
+
+    const char* name() const override { return name_; }
+    const char* shortDescription() const override { return descr_; }
+
+    MOCK_METHOD1(init, void(gmx::CommandLineModuleSettings* settings));
+    MOCK_METHOD2(run, int(int argc, char* argv[]));
+    MOCK_CONST_METHOD1(writeHelp, void(const gmx::CommandLineHelpContext& context));
+
+    //! Sets the expected display name for writeHelp() calls.
+    void setExpectedDisplayName(const char* expected) { expectedDisplayName_ = expected; }
+
+private:
+    //! Checks the context passed to writeHelp().
+    void checkHelpContext(const gmx::CommandLineHelpContext& context) const;
+
+    const char* name_;
+    const char* descr_;
+    std::string expectedDisplayName_;
+};
+
+/*! \internal \brief
+ * Mock implementation of gmx::ICommandLineOptionsModule.
+ *
+ * \ingroup module_commandline
+ */
+class MockOptionsModule : public gmx::ICommandLineOptionsModule
+{
+public:
+    MockOptionsModule();
+    ~MockOptionsModule() override;
+
+    MOCK_METHOD1(init, void(gmx::CommandLineModuleSettings* settings));
+    MOCK_METHOD2(initOptions,
+                 void(gmx::IOptionsContainer* options, gmx::ICommandLineOptionsModuleSettings* settings));
+    MOCK_METHOD0(optionsFinished, void());
+    MOCK_METHOD0(run, int());
+};
+
+/*! \internal \brief
+ * Test fixture for tests using gmx::CommandLineModuleManager.
+ *
+ * \ingroup module_commandline
+ */
+class CommandLineModuleManagerTestBase : public gmx::test::StringTestBase
+{
+public:
+    CommandLineModuleManagerTestBase();
+    ~CommandLineModuleManagerTestBase() override;
+
+    //! Creates the manager to run the given command line.
+    void initManager(const CommandLine& args, const char* realBinaryName);
+    //! Adds a mock module to the manager.
+    MockModule& addModule(const char* name, const char* description);
+    //! Adds a mock module using gmx::Options to the manager.
+    MockOptionsModule& addOptionsModule(const char* name, const char* description);
+    //! Adds a mock help topic to the manager.
+    MockHelpTopic& addHelpTopic(const char* name, const char* title);
+
+    /*! \brief
+     * Returns the manager for this test.
+     *
+     * initManager() must have been called.
+     */
+    CommandLineModuleManager& manager();
+
+    /*! \brief
+     * Checks all output from the manager using reference data.
+     *
+     * Both output to `stdout` and to files is checked.
+     *
+     * The manager is put into quiet mode by default, so the manager will
+     * only print out information if, e.g., help is explicitly requested.
+     */
+    void checkRedirectedOutput();
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/commandline/viewit.h b/src/include/gromacs/commandline/viewit.h
new file mode 100644 (file)
index 0000000..58271c1
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Provides function to open output files automatically (with some X11
+ * programs).
+ *
+ * \inpublicapi
+ * \ingroup module_commandline
+ */
+#ifndef GMX_COMMANDLINE_VIEWIT_H
+#define GMX_COMMANDLINE_VIEWIT_H
+
+struct gmx_output_env_t;
+struct t_filenm;
+
+/*! \brief
+ * Executes an external (X11) command to view a file.
+ *
+ * Currently eps, xpm, xvg and pdb are supported.
+ * Default programs are provided, can be overriden with environment vars
+ * (but note that if the caller provides program-specific \p opts, setting the
+ * environment variable most likely breaks things).
+ */
+void do_view(const gmx_output_env_t* oenv, const char* fn, const char* opts);
+
+/*! \brief
+ * Calls do_view() for all viewable output files.
+ */
+void view_all(const gmx_output_env_t* oenv, int nf, t_filenm fnm[]);
+
+#endif
diff --git a/src/include/gromacs/compat/mp11.h b/src/include/gromacs/compat/mp11.h
new file mode 100644 (file)
index 0000000..d474bbe
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Provides ported functions/classes from boost::mp11
+ *
+ * Adapted from the Boost Library 1.67
+ *
+ * \author Roland Schulz <roland.schulz@intel.com>
+ * \ingroup module_compat
+ * \inlibraryapi
+ */
+#ifndef GMX_COMPAT_MP11_H
+#define GMX_COMPAT_MP11_H
+
+#include <utility>
+
+#include "gromacs/utility/exceptions.h"
+
+namespace gmx
+{
+namespace compat
+{
+
+/** \internal \brief Simplified analogue of boost::mp11::mp_with_index, compatible only with C++17 and up.
+ *
+ * \c mp_with_index<N>(i, f) calls \p f with \c mp_size_t<i>() and returns the result.
+ * \p i must be less than \p N.
+ *
+ * Example usage:
+ * \code
+    constexpr int foo_max = 3;
+    template<int i, typename = std::enable_if_t<(i < foo_max)>>
+    bool constexpr foo();
+
+    bool bar(int i)
+    {
+        return mp_with_index<foo_max>(i, [](auto i) {
+            return foo<i>();
+        });
+    }
+ * \endcode
+ */
+template<std::size_t N, class F, typename std::enable_if<(N <= 1)>::type* = nullptr>
+static auto mp_with_index(std::size_t i, F&& f)
+{
+    // Last step of recursion. Must have one active "return" for proper type deduction.
+    if (i == N - 1)
+    {
+        return std::forward<F>(f)(std::integral_constant<std::size_t, N - 1>());
+    }
+    else
+    {
+        const std::string errorMessage =
+                "Invalid arguments of mp_with_index (i=" + std::to_string(i) + ")";
+        GMX_THROW(InternalError(errorMessage));
+    }
+}
+
+// Doxygen does not like recursive templates.
+//! \cond
+template<std::size_t N, class F, typename std::enable_if<(N > 1)>::type* = nullptr>
+static auto mp_with_index(std::size_t i, F&& f)
+{
+    if (i == N - 1)
+    {
+        return std::forward<F>(f)(std::integral_constant<std::size_t, N - 1>());
+    }
+    else
+    {
+        return mp_with_index<N - 1>(i, std::forward<F>(f));
+    }
+}
+//! \endcond
+
+} // namespace compat
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/compat/pointers.h b/src/include/gromacs/compat/pointers.h
new file mode 100644 (file)
index 0000000..5bb1ff6
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Provides ported functions/classes from gsl/pointers
+ *
+ * Adapted from the Guidelines Support Library v2.0.0. at
+ * https://github.com/Microsoft/GSL
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_compat
+ * \inlibraryapi
+ */
+#ifndef GMX_COMPAT_POINTERS_H
+#define GMX_COMPAT_POINTERS_H
+
+#include <type_traits>
+#include <utility>
+
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+namespace compat
+{
+
+//! Contract-assurance macros that work like a simple version of the GSL ones
+//! \{
+#define Expects(cond) GMX_ASSERT((cond), "Precondition violation")
+#define Ensures(cond) GMX_ASSERT((cond), "Postcondition violation")
+//! \}
+
+/*! \libinternal
+ * \brief Restricts a pointer or smart pointer to only hold non-null values.
+ *
+ * Has zero size overhead over T.
+ *
+ * If T is a pointer (i.e. T == U*) then
+ * - allow construction from U*
+ * - disallow construction from nullptr_t
+ * - disallow default construction
+ * - ensure construction from null U* fails (only in debug builds)
+ * - allow implicit conversion to U*
+ *
+ * \todo Eliminate this when we require a version of C++ that supports
+ * std::not_null.
+ */
+template<class T>
+class not_null
+{
+public:
+    static_assert(std::is_assignable<T&, std::nullptr_t>::value, "T cannot be assigned nullptr.");
+
+    //! Move constructor. Asserts in debug mode if \c is nullptr.
+    template<typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+    constexpr explicit not_null(U&& u) : ptr_(std::forward<U>(u))
+    {
+        Expects(ptr_ != nullptr);
+    }
+
+    //! Simple constructor. Asserts in debug mode if \c u is nullptr.
+    template<typename = std::enable_if_t<!std::is_same<std::nullptr_t, T>::value>>
+    constexpr explicit not_null(T u) : ptr_(u)
+    {
+        Expects(ptr_ != nullptr);
+    }
+
+    //! Copy constructor.
+    template<typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
+    constexpr not_null(const not_null<U>& other) : not_null(other.get())
+    {
+    }
+
+    //! Default constructors and assignment.
+    //! \{
+    not_null(not_null&& other) noexcept = default;
+    not_null(const not_null& other)     = default;
+    not_null& operator=(const not_null& other) = default;
+    //! \}
+
+    //! Getters
+    //! \{
+    constexpr T get() const
+    {
+        Ensures(ptr_ != nullptr);
+        return ptr_;
+    }
+
+    constexpr   operator T() const { return get(); }
+    constexpr T operator->() const { return get(); }
+    //! \}
+
+    //! Deleted to prevent compilation when someone attempts to assign a null pointer constant.
+    //! \{
+    not_null(std::nullptr_t) = delete;
+    not_null& operator=(std::nullptr_t) = delete;
+    //! \}
+
+    //! Deleted unwanted operators because pointers only point to single objects.
+    //! \{
+    not_null& operator++()                     = delete;
+    not_null& operator--()                     = delete;
+    not_null  operator++(int)                  = delete;
+    not_null  operator--(int)                  = delete;
+    not_null& operator+=(std::ptrdiff_t)       = delete;
+    not_null& operator-=(std::ptrdiff_t)       = delete;
+    void      operator[](std::ptrdiff_t) const = delete;
+    //! \}
+
+private:
+    T ptr_;
+};
+
+//! Convenience function for making not_null pointers from plain pointers.
+template<class T>
+not_null<T> make_not_null(T&& t)
+{
+    return not_null<std::remove_cv_t<std::remove_reference_t<T>>>{ std::forward<T>(t) };
+}
+
+//! Convenience function for making not_null pointers from smart pointers.
+template<class T>
+not_null<typename T::pointer> make_not_null(T& t)
+{
+    return not_null<typename std::remove_reference_t<T>::pointer>{ t.get() };
+}
+
+//! Operators to compare not_null pointers.
+//! \{
+template<class T, class U>
+auto operator==(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() == rhs.get())
+{
+    return lhs.get() == rhs.get();
+}
+
+template<class T, class U>
+auto operator!=(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() != rhs.get())
+{
+    return lhs.get() != rhs.get();
+}
+
+template<class T, class U>
+auto operator<(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() < rhs.get())
+{
+    return lhs.get() < rhs.get();
+}
+
+template<class T, class U>
+auto operator<=(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() <= rhs.get())
+{
+    return lhs.get() <= rhs.get();
+}
+
+template<class T, class U>
+auto operator>(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() > rhs.get())
+{
+    return lhs.get() > rhs.get();
+}
+
+template<class T, class U>
+auto operator>=(const not_null<T>& lhs, const not_null<U>& rhs) -> decltype(lhs.get() >= rhs.get())
+{
+    return lhs.get() >= rhs.get();
+}
+//! \}
+
+//! Deleted unwanted arithmetic operators.
+//! \{
+template<class T, class U>
+std::ptrdiff_t operator-(const not_null<T>&, const not_null<U>&) = delete;
+template<class T>
+not_null<T> operator-(const not_null<T>&, std::ptrdiff_t) = delete;
+template<class T>
+not_null<T> operator+(const not_null<T>&, std::ptrdiff_t) = delete;
+template<class T>
+not_null<T> operator+(std::ptrdiff_t, const not_null<T>&) = delete;
+//! \}
+
+} // namespace compat
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/compat/utility.h b/src/include/gromacs/compat/utility.h
new file mode 100644 (file)
index 0000000..705b6a7
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Provides backported functions/classes from utility
+ *
+ * \todo Remove when CUDA 11 is a requirement.
+ *
+ * \author Roland Schulz <roland.schulz@intel.com>
+ * \ingroup module_compat
+ * \inlibraryapi
+ */
+#ifndef GMX_COMPAT_UTILITY_H
+#define GMX_COMPAT_UTILITY_H
+namespace gmx
+{
+namespace compat
+{
+//! Forms lvalue reference to const type of t
+template<class T>
+constexpr const T& as_const(T& t) noexcept
+{
+    return t;
+}
+} // namespace compat
+} // namespace gmx
+#endif
diff --git a/src/include/gromacs/coordinateio/coordinatefile.h b/src/include/gromacs/coordinateio/coordinatefile.h
new file mode 100644 (file)
index 0000000..fc7fa14
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * CoordinateFile takes care of opening files and writing output to them.
+ *
+ * \author
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_COORDINATEIO_COORDINATEFILE_H
+#define GMX_COORDINATEIO_COORDINATEFILE_H
+
+#include <string>
+#include <utility>
+
+#include "gromacs/coordinateio/ioutputadapter.h"
+#include "gromacs/coordinateio/outputadaptercontainer.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/atoms.h"
+
+struct gmx_mtop_t;
+struct t_trxstatus;
+
+namespace gmx
+{
+
+class Selection;
+class TrajectoryFrameWriter;
+struct OutputRequirements;
+
+/*! \brief
+ * Factory function for TrajectoryFrameWriter.
+ *
+ * Used to initialize a new instance of TrajectoryFrameWriter with the user supplied information
+ * for writing trajectory data to disk. Information needed is the file type, file name
+ * corresponding to the type, if available topology information and selection information.
+ *
+ * If supplied, the modules contained within \p adapters are registered on
+ * the TrajectoryFrameWriter if possible.
+ *
+ * The factory function is responsible for the initial santity checks concerning file types and
+ * availability of topology information, with the registration of modules being the second part.
+ *
+ * \param[in] top                    Pointer to full topology or null.
+ * \param[in] sel                    Reference to global selection used to construct the object.
+ * \param[in] filename               Name of new output file, used to deduce file type.
+ * \param[in] atoms                  Smart Pointer to atoms data or null.
+ * \param[in] requirements           Container for settings obtained to specify which
+ *                                   OutputAdapters should be registered.
+ * \throws    InconsistentInputError When user input and requirements don't match.
+ */
+std::unique_ptr<TrajectoryFrameWriter> createTrajectoryFrameWriter(const gmx_mtop_t*  top,
+                                                                   const Selection&   sel,
+                                                                   const std::string& filename,
+                                                                   AtomsDataPtr       atoms,
+                                                                   OutputRequirements requirements);
+
+/*!\brief
+ * Low level method to take care of only file opening and closing.
+ *
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ *
+ */
+class TrajectoryFileOpener
+{
+public:
+    /*! \brief
+     * Constructor, taking all arguments needed to open valid coordinate files of any type.
+     *
+     * \param[in] name Name of the file to create.
+     * \param[in] filetype Internal filetype to know which kind we are going to have.
+     * \param[in] sel Reference to selection of atoms to write. Needs to be valid for
+     *                longer than the lifetime of the object created here.
+     * \param[in] mtop Topology used to create TNG output. Needs to be valid for longer
+     *                 than the object created here.
+     */
+    TrajectoryFileOpener(const std::string& name, int filetype, const Selection& sel, const gmx_mtop_t* mtop) :
+        outputFileName_(name), outputFile_(nullptr), filetype_(filetype), sel_(sel), mtop_(mtop)
+    {
+    }
+
+    /*! \brief
+     * Closes new trajectory file after finishing the writing to it.
+     */
+    ~TrajectoryFileOpener();
+
+    /*! \brief
+     * Get access to initialized output file object.
+     *
+     * Performs lazy initialization if needed.
+     */
+    t_trxstatus* outputFile();
+
+private:
+    //! Name for the new coordinate file.
+    std::string outputFileName_;
+
+    //! File pointer to the coordinate file being written.
+    t_trxstatus* outputFile_;
+
+    //! Storage of file type for determing what kind of file will be written to disk.
+    int filetype_;
+
+    /*! \brief
+     * Selection of atoms to write out.
+     *
+     * Currently, CoordinateFile expects that the lifetime of the selection is longer
+     * than that of itself, and that the selection remains unchanged during this lifetime.
+     * A better approach will be to pass the selection to it and expect it to
+     * manage the lifetime instead.
+     */
+    const Selection& sel_;
+
+    //! Pointer to topology information if available.
+    const gmx_mtop_t* mtop_;
+};
+
+/*!\brief
+ * Writes coordinate frames to a sink, e.g. a trajectory file.
+ *
+ * Writes all frames passed to the trajectory file handle it
+ * manages. It can use IOutputAdapter modules to transform the
+ * frame data before writing. If any transform modules are used,
+ * makes a deep copy of the frame contents.
+ *
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ *
+ */
+class TrajectoryFrameWriter
+{
+public:
+    friend std::unique_ptr<TrajectoryFrameWriter> createTrajectoryFrameWriter(const gmx_mtop_t* top,
+                                                                              const Selection&  sel,
+                                                                              const std::string& filename,
+                                                                              AtomsDataPtr atoms,
+                                                                              OutputRequirements requirements);
+
+    /*! \brief
+     * Writes the input frame, after applying any IOutputAdapters.
+     *
+     * \param[in] framenumber Number of frame being currently processed.
+     * \param[in] input View of the constant t_trxframe object provided by the
+     *                  method that calls the output manager.
+     */
+    void prepareAndWriteFrame(int framenumber, const t_trxframe& input);
+
+private:
+    /*! \brief
+     * Creates fully initialized object.
+     *
+     * \param[in] name Name of the file to create.
+     * \param[in] filetype Internal filetype to know which kind we are going to have.
+     * \param[in] sel Reference to selection of atoms to write. Needs to be valid for
+     *                longer than the lifetime of the object created here.
+     * \param[in] mtop Topology used to create TNG output. Needs to be valid for longer
+     *                 than the object created here.
+     * \param[in] adapters Container of methods that can modify the information written
+     *                     to the new file.
+     */
+    TrajectoryFrameWriter(const std::string&     name,
+                          int                    filetype,
+                          const Selection&       sel,
+                          const gmx_mtop_t*      mtop,
+                          OutputAdapterContainer adapters) :
+        file_(name, filetype, sel, mtop), outputAdapters_(std::move(adapters))
+    {
+    }
+
+    //! Underlying object for open/writing to file.
+    TrajectoryFileOpener file_;
+
+    //! Storage for list of output adapters.
+    OutputAdapterContainer outputAdapters_;
+
+    //! Local storage for modified positions.
+    std::vector<RVec> localX_;
+    //! Local storage for modified velocities.
+    std::vector<RVec> localV_;
+    //! Local storage for modified forces.
+    std::vector<RVec> localF_;
+    //! Local storage for modified indices.
+    std::vector<int> localIndex_;
+};
+
+//! Smart pointer to manage the TrajectoryFrameWriter object.
+using TrajectoryFrameWriterPointer = std::unique_ptr<TrajectoryFrameWriter>;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/enums.h b/src/include/gromacs/coordinateio/enums.h
new file mode 100644 (file)
index 0000000..b1b1003
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Enum class defining the different requirements that outputadapters
+ * have for the output file type. OutputManager objects can only be built
+ * with OutputAdapters whose requirements can be implemented with the available input.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \libinternal
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_COORDINATEIO_ENUMS_H
+#define GMX_COORDINATEIO_ENUMS_H
+
+namespace gmx
+{
+
+/*!\brief
+ * The enums here define the flags specifying the requirements
+ * of different outputadapter modules.
+ *
+ * When building the object for deciding on the output to a new coordinate file,
+ * the CoordinateFile object needs to be able to validate that the dependencies of
+ * attached IOutputAdapters are fulfilled. Classes and interfaces that use
+ * the enum can check their dependencies against the information encoded in the
+ * flags and can then perform an appropriate reaction if there is a mismatch.
+ * \todo Use std::bitset<16> for the entries.
+ *
+ * \libinternal
+ * \ingroup module_coordinateio
+ *
+ */
+enum class CoordinateFileFlags : unsigned long
+{
+    /*! \brief
+     * Base setting that says that the module has no requirements.
+     *
+     * Sets the flags to default setting to make sure all output methods
+     * are supported.
+     */
+    Base = 1 << 0,
+    /*! \brief
+     * Requires output method to support force output.
+     *
+     * If set, only output methods supporting writing of forces will work,
+     * others will generate an invalid input error.
+     */
+    RequireForceOutput = 1 << 1,
+    /*! \brief
+     * Requires output method to support velocity output.
+     *
+     * If set, only writing to files that support velocity output will succeed.
+     * Other writing methods will generate an error.
+     */
+    RequireVelocityOutput = 1 << 2,
+    /*! \brief
+     * Requires support for connection information in output format.
+     *
+     * If set, only file output that supports writing of connection information will succeed.
+     * This means for now that only PDB and TNG files can be written. Other file writing
+     * methods will fail.
+     */
+    RequireAtomConnections = 1 << 3,
+    /*! \brief
+     * Requires that output format supports the writing of atom information to the file.
+     *
+     * If set, files will only be written if they can output the information from t_atoms
+     * and otherwise create an error while writing.
+     */
+    RequireAtomInformation = 1 << 4,
+    /*! \brief
+     * Requires that output format supports writing user-specified output precision.
+     *
+     * If set, output will only be written if the format supports the writing
+     * of custom precision of the included data.
+     */
+    RequireChangedOutputPrecision = 1 << 5,
+    /*! \brief
+     * Requires that output format supports writing time to the file.
+     */
+    RequireNewFrameStartTime = 1 << 6,
+    /*! \brief
+     * Requires that output format supports writing time to the file.
+     */
+    RequireNewFrameTimeStep = 1 << 7,
+    /*! \brief
+     * Requires that output format supports writing box information.
+     */
+    RequireNewBox = 1 << 8,
+    /*! \brief
+     * Requires output to support changes to selection of coordinates.
+     *
+     * Default for most methods, will need to be able to write coordinates to
+     * output file or generate an error.
+     */
+    RequireCoordinateSelection = 1 << 9,
+    //! Needed for enumeration array.
+    Count
+};
+
+//! Conversion of flag to its corresponding unsigned long value.
+inline unsigned long convertFlag(CoordinateFileFlags flag)
+{
+    return static_cast<unsigned long>(flag);
+}
+
+//! Enum class for setting basic flags in a t_trxframe
+enum class ChangeSettingType : int
+{
+    PreservedIfPresent,
+    Always,
+    Never,
+    Count
+};
+
+//! Enum class for t_atoms settings
+enum class ChangeAtomsType
+{
+    PreservedIfPresent,
+    AlwaysFromStructure,
+    Never,
+    Always,
+    Count
+};
+
+//! Enum class for setting fields new or not.
+enum class ChangeFrameInfoType
+{
+    PreservedIfPresent,
+    Always,
+    Count
+};
+
+//! Enum class for setting frame time from user input.
+enum class ChangeFrameTimeType
+{
+    PreservedIfPresent,
+    StartTime,
+    TimeStep,
+    Both,
+    Count
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/ioutputadapter.h b/src/include/gromacs/coordinateio/ioutputadapter.h
new file mode 100644 (file)
index 0000000..a21ae8c
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::IOutputAdapter interface for modifying coordinate
+ * file structures before writing them to disk.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_COORDINATEIO_IOUTPUTADAPTER_H
+#define GMX_COORDINATEIO_IOUTPUTADAPTER_H
+
+#include <memory>
+
+#include "gromacs/utility/classhelpers.h"
+
+#include "enums.h"
+
+struct t_trxframe;
+
+namespace gmx
+{
+
+/*!\brief
+ * OutputAdapter class for handling trajectory file flag setting and processing.
+ *
+ * This interface provides the base point upon which modules that modify trajectory frame
+ * datastructures should be build. The interface itself does not provide any direct means
+ * to modify the data, but only gives the virtual method to perform work on a
+ * t_trxframe object. Classes that modify trajectory frames should implement this interface.
+ *
+ * \inlibraryapi
+ * \ingroup module_coordinateio
+ *
+ */
+class IOutputAdapter
+{
+public:
+    /*! \brief
+     * Default constructor for IOutputAdapter interface.
+     */
+    IOutputAdapter() {}
+    virtual ~IOutputAdapter() {}
+    //! Move constructor for old object.
+    explicit IOutputAdapter(IOutputAdapter&& old) noexcept = default;
+
+    /*! \brief
+     * Change t_trxframe according to user input.
+     *
+     * \param[in] framenumber Frame number as reported from the
+     *                        trajectoryanalysis framework or set by user.
+     * \param[in,out] input   Pointer to trajectory analysis frame that will
+     *                        be worked on.
+     */
+    virtual void processFrame(int framenumber, t_trxframe* input) = 0;
+
+    /*! \brief
+     * Checks that the abilities of the output writer are sufficient for this adapter.
+     *
+     * It can happen that a method to write coordinate files does not match with
+     * a requested operation on the input data (e.g. the user requires velocities or
+     * forces to be written to a PDB file).
+     * To check those dependencies, derived classes need to implement a version of this
+     * function to make sure that only matching methods can be used.
+     *
+     * \param[in] abilities The abilities of an output method that need to be checked against
+     *                      the dependencies created by using the derived method.
+     * \throws InconsistentInputError If dependencies can not be matched to abilities.
+     */
+    virtual void checkAbilityDependencies(unsigned long abilities) const = 0;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(IOutputAdapter);
+};
+
+//! Smart pointer to manage the frame adapter object.
+using OutputAdapterPointer = std::unique_ptr<IOutputAdapter>;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/outputadaptercontainer.h b/src/include/gromacs/coordinateio/outputadaptercontainer.h
new file mode 100644 (file)
index 0000000..19d6fca
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::OutputAdapterContainer, a storage object for
+ * multiple outputadapters derived from the IOutputadaper interface.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_COORDINATEIO_OUTPUTADAPTERCONTAINER_H
+#define GMX_COORDINATEIO_OUTPUTADAPTERCONTAINER_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/coordinateio/ioutputadapter.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Storage for output adapters that modify the state of a t_trxframe object.
+ *
+ * The OutputAdapterContainer is responsible for storing the number of
+ * OutputAdapters, as well as the bitmask representing the current requirements
+ * for constructing an CoordinateFile object with the modules registered. It is responsible
+ * for ensuring that no module can be registered multiple times, and that the
+ * correct order for some modifications is observed (e.g. we can not reduce the
+ * number of coordinates written to a file before we have set all the other flags).
+ * Any other behaviour indicates a programming error and triggers an assertion.
+ *
+ * The abilities that need to be cross checked for the container are usually constrained
+ * by the file format the coordinate data will be written to. When declaring new abilities,
+ * these must match the file type for the output.
+ *
+ * \todo Keeping track of those abilities has to be the responsibility of an object
+ *       implementing and interface that declares it capabilities and will execute the
+ *       the function of writing to a file.
+ * \todo This could be changed to instead construct the container with a pointer to an
+ *       ICoordinateOutputWriter that can be passed to the IOutputAdapter modules to check
+ *       their cross-dependencies.
+ */
+class OutputAdapterContainer
+{
+public:
+    //! Only allow constructing the container with defined output abilities.
+    explicit OutputAdapterContainer(unsigned long abilities) : abilities_(abilities) {}
+    //! Allow abilities to be also defined using the enum class.
+    explicit OutputAdapterContainer(CoordinateFileFlags abilities) :
+        abilities_(convertFlag(abilities))
+    {
+    }
+
+    /*! \brief
+     * Add an adapter of a type not previously added.
+     *
+     * Only one adapter of each type can be registered, and the order of adapters
+     * is predefined in the underlying storage object.
+     * Calls internal checks to make sure that the new adapter does not violate
+     * any of the preconditions set to make an CoordinateFile object containing
+     * the registered modules.
+     *
+     * \param[in] adapter unique_ptr to adapter, with container taking ownership here.
+     * \param[in] type What kind of adapter is being added.
+     * \throws InternalError When registering an adapter of a type already registered .
+     * \throws InconsistentInputError When incompatible modules are added.
+     */
+    void addAdapter(OutputAdapterPointer adapter, CoordinateFileFlags type);
+
+    //! Get vector of all registered adapters.
+    ArrayRef<const OutputAdapterPointer> getAdapters() { return outputAdapters_; }
+    //! Get info if we have any registered adapters.
+    bool isEmpty() const;
+
+private:
+    //! Array of registered modules.
+    EnumerationArray<CoordinateFileFlags, OutputAdapterPointer> outputAdapters_;
+    //! Construction time bitmask declaring what the OutputManager can do.
+    unsigned long abilities_ = convertFlag(CoordinateFileFlags::Base);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/outputadapters.h b/src/include/gromacs/coordinateio/outputadapters.h
new file mode 100644 (file)
index 0000000..7a21ac8
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Public API convenience header for accessing outputadapters.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_COORDINATEIO_OUTPUTADAPTERS_H
+#define GMX_COORDINATEIO_OUTPUTADAPTERS_H
+
+#include "gromacs/coordinateio/outputadapters/outputselector.h"
+#include "gromacs/coordinateio/outputadapters/setatoms.h"
+#include "gromacs/coordinateio/outputadapters/setbox.h"
+#include "gromacs/coordinateio/outputadapters/setforces.h"
+#include "gromacs/coordinateio/outputadapters/setprecision.h"
+#include "gromacs/coordinateio/outputadapters/setstarttime.h"
+#include "gromacs/coordinateio/outputadapters/settimestep.h"
+#include "gromacs/coordinateio/outputadapters/setvelocities.h"
+
+#endif
diff --git a/src/include/gromacs/coordinateio/outputadapters/outputselector.h b/src/include/gromacs/coordinateio/outputadapters/outputselector.h
new file mode 100644 (file)
index 0000000..4fc9e17
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::OutputSelector.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_COORDINATEIO_OUTPUTSELECTOR_H
+#define GMX_COORDINATEIO_OUTPUTSELECTOR_H
+
+#include <algorithm>
+
+#include "gromacs/coordinateio/ioutputadapter.h"
+#include "gromacs/selection/selectionoption.h"
+#include "gromacs/topology/atoms.h"
+
+namespace gmx
+{
+
+/*!\brief
+ * OutputSelector class controls setting which coordinates are actually written.
+ *
+ * This adapter selects a subset of the particles and their data for output.
+ * The subset is specified via a selection object (see \ref module_selection).
+ * The corresponding particle data (coordinates, velocities, forces) and
+ * topology information is copied from as available from the input data.
+ *
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ *
+ */
+class OutputSelector : public IOutputAdapter
+{
+public:
+    /*! \brief
+     * Construct OutputSelector object with initial selection.
+     *
+     * Can be used to initialize OutputSelector from outside of trajectoryanalysis
+     * framework.
+     */
+    explicit OutputSelector(const Selection& sel) : sel_(sel), selectionAtoms_(nullptr)
+    {
+        GMX_RELEASE_ASSERT(sel.isValid() && sel.hasOnlyAtoms(),
+                           "Need a valid selection out of simple atom indices");
+    }
+    /*! \brief
+     *  Move assignment constructor for OutputSelector.
+     */
+    OutputSelector(OutputSelector&& old) noexcept = default;
+
+    ~OutputSelector() override {}
+
+    /*! \brief
+     * Change coordinate frame information for output.
+     *
+     * Takes the previously internally stored coordinates and saves them again.
+     * Applies correct number of atoms in this case.
+     *
+     * \param[in] input Coordinate frame to be modified later.
+     */
+    void processFrame(int /*framenumber*/, t_trxframe* input) override;
+
+    void checkAbilityDependencies(unsigned long /* abilities */) const override {}
+
+private:
+    /*! \brief
+     * Selection of atoms that will be written to disk.
+     *
+     * Internal selection of atoms chosen by the user that will be written
+     * to disk during processing.
+     */
+    const Selection& sel_;
+    /*! \brief
+     * Local storage of modified atoms.
+     *
+     * When selection input information, we might will have to adjust the
+     * atoms content to match the new output. To perform this, we keep track
+     * of modified atoms information in this object that is not used by default.
+     */
+    AtomsDataPtr selectionAtoms_;
+    //! Local storage for coordinates
+    std::vector<RVec> localX_;
+    //! Local storage for velocities
+    std::vector<RVec> localV_;
+    //! Local storage for forces
+    std::vector<RVec> localF_;
+    //! Local storage for atom indices
+    std::vector<int> localIndex_;
+};
+
+//! Smart pointer to manage the object.
+using OutputSelectorPointer = std::unique_ptr<OutputSelector>;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/outputadapters/setatoms.h b/src/include/gromacs/coordinateio/outputadapters/setatoms.h
new file mode 100644 (file)
index 0000000..2091cf3
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::SetAtoms.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_COORDINATEIO_SETATOMS_H
+#define GMX_COORDINATEIO_SETATOMS_H
+
+#include <algorithm>
+
+#include "gromacs/coordinateio/ioutputadapter.h"
+#include "gromacs/topology/atoms.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+/*!\brief
+ * SetAtoms class controls availability of atoms data.
+ *
+ * This modules allows the user to specify if a coordinate frame
+ * should contain the t_atoms data structure or not, and sets it in the
+ * new coordinate frame from either the current topology or from
+ * the data in the coordinate frame. The data is later used to identify
+ * if certain output file types are legal or not.
+ *
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ *
+ */
+class SetAtoms : public IOutputAdapter
+{
+public:
+    /*! \brief
+     * Construct SetAtoms object with choice for boolean value
+     * for availability of the t_atoms struct.
+     *
+     * Can be used to initialize SetAtoms from outside of trajectoryanalysis
+     * framework.
+     */
+    explicit SetAtoms(ChangeAtomsType atomFlag, AtomsDataPtr inputAtoms) :
+        atomFlag_(atomFlag), haveStructureFileAtoms_(false), atoms_(std::move(inputAtoms))
+    {
+        if (atoms_ != nullptr)
+        {
+            haveStructureFileAtoms_ = true;
+        }
+        if (atomFlag_ == ChangeAtomsType::Never)
+        {
+            moduleRequirements_ = CoordinateFileFlags::Base;
+        }
+        else
+        {
+            moduleRequirements_ = CoordinateFileFlags::RequireAtomInformation;
+        }
+    }
+    /*! \brief
+     *  Move constructor for SetAtoms.
+     */
+    SetAtoms(SetAtoms&& old) noexcept :
+        atomFlag_(old.atomFlag_),
+        haveStructureFileAtoms_(old.haveStructureFileAtoms_),
+        atoms_(std::move(old.atoms_))
+    {
+    }
+
+    ~SetAtoms() override {}
+
+    /*! \brief
+     * Change coordinate frame information for output.
+     *
+     * Changes the frame t_atoms struct according to user choice and
+     * availability.
+     *
+     * \param[in] input Coordinate frame to be modified later.
+     */
+    void processFrame(int /*framenumber*/, t_trxframe* input) override;
+
+    void checkAbilityDependencies(unsigned long abilities) const override;
+
+private:
+    //! Local function to check that we have a proper t_atoms struct available.
+    bool haveStructureFileAtoms() const { return haveStructureFileAtoms_; }
+    /*! \brief
+     *  Checking if t_trxframe has the atom information saved within.
+     *
+     *  \param[in] input t_trxframe before we start modifying it.
+     */
+    static bool haveFrameAtoms(const t_trxframe& input);
+    //! Test if the atoms data is available for writing.
+    bool haveAtoms(const t_trxframe& input) const
+    {
+        return haveStructureFileAtoms() || haveFrameAtoms(input);
+    }
+    //! Return pointer to t_atoms.
+    t_atoms* atoms()
+    {
+        GMX_RELEASE_ASSERT(haveStructureFileAtoms(), "No atoms information available");
+        return atoms_.get();
+    }
+    //! Flag provided for setting atoms in coordinate frame from user.
+    ChangeAtomsType atomFlag_;
+    //! Flag set if input atoms have been valid from the beginning.
+    bool haveStructureFileAtoms_;
+    /*! \brief
+     *  Atoms information if available.
+     *
+     *  Note, the module takes ownership of the information and
+     *  will clean it up on exit.
+     */
+    AtomsDataPtr atoms_;
+    //! Requirements obtained from user input.
+    CoordinateFileFlags moduleRequirements_;
+};
+
+//! Smart pointer to manage the object.
+using SetAtomsPointer = std::unique_ptr<SetAtoms>;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/outputadapters/setbox.h b/src/include/gromacs/coordinateio/outputadapters/setbox.h
new file mode 100644 (file)
index 0000000..b54f332
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::SetBox.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_COORDINATEIO_SETBOX_H
+#define GMX_COORDINATEIO_SETBOX_H
+
+#include <memory>
+
+#include "gromacs/coordinateio/ioutputadapter.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/trajectory/trajectoryframe.h"
+
+namespace gmx
+{
+
+/*!\brief
+ * Allows changing box information when writing a coordinate file.
+ *
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ */
+class SetBox : public IOutputAdapter
+{
+public:
+    /*! \brief
+     * Construct SetBox object with a new user defined box.
+     */
+    explicit SetBox(const matrix box) { copy_mat(box, box_); }
+    /*! \brief
+     *  Move constructor for SetBox.
+     */
+    SetBox(SetBox&& old) noexcept { copy_mat(old.box_, box_); }
+
+    ~SetBox() override { clear_mat(box_); }
+
+    /*! \brief
+     * Change coordinate frame information for output.
+     *
+     * In this case, box information is added to the \p t_trxframe object
+     * depending on the user input.
+     *
+     * \param[in] input Coordinate frame to be modified later.
+     */
+    void processFrame(int /*framenumner*/, t_trxframe* input) override
+    {
+        copy_mat(box_, input->box);
+    }
+
+    void checkAbilityDependencies(unsigned long /* abilities */) const override {}
+
+private:
+    //! New box information from the user.
+    matrix box_;
+};
+
+//! Smart pointer to manage the object.
+using SetBoxPointer = std::unique_ptr<SetBox>;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/outputadapters/setforces.h b/src/include/gromacs/coordinateio/outputadapters/setforces.h
new file mode 100644 (file)
index 0000000..2244511
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::SetForces.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_COORDINATEIO_SETFORCES_H
+#define GMX_COORDINATEIO_SETFORCES_H
+
+#include <memory>
+
+#include "gromacs/coordinateio/ioutputadapter.h"
+
+namespace gmx
+{
+
+/*!\brief
+ * SetForces class allows changing writing of forces to file.
+ *
+ * This class allows the user to define if forces should be written
+ * to the output coordinate file, and checks if they are available from the
+ * currently processed data.
+ *
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ *
+ */
+class SetForces : public IOutputAdapter
+{
+public:
+    /*! \brief
+     * Construct SetForces object with choice for boolean value.
+     *
+     * Can be used to initialize SetForces from outside of trajectoryanalysis
+     * with the user specified option to write coordinate forces or not.
+     */
+    explicit SetForces(ChangeSettingType force) : force_(force)
+    {
+        if (force == ChangeSettingType::Never)
+        {
+            moduleRequirements_ = CoordinateFileFlags::Base;
+        }
+        else
+        {
+            moduleRequirements_ = CoordinateFileFlags::RequireForceOutput;
+        }
+    }
+    /*! \brief
+     *  Move constructor for SetForces.
+     */
+    SetForces(SetForces&& old) noexcept = default;
+
+    ~SetForces() override {}
+
+    /*! \brief
+     * Change coordinate frame information for output.
+     *
+     * In this case, the correct flag for writing the forces is applied
+     * to the output frame, depending on user selection and availability
+     * in the input data.
+     *
+     * \param[in] input Coordinate frame to be modified later.
+     */
+    void processFrame(int /*framenumner*/, t_trxframe* input) override;
+
+    void checkAbilityDependencies(unsigned long abilities) const override;
+
+private:
+    /*! \brief
+     * Flag to specify if forces should be written.
+     *
+     * Internal storage for the user choice for writing coordinate forces.
+     */
+    ChangeSettingType force_;
+    //! Local requirements to be determined from user input.
+    CoordinateFileFlags moduleRequirements_;
+};
+
+//! Smart pointer to manage the object.
+using SetForcesPointer = std::unique_ptr<SetForces>;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/outputadapters/setprecision.h b/src/include/gromacs/coordinateio/outputadapters/setprecision.h
new file mode 100644 (file)
index 0000000..31971fe
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::SetPrecision.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_FILEIO_SETPRECISION_H
+#define GMX_FILEIO_SETPRECISION_H
+
+#include <algorithm>
+
+#include "gromacs/coordinateio/ioutputadapter.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*!\brief
+ * SetPrecision class allows changing file writing precision.
+ *
+ * This class allows the user to define the precision for writing
+ * coordinate data to output files.
+ *
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ *
+ */
+class SetPrecision : public IOutputAdapter
+{
+public:
+    /*! \brief
+     * Construct SetPrecision object with user defined value.
+     *
+     * Can be used to initialize SetPrecision from outside of trajectoryanalysis
+     * with the user specified option to change precision or not.
+     *
+     * \param[in] precision User defined value for output precision in file types that support it.
+     */
+    explicit SetPrecision(int precision) : precision_(precision)
+    {
+        // Only request special treatment if precision is not the default.
+        if (precision == 3)
+        {
+            moduleRequirements_ = CoordinateFileFlags::Base;
+        }
+        else
+        {
+            moduleRequirements_ = CoordinateFileFlags::RequireChangedOutputPrecision;
+        }
+    }
+    /*! \brief
+     *  Move constructor for SetPrecision.
+     */
+    SetPrecision(SetPrecision&& old) noexcept = default;
+
+    ~SetPrecision() override {}
+
+    void processFrame(int /*framenumber*/, t_trxframe* input) override;
+
+    void checkAbilityDependencies(unsigned long abilities) const override;
+
+private:
+    //! User specified changes to default precision.
+    int precision_;
+    //! Module requirements dependent on user input.
+    CoordinateFileFlags moduleRequirements_;
+};
+
+//! Smart pointer to manage the outputselector object.
+using SetPrecisionPointer = std::unique_ptr<SetPrecision>;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/outputadapters/setstarttime.h b/src/include/gromacs/coordinateio/outputadapters/setstarttime.h
new file mode 100644 (file)
index 0000000..6f47e16
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::SetStartTime
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_FILEIO_SETSTARTTIME_H
+#define GMX_FILEIO_SETSTARTTIME_H
+
+#include "gromacs/coordinateio/ioutputadapter.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*!\brief
+ * SetStartTime class allows changing trajectory time information.
+ *
+ * This class allows the user to set custom start time information for the
+ * current frame in a trajectory.
+ *
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ *
+ */
+class SetStartTime : public IOutputAdapter
+{
+public:
+    /*! \brief
+     * Construct object with choice for how to change initial time.
+     *
+     * \param[in] startTime User defined value for the initial time.
+     */
+    explicit SetStartTime(real startTime) :
+        startTime_(startTime), haveProcessedFirstFrame_(false), differenceToInitialTime_(0)
+    {
+    }
+    /*! \brief
+     *  Move constructor for SetStartTime.
+     */
+    SetStartTime(SetStartTime&& old) noexcept = default;
+
+    ~SetStartTime() override {}
+
+    void processFrame(int /* framenumber */, t_trxframe* input) override;
+
+    void checkAbilityDependencies(unsigned long /* abilities */) const override {}
+
+private:
+    /*! \brief
+     * Set initial time from first processed frame.
+     *
+     * Calculates the time shift between the user set time and the time
+     * in the coordinate frame being processed from the first processed coordinate
+     * frame. This time shift is then used to calculate new frame times for each processed
+     * coordinate frame.
+     *
+     * \param[in] initialTime Time value obtained from first frame.
+     */
+    void setInitialTime(real initialTime);
+
+    /*! \brief
+     * Stores the value of the initial time.
+     *
+     * In case users supply a new time step, the initial time of the
+     * processed coordinate frame is stored here. In case the user also supplies
+     * a new initial time, this variable is set to the user input instead.
+     */
+    real startTime_;
+    //! Has the first frame been processed?
+    bool haveProcessedFirstFrame_;
+    /*! \brief
+     * If the initial time is changed, we need to keep track of the initial
+     * time difference to adjust the time of all following frames.
+     */
+    real differenceToInitialTime_;
+};
+
+//! Smart pointer to manage the object.
+using SetStartTimePointer = std::unique_ptr<SetStartTime>;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/outputadapters/settimestep.h b/src/include/gromacs/coordinateio/outputadapters/settimestep.h
new file mode 100644 (file)
index 0000000..fbae845
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::SetTimeStep
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_FILEIO_SETTIMESTEP_H
+#define GMX_FILEIO_SETTIMESTEP_H
+
+#include "gromacs/coordinateio/ioutputadapter.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*!\brief
+ * SetTimeStep class allows changing trajectory time information.
+ *
+ * This class allows the user to set custom time step information for the
+ * current frame in a trajectory.
+ *
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ *
+ */
+class SetTimeStep : public IOutputAdapter
+{
+public:
+    /*! \brief
+     * Construct SetTime object with choice for how to change time.
+     *
+     * Can be used to initialize SetTime from outside of trajectoryanalysis
+     * with the user specified option to change frame time information or not.
+     *
+     * \param[in] timeStep User defined value for the time step.
+     */
+    explicit SetTimeStep(real timeStep) :
+        timeStep_(timeStep), previousFrameTime_(0.0), haveProcessedFirstFrame_(false)
+    {
+    }
+    /*! \brief
+     *  Move constructor for SetTimeStep.
+     */
+    SetTimeStep(SetTimeStep&& old) noexcept = default;
+
+    ~SetTimeStep() override {}
+
+    void processFrame(int framenumber, t_trxframe* input) override;
+
+    void checkAbilityDependencies(unsigned long /* abilities */) const override {}
+
+private:
+    /*! \brief
+     * Calculates the time of the current coordinate frame based on user input.
+     *
+     * If the current frame is the first one, no changes to the time are made.
+     * For subsequent frames, the new frame time is based on the user input
+     * and the time of the previous frame.
+     *
+     * \param[in] currentInputFrameTime Input from processed coordinate frame.
+     */
+    real calculateNewFrameTime(real currentInputFrameTime);
+
+    //! Time difference between frames.
+    real timeStep_;
+    //! Time of the previous frame.
+    real previousFrameTime_;
+    //! Has the first frame been processed?
+    bool haveProcessedFirstFrame_;
+};
+
+//! Smart pointer to manage the object.
+using SetTimeStepPointer = std::unique_ptr<SetTimeStep>;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/outputadapters/setvelocities.h b/src/include/gromacs/coordinateio/outputadapters/setvelocities.h
new file mode 100644 (file)
index 0000000..5eca665
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx:SetVelocities.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ */
+#ifndef GMX_COORDINATEIO_SETVELOCITIES_H
+#define GMX_COORDINATEIO_SETVELOCITIES_H
+
+#include <memory>
+
+#include "gromacs/coordinateio/ioutputadapter.h"
+
+namespace gmx
+{
+
+/*!\brief
+ * SetVelocities class allows changing writing of velocities to file.
+ *
+ * This class allows the user to define if velocities should be written
+ * to the output coordinate file, and checks if they are available from the
+ * currently processed data.
+ *
+ * \inpublicapi
+ * \ingroup module_coordinateio
+ *
+ */
+class SetVelocities : public IOutputAdapter
+{
+public:
+    /*! \brief
+     * Construct SetVelocities object with choice for boolean value.
+     *
+     * Can be used to initialize SetVelocities from outside of trajectoryanalysis
+     * with the user specified option to write coordinate velocities or not.
+     */
+    explicit SetVelocities(ChangeSettingType velocity) : velocity_(velocity)
+    {
+        if (velocity_ == ChangeSettingType::Never)
+        {
+            moduleRequirements_ = CoordinateFileFlags::Base;
+        }
+        else
+        {
+            moduleRequirements_ = CoordinateFileFlags::RequireVelocityOutput;
+        }
+    }
+    /*! \brief
+     *  Move constructor for SetVelocities.
+     */
+    SetVelocities(SetVelocities&& old) noexcept = default;
+
+    ~SetVelocities() override {}
+
+    /*! \brief
+     * Change coordinate frame information for output.
+     *
+     * In this case, the correct flag for writing the velocities is applied
+     * to the output frame, depending on user selection and availability
+     * in the input data.
+     *
+     * \param[in] input Coordinate frame to be modified later.
+     */
+    void processFrame(int /*framenumber*/, t_trxframe* input) override;
+
+    void checkAbilityDependencies(unsigned long abilities) const override;
+
+private:
+    //! Flag to specify if velocities should be written.
+    ChangeSettingType velocity_;
+    //! Local requirements determined from user input.
+    CoordinateFileFlags moduleRequirements_;
+};
+
+//! Smart pointer to manage the object.
+using SetVelocitiesPointer = std::unique_ptr<SetVelocities>;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/requirements.h b/src/include/gromacs/coordinateio/requirements.h
new file mode 100644 (file)
index 0000000..b49f2cd
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*!\file
+ * \brief
+ * Storage object for requirements to build coordinate file writer.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \ingroup module_coordinateio
+ * \inlibraryapi
+ */
+#ifndef GMX_COORDINATEIO_OUTPUTREQUIREMENTS_H
+#define GMX_COORDINATEIO_OUTPUTREQUIREMENTS_H
+
+#include <vector>
+
+#include "gromacs/math/vec.h"
+#include "gromacs/options/ioptionscontainer.h"
+
+#include "enums.h"
+
+namespace gmx
+{
+
+/*!\brief
+ * Container for the user input values that will be used by the builder
+ * to determine which OutputAdapters should/could/will be registered
+ * to the coordinate file writer.
+ */
+class OutputRequirementOptionDirector
+{
+public:
+    /*! \brief
+     * Populate requirements from option interface.
+     *
+     * \param[in] options Pointer to global options framework.
+     */
+    void initOptions(IOptionsContainer* options);
+
+    /*! \brief
+     * Provide processed requirements to create on coordinate file writing method.
+     */
+    struct OutputRequirements process() const;
+
+private:
+    //! Should velocities be written.
+    ChangeSettingType velocity_ = ChangeSettingType::PreservedIfPresent;
+    //! Should forces be written.
+    ChangeSettingType force_ = ChangeSettingType::PreservedIfPresent;
+    //! Precision used in output file.
+    int prec_ = 3;
+    //! If a new precision value has been set.
+    bool setNewPrecision_ = false;
+    //! Time for first frame to start.
+    real startTimeValue_ = 0;
+    //! Time step to use between frames.
+    real timeStepValue_ = 0;
+    //! If start time value has been assigned.
+    bool setNewStartTime_ = false;
+    //! If a new time step value has been assigned.
+    bool setNewTimeStep_ = false;
+    //! User supplied diagonal box vector.
+    std::vector<real> newBoxVector_;
+    //! If a new box vector has been set.
+    bool setNewBox_ = false;
+    //! Should frame atom setting be changed.
+    ChangeAtomsType atoms_ = ChangeAtomsType::PreservedIfPresent;
+};
+
+/*! \brief
+ * Finalized version of requirements after processing.
+ */
+struct OutputRequirements
+{
+    //! Should velocities be written.
+    ChangeSettingType velocity = ChangeSettingType::PreservedIfPresent;
+    //! Should forces be written.
+    ChangeSettingType force = ChangeSettingType::PreservedIfPresent;
+    //! Should precision be changed.
+    ChangeFrameInfoType precision = ChangeFrameInfoType::PreservedIfPresent;
+    //! Precision used in output file.
+    int prec = 3;
+    //! Should frame start time be changed.
+    ChangeFrameTimeType frameTime = ChangeFrameTimeType::PreservedIfPresent;
+    //! Time for first frame to start.
+    real startTimeValue = 0;
+    //! Time step to use between frames.
+    real timeStepValue = 0;
+    //! Box vector converted to matrix format.
+    matrix newBox = { { 0 } };
+    //! Should frame box be changed.
+    ChangeFrameInfoType box = ChangeFrameInfoType::PreservedIfPresent;
+    //! Should frame atom setting be changed.
+    ChangeAtomsType atoms = ChangeAtomsType::PreservedIfPresent;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/tests/coordinate_test.h b/src/include/gromacs/coordinateio/tests/coordinate_test.h
new file mode 100644 (file)
index 0000000..5e49390
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*!\file
+ * \libinternal
+ * \brief
+ * Helper classes for coordinatefile and coordinateio tests
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_coordinateio
+ */
+
+#ifndef GMX_COORDINATEIO_TESTS_COORDINATEIO_H
+#define GMX_COORDINATEIO_TESTS_COORDINATEIO_H
+
+#include <utility>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/coordinateio/coordinatefile.h"
+#include "gromacs/coordinateio/ioutputadapter.h"
+#include "gromacs/coordinateio/requirements.h"
+#include "gromacs/options/options.h"
+#include "gromacs/options/optionsassigner.h"
+#include "gromacs/selection/selectioncollection.h"
+#include "gromacs/selection/selectionoption.h"
+#include "gromacs/selection/selectionoptionmanager.h"
+#include "gromacs/trajectoryanalysis/topologyinformation.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/smalloc.h"
+
+#include "testutils/cmdlinetest.h"
+#include "testutils/testasserts.h"
+#include "testutils/testfilemanager.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+/*!\brief
+ * Create minimal TrajectoryFrameWriter using the provided builder.
+ *
+ * \param[in] filename      Name of file to create object for.
+ * \param[in] topology      Reference to input top.
+ * \param[in] selection     Reference to input selection.
+ * \param[in] requirements  Requirements for constructing OutputManagar.
+ * \throws InconsistentInputError When builder can not create the CoordinateFile.
+ * \returns unique_ptr to new CoordinateFile object.
+ */
+inline TrajectoryFrameWriterPointer createMinimalTrajectoryFrameWriter(const std::string& filename,
+                                                                       const TopologyInformation& topology,
+                                                                       const Selection& selection,
+                                                                       OutputRequirements requirements)
+{
+    return createTrajectoryFrameWriter(topology.mtop(),
+                                       selection,
+                                       filename,
+                                       topology.hasTopology() ? topology.copyAtoms() : nullptr,
+                                       requirements);
+}
+/*! \libinternal \brief
+ * Helper class for tests that need an initialized selection.
+ */
+class ModuleSelection
+{
+public:
+    ModuleSelection() : manager_(&sc_)
+    {
+        options_.addManager(&manager_);
+        sc_.setReferencePosType("atom");
+        sc_.setOutputPosType("atom");
+        top_.fillFromInputFile(TestFileManager::getInputFilePath("lysozyme.pdb"));
+        sc_.setTopology(top_.mtop(), 0);
+    }
+
+    /*! \brief
+     * Method to add a valid selection option to the Module, or an invalid
+     * one for testing.
+     *
+     * \param[in] sel Selection to add option to.
+     * \param[in] useValid Set if the added selection should be valid for the module.
+     */
+    void addOptionForSelection(Selection* sel, bool useValid);
+
+    /*! \brief
+     * Set the actual values for the selection.
+     *
+     * \param[in] options Option to set values for.
+     * \param[in] sel Selection to use.
+     * \param[in] useValid Set if the added selection should be valid for the module.
+     */
+    void setSelectionOptionValues(Options* options, Selection* sel, bool useValid);
+
+    /*! \brief
+     * Get pointer to options to set values.
+     *
+     * \returns Pointer to options.
+     */
+    Options* getOption() { return &options_; }
+
+private:
+    //! Selection collection used for handling test selection.
+    SelectionCollection sc_;
+    //! Selection manager for test selection.
+    SelectionOptionManager manager_;
+    //! Options manager for test selection input.
+    Options options_;
+    //! Topology information needed for test selection atoms.
+    TopologyInformation top_;
+};
+
+inline void ModuleSelection::addOptionForSelection(Selection* sel, bool useValid)
+{
+    if (useValid)
+    {
+        options_.addOption(SelectionOption("sel").store(sel).onlyAtoms());
+    }
+    else
+    {
+        options_.addOption(SelectionOption("sel").store(sel).dynamicMask());
+    }
+}
+
+inline void ModuleSelection::setSelectionOptionValues(Options* options, Selection* sel, bool useValid)
+{
+    OptionsAssigner assigner(options);
+    assigner.start();
+    assigner.startOption("sel");
+    if (useValid)
+    {
+        assigner.appendValue("all");
+    }
+    else
+    {
+        assigner.appendValue("res_cog of all");
+    }
+    assigner.finishOption();
+    assigner.finish();
+
+    ASSERT_TRUE(sel->isValid());
+    EXPECT_NO_THROW(sc_.compile());
+}
+
+/*! \libinternal \brief
+ * Test fixture to test matching file types for modules.
+ */
+class ModuleTest : public gmx::test::CommandLineTestBase, public ::testing::WithParamInterface<const char*>
+{
+public:
+    /*! \brief
+     * Run the builder to create an TrajectoryFrameWriter during tests.
+     *
+     * \param[in] filename Name for output file, to determine filetype during construction.
+     * \param[in] requirements Requirements for adding to the object.
+     * \returns The newly created object.
+     */
+    TrajectoryFrameWriterPointer runTest(const char* filename, const OutputRequirements& requirements) const
+    {
+        return createMinimalTrajectoryFrameWriter(filename, dummyTopology_, dummySelection_, requirements);
+    }
+    //! Add topology information to test if needed.
+    void addTopology()
+    {
+        dummyTopology_.fillFromInputFile(TestFileManager::getInputFilePath("lysozyme.pdb"));
+    }
+    //! Dummy topology to use to create CoordinateFile.
+    TopologyInformation dummyTopology_;
+    //! Dummy selection.
+    Selection dummySelection_;
+};
+
+} // namespace test
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/tests/outputadapters.h b/src/include/gromacs/coordinateio/tests/outputadapters.h
new file mode 100644 (file)
index 0000000..64569a0
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*!\file
+ * \libinternal
+ * \brief
+ * Helpers and data for outputadapter module tests.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_coordinateio
+ */
+
+#ifndef GMX_COORDINATEIO_TESTS_MODULE_H
+#define GMX_COORDINATEIO_TESTS_MODULE_H
+
+#include "gmxpre.h"
+
+#include "config.h"
+
+#include <gtest/gtest.h>
+
+#include "gromacs/coordinateio/outputadapters/outputselector.h"
+#include "gromacs/coordinateio/outputadapters/setatoms.h"
+#include "gromacs/coordinateio/outputadapters/setbox.h"
+#include "gromacs/coordinateio/outputadapters/setforces.h"
+#include "gromacs/coordinateio/outputadapters/setprecision.h"
+#include "gromacs/coordinateio/outputadapters/setstarttime.h"
+#include "gromacs/coordinateio/outputadapters/settimestep.h"
+#include "gromacs/coordinateio/outputadapters/setvelocities.h"
+
+#include "gromacs/coordinateio/tests/coordinate_test.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+/*!\libinternal \brief  Helper to test supported file names. */
+class SetAtomsSupportedFiles : public ModuleTest
+{
+public:
+    void prepareTest(const char* filename)
+    {
+        addTopology();
+        //! Storage for requirements.
+        OutputRequirements requirements;
+
+        requirements.atoms = ChangeAtomsType::AlwaysFromStructure;
+
+        EXPECT_NO_THROW(runTest(filename, requirements));
+    }
+};
+
+/*!\libinternal \brief  Helper to test supported file names. */
+class SetAtomsUnSupportedFiles : public ModuleTest
+{
+public:
+    void prepareTest(const char* filename)
+    {
+        //! Storage for requirements.
+        OutputRequirements requirements;
+
+        requirements.atoms = ChangeAtomsType::AlwaysFromStructure;
+
+        EXPECT_THROW(runTest(filename, requirements), InconsistentInputError);
+    }
+};
+
+/*!\libinternal \brief  Helper to test supported file names. */
+class AnyOutputSupportedFiles : public ModuleTest, public ModuleSelection
+{
+public:
+    void prepareTest(const char* filename)
+    {
+        addTopology();
+        //! Storage for requirements.
+        OutputRequirements requirements;
+        //! Local atoms
+        Selection sel;
+        //! Local box
+        matrix box;
+
+        clear_mat(box);
+
+        addOptionForSelection(&dummySelection_, true);
+        setSelectionOptionValues(getOption(), &dummySelection_, true);
+
+        copy_mat(requirements.newBox, box);
+        requirements.box       = ChangeFrameInfoType::Always;
+        requirements.frameTime = ChangeFrameTimeType::Both;
+
+        EXPECT_NO_THROW(runTest(filename, requirements));
+    }
+};
+
+/*!\libinternal \brief  Helper to test support for disabling all output options. */
+class NoOptionalOutput : public ModuleTest
+{
+public:
+    void prepareTest(const char* filename)
+    {
+        addTopology();
+        //! Storage for requirements.
+        OutputRequirements requirements;
+
+        requirements.atoms     = ChangeAtomsType::Never;
+        requirements.velocity  = ChangeSettingType::Never;
+        requirements.force     = ChangeSettingType::Never;
+        requirements.precision = ChangeFrameInfoType::Always;
+        requirements.prec      = 3;
+
+        EXPECT_NO_THROW(runTest(filename, requirements));
+    }
+};
+
+/*!\libinternal \brief  Helper to test that invalid selection is rejected */
+class OutputSelectorDeathTest : public ModuleTest, public ModuleSelection
+{
+public:
+    void prepareTest()
+    {
+        //! Storage for frameadapters.
+        OutputAdapterContainer adapters(CoordinateFileFlags::Base);
+        //! Local atoms
+        Selection sel;
+
+        addOptionForSelection(&sel, false);
+        setSelectionOptionValues(getOption(), &sel, false);
+
+        GMX_ASSERT_DEATH_IF_SUPPORTED(adapters.addAdapter(std::make_unique<OutputSelector>(sel),
+                                                          CoordinateFileFlags::RequireCoordinateSelection),
+                                      "Need a valid selection out of simple atom indices");
+    }
+};
+
+/*!\libinternal \brief  Helper to test supported file names. */
+class SetVelocitySupportedFiles : public ModuleTest
+{
+public:
+    void prepareTest(const char* filename)
+    {
+        addTopology();
+        //! Storage for requirements.
+        OutputRequirements requirements;
+
+        requirements.velocity = ChangeSettingType::Always;
+
+        EXPECT_NO_THROW(runTest(filename, requirements));
+    }
+};
+
+/*!\libinternal \brief  Helper to test supported file names. */
+class SetVelocityUnSupportedFiles : public ModuleTest
+{
+public:
+    void prepareTest(const char* filename)
+    {
+        //! Storage for requirements.
+        OutputRequirements requirements;
+
+        requirements.velocity = ChangeSettingType::Always;
+
+        EXPECT_THROW(runTest(filename, requirements), InconsistentInputError);
+    }
+};
+
+/*!\libinternal \brief  Helper to test supported file names. */
+class SetForceSupportedFiles : public ModuleTest
+{
+public:
+    void prepareTest(const char* filename)
+    {
+        addTopology();
+        //! Storage for requirements.
+        OutputRequirements requirements;
+
+        requirements.force = ChangeSettingType::Always;
+
+        EXPECT_NO_THROW(runTest(filename, requirements));
+    }
+};
+
+/*!\libinternal \brief  Helper to test supported file names. */
+class SetForceUnSupportedFiles : public ModuleTest
+{
+public:
+    void prepareTest(const char* filename)
+    {
+        //! Storage for requirements.
+        OutputRequirements requirements;
+
+        requirements.force = ChangeSettingType::Always;
+
+        EXPECT_THROW(runTest(filename, requirements), InconsistentInputError);
+    }
+};
+
+/*!\libinternal \brief  Helper to test supported file names. */
+class SetPrecisionSupportedFiles : public ModuleTest
+{
+public:
+    /*! \brief Set up the test case before running.
+     *
+     * \param[in] filename Name of the outputfile used to specify file type.
+     */
+    void prepareTest(const char* filename)
+    {
+        addTopology();
+        OutputRequirements requirements;
+
+        requirements.precision = ChangeFrameInfoType::Always;
+        requirements.prec      = 5;
+
+        EXPECT_NO_THROW(runTest(filename, requirements));
+    }
+};
+
+/*!\libinternal \brief  Helper to test supported file names. */
+class SetPrecisionUnSupportedFiles : public ModuleTest
+{
+public:
+    /*! \brief Set up the test case before running.
+     *
+     * \param[in] filename Name of the outputfile used to specify file type.
+     */
+    void prepareTest(const char* filename)
+    {
+        OutputRequirements requirements;
+
+        requirements.precision = ChangeFrameInfoType::Always;
+        requirements.prec      = 5;
+
+        EXPECT_THROW(runTest(filename, requirements), InconsistentInputError);
+    }
+};
+
+//! Names here work for setAtoms module
+const char* const setAtomsSupported[] = {
+#if GMX_USE_TNG
+    "spc2-traj.tng",
+#endif
+    "spc2-traj.gro",
+    "spc2-traj.pdb",
+};
+
+//! Names here don't work for setAtoms module
+const char* const setAtomsUnSupported[] = { "spc2-traj.trr", "spc2-traj.xtc", "spc2-traj.g96" };
+
+/*! \brief
+ *  Names here work for stuff that has no specific requirements.
+ *
+ *  PDB and GRO format are not tested here because they also require atoms information
+ *  that is incompatible with the other output formats.
+ */
+const char* const anySupported[] = { "spc2-traj.trr",
+#if GMX_USE_TNG
+                                     "spc2-traj.tng",
+#endif
+                                     "spc2-traj.xtc",
+                                     "spc2-traj.g96" };
+
+//! Names here work for setVelocity module
+const char* const setVelocitySupported[] = {
+#if GMX_USE_TNG
+    "spc2-traj.tng",
+#endif
+    "spc2-traj.gro",
+    "spc2-traj.trr",
+};
+
+//! Names here don't work for setVelocity module
+const char* const setVelocityUnSupported[] = { "spc2-traj.xtc", "spc2-traj.pdb", "spc2-traj.g96" };
+
+//! Names here work for setForce module
+const char* const setForceSupported[] = {
+#if GMX_USE_TNG
+    "spc2-traj.tng",
+#endif
+    "spc2-traj.trr",
+};
+
+//! Names here don't work for setForce module
+const char* const setForceUnSupported[] = { "spc2-traj.xtc",
+                                            "spc2-traj.pdb",
+                                            "spc2-traj.gro",
+                                            "spc2-traj.g96" };
+
+//! Names here work for setPrecision module
+const char* const setPrecisionSupported[] = {
+#if GMX_USE_TNG
+    "spc2-traj.tng",
+#endif
+    "spc2-traj.xtc",
+};
+
+//! Names here don't work for setPrecision module
+const char* const setPrecisionUnSupported[] = { "spc2-traj.trr",
+                                                "spc2-traj.pdb",
+                                                "spc2-traj.gro",
+                                                "spc2-traj.g96" };
+
+
+} // namespace test
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/tests/requirements.h b/src/include/gromacs/coordinateio/tests/requirements.h
new file mode 100644 (file)
index 0000000..8a91f71
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*!\file
+ * \libinternal
+ * \brief
+ * Helpers and data for flag setting method.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_coordinateio
+ */
+
+#ifndef GMX_COORDINATEIO_TESTS_FLAG_H
+#define GMX_COORDINATEIO_TESTS_FLAG_H
+
+#include "gmxpre.h"
+
+#include <gtest/gtest.h>
+
+#include "gromacs/coordinateio/requirements.h"
+#include "gromacs/options/options.h"
+#include "gromacs/utility/any.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "gromacs/coordinateio/tests/coordinate_test.h"
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+
+namespace test
+{
+
+//! Test enums to decide on how to use options framework
+enum class TestEnums
+{
+    efTestString,
+    efTestInt,
+    efTestFloat
+};
+
+/*!\brief
+ * Helper function to split string with multiple values into option settings.
+ *
+ * \param[in] assigner     Options assigner to use.
+ * \param[in] optionValues String to split that contains option values.
+ * \param[in] type         Determine which input time we are using.
+ */
+static void splitAndAddValues(OptionsAssigner* assigner, const std::string& optionValues, TestEnums type)
+{
+    std::vector<std::string> strings = splitString(optionValues);
+    for (const auto& entry : strings)
+    {
+        switch (type)
+        {
+            case (TestEnums::efTestFloat):
+            {
+                real value = std::stod(entry);
+                Any  var(value);
+                assigner->appendValue(var);
+                break;
+            }
+            case (TestEnums::efTestString):
+            {
+                assigner->appendValue(entry);
+                break;
+            }
+            case (TestEnums::efTestInt):
+            {
+                int value = std::stoi(entry);
+                Any var(value);
+                assigner->appendValue(var);
+                break;
+            }
+            default: GMX_THROW(InternalError("No such input type"));
+        }
+    }
+}
+
+
+/*! \libinternal \brief
+ * Test fixture to test user inputs.
+ */
+class FlagTest : public gmx::test::CommandLineTestBase
+{
+public:
+    FlagTest() { requirementsBuilder_.initOptions(&options_); }
+
+    /*! \brief
+     * Set value for options in flag setting object.
+     *
+     * \param[in] optionName   Name of the option to add value for.
+     * \param[in] optionValues Values to set for option.
+     * \param[in] options      Container for options.
+     * \param[in] type         Need to know type of entries.
+     */
+    static void setModuleFlag(const std::string& optionName,
+                              const std::string& optionValues,
+                              Options*           options,
+                              TestEnums          type)
+    {
+        OptionsAssigner assigner(options);
+        assigner.start();
+        assigner.startOption(optionName.c_str());
+        splitAndAddValues(&assigner, optionValues, type);
+
+        assigner.finishOption();
+        assigner.finish();
+    }
+
+    //! Storage of requirments.
+    OutputRequirementOptionDirector requirementsBuilder_;
+    //! Options storage
+    Options options_;
+};
+
+} // namespace test
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/coordinateio/tests/testmodule.h b/src/include/gromacs/coordinateio/tests/testmodule.h
new file mode 100644 (file)
index 0000000..dc68a35
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*!\file
+ * \internal
+ * \brief
+ * Dummy module used for tests and as an implementation example.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \libinternal
+ * \ingroup module_coordinateio
+ */
+
+#ifndef GMX_COORDINATEIO_TESTMODULE_H
+#define GMX_COORDINATEIO_TESTMODULE_H
+
+#include "gromacs/coordinateio/ioutputadapter.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+class DummyOutputModule : public IOutputAdapter
+{
+public:
+    explicit DummyOutputModule(CoordinateFileFlags requirementsFlag) :
+        moduleRequirements_(requirementsFlag)
+    {
+    }
+
+    DummyOutputModule(DummyOutputModule&& old) noexcept = default;
+
+    ~DummyOutputModule() override {}
+
+    void processFrame(int /*framenumber*/, t_trxframe* /*input*/) override {}
+
+    void checkAbilityDependencies(unsigned long abilities) const override;
+
+private:
+    //! Local requirements
+    CoordinateFileFlags moduleRequirements_;
+};
+
+} // namespace test
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/correlationfunctions/autocorr.h b/src/include/gromacs/correlationfunctions/autocorr.h
new file mode 100644 (file)
index 0000000..3f3ccb2
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal
+ * \defgroup module_correlationfunctions Correlation functions
+ * \ingroup group_analysismodules
+ * \brief
+ * Compute correlation functions and fit analytical functions to the result.
+ */
+/*! \libinternal
+ * \file
+ * \brief
+ * Declares routine for computing autocorrelation functions
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \inlibraryapi
+ * \ingroup module_correlationfunctions
+ */
+#ifndef GMX_AUTOCORR_H
+#define GMX_AUTOCORR_H
+
+#include "gromacs/commandline/pargs.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_output_env_t;
+
+/*! \brief Normal correlation f(t)*f(t+dt) */
+#define eacNormal (1 << 0)
+/*! \brief Cosine correlation cos(f(t)-f(t+dt)) */
+#define eacCos (1 << 1)
+/*! \brief Vector correlation f(t).f(t+dt) */
+#define eacVector (1 << 2)
+/*! \brief Norm of cross product |f(t) (x) f(t+dt)| */
+#define eacRcross (1 << 3 | eacVector)
+/*! \brief Vector with Legendre polynomial of order 0 (same as vector) */
+#define eacP0 (1 << 4 | eacVector)
+/*! \brief Vector with Legendre polynomial of order P_1(f(t).f(t+dt)) */
+#define eacP1 (1 << 5 | eacVector)
+/*! \brief Vector with Legendre polynomial of order P_2(f(t).f(t+dt)) */
+#define eacP2 (1 << 6 | eacVector)
+/*! \brief Vector with Legendre polynomial of order P_3(f(t).f(t+dt)) */
+#define eacP3 (1 << 7 | eacVector)
+/*! \brief Vector with Legendre polynomial of order P_4(f(t).f(t+dt)) */
+#define eacP4 (1 << 8 | eacVector)
+/*! \brief Binary identy correlation (f(t) == f(t+dt)) */
+#define eacIden (1 << 9) // Not supported for multiple cores
+
+/*! \brief
+ * Add commandline arguments related to autocorrelations to the existing array.
+ * *npargs must be initialised to the number of elements in pa,
+ * it will be incremented appropriately.
+ *
+ * \param npargs The number of arguments before and after (is changed in this function)
+ * \param[in] pa The initial argument list
+ * \return the new array
+ */
+t_pargs* add_acf_pargs(int* npargs, t_pargs* pa);
+
+/*! \brief
+ * Returns the number of points to output from a correlation function.
+ * Works only AFTER do_auto_corr has been called!
+ * \return the output length for the correlation function
+ */
+int get_acfnout();
+
+/*! \brief
+ * Returns the fitting function selected.
+ * Works only AFTER do_auto_corr has been called!
+ * \return the fit function type.
+ */
+int get_acffitfn();
+
+/*! \brief
+ * Calls low_do_autocorr (see below). add_acf_pargs has to be called before this
+ * can be used.
+ * \param[in] fn File name for xvg output (may this be NULL)?
+ * \param[in] oenv The output environment information
+ * \param[in] title is the title in the output file
+ * \param[in] nframes is the number of frames in the time series
+ * \param[in] nitem is the number of items
+ * \param[in,out] c1 is an array of dimension [ 0 .. nitem-1 ] [ 0 .. nframes-1 ]
+ *          on output, this array is filled with the correlation function
+ *          to reduce storage
+ * \param[in] dt is the time between frames
+ * \param[in] mode Different types of ACF can be done, see above
+ * \param[in] bAver    If set, all ndih C(t) functions are averaged into a single
+ *          C(t)
+ */
+void do_autocorr(const char*             fn,
+                 const gmx_output_env_t* oenv,
+                 const char*             title,
+                 int                     nframes,
+                 int                     nitem,
+                 real**                  c1,
+                 real                    dt,
+                 unsigned long           mode,
+                 gmx_bool                bAver);
+
+/*! \brief
+ * Low level computation of autocorrelation functions
+ *
+ * do_autocorr calculates autocorrelation functions for many things.
+ * It takes a 2 d array containing nitem arrays of length nframes
+ * for each item the ACF is calculated.
+ *
+ * A number of "modes" exist for computation of the ACF controlled
+ * by variable mode, with the following meaning.
+ *
+ * Mode       | Function
+ * -----------|------------
+ * eacNormal  | C(t) = < X (tau) * X (tau+t) >
+ * eacCos     | C(t) = < cos (X(tau) - X(tau+t)) >
+ * eacIden    | C(t) = < (X(tau) == X(tau+t)) > (not fully supported yet)
+ * eacVector  | C(t) = < X(tau) * X(tau+t)
+ * eacP1      | C(t) = < cos (X(tau) * X(tau+t) >
+ * eacP2      | C(t) = 1/2 * < 3 cos (X(tau) * X(tau+t) - 1 >
+ * eacRcross  | C(t) = < ( X(tau) * X(tau+t) )^2 >
+ *
+ * For modes eacVector, eacP1, eacP2 and eacRcross the input should be
+ * 3 x nframes long, where each triplet is taken as a 3D vector
+ *
+ * For mode eacCos inputdata must be in radians, not degrees!
+ *
+ * Other parameters are:
+ *
+ * \param[in] fn is output filename (.xvg) where the correlation function(s) are printed
+ * \param[in] oenv controls output file properties
+ * \param[in] title is the title in the output file
+ * \param[in] nframes is the number of frames in the time series
+ * \param[in] nitem is the number of items
+ * \param[in] nout
+ * \param[inout] c1 is an array of dimension [ 0 .. nitem-1 ] [ 0 .. nframes-1 ]
+ *          on output, this array is filled with the correlation function
+ *          to reduce storage
+ * \param[in] dt is the time between frames
+ * \param[in] mode Different types of ACF can be done, see above
+ * \param[in] nrestart     is the number of steps between restarts for direct ACFs
+ *              (i.e. without FFT) When set to 1 all points are used as
+ *              time origin for averaging
+ * \param[in] bAver    If set, all ndih C(t) functions are averaged into a single
+ *          C(t)
+ * \param[in] bNormalize   If set, all ACFs will be normalized to start at 0
+ * \param[in] bVerbose If set output to console will be generated
+ * \param[in] tbeginfit Time to start fitting to the ACF
+ * \param[in] tendfit Time to end fitting to the ACF
+ * \param[in] nfitparm Number of fitting parameters in a multi-exponential fit
+ */
+void low_do_autocorr(const char*             fn,
+                     const gmx_output_env_t* oenv,
+                     const char*             title,
+                     int                     nframes,
+                     int                     nitem,
+                     int                     nout,
+                     real**                  c1,
+                     real                    dt,
+                     unsigned long           mode,
+                     int                     nrestart,
+                     gmx_bool                bAver,
+                     gmx_bool                bNormalize,
+                     gmx_bool                bVerbose,
+                     real                    tbeginfit,
+                     real                    tendfit,
+                     int                     nfitparm);
+
+#endif
diff --git a/src/include/gromacs/correlationfunctions/crosscorr.h b/src/include/gromacs/correlationfunctions/crosscorr.h
new file mode 100644 (file)
index 0000000..38f9fff
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal
+ * \file
+ * \brief
+ * Declares routine for computing a cross correlation between two data sets
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \inlibraryapi
+ * \ingroup module_correlationfunctions
+ */
+#ifndef GMX_CROSSCORR_H
+#define GMX_CROSSCORR_H
+
+#include "gromacs/utility/real.h"
+
+/*! \brief
+ * fft cross correlation algorithm.
+ * Computes corr = f (.) g
+ *
+ * \param[in] n number of data point
+ * \param[in] f First function
+ * \param[in] g Second function
+ * \param[out] corr The cross correlation
+ */
+void cross_corr(int n, real f[], real g[], real corr[]);
+
+/*! \brief
+ * fft cross correlation algorithm.
+ *
+ * Computes corr[n] = f[n][i] (.) g[n][i], that is for nFunc
+ * pairs of arrays n the cross correlation is computed in parallel
+ * using OpenMP.
+ *
+ * \param[in] nFunc nuber of function to crosscorrelate
+ * \param[in] nData number of data point in eatch function
+ * \param[in] f 2D array of first function to crosscorrelate
+ * \param[in] g 2D array of second function to crosscorrelate
+ * \param[out] corr 2D array of the cross correlations
+ */
+void many_cross_corr(int nFunc, int* nData, real** f, real** g, real** corr);
+
+#endif
diff --git a/src/include/gromacs/correlationfunctions/expfit.h b/src/include/gromacs/correlationfunctions/expfit.h
new file mode 100644 (file)
index 0000000..a17efa4
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal
+ * \file
+ * \brief
+ * Declares routine for fitting a data set to a curve
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \inlibraryapi
+ * \ingroup module_correlationfunctions
+ */
+#ifndef GMX_EXPFIT_H
+#define GMX_EXPFIT_H
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_output_env_t;
+
+/*! \brief
+ * Enum to select fitting functions
+ */
+enum
+{
+    effnNONE,
+    effnEXP1,
+    effnEXP2,
+    effnEXPEXP,
+    effnEXP5,
+    effnEXP7,
+    effnEXP9,
+    effnVAC,
+    effnERF,
+    effnERREST,
+    effnPRES,
+    effnNR
+};
+
+/*! \brief
+ * Short name of each function type.
+ * This is exported for now in order to use when
+ * calling parse_common_args.
+ */
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+extern const char* s_ffn[effnNR + 2];
+
+/*! \brief
+ * Returns  description corresponding to the enum above, or NULL if out of range
+ * \param[in] effn Index
+ * \return Description or NULL
+ */
+const char* effnDescription(int effn);
+
+/*! \brief
+ * Returns  number of function parameters associated with a fitting function.
+ * \param[in] effn Index
+ * \return number or -1 if index out of range
+ */
+int effnNparams(int effn);
+
+/*! \brief
+ * Returns  corresponding to the selected enum option in sffn
+ * \param[in] sffn Two dimensional string array coming from parse_common_args
+ * \return the ffn enum
+ */
+int sffn2effn(const char** sffn);
+
+/*! \brief
+ * Returns the value of fit function eFitFn at x
+ * \param[in] eFitFn the index to the fitting function (0 .. effnNR)
+ * \param[in] parm Array of parameters, the length of which depends on eFitFn
+ * \param[in] x The value of x
+ * \return the value of the fit
+ */
+double fit_function(int eFitFn, const double parm[], double x);
+
+/*! \brief
+ * Use Levenberg-Marquardt method to fit to a nfitparm parameter exponential
+ * or to a transverse current autocorrelation function.
+ *
+ * If x == NULL, the timestep dt will be used to create a time axis.
+ * \param[in] ndata Number of data points
+ * \param[in] c1 The data points
+ * \param[in] sig The standard deviation in the points (can be NULL)
+ * \param[in] dt The time step
+ * \param[in] x0 The X-axis (may be NULL, see above)
+ * \param[in] begintimefit Starting time for fitting
+ * \param[in] endtimefit Ending time for fitting
+ * \param[in] oenv Output formatting information
+ * \param[in] bVerbose Should the routine write to console?
+ * \param[in] eFitFn Fitting function (0 .. effnNR)
+ * \param[inout] fitparms[] Fitting parameters, see printed manual for a
+ * detailed description. Note that in all implemented cases the parameters
+ * corresponding to time constants will be generated with increasing values.
+ * Such input parameters should therefore be provided in increasing order.
+ * If this is not the case or if subsequent time parameters differ by less than
+ * a factor of 2, they will be modified to ensure tau_i+1 >= 2 tau_i.
+ * \param[in] fix Constrains fit parameter i at it's starting value, when the i'th bit
+ * of fix is set. This works only when the N last parameters are fixed
+ * but not when a parameter somewhere in the middle needs to be fixed.
+ * \param[in] fn_fitted If not NULL file to print the data and fitted curve to
+ * \return integral.
+ */
+real do_lmfit(int                     ndata,
+              const real              c1[],
+              real                    sig[],
+              real                    dt,
+              const real*             x0,
+              real                    begintimefit,
+              real                    endtimefit,
+              const gmx_output_env_t* oenv,
+              gmx_bool                bVerbose,
+              int                     eFitFn,
+              double                  fitparms[],
+              int                     fix,
+              const char*             fn_fitted);
+
+/*! \brief
+ * Fit an autocorrelation function to a pre-defined functional form
+ *
+ * \todo check parameters
+ * \param[in] ncorr
+ * \param[in] fitfn Fitting function (0 .. effnNR)
+ * \param[in] oenv Output formatting information
+ * \param[in] bVerbose Should the routine write to console?
+ * \param[in] tbeginfit Starting time for fitting
+ * \param[in] tendfit Ending time for fitting
+ * \param[in] dt The time step
+ * \param[in] c1 The data points
+ * \param[inout] fit The fitting parameters
+ * \return the integral over the autocorrelation function?
+ */
+real fit_acf(int                     ncorr,
+             int                     fitfn,
+             const gmx_output_env_t* oenv,
+             gmx_bool                bVerbose,
+             real                    tbeginfit,
+             real                    tendfit,
+             real                    dt,
+             real                    c1[],
+             real*                   fit);
+
+#endif
diff --git a/src/include/gromacs/correlationfunctions/gmx_lmcurve.h b/src/include/gromacs/correlationfunctions/gmx_lmcurve.h
new file mode 100644 (file)
index 0000000..2886479
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal
+ * \file
+ * \brief
+ * Declares a driver routine for lmfit.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \ingroup module_correlationfunctions
+ */
+#ifndef GMX_CORRELATION_FUNCTIONS_GMX_LMCURVE_H
+#define GMX_CORRELATION_FUNCTIONS_GMX_LMCURVE_H
+#include "gromacs/correlationfunctions/expfit.h"
+/*! \brief function type for passing to fitting routine */
+typedef double (*t_lmcurve)(double x, const double* a);
+/*! \brief lmfit_exp supports fitting of different functions
+ *
+ * This routine calls the Levenberg-Marquardt non-linear fitting
+ * routine for fitting a data set with errors to a target function.
+ * Fitting routines included in gromacs in src/external/lmfit.
+ */
+bool lmfit_exp(int          nfit,
+               const double x[],
+               const double y[],
+               const double dy[],
+               double       parm[],
+               bool         bVerbose,
+               int          eFitFn,
+               int          nfix);
+
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+extern t_lmcurve lmcurves[effnNR + 1];
+
+#endif
diff --git a/src/include/gromacs/correlationfunctions/integrate.h b/src/include/gromacs/correlationfunctions/integrate.h
new file mode 100644 (file)
index 0000000..9e2ec82
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal
+ * \file
+ * \brief
+ * Declares routines for integrating a data set
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \inlibraryapi
+ * \ingroup module_correlationfunctions
+ */
+#ifndef GMX_INTEGRATE_H
+#define GMX_INTEGRATE_H
+
+#include <stdio.h>
+
+#include "gromacs/utility/real.h"
+
+/*! \brief
+ * Integrate the equispaced data in c[] from 0 to n using trapezium rule.
+ * If fit != NULL the fit is written as well.
+ * \param[in] fp File pointer to write to (maybe NULL)
+ * \param[in] n Number of data points
+ * \param[in] dt The time step between data points
+ * \param[in] c The data set
+ * \param[in] fit Fit to the function that is printed too if not a NULL pointer is passed.
+ * \param[in] nskip Determines whether all elements are written to the output file
+ * (written when i % nskip == 0)
+ * \return The integral
+ */
+real print_and_integrate(FILE* fp, int n, real dt, const real c[], const real* fit, int nskip);
+
+/*! \brief
+ * Integrate data in y using the trapezium rule, and, if given, use dy as weighting
+ *
+ * \param[in] n The number of data points
+ * \param[in] x The x coordinate
+ * \param[in] y The y data (function values)
+ * \param[in] dy The uncertainties (can be NULL)
+ * \param[in] aver_start should be set to a value where the function has
+ * converged to 0.
+ * \param[out] stddev The standard deviation in the integral
+ * \return the integral
+ */
+real evaluate_integral(int n, const real x[], const real y[], const real dy[], real aver_start, real* stddev);
+
+#endif
diff --git a/src/include/gromacs/correlationfunctions/manyautocorrelation.h b/src/include/gromacs/correlationfunctions/manyautocorrelation.h
new file mode 100644 (file)
index 0000000..49f9796
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal
+ * \file
+ * \brief
+ * Declares routine for computing many correlation functions using OpenMP
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \inlibraryapi
+ * \ingroup module_correlationfunctions
+ */
+#ifndef GMX_MANYAUTOCORRELATION_H
+#define GMX_MANYAUTOCORRELATION_H
+
+#include <vector>
+
+#include "gromacs/fft/fft.h"
+#include "gromacs/utility/real.h"
+
+/*! \brief
+ * Perform many autocorrelation calculations.
+ *
+ * This routine performs many autocorrelation function calculations using FFTs.
+ * The GROMACS FFT library wrapper is employed. On return the c vector contain
+ * a symmetric function that is useful for further FFT:ing, for instance in order to
+ * compute spectra.
+ *
+ * The vectors c[i] should all have the same length, but this is not checked for.
+ *
+ * The c arrays will be extend and filled with zero beyond ndata before
+ * computing the correlation.
+ *
+ * The functions uses OpenMP parallellization.
+ *
+ * \param[inout] c Data array
+ * \return fft error code, or zero if everything went fine (see fft/fft.h)
+ * \throws gmx::InconsistentInputError if the input is inconsistent.
+ */
+int many_auto_correl(std::vector<std::vector<real>>* c);
+
+#endif
diff --git a/src/include/gromacs/correlationfunctions/polynomials.h b/src/include/gromacs/correlationfunctions/polynomials.h
new file mode 100644 (file)
index 0000000..b5a52e1
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2018, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal
+ * \file
+ * \brief
+ * Declares routine for computing a Legendre polynomial
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \inlibraryapi
+ * \ingroup module_correlationfunctions
+ */
+#ifndef GMX_POLYNOMIALS_H
+#define GMX_POLYNOMIALS_H
+
+#include "gromacs/utility/real.h"
+
+/*! \brief
+ * Return Legendre polynomial value Pm(x)
+ * http://en.wikipedia.org/wiki/Legendre_polynomials
+ * \param[in] x The value
+ * \param[in] m The order of the polynomial (0-4 are supported)
+ * \return Pm(x)
+ */
+real LegendreP(real x, unsigned int m);
+
+#endif
diff --git a/src/include/gromacs/correlationfunctions/tests/correlationdataset.h b/src/include/gromacs/correlationfunctions/tests/correlationdataset.h
new file mode 100644 (file)
index 0000000..86cacc0
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares helper class for autocorrelation tests
+ *
+ * \author Anders G&auml;rden&auml;s <anders.gardenas@gmail.com>
+ * \ingroup module_correlationfunctions
+ */
+#ifndef GMX_CORRELATIONDATASET_H
+#define GMX_CORRELATIONDATASET_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/real.h"
+
+class CorrelationDataSet
+{
+    double** tempValues_;
+
+    int    nrLines_;
+    int    nrColumns_;
+    double startTime_;
+    double endTime_;
+    double dt_;
+
+public:
+    /*! \brief
+     * Constructor
+     * \param[in] fileName containing function to test. *.xvg
+     */
+    explicit CorrelationDataSet(const std::string& fileName);
+
+    /*! \brief
+     * Return a value at an index
+     * \param[in] set the set number
+     * \param[in] t the time index of the value
+     */
+    real getValue(int set, int t) const;
+
+    /*! \brief
+     * Return the nummber of columns
+     */
+    int getNrColumns() const { return nrColumns_; }
+
+    /*! \brief
+     * Return the nummber of Lines
+     */
+    int getNrLines() const { return nrLines_; }
+
+    /*! \brief
+     * Return the time witch the function starts at
+     */
+    real getStartTime() const { return startTime_; }
+
+    /*! \brief
+     * Return the time the function ends at
+     */
+    real getEndTime() const { return endTime_; }
+
+    /*! \brief
+     * return delta time
+     */
+    real getDt() const { return dt_; }
+
+    /*! \brief
+     * Destructor
+     */
+    ~CorrelationDataSet();
+
+private:
+    //! This class should not be copyable or assignable
+    GMX_DISALLOW_COPY_AND_ASSIGN(CorrelationDataSet);
+};
+
+#endif
diff --git a/src/include/gromacs/domdec/atomdistribution.h b/src/include/gromacs/domdec/atomdistribution.h
new file mode 100644 (file)
index 0000000..604505d
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares the AtomDistribution struct.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+#ifndef GMX_DOMDEC_ATOMDISTRIBUTION_H
+#define GMX_DOMDEC_ATOMDISTRIBUTION_H
+
+#include <array>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+
+/*! \internal
+ * \brief Distribution of atom groups over the domain (only available on the master rank)
+ */
+struct AtomDistribution
+{
+    /*! \internal
+     * \brief Collection of local group and atom counts for a domain
+     */
+    struct DomainAtomGroups
+    {
+        gmx::ArrayRef<const int> atomGroups; /**< List of our atom groups */
+        int                      numAtoms;   /**< Our number of local atoms */
+    };
+
+    /*! \brief Constructor */
+    AtomDistribution(const ivec numCells, int numAtomGroups, int numAtoms);
+
+    std::vector<DomainAtomGroups> domainGroups; /**< Group and atom division over ranks/domains */
+    std::vector<int>              atomGroups; /**< The atom group division of the whole system, pointed into by counts[].atomGroups */
+
+    /* Temporary buffers, stored permanently here to avoid reallocation */
+    std::array<std::vector<real>, DIM> cellSizesBuffer; /**< Cell boundaries, sizes: num_cells_in_dim + 1 */
+    std::vector<int>       intBuffer;  /**< Buffer for communicating cg and atom counts */
+    std::vector<gmx::RVec> rvecBuffer; /**< Buffer for state scattering and gathering */
+};
+
+/*! \brief Returns state scatter/gather buffer element counts and displacements
+ *
+ * NOTE: Should only be called with a pointer to a valid ma struct
+ *       (only available on the master rank).
+ */
+void get_commbuffer_counts(AtomDistribution* ma, int** counts, int** disps);
+
+#endif
diff --git a/src/include/gromacs/domdec/box.h b/src/include/gromacs/domdec/box.h
new file mode 100644 (file)
index 0000000..7974b80
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file declares functions used by the domdec module
+ * for (bounding) box and pbc information generation.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_BOX_H
+#define GMX_DOMDEC_BOX_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/gmxmpi.h"
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+struct gmx_ddbox_t;
+struct gmx_domdec_t;
+struct t_commrec;
+struct t_inputrec;
+enum class DDRole;
+
+/*! \brief Set the box and PBC data in \p ddbox */
+void set_ddbox(const gmx_domdec_t&            dd,
+               bool                           masterRankHasTheSystemState,
+               const matrix                   box,
+               bool                           calculateUnboundedSize,
+               gmx::ArrayRef<const gmx::RVec> x,
+               gmx_ddbox_t*                   ddbox);
+
+/*! \brief Set the box and PBC data in \p ddbox */
+void set_ddbox_cr(DDRole                         ddRole,
+                  MPI_Comm                       communicator,
+                  const ivec*                    dd_nc,
+                  const t_inputrec&              ir,
+                  const matrix                   box,
+                  gmx::ArrayRef<const gmx::RVec> x,
+                  gmx_ddbox_t*                   ddbox);
+
+#endif
diff --git a/src/include/gromacs/domdec/cellsizes.h b/src/include/gromacs/domdec/cellsizes.h
new file mode 100644 (file)
index 0000000..794eb27
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares DD cell-size related functions.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_DOMDEC_CELLSIZES_H
+#define GMX_DOMDEC_DOMDEC_CELLSIZES_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/timing/wallcycle.h"
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+struct gmx_ddbox_t;
+struct gmx_domdec_comm_t;
+struct gmx_domdec_t;
+
+/*! \brief Options for setting up a regular, possibly static load balanced, cell grid geometry */
+enum
+{
+    setcellsizeslbLOCAL,     //!< Set cell sizes locally on each rank
+    setcellsizeslbMASTER,    //!< Set cell sizes on master rank only
+    setcellsizeslbPULSE_ONLY //!< Only set the communication pulses, not the cell sizes
+};
+
+/*! \brief Returns the minimum allowed distance between lower and upper bounds of zones along dimension dim_ind */
+real grid_jump_limit(const gmx_domdec_comm_t* comm, real cutoff, int dim_ind);
+
+/*! \brief Sets up an initial, non-staggered grid geometry, possibly using static load balancing
+ *
+ * The number of communication pulses per dimension is returned in numPulses.
+ * When setmode==setcellsizeslbMASTER, the cell boundaries per dimension are
+ * returned, otherwise an empty arrayref is returned.
+ */
+gmx::ArrayRef<const std::vector<real>>
+set_dd_cell_sizes_slb(gmx_domdec_t* dd, const gmx_ddbox_t* ddbox, int setmode, ivec numPulses);
+
+/*! \brief General cell size adjustment, possibly applying dynamic load balancing */
+void set_dd_cell_sizes(gmx_domdec_t*      dd,
+                       const gmx_ddbox_t* ddbox,
+                       gmx_bool           bDynamicBox,
+                       gmx_bool           bUniform,
+                       gmx_bool           bDoDLB,
+                       int64_t            step,
+                       gmx_wallcycle*     wcycle);
+
+#endif
diff --git a/src/include/gromacs/domdec/collect.h b/src/include/gromacs/domdec/collect.h
new file mode 100644 (file)
index 0000000..6ac84f6
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares functions to collect state data to the master rank.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_COLLECT_H
+#define GMX_DOMDEC_COLLECT_H
+
+#include "gromacs/math/vectypes.h"
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+struct gmx_domdec_t;
+class t_state;
+
+/*! \brief Gathers rvec arrays \p localVector to \p globalVector on the master rank */
+void dd_collect_vec(gmx_domdec_t*                  dd,
+                    int                            ddpCount,
+                    int                            ddpCountCgGl,
+                    gmx::ArrayRef<const int>       localCGNumbers,
+                    gmx::ArrayRef<const gmx::RVec> localVector,
+                    gmx::ArrayRef<gmx::RVec>       globalVector);
+
+/*! \brief Gathers state \p localState to \p globalState on the master rank */
+void dd_collect_state(gmx_domdec_t* dd, const t_state* localState, t_state* globalState);
+
+#endif
diff --git a/src/include/gromacs/domdec/computemultibodycutoffs.h b/src/include/gromacs/domdec/computemultibodycutoffs.h
new file mode 100644 (file)
index 0000000..ddc1447
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file declares the function for computing the required
+ * cutoff distance for inter-domain multi-body interactions, when
+ * those exist.
+ *
+ * \inlibraryapi
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_COMPUTEMULTIBODYCUTOFFS_H
+#define GMX_DOMDEC_COMPUTEMULTIBODYCUTOFFS_H
+
+#include "gromacs/math/vectypes.h"
+
+struct gmx_mtop_t;
+struct t_inputrec;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class MDLogger;
+enum class DDBondedChecking : bool;
+} // namespace gmx
+
+/*! \brief Calculate the maximum distance involved in 2-body and multi-body bonded interactions */
+void dd_bonded_cg_distance(const gmx::MDLogger&           mdlog,
+                           const gmx_mtop_t&              mtop,
+                           const t_inputrec&              ir,
+                           gmx::ArrayRef<const gmx::RVec> x,
+                           const matrix                   box,
+                           gmx::DDBondedChecking          ddBondedChecking,
+                           real*                          r_2b,
+                           real*                          r_mb);
+
+#endif
diff --git a/src/include/gromacs/domdec/distribute.h b/src/include/gromacs/domdec/distribute.h
new file mode 100644 (file)
index 0000000..8b865fd
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares the atom distribution function.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+#ifndef GMX_DOMDEC_DOMDEC_DISTRIBUTE_H
+#define GMX_DOMDEC_DOMDEC_DISTRIBUTE_H
+
+#include "gromacs/utility/basedefinitions.h"
+
+struct df_history_t;
+struct gmx_ddbox_t;
+struct gmx_domdec_t;
+struct gmx_mtop_t;
+struct t_block;
+class t_state;
+
+namespace gmx
+{
+class MDLogger;
+}
+
+/*! \brief Distributes the state from the master rank to all DD ranks */
+void distributeState(const gmx::MDLogger& mdlog,
+                     gmx_domdec_t*        dd,
+                     const gmx_mtop_t&    mtop,
+                     t_state*             state_global,
+                     const gmx_ddbox_t&   ddbox,
+                     t_state*             state_local);
+
+/*! \brief Distribute the dfhist struct from the master rank to all DD ranks
+ *
+ * Used by the modular simulator checkpointing
+ *
+ * \param dd  Domain decomposition information
+ * \param dfhist  Free energy history struct
+ */
+void dd_distribute_dfhist(gmx_domdec_t* dd, df_history_t* dfhist);
+
+#endif
diff --git a/src/include/gromacs/domdec/dlb.h b/src/include/gromacs/domdec/dlb.h
new file mode 100644 (file)
index 0000000..4769697
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file declares functions to interact with the dynamic load
+ * balancing machinery.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_DLB_H
+#define GMX_DOMDEC_DLB_H
+
+#include "gromacs/utility/real.h"
+
+struct gmx_domdec_t;
+struct t_commrec;
+
+
+/*! \brief We check if to turn on DLB at the first and every 100 DD partitionings.
+ * With large imbalance DLB will turn on at the first step, so we can
+ * make the interval so large that the MPI overhead of the check is negligible.
+ */
+constexpr int c_checkTurnDlbOnInterval = 100;
+/*! \brief We need to check if DLB results in worse performance and then turn it off.
+ * We check this more often then for turning DLB on, because the DLB can scale
+ * the domains very rapidly, so if unlucky the load imbalance can go up quickly
+ * and furthermore, we are already synchronizing often with DLB, so
+ * the overhead of the MPI Bcast is not that high.
+ */
+constexpr int c_checkTurnDlbOffInterval = 20;
+
+
+/*! \brief Return the PME/PP force load ratio, or -1 if nothing was measured.
+ *
+ * Should only be called on the DD master node.
+ */
+float dd_pme_f_ratio(const gmx_domdec_t* dd);
+
+//! Sets the cell size limits for DD to suit dynamic load balancing.
+void set_dlb_limits(gmx_domdec_t* dd);
+
+/*! \brief Limit DLB to preserve the option of returning to the current cut-off.
+ *
+ * Domain boundary changes due to the DD dynamic load balancing can limit
+ * the cut-off distance that can be set in change_dd_cutoff. This function
+ * sets/changes the DLB limit such that using the passed (pair-list) cut-off
+ * should still be possible after subsequently setting a shorter cut-off
+ * with change_dd_cutoff.
+ */
+void set_dd_dlb_max_cutoff(struct t_commrec* cr, real cutoff);
+
+/*! \brief Sets whether we should later check the load imbalance data, so that
+ * we can trigger dynamic load balancing if enough imbalance has
+ * arisen.
+ *
+ * Used after PME load balancing unlocks DLB, so that the check
+ * whether DLB will be useful can happen immediately.
+ */
+void dd_dlb_set_should_check_whether_to_turn_dlb_on(gmx_domdec_t* dd, bool bValue);
+
+/*! \brief Returns if we should check whether there has been enough
+ * load imbalance to trigger dynamic load balancing.
+ *
+ * We need to check whether we check because it might be always off.
+ */
+bool dd_dlb_get_should_check_whether_to_turn_dlb_on(gmx_domdec_t* dd);
+
+/*! \brief Return if we are currently using dynamic load balancing */
+bool dd_dlb_is_on(const gmx_domdec_t* dd);
+
+/*! \brief Return if the DLB lock is set */
+bool dd_dlb_is_locked(const gmx_domdec_t* dd);
+
+/*! \brief Set a lock such that with DLB=auto DLB cannot get turned on */
+void dd_dlb_lock(struct gmx_domdec_t* dd);
+
+/*! \brief Clear a lock such that with DLB=auto DLB may get turned on later */
+void dd_dlb_unlock(struct gmx_domdec_t* dd);
+
+#endif
diff --git a/src/include/gromacs/domdec/dlbtiming.h b/src/include/gromacs/domdec/dlbtiming.h
new file mode 100644 (file)
index 0000000..39b2aed
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief This file declares functions for timing the load imbalance due to domain decomposition.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_DLBTIMING_H
+#define GMX_DOMDEC_DLBTIMING_H
+
+#include "gromacs/mdtypes/commrec.h"
+
+struct BalanceRegion;
+struct gmx_domdec_t;
+struct t_nrnb;
+
+/*! \brief Tells if we should open the balancing region */
+enum class DdAllowBalanceRegionReopen
+{
+    no, //!< Do not allow opening an already open region
+    yes //!< Allow opening an already open region
+};
+
+/*! \brief Tells if we had to wait for a GPU to finish computation */
+enum class DdBalanceRegionWaitedForGpu
+{
+    no, //!< The GPU finished computation before the CPU needed the result
+    yes //!< We had to wait for the GPU to finish computation
+};
+
+/*! \brief Re-open the, already opened, load balance timing region
+ *
+ * This function should be called after every MPI communication that occurs
+ * in the main MD loop.
+ * Note that the current setup assumes that all MPI communication acts like
+ * a global barrier. But if some ranks don't participate in communication
+ * or if some ranks communicate faster with neighbors than others,
+ * the obtained timings might not accurately reflect the computation time.
+ *
+ * \param[in,out] dd  The domain decomposition struct
+ */
+void ddReopenBalanceRegionCpu(const gmx_domdec_t* dd);
+
+/*! \libinternal
+ * \brief Manager for starting and stopping the dynamic load balancing region
+ */
+class DDBalanceRegionHandler
+{
+public:
+    //! Constructor, pass a pointer to t_commrec or nullptr when not using domain decomposition
+    DDBalanceRegionHandler(const t_commrec* cr) :
+        useBalancingRegion_(cr != nullptr ? havePPDomainDecomposition(cr) : false),
+        dd_(cr != nullptr ? cr->dd : nullptr)
+    {
+    }
+
+    /*! \brief Returns whether were are actually using the balancing region
+     */
+    bool useBalancingRegion() const { return useBalancingRegion_; }
+
+    /*! \brief Open the load balance timing region on the CPU
+     *
+     * Opens the balancing region for timing how much time it takes to perform
+     * the (balancable part of) the MD step. This should be called right after
+     * the last communication during the previous step to maximize the region.
+     * In practice this means right after the force communication finished
+     * or just before neighbor search at search steps.
+     * It is assumed that computation done in the region either scales along
+     * with the domain size or takes constant time.
+     *
+     * \param[in] allowReopen  Allows calling with a potentially already opened region
+     */
+    void openBeforeForceComputationCpu(DdAllowBalanceRegionReopen allowReopen) const
+    {
+        if (useBalancingRegion_)
+        {
+            openRegionCpuImpl(allowReopen);
+        }
+    }
+
+    /*! \brief Open the load balance timing region for the CPU
+     *
+     * This can only be called within a region that is open on the CPU side.
+     */
+    void openBeforeForceComputationGpu() const
+    {
+        if (useBalancingRegion_)
+        {
+            openRegionGpuImpl();
+        }
+    }
+
+    /*! \brief Re-open the, already opened, load balance timing region
+     *
+     * This function should be called after every MPI communication that occurs
+     * in the main MD loop.
+     * Note that the current setup assumes that all MPI communication acts like
+     * a global barrier. But if some ranks don't participate in communication
+     * or if some ranks communicate faster with neighbors than others,
+     * the obtained timings might not accurately reflect the computation time.
+     */
+    void reopenRegionCpu() const
+    {
+        if (useBalancingRegion_)
+        {
+            ddReopenBalanceRegionCpu(dd_);
+        }
+    }
+
+    /*! \brief Close the load balance timing region on the CPU side
+     */
+    void closeAfterForceComputationCpu() const
+    {
+        if (useBalancingRegion_)
+        {
+            closeRegionCpuImpl();
+        }
+    }
+
+    /*! \brief Close the load balance timing region on the GPU side
+     *
+     * This should be called after the CPU receives the last (local) results
+     * from the GPU. The wait time for these results is estimated, depending
+     * on the \p waitedForGpu parameter.
+     * If called on an already closed region, this call does nothing.
+     *
+     * \param[in] waitCyclesGpuInCpuRegion  The time we waited for the GPU earlier, overlapping completely with the open CPU region
+     * \param[in] waitedForGpu              Tells if we waited for the GPU to finish now
+     */
+    void closeAfterForceComputationGpu(float                       waitCyclesGpuInCpuRegion,
+                                       DdBalanceRegionWaitedForGpu waitedForGpu) const
+    {
+        if (useBalancingRegion_)
+        {
+            closeRegionGpuImpl(waitCyclesGpuInCpuRegion, waitedForGpu);
+        }
+    }
+
+private:
+    /*! \brief Open the load balance timing region on the CPU
+     *
+     * \param[in] allowReopen  Allows calling with a potentially already opened region
+     */
+    void openRegionCpuImpl(DdAllowBalanceRegionReopen allowReopen) const;
+
+    /*! \brief Open the load balance timing region for the GPU
+     *
+     * This can only be called within a region that is open on the CPU side.
+     */
+    void openRegionGpuImpl() const;
+
+    /*! \brief Close the load balance timing region on the CPU side
+     */
+    void closeRegionCpuImpl() const;
+
+    /*! \brief Close the load balance timing region on the GPU side
+     *
+     * \param[in] waitCyclesGpuInCpuRegion  The time we waited for the GPU earlier, overlapping completely with the open CPU region
+     * \param[in] waitedForGpu              Tells if we waited for the GPU to finish now
+     */
+    void closeRegionGpuImpl(float waitCyclesGpuInCpuRegion, DdBalanceRegionWaitedForGpu waitedForGpu) const;
+
+    //! Tells whether the balancing region should be active
+    bool useBalancingRegion_;
+    //! A pointer to the DD struct, only valid with useBalancingRegion_=true
+    gmx_domdec_t* dd_;
+};
+
+/*! \brief Returns a pointer to a constructed \p BalanceRegion struct
+ *
+ * Should be replaced by a proper constructor once BalanceRegion is a proper
+ * class (requires restructering in domdec.cpp).
+ */
+BalanceRegion* ddBalanceRegionAllocate();
+
+/*! \brief Start the force flop count */
+void dd_force_flop_start(struct gmx_domdec_t* dd, t_nrnb* nrnb);
+
+/*! \brief Stop the force flop count */
+void dd_force_flop_stop(struct gmx_domdec_t* dd, t_nrnb* nrnb);
+
+//! Clear the cycle counts used for tuning.
+void clear_dd_cycle_counts(gmx_domdec_t* dd);
+
+#endif
diff --git a/src/include/gromacs/domdec/domdec.h b/src/include/gromacs/domdec/domdec.h
new file mode 100644 (file)
index 0000000..c7d7483
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2005 - 2014, The GROMACS development team.
+ * Copyright (c) 2015,2016,2017,2018,2019 by the GROMACS development team.
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \defgroup module_domdec Spatial domain decomposition (for parallelization over MPI)
+ * \ingroup group_mdrun
+ *
+ * \brief Manages the decomposition of the simulation volume over MPI
+ * ranks to try to distribute work evenly with minimal communication
+ * overheads.
+ *
+ * \todo Get domdec stuff out of mdtypes/commrec.h
+ *
+ * \author Berk Hess <hess@kth.se>
+ *
+ */
+
+/*! \libinternal \file
+ *
+ * \brief This file declares functions for mdrun to call to manage the
+ * details of its domain decomposition.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_DOMDEC_H
+#define GMX_DOMDEC_DOMDEC_H
+
+#include <vector>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_domdec_t;
+struct gmx_ddbox_t;
+struct gmx_domdec_zones_t;
+struct gmx_localtop_t;
+struct gmx_mtop_t;
+struct t_commrec;
+struct t_forcerec;
+struct t_inputrec;
+struct t_mdatoms;
+struct t_nrnb;
+struct gmx_wallcycle;
+enum class PbcType : int;
+class t_state;
+class DeviceContext;
+class GpuEventSynchronizer;
+
+namespace gmx
+{
+struct AtomInfoWithinMoleculeBlock;
+class DeviceStreamManager;
+class ForceWithShiftForces;
+class MDLogger;
+class RangePartitioning;
+class VirtualSitesHandler;
+template<typename>
+class ArrayRef;
+template<typename, size_t>
+class FixedCapacityVector;
+} // namespace gmx
+
+/*! \brief Returns the global topology atom number belonging to local atom index i.
+ *
+ * This function is intended for writing ASCII output
+ * and returns atom numbers starting at 1.
+ * When dd=NULL returns i+1.
+ */
+int ddglatnr(const gmx_domdec_t* dd, int i);
+
+/*! \brief Store the global cg indices of the home cgs in state,
+ *
+ * This means it can be reset, even after a new DD partitioning.
+ */
+void dd_store_state(const gmx_domdec_t& dd, t_state* state);
+
+/*! \brief Returns a pointer to the gmx_domdec_zones_t struct */
+struct gmx_domdec_zones_t* domdec_zones(struct gmx_domdec_t* dd);
+
+/*! \brief Returns the range for atoms in zones*/
+int dd_numAtomsZones(const gmx_domdec_t& dd);
+
+/*! \brief Returns the number of home atoms */
+int dd_numHomeAtoms(const gmx_domdec_t& dd);
+
+/*! \brief Returns the atom range in the local state for atoms that need to be present in mdatoms */
+int dd_natoms_mdatoms(const gmx_domdec_t& dd);
+
+/*! \brief Returns the atom range in the local state for atoms involved in virtual sites */
+int dd_natoms_vsite(const gmx_domdec_t& dd);
+
+/*! \brief Sets the atom range for atom in the local state for atoms received in constraints communication */
+void dd_get_constraint_range(const gmx_domdec_t& dd, int* at_start, int* at_end);
+
+/*! \libinternal \brief Struct for passing around the number of PME domains */
+struct NumPmeDomains
+{
+    int x; //!< The number of PME domains along dimension x
+    int y; //!< The number of PME domains along dimension y
+};
+
+/*! \brief Returns the number of PME domains, can be called with dd=NULL */
+NumPmeDomains getNumPmeDomains(const gmx_domdec_t* dd);
+
+/*! \brief Returns the set of DD ranks that communicate with pme node cr->nodeid */
+std::vector<int> get_pme_ddranks(const t_commrec* cr, int pmenodeid);
+
+/*! \brief Returns the maximum shift for coordinate communication in PME, dim x */
+int dd_pme_maxshift_x(const gmx_domdec_t& dd);
+
+/*! \brief Returns the maximum shift for coordinate communication in PME, dim y */
+int dd_pme_maxshift_y(const gmx_domdec_t& dd);
+
+/*! \brief Return whether update groups are used */
+bool ddUsesUpdateGroups(const gmx_domdec_t& dd);
+
+/*! \brief Returns whether molecules are always whole, i.e. not broken by PBC */
+bool dd_moleculesAreAlwaysWhole(const gmx_domdec_t& dd);
+
+/*! \brief Returns if we need to do pbc for calculating bonded interactions */
+bool dd_bonded_molpbc(const gmx_domdec_t& dd, PbcType pbcType);
+
+/*! \brief Change the DD non-bonded communication cut-off.
+ *
+ * This could fail when trying to increase the cut-off,
+ * then FALSE will be returned and the cut-off is not modified.
+ *
+ * \param[in] cr               Communication recrod
+ * \param[in] box              Box matrix, used for computing the dimensions of the system
+ * \param[in] x                Position vector, used for computing the dimensions of the system
+ * \param[in] cutoffRequested  The requested atom to atom cut-off distance, usually the pair-list cutoff distance
+ */
+bool change_dd_cutoff(t_commrec* cr, const matrix box, gmx::ArrayRef<const gmx::RVec> x, real cutoffRequested);
+
+/*! \brief Set up communication for averaging GPU wait times over domains
+ *
+ * When domains (PP MPI ranks) share a GPU, the individual GPU wait times
+ * are meaningless, as it depends on the order in which tasks on the same
+ * GPU finish. Therefore there wait times need to be averaged over the ranks
+ * sharing the same GPU. This function sets up the communication for that.
+ */
+void dd_setup_dlb_resource_sharing(const t_commrec* cr, int gpu_id);
+
+/*! \brief Cycle counter indices used internally in the domain decomposition */
+enum
+{
+    ddCyclStep,
+    ddCyclPPduringPME,
+    ddCyclF,
+    ddCyclWaitGPU,
+    ddCyclPME,
+    ddCyclNr
+};
+
+/*! \brief Add the wallcycle count to the DD counter */
+void dd_cycles_add(const gmx_domdec_t* dd, float cycles, int ddCycl);
+
+/*! \brief Communicate the coordinates to the neighboring cells and do pbc. */
+void dd_move_x(struct gmx_domdec_t* dd, const matrix box, gmx::ArrayRef<gmx::RVec> x, gmx_wallcycle* wcycle);
+
+/*! \brief Sum the forces over the neighboring cells.
+ *
+ * When fshift!=NULL the shift forces are updated to obtain
+ * the correct virial from the single sum including f.
+ */
+void dd_move_f(struct gmx_domdec_t* dd, gmx::ForceWithShiftForces* forceWithShiftForces, gmx_wallcycle* wcycle);
+
+/*! \brief Communicate a real for each atom to the neighboring cells. */
+void dd_atom_spread_real(struct gmx_domdec_t* dd, real v[]);
+
+/*! \brief Sum the contributions to a real for each atom over the neighboring cells. */
+void dd_atom_sum_real(struct gmx_domdec_t* dd, real v[]);
+
+/*! \brief Reset all the statistics and counters for total run counting */
+void reset_dd_statistics_counters(struct gmx_domdec_t* dd);
+
+/* In domdec_con.c */
+
+/*! \brief Communicates the virtual site forces, reduces the shift forces when \p fshift != NULL */
+void dd_move_f_vsites(const gmx_domdec_t& dd, gmx::ArrayRef<gmx::RVec> f, gmx::ArrayRef<gmx::RVec> fshift);
+
+/*! \brief Clears the forces for virtual sites */
+void dd_clear_f_vsites(const gmx_domdec_t& dd, gmx::ArrayRef<gmx::RVec> f);
+
+/*! \brief Move x0 and also x1 if x1!=NULL. bX1IsCoord tells if to do PBC on x1 */
+void dd_move_x_constraints(struct gmx_domdec_t*     dd,
+                           const matrix             box,
+                           gmx::ArrayRef<gmx::RVec> x0,
+                           gmx::ArrayRef<gmx::RVec> x1,
+                           bool                     bX1IsCoord);
+
+/*! \brief Communicates the coordinates involved in virtual sites */
+void dd_move_x_vsites(const gmx_domdec_t& dd, const matrix box, rvec* x);
+/*! \brief Communicates the positions and velocities involved in virtual sites */
+void dd_move_x_and_v_vsites(const gmx_domdec_t& dd, const matrix box, rvec* x, rvec* v);
+
+/*! \brief Returns the local atom count array for all constraints
+ *
+ * The local atom count for a constraint, possible values 2/1/0, is needed
+ * to avoid not/double-counting contributions linked to the Lagrange
+ * multiplier, such as the virial and free-energy derivatives.
+ *
+ * \note When \p dd = nullptr, an empty reference is returned.
+ */
+gmx::ArrayRef<const int> dd_constraints_nlocalatoms(const gmx_domdec_t* dd);
+
+/*! \brief Construct local state */
+void dd_init_local_state(const gmx_domdec_t& dd, const t_state* state_global, t_state* local_state);
+
+/*! \brief Construct the GPU halo exchange object(s).
+ *
+ * \param[in] mdlog               The logger object.
+ * \param[in] cr                  The commrec object.
+ * \param[in] deviceStreamManager Manager of the GPU context and streams.
+ * \param[in] wcycle              The wallclock counter.
+ */
+void constructGpuHaloExchange(const gmx::MDLogger&            mdlog,
+                              const t_commrec&                cr,
+                              const gmx::DeviceStreamManager& deviceStreamManager,
+                              gmx_wallcycle*                  wcycle);
+
+/*! \brief
+ * (Re-) Initialization for GPU halo exchange
+ * \param [in] cr                   The commrec object
+ * \param [in] d_coordinatesBuffer  pointer to coordinates buffer in GPU memory
+ * \param [in] d_forcesBuffer       pointer to forces buffer in GPU memory
+ */
+void reinitGpuHaloExchange(const t_commrec&        cr,
+                           DeviceBuffer<gmx::RVec> d_coordinatesBuffer,
+                           DeviceBuffer<gmx::RVec> d_forcesBuffer);
+
+
+/*! \brief GPU halo exchange of coordinates buffer.
+ * \param [in] cr                The commrec object
+ * \param [in] box               Coordinate box (from which shifts will be constructed)
+ * \param [in] dependencyEvent   Dependency event for this operation
+ * \returns                      Event recorded when this operation has been launched
+ */
+GpuEventSynchronizer* communicateGpuHaloCoordinates(const t_commrec&      cr,
+                                                    const matrix          box,
+                                                    GpuEventSynchronizer* dependencyEvent);
+
+/*! \brief  Wait for copy of nonlocal part of coordinate array from GPU to CPU
+ * following coordinate halo exchange
+ * \param [in] cr   The commrec object
+ * \param [in] accumulateForces  True if forces should accumulate, otherwise they are set
+ * \param [in] dependencyEvents  Dependency events for this operation
+ */
+void communicateGpuHaloForces(const t_commrec&                                    cr,
+                              bool                                                accumulateForces,
+                              gmx::FixedCapacityVector<GpuEventSynchronizer*, 2>* dependencyEvents);
+
+/*! \brief Wraps the \c positions so that atoms from the same
+ * update group share the same periodic image wrt \c box.
+ *
+ * When DD and update groups are in use, the simulation master rank
+ * should call this to ensure that e.g. when restarting a simulation
+ * that did not use update groups that the coordinates satisfy the new
+ * requirements.
+ *
+ * This function can probably be removed when even single-rank
+ * simulations use domain decomposition, because then the choice of
+ * whether update groups are used is probably going to be the same
+ * regardless of the rank count.
+ *
+ * \param[in]    dd         The DD manager
+ * \param[in]    mtop       The system topology
+ * \param[in]    box        The global system box
+ * \param[in]    positions  The global system positions
+ */
+void putUpdateGroupAtomsInSamePeriodicImage(const gmx_domdec_t&      dd,
+                                            const gmx_mtop_t&        mtop,
+                                            const matrix             box,
+                                            gmx::ArrayRef<gmx::RVec> positions);
+
+#endif
diff --git a/src/include/gromacs/domdec/domdec_constraints.h b/src/include/gromacs/domdec/domdec_constraints.h
new file mode 100644 (file)
index 0000000..b2e366d
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2005,2006,2007,2008,2009 by the GROMACS development team.
+ * Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file declares functions for domdec to use
+ * while managing inter-atomic constraints.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_DOMDEC_CONSTRAINTS_H
+#define GMX_DOMDEC_DOMDEC_CONSTRAINTS_H
+
+#include "gromacs/utility/arrayref.h"
+
+namespace gmx
+{
+class Constraints;
+}
+
+struct gmx_domdec_t;
+struct gmx_mtop_t;
+struct InteractionList;
+
+/*! \brief Clears the local indices for the constraint communication setup */
+void dd_clear_local_constraint_indices(gmx_domdec_t* dd);
+
+/*! \brief Sets up communication and atom indices for all local+connected constraints */
+int dd_make_local_constraints(struct gmx_domdec_t*           dd,
+                              int                            at_start,
+                              const struct gmx_mtop_t&       mtop,
+                              gmx::ArrayRef<const int64_t>   atomInfo,
+                              gmx::Constraints*              constr,
+                              int                            nrec,
+                              gmx::ArrayRef<InteractionList> il_local);
+
+/*! \brief Initializes the data structures for constraint communication */
+void init_domdec_constraints(gmx_domdec_t* dd, const gmx_mtop_t& mtop);
+
+#endif
diff --git a/src/include/gromacs/domdec/domdec_internal.h b/src/include/gromacs/domdec/domdec_internal.h
new file mode 100644 (file)
index 0000000..d82bafa
--- /dev/null
@@ -0,0 +1,814 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares implementation functions and types for the domain
+ * decomposition module.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+#ifndef GMX_DOMDEC_DOMDEC_INTERNAL_H
+#define GMX_DOMDEC_DOMDEC_INTERNAL_H
+
+#include "config.h"
+
+#include "gromacs/domdec/domdec.h"
+#include "gromacs/domdec/domdec_struct.h"
+#include "gromacs/mdlib/updategroupscog.h"
+#include "gromacs/timing/cyclecounter.h"
+#include "gromacs/topology/block.h"
+
+struct t_commrec;
+
+/*! \cond INTERNAL */
+
+#define DD_NLOAD_MAX 9
+
+struct BalanceRegion;
+
+namespace gmx
+{
+enum class DdRankOrder : int;
+}
+// namespace
+
+
+//! Indices to communicate in a dimension
+struct gmx_domdec_ind_t
+{
+    //! @{
+    /*! \brief The numbers of charge groups to send and receive for each
+     * cell that requires communication, the last entry contains the total
+     * number of atoms that needs to be communicated.
+     */
+    int nsend[DD_MAXIZONE + 2] = {};
+    int nrecv[DD_MAXIZONE + 2] = {};
+    //! @}
+    //! The charge groups to send
+    std::vector<int> index;
+    //! @{
+    /* The atom range for non-in-place communication */
+    int cell2at0[DD_MAXIZONE] = {};
+    int cell2at1[DD_MAXIZONE] = {};
+    //! @}
+};
+
+//! Things relating to index communication
+struct gmx_domdec_comm_dim_t
+{
+    /* Returns the number of grid pulses (the number of domains in the halo along this dimension) */
+    int numPulses() const { return ind.size(); }
+
+    /**< For dlb, for use with edlbAUTO          */
+    int np_dlb = 0;
+    /**< The indices to communicate, size np     */
+    std::vector<gmx_domdec_ind_t> ind;
+    /**< Can we receive data in place?            */
+    bool receiveInPlace = false;
+};
+
+/*! \brief Load balancing data along a dim used on the master rank of that dim */
+struct RowMaster
+{
+    struct Bounds
+    {
+        /**< State var.: max lower bound., incl. neighbors */
+        real cellFracLowerMax = 0;
+        /**< State var.: min upper bound., incl. neighbors */
+        real cellFracUpperMin = 0;
+        /**< Temp. var.: lower limit for cell boundary */
+        real boundMin = 0;
+        /**< Temp. var.: upper limit for cell boundary */
+        real boundMax = 0;
+    };
+
+    /**< Temp. var.: is this cell size at the limit */
+    std::vector<bool> isCellMin;
+    /**< State var.: cell boundaries, box relative */
+    std::vector<real> cellFrac;
+    /**< Temp. var.: old cell size */
+    std::vector<real> oldCellFrac;
+    /**< Cell bounds */
+    std::vector<Bounds> bounds;
+    /**< State var.: is DLB limited in this row */
+    bool dlbIsLimited = false;
+    /**< Temp. var.  */
+    std::vector<real> buf_ncd;
+};
+
+/*! \brief Struct for managing cell sizes with DLB along a dimension */
+struct DDCellsizesWithDlb
+{
+    /**< Cell row root struct, only available on the first rank in a row */
+    std::unique_ptr<RowMaster> rowMaster;
+    /**< The cell sizes, in fractions, along a row, not available on the first rank in a row */
+    std::vector<real> fracRow;
+    /**< The lower corner, in fractions, in triclinic space */
+    real fracLower = 0;
+    /**< The upper corner, in fractions, in triclinic space */
+    real fracUpper = 0;
+    /**< The maximum lower corner among all our neighbors */
+    real fracLowerMax = 0;
+    /**< The minimum upper corner among all our neighbors */
+    real fracUpperMin = 0;
+};
+
+/*! \brief Struct for compute load commuication
+ *
+ * Here floats are accurate enough, since these variables
+ * only influence the load balancing, not the actual MD results.
+ */
+typedef struct domdec_load
+{
+    /**< The number of load recordings */
+    int nload = 0;
+    /**< Scan of the sum of load over dimensions */
+    float* load = nullptr;
+    /**< The sum of the load over the ranks up to our current dimension */
+    float sum = 0;
+    /**< The maximum over the ranks contributing to \p sum */
+    float max = 0;
+    /**< Like \p sum, but takes the maximum when the load balancing is limited */
+    float sum_m = 0;
+    /**< Minimum cell volume, relative to the box */
+    float cvol_min = 0;
+    /**< The PP time during which PME can overlap */
+    float mdf = 0;
+    /**< The PME-only rank load */
+    float pme = 0;
+    /**< Bit flags that tell if DLB was limited, per dimension */
+    int flags = 0;
+} domdec_load_t;
+
+/*! \brief Data needed to sort an atom to the desired location in the local state */
+typedef struct gmx_cgsort
+{
+    /**< Neighborsearch grid cell index */
+    int nsc = 0;
+    /**< Global atom/charge group index */
+    int ind_gl = 0;
+    /**< Local atom/charge group index */
+    int ind = 0;
+} gmx_cgsort_t;
+
+/*! \brief Temporary buffers for sorting atoms */
+typedef struct gmx_domdec_sort
+{
+    /**< Sorted array of indices */
+    std::vector<gmx_cgsort_t> sorted;
+    /**< Array of stationary atom/charge group indices */
+    std::vector<gmx_cgsort_t> stationary;
+    /**< Array of moved atom/charge group indices */
+    std::vector<gmx_cgsort_t> moved;
+    /**< Integer buffer for sorting */
+    std::vector<int> intBuffer;
+    /**< Int64 buffer for sorting */
+    std::vector<int64_t> int64Buffer;
+} gmx_domdec_sort_t;
+
+/*! \brief Manages atom ranges and order for the local state atom vectors */
+class DDAtomRanges
+{
+public:
+    /*! \brief The local state atom order
+     *
+     * This enum determines the order of the atoms in the local state.
+     * ddnatHOME and ddnatZONE should be first and second,
+     * the others can be ordered as wanted.
+     */
+    enum class Type : int
+    {
+        Home,        /**< The home atoms */
+        Zones,       /**< All zones in the eighth shell */
+        Vsites,      /**< Atoms communicated for virtual sites */
+        Constraints, /**< Atoms communicated for constraints */
+        Number       /**< Not a count, only present for convenience */
+    };
+
+    /*! \brief Returns the start atom index for range \p rangeType */
+    int start(Type rangeType) const
+    {
+        if (rangeType == Type::Home)
+        {
+            return 0;
+        }
+        else
+        {
+            return end_[static_cast<int>(rangeType) - 1];
+        }
+    }
+
+    /*! \brief Returns the end atom index for range \p rangeType */
+    int end(Type rangeType) const { return end_[static_cast<int>(rangeType)]; }
+
+    /*! \brief Returns the number of home atoms */
+    int numHomeAtoms() const { return end_[static_cast<int>(Type::Home)]; }
+
+    /*! \brief Returns the total number of atoms */
+    int numAtomsTotal() const { return end_[static_cast<int>(Type::Number) - 1]; }
+
+    /*! \brief Sets the end index of range \p rangeType to \p end
+     *
+     * This should be called either with Type::Home or with a type
+     * that is larger than that passed in the previous call to setEnd.
+     * A release assertion for these conditions are present.
+     */
+    void setEnd(Type rangeType, int end)
+    {
+        GMX_RELEASE_ASSERT(rangeType == Type::Home || rangeType > lastTypeSet_,
+                           "Can only set either home or a larger type than the last one");
+
+        for (int i = static_cast<int>(rangeType); i < static_cast<int>(Type::Number); i++)
+        {
+            end_[i] = end;
+        }
+
+        lastTypeSet_ = rangeType;
+    }
+
+private:
+    /*! \brief The list of end atom indices */
+    std::array<int, static_cast<int>(Type::Number)> end_;
+    Type                                            lastTypeSet_ = Type::Number;
+};
+
+/*! \brief Enum of dynamic load balancing states
+ *
+ * Allowed DLB states and transitions
+ * - intialization at startup:
+ *                             -> offUser ("-dlb no")
+ *                             -> onUser  ("-dlb yes")
+ *                             -> offCanTurnOn ("-dlb auto")
+ *
+ * - in automatic mode (i.e. initial state offCanTurnOn):
+ *   offCanTurnOn         -> onCanTurnOff
+ *   offCanTurnOn         -> offForever
+ *   offCanTurnOn         -> offTemporarilyLocked
+ *   offTemporarilyLocked -> offCanTurnOn
+ *   onCanTurnOff         -> offCanTurnOn
+ */
+enum class DlbState
+{
+    offUser,    /**< DLB is permanently off per user request */
+    offForever, /**< DLB is off due to a runtime condition (not supported or causes performance loss) and will never be turned on */
+    offCanTurnOn,         /**< DLB is off and will turn on on imbalance */
+    offTemporarilyLocked, /**< DLB is off and temporarily can't turn on */
+    onCanTurnOff,         /**< DLB is on and can turn off when slow */
+    onUser,               /**< DLB is permanently on per user request */
+    Count                 /**< The number of DLB states */
+};
+
+/*! \brief The PME domain decomposition for one dimension */
+typedef struct gmx_ddpme
+{
+    /**< The dimension */
+    int dim = 0;
+    /**< Tells if DD and PME dims match */
+    gmx_bool dim_match = false;
+    /**< The number of PME ranks/domains in this dimension */
+    int nslab = 0;
+    /**< Cell sizes for determining the PME comm. with SLB */
+    real* slb_dim_f = nullptr;
+    /**< The minimum pp node location, size nslab */
+    int* pp_min = nullptr;
+    /**< The maximum pp node location, size nslab */
+    int* pp_max = nullptr;
+    /**< The maximum shift for coordinate redistribution in PME */
+    int maxshift = 0;
+} gmx_ddpme_t;
+
+struct gmx_ddzone_t
+{
+    /**< The minimum bottom of this zone                        */
+    real min0 = 0;
+    /**< The maximum top of this zone                           */
+    real max1 = 0;
+    /**< The minimum top of this zone                           */
+    real min1 = 0;
+    /**< The maximum bottom communicaton height for this zone   */
+    real mch0 = 0;
+    /**< The maximum top communicaton height for this zone      */
+    real mch1 = 0;
+    /**< The bottom value of the first cell in this zone        */
+    real p1_0 = 0;
+    /**< The top value of the first cell in this zone           */
+    real p1_1 = 0;
+    /**< Bool disguised as a real, 1 when the above data has been set. 0 otherwise */
+    real dataSet = 0;
+};
+
+/*! \brief The number of reals in gmx_ddzone_t */
+constexpr int c_ddzoneNumReals = 8;
+
+template<typename T>
+class DDBufferAccess;
+
+/*! \brief Temporary storage container that minimizes (re)allocation and clearing
+ *
+ * This is only the storage, actual access happens through DDBufferAccess.
+ * All methods check if the buffer is (not) in use.
+ */
+template<typename T>
+class DDBuffer
+{
+private:
+    /*! \brief Returns a buffer of size \p numElements, the elements are undefined */
+    gmx::ArrayRef<T> resize(size_t numElements)
+    {
+        GMX_ASSERT(isInUse_, "Should only operate on acquired buffers");
+
+        if (numElements > buffer_.size())
+        {
+            buffer_.resize(numElements);
+        }
+
+        return gmx::arrayRefFromArray(buffer_.data(), numElements);
+    }
+
+    /*! \brief Acquire the buffer for use with size set to \p numElements, the elements are undefined */
+    gmx::ArrayRef<T> acquire(size_t numElements)
+    {
+        GMX_RELEASE_ASSERT(!isInUse_, "Should only request free buffers");
+        isInUse_ = true;
+
+        return resize(numElements);
+    }
+
+    /*! \brief Releases the buffer, buffer_ should not be used after this */
+    void release()
+    {
+        GMX_RELEASE_ASSERT(isInUse_, "Should only release buffers in use");
+        isInUse_ = false;
+    }
+
+    std::vector<T> buffer_;          /**< The actual memory buffer */
+    bool           isInUse_ = false; /**< Flag that tells whether the buffer is in use */
+
+    friend class DDBufferAccess<T>;
+};
+
+/*! \brief Class that manages access to a temporary memory buffer */
+template<typename T>
+class DDBufferAccess
+{
+public:
+    /*! \brief Constructor, returns a buffer of size \p numElements, element values are undefined
+     *
+     * \note The actual memory buffer \p ddBuffer can not be used to
+     *       create other DDBufferAccess objects until the one created
+     *       here is destroyed.
+     */
+    DDBufferAccess(DDBuffer<T>& ddBuffer, size_t numElements) : ddBuffer_(ddBuffer)
+    {
+        buffer = ddBuffer_.acquire(numElements);
+    }
+
+    ~DDBufferAccess() { ddBuffer_.release(); }
+
+    /*! \brief Resizes the buffer to \p numElements, new elements are undefined
+     *
+     * \note The buffer arrayref is updated after this call.
+     */
+    void resize(size_t numElements) { buffer = ddBuffer_.resize(numElements); }
+
+private:
+    DDBuffer<T>& ddBuffer_; /**< Reference to the storage class */
+public:
+    gmx::ArrayRef<T> buffer; /**< The access to the memory buffer */
+};
+
+/*! \brief Temporary buffer for setting up communiation over one pulse and all zones in the halo */
+struct dd_comm_setup_work_t
+{
+    /**< The local atom group indices to send */
+    std::vector<int> localAtomGroupBuffer;
+    /**< Buffer for collecting the global atom group indices to send */
+    std::vector<int> atomGroupBuffer;
+    /**< Buffer for collecting the atom group positions to send */
+    std::vector<gmx::RVec> positionBuffer;
+    /**< The number of atoms contained in the atom groups to send */
+    int nat = 0;
+    /**< The number of atom groups to send for the last zone */
+    int nsend_zone = 0;
+};
+
+/*! \brief Information about the simulated system */
+struct DDSystemInfo
+{
+    //! True when update groups are used
+    bool useUpdateGroups = false;
+    //! Update atom grouping for each molecule type
+    gmx::ArrayRef<const gmx::RangePartitioning> updateGroupingsPerMoleculeType;
+    //! The maximum radius over all update groups
+    real maxUpdateGroupRadius;
+
+    //! Are molecules always whole, i.e. not broken by PBC?
+    bool moleculesAreAlwaysWhole = false;
+    //! Are there inter-domain bonded interactions?
+    bool haveInterDomainBondeds = false;
+    //! Are there inter-domain multi-body interactions?
+    bool haveInterDomainMultiBodyBondeds = false;
+
+    //! Cut-off for multi-body interactions
+    real minCutoffForMultiBody = 0;
+    //! Cut-off for non-bonded/2-body interactions
+    real cutoff = 0;
+    //! The lower limit for the DD cell size
+    real cellsizeLimit = 0;
+
+    //! Can atoms connected by constraints be assigned to different domains?
+    bool mayHaveSplitConstraints = false;
+    //! Can atoms connected by settles be assigned to different domains?
+    bool mayHaveSplitSettles = false;
+    //! Estimated communication range needed for constraints
+    real constraintCommunicationRange = 0;
+
+    //! Whether to only communicate atoms beyond the non-bonded cut-off when they are involved in bonded interactions with non-local atoms
+    bool filterBondedCommunication = false;
+    //! Whether to increase the multi-body cut-off beyond the minimum required
+    bool increaseMultiBodyCutoff = false;
+};
+
+/*! \brief Settings that affect the behavior of the domain decomposition
+ *
+ * These settings depend on options chosen by the user, set by enviroment
+ * variables, as well as hardware support. The initial DLB state also
+ * depends on the integrator.
+ *
+ * Note: Settings that depend on the simulated system are in DDSystemInfo.
+ */
+struct DDSettings
+{
+    //! Use MPI_Sendrecv communication instead of non-blocking calls
+    bool useSendRecv2 = false;
+
+    /* Information for managing the dynamic load balancing */
+    //! Maximum DLB scaling per load balancing step in percent
+    int dlb_scale_lim = 0;
+    //! Flop counter (0=no,1=yes,2=with (eFlop-1)*5% noise
+    int eFlop = 0;
+
+    //! Whether to order the DD dimensions from z to x
+    bool useDDOrderZYX = false;
+
+    //! Whether to use MPI Cartesian reordering of communicators, when supported (almost never)
+    bool useCartesianReorder = true;
+
+    //! Whether we should record the load
+    bool recordLoad = false;
+
+    /* Debugging */
+    //! Step interval for dumping the local+non-local atoms to pdb
+    int nstDDDump = 0;
+    //! Step interval for duming the DD grid to pdb
+    int nstDDDumpGrid = 0;
+    //! DD debug print level: 0, 1, 2
+    int DD_debug = 0;
+
+    //! The DLB state at the start of the run
+    DlbState initialDlbState = DlbState::offCanTurnOn;
+};
+
+/*! \brief Information on how the DD ranks are set up */
+struct DDRankSetup
+{
+    /**< The rank ordering */
+    gmx::DdRankOrder rankOrder;
+
+    /**< The number of particle-particle (non PME-only) ranks */
+    int numPPRanks = 0;
+    /**< The DD PP grid */
+    ivec numPPCells = { 0, 0, 0 };
+
+    /* PME and Cartesian communicator stuff */
+    bool usePmeOnlyRanks = false;
+    /**< The number of decomposition dimensions for PME, 0: no PME */
+    int npmedecompdim = 0;
+    /**< The number of ranks doing PME (PP/PME or only PME) */
+    int numRanksDoingPme = 0;
+    /**< The number of PME ranks/domains along x */
+    int npmenodes_x = 0;
+    /**< The number of PME ranks/domains along y */
+    int npmenodes_y = 0;
+    /**< The 1D or 2D PME domain decomposition setup */
+    gmx_ddpme_t ddpme[2];
+};
+
+/*! \brief Information on Cartesian MPI setup of the DD ranks */
+struct CartesianRankSetup
+{
+    /**< Use Cartesian communication between PP and PME ranks */
+    bool bCartesianPP_PME = false;
+    /**< Cartesian grid for combinted PP+PME ranks */
+    ivec ntot = {};
+    /**< The number of dimensions for the PME setup that are Cartesian */
+    int cartpmedim = 0;
+    /**< The Cartesian index to sim rank conversion, used with bCartesianPP_PME */
+    std::vector<int> ddindex2simnodeid;
+
+    /* The DD particle-particle nodes only */
+    /**< Use a Cartesian communicator for PP */
+    bool bCartesianPP = false;
+    /**< The Cartesian index to DD rank conversion, used with bCartesianPP */
+    std::vector<int> ddindex2ddnodeid;
+};
+
+/*! \brief Struct for domain decomposition communication
+ *
+ * This struct contains most information about domain decomposition
+ * communication setup, some communication buffers, some statistics
+ * and also the setup for the communication between particle-particle
+ * and PME only ranks.
+ *
+ * All arrays are indexed with 0 to dd->ndim (not Cartesian indexing),
+ * unless stated otherwise.
+ */
+struct gmx_domdec_comm_t // NOLINT (clang-analyzer-optin.performance.Padding)
+{
+    /**< Constant parameters that control DD behavior */
+    DDSettings ddSettings;
+
+    /**< Information on how the DD ranks are set up */
+    DDRankSetup ddRankSetup;
+    /**< Information on the Cartesian part of the DD rank setup */
+    CartesianRankSetup cartesianRankSetup;
+
+    /* Charge group / atom sorting */
+    /**< Data structure for cg/atom sorting */
+    std::unique_ptr<gmx_domdec_sort_t> sort;
+
+    //! Centers of mass of local update groups
+    std::unique_ptr<gmx::UpdateGroupsCog> updateGroupsCog;
+
+    /* Data for the optional filtering of communication of atoms for bonded interactions */
+    /**< Links between atoms through bonded interactions */
+    t_blocka* bondedLinks = nullptr;
+
+    /* The DLB state, possible values are defined above */
+    DlbState dlbState;
+    /* With dlbState=DlbState::offCanTurnOn, should we check if to DLB on at the next DD? */
+    gmx_bool bCheckWhetherToTurnDlbOn = false;
+    /* The first DD count since we are running without DLB */
+    int ddPartioningCountFirstDlbOff = 0;
+
+    /* Cell sizes for static load balancing, first index cartesian */
+    real** slb_frac = nullptr;
+
+    /**< Information about the simulated system */
+    DDSystemInfo systemInfo;
+
+    /* The width of the communicated boundaries */
+    /**< Cut-off for multi-body interactions, also 2-body bonded when \p cutoff_mody > \p cutoff */
+    real cutoff_mbody = 0;
+    /**< The minimum guaranteed cell-size, Cartesian indexing */
+    gmx::RVec cellsize_min = { 0, 0, 0 };
+    /**< The minimum guaranteed cell-size with dlb=auto */
+    gmx::RVec cellsize_min_dlb = { 0, 0, 0 };
+    /**< The lower limit for the DD cell size with DLB */
+    real cellsize_limit = 0;
+    /**< Effectively no NB cut-off limit with DLB for systems without PBC? */
+    bool bVacDLBNoLimit = false;
+
+    /** With PME load balancing we set limits on DLB */
+    bool bPMELoadBalDLBLimits = false;
+    /** DLB needs to take into account that we want to allow this maximum
+     *  cut-off (for PME load balancing), this could limit cell boundaries.
+     */
+    real PMELoadBal_max_cutoff = 0;
+
+    /**< box lower corner, required with dim's without pbc and -gcom */
+    gmx::RVec box0 = { 0, 0, 0 };
+    /**< box size, required with dim's without pbc and -gcom */
+    gmx::RVec box_size = { 0, 0, 0 };
+
+    /**< The DD cell lower corner, in triclinic space */
+    gmx::RVec cell_x0 = { 0, 0, 0 };
+    /**< The DD cell upper corner, in triclinic space */
+    gmx::RVec cell_x1 = { 0, 0, 0 };
+
+    /**< The old \p cell_x0, to check cg displacements */
+    gmx::RVec old_cell_x0 = { 0, 0, 0 };
+    /**< The old \p cell_x1, to check cg displacements */
+    gmx::RVec old_cell_x1 = { 0, 0, 0 };
+
+    /** The communication setup and charge group boundaries for the zones */
+    gmx_domdec_zones_t zones;
+
+    /* The zone limits for DD dimensions 1 and 2 (not 0), determined from
+     * cell boundaries of neighboring cells for staggered grids when using
+     * dynamic load balancing.
+     */
+    /**< Zone limits for dim 1 with staggered grids */
+    std::array<gmx_ddzone_t, 2> zone_d1;
+    /**< Zone limits for dim 2 with staggered grids */
+    gmx_ddzone_t zone_d2[2][2];
+
+    /** The coordinate/force communication setup and indices */
+    std::array<gmx_domdec_comm_dim_t, DIM> cd;
+    /** Restricts the maximum number of cells to communicate with in one dimension
+     *
+     * Dynamic load balancing is not permitted to change sizes if it
+     * would violate this restriction. */
+    int maxpulse = 0;
+
+    /** Which cg distribution is stored on the master node,
+     *  stored as DD partitioning call count.
+     */
+    int64_t master_cg_ddp_count = 0;
+
+    /** The number of cg's received from the direct neighbors */
+    std::array<int, DD_MAXZONE> zone_ncg1 = { 0 };
+
+    /** The atom ranges in the local state */
+    DDAtomRanges atomRanges;
+
+    /** Array for signalling if atoms have moved to another domain */
+    std::vector<int> movedBuffer;
+
+    /** Communication int buffer for general use */
+    DDBuffer<int> intBuffer;
+
+    /** Communication rvec buffer for general use */
+    DDBuffer<gmx::RVec> rvecBuffer;
+
+    /* Temporary storage for thread parallel communication setup */
+    /**< Thread-local work data */
+    std::vector<dd_comm_setup_work_t> dth;
+
+    /* Communication buffer only used with multiple grid pulses */
+    /**< Another rvec comm. buffer */
+    DDBuffer<gmx::RVec> rvecBuffer2;
+
+    /* Communication buffers for local redistribution */
+    /**< Charge group flag comm. buffers */
+    std::array<std::vector<int>, DIM * 2> cggl_flag;
+    /**< Charge group center comm. buffers */
+    std::array<std::vector<gmx::RVec>, DIM * 2> cgcm_state;
+
+    /* Cell sizes for dynamic load balancing */
+    std::vector<DDCellsizesWithDlb> cellsizesWithDlb;
+
+    /* Stuff for load communication */
+    /**< The recorded load data */
+    domdec_load_t* load = nullptr;
+    /**< The number of MPI ranks sharing the GPU our rank is using */
+    int nrank_gpu_shared = 0;
+#if GMX_MPI
+    /**< The MPI load communicator */
+    MPI_Comm* mpi_comm_load = nullptr;
+    /**< The MPI load communicator for ranks sharing a GPU */
+    MPI_Comm mpi_comm_gpu_shared;
+#endif
+
+    /**< Struct for timing the force load balancing region */
+    BalanceRegion* balanceRegion = nullptr;
+
+    /* Cycle counters over nstlist steps */
+    /**< Total cycles counted */
+    std::array<float, ddCyclNr> cycl = { 0 };
+    /**< The number of cycle recordings */
+    std::array<int, ddCyclNr> cycl_n = { 0 };
+    /**< The maximum cycle count */
+    std::array<float, ddCyclNr> cycl_max = { 0 };
+    /**< Total flops counted */
+    double flop = 0.0;
+    /**< The number of flop recordings */
+    int flop_n = 0;
+    /** How many times did we have load measurements */
+    int n_load_have = 0;
+    /** How many times have we collected the load measurements */
+    int n_load_collect = 0;
+
+    /* Cycle count history for DLB checks */
+    /**< The averaged cycles per step over the last nstlist step before turning on DLB */
+    float cyclesPerStepBeforeDLB = 0;
+    /**< The running average of the cycles per step during DLB */
+    float cyclesPerStepDlbExpAverage = 0;
+    /**< Have we turned off DLB (after turning DLB on)? */
+    bool haveTurnedOffDlb = false;
+    /**< The DD step at which we last measured that DLB off was faster than DLB on, 0 if there was no such step */
+    int64_t dlbSlowerPartitioningCount = 0;
+
+    /* Statistics for atoms */
+    /**< The atoms per range, summed over the steps */
+    double sum_nat[static_cast<int>(DDAtomRanges::Type::Number)] = {};
+
+    /* Statistics for calls and times */
+    /**< The number of partioning calls */
+    int ndecomp = 0;
+    /**< The number of load recordings */
+    int nload = 0;
+    /**< Total MD step time */
+    double load_step = 0.0;
+    /**< Total PP force time */
+    double load_sum = 0.0;
+    /**< Max \p load_sum over the ranks */
+    double load_max = 0.0;
+    /**< Was load balancing limited, per DD dim */
+    gmx::IVec load_lim = { 0, 0, 0 };
+    /**< Total time on PP done during PME overlap time */
+    double load_mdf = 0.0;
+    /**< Total time on our PME-only rank */
+    double load_pme = 0.0;
+
+    /** The last partition step */
+    int64_t partition_step = INT_MIN;
+};
+
+/*! \brief DD zone permutation
+ *
+ * Zone permutation from the Cartesian x-major/z-minor order to an order
+ * that leads to consecutive charge groups for neighbor searching.
+ * TODO: It should be possible to remove this now that the group scheme is removed
+ */
+static const int zone_perm[3][4] = { { 0, 0, 0, 0 }, { 1, 0, 0, 0 }, { 3, 0, 1, 2 } };
+
+/*! \brief DD zone reordering to Cartesian order
+ *
+ * Index to reorder the zone such that the end up in Cartesian order
+ * with dimension index 0 major and dimension index 2 minor.
+ */
+static const int zone_reorder_cartesian[DD_MAXZONE] = { 0, 1, 3, 2, 5, 4, 6, 7 };
+
+/* dd_zo and dd_zp3 is set up such that i zones with non-zero
+ * components see only j zones with that component 0.
+ */
+
+/*! \brief Returns the DD cut-off distance for multi-body interactions */
+real dd_cutoff_multibody(const gmx_domdec_t* dd);
+
+/*! \brief Returns the DD cut-off distance for two-body interactions */
+real dd_cutoff_twobody(const gmx_domdec_t* dd);
+
+/*! \brief Returns the domain index given the number of domains and the domain coordinates
+ *
+ * This order is required to minimize the coordinate communication in PME
+ * which uses decomposition in the x direction.
+ */
+static inline int dd_index(const ivec numDomains, const ivec domainCoordinates)
+{
+    return ((domainCoordinates[XX] * numDomains[YY] + domainCoordinates[YY]) * numDomains[ZZ])
+           + domainCoordinates[ZZ];
+};
+
+/*! Returns the size of the buffer to hold fractional cell boundaries for DD dimension index dimIndex */
+static inline int ddCellFractionBufferSize(const gmx_domdec_t* dd, int dimIndex)
+{
+    return dd->numCells[dd->dim[dimIndex]] + 1 + dimIndex * 2 + 1 + dimIndex;
+}
+
+/*! \brief Maximum number of ranks for using send/recv for state scattering and gathering
+ *
+ * Use separate MPI send and receive commands
+ * when #ranks <= c_maxNumRanksUseSendRecvForScatterAndGather
+ * This saves memory (and some copying for small #ranks).
+ * For high parallelization scatter and gather calls are used.
+ */
+static constexpr int c_maxNumRanksUseSendRecvForScatterAndGather = 4;
+
+/*! \brief Make DD cells larger by this factor than the limit to avoid rounding issues */
+static constexpr double DD_CELL_MARGIN = 1.0001;
+
+/*! \brief Factor for checking DD cell size limitation during DLB, should be in between 1 and DD_CELL_MARGIN */
+static constexpr double DD_CELL_MARGIN2 = 1.00005;
+
+/*! \brief With pressure scaling, keep cell sizes 2% above the limit to allow for some scaling */
+static constexpr double DD_PRES_SCALE_MARGIN = 1.02;
+
+/*! \endcond */
+
+#endif
diff --git a/src/include/gromacs/domdec/domdec_network.h b/src/include/gromacs/domdec/domdec_network.h
new file mode 100644 (file)
index 0000000..48d8702
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2008-2018, The GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief This file declares functions for (mostly) the domdec module
+ * to use MPI functionality
+ *
+ * \todo Wrap the raw dd_bcast in md.cpp into a higher-level function
+ * in the domdec module, then this file can be module-internal.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+#ifndef GMX_DOMDEC_DOMDEC_NETWORK_H
+#define GMX_DOMDEC_DOMDEC_NETWORK_H
+
+#include "gromacs/math/vectypes.h"
+
+struct gmx_domdec_t;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+/* \brief */
+enum
+{
+    dddirForward,
+    dddirBackward
+};
+
+/*! \brief Move T values in the communication region one cell along
+ * the domain decomposition
+ *
+ * Moves in the dimension indexed by ddDimensionIndex, either forward
+ * (direction=dddirFoward) or backward (direction=dddirBackward).
+ *
+ * \todo This function template is deprecated, new calls should be
+ * made to the version taking ArrayRef parameters and this function
+ * template removed when unused.
+ */
+template<typename T>
+void ddSendrecv(const gmx_domdec_t* dd,
+                int                 ddDimensionIndex,
+                int                 direction,
+                T*                  sendBuffer,
+                int                 numElementsToSend,
+                T*                  receiveBuffer,
+                int                 numElementsToReceive);
+
+//! Extern declaration for int specialization
+extern template void ddSendrecv<int>(const gmx_domdec_t* dd,
+                                     int                 ddDimensionIndex,
+                                     int                 direction,
+                                     int*                buf_s,
+                                     int                 n_s,
+                                     int*                buf_r,
+                                     int                 n_r);
+
+//! Extern declaration for real specialization
+extern template void ddSendrecv<real>(const gmx_domdec_t* dd,
+                                      int                 ddDimensionIndex,
+                                      int                 direction,
+                                      real*               buf_s,
+                                      int                 n_s,
+                                      real*               buf_r,
+                                      int                 n_r);
+
+//! Extern declaration for rvec specialization
+extern template void ddSendrecv<rvec>(const gmx_domdec_t* dd,
+                                      int                 ddDimensionIndex,
+                                      int                 direction,
+                                      rvec*               buf_s,
+                                      int                 n_s,
+                                      rvec*               buf_r,
+                                      int                 n_r);
+
+/*! \brief Move a view of T values in the communication region one
+ * cell along the domain decomposition
+ *
+ * Moves in the dimension indexed by ddDimensionIndex, either forward
+ * (direction=dddirFoward) or backward (direction=dddirBackward).
+ */
+template<typename T>
+void ddSendrecv(const gmx_domdec_t* dd,
+                int                 ddDimensionIndex,
+                int                 direction,
+                gmx::ArrayRef<T>    sendBuffer,
+                gmx::ArrayRef<T>    receiveBuffer);
+
+//! Extern declaration for int specialization
+extern template void ddSendrecv<int>(const gmx_domdec_t* dd,
+                                     int                 ddDimensionIndex,
+                                     int                 direction,
+                                     gmx::ArrayRef<int>  sendBuffer,
+                                     gmx::ArrayRef<int>  receiveBuffer);
+
+//! Extern declaration for real specialization
+extern template void ddSendrecv<real>(const gmx_domdec_t* dd,
+                                      int                 ddDimensionIndex,
+                                      int                 direction,
+                                      gmx::ArrayRef<real> sendBuffer,
+                                      gmx::ArrayRef<real> receiveBuffer);
+
+//! Extern declaration for gmx::RVec specialization
+extern template void ddSendrecv<gmx::RVec>(const gmx_domdec_t*      dd,
+                                           int                      ddDimensionIndex,
+                                           int                      direction,
+                                           gmx::ArrayRef<gmx::RVec> sendBuffer,
+                                           gmx::ArrayRef<gmx::RVec> receiveBuffer);
+
+/*! \brief Move revc's in the comm. region one cell along the domain decomposition
+ *
+ * Moves in dimension indexed by ddimind, simultaneously in the forward
+ * and backward directions.
+ */
+void dd_sendrecv2_rvec(const struct gmx_domdec_t* dd,
+                       int                        ddimind,
+                       rvec*                      buf_s_fw,
+                       int                        n_s_fw,
+                       rvec*                      buf_r_fw,
+                       int                        n_r_fw,
+                       rvec*                      buf_s_bw,
+                       int                        n_s_bw,
+                       rvec*                      buf_r_bw,
+                       int                        n_r_bw);
+
+
+/* The functions below perform the same operations as the MPI functions
+ * with the same name appendices, but over the domain decomposition
+ * nodes only.
+ * The DD master node is the master for these operations.
+ */
+
+/*! \brief Broadcasts \p nbytes from \p data on \p DDMASTERRANK to all PP ranks */
+void dd_bcast(const gmx_domdec_t* dd, int nbytes, void* data);
+
+/*! \brief Copies \p nbytes from \p src to \p dest on \p DDMASTERRANK
+ * and then broadcasts to \p dest on all PP ranks */
+void dd_bcastc(const gmx_domdec_t* dd, int nbytes, void* src, void* dest);
+
+/*! \brief Scatters \p nbytes from \p src on \p DDMASTERRANK to all PP ranks, received in \p dest */
+void dd_scatter(const gmx_domdec_t* dd, int nbytes, const void* src, void* dest);
+
+/*! \brief Gathers \p nbytes from \p src on all PP ranks, received in \p dest on \p DDMASTERRANK */
+void dd_gather(const gmx_domdec_t* dd, int nbytes, const void* src, void* dest);
+
+/*! \brief Scatters \p scounts bytes from \p src on \p DDMASTERRANK to all PP ranks, receiving \p rcount bytes in \p dest.
+ *
+ * See man MPI_Scatterv for details of how to construct scounts and disps.
+ * If rcount==0, rbuf is allowed to be NULL */
+void dd_scatterv(const gmx_domdec_t* dd, int* scounts, int* disps, const void* sbuf, int rcount, void* rbuf);
+
+/*! \brief Gathers \p rcount bytes from \p src on all PP ranks, received in \p scounts bytes in \p dest on \p DDMASTERRANK.
+ *
+ * See man MPI_Gatherv for details of how to construct scounts and disps.
+ *
+ * If scount==0, sbuf is allowed to be NULL */
+void dd_gatherv(const gmx_domdec_t* dd, int scount, const void* sbuf, int* rcounts, int* disps, void* rbuf);
+
+#endif
diff --git a/src/include/gromacs/domdec/domdec_setup.h b/src/include/gromacs/domdec/domdec_setup.h
new file mode 100644 (file)
index 0000000..dc4202c
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares functions for choosing the DD grid setup
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+#ifndef GMX_DOMDEC_DOMDEC_SETUP_H
+#define GMX_DOMDEC_DOMDEC_SETUP_H
+
+#include "gromacs/math/vec.h"
+#include "gromacs/utility/gmxmpi.h"
+
+enum class DDRole;
+struct DDSettings;
+struct DDSystemInfo;
+struct gmx_ddbox_t;
+struct gmx_mtop_t;
+struct t_commrec;
+struct t_inputrec;
+
+namespace gmx
+{
+struct DomdecOptions;
+struct MDModulesNotifiers;
+class MDLogger;
+class SeparatePmeRanksPermitted;
+template<typename T>
+class ArrayRef;
+} // namespace gmx
+
+/*! \brief Returns the volume fraction of the system that is communicated */
+real comm_box_frac(const gmx::IVec& dd_nc, real cutoff, const gmx_ddbox_t& ddbox);
+
+/*! \internal
+ * \brief Describes the DD grid setup
+ *
+ * This struct is for temporary use when choosing and initializing
+ * the domain decomposition grid.
+ */
+struct DDGridSetup
+{
+    //! The number of separate PME ranks, 0 if none or all ranks do PME
+    int numPmeOnlyRanks = 0;
+    //! The number of domains along each dimension
+    ivec numDomains = { 0, 0, 0 };
+    //! The number of dimensions which we decompose in domains
+    int numDDDimensions = 0;
+    //! The domain decomposition dimensions, the first numDDDimensions entries are used
+    ivec ddDimensions = { -1, -1, -1 };
+};
+
+/*! \brief Checks for ability to use separate PME ranks
+ *
+ * Disables automatic usage if:
+ * some MDModule could not use separate PME ranks,
+ * GPU setup is not compatible with separate PME ranks,
+ * user provided explicit DD grid
+ * or total number of ranks is not large enough to use PME ranks
+ */
+gmx::SeparatePmeRanksPermitted checkForSeparatePmeRanks(const gmx::MDModulesNotifiers& notifiers,
+                                                        const gmx::DomdecOptions&      options,
+                                                        int  numRanksRequested,
+                                                        bool useGpuForNonbonded,
+                                                        bool useGpuForPme);
+
+/*! \brief Checks that requests for PP and PME ranks honor basic expectations
+ *
+ * Issues a fatal error if there are more PME ranks than PP, if the
+ * count of PP ranks has a prime factor that is too large to be likely
+ * to have good performance or PME-only ranks could not be used,
+ * but requested with -npme > 0 */
+void checkForValidRankCountRequests(int                                   numRanksRequested,
+                                    bool                                  usingPme,
+                                    int                                   numPmeRanksRequested,
+                                    const gmx::SeparatePmeRanksPermitted& separatePmeRanksPermitted,
+                                    bool checkForLargePrimeFactors);
+
+/*! \brief Return the minimum cell size (in nm) required for DD */
+real getDDGridSetupCellSizeLimit(const gmx::MDLogger& mdlog,
+                                 bool                 bDynLoadBal,
+                                 real                 dlb_scale,
+                                 const t_inputrec&    ir,
+                                 real                 systemInfoCellSizeLimit);
+
+/*! \brief Determines the DD grid setup
+ *
+ * Either implements settings required by the user, or otherwise
+ * chooses estimated optimal number of separate PME ranks and DD grid
+ * cell setup, DD cell size limits, and the initial ddbox.
+ */
+DDGridSetup getDDGridSetup(const gmx::MDLogger&                  mdlog,
+                           DDRole                                ddRole,
+                           MPI_Comm                              communicator,
+                           int                                   numRanksRequested,
+                           const gmx::DomdecOptions&             options,
+                           const DDSettings&                     ddSettings,
+                           const DDSystemInfo&                   systemInfo,
+                           real                                  cellSizeLimit,
+                           const gmx_mtop_t&                     mtop,
+                           const t_inputrec&                     ir,
+                           const gmx::SeparatePmeRanksPermitted& separatePmeRanksPermitted,
+                           const matrix                          box,
+                           gmx::ArrayRef<const gmx::RVec>        xGlobal,
+                           gmx_ddbox_t*                          ddbox);
+
+#endif
diff --git a/src/include/gromacs/domdec/domdec_specatomcomm.h b/src/include/gromacs/domdec/domdec_specatomcomm.h
new file mode 100644 (file)
index 0000000..ab7d723
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2005,2006,2007,2008,2009 by the GROMACS development team.
+ * Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file declares functions for domdec to use
+ * while managing communication of atoms required for special purposes
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_DOMDEC_SPECATOMCOMM_H
+#define GMX_DOMDEC_DOMDEC_SPECATOMCOMM_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_domdec_t;
+struct t_commrec;
+
+namespace gmx
+{
+template<typename T>
+class HashedMap;
+} // namespace gmx
+
+/*! \internal \brief The communication setup along a single dimension */
+struct gmx_specatsend_t
+{
+    std::vector<int> a;     /**< The indices of atoms to send */
+    int              nrecv; /**< The number of atoms to receive */
+};
+
+/*! \internal \brief Struct with setup and buffers for special atom communication */
+struct gmx_domdec_specat_comm_t
+{
+    /* The number of indices to receive during the setup */
+    int nreq[DIM][2][2] = { { { 0 } } }; /**< The nr. of atoms requested, per DIM, direction and direct/indirect */
+    /* The atoms to send */
+    gmx_specatsend_t  spas[DIM][2]; /**< The communication setup per DIM, direction */
+    std::vector<bool> sendAtom;     /**< Work buffer that tells if spec.atoms should be sent */
+
+    /* Send buffers */
+    std::vector<int>       ibuf;  /**< Integer send buffer */
+    std::vector<gmx::RVec> vbuf;  /**< rvec send buffer */
+    std::vector<gmx::RVec> vbuf2; /**< rvec send buffer */
+    /* The range in the local buffer(s) for received atoms */
+    int at_start; /**< Start index of received atoms */
+    int at_end;   /**< End index of received atoms */
+};
+
+/*! \brief Communicates the force for special atoms, the shift forces are reduced with \p fshift != NULL */
+void dd_move_f_specat(const gmx_domdec_t* dd, gmx_domdec_specat_comm_t* spac, rvec* f, rvec* fshift);
+
+/*! \brief Communicates the coordinates for special atoms
+ *
+ * \param[in]     dd         Domain decomposition struct
+ * \param[in]     spac       Special atom communication struct
+ * \param[in]     box        Box, used for pbc
+ * \param[in,out] x0         Vector to communicate
+ * \param[in,out] x1         Vector to communicate, when != NULL
+ * \param[in]     bX1IsCoord Tells is \p x1 is a coordinate vector (needs pbc)
+ */
+void dd_move_x_specat(const gmx_domdec_t*       dd,
+                      gmx_domdec_specat_comm_t* spac,
+                      const matrix              box,
+                      rvec*                     x0,
+                      rvec*                     x1,
+                      gmx_bool                  bX1IsCoord);
+
+/*! \brief Sets up the communication for special atoms
+ *
+ * \param[in]     dd           Domain decomposition struct
+ * \param[in,out] ireq         List of requested atom indices, updated due to aggregation
+ * \param[in,out] spac         Special atom communication struct
+ * \param[out]    ga2la_specat Global to local special atom index
+ * \param[in]     at_start     Index in local state where to start storing communicated atoms
+ * \param[in]     vbuf_fac     Buffer factor, 1 or 2 for communicating 1 or 2 vectors
+ * \param[in]     specat_type  Name of the special atom, used for error message
+ * \param[in]     add_err      Text to add at the end of error message when atoms can't be found
+ */
+int setup_specat_communication(gmx_domdec_t*             dd,
+                               std::vector<int>*         ireq,
+                               gmx_domdec_specat_comm_t* spac,
+                               gmx::HashedMap<int>*      ga2la_specat,
+                               int                       at_start,
+                               int                       vbuf_fac,
+                               const char*               specat_type,
+                               const char*               add_err);
+
+#endif
diff --git a/src/include/gromacs/domdec/domdec_struct.h b/src/include/gromacs/domdec/domdec_struct.h
new file mode 100644 (file)
index 0000000..911f1e7
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares structures related to domain decomposition.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+#ifndef GMX_DOMDEC_DOMDEC_STRUCT_H
+#define GMX_DOMDEC_DOMDEC_STRUCT_H
+
+#include <cstddef>
+
+#include <array>
+#include <memory>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/block.h"
+#include "gromacs/utility/gmxmpi.h"
+#include "gromacs/utility/range.h"
+#include "gromacs/utility/real.h"
+
+//! Max number of zones in domain decomposition
+#define DD_MAXZONE 8
+//! Max number of izones in domain decomposition
+#define DD_MAXIZONE 4
+
+struct AtomDistribution;
+struct gmx_domdec_comm_t;
+struct gmx_domdec_constraints_t;
+struct gmx_domdec_specat_comm_t;
+class gmx_ga2la_t;
+struct gmx_pme_comm_n_box_t;
+struct t_inputrec;
+class gmx_reverse_top_t;
+struct gmx_mtop_t;
+struct ReverseTopOptions;
+
+namespace gmx
+{
+template<typename T>
+class HashedMap;
+class LocalAtomSetManager;
+class LocalTopologyChecker;
+class GpuHaloExchange;
+} // namespace gmx
+
+/*! \internal
+ * \brief Pair interaction zone and atom range for an i-zone
+ */
+struct DDPairInteractionRanges
+{
+    //! The index of this i-zone in the i-zone list
+    int iZoneIndex = -1;
+    //! The range of j-zones
+    gmx::Range<int> jZoneRange;
+    //! The i-atom range
+    gmx::Range<int> iAtomRange;
+    //! The j-atom range
+    gmx::Range<int> jAtomRange;
+    //! Minimum shifts to consider
+    gmx::IVec shift0 = { 0, 0, 0 };
+    //! Maximum shifts to consider
+    gmx::IVec shift1 = { 0, 0, 0 };
+};
+
+typedef struct gmx_domdec_zone_size
+{
+    /* Zone lower corner in triclinic coordinates         */
+    gmx::RVec x0 = { 0, 0, 0 };
+    /* Zone upper corner in triclinic coordinates         */
+    gmx::RVec x1 = { 0, 0, 0 };
+    /* Zone bounding box lower corner in Cartesian coords */
+    gmx::RVec bb_x0 = { 0, 0, 0 };
+    /* Zone bounding box upper corner in Cartesian coords */
+    gmx::RVec bb_x1 = { 0, 0, 0 };
+} gmx_domdec_zone_size_t;
+
+struct gmx_domdec_zones_t
+{
+    /* The number of zones including the home zone */
+    int n = 0;
+    /* The shift of the zones with respect to the home zone */
+    std::array<ivec, DD_MAXZONE> shift;
+    /* The charge group boundaries for the zones */
+    std::array<int, DD_MAXZONE + 1> cg_range;
+    /* The pair interaction zone and atom ranges per each i-zone */
+    std::vector<DDPairInteractionRanges> iZones;
+    /* Boundaries of the zones */
+    std::array<gmx_domdec_zone_size_t, DD_MAXZONE> size;
+    /* The cg density of the home zone */
+    real dens_zone0 = 0;
+};
+
+struct gmx_ddbox_t
+{
+    int       npbcdim;
+    int       nboundeddim;
+    gmx::RVec box0     = { 0, 0, 0 };
+    gmx::RVec box_size = { 0, 0, 0 };
+    /* Tells if the box is skewed for each of the three cartesian directions */
+    gmx::IVec tric_dir = { 0, 0, 0 };
+    gmx::RVec skew_fac = { 0, 0, 0 };
+    /* Orthogonal vectors for triclinic cells, Cartesian index */
+    rvec v[DIM][DIM];
+    /* Normal vectors for the cells walls */
+    rvec normal[DIM];
+};
+
+/*! \internal \brief Provides information about properties of the unit cell */
+struct UnitCellInfo
+{
+    //! Constructor
+    UnitCellInfo(const t_inputrec& ir);
+
+    //! We have PBC from dim 0 (X) up to npbcdim
+    int npbcdim;
+    //! The system is bounded from 0 (X) to numBoundedDimensions
+    int numBoundedDimensions;
+    //! Tells whether the box bounding the atoms is dynamic
+    bool ddBoxIsDynamic;
+    //! Screw PBC?
+    bool haveScrewPBC;
+};
+
+struct gmx_domdec_t
+{ //NOLINT(clang-analyzer-optin.performance.Padding)
+    //! Constructor, only partial for now
+    gmx_domdec_t(const t_inputrec& ir);
+    ~gmx_domdec_t();
+
+    /* The DD particle-particle nodes only */
+    /* The communication setup within the communicator all
+     * defined in dd->comm in domdec.c
+     */
+    int      nnodes       = 1;
+    MPI_Comm mpi_comm_all = MPI_COMM_NULL;
+    /* The local DD cell index and rank */
+    gmx::IVec ci         = { 0, 0, 0 };
+    int       rank       = 0;
+    gmx::IVec master_ci  = { 0, 0, 0 };
+    int       masterrank = 0;
+    /* Communication with the PME only nodes */
+    int                   pme_nodeid           = 0;
+    gmx_bool              pme_receive_vir_ener = false;
+    gmx_pme_comm_n_box_t* cnb                  = nullptr;
+    int                   nreq_pme             = 0;
+    MPI_Request           req_pme[8];
+
+    /* Properties of the unit cell */
+    UnitCellInfo unitCellInfo;
+
+    /* The communication setup, identical for each cell, cartesian index */
+    //! Todo: refactor nbnxm to not rely on this sometimes being a nullptr so this can be IVec
+    ivec      numCells = { 0, 0, 0 };
+    int       ndim     = 0;
+    gmx::IVec dim      = { 0, 0, 0 }; /* indexed by 0 to ndim */
+
+    /* Forward and backward neighboring cells, indexed by 0 to ndim */
+    int neighbor[DIM][2] = { { 0, 0 }, { 0, 0 }, { 0, 0 } };
+
+    /* Only available on the master node */
+    std::unique_ptr<AtomDistribution> ma;
+
+    /* Global atom number to interaction list */
+    std::unique_ptr<gmx_reverse_top_t> reverse_top;
+
+    /* Whether we have non-self exclusion */
+    bool haveExclusions = false;
+
+    /* Vsite stuff */
+    gmx::HashedMap<int>*      ga2la_vsite = nullptr;
+    gmx_domdec_specat_comm_t* vsite_comm  = nullptr;
+    std::vector<int>          vsite_requestedGlobalAtomIndices;
+
+    /* Constraint stuff */
+    gmx_domdec_constraints_t* constraints     = nullptr;
+    gmx_domdec_specat_comm_t* constraint_comm = nullptr;
+
+    /* The number of home atoms */
+    int numHomeAtoms = 0;
+    /* Global atom group indices for the home and all non-home groups */
+    std::vector<int> globalAtomGroupIndices;
+
+    /* Index from the local atoms to the global atoms, covers home and received zones */
+    std::vector<int> globalAtomIndices;
+
+    /* Global atom number to local atom number list */
+    gmx_ga2la_t* ga2la = nullptr;
+
+    /* Communication stuff */
+    gmx_domdec_comm_t* comm = nullptr;
+
+    /* The partioning count, to keep track of the state */
+    int64_t ddp_count = 0;
+
+    /* The managed atom sets that are updated in domain decomposition */
+    gmx::LocalAtomSetManager* atomSets = nullptr;
+
+    //! The handler for checking whether the local topology is missing interactions
+    std::unique_ptr<gmx::LocalTopologyChecker> localTopologyChecker;
+
+    /* gmx_pme_recv_f buffer */
+    std::vector<gmx::RVec> pmeForceReceiveBuffer;
+
+    /* GPU halo exchange objects: this structure supports a vector of pulses for each dimension */
+    std::vector<std::unique_ptr<gmx::GpuHaloExchange>> gpuHaloExchange[DIM];
+};
+
+//! Are we the master node for domain decomposition
+static inline bool DDMASTER(const gmx_domdec_t& dd)
+{
+    return dd.rank == dd.masterrank;
+};
+
+//! Are we the master node for domain decomposition, deprecated
+static inline bool DDMASTER(const gmx_domdec_t* dd)
+{
+    return dd->rank == dd->masterrank;
+};
+
+#endif
diff --git a/src/include/gromacs/domdec/domdec_vsite.h b/src/include/gromacs/domdec/domdec_vsite.h
new file mode 100644 (file)
index 0000000..3ee206f
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2005,2006,2007,2008,2009 by the GROMACS development team.
+ * Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file declares functions for domdec to use
+ * while managing virtual sites.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_DOMDEC_VSITE_H
+#define GMX_DOMDEC_DOMDEC_VSITE_H
+
+#include "gromacs/utility/arrayref.h"
+
+struct gmx_domdec_t;
+struct InteractionList;
+
+/*! \brief Clears the local indices for the virtual site communication setup */
+void dd_clear_local_vsite_indices(struct gmx_domdec_t* dd);
+
+/*! \brief Sets up communication and atom indices for all local vsites */
+int dd_make_local_vsites(struct gmx_domdec_t* dd, int at_start, gmx::ArrayRef<InteractionList> lil);
+
+/*! \brief Initializes the data structures for virtual site communication */
+void init_domdec_vsites(struct gmx_domdec_t* dd, int n_intercg_vsite);
+
+#endif
diff --git a/src/include/gromacs/domdec/dump.h b/src/include/gromacs/domdec/dump.h
new file mode 100644 (file)
index 0000000..01c004a
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file declares functions for DD to write PDB files
+ * e.g. when reporting problems.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_DUMP_H
+#define GMX_DOMDEC_DUMP_H
+
+#include <cstdint>
+
+#include "gromacs/math/vectypes.h"
+
+struct gmx_ddbox_t;
+struct gmx_domdec_t;
+struct gmx_mtop_t;
+struct t_commrec;
+
+//! Write the DD grid to a PDB file
+void write_dd_grid_pdb(const char* fn, int64_t step, gmx_domdec_t* dd, matrix box, gmx_ddbox_t* ddbox);
+
+/*! \brief Dump a pdb file with the current DD home + communicated atoms.
+ *
+ * When natoms=-1, dump all known atoms.
+ */
+void write_dd_pdb(const char*       fn,
+                  int64_t           step,
+                  const char*       title,
+                  const gmx_mtop_t& mtop,
+                  const t_commrec*  cr,
+                  int               natoms,
+                  const rvec        x[],
+                  const matrix      box);
+
+#endif
diff --git a/src/include/gromacs/domdec/ga2la.h b/src/include/gromacs/domdec/ga2la.h
new file mode 100644 (file)
index 0000000..c073089
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Defines structures and functions for mapping from global to local atom
+ * indices. The functions are performance critical and should be inlined.
+ *
+ * \inlibraryapi
+ * \ingroup module_domdec
+ *
+ * \author Berk Hess <hess@kth.se>
+ *
+ */
+#ifndef GMX_DOMDEC_GA2LA_H
+#define GMX_DOMDEC_GA2LA_H
+
+#include <vector>
+
+#include "gromacs/domdec/hashedmap.h"
+#include "gromacs/utility/gmxassert.h"
+
+/*! \libinternal \brief Global to local atom mapping
+ *
+ * Used for efficient mapping from global atom indices to local atom indices
+ * in the domain decomposition.
+ */
+class gmx_ga2la_t
+{
+public:
+    /*! \libinternal \brief Structure for the local atom info */
+    struct Entry
+    {
+        int la;   /**< The local atom index */
+        int cell; /**< The DD zone index for neighboring domains, zone+zone otherwise */
+    };
+
+    /*! \brief Constructor
+     *
+     * \param[in] numAtomsTotal  The total number of atoms in the system
+     * \param[in] numAtomsLocal  An estimate of the number of home+communicated atoms
+     */
+    gmx_ga2la_t(int numAtomsTotal, int numAtomsLocal);
+    ~gmx_ga2la_t() { usingDirect_ ? data_.direct.~vector() : data_.hashed.~HashedMap(); }
+
+    /*! \brief Inserts an entry, there should not already be an entry for \p a_gl
+     *
+     * \param[in]  a_gl   The global atom index
+     * \param[in]  value  The value to set for this index
+     */
+    void insert(int a_gl, const Entry& value)
+    {
+        GMX_ASSERT(a_gl >= 0, "Only global atom indices >= 0 are supported");
+        if (usingDirect_)
+        {
+            GMX_ASSERT(data_.direct[a_gl].cell == -1,
+                       "The key to be inserted should not be present");
+            data_.direct[a_gl] = value;
+        }
+        else
+        {
+            data_.hashed.insert(a_gl, value);
+        }
+    }
+
+    //! Delete the entry for global atom a_gl
+    void erase(int a_gl)
+    {
+        if (usingDirect_)
+        {
+            data_.direct[a_gl].cell = -1;
+        }
+        else
+        {
+            data_.hashed.erase(a_gl);
+        }
+    }
+
+    //! Returns a pointer to the entry when present, nullptr otherwise
+    const Entry* find(int a_gl) const
+    {
+        if (usingDirect_)
+        {
+            return (data_.direct[a_gl].cell == -1) ? nullptr : &(data_.direct[a_gl]);
+        }
+        else
+        {
+            return (data_.hashed.find(a_gl));
+        }
+    }
+
+    //! Returns the local atom index if it is a home atom, nullptr otherwise
+    const int* findHome(int a_gl) const
+    {
+        const Entry* const e = find(a_gl);
+        return (e && e->cell == 0) ? &(e->la) : nullptr;
+    }
+
+    /*! \brief Returns a reference to the entry for a_gl
+     *
+     * A non-release assert checks that a_gl is present.
+     */
+    Entry& at(int a_gl)
+    {
+        if (usingDirect_)
+        {
+            GMX_ASSERT(data_.direct[a_gl].cell >= 0, "a_gl should be present");
+            return data_.direct[a_gl];
+        }
+        else
+        {
+            Entry* search = data_.hashed.find(a_gl);
+            GMX_ASSERT(search, "a_gl should be present");
+            return *search;
+        }
+    }
+
+    /*! \brief Clear all the entries in the list.
+     *
+     * \param[in] resizeHashTable  When true the hash table is optimized based on the current number of entries stored
+     */
+    void clear(const bool resizeHashTable)
+    {
+        if (usingDirect_)
+        {
+            for (Entry& entry : data_.direct)
+            {
+                entry.cell = -1;
+            }
+        }
+        else if (resizeHashTable)
+        {
+            data_.hashed.clearAndResizeHashTable();
+        }
+        else
+        {
+            data_.hashed.clear();
+        }
+    }
+
+private:
+    union Data
+    {
+        std::vector<Entry>    direct;
+        gmx::HashedMap<Entry> hashed;
+        // constructor and destructor function in parent class
+        Data() {}
+        ~Data() {}
+    } data_;
+    const bool usingDirect_;
+};
+
+#endif
diff --git a/src/include/gromacs/domdec/gpuhaloexchange.h b/src/include/gromacs/domdec/gpuhaloexchange.h
new file mode 100644 (file)
index 0000000..431407b
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declaration of GPU halo exchange.
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+#ifndef GMX_DOMDEC_GPUHALOEXCHANGE_H
+#define GMX_DOMDEC_GPUHALOEXCHANGE_H
+
+#include <memory>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/fixedcapacityvector.h"
+#include "gromacs/utility/gmxmpi.h"
+
+struct gmx_domdec_t;
+struct gmx_wallcycle;
+class DeviceContext;
+class DeviceStream;
+class GpuEventSynchronizer;
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief Manages GPU Halo Exchange object */
+class GpuHaloExchange
+{
+
+public:
+    /*! \brief Creates GPU Halo Exchange object.
+     *
+     * Coordinate Halo exchange will be performed in its own stream
+     * with appropriate event-based synchronization, and the \c
+     * communicateHaloCoordinates method must be called before any
+     * subsequent operations that access non-local parts of the
+     * coordinate buffer (such as the non-local non-bonded
+     * kernels). It also must be called after the local coordinates
+     * buffer operations (where the coordinates are copied to the
+     * device and hence the \c coordinatesReadyOnDeviceEvent is
+     * recorded). Force Halo exchange will also be performed in its
+     * own stream with appropriate event-based synchronization, and
+     * the \c communicateHaloForces method must be called after the
+     * non-local buffer operations, after the local force buffer has
+     * been copied to the GPU (if CPU forces are present), and before
+     * the local buffer operations. The force halo exchange does not
+     * yet support virial steps.
+     *
+     * \param [inout] dd                       domdec structure
+     * \param [in]    dimIndex                 the dimension index for this instance
+     * \param [in]    mpi_comm_mysim           communicator used for simulation
+     * \param [in]    deviceContext            GPU device context
+     * \param [in]    pulse                    the communication pulse for this instance
+     * \param [in]    wcycle                   The wallclock counter
+     */
+    GpuHaloExchange(gmx_domdec_t*        dd,
+                    int                  dimIndex,
+                    MPI_Comm             mpi_comm_mysim,
+                    const DeviceContext& deviceContext,
+                    int                  pulse,
+                    gmx_wallcycle*       wcycle);
+    ~GpuHaloExchange();
+    GpuHaloExchange(GpuHaloExchange&& source) noexcept;
+    GpuHaloExchange& operator=(GpuHaloExchange&& source) noexcept;
+
+    /*! \brief
+     *
+     * Initialization for GPU halo exchange of coordinates buffer
+     * \param [in] d_coordinateBuffer   pointer to coordinates buffer in GPU memory
+     * \param [in] d_forcesBuffer   pointer to coordinates buffer in GPU memory
+     */
+    void reinitHalo(DeviceBuffer<RVec> d_coordinateBuffer, DeviceBuffer<RVec> d_forcesBuffer);
+
+
+    /*! \brief GPU halo exchange of coordinates buffer.
+     *
+     * Must be called after local setCoordinates (which records an
+     * event when the coordinate data has been copied to the
+     * device).
+     * \param [in] box               Coordinate box (from which shifts will be constructed)
+     * \param [in] dependencyEvent   Dependency event for this operation
+     * \returns                      Event recorded when this operation has been launched
+     */
+    GpuEventSynchronizer* communicateHaloCoordinates(const matrix box, GpuEventSynchronizer* dependencyEvent);
+
+    /*! \brief GPU halo exchange of force buffer.
+     * \param[in] accumulateForces  True if forces should accumulate, otherwise they are set
+     * \param[in] dependencyEvents  Dependency events for this operation
+     */
+    void communicateHaloForces(bool                                           accumulateForces,
+                               FixedCapacityVector<GpuEventSynchronizer*, 2>* dependencyEvents);
+
+    /*! \brief Get the event synchronizer for the forces ready on device.
+     *  \returns  The event to synchronize the stream that consumes forces on device.
+     */
+    GpuEventSynchronizer* getForcesReadyOnDeviceEvent();
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/domdec/gpuhaloexchange_impl.cuh b/src/include/gromacs/domdec/gpuhaloexchange_impl.cuh
new file mode 100644 (file)
index 0000000..c298346
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares CUDA implementation of GPU Halo Exchange.
+ *
+ * This header file is needed to include from both the device-side
+ * kernels file, and the host-side management code.
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ *
+ * \ingroup module_domdec
+ */
+#ifndef GMX_DOMDEC_GPUHALOEXCHANGE_IMPL_H
+#define GMX_DOMDEC_GPUHALOEXCHANGE_IMPL_H
+
+#include "gromacs/domdec/gpuhaloexchange.h"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/gpueventsynchronizer.h"
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/utility/gmxmpi.h"
+
+struct gmx_wallcycle;
+
+namespace gmx
+{
+
+/*! \brief switch for whether coordinates or force halo is being applied */
+enum class HaloQuantity
+{
+    HaloCoordinates,
+    HaloForces
+};
+
+/*! \internal \brief Class with interfaces and data for GPU Halo Exchange */
+class GpuHaloExchange::Impl
+{
+
+public:
+    /*! \brief Creates GPU Halo Exchange object.
+     *
+     * \param [inout] dd                       domdec structure
+     * \param [in]    dimIndex                 the dimension index for this instance
+     * \param [in]    mpi_comm_mysim           communicator used for simulation
+     * \param [in]    deviceContext            GPU device context
+     * \param [in]    pulse                    the communication pulse for this instance
+     * \param [in]    wcycle                   The wallclock counter
+     */
+    Impl(gmx_domdec_t*        dd,
+         int                  dimIndex,
+         MPI_Comm             mpi_comm_mysim,
+         const DeviceContext& deviceContext,
+         int                  pulse,
+         gmx_wallcycle*       wcycle);
+    ~Impl();
+
+    /*! \brief
+     * (Re-) Initialization for GPU halo exchange
+     * \param [in] d_coordinatesBuffer  pointer to coordinates buffer in GPU memory
+     * \param [in] d_forcesBuffer   pointer to forces buffer in GPU memory
+     */
+    void reinitHalo(float3* d_coordinatesBuffer, float3* d_forcesBuffer);
+
+
+    /*! \brief
+     * GPU halo exchange of coordinates buffer
+     * \param [in] box  Coordinate box (from which shifts will be constructed)
+     * \param [in] dependencyEvent   Dependency event for this operation
+     * \returns                      Event recorded when this operation has been launched
+     */
+    GpuEventSynchronizer* communicateHaloCoordinates(const matrix box, GpuEventSynchronizer* dependencyEvent);
+
+    /*! \brief  GPU halo exchange of force buffer
+     * \param [in] accumulateForces  True if forces should accumulate, otherwise they are set
+     * \param [in] dependencyEvents  Dependency events for this operation
+     */
+    void communicateHaloForces(bool                                           accumulateForces,
+                               FixedCapacityVector<GpuEventSynchronizer*, 2>* dependencyEvents);
+
+    /*! \brief Get the event synchronizer for the forces ready on device.
+     *  \returns  The event to synchronize the stream that consumes forces on device.
+     */
+    GpuEventSynchronizer* getForcesReadyOnDeviceEvent();
+
+private:
+    /*! \brief Data transfer wrapper for GPU halo exchange
+     * \param [in] sendPtr      send buffer address
+     * \param [in] sendSize     number of elements to send
+     * \param [in] sendRank     rank of destination
+     * \param [in] recvPtr      receive buffer address
+     * \param [in] recvSize     number of elements to receive
+     * \param [in] recvRank     rank of source
+     */
+    void communicateHaloData(float3* sendPtr, int sendSize, int sendRank, float3* recvPtr, int recvSize, int recvRank);
+
+    /*! \brief Data transfer for GPU halo exchange using CUDA memcopies
+     * \param [inout] sendPtr    address to send data from
+     * \param [in] sendSize      number of atoms to be sent
+     * \param [in] sendRank      rank to send data to
+     * \param [in] remotePtr     remote address to recv data
+     * \param [in] recvRank      rank to recv data from
+     */
+    void communicateHaloDataWithCudaDirect(float3* sendPtr, int sendSize, int sendRank, float3* remotePtr, int recvRank);
+
+    /*! \brief Data transfer wrapper for GPU halo exchange using MPI_send and MPI_Recv
+     * \param [in] sendPtr      send buffer address
+     * \param [in] sendSize     number of elements to send
+     * \param [in] sendRank     rank of destination
+     * \param [in] recvPtr      receive buffer address
+     * \param [in] recvSize     number of elements to receive
+     * \param [in] recvRank     rank of source
+     */
+    void communicateHaloDataWithCudaMPI(float3* sendPtr,
+                                        int     sendSize,
+                                        int     sendRank,
+                                        float3* recvPtr,
+                                        int     recvSize,
+                                        int     recvRank);
+
+    /*! \brief Exchange coordinate-ready event with neighbor ranks and enqueue wait in halo stream
+     * \param [in] eventSync    event recorded when coordinates/forces are ready to device
+     */
+    void enqueueWaitRemoteCoordinatesReadyEvent(GpuEventSynchronizer* coordinatesReadyOnDeviceEvent);
+
+    //! Domain decomposition object
+    gmx_domdec_t* dd_ = nullptr;
+    //! map of indices to be sent from this rank
+    gmx::HostVector<int> h_indexMap_;
+    //! device copy of index map
+    int* d_indexMap_ = nullptr;
+    //! number of elements in index map array
+    int indexMapSize_ = -1;
+    //! number of elements allocated in index map array
+    int indexMapSizeAlloc_ = -1;
+    //! device buffer for sending packed data
+    float3* d_sendBuf_ = nullptr;
+    //! number of atoms in sendbuf array
+    int sendBufSize_ = -1;
+    //! number of atoms allocated in sendbuf array
+    int sendBufSizeAlloc_ = -1;
+    //! device buffer for receiving packed data
+    float3* d_recvBuf_ = nullptr;
+    //! maximum size of packed buffer
+    int maxPackedBufferSize_ = 0;
+    //! number of atoms in recvbuf array
+    int recvBufSize_ = -1;
+    //! number of atoms allocated in recvbuf array
+    int recvBufSizeAlloc_ = -1;
+    //! rank to send data to for X
+    int sendRankX_ = 0;
+    //! rank to recv data from for X
+    int recvRankX_ = 0;
+    //! rank to send data to for F
+    int sendRankF_ = 0;
+    //! rank to recv data from for F
+    int recvRankF_ = 0;
+    //! send copy size from this rank for X
+    int xSendSize_ = 0;
+    //! recv copy size to this rank for X
+    int xRecvSize_ = 0;
+    //! send copy size from this rank for F
+    int fSendSize_ = 0;
+    //! recv copy size to this rank for F
+    int fRecvSize_ = 0;
+    //! number of home atoms - offset of local halo region
+    int numHomeAtoms_ = 0;
+    //! remote GPU coordinates buffer pointer for pushing data
+    float3* remoteXPtr_ = nullptr;
+    //! remote GPU force buffer pointer for pushing data
+    float3* remoteFPtr_ = nullptr;
+    //! Periodic Boundary Conditions for this rank
+    bool usePBC_ = false;
+    //! force shift buffer on device
+    float3* d_fShift_ = nullptr;
+    //! Event triggered when halo transfer has been launched with direct CUD memory copy
+    GpuEventSynchronizer* haloDataTransferLaunched_ = nullptr;
+    //! MPI communicator used for simulation
+    MPI_Comm mpi_comm_mysim_;
+    //! GPU context object
+    const DeviceContext& deviceContext_;
+    //! CUDA stream for this halo exchange
+    DeviceStream* haloStream_;
+    //! full coordinates buffer in GPU memory
+    float3* d_x_ = nullptr;
+    //! full forces buffer in GPU memory
+    float3* d_f_ = nullptr;
+    //! An event recorded once the exchanged forces are ready on the GPU
+    GpuEventSynchronizer fReadyOnDevice_;
+    //! The dimension index corresponding to this halo exchange instance
+    int dimIndex_ = 0;
+    //! The pulse corresponding to this halo exchange instance
+    int pulse_ = 0;
+    //! The wallclock counter
+    gmx_wallcycle* wcycle_ = nullptr;
+    //! The atom offset for receive (x) or send (f) for dimension index and pulse corresponding to this halo exchange instance
+    int atomOffset_ = 0;
+    //! Event triggered when coordinate halo has been launched
+    GpuEventSynchronizer coordinateHaloLaunched_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/domdec/hashedmap.h b/src/include/gromacs/domdec/hashedmap.h
new file mode 100644 (file)
index 0000000..6877b7d
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Defines structures and functions for mapping from keys to entries
+ * indices using a hash table.
+ * The functions are performance critical and should be inlined.
+ *
+ * \inlibraryapi
+ * \ingroup module_domdec
+ *
+ * \author Berk Hess <hess@kth.se>
+ *
+ */
+#ifndef GMX_DOMDEC_HASHEDMAP_H
+#define GMX_DOMDEC_HASHEDMAP_H
+
+#include <climits>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "gromacs/compat/utility.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/exceptions.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief Unordered key to value mapping
+ *
+ * Efficiently manages mapping from integer keys to values.
+ * Note that this basically implements a subset of the functionality of
+ * std::unordered_map, but is an order of magnitude faster.
+ */
+template<class T>
+class HashedMap
+{
+private:
+    /*! \libinternal \brief Structure for the key/value hash table */
+    struct hashEntry
+    {
+        int key = -1;  /**< The key */
+        T   value;     /**< The value(s) */
+        int next = -1; /**< Index in the list of the next element with the same hash, -1 if none */
+    };
+
+    /*! \brief The table size is set to at least this factor time the nr of keys */
+    static constexpr float c_relTableSizeSetMin = 1.5;
+    /*! \brief Threshold for increasing the table size */
+    static constexpr float c_relTableSizeThresholdMin = 1.3;
+    /*! \brief Threshold for decreasing the table size */
+    static constexpr float c_relTableSizeThresholdMax = 3.5;
+
+    /*! \brief Resizes the table
+     *
+     * \param[in] numElementsEstimate  An estimate of the number of elements that will be stored
+     */
+    void resize(int numElementsEstimate)
+    {
+        GMX_RELEASE_ASSERT(numElements_ == 0, "Table needs to be empty for resize");
+
+        /* The fraction of table entries with 0   size lists is e^-f.
+         * The fraction of table entries with >=1 size lists is 1 - e^-f
+         * where f is: the #elements / tableSize
+         * The fraction of elements not in the direct list is: 1 - (1 - e^-f)/f.
+         * Thus the optimal table size is roughly double #elements.
+         */
+        /* Make the hash table a power of 2 and at least 1.5 * #elements */
+        int tableSize = 64;
+        while (tableSize <= INT_MAX / 2
+               && static_cast<float>(numElementsEstimate) * c_relTableSizeSetMin > tableSize)
+        {
+            tableSize *= 2;
+        }
+        table_.resize(tableSize);
+
+        /* Table size is a power of 2, so a binary mask gives the hash */
+        bitMask_                        = tableSize - 1;
+        startIndexForSpaceForListEntry_ = tableSize;
+    }
+
+public:
+    /*! \brief Constructor
+     *
+     * \param[in] numElementsEstimate  An estimate of the number of elements that will be stored, used for optimizing initial performance
+     *
+     * Note that the estimate of the number of elements is only relevant
+     * for the performance up until the first call to clear(), after which
+     * table size is optimized based on the actual number of elements.
+     */
+    HashedMap(int numElementsEstimate) { resize(numElementsEstimate); }
+
+    /*! \brief Returns the number of elements */
+    int size() const { return numElements_; }
+
+    /*! \brief Returns the number of buckets, i.e. the number of possible hashes */
+    int bucket_count() const { return bitMask_ + 1; }
+
+private:
+    /*! \brief Inserts or assigns a key and value
+     *
+     * \tparam    allowAssign  Sets whether assignment of a key that is present is allowed
+     * \param[in] key          The key for the entry
+     * \param[in] value        The value for the entry
+     * \throws InvalidInputError from a debug build when attempting to insert a duplicate key with \p allowAssign=true
+     */
+    // cppcheck-suppress unusedPrivateFunction
+    template<bool allowAssign>
+    void insert_assign(int key, const T& value)
+    {
+        size_t ind = (key & bitMask_);
+
+        if (table_[ind].key >= 0)
+        {
+            /* Loop over the entries for this hash.
+             * If we find the matching key, return the value.
+             */
+            int ind_prev = ind;
+            if (table_[ind_prev].key == key)
+            {
+                if (allowAssign)
+                {
+                    table_[ind].value = value;
+                    return;
+                }
+                else
+                {
+// Note: This is performance critical, so we only throw in debug mode
+#ifndef NDEBUG
+                    GMX_THROW(InvalidInputError("Attempt to insert duplicate key"));
+#endif
+                }
+            }
+            while (table_[ind_prev].next >= 0)
+            {
+                ind_prev = table_[ind_prev].next;
+                if (table_[ind_prev].key == key)
+                {
+                    if (allowAssign)
+                    {
+                        table_[ind_prev].value = value;
+                        return;
+                    }
+                    else
+                    {
+#ifndef NDEBUG
+                        GMX_THROW(InvalidInputError("Attempt to insert duplicate key"));
+#endif
+                    }
+                }
+            }
+            /* Search for space in table_ */
+            ind = startIndexForSpaceForListEntry_;
+            while (ind < table_.size() && table_[ind].key >= 0)
+            {
+                ind++;
+            }
+            /* If we are at the end of the list we need to increase the size */
+            if (ind == table_.size())
+            {
+                table_.resize(table_.size() + 1);
+            }
+            table_[ind_prev].next = ind;
+
+            startIndexForSpaceForListEntry_ = ind + 1;
+        }
+
+        table_[ind].key   = key;
+        table_[ind].value = value;
+
+        numElements_ += 1;
+    }
+
+public:
+    /*! \brief Inserts entry, key should not already be present
+     *
+     * \param[in] key    The key for the entry
+     * \param[in] value  The value for the entry
+     * \throws InvalidInputError from a debug build when attempting to inser         */
+    void insert(int key, const T& value) { insert_assign<false>(key, value); }
+
+    /*! \brief Inserts an entry when the key is not present, otherwise sets the value
+     *
+     * \param[in] key    The key for the entry
+     * \param[in] value  The value for the entry
+     */
+    void insert_or_assign(int key, const T& value) { insert_assign<true>(key, value); }
+
+    /*! \brief Delete the entry for key \p key, when present
+     *
+     * \param[in] key  The key
+     */
+    void erase(int key)
+    {
+        int ind_prev = -1;
+        int ind      = (key & bitMask_);
+        do
+        {
+            if (table_[ind].key == key)
+            {
+                if (ind_prev >= 0)
+                {
+                    table_[ind_prev].next = table_[ind].next;
+
+                    /* This index is a linked entry, so we free an entry.
+                     * Check if we are creating the first empty space.
+                     */
+                    if (ind < startIndexForSpaceForListEntry_)
+                    {
+                        startIndexForSpaceForListEntry_ = ind;
+                    }
+                }
+                table_[ind].key  = -1;
+                table_[ind].next = -1;
+
+                numElements_ -= 1;
+
+                return;
+            }
+            ind_prev = ind;
+            ind      = table_[ind].next;
+        } while (ind >= 0);
+    }
+
+    /*! \brief Returns a pointer to the value for the given key or nullptr when not present
+     *
+     * \todo Use std::as_const when CUDA 11 is a requirement.
+     *
+     * \param[in] key  The key
+     * \return a pointer to value for the given key or nullptr when not present
+     */
+    T* find(int key) { return const_cast<T*>(gmx::compat::as_const(*this).find(key)); }
+
+    /*! \brief Returns a pointer to the value for the given key or nullptr when not present
+     *
+     * \param[in] key  The key
+     * \return a pointer to value for the given key or nullptr when not present
+     */
+    const T* find(int key) const
+    {
+        int ind = (key & bitMask_);
+        do
+        {
+            if (table_[ind].key == key)
+            {
+                return &table_[ind].value;
+            }
+            ind = table_[ind].next;
+        } while (ind >= 0);
+
+        return nullptr;
+    }
+
+    //! Clear all the entries in the list
+    void clear()
+    {
+        for (hashEntry& entry : table_)
+        {
+            entry.key  = -1;
+            entry.next = -1;
+        }
+        startIndexForSpaceForListEntry_ = bucket_count();
+        numElements_                    = 0;
+    }
+
+    /*! \brief Clear all the entries in the list and resizes the hash table
+     *
+     * Optimizes the size of the hash table based on the current
+     * number of elements stored.
+     */
+    void clearAndResizeHashTable()
+    {
+        const int oldNumElements = numElements_;
+
+        clear();
+
+        /* Resize the hash table when the occupation is far from optimal.
+         * Do not resize with 0 elements to avoid minimal size when clear()
+         * is called twice in a row.
+         */
+        if (oldNumElements > 0
+            && (oldNumElements * c_relTableSizeThresholdMax < bucket_count()
+                || oldNumElements * c_relTableSizeThresholdMin > bucket_count()))
+        {
+            resize(oldNumElements);
+        }
+    }
+
+private:
+    /*! \brief The hash table list */
+    std::vector<hashEntry> table_;
+    /*! \brief The bit mask for computing the hash of a key */
+    int bitMask_ = 0;
+    /*! \brief Index in table_ at which to start looking for empty space for a new linked list entry */
+    int startIndexForSpaceForListEntry_ = 0;
+    /*! \brief The number of elements currently stored in the table */
+    int numElements_ = 0;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/domdec/localatomset.h b/src/include/gromacs/domdec/localatomset.h
new file mode 100644 (file)
index 0000000..41599d3
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \libinternal \brief
+ * Declares gmx::LocalAtomSet.
+ *
+ * \author Christian Blau <cblau@gwdg.de>
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+#ifndef GMX_DOMDEC_LOCALATOMSET_H
+#define GMX_DOMDEC_LOCALATOMSET_H
+
+#include <cstddef>
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+namespace internal
+{
+class LocalAtomSetData;
+} // namespace internal
+/*! \libinternal \brief
+ * A local atom set collects local, global and collective indices of
+ * the home atoms on a rank. The indices of the home atoms are automatically
+ * updated during domain decomposition, thus gmx::LocalAtomSet::localIndex
+ * enables iteration over local atoms properties like coordinates or forces.
+ * TODO: add a LocalAtomSet iterator.
+ *
+ * To generate a LocalAtomSet call gmx::LocalAtomSetManger::add and keep the
+ * handle to the LocalAtomSet returned from this call.
+ *
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+class LocalAtomSet
+{
+public:
+    friend class LocalAtomSetManager;
+    /*! \brief Maps indices on rank [0..numAtomsLocal_) to global atom indicices.
+     *
+     * \returns the collective index.
+     */
+    ArrayRef<const int> collectiveIndex() const;
+    /*! \brief Global indices of the atoms in this set.
+     *
+     * \note For best performance, store and use a local copy of the arrayref.
+     *
+     * \returns the global index.
+     */
+    ArrayRef<const int> globalIndex() const;
+    /*! \brief Local indices of the atoms.
+     *
+     * For example, the i-th local atom coordinate of this set is
+     * x[atomSet.localIndex()[i]].
+     *
+     * When using in a loop other than a range-based for loop,
+     * performance may improve if the ArrayRef is stored in
+     * a local variable before the loop is entered.
+     * Updated within domain-decomposition.
+     *
+     * \note For best performance, store and use a local copy of the ArrayRef.
+     *
+     * \returns the local index.
+     */
+    ArrayRef<const int> localIndex() const;
+    /*! \brief The number of atoms from this group index on this rank.
+     *
+     * \note For best performance, store and use a local copy of the ArrayRef.
+     */
+    std::size_t numAtomsLocal() const;
+    /*! \brief The number of all atoms from this group index on all ranks together.
+     *
+     * \note For best performance, store and use a local copy.
+     */
+    std::size_t numAtomsGlobal() const;
+
+private:
+    /*! \brief Constructs a new atom set by setting a reference to its
+     * internal data.
+     * \param[in] data The data for the atom set is stored
+     * in LocalAtomSetData, which is manged by \ref gmx::LocalAtomSetManager.
+     */
+    explicit LocalAtomSet(const internal::LocalAtomSetData& data);
+
+    const internal::LocalAtomSetData* data_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/domdec/localatomsetdata.h b/src/include/gromacs/domdec/localatomsetdata.h
new file mode 100644 (file)
index 0000000..a41c6dc
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \internal \brief
+ * Declares gmx::internal::LocalAtomSetData.
+ *
+ * \author Christian Blau <cblau@gwdg.de>
+ * \ingroup module_domdec
+ */
+#ifndef GMX_DOMDEC_LOCALATOMSETDATA_H
+#define GMX_DOMDEC_LOCALATOMSETDATA_H
+
+#include <numeric>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+
+class gmx_ga2la_t;
+
+namespace gmx
+{
+
+namespace internal
+{
+
+/* \brief Internal class for storing and managing atom indices of an atom set.
+ */
+class LocalAtomSetData
+{
+public:
+    /*! \brief Store the data for an atom set with an index group.
+     *
+     * Prior to domain decomposition, local atom indices are global atom indices
+     * and the collective index runs from 0..numberOfAtoms-1.
+     * local and collective indices will be updated in setLocalAndCollectiveIndices
+     * to match domain decompostion if domain decomposition is performed.
+     *
+     * \todo remove this constructor once all indices are represented
+     *       as gmx::index instead of int.
+     *
+     * \note Not created if the internal int type does match gmx::index
+     *
+     * \param[in] globalAtomIndex Indices of the atoms to be managed
+     */
+    template<typename T = void, typename U = std::enable_if_t<!std::is_same_v<int, index>, T>>
+    explicit LocalAtomSetData(ArrayRef<const int> globalAtomIndex) :
+        globalIndex_(globalAtomIndex.begin(), globalAtomIndex.end()),
+        localIndex_(globalAtomIndex.begin(), globalAtomIndex.end())
+    {
+        collectiveIndex_.resize(localIndex_.size());
+        std::iota(collectiveIndex_.begin(), collectiveIndex_.end(), 0);
+    }
+
+    /*! \brief Store the data for an atom set with an index group.
+     *
+     * Prior to domain decomposition, local atom indices are global atom indices
+     * and the collective index runs from 0..numberOfAtoms-1.
+     * local and collective indices will be updated in setLocalAndCollectiveIndices
+     * to match domain decompostion if domain decomposition is performed.
+     *
+     * \param[in] globalAtomIndex Indices of the atoms to be managed
+     */
+    explicit LocalAtomSetData(ArrayRef<const index> globalAtomIndex);
+
+    /*! \brief Sets the local and collective indices from a lookup in ga2la.
+     *
+     * Calculate local and collective indices of home atoms, assuming a valid
+     * global atom to local atom look-up table.
+     *
+     * \param[in] ga2la lookup table that reports if an atom is local.
+     */
+    void setLocalAndCollectiveIndices(const gmx_ga2la_t& ga2la);
+    /*! \brief Global indices of the atoms in this set. */
+    const std::vector<int> globalIndex_;
+    /*! \brief Maps indices on this rank [0..num_atoms_local_) to global atom indicices,
+     * so that localIndex[i] identifies the same atom as globalIndex[collectiveIndex[i]].
+     *
+     * This translation of locally dense atom data to global representation,
+     * allows to adresses per-atom properties, e.g., scattering factors,
+     * that are stored in a global continuous array for each atom of the atom set.
+     */
+    std::vector<int> collectiveIndex_;
+    /*! \brief Local indices of the atoms.
+     * Access the i-th local atom coordinate of this set by x[local_index_[i]].
+     * Constructed and updated every domain-decomposition step.
+     */
+    std::vector<int> localIndex_;
+};
+
+} // namespace internal
+
+} // namespace gmx
+
+
+#endif
diff --git a/src/include/gromacs/domdec/localatomsetmanager.h b/src/include/gromacs/domdec/localatomsetmanager.h
new file mode 100644 (file)
index 0000000..497efd7
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::LocalAtomSetManager
+ *
+ * \author Christian Blau <cblau@gwdg.de>
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_LOCALATOMSETMANAGER_H
+#define GMX_DOMDEC_LOCALATOMSETMANAGER_H
+
+#include <memory>
+
+#include "gromacs/utility/basedefinitions.h"
+
+class gmx_ga2la_t;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class LocalAtomSet;
+
+/*! \libinternal \brief
+ * Hands out handles to local atom set indices and triggers index recalculation
+ * for all sets upon domain decomposition if run in parallel.
+ *
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+class LocalAtomSetManager
+{
+public:
+    LocalAtomSetManager();
+    ~LocalAtomSetManager();
+#ifndef DOXYGEN
+    /*! \brief Add a new atom set to be managed and give back a handle.
+     *
+     * \todo remove this routine once all indices are represented as
+     *       gmx::index instead of int.
+     *
+     * \note Not created if the internal int type does match index
+     *
+     * \tparam T template parameter to use SFINAE for conditional function
+     *           activation
+     * \tparam U template parameter for conditional function activation
+     *
+     * \param[in] globalAtomIndex Indices of the atoms to be managed
+     * \returns Handle to LocalAtomSet.
+     */
+    template<typename T = void, typename U = std::enable_if_t<!std::is_same_v<int, index>, T>>
+    LocalAtomSet add(ArrayRef<const int> globalAtomIndex);
+#endif
+    /*! \brief Add a new atom set to be managed and give back a handle.
+     *
+     * \param[in] globalAtomIndex Indices of the atoms to be managed
+     * \returns Handle to LocalAtomSet.
+     */
+    LocalAtomSet add(ArrayRef<const index> globalAtomIndex);
+
+    /*! \brief Recalculate local and collective indices from ga2la.
+     * Uses global atom to local atom lookup structure to
+     * update atom indices.
+     */
+    void setIndicesInDomainDecomposition(const gmx_ga2la_t& ga2la);
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/domdec/localtopology.h b/src/include/gromacs/domdec/localtopology.h
new file mode 100644 (file)
index 0000000..14ca253
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file makes declarations used for building
+ * the local topology
+ *
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_LOCALTOPOLOGY_H
+#define GMX_DOMDEC_LOCALTOPOLOGY_H
+
+#include "gromacs/math/vectypes.h"
+
+struct gmx_domdec_t;
+struct gmx_domdec_zones_t;
+struct gmx_localtop_t;
+struct gmx_mtop_t;
+struct t_forcerec;
+struct t_mdatoms;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/*! \brief Generate the local topology and virtual site data
+ *
+ * \returns Total count of bonded interactions in the local topology on this domain */
+int dd_make_local_top(struct gmx_domdec_t*           dd,
+                      struct gmx_domdec_zones_t*     zones,
+                      int                            npbcdim,
+                      matrix                         box,
+                      rvec                           cellsize_min,
+                      const ivec                     npulse,
+                      t_forcerec*                    fr,
+                      gmx::ArrayRef<const gmx::RVec> coordinates,
+                      const gmx_mtop_t&              top,
+                      gmx::ArrayRef<const int64_t>   atomInfo,
+                      gmx_localtop_t*                ltop);
+
+#endif
diff --git a/src/include/gromacs/domdec/localtopologychecker.h b/src/include/gromacs/domdec/localtopologychecker.h
new file mode 100644 (file)
index 0000000..5ea7d63
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file declares functionality for checking whether
+ * local topologies describe all bonded interactions.
+ *
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_LOCALTOPOLOGYCHECKER_H
+#define GMX_DOMDEC_LOCALTOPOLOGYCHECKER_H
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+
+struct gmx_localtop_t;
+struct gmx_mtop_t;
+struct t_commrec;
+struct t_inputrec;
+class t_state;
+
+namespace gmx
+{
+class MDLogger;
+class ObservablesReducerBuilder;
+} // namespace gmx
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief Has responsibility for checking that the local topology distributed
+ * across domains describes a total number of bonded interactions that matches
+ * the system topology
+ *
+ * This uses the ObservablesReducer framework to check that the count
+ * of bonded interactions in the local topology made for each domain
+ * sums to the expected value. Because this check is not urgent, the
+ * communication that it requires is done at the next opportunity,
+ * rather than requiring extra communication. If the check fails, a
+ * fatal error stops execution. In principle, if there was a bug,
+ * GROMACS might crash in the meantime because of the wrong
+ * forces. However as a bug is unlikely we optimize by avoiding
+ * creating extra overhead from communication.
+ */
+class LocalTopologyChecker
+{
+public:
+    /*! \brief Constructor
+     * \param[in]    mdlog            Logger
+     * \param[in]    cr               Communication object
+     * \param[in]    mtop             Global system topology
+     * \param[in]    localTopology    The local topology
+     * \param[in]    localState       The local state
+     * \param[in]    useUpdateGroups  Whether update groups are in use
+     * \param[in]    observablesReducerBuilder  Handle to builder for ObservablesReducer
+     */
+    LocalTopologyChecker(const MDLogger&            mdlog,
+                         const t_commrec*           cr,
+                         const gmx_mtop_t&          mtop,
+                         const gmx_localtop_t&      localTopology,
+                         const t_state&             localState,
+                         bool                       useUpdateGroups,
+                         ObservablesReducerBuilder* observablesReducerBuilder);
+    //! Destructor
+    ~LocalTopologyChecker();
+    //! Move constructor
+    LocalTopologyChecker(LocalTopologyChecker&& other) noexcept;
+    //! Move assignment
+    LocalTopologyChecker& operator=(LocalTopologyChecker&& other) noexcept;
+
+    /*! \brief Set that the local topology should be checked via
+     * observables reduction whenever that reduction is required by
+     * another module. In case of a single domain a direct assertion
+     * is performed instead.
+     */
+    void scheduleCheckOfLocalTopology(int numBondedInteractionsToReduce);
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+#endif
diff --git a/src/include/gromacs/domdec/makebondedlinks.h b/src/include/gromacs/domdec/makebondedlinks.h
new file mode 100644 (file)
index 0000000..7bb6f05
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declares a function that makes the list of links between
+ * atoms connected by bonded interactions.
+ *
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_MAKEBONDEDLINKS_H
+#define GMX_DOMDEC_MAKEBONDEDLINKS_H
+
+struct gmx_domdec_t;
+struct gmx_mtop_t;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+struct AtomInfoWithinMoleculeBlock;
+} // namespace gmx
+
+/*! \brief Generate a list of links between atoms that are linked by bonded interactions
+ *
+ * Also stores whether atoms are linked in \p atomInfoForEachMoleculeBlock.
+ */
+void makeBondedLinks(gmx_domdec_t*                                   dd,
+                     const gmx_mtop_t&                               mtop,
+                     gmx::ArrayRef<gmx::AtomInfoWithinMoleculeBlock> atomInfoForEachMoleculeBlock);
+
+#endif
diff --git a/src/include/gromacs/domdec/mdsetup.h b/src/include/gromacs/domdec/mdsetup.h
new file mode 100644 (file)
index 0000000..0023b79
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Contains functions relevant to simulation setup in MD drivers
+ *
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_MDSETUP_H
+#define GMX_DOMDEC_MDSETUP_H
+
+#include "gromacs/math/vectypes.h"
+
+struct bonded_threading_t;
+struct gmx_localtop_t;
+struct gmx_mtop_t;
+struct gmx_shellfc_t;
+struct t_commrec;
+struct t_forcerec;
+struct t_inputrec;
+struct t_mdatoms;
+
+namespace gmx
+{
+class Constraints;
+class ForceBuffers;
+class MDAtoms;
+class VirtualSitesHandler;
+
+/*! \brief Gets the local shell with domain decomposition
+ *
+ * \param[in]     cr        Communication record
+ * \param[in]     md        The MD atom data
+ * \param[in,out] shfc      The shell/flexible-constraint data
+ */
+void make_local_shells(const t_commrec* cr, const t_mdatoms& md, gmx_shellfc_t* shfc);
+
+/*! \brief Sets atom data for several MD algorithms
+ *
+ * Most MD algorithms require two different setup calls:
+ * one for initialization and parameter setting and one for atom data setup.
+ * This routine sets the atom data for the (locally available) atoms.
+ * This is called at the start of serial runs and during domain decomposition.
+ *
+ * \param[in]     cr         Communication record
+ * \param[in]     inputrec   Input parameter record
+ * \param[in]     top_global The global topology
+ * \param[in,out] top        The local topology
+ * \param[in,out] fr         The force calculation parameter/data record
+ * \param[out]    force      The force buffer
+ * \param[out]    mdAtoms    The MD atom data
+ * \param[in,out] constr     The constraints handler, can be NULL
+ * \param[in,out] vsite      The virtual site data, can be NULL
+ * \param[in,out] shellfc    The shell/flexible-constraint data, can be NULL
+ */
+void mdAlgorithmsSetupAtomData(const t_commrec*     cr,
+                               const t_inputrec&    inputrec,
+                               const gmx_mtop_t&    top_global,
+                               gmx_localtop_t*      top,
+                               t_forcerec*          fr,
+                               ForceBuffers*        force,
+                               MDAtoms*             mdAtoms,
+                               Constraints*         constr,
+                               VirtualSitesHandler* vsite,
+                               gmx_shellfc_t*       shellfc);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/domdec/nsgrid.h b/src/include/gromacs/domdec/nsgrid.h
new file mode 100644 (file)
index 0000000..f5d1f6a
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_NSGRID_H
+#define GMX_MDLIB_NSGRID_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_domdec_t;
+struct gmx_ddbox_t;
+
+/*! \brief Used when estimating the interaction density.
+ *
+ * c_gridStdDevFactor * stddev estimates the interaction density. The
+ * value sqrt(3) == 1.73205080757 gives a uniform load for a
+ * rectangular 3D block of charge groups. For a sphere, it is not a
+ * bad approximation for 4x1x1 up to 4x2x2.
+ *
+ * \todo It would be nicer to use sqrt(3) here, when all code that
+ * includes this file is in C++, which will let us cope with the
+ * std::sqrt<T> on Windows. */
+constexpr real c_gridStdDevFactor = 1.73205080757;
+
+void get_nsgrid_boundaries(int                  nboundeddim,
+                           matrix               box,
+                           struct gmx_domdec_t* dd,
+                           gmx_ddbox_t*         ddbox,
+                           gmx::RVec*           gr0,
+                           gmx::RVec*           gr1,
+                           int                  ncg,
+                           rvec*                cgcm,
+                           rvec                 grid_x0,
+                           rvec                 grid_x1);
+/* Return the ns grid boundaries grid_x0 and grid_x1
+ * and the estimate for the grid density.
+ * For non-bounded dimensions the boundaries are determined
+ * from the average and std.dev. of cgcm.
+ * The are determined from box, unless gr0!=NULL or gr1!=NULL,
+ * then they are taken from gr0 or gr1.
+ * With dd and unbounded dimensions, the proper grid borders for cells
+ * on the edges are determined from cgcm.
+ */
+
+#endif
diff --git a/src/include/gromacs/domdec/options.h b/src/include/gromacs/domdec/options.h
new file mode 100644 (file)
index 0000000..6d5b291
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file declares command-line options for mdrun related to
+ * domain decomposition.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_OPTIONS_H
+#define GMX_DOMDEC_OPTIONS_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*! \brief The options for the domain decomposition MPI task ordering. */
+enum class DdRankOrder
+{
+    select,     //!< First value (needed to cope with command-line parsing)
+    interleave, //!< Interleave the PP and PME ranks
+    pp_pme,     //!< First all PP ranks, all PME rank at the end
+    cartesian,  //!< Use Cartesian communicators for PP, PME and PP-PME
+    Count       //!< The number of options
+};
+
+/*! \brief The options for the dynamic load balancing. */
+enum class DlbOption
+{
+    select,           //!< First value (needed to cope with command-line parsing)
+    turnOnWhenUseful, //!< Turn on DLB when we think it would improve performance
+    no,               //!< Never turn on DLB
+    yes,              //!< Turn on DLB from the start and keep it on
+    Count             //!< The number of options
+};
+
+/*! \brief Options for checking bonded interactions.
+ *
+ * These values must match the bool false and true used for mdrun -ddcheck */
+enum class DDBondedChecking : bool
+{
+    ExcludeZeroLimit = false, //!< Do not check bonded interactions that go to 0 for large distances
+    All              = true   //!< Check all bonded interactions
+};
+
+/*! \libinternal \brief Structure containing all (command line) options for the domain decomposition */
+struct DomdecOptions
+{
+    //! Style of bonded-interaction checking
+    DDBondedChecking ddBondedChecking = DDBondedChecking::All;
+    //! If true, don't communicate all atoms between the non-bonded cut-off and the larger bonded cut-off, but only those that have non-local bonded interactions. This significantly reduces the communication volume.
+    bool useBondedCommunication = true;
+    //! The domain decomposition grid cell count, 0 means let domdec choose based on the number of ranks.
+    ivec numCells = { 0 };
+    //! The number of separate PME ranks requested, -1 = auto.
+    int numPmeRanks = -1;
+    //! Ordering of the PP and PME ranks, values from enum above.
+    DdRankOrder rankOrder = DdRankOrder::interleave;
+    //! The minimum communication range, used for extended the communication range for bonded interactions (nm).
+    real minimumCommunicationRange = 0;
+    //! Communication range for atom involved in constraints (P-LINCS) (nm).
+    real constraintCommunicationRange = 0;
+    //! Dynamic load balancing option, values from enum above.
+    DlbOption dlbOption = DlbOption::turnOnWhenUseful;
+    /*! \brief Fraction in (0,1) by whose reciprocal the initial
+     * DD cell size will be increased in order to provide a margin
+     * in which dynamic load balancing can act, while preserving
+     * the minimum cell size. */
+    real dlbScaling = 0.8;
+    //! String containing a vector of the relative sizes in the x direction of the corresponding DD cells.
+    const char* cellSizeX = nullptr;
+    //! String containing a vector of the relative sizes in the y direction of the corresponding DD cells.
+    const char* cellSizeY = nullptr;
+    //! String containing a vector of the relative sizes in the z direction of the corresponding DD cells.
+    const char* cellSizeZ = nullptr;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/domdec/partition.h b/src/include/gromacs/domdec/partition.h
new file mode 100644 (file)
index 0000000..7f7d815
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file declares functions for mdrun to call to make a new
+ * domain decomposition, and check it.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_PARTITION_H
+#define GMX_DOMDEC_PARTITION_H
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_ddbox_t;
+struct gmx_domdec_t;
+struct gmx_localtop_t;
+struct gmx_mtop_t;
+struct gmx_wallcycle;
+struct pull_t;
+struct t_commrec;
+struct t_forcerec;
+struct t_inputrec;
+struct t_nrnb;
+class t_state;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class Constraints;
+class ForceBuffers;
+class ImdSession;
+class MDAtoms;
+class MDLogger;
+class VirtualSitesHandler;
+
+//! Check whether the DD grid has moved too far for correctness.
+bool check_grid_jump(int64_t step, const gmx_domdec_t* dd, real cutoff, const gmx_ddbox_t* ddbox, bool bFatal);
+
+/*! \brief Print statistics for domain decomposition communication */
+void print_dd_statistics(const t_commrec* cr, const t_inputrec& inputrec, FILE* fplog);
+
+/*! \brief Partition the system over the nodes.
+ *
+ * step is only used for printing error messages.
+ * If bMasterState==TRUE then state_global from the master node is used,
+ * else state_local is redistributed between the nodes.
+ * When f!=NULL, *f will be reallocated to the size of state_local.
+ *
+ * \param[in] fplog         Pointer to the log file
+ * \param[in] mdlog         MD file logger
+ * \param[in] step          Current step
+ * \param[in] cr            Communication record
+ * \param[in] bMasterState  Is it a master state
+ * \param[in] nstglobalcomm Will globals be computed on this step
+ * \param[in] state_global  Global state
+ * \param[in] top_global    Global topology
+ * \param[in] inputrec      Input record
+ * \param[in] imdSession    IMD handle
+ * \param[in] pull_work     Pulling data
+ * \param[in] state_local   Local state
+ * \param[in] f             Force buffer
+ * \param[in] mdAtoms       MD atoms
+ * \param[in] top_local     Local topology
+ * \param[in] fr            Force record
+ * \param[in] vsite         Virtual sites handler
+ * \param[in] constr        Constraints
+ * \param[in] nrnb          Cycle counters
+ * \param[in] wcycle        Timers
+ * \param[in] bVerbose      Be verbose
+ */
+void dd_partition_system(FILE*                     fplog,
+                         const gmx::MDLogger&      mdlog,
+                         int64_t                   step,
+                         const t_commrec*          cr,
+                         bool                      bMasterState,
+                         int                       nstglobalcomm,
+                         t_state*                  state_global,
+                         const gmx_mtop_t&         top_global,
+                         const t_inputrec&         inputrec,
+                         gmx::ImdSession*          imdSession,
+                         pull_t*                   pull_work,
+                         t_state*                  state_local,
+                         gmx::ForceBuffers*        f,
+                         gmx::MDAtoms*             mdAtoms,
+                         gmx_localtop_t*           top_local,
+                         t_forcerec*               fr,
+                         gmx::VirtualSitesHandler* vsite,
+                         gmx::Constraints*         constr,
+                         t_nrnb*                   nrnb,
+                         gmx_wallcycle*            wcycle,
+                         bool                      bVerbose);
+
+} // namespace gmx
+#endif
diff --git a/src/include/gromacs/domdec/redistribute.h b/src/include/gromacs/domdec/redistribute.h
new file mode 100644 (file)
index 0000000..68d8bd9
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares the atom redistribution function.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+#ifndef GMX_DOMDEC_DOMDEC_REDISTRIBUTE_H
+#define GMX_DOMDEC_DOMDEC_REDISTRIBUTE_H
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_domdec_t;
+struct t_forcerec;
+struct t_nrnb;
+class t_state;
+
+/*! \brief Redistribute the atoms to their, new, local domains */
+void dd_redistribute_cg(FILE*         fplog,
+                        int64_t       step,
+                        gmx_domdec_t* dd,
+                        ivec          tric_dir,
+                        t_state*      state,
+                        t_forcerec*   fr,
+                        t_nrnb*       nrnb,
+                        int*          ncg_moved);
+
+#endif
diff --git a/src/include/gromacs/domdec/reversetopology.h b/src/include/gromacs/domdec/reversetopology.h
new file mode 100644 (file)
index 0000000..b8cbe28
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file makes declarations used for building
+ * the reverse topology
+ *
+ * \inlibraryapi
+ * \ingroup module_domdec
+ */
+
+#ifndef GMX_DOMDEC_REVERSETOPOLOGY_H
+#define GMX_DOMDEC_REVERSETOPOLOGY_H
+
+#include <cstdio>
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/mdlib/vsite.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/utility/listoflists.h"
+
+struct gmx_domdec_t;
+struct gmx_ffparams_t;
+struct gmx_mtop_t;
+struct t_atoms;
+struct t_inputrec;
+struct ReverseTopOptions;
+
+namespace gmx
+{
+class VirtualSitesHandler;
+enum class DDBondedChecking : bool;
+} // namespace gmx
+
+//! Options for linking atoms in make_reverse_ilist
+enum class AtomLinkRule
+{
+    FirstAtom,        //!< Link all interactions to the first atom in the atom list
+    AllAtomsInBondeds //!< Link bonded interactions to all atoms involved, don't link vsites
+};
+
+struct MolblockIndices
+{
+    int a_start;
+    int a_end;
+    int natoms_mol;
+    int type;
+};
+
+struct reverse_ilist_t
+{
+    std::vector<int> index;              /* Index for each atom into il          */
+    std::vector<int> il;                 /* ftype|type|a0|...|an|ftype|...       */
+    int              numAtomsInMolecule; /* The number of atoms in this molecule */
+};
+
+/*! \internal \brief Struct for thread local work data for local topology generation */
+struct thread_work_t
+{
+    /*! \brief Constructor
+     *
+     * \param[in] ffparams  The interaction parameters, the lifetime of the created object should not exceed the lifetime of the passed parameters
+     */
+    thread_work_t(const gmx_ffparams_t& ffparams) : idef(ffparams) {}
+
+    InteractionDefinitions         idef;               /**< Partial local topology */
+    std::unique_ptr<gmx::VsitePbc> vsitePbc = nullptr; /**< vsite PBC structure */
+    int numBondedInteractions               = 0; /**< The number of bonded interactions observed */
+    gmx::ListOfLists<int> excl;                  /**< List of exclusions */
+    int                   excl_count = 0;        /**< The total exclusion count for \p excl */
+};
+
+/*! \internal \brief Options for setting up gmx_reverse_top_t */
+struct ReverseTopOptions
+{
+    //! Constructor, constraints and settles are not including with a single argument
+    ReverseTopOptions(gmx::DDBondedChecking ddBondedChecking,
+                      bool                  includeConstraints = false,
+                      bool                  includeSettles     = false) :
+        ddBondedChecking(ddBondedChecking),
+        includeConstraints(includeConstraints),
+        includeSettles(includeSettles)
+    {
+    }
+
+    //! \brief For which bonded interactions to check assignments
+    const gmx::DDBondedChecking ddBondedChecking;
+    //! \brief Whether constraints are stored in this reverse top
+    const bool includeConstraints;
+    //! \brief Whether settles are stored in this reverse top
+    const bool includeSettles;
+};
+
+//! \internal \brief Reverse topology class
+class gmx_reverse_top_t
+{
+public:
+    //! Constructor
+    gmx_reverse_top_t(const gmx_mtop_t& mtop, bool useFreeEnergy, const ReverseTopOptions& reverseTopOptions);
+    //! Destructor
+    ~gmx_reverse_top_t();
+
+    //! Gets the options that configured the construction
+    const ReverseTopOptions& options() const;
+
+    //! Gets the interaction list for the given molecule type
+    const reverse_ilist_t& interactionListForMoleculeType(int moleculeType) const;
+
+    //! Returns the molecule block indices
+    gmx::ArrayRef<const MolblockIndices> molblockIndices() const;
+    //! Returns whether the reverse topology describes intermolecular interactions
+    bool hasIntermolecularInteractions() const;
+    //! Gets the interaction list for any intermolecular interactions
+    const reverse_ilist_t& interactionListForIntermolecularInteractions() const;
+    //! Returns whether the reverse topology describes interatomic interactions
+    bool hasInterAtomicInteractions() const;
+    //! Returns whether there are interactions of type F_POSRES and/or F_FBPOSRES
+    bool hasPositionRestraints() const;
+    //! Returns the per-thread working structures for making the local topology
+    gmx::ArrayRef<thread_work_t> threadWorkObjects() const;
+    //! Returns whether the local topology listed-forces interactions should be sorted
+    bool doListedForcesSorting() const;
+
+    //! Private implementation definition
+    struct Impl;
+    //! Private implementation declaration
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \brief Returns the number of atom entries for il in gmx_reverse_top_t */
+int nral_rt(int ftype);
+
+/*! \brief Return whether interactions of type \p ftype need to be assigned exactly once */
+bool dd_check_ftype(int ftype, const ReverseTopOptions& rtOptions);
+
+//! Molecular topology indices of a global molecule a global atom belongs to
+struct MolecularTopologyAtomIndices
+{
+    //! The index of the molecule block
+    int blockIndex;
+    //! The molecule type
+    int moleculeType;
+    //! The index of the molecule in the block
+    int moleculeIndex;
+    //! The index of the atom in the molecule
+    int atomIndex;
+};
+
+//! Return global topology molecule information for global atom index \p globalAtomIndex
+MolecularTopologyAtomIndices globalAtomIndexToMoltypeIndices(gmx::ArrayRef<const MolblockIndices> molblockIndices,
+                                                             int globalAtomIndex);
+
+/*! \brief Make the reverse ilist: a list of bonded interactions linked to atoms */
+void make_reverse_ilist(const InteractionLists&  ilist,
+                        const t_atoms*           atoms,
+                        const ReverseTopOptions& rtOptions,
+                        AtomLinkRule             atomLinkRule,
+                        reverse_ilist_t*         ril_mt);
+
+/*! \brief Generate and store the reverse topology */
+void dd_make_reverse_top(FILE*                           fplog,
+                         gmx_domdec_t*                   dd,
+                         const gmx_mtop_t&               mtop,
+                         const gmx::VirtualSitesHandler* vsite,
+                         const t_inputrec&               inputrec,
+                         gmx::DDBondedChecking           ddBondedChecking);
+
+#endif
diff --git a/src/include/gromacs/domdec/utility.h b/src/include/gromacs/domdec/utility.h
new file mode 100644 (file)
index 0000000..09c8b9a
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares utility functions used in the domain decomposition module.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+#ifndef GMX_DOMDEC_DOMDEC_UTILITY_H
+#define GMX_DOMDEC_DOMDEC_UTILITY_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/atominfo.h"
+
+#include "domdec_internal.h"
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/*! \brief Returns true if the DLB state indicates that the balancer is on. */
+static inline bool isDlbOn(const gmx_domdec_comm_t* comm)
+{
+    return (comm->dlbState == DlbState::onCanTurnOff || comm->dlbState == DlbState::onUser);
+};
+
+/*! \brief Returns true if the DLB state indicates that the balancer is off/disabled.
+ */
+static inline bool isDlbDisabled(const DlbState& dlbState)
+{
+    return (dlbState == DlbState::offUser || dlbState == DlbState::offForever);
+};
+
+/*! \brief Returns true if the DLB state indicates that the balancer is off/disabled.
+ */
+static inline bool isDlbDisabled(const gmx_domdec_comm_t* comm)
+{
+    return isDlbDisabled(comm->dlbState);
+};
+
+/*! \brief Returns the character, x/y/z, corresponding to dimension dim */
+char dim2char(int dim);
+
+/*! \brief Sets matrix to convert from Cartesian to lattice coordinates */
+void make_tric_corr_matrix(int npbcdim, const matrix box, matrix tcm);
+
+/*! \brief Ensure box obeys the screw restrictions, fatal error if not */
+void check_screw_box(const matrix box);
+
+/*! \brief Return the atom information flags for atom a */
+static inline int ddGetAtomInfo(gmx::ArrayRef<const gmx::AtomInfoWithinMoleculeBlock> atomInfoForEachMoleculeBlock,
+                                int                                                   a)
+{
+    size_t index = 0;
+    while (a >= atomInfoForEachMoleculeBlock[index].indexOfLastAtomInMoleculeBlock)
+    {
+        index++;
+    }
+    const gmx::AtomInfoWithinMoleculeBlock& atomInfoOfMoleculeBlock = atomInfoForEachMoleculeBlock[index];
+
+    return atomInfoOfMoleculeBlock.atomInfo[(a - atomInfoOfMoleculeBlock.indexOfFirstAtomInMoleculeBlock)
+                                            % atomInfoOfMoleculeBlock.atomInfo.size()];
+};
+
+/*! \brief Returns the number of MD steps for which load has been recorded */
+static inline int dd_load_count(const gmx_domdec_comm_t* comm)
+{
+    return (comm->ddSettings.eFlop ? comm->flop_n : comm->cycl_n[ddCyclF]);
+}
+
+/*! \brief Ensure fr and state can hold numAtoms atoms
+ *
+ * \param[in]  fr        Force record
+ * \param[in]  state     Current state
+ * \param[out] numAtoms  Number of atoms
+ */
+void dd_resize_atominfo_and_state(t_forcerec* fr, t_state* state, int numAtoms);
+
+/*! \brief Returns a domain-to-domain cutoff distance given an atom-to-atom cutoff */
+static inline real atomToAtomIntoDomainToDomainCutoff(const DDSystemInfo& systemInfo, real cutoff)
+{
+    if (systemInfo.useUpdateGroups)
+    {
+        cutoff += 2 * systemInfo.maxUpdateGroupRadius;
+    }
+
+    return cutoff;
+}
+
+/*! \brief Returns an atom-to-domain cutoff distance given a domain-to-domain cutoff */
+static inline real domainToDomainIntoAtomToDomainCutoff(const DDSystemInfo& systemInfo, real cutoff)
+{
+    if (systemInfo.useUpdateGroups)
+    {
+        cutoff -= systemInfo.maxUpdateGroupRadius;
+    }
+
+    return cutoff;
+}
+
+#endif
diff --git a/src/include/gromacs/essentialdynamics/edsam.h b/src/include/gromacs/essentialdynamics/edsam.h
new file mode 100644 (file)
index 0000000..957f8c8
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief
+ * Declares functions to calculate both essential dynamics constraints
+ * as well as flooding potentials and forces.
+ *
+ * \authors Bert de Groot <bgroot@gwdg.de>, Oliver Lange <oliver.lange@tum.de>,
+ * Carsten Kutzner <ckutzne@gwdg.de>
+ *
+ * \inlibraryapi
+ */
+#ifndef GMX_ESSENTIALDYNAMICS_EDSAM_H
+#define GMX_ESSENTIALDYNAMICS_EDSAM_H
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+
+/*! \brief Abstract type for essential dynamics
+ *
+ * The main type is defined only in edsam.cpp
+ */
+struct gmx_edsam;
+struct gmx_domdec_t;
+struct gmx_mtop_t;
+struct gmx_output_env_t;
+struct ObservablesHistory;
+struct t_commrec;
+struct t_filenm;
+struct t_inputrec;
+class t_state;
+
+namespace gmx
+{
+enum class StartingBehavior;
+class Constraints;
+template<typename>
+class ArrayRef;
+
+class EssentialDynamics
+{
+public:
+    EssentialDynamics();
+    ~EssentialDynamics();
+
+    /*! \brief Getter for working data
+     *
+     * This is needed while the module is still under
+     * construction. */
+    gmx_edsam* getLegacyED();
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+class MDLogger;
+} // namespace gmx
+
+/*! \brief Applies essential dynamics constrains as defined in the .edi input file.
+ *
+ * \param ir                MD input parameter record.
+ * \param step              Number of the time step.
+ * \param cr                Data needed for MPI communication.
+ * \param coords            The local positions on this processor.
+ * \param velocities        The local velocities.
+ * \param box               The simulation box.
+ * \param ed                The essential dynamics data.
+ */
+void do_edsam(const t_inputrec*        ir,
+              int64_t                  step,
+              const t_commrec*         cr,
+              gmx::ArrayRef<gmx::RVec> coords,
+              gmx::ArrayRef<gmx::RVec> velocities,
+              const matrix             box,
+              gmx_edsam*               ed);
+
+
+/*! \brief Initializes the essential dynamics and flooding module.
+ *
+ * \param mdlog             Logger.
+ * \param ediFileName       Essential dynamics input file.
+ * \param edoFileName       Output file for essential dynamics data.
+ * \param mtop              Molecular topology.
+ * \param ir                MD input parameter record.
+ * \param cr                Data needed for MPI communication.
+ * \param constr            Data structure keeping the constraint information.
+ * \param globalState       The global state, only used on the master rank.
+ * \param oh                The observables history container.
+ * \param oenv              The output environment information.
+ * \param startingBehavior  Describes whether this is a restart appending to output files
+ *
+ * \returns                 A pointer to the ED data structure.
+ */
+std::unique_ptr<gmx::EssentialDynamics> init_edsam(const gmx::MDLogger&    mdlog,
+                                                   const char*             ediFileName,
+                                                   const char*             edoFileName,
+                                                   const gmx_mtop_t&       mtop,
+                                                   const t_inputrec&       ir,
+                                                   const t_commrec*        cr,
+                                                   gmx::Constraints*       constr,
+                                                   const t_state*          globalState,
+                                                   ObservablesHistory*     oh,
+                                                   const gmx_output_env_t* oenv,
+                                                   gmx::StartingBehavior   startingBehavior);
+
+/*! \brief Make a selection of the home atoms for the ED groups.
+ *
+ * Should be called at every domain decomposition.
+ *
+ * \param dd                Domain decomposition data.
+ * \param ed                Essential dynamics and flooding data.
+ */
+void dd_make_local_ed_indices(gmx_domdec_t* dd, gmx_edsam* ed);
+
+
+/*! \brief Evaluate the flooding potential(s) and forces as requested in the .edi input file.
+ *
+ * \param cr                Data needed for MPI communication.
+ * \param ir                MD input parameter record.
+ * \param coords            Positions on the local processor.
+ * \param force             Forcefield forces to which the flooding forces are added.
+ * \param ed                The essential dynamics data.
+ * \param box               The simulation box.
+ * \param step              Number of the time step.
+ * \param bNS               Are we in a neighbor searching step?
+ */
+void do_flood(const t_commrec*               cr,
+              const t_inputrec&              ir,
+              gmx::ArrayRef<const gmx::RVec> coords,
+              gmx::ArrayRef<gmx::RVec>       force,
+              gmx_edsam*                     ed,
+              const matrix                   box,
+              int64_t                        step,
+              bool                           bNS);
+
+#endif
diff --git a/src/include/gromacs/ewald/calculate_spline_moduli.h b/src/include/gromacs/ewald/calculate_spline_moduli.h
new file mode 100644 (file)
index 0000000..abdf422
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_EWALD_CALCULATE_SPLINE_MODULI_H
+#define GMX_EWALD_CALCULATE_SPLINE_MODULI_H
+
+#include "spline_vectors.h"
+
+/* Calulate plain SPME B-spline interpolation */
+void make_bspline_moduli(splinevec bsp_mod, int nx, int ny, int nz, int order);
+
+/* Calculate the P3M B-spline moduli */
+void make_p3m_bspline_moduli(splinevec bsp_mod, int nx, int ny, int nz, int order);
+
+#endif
diff --git a/src/include/gromacs/ewald/ewald.h b/src/include/gromacs/ewald/ewald.h
new file mode 100644 (file)
index 0000000..809bc8f
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \defgroup module_ewald Ewald-family treatments of long-ranged forces
+ * \ingroup group_mdrun
+ *
+ * \brief Computes energies and forces for long-ranged interactions
+ * using the Ewald decomposition. Includes plain Ewald, PME, P3M for
+ * Coulomb, PME for Lennard-Jones, load-balancing for PME, and
+ * supporting code.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Erik Lindahl <erik@kth.se>
+ * \author Roland Schulz <roland@rschulz.eu>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Christan Wennberg <cwennberg@kth.se>
+ */
+
+/*! \libinternal \file
+ *
+ * \brief This file contains function declarations necessary for
+ * computing energies and forces for the plain-Ewald long-ranged part,
+ * and the correction for overall system charge for all Ewald-family
+ * methods.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_EWALD_H
+#define GMX_EWALD_EWALD_H
+
+#include <stdio.h>
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+struct t_commrec;
+struct t_forcerec;
+struct t_inputrec;
+struct t_complex;
+enum class FreeEnergyPerturbationType : int;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+struct gmx_ewald_tab_t
+{
+    gmx_ewald_tab_t(const t_inputrec& ir, FILE* fp);
+
+    ~gmx_ewald_tab_t();
+
+    int nx;
+    int ny;
+    int nz;
+    int kmax;
+
+    std::vector<t_complex> tab_xy;
+    std::vector<t_complex> tab_qxyz;
+};
+
+/*! \brief Do the long-ranged part of an Ewald calculation */
+real do_ewald(bool                           havePbcXY2Walls,
+              real                           wallEwaldZfac,
+              real                           epsilonR,
+              FreeEnergyPerturbationType     freeEnergyPerturbationType,
+              gmx::ArrayRef<const gmx::RVec> coords,
+              gmx::ArrayRef<gmx::RVec>       forces,
+              gmx::ArrayRef<const real>      chargeA,
+              gmx::ArrayRef<const real>      chargeB,
+              const matrix                   box,
+              const t_commrec*               commrec,
+              int                            natoms,
+              matrix                         lrvir,
+              real                           ewaldcoeff,
+              real                           lambda,
+              real*                          dvdlambda,
+              gmx_ewald_tab_t*               et);
+
+/*! \brief Calculate the correction to the Ewald sum, due to a net system
+ * charge.
+ *
+ * Should only be called on one thread. */
+real ewald_charge_correction(const t_commrec*            commrec,
+                             real                        epsilonR,
+                             real                        ewaldcoeffQ,
+                             gmx::ArrayRef<const double> qsum,
+                             real                        lambda,
+                             const matrix                box,
+                             real*                       dvdlambda,
+                             tensor                      vir);
+
+#endif
diff --git a/src/include/gromacs/ewald/ewald_utils.h b/src/include/gromacs/ewald/ewald_utils.h
new file mode 100644 (file)
index 0000000..20db56f
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2017,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declares utility functions related to Ewald.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Szilárd Páll <pall.szilard@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_ewald
+ */
+#ifndef GMX_EWALD_UTILS_H
+#define GMX_EWALD_UTILS_H
+
+#include "gromacs/math/vec.h"
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+/*! \brief Computes the Ewald splitting coefficient for Coulomb
+ *
+ * Returns a value of beta that satisfies rtol > erfc(beta * rc)
+ * (and is very close to equality). That value is used the same way in
+ * all Coulomb-based Ewald methods.
+ *
+ * \param[in] rc    Cutoff radius
+ * \param[in] rtol  Required maximum value of the short-ranged
+ *                  potential at the cutoff (ie. ewald-rtol)
+ * \return          The value of the splitting coefficient that
+ *                  produces the required dtol at rc.
+ */
+real calc_ewaldcoeff_q(real rc, real rtol);
+
+/*! \brief Computes the Ewald splitting coefficient for LJ
+ *
+ * Returns a value of beta that satisfies dtol > erfc(beta * rc) * (1
+ * + beta^2 * rc^2 + 0.5 * beta^4 * rc^4) (and is very close to
+ * equality), which is used in LJ-PME.
+ *
+ * \param[in] rc    Cutoff radius
+ * \param[in] rtol  Required maximum value of the short-ranged
+ *                  potential at the cutoff (ie. ewald-rtol-lj)
+ * \return          The value of the splitting coefficient that
+ *                  produces the required dtol at rc.
+ */
+real calc_ewaldcoeff_lj(real rc, real rtol);
+
+
+/*! \libinternal \brief Class to handle box scaling for Ewald and PME.
+ *
+ * At construction contents of inputrec determine whether scaling is necessary
+ * as well as the scaling factor used. Later, the scaleBox method can be used
+ * to apply the appropriate scaling (if needed) for Ewald-based methods.
+ *
+ */
+class EwaldBoxZScaler
+{
+
+private:
+    bool scaleWithWalls_; /**< True if the simulation uses two walls and the box needs to be scaled in PME */
+    real scalingFactor_; /**< Box The scaling factor PME uses with walls */
+
+public:
+    EwaldBoxZScaler() = delete;
+
+    /*! \brief Constructor that takes the input record to initialize Ewald box scaling appropriately. */
+    EwaldBoxZScaler(bool havePbcXY2Walls, real wallEwaldZfac)
+    {
+        if (havePbcXY2Walls)
+        {
+            scaleWithWalls_ = true;
+            scalingFactor_  = wallEwaldZfac;
+        }
+        else
+        {
+            scaleWithWalls_ = false;
+            scalingFactor_  = 1;
+        }
+    }
+
+    /*! \brief Copy and scale the box for PME.
+     *
+     * When PME is used with 2D periodicity and two walls, the
+     * copy of the \p box passed is scaled with the Z scaling factor.
+     *
+     * \param[in] box        The current box matrix
+     * \param[out] scaledBox Scaled copy of the box matrix.
+     */
+    void scaleBox(const matrix box, matrix scaledBox) const
+    {
+        GMX_ASSERT(box, "invalid source box pointer");
+        GMX_ASSERT(scaledBox, "invalid target box pointer");
+
+        copy_mat(box, scaledBox);
+        if (scaleWithWalls_)
+        {
+            svmul(scalingFactor_, scaledBox[ZZ], scaledBox[ZZ]);
+        }
+    }
+};
+
+#endif
diff --git a/src/include/gromacs/ewald/long_range_correction.h b/src/include/gromacs/ewald/long_range_correction.h
new file mode 100644 (file)
index 0000000..b70e3b7
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file contains function declarations necessary for
+ * computing energies and forces for the PME long-ranged part (Coulomb
+ * and LJ).
+ *
+ * \author Erik Lindahl <erik@kth.se>
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_LONG_RANGE_CORRECTION_H
+#define GMX_EWALD_LONG_RANGE_CORRECTION_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+struct t_commrec;
+struct t_forcerec;
+struct t_inputrec;
+enum class EwaldGeometry : int;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/*! \brief Calculate long-range Ewald correction terms.
+ *
+ * Calculate correction for electrostatic surface dipole terms.
+ */
+void ewald_LRcorrection(int                            numAtomsLocal,
+                        const t_commrec*               commrec,
+                        int                            numThreads,
+                        int                            thread,
+                        real                           epsilonR,
+                        gmx::ArrayRef<const double>    qsum,
+                        EwaldGeometry                  ewaldGeometry,
+                        real                           epsilonSurface,
+                        bool                           havePbcXY2Walls,
+                        real                           wallEwaldZfac,
+                        gmx::ArrayRef<const real>      chargeA,
+                        gmx::ArrayRef<const real>      chargeB,
+                        bool                           bHaveChargePerturbed,
+                        gmx::ArrayRef<const gmx::RVec> coords,
+                        const matrix                   box,
+                        gmx::ArrayRef<const gmx::RVec> mu_tot,
+                        gmx::ArrayRef<gmx::RVec>       forces,
+                        real*                          Vcorr_q,
+                        real                           lambda_q,
+                        real*                          dvdlambda_q);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme.cuh b/src/include/gromacs/ewald/pme.cuh
new file mode 100644 (file)
index 0000000..af0e258
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ * \brief This file defines the PME CUDA-specific kernel parameter data structure.
+ * \todo Rename the file (pme-gpu-types.cuh?), reconsider inheritance approach.
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ */
+
+#ifndef GMX_EWALD_PME_CUH
+#define GMX_EWALD_PME_CUH
+
+#include "gromacs/math/vectypes.h" // for DIM
+
+#include "pme_gpu_constants.h"
+#include "pme_gpu_internal.h" // for GridOrdering
+#include "pme_gpu_types.h"
+
+/*! \brief \internal
+ * An alias for PME parameters in CUDA.
+ * \todo Remove if we decide to unify CUDA and OpenCL
+ */
+struct PmeGpuCudaKernelParams : PmeGpuKernelParamsBase
+{
+    // Place CUDA-specific stuff here
+};
+
+#endif
diff --git a/src/include/gromacs/ewald/pme.h b/src/include/gromacs/ewald/pme.h
new file mode 100644 (file)
index 0000000..93145d4
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file contains function declarations necessary for
+ * computing energies and forces for the PME long-ranged part (Coulomb
+ * and LJ).
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_H
+#define GMX_EWALD_PME_H
+
+#include <string>
+#include <vector>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gpu_macros.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_hw_info_t;
+struct t_commrec;
+struct t_inputrec;
+struct t_nrnb;
+struct PmeGpu;
+struct gmx_wallclock_gpu_pme_t;
+struct gmx_enerdata_t;
+struct gmx_mtop_t;
+struct gmx_pme_t;
+struct gmx_wallcycle;
+struct NumPmeDomains;
+
+class DeviceContext;
+class DeviceStream;
+enum class GpuTaskCompletion;
+class PmeGpuProgram;
+class GpuEventSynchronizer;
+
+/*! \brief Hack to selectively enable some parts of PME during unit testing.
+ *
+ * Set to \c false by default. If any of the tests sets it to \c true, it will
+ * make the compatibility check consider PME to be supported in SYCL builds.
+ *
+ * Currently we don't have proper PME implementation with SYCL, but we still want
+ * to run tests for some of the kernels.
+ *
+ * \todo Remove after #3927 is done and PME is fully enabled in SYCL builds.
+ */
+//NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+extern bool g_allowPmeWithSyclForTesting;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class ForceWithVirial;
+class MDLogger;
+enum class PinningPolicy : int;
+class StepWorkload;
+
+/*! \libinternal \brief Class for managing usage of separate PME-only ranks
+ *
+ * Used for checking if some parts of the code could not use PME-only ranks
+ *
+ */
+class SeparatePmeRanksPermitted
+{
+public:
+    //! Disables PME ranks permitted flag with a reason
+    void disablePmeRanks(const std::string& reason);
+    //! Return status of PME ranks usage
+    bool permitSeparatePmeRanks() const;
+    //! Returns all reasons, for not using PME ranks
+    std::string reasonsWhyDisabled() const;
+
+private:
+    //! Flag that informs whether simualtion could use dedicated PME ranks
+    bool permitSeparatePmeRanks_ = true;
+    //! Storage for all reasons, why PME ranks could not be used
+    std::vector<std::string> reasons_;
+};
+
+} // namespace gmx
+
+enum
+{
+    GMX_SUM_GRID_FORWARD,
+    GMX_SUM_GRID_BACKWARD
+};
+
+/*! \brief Possible PME codepaths on a rank.
+ * \todo: make this enum class with gmx_pme_t C++ refactoring
+ */
+enum class PmeRunMode
+{
+    None,  //!< No PME task is done
+    CPU,   //!< Whole PME computation is done on CPU
+    GPU,   //!< Whole PME computation is done on GPU
+    Mixed, //!< Mixed mode: only spread and gather run on GPU; FFT and solving are done on CPU.
+};
+
+/*! \brief Return the smallest allowed PME grid size for \p pmeOrder */
+int minimalPmeGridSize(int pmeOrder);
+
+//! Return whether the grid of \c pme is identical to \c grid_size.
+bool gmx_pme_grid_matches(const gmx_pme_t& pme, const ivec grid_size);
+
+/*! \brief Check restrictions on pme_order and the PME grid nkx,nky,nkz.
+ *
+ * With errorsAreFatal=true, an exception or fatal error is generated
+ * on violation of restrictions.
+ * With errorsAreFatal=false, false is returned on violation of restrictions.
+ * When all restrictions are obeyed, true is returned.
+ * Argument useThreads tells if any MPI rank doing PME uses more than 1 threads.
+ * If at calling useThreads is unknown, pass true for conservative checking.
+ *
+ * The PME GPU restrictions are checked separately during pme_gpu_init().
+ */
+bool gmx_pme_check_restrictions(int  pme_order,
+                                int  nkx,
+                                int  nky,
+                                int  nkz,
+                                int  numPmeDomainsAlongX,
+                                bool useThreads,
+                                bool errorsAreFatal);
+
+/*! \brief Construct PME data
+ *
+ * \throws   gmx::InconsistentInputError if input grid sizes/PME order are inconsistent.
+ * \returns  Pointer to newly allocated and initialized PME data.
+ *
+ * \todo We should evolve something like a \c GpuManager that holds \c
+ * DeviceInformation* and \c PmeGpuProgram* and perhaps other
+ * related things whose lifetime can/should exceed that of a task (or
+ * perhaps task manager). See Issue #2522.
+ */
+gmx_pme_t* gmx_pme_init(const t_commrec*     cr,
+                        const NumPmeDomains& numPmeDomains,
+                        const t_inputrec*    ir,
+                        gmx_bool             bFreeEnergy_q,
+                        gmx_bool             bFreeEnergy_lj,
+                        gmx_bool             bReproducible,
+                        real                 ewaldcoeff_q,
+                        real                 ewaldcoeff_lj,
+                        int                  nthread,
+                        PmeRunMode           runMode,
+                        PmeGpu*              pmeGpu,
+                        const DeviceContext* deviceContext,
+                        const DeviceStream*  deviceStream,
+                        const PmeGpuProgram* pmeGpuProgram,
+                        const gmx::MDLogger& mdlog);
+
+/*! \brief As gmx_pme_init, but takes most settings, except the grid/Ewald coefficients, from
+ * pme_src. This is only called when the PME cut-off/grid size changes.
+ */
+void gmx_pme_reinit(gmx_pme_t**       pmedata,
+                    const t_commrec*  cr,
+                    gmx_pme_t*        pme_src,
+                    const t_inputrec* ir,
+                    const ivec        grid_size,
+                    real              ewaldcoeff_q,
+                    real              ewaldcoeff_lj);
+
+/*! \brief Destroys the PME data structure.*/
+void gmx_pme_destroy(gmx_pme_t* pme);
+
+/*! \brief Do a PME calculation on a CPU for the long range electrostatics and/or LJ.
+ *
+ * Computes the PME forces and the energy and viral, when requested,
+ * for all atoms in \p coordinates. Forces, when requested, are added
+ * to the buffer \p forces, which is allowed to contain more elements
+ * than the number of elements in \p coordinates.
+ * The meaning of \p flags is defined above, and determines which
+ * parts of the calculation are performed.
+ *
+ * \return 0 indicates all well, non zero is an error code.
+ */
+int gmx_pme_do(struct gmx_pme_t*              pme,
+               gmx::ArrayRef<const gmx::RVec> coordinates,
+               gmx::ArrayRef<gmx::RVec>       forces,
+               gmx::ArrayRef<const real>      chargeA,
+               gmx::ArrayRef<const real>      chargeB,
+               gmx::ArrayRef<const real>      c6A,
+               gmx::ArrayRef<const real>      c6B,
+               gmx::ArrayRef<const real>      sigmaA,
+               gmx::ArrayRef<const real>      sigmaB,
+               const matrix                   box,
+               const t_commrec*               cr,
+               int                            maxshift_x,
+               int                            maxshift_y,
+               t_nrnb*                        nrnb,
+               gmx_wallcycle*                 wcycle,
+               matrix                         vir_q,
+               matrix                         vir_lj,
+               real*                          energy_q,
+               real*                          energy_lj,
+               real                           lambda_q,
+               real                           lambda_lj,
+               real*                          dvdlambda_q,
+               real*                          dvdlambda_lj,
+               const gmx::StepWorkload&       stepWork);
+
+/*! \brief Calculate the PME grid energy V for n charges.
+ *
+ * The potential (found in \p pme) must have been found already with a
+ * call to gmx_pme_do(). Note that the charges are not spread on the grid in the
+ * pme struct. Currently does not work in parallel or with free
+ * energy.
+ */
+real gmx_pme_calc_energy(gmx_pme_t* pme, gmx::ArrayRef<const gmx::RVec> x, gmx::ArrayRef<const real> q);
+
+/*! \brief
+ * This function updates the local atom data on GPU after DD (charges, coordinates, etc.).
+ * TODO: it should update the PME CPU atom data as well.
+ * (currently PME CPU call gmx_pme_do() gets passed the input pointers for each computation).
+ *
+ * \param[in,out] pme        The PME structure.
+ * \param[in]     numAtoms   The number of particles.
+ * \param[in]     chargesA   The pointer to the array of particle charges in the normal state or FEP
+ * state A. Can be nullptr if PME is not performed on the GPU.
+ * \param[in]     chargesB   The pointer to the array of particle charges in state B. Only used if
+ * charges are perturbed and can otherwise be nullptr.
+ */
+void gmx_pme_reinit_atoms(gmx_pme_t*                pme,
+                          int                       numAtoms,
+                          gmx::ArrayRef<const real> chargesA,
+                          gmx::ArrayRef<const real> chargesB);
+
+/* A block of PME GPU functions */
+
+/*! \brief Checks whether the GROMACS build allows to run PME on GPU.
+ * TODO: this partly duplicates an internal PME assert function
+ * pme_gpu_check_restrictions(), except that works with a
+ * formed gmx_pme_t structure. Should that one go away/work with inputrec?
+ *
+ * \param[out] error   If non-null, the error message when PME is not supported on GPU.
+ *
+ * \returns true if PME can run on GPU on this build, false otherwise.
+ */
+bool pme_gpu_supports_build(std::string* error);
+
+/*! \brief Checks whether the detected (GPU) hardware allows to run PME on GPU.
+ *
+ * \param[in]  hwinfo  Information about the detected hardware
+ * \param[out] error   If non-null, the error message when PME is not supported on GPU.
+ *
+ * \returns true if PME can run on GPU on this build, false otherwise.
+ */
+bool pme_gpu_supports_hardware(const gmx_hw_info_t& hwinfo, std::string* error);
+
+/*! \brief Checks whether the input system allows to run PME on GPU.
+ * TODO: this partly duplicates an internal PME assert function
+ * pme_gpu_check_restrictions(), except that works with a
+ * formed gmx_pme_t structure. Should that one go away/work with inputrec?
+ *
+ * \param[in]  ir     Input system.
+ * \param[out] error  If non-null, the error message if the input is not supported on GPU.
+ *
+ * \returns true if PME can run on GPU with this input, false otherwise.
+ */
+bool pme_gpu_supports_input(const t_inputrec& ir, std::string* error);
+
+/*! \brief
+ * Returns the active PME codepath (CPU, GPU, mixed).
+ * \todo This is a rather static data that should be managed by the higher level task scheduler.
+ *
+ * \param[in]  pme            The PME data structure.
+ * \returns active PME codepath.
+ */
+PmeRunMode pme_run_mode(const gmx_pme_t* pme);
+
+/*! \libinternal \brief
+ * Return the pinning policy appropriate for this build configuration
+ * for relevant buffers used for PME task on this rank (e.g. running
+ * on a GPU). */
+gmx::PinningPolicy pme_get_pinning_policy();
+
+/*! \brief
+ * Tells if PME is enabled to run on GPU (not necessarily active at the moment).
+ * \todo This is a rather static data that should be managed by the hardware assignment manager.
+ * For now, it is synonymous with the active PME codepath (in the absence of dynamic switching).
+ *
+ * \param[in]  pme            The PME data structure.
+ * \returns true if PME can run on GPU, false otherwise.
+ */
+inline bool pme_gpu_task_enabled(const gmx_pme_t* pme)
+{
+    return (pme != nullptr) && (pme_run_mode(pme) != PmeRunMode::CPU);
+}
+
+/*! \brief Returns the block size requirement
+ *
+ * The GPU version of PME requires that the coordinates array have a
+ * size divisible by the returned number.
+ *
+ * \param[in]  pme  The PME data structure.
+ */
+GPU_FUNC_QUALIFIER int pme_gpu_get_block_size(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme))
+        GPU_FUNC_TERM_WITH_RETURN(0);
+
+// The following functions are all the PME GPU entry points,
+// currently inlining to nothing on non-CUDA builds.
+
+/*! \brief
+ * Resets the PME GPU timings. To be called at the reset step.
+ *
+ * \param[in] pme            The PME structure.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_reset_timings(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme)) GPU_FUNC_TERM;
+
+/*! \brief
+ * Copies the PME GPU timings to the gmx_wallclock_gpu_pme_t structure (for log output). To be called at the run end.
+ *
+ * \param[in] pme               The PME structure.
+ * \param[in] timings           The gmx_wallclock_gpu_pme_t structure.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_get_timings(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme),
+                                            gmx_wallclock_gpu_pme_t* GPU_FUNC_ARGUMENT(timings)) GPU_FUNC_TERM;
+
+/* The main PME GPU functions */
+
+/*! \brief
+ * Prepares PME on GPU computation (updating the box if needed)
+ * \param[in] pme               The PME data structure.
+ * \param[in] box               The unit cell box.
+ * \param[in] wcycle            The wallclock counter.
+ * \param[in] stepWork          The required work for this simulation step
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_prepare_computation(gmx_pme_t*     GPU_FUNC_ARGUMENT(pme),
+                                                    const matrix   GPU_FUNC_ARGUMENT(box),
+                                                    gmx_wallcycle* GPU_FUNC_ARGUMENT(wcycle),
+                                                    const gmx::StepWorkload& GPU_FUNC_ARGUMENT(stepWork)) GPU_FUNC_TERM;
+
+/*! \brief
+ * Launches first stage of PME on GPU - spreading kernel.
+ *
+ * \param[in] pme                The PME data structure.
+ * \param[in] xReadyOnDevice     Event synchronizer indicating that the coordinates
+ * are ready in the device memory; nullptr allowed only on separate PME ranks.
+ * \param[in] wcycle             The wallclock counter.
+ * \param[in] lambdaQ            The Coulomb lambda of the current state of the
+ * system. Only used if FEP of Coulomb is active.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_launch_spread(gmx_pme_t*            GPU_FUNC_ARGUMENT(pme),
+                                              GpuEventSynchronizer* GPU_FUNC_ARGUMENT(xReadyOnDevice),
+                                              gmx_wallcycle*        GPU_FUNC_ARGUMENT(wcycle),
+                                              real GPU_FUNC_ARGUMENT(lambdaQ)) GPU_FUNC_TERM;
+
+/*! \brief
+ * Launches middle stages of PME (FFT R2C, solving, FFT C2R) either on GPU or on CPU, depending on the run mode.
+ *
+ * \param[in] pme               The PME data structure.
+ * \param[in] wcycle            The wallclock counter.
+ * \param[in] stepWork          The required work for this simulation step
+ */
+GPU_FUNC_QUALIFIER void
+pme_gpu_launch_complex_transforms(gmx_pme_t*               GPU_FUNC_ARGUMENT(pme),
+                                  gmx_wallcycle*           GPU_FUNC_ARGUMENT(wcycle),
+                                  const gmx::StepWorkload& GPU_FUNC_ARGUMENT(stepWork)) GPU_FUNC_TERM;
+
+/*! \brief
+ * Launches last stage of PME on GPU - force gathering and D2H force transfer.
+ *
+ * \param[in] pme               The PME data structure.
+ * \param[in] wcycle            The wallclock counter.
+ * \param[in] lambdaQ           The Coulomb lambda to use when calculating the results.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_launch_gather(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme),
+                                              gmx_wallcycle*   GPU_FUNC_ARGUMENT(wcycle),
+                                              real GPU_FUNC_ARGUMENT(lambdaQ)) GPU_FUNC_TERM;
+
+/*! \brief
+ * Attempts to complete PME GPU tasks.
+ *
+ * The \p completionKind argument controls whether the function blocks until all
+ * PME GPU tasks enqueued completed (as pme_gpu_wait_finish_task() does) or only
+ * checks and returns immediately if they did not.
+ * When blocking or the tasks have completed it also gets the output forces
+ * by assigning the ArrayRef to the \p forces pointer passed in.
+ * Virial/energy are also outputs if they were to be computed.
+ *
+ * \param[in]  pme             The PME data structure.
+ * \param[in]  stepWork        The required work for this simulation step
+ * \param[in]  wcycle          The wallclock counter.
+ * \param[out] forceWithVirial The output force and virial
+ * \param[out] enerd           The output energies
+ * \param[in]  lambdaQ         The Coulomb lambda to use when calculating the results.
+ * \param[in]  completionKind  Indicates whether PME task completion should only be checked rather
+ *                             than waited for
+ * \returns                    True if the PME GPU tasks have completed
+ */
+GPU_FUNC_QUALIFIER bool pme_gpu_try_finish_task(gmx_pme_t*               GPU_FUNC_ARGUMENT(pme),
+                                                const gmx::StepWorkload& GPU_FUNC_ARGUMENT(stepWork),
+                                                gmx_wallcycle*           GPU_FUNC_ARGUMENT(wcycle),
+                                                gmx::ForceWithVirial* GPU_FUNC_ARGUMENT(forceWithVirial),
+                                                gmx_enerdata_t*       GPU_FUNC_ARGUMENT(enerd),
+                                                real                  GPU_FUNC_ARGUMENT(lambdaQ),
+                                                GpuTaskCompletion GPU_FUNC_ARGUMENT(completionKind))
+        GPU_FUNC_TERM_WITH_RETURN(false);
+
+/*! \brief
+ * Blocks until PME GPU tasks are completed, and gets the output forces and virial/energy
+ * (if they were to be computed).
+ *
+ * \param[in]  pme             The PME data structure.
+ * \param[in]  stepWork        The required work for this simulation step
+ * \param[in]  wcycle          The wallclock counter.
+ * \param[out] forceWithVirial The output force and virial
+ * \param[out] enerd           The output energies
+ * \param[in]  lambdaQ         The Coulomb lambda to use when calculating the results.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_wait_and_reduce(gmx_pme_t*               GPU_FUNC_ARGUMENT(pme),
+                                                const gmx::StepWorkload& GPU_FUNC_ARGUMENT(stepWork),
+                                                gmx_wallcycle*           GPU_FUNC_ARGUMENT(wcycle),
+                                                gmx::ForceWithVirial* GPU_FUNC_ARGUMENT(forceWithVirial),
+                                                gmx_enerdata_t*       GPU_FUNC_ARGUMENT(enerd),
+                                                real GPU_FUNC_ARGUMENT(lambdaQ)) GPU_FUNC_TERM;
+
+/*! \brief
+ * The PME GPU reinitialization function that is called both at the end of any PME computation and on any load balancing.
+ *
+ * Clears the internal grid and energy/virial buffers; it is not safe to start
+ * the PME computation without calling this.
+ * Note that unlike in the nbnxn module, the force buffer does not need clearing.
+ *
+ * \todo Rename this function to *clear* -- it clearly only does output resetting
+ * and we should be clear about what the function does..
+ *
+ * \param[in] pme            The PME data structure.
+ * \param[in] wcycle         The wallclock counter.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_reinit_computation(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme),
+                                                   gmx_wallcycle* GPU_FUNC_ARGUMENT(wcycle)) GPU_FUNC_TERM;
+
+/*! \brief Set pointer to device copy of coordinate data.
+ * \param[in] pme            The PME data structure.
+ * \param[in] d_x            The pointer to the positions buffer to be set
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_set_device_x(const gmx_pme_t*        GPU_FUNC_ARGUMENT(pme),
+                                             DeviceBuffer<gmx::RVec> GPU_FUNC_ARGUMENT(d_x)) GPU_FUNC_TERM;
+
+/*! \brief Get pointer to device copy of force data.
+ * \param[in] pme            The PME data structure.
+ * \returns                  Pointer to force data
+ */
+GPU_FUNC_QUALIFIER DeviceBuffer<gmx::RVec> pme_gpu_get_device_f(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme))
+        GPU_FUNC_TERM_WITH_RETURN(DeviceBuffer<gmx::RVec>{});
+
+/*! \brief Get pointer to the device synchronizer object that allows syncing on PME force calculation completion
+ * \param[in] pme            The PME data structure.
+ * \returns                  Pointer to synchronizer
+ */
+GPU_FUNC_QUALIFIER GpuEventSynchronizer* pme_gpu_get_f_ready_synchronizer(const gmx_pme_t* GPU_FUNC_ARGUMENT(pme))
+        GPU_FUNC_TERM_WITH_RETURN(nullptr);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_coordinate_receiver_gpu.h b/src/include/gromacs/ewald/pme_coordinate_receiver_gpu.h
new file mode 100644 (file)
index 0000000..81f640d
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declaration of class which receives coordinates to GPU memory on PME task
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ * \inlibraryapi
+ * \ingroup module_ewald
+ */
+#ifndef GMX_PMECOORDINATERECEIVERGPU_H
+#define GMX_PMECOORDINATERECEIVERGPU_H
+
+#include <memory>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/gmxmpi.h"
+
+class DeviceStream;
+struct PpRanks;
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+
+class PmeCoordinateReceiverGpu
+{
+
+public:
+    /*! \brief Creates PME GPU coordinate receiver object
+     * \param[in] pmeStream       CUDA stream used for PME computations
+     * \param[in] comm            Communicator used for simulation
+     * \param[in] ppRanks         List of PP ranks
+     */
+    PmeCoordinateReceiverGpu(const DeviceStream& pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks);
+    ~PmeCoordinateReceiverGpu();
+
+    /*! \brief
+     * send coordinates buffer address to PP rank
+     * \param[in] d_x   coordinates buffer in GPU memory
+     */
+    void sendCoordinateBufferAddressToPpRanks(DeviceBuffer<RVec> d_x);
+
+
+    /*! \brief
+     * Receive coordinate synchronizer pointer from the PP ranks.
+     * \param[in] ppRank  PP rank to receive the synchronizer from.
+     */
+    void receiveCoordinatesSynchronizerFromPpCudaDirect(int ppRank);
+
+    /*! \brief
+     * Used for lib MPI, receives co-ordinates from PP ranks
+     * \param[in] recvbuf   coordinates buffer in GPU memory
+     * \param[in] numAtoms  starting element in buffer
+     * \param[in] numBytes  number of bytes to transfer
+     * \param[in] ppRank    PP rank to send data
+     */
+    void launchReceiveCoordinatesFromPpCudaMpi(DeviceBuffer<RVec> recvbuf, int numAtoms, int numBytes, int ppRank);
+
+    /*! \brief
+     * For lib MPI, wait for coordinates from PP ranks
+     * For thread MPI, enqueue PP co-ordinate transfer event into PME stream
+     */
+    void synchronizeOnCoordinatesFromPpRanks();
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_coordinate_receiver_gpu_impl.h b/src/include/gromacs/ewald/pme_coordinate_receiver_gpu_impl.h
new file mode 100644 (file)
index 0000000..604079c
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declaration of class which receives coordinates to GPU memory on PME task
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ *
+ * \ingroup module_ewald
+ */
+#ifndef GMX_PMECOORDINATERECEIVERGPU_IMPL_H
+#define GMX_PMECOORDINATERECEIVERGPU_IMPL_H
+
+#include <vector>
+
+#include "gromacs/ewald/pme_coordinate_receiver_gpu.h"
+#include "gromacs/utility/arrayref.h"
+
+class GpuEventSynchronizer;
+
+namespace gmx
+{
+/*! \internal \brief Class with interfaces and data for CUDA version of PME coordinate receiving functionality */
+
+class PmeCoordinateReceiverGpu::Impl
+{
+
+public:
+    /*! \brief Creates PME GPU coordinate receiver object
+     * \param[in] pmeStream       CUDA stream used for PME computations
+     * \param[in] comm            Communicator used for simulation
+     * \param[in] ppRanks         List of PP ranks
+     */
+    Impl(const DeviceStream& pmeStream, MPI_Comm comm, gmx::ArrayRef<PpRanks> ppRanks);
+    ~Impl();
+
+    /*! \brief
+     * send coordinates buffer address to PP rank
+     * \param[in] d_x   coordinates buffer in GPU memory
+     */
+    void sendCoordinateBufferAddressToPpRanks(DeviceBuffer<RVec> d_x);
+
+    /*! \brief
+     * Receive coordinate synchronizer pointer from the PP ranks.
+     * \param[in] ppRank  PP rank to receive the synchronizer from.
+     */
+    void receiveCoordinatesSynchronizerFromPpCudaDirect(int ppRank);
+
+    /*! \brief
+     * Used for lib MPI, receives co-ordinates from PP ranks
+     * \param[in] recvbuf   coordinates buffer in GPU memory
+     * \param[in] numAtoms  starting element in buffer
+     * \param[in] numBytes  number of bytes to transfer
+     * \param[in] ppRank    PP rank to send data
+     */
+    void launchReceiveCoordinatesFromPpCudaMpi(DeviceBuffer<RVec> recvbuf, int numAtoms, int numBytes, int ppRank);
+
+    /*! \brief
+     * For lib MPI, wait for coordinates from PP ranks
+     * For thread MPI, enqueue PP co-ordinate transfer event into PME stream
+     */
+    void synchronizeOnCoordinatesFromPpRanks();
+
+private:
+    //! CUDA stream for PME operations
+    const DeviceStream& pmeStream_;
+    //! communicator for simulation
+    MPI_Comm comm_;
+    //! list of PP ranks
+    gmx::ArrayRef<PpRanks> ppRanks_;
+    //! vector of MPI requests
+    std::vector<MPI_Request> request_;
+    //! vector of synchronization events to receive from PP tasks
+    std::vector<GpuEventSynchronizer*> ppSync_;
+    //! counter of messages to receive
+    int recvCount_ = 0;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_force_sender_gpu.h b/src/include/gromacs/ewald/pme_force_sender_gpu.h
new file mode 100644 (file)
index 0000000..44933df
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declaration of class which sends PME Force from GPU memory to PP task
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ * \inlibraryapi
+ * \ingroup module_ewald
+ */
+#ifndef GMX_PMEFORCESENDERGPU_H
+#define GMX_PMEFORCESENDERGPU_H
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/utility/gmxmpi.h"
+
+class GpuEventSynchronizer;
+class DeviceContext;
+class DeviceStream;
+
+/*! \libinternal
+ * \brief Contains information about the PP ranks that partner this PME rank. */
+struct PpRanks
+{
+    //! The MPI rank ID of this partner PP rank.
+    int rankId = -1;
+    //! The number of atoms to communicate with this partner PP rank.
+    int numAtoms = -1;
+};
+
+namespace gmx
+{
+
+template<typename T>
+class ArrayRef;
+
+/*! \libinternal
+ * \brief Manages sending forces from PME-only ranks to their PP ranks. */
+class PmeForceSenderGpu
+{
+
+public:
+    /*! \brief Creates PME GPU Force sender object
+     * \param[in] pmeForcesReady  Event synchronizer marked when PME forces are ready on the GPU
+     * \param[in] comm            Communicator used for simulation
+     * \param[in] deviceContext   GPU context
+     * \param[in] ppRanks         List of PP ranks
+     */
+    PmeForceSenderGpu(GpuEventSynchronizer*  pmeForcesReady,
+                      MPI_Comm               comm,
+                      const DeviceContext&   deviceContext,
+                      gmx::ArrayRef<PpRanks> ppRanks);
+    ~PmeForceSenderGpu();
+
+    /*! \brief
+     * Sets location of force to be sent to each PP rank
+     * \param[in] d_f   force buffer in GPU memory
+     */
+    void setForceSendBuffer(DeviceBuffer<RVec> d_f);
+
+    /*! \brief
+     * Send force to PP rank (used with Thread-MPI)
+     * \param[in] ppRank                   PP rank to receive data
+     * \param[in] numAtoms                 number of atoms to send
+     * \param[in] sendForcesDirectToPpGpu  whether forces are transferred direct to remote GPU memory
+     */
+    void sendFToPpCudaDirect(int ppRank, int numAtoms, bool sendForcesDirectToPpGpu);
+
+    /*! \brief
+     * Send force to PP rank (used with Lib-MPI)
+     * \param[in] sendbuf  force buffer in GPU memory
+     * \param[in] offset   starting element in buffer
+     * \param[in] numBytes number of bytes to transfer
+     * \param[in] ppRank   PP rank to receive data
+     * \param[in] request  MPI request to track asynchronous MPI call status
+     */
+    void sendFToPpCudaMpi(DeviceBuffer<RVec> sendbuf, int offset, int numBytes, int ppRank, MPI_Request* request);
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_force_sender_gpu_impl.h b/src/include/gromacs/ewald/pme_force_sender_gpu_impl.h
new file mode 100644 (file)
index 0000000..6517d82
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declaration of class which sends PME Force from GPU memory to PP task
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ *
+ * \ingroup module_ewald
+ */
+#ifndef GMX_PMEFORCESENDERGPU_IMPL_H
+#define GMX_PMEFORCESENDERGPU_IMPL_H
+
+#include <atomic>
+#include <new>
+
+#include "gromacs/ewald/pme_force_sender_gpu.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/utility/arrayref.h"
+
+// Portable definition of cache line size
+#ifdef __cpp_lib_hardware_interference_size
+using std::hardware_destructive_interference_size;
+#else
+constexpr std::size_t hardware_destructive_interference_size = 64;
+#endif
+
+class GpuEventSynchronizer;
+
+namespace gmx
+{
+
+/*! \internal \brief Class with interfaces and data for CUDA version of PME Force sending functionality*/
+
+typedef struct CacheLineAlignedFlag
+{
+    alignas(hardware_destructive_interference_size) bool flag;
+} CacheLineAlignedFlag;
+
+class PmeForceSenderGpu::Impl
+{
+
+public:
+    /*! \brief Creates PME GPU Force sender object
+     * \param[in] pmeForcesReady  Event synchronizer marked when PME forces are ready on the GPU
+     * \param[in] comm            Communicator used for simulation
+     * \param[in] deviceContext   GPU context
+     * \param[in] ppRanks         List of PP ranks
+     */
+    Impl(GpuEventSynchronizer*  pmeForcesReady,
+         MPI_Comm               comm,
+         const DeviceContext&   deviceContext,
+         gmx::ArrayRef<PpRanks> ppRanks);
+    // NOLINTNEXTLINE(performance-trivially-destructible)
+    ~Impl();
+
+    /*! \brief
+     * Sets location of force to be sent to each PP rank
+     * \param[in] d_f   force buffer in GPU memory
+     */
+    void setForceSendBuffer(DeviceBuffer<Float3> d_f);
+
+    /*! \brief
+     * Send force to PP rank (used with Thread-MPI)
+     * \param[in] ppRank                   PP rank to receive data
+     * \param[in] numAtoms                 number of atoms to send
+     * \param[in] sendForcesDirectToPpGpu  whether forces are transferred direct to remote GPU memory
+     */
+    void sendFToPpCudaDirect(int ppRank, int numAtoms, bool sendForcesDirectToPpGpu);
+
+    /*! \brief
+     * Send force to PP rank (used with Lib-MPI)
+     * \param[in] sendbuf  force buffer in GPU memory
+     * \param[in] offset   starting element in buffer
+     * \param[in] numBytes number of bytes to transfer
+     * \param[in] ppRank   PP rank to receive data
+     * \param[in] request  MPI request to track asynchronous MPI call status
+     */
+    void sendFToPpCudaMpi(DeviceBuffer<RVec> sendbuf, int offset, int numBytes, int ppRank, MPI_Request* request);
+
+private:
+    //! Event indicating when PME forces are ready on the GPU in order for PP stream to sync with the PME stream
+    GpuEventSynchronizer* pmeForcesReady_;
+    //! communicator for simulation
+    MPI_Comm comm_;
+    //! list of PP ranks
+    gmx::ArrayRef<PpRanks> ppRanks_;
+    //! Streams used for pushing force to remote PP ranks
+    std::vector<std::unique_ptr<DeviceStream>> ppCommStream_;
+    //! Events used for manging sync with remote PP ranks
+    std::vector<std::unique_ptr<GpuEventSynchronizer>> ppCommEvent_;
+    //! Vector of flags to track when PP transfer events have been recorded
+    std::vector<std::atomic<CacheLineAlignedFlag>> ppCommEventRecorded_;
+    //! Addresses of local force buffers to send to remote PP ranks
+    std::vector<DeviceBuffer<RVec>> localForcePtr_;
+    //! GPU context handle (not used in CUDA)
+    const DeviceContext& deviceContext_;
+    //! Vector of CPU force buffer pointers for multiple remote PP tasks
+    std::vector<float3*> pmeRemoteCpuForcePtr_;
+    //! Vector of GPU force buffer pointers for multiple remote PP tasks
+    std::vector<float3*> pmeRemoteGpuForcePtr_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gather.h b/src/include/gromacs/ewald/pme_gather.h
new file mode 100644 (file)
index 0000000..a088f25
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_EWALD_PME_GATHER_H
+#define GMX_EWALD_PME_GATHER_H
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+class PmeAtomComm;
+struct gmx_pme_t;
+struct splinedata_t;
+
+void gather_f_bsplines(const struct gmx_pme_t* pme,
+                       const real*             grid,
+                       gmx_bool                bClearF,
+                       const PmeAtomComm*      atc,
+                       const splinedata_t*     spline,
+                       real                    scale);
+
+real gather_energy_bsplines(struct gmx_pme_t* pme, const real* grid, PmeAtomComm* atc);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gather_sycl.h b/src/include/gromacs/ewald/pme_gather_sycl.h
new file mode 100644 (file)
index 0000000..c6b07f3
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Implements PME GPU gather in SYCL.
+ *
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ */
+
+#include "gromacs/gpu_utils/gmxsycl.h"
+
+#include "gromacs/gpu_utils/syclutils.h"
+
+#include "pme_grid.h"
+#include "pme_gpu_types_host.h"
+
+struct PmeGpuGridParams;
+struct PmeGpuAtomParams;
+struct PmeGpuDynamicParams;
+
+template<int order, bool wrapX, bool wrapY, int numGrids, bool readGlobal, ThreadsPerAtom threadsPerAtom, int subGroupSize>
+class PmeGatherKernel : public ISyclKernelFunctor
+{
+public:
+    PmeGatherKernel();
+    void            setArg(size_t argIndex, void* arg) override;
+    cl::sycl::event launch(const KernelLaunchConfig& config, const DeviceStream& deviceStream) override;
+
+private:
+    PmeGpuGridParams*    gridParams_;
+    PmeGpuAtomParams*    atomParams_;
+    PmeGpuDynamicParams* dynamicParams_;
+    void                 reset();
+};
diff --git a/src/include/gromacs/ewald/pme_gpu_calculate_splines.clh b/src/include/gromacs/ewald/pme_gpu_calculate_splines.clh
new file mode 100644 (file)
index 0000000..6485a62
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_EWALD_PME_GPU_CALCULATE_SPLINES_CLH
+#define GMX_EWALD_PME_GPU_CALCULATE_SPLINES_CLH
+
+/*! \internal \file
+ * \brief This file defines the PME OpenCL inline device functions for computing splines.
+ * This closely mirrors pme_gpu_calculate_splines.cuh (which is used in CUDA kernels), except with no templates.
+ * Instead of templated parameters this file expects following defines during compilation:
+ * - order - PME interpolation order;
+ * - atomsPerWarp - number of atoms processed by a warp (fixed for spread and gather kernels to be the same);
+ * - c_skipNeutralAtoms - same as in pme_gpu_constants.h.
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_ewald
+ */
+
+/*! \internal \brief
+ * Gets a base of the unique index to an element in a spline parameter buffer (theta/dtheta),
+ * which is laid out for GPU spread/gather kernels. The base only corresponds to the atom index within the execution block.
+ * Feed the result into getSplineParamIndex() to get a full index.
+ * TODO: it's likely that both parameters can be just replaced with a single atom index, as they are derived from it.
+ * Do that, verifying that the generated code is not bloated, and/or revise the spline indexing scheme.
+ * Removing warp dependency would also be nice (and would probably coincide with removing atomsPerWarp).
+ *
+ * \param[in] warpIndex        Warp index wrt the block.
+ * \param[in] atomWarpIndex    Atom index wrt the warp (from 0 to atomsPerWarp - 1).
+ *
+ * \returns Index into theta or dtheta array using GPU layout.
+ */
+inline int getSplineParamIndexBase(int warpIndex, int atomWarpIndex)
+{
+    assert((atomWarpIndex >= 0) && (atomWarpIndex < atomsPerWarp));
+    const int dimIndex    = 0;
+    const int splineIndex = 0;
+    // The zeroes are here to preserve the full index formula for reference
+    return (((splineIndex + order * warpIndex) * DIM + dimIndex) * atomsPerWarp + atomWarpIndex);
+}
+
+/*! \internal \brief
+ * Gets a unique index to an element in a spline parameter buffer (theta/dtheta),
+ * which is laid out for GPU spread/gather kernels. The index is wrt to the execution block,
+ * in range(0, atomsPerBlock * order * DIM).
+ * This function consumes result of getSplineParamIndexBase() and adjusts it for \p dimIndex and \p splineIndex.
+ *
+ * \param[in] paramIndexBase   Must be result of getSplineParamIndexBase().
+ * \param[in] dimIndex         Dimension index (from 0 to 2)
+ * \param[in] splineIndex      Spline contribution index (from 0 to \p order - 1)
+ *
+ * \returns Index into theta or dtheta array using GPU layout.
+ */
+inline int getSplineParamIndex(int paramIndexBase, int dimIndex, int splineIndex)
+{
+    assert((dimIndex >= XX) && (dimIndex < DIM));
+    assert((splineIndex >= 0) && (splineIndex < order));
+    return (paramIndexBase + (splineIndex * DIM + dimIndex) * atomsPerWarp);
+}
+
+/*! \brief
+ * A function for optionally skipping neutral charges, depending on c_skipNeutralAtoms.
+ *
+ * \param[in] coefficient     The atom charge/coefficient.
+ * \returns                   Non-0 if atom should be processed, 0 otherwise.
+ */
+inline int pme_gpu_check_atom_charge(const float coefficient)
+{
+    assert(isfinite(coefficient));
+    return c_skipNeutralAtoms ? (coefficient != 0.0F) : 1;
+}
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gpu_calculate_splines.cuh b/src/include/gromacs/ewald/pme_gpu_calculate_splines.cuh
new file mode 100644 (file)
index 0000000..d8eefd4
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Implements helper routines for PME gather and spline routines.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ */
+
+#include "gmxpre.h"
+
+#include <cassert>
+
+#include "gromacs/gpu_utils/cuda_kernel_utils.cuh"
+#include "gromacs/gpu_utils/vectype_ops.cuh"
+
+#include "pme.cuh"
+#include "pme_grid.h"
+
+/*! \internal \brief
+ * Gets a base of the unique index to an element in a spline parameter buffer (theta/dtheta),
+ * which is laid out for GPU spread/gather kernels. The base only corresponds to the atom index within the execution block.
+ * Feed the result into getSplineParamIndex() to get a full index.
+ * TODO: it's likely that both parameters can be just replaced with a single atom index, as they are derived from it.
+ * Do that, verifying that the generated code is not bloated, and/or revise the spline indexing scheme.
+ * Removing warp dependency would also be nice (and would probably coincide with removing c_pmeSpreadGatherAtomsPerWarp).
+ *
+ * \tparam order               PME order
+ * \tparam atomsPerWarp        Number of atoms processed by a warp
+ * \param[in] warpIndex        Warp index wrt the block.
+ * \param[in] atomWarpIndex    Atom index wrt the warp (from 0 to atomsPerWarp - 1).
+ *
+ * \returns Index into theta or dtheta array using GPU layout.
+ */
+template<int order, int atomsPerWarp>
+int __device__ __forceinline__ getSplineParamIndexBase(int warpIndex, int atomWarpIndex)
+{
+    assert((atomWarpIndex >= 0) && (atomWarpIndex < atomsPerWarp));
+    const int dimIndex    = 0;
+    const int splineIndex = 0;
+    // The zeroes are here to preserve the full index formula for reference
+    return (((splineIndex + order * warpIndex) * DIM + dimIndex) * atomsPerWarp + atomWarpIndex);
+}
+
+/*! \internal \brief
+ * Gets a unique index to an element in a spline parameter buffer (theta/dtheta),
+ * which is laid out for GPU spread/gather kernels. The index is wrt to the execution block,
+ * in range(0, atomsPerBlock * order * DIM).
+ * This function consumes result of getSplineParamIndexBase() and adjusts it for \p dimIndex and \p splineIndex.
+ *
+ * \tparam order               PME order
+ * \tparam atomsPerWarp        Number of atoms processed by a warp
+ * \param[in] paramIndexBase   Must be result of getSplineParamIndexBase().
+ * \param[in] dimIndex         Dimension index (from 0 to 2)
+ * \param[in] splineIndex      Spline contribution index (from 0 to \p order - 1)
+ *
+ * \returns Index into theta or dtheta array using GPU layout.
+ */
+template<int order, int atomsPerWarp>
+int __device__ __forceinline__ getSplineParamIndex(int paramIndexBase, int dimIndex, int splineIndex)
+{
+    assert((dimIndex >= XX) && (dimIndex < DIM));
+    assert((splineIndex >= 0) && (splineIndex < order));
+    return (paramIndexBase + (splineIndex * DIM + dimIndex) * atomsPerWarp);
+}
+
+/*! \internal \brief
+ * An inline CUDA function for skipping the zero-charge atoms.
+ *
+ * \returns                        Non-0 if atom should be processed, 0 otherwise.
+ * \param[in] coefficient          The atom charge.
+ *
+ * This is called from the spline_and_spread and gather PME kernels.
+ */
+bool __device__ __forceinline__ pme_gpu_check_atom_charge(const float coefficient)
+{
+    assert(isfinite(coefficient));
+    return c_skipNeutralAtoms ? (coefficient != 0.0F) : true;
+}
+
+//! Controls if the atom and charge data is prefeched into shared memory or loaded per thread from global
+static const bool c_useAtomDataPrefetch = true;
+
+/*! \brief Asserts if the argument is finite.
+ *
+ *  The function works for any data type, that can be casted to float. Note that there is also
+ *  a specialized implementation for float3 data type.
+ *
+ * \param[in] arg  Argument to check.
+ */
+template<typename T>
+__device__ inline void assertIsFinite(T arg);
+
+template<>
+__device__ inline void assertIsFinite(float3 gmx_unused arg)
+{
+    assert(isfinite(static_cast<float>(arg.x)));
+    assert(isfinite(static_cast<float>(arg.y)));
+    assert(isfinite(static_cast<float>(arg.z)));
+}
+
+template<typename T>
+__device__ inline void assertIsFinite(T gmx_unused arg)
+{
+    assert(isfinite(static_cast<float>(arg)));
+}
+
+/*! \brief
+ * General purpose function for loading atom-related data from global to shared memory.
+ *
+ * \tparam     T                  Data type (float/int/...)
+ * \tparam     atomsPerBlock      Number of atoms processed by a block - should be accounted for in
+ * the size of the shared memory array.
+ * \tparam     dataCountPerAtom   Number of data elements per single atom (e.g. DIM for an rvec
+ * coordinates array).
+ * \param[out] sm_destination     Shared memory array for output.
+ * \param[in]  gm_source          Global memory array for input.
+ */
+template<typename T, int atomsPerBlock, int dataCountPerAtom>
+__device__ __forceinline__ void pme_gpu_stage_atom_data(T* __restrict__ sm_destination,
+                                                        const T* __restrict__ gm_source)
+{
+    const int blockIndex       = blockIdx.y * gridDim.x + blockIdx.x;
+    const int threadLocalIndex = ((threadIdx.z * blockDim.y + threadIdx.y) * blockDim.x) + threadIdx.x;
+    const int localIndex       = threadLocalIndex;
+    const int globalIndexBase = blockIndex * atomsPerBlock * dataCountPerAtom;
+    const int globalIndex     = globalIndexBase + localIndex;
+    if (localIndex < atomsPerBlock * dataCountPerAtom)
+    {
+        assertIsFinite(gm_source[globalIndex]);
+        sm_destination[localIndex] = gm_source[globalIndex];
+    }
+}
+
+/*! \brief
+ * PME GPU spline parameter and gridline indices calculation.
+ * This corresponds to the CPU functions calc_interpolation_idx() and make_bsplines().
+ * First stage of the whole kernel.
+ *
+ * \tparam     order                PME interpolation order.
+ * \tparam     atomsPerBlock        Number of atoms processed by a block - should be accounted for
+ *                                  in the sizes of the shared memory arrays.
+ * \tparam     atomsPerWarp         Number of atoms processed by a warp
+ * \tparam     writeSmDtheta        Bool controlling if the theta derivative should be written to
+ *                                  shared memory. Enables calculation of dtheta if set.
+ * \tparam     writeGlobal          A boolean which tells if the theta values and gridlines should
+ *                                  be written to global memory. Enables calculation of dtheta if
+ *                                  set.
+ * \tparam     numGrids             The number of grids using the splines.
+ * \param[in]  kernelParams         Input PME CUDA data in constant memory.
+ * \param[in]  atomIndexOffset      Starting atom index for the execution block w.r.t. global memory.
+ * \param[in]  atomX                Atom coordinate of atom processed by thread.
+ * \param[in]  atomCharge           Atom charge/coefficient of atom processed by thread.
+ * \param[out] sm_theta             Atom spline values in the shared memory.
+ * \param[out] sm_dtheta            Derivative of atom spline values in shared memory.
+ * \param[out] sm_gridlineIndices   Atom gridline indices in the shared memory.
+ */
+
+template<int order, int atomsPerBlock, int atomsPerWarp, bool writeSmDtheta, bool writeGlobal, int numGrids>
+__device__ __forceinline__ void calculate_splines(const PmeGpuCudaKernelParams kernelParams,
+                                                  const int                    atomIndexOffset,
+                                                  const float3                 atomX,
+                                                  const float                  atomCharge,
+                                                  float* __restrict__ sm_theta,
+                                                  float* __restrict__ sm_dtheta,
+                                                  int* __restrict__ sm_gridlineIndices)
+{
+    assert(numGrids == 1 || numGrids == 2);
+    assert(numGrids == 1 || c_skipNeutralAtoms == false);
+
+    /* Global memory pointers for output */
+    float* __restrict__ gm_theta         = kernelParams.atoms.d_theta;
+    float* __restrict__ gm_dtheta        = kernelParams.atoms.d_dtheta;
+    int* __restrict__ gm_gridlineIndices = kernelParams.atoms.d_gridlineIndices;
+
+    /* Fractional coordinates */
+    __shared__ float sm_fractCoords[atomsPerBlock * DIM];
+
+    /* Thread index w.r.t. block */
+    const int threadLocalId =
+            (threadIdx.z * (blockDim.x * blockDim.y)) + (threadIdx.y * blockDim.x) + threadIdx.x;
+    /* Warp index w.r.t. block - could probably be obtained easier? */
+    const int warpIndex = threadLocalId / warp_size;
+    /* Atom index w.r.t. warp - alternating 0 1 0 1 .. */
+    const int atomWarpIndex = threadIdx.z % atomsPerWarp;
+    /* Atom index w.r.t. block/shared memory */
+    const int atomIndexLocal = warpIndex * atomsPerWarp + atomWarpIndex;
+
+    /* Spline contribution index in one dimension */
+    const int threadLocalIdXY = (threadIdx.y * blockDim.x) + threadIdx.x;
+    const int orderIndex      = threadLocalIdXY / DIM;
+    /* Dimension index */
+    const int dimIndex = threadLocalIdXY % DIM;
+
+    /* Multi-purpose index of rvec/ivec atom data */
+    const int sharedMemoryIndex = atomIndexLocal * DIM + dimIndex;
+
+    float splineData[order];
+
+    const int localCheck = (dimIndex < DIM) && (orderIndex < 1);
+
+    /* we have 4 threads per atom, but can only use 3 here for the dimensions */
+    if (localCheck)
+    {
+        /* Indices interpolation */
+
+        if (orderIndex == 0)
+        {
+            int   tableIndex, tInt;
+            float n, t;
+            assert(atomIndexLocal < DIM * atomsPerBlock);
+            /* Accessing fields in fshOffset/nXYZ/recipbox/... with dimIndex offset
+             * puts them into local memory(!) instead of accessing the constant memory directly.
+             * That's the reason for the switch, to unroll explicitly.
+             * The commented parts correspond to the 0 components of the recipbox.
+             */
+            switch (dimIndex)
+            {
+                case XX:
+                    tableIndex = kernelParams.grid.tablesOffsets[XX];
+                    n          = kernelParams.grid.realGridSizeFP[XX];
+                    t          = atomX.x * kernelParams.current.recipBox[dimIndex][XX]
+                        + atomX.y * kernelParams.current.recipBox[dimIndex][YY]
+                        + atomX.z * kernelParams.current.recipBox[dimIndex][ZZ];
+                    break;
+
+                case YY:
+                    tableIndex = kernelParams.grid.tablesOffsets[YY];
+                    n          = kernelParams.grid.realGridSizeFP[YY];
+                    t = /*atomX.x * kernelParams.current.recipBox[dimIndex][XX] + */ atomX.y
+                                * kernelParams.current.recipBox[dimIndex][YY]
+                        + atomX.z * kernelParams.current.recipBox[dimIndex][ZZ];
+                    break;
+
+                case ZZ:
+                    tableIndex = kernelParams.grid.tablesOffsets[ZZ];
+                    n          = kernelParams.grid.realGridSizeFP[ZZ];
+                    t          = /*atomX.x * kernelParams.current.recipBox[dimIndex][XX] + atomX.y * kernelParams.current.recipBox[dimIndex][YY] + */ atomX
+                                .z
+                        * kernelParams.current.recipBox[dimIndex][ZZ];
+                    break;
+            }
+            const float shift = c_pmeMaxUnitcellShift;
+            /* Fractional coordinates along box vectors, adding a positive shift to ensure t is positive for triclinic boxes */
+            t    = (t + shift) * n;
+            tInt = static_cast<int>(t);
+            assert(sharedMemoryIndex < atomsPerBlock * DIM);
+            sm_fractCoords[sharedMemoryIndex] = t - tInt;
+            tableIndex += tInt;
+            assert(tInt >= 0);
+            assert(tInt < c_pmeNeighborUnitcellCount * n);
+
+            // TODO have shared table for both parameters to share the fetch, as index is always same?
+            // TODO compare texture/LDG performance
+            sm_fractCoords[sharedMemoryIndex] += fetchFromParamLookupTable(
+                    kernelParams.grid.d_fractShiftsTable, kernelParams.fractShiftsTableTexture, tableIndex);
+            sm_gridlineIndices[sharedMemoryIndex] =
+                    fetchFromParamLookupTable(kernelParams.grid.d_gridlineIndicesTable,
+                                              kernelParams.gridlineIndicesTableTexture,
+                                              tableIndex);
+            if (writeGlobal)
+            {
+                gm_gridlineIndices[atomIndexOffset * DIM + sharedMemoryIndex] =
+                        sm_gridlineIndices[sharedMemoryIndex];
+            }
+        }
+
+        /* B-spline calculation */
+
+        const int chargeCheck = pme_gpu_check_atom_charge(atomCharge);
+        /* With FEP (numGrids == 2), we might have 0 charge in state A, but !=0 in state B, so we always calculate splines */
+        if (numGrids == 2 || chargeCheck)
+        {
+            float div;
+            int o = orderIndex; // This is an index that is set once for PME_GPU_PARALLEL_SPLINE == 1
+
+            const float dr = sm_fractCoords[sharedMemoryIndex];
+            assert(isfinite(dr));
+
+            /* dr is relative offset from lower cell limit */
+            splineData[order - 1] = 0.0F;
+            splineData[1]         = dr;
+            splineData[0]         = 1.0F - dr;
+
+#pragma unroll
+            for (int k = 3; k < order; k++)
+            {
+                div               = 1.0F / (k - 1.0F);
+                splineData[k - 1] = div * dr * splineData[k - 2];
+#pragma unroll
+                for (int l = 1; l < (k - 1); l++)
+                {
+                    splineData[k - l - 1] =
+                            div * ((dr + l) * splineData[k - l - 2] + (k - l - dr) * splineData[k - l - 1]);
+                }
+                splineData[0] = div * (1.0F - dr) * splineData[0];
+            }
+
+            const int thetaIndexBase =
+                    getSplineParamIndexBase<order, atomsPerWarp>(warpIndex, atomWarpIndex);
+            const int thetaGlobalOffsetBase = atomIndexOffset * DIM * order;
+            /* only calculate dtheta if we are saving it to shared or global memory */
+            if (writeSmDtheta || writeGlobal)
+            {
+                /* Differentiation and storing the spline derivatives (dtheta) */
+#pragma unroll
+                for (o = 0; o < order; o++)
+                {
+                    const int thetaIndex =
+                            getSplineParamIndex<order, atomsPerWarp>(thetaIndexBase, dimIndex, o);
+
+                    const float dtheta = ((o > 0) ? splineData[o - 1] : 0.0F) - splineData[o];
+                    assert(isfinite(dtheta));
+                    assert(thetaIndex < order * DIM * atomsPerBlock);
+                    if (writeSmDtheta)
+                    {
+                        sm_dtheta[thetaIndex] = dtheta;
+                    }
+                    if (writeGlobal)
+                    {
+                        const int thetaGlobalIndex  = thetaGlobalOffsetBase + thetaIndex;
+                        gm_dtheta[thetaGlobalIndex] = dtheta;
+                    }
+                }
+            }
+
+            div                   = 1.0F / (order - 1.0F);
+            splineData[order - 1] = div * dr * splineData[order - 2];
+#pragma unroll
+            for (int k = 1; k < (order - 1); k++)
+            {
+                splineData[order - k - 1] = div
+                                            * ((dr + k) * splineData[order - k - 2]
+                                               + (order - k - dr) * splineData[order - k - 1]);
+            }
+            splineData[0] = div * (1.0F - dr) * splineData[0];
+
+            /* Storing the spline values (theta) */
+#pragma unroll
+            for (o = 0; o < order; o++)
+            {
+                const int thetaIndex =
+                        getSplineParamIndex<order, atomsPerWarp>(thetaIndexBase, dimIndex, o);
+                assert(thetaIndex < order * DIM * atomsPerBlock);
+                sm_theta[thetaIndex] = splineData[o];
+                assert(isfinite(sm_theta[thetaIndex]));
+                if (writeGlobal)
+                {
+                    const int thetaGlobalIndex = thetaGlobalOffsetBase + thetaIndex;
+                    gm_theta[thetaGlobalIndex] = splineData[o];
+                }
+            }
+        }
+    }
+}
diff --git a/src/include/gromacs/ewald/pme_gpu_calculate_splines.h b/src/include/gromacs/ewald/pme_gpu_calculate_splines.h
new file mode 100644 (file)
index 0000000..ab87a73
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_EWALD_PME_GPU_UTILS_H
+#define GMX_EWALD_PME_GPU_UTILS_H
+
+/*! \internal \file
+ * \brief This file defines small PME GPU inline host/device functions.
+ * Note that OpenCL device-side functions can't use C++ features, so they are
+ * located in a similar file pme_gpu_utils.clh.
+ * Be sure to keep the logic in sync in both files when changing it!
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#include <cassert>
+
+#include "gromacs/math/vectypes.h"
+
+struct PmeGpu;
+
+/*! \internal \brief
+ * Gets a base of the unique index to an element in a spline parameter buffer (theta/dtheta),
+ * which is laid out for GPU spread/gather kernels. The base only corresponds to the atom index within the execution block.
+ * Feed the result into getSplineParamIndex() to get a full index.
+ * TODO: it's likely that both parameters can be just replaced with a single atom index, as they are derived from it.
+ * Do that, verifying that the generated code is not bloated, and/or revise the spline indexing scheme.
+ * Removing warp dependency would also be nice (and would probably coincide with removing c_pmeSpreadGatherAtomsPerWarp).
+ *
+ * \tparam order               PME order
+ * \tparam atomsPerWarp        Number of atoms processed by a warp
+ * \param[in] warpIndex        Warp index wrt the block.
+ * \param[in] atomWarpIndex    Atom index wrt the warp (from 0 to atomsPerWarp - 1).
+ *
+ * \returns Index into theta or dtheta array using GPU layout.
+ */
+template<int order, int atomsPerWarp>
+int inline getSplineParamIndexBase(int warpIndex, int atomWarpIndex)
+{
+    assert((atomWarpIndex >= 0) && (atomWarpIndex < atomsPerWarp));
+    const int dimIndex    = 0;
+    const int splineIndex = 0;
+    // The zeroes are here to preserve the full index formula for reference
+    return (((splineIndex + order * warpIndex) * DIM + dimIndex) * atomsPerWarp + atomWarpIndex);
+}
+
+/*! \internal \brief
+ * Gets a unique index to an element in a spline parameter buffer (theta/dtheta),
+ * which is laid out for GPU spread/gather kernels. The index is wrt to the execution block,
+ * in range(0, atomsPerBlock * order * DIM).
+ * This function consumes result of getSplineParamIndexBase() and adjusts it for \p dimIndex and \p splineIndex.
+ *
+ * \tparam order               PME order
+ * \tparam atomsPerWarp        Number of atoms processed by a warp
+ * \param[in] paramIndexBase   Must be result of getSplineParamIndexBase().
+ * \param[in] dimIndex         Dimension index (from 0 to 2)
+ * \param[in] splineIndex      Spline contribution index (from 0 to \p order - 1)
+ *
+ * \returns Index into theta or dtheta array using GPU layout.
+ */
+template<int order, int atomsPerWarp>
+int inline getSplineParamIndex(int paramIndexBase, int dimIndex, int splineIndex)
+{
+    assert((dimIndex >= XX) && (dimIndex < DIM));
+    assert((splineIndex >= 0) && (splineIndex < order));
+    return (paramIndexBase + (splineIndex * DIM + dimIndex) * atomsPerWarp);
+}
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gpu_calculate_splines_sycl.h b/src/include/gromacs/ewald/pme_gpu_calculate_splines_sycl.h
new file mode 100644 (file)
index 0000000..998ce6b
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Implements helper routines for PME gather and spline routines.
+ *
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ */
+
+#include "gmxpre.h"
+
+#include <cassert>
+
+#include "gromacs/gpu_utils/gmxsycl.h"
+#include "gromacs/gpu_utils/gputraits_sycl.h"
+#include "gromacs/gpu_utils/sycl_kernel_utils.h"
+
+#include "pme_grid.h"
+#include "pme_gpu_constants.h"
+#include "pme_gpu_types.h"
+
+namespace
+{
+
+/*! \brief Asserts if the argument is finite.
+ *
+ *  The function works for any data type, that can be casted to float. Note that there is also
+ *  a specialized implementation for float3 data type.
+ *
+ * \param[in] arg  Argument to check.
+ */
+template<typename T>
+inline void assertIsFinite(T arg);
+
+#if defined(NDEBUG) || GMX_SYCL_HIPSYCL
+// We have no cl::sycl::isfinite in hipSYCL yet
+template<typename T>
+inline void assertIsFinite(T /* arg */)
+{
+}
+#else
+template<>
+inline void assertIsFinite(Float3 gmx_used_in_debug arg)
+{
+    assert(cl::sycl::isfinite(arg[0]));
+    assert(cl::sycl::isfinite(arg[1]));
+    assert(cl::sycl::isfinite(arg[2]));
+}
+
+template<typename T>
+inline void assertIsFinite(T gmx_used_in_debug arg)
+{
+    assert(cl::sycl::isfinite(static_cast<float>(arg)));
+}
+#endif
+
+} // namespace
+
+using cl::sycl::access::fence_space;
+using cl::sycl::access::mode;
+using cl::sycl::access::target;
+
+/*! \internal \brief
+ * Gets a base of the unique index to an element in a spline parameter buffer (theta/dtheta),
+ * which is laid out for GPU spread/gather kernels. The base only corresponds to the atom index within the execution block.
+ * Feed the result into getSplineParamIndex() to get a full index.
+ * TODO: it's likely that both parameters can be just replaced with a single atom index, as they are derived from it.
+ * Do that, verifying that the generated code is not bloated, and/or revise the spline indexing scheme.
+ * Removing warp dependency would also be nice (and would probably coincide with removing c_pmeSpreadGatherAtomsPerWarp).
+ *
+ * \tparam order                 PME order
+ * \tparam atomsPerSubGroup      Number of atoms processed by a sub group
+ * \param[in] subGroupIndex      Sub group index in the work group.
+ * \param[in] atomSubGroupIndex  Atom index in the sub group (from 0 to atomsPerSubGroup - 1).
+ *
+ * \returns Index into theta or dtheta array using GPU layout.
+ */
+template<int order, int atomsPerSubGroup>
+static inline int getSplineParamIndexBase(int subGroupIndex, int atomSubGroupIndex)
+{
+    assert((atomSubGroupIndex >= 0) && (atomSubGroupIndex < atomsPerSubGroup));
+    constexpr int dimIndex    = 0;
+    constexpr int splineIndex = 0;
+    // The zeroes are here to preserve the full index formula for reference
+    return (((splineIndex + order * subGroupIndex) * DIM + dimIndex) * atomsPerSubGroup + atomSubGroupIndex);
+}
+
+/*! \internal \brief
+ * Gets a unique index to an element in a spline parameter buffer (theta/dtheta),
+ * which is laid out for GPU spread/gather kernels. The index is wrt to the execution block,
+ * in range(0, atomsPerBlock * order * DIM).
+ * This function consumes result of getSplineParamIndexBase() and adjusts it for \p dimIndex and \p splineIndex.
+ *
+ * \tparam order               PME order
+ * \tparam atomsPerSubGroup    Number of atoms processed by a sub group
+ * \param[in] paramIndexBase   Must be result of getSplineParamIndexBase().
+ * \param[in] dimIndex         Dimension index (from 0 to 2)
+ * \param[in] splineIndex      Spline contribution index (from 0 to \p order - 1)
+ *
+ * \returns Index into theta or dtheta array using GPU layout.
+ */
+template<int order, int atomsPerSubGroup>
+static inline int getSplineParamIndex(int paramIndexBase, int dimIndex, int splineIndex)
+{
+    assert((dimIndex >= XX) && (dimIndex < DIM));
+    assert((splineIndex >= 0) && (splineIndex < order));
+    return (paramIndexBase + (splineIndex * DIM + dimIndex) * atomsPerSubGroup);
+}
+
+/*! \internal \brief
+ * An inline function for skipping the zero-charge atoms when we have \c c_skipNeutralAtoms set to \c true.
+ *
+ * \returns                   \c true if atom should be processed, \c false otherwise.
+ * \param[in] charge          The atom charge.
+ */
+static inline bool pmeGpuCheckAtomCharge(const float charge)
+{
+    assertIsFinite(charge);
+    return c_skipNeutralAtoms ? (charge != 0.0F) : true;
+}
+
+/*! \brief
+ * General purpose function for loading atom-related data from global to shared memory.
+ *
+ * \tparam T Data type (float/int/...).
+ * \tparam atomsPerWorkGroup Number of atoms processed by a block - should be accounted for
+ *                           in the size of the shared memory array.
+ * \tparam dataCountPerAtom Number of data elements
+ *                          per single atom (e.g. \c DIM for an rvec coordinates array).
+ * \param[out] sm_destination Shared memory array for output.
+ * \param[in]  gm_source Global memory array for input.
+ * \param[in]  itemIdx SYCL thread ID.
+ */
+template<typename T, int atomsPerWorkGroup, int dataCountPerAtom>
+static inline void pmeGpuStageAtomData(cl::sycl::local_ptr<T>        sm_destination,
+                                       const cl::sycl::global_ptr<T> gm_source,
+                                       cl::sycl::nd_item<3>          itemIdx)
+{
+    const int blockIndex      = itemIdx.get_group_linear_id();
+    const int localIndex      = itemIdx.get_local_linear_id();
+    const int globalIndexBase = blockIndex * atomsPerWorkGroup * dataCountPerAtom;
+    const int globalIndex     = globalIndexBase + localIndex;
+    if (localIndex < atomsPerWorkGroup * dataCountPerAtom)
+    {
+        assertIsFinite(gm_source[globalIndex]);
+        sm_destination[localIndex] = gm_source[globalIndex];
+    }
+}
+
+/*! \brief
+ * PME GPU spline parameter and gridline indices calculation.
+ * This corresponds to the CPU functions calc_interpolation_idx() and make_bsplines().
+ * First stage of the whole kernel.
+ *
+ * \tparam order                PME interpolation order.
+ * \tparam atomsPerBlock        Number of atoms processed by a block - should be accounted for
+ *                              in the sizes of the shared memory arrays.
+ * \tparam atomsPerWarp         Number of atoms processed by a warp
+ * \tparam writeSmDtheta        Bool controlling if the theta derivative should be written to
+ *                              shared memory. Enables calculation of dtheta if set.
+ * \tparam writeGlobal          A boolean which tells if the theta values and gridlines should
+ *                              be written to global memory. Enables calculation of dtheta if set.
+ * \tparam numGrids             The number of grids using the splines.
+ * \tparam subGroupSize         The size of a sub-group (warp).
+ * \param[in]  atomIndexOffset        Starting atom index for the execution block in the global
+ *                                    memory.
+ * \param[in]  atomX                  Coordinates of atom processed by thread.
+ * \param[in]  atomCharge             Charge/coefficient of atom processed by thread.
+ * \param[in]  tablesOffsets          Offsets for X/Y/Z components of \p gm_fractShiftsTable and
+ *                                    \p gm_gridlineIndicesTable.
+ * \param[in]  realGridSizeFP         Real-space grid dimensions, converted to floating point.
+ * \param[in]  currentRecipBox0       Current reciprocal (inverted unit cell) box, vector 1.
+ * \param[in]  currentRecipBox1       Current reciprocal (inverted unit cell) box, vector 2.
+ * \param[in]  currentRecipBox2       Current reciprocal (inverted unit cell) box, vector 3.
+ * \param[out] gm_theta               Atom spline values in the global memory.
+ *                                    Used only if \p writeGlobal is \c true.
+ * \param[out] gm_dtheta              Derivatives of atom spline values in the global memory.
+ *                                    Used only if \p writeGlobal is \c true.
+ * \param[out] gm_gridlineIndices     Atom gridline indices in the global memory.
+ *                                    Used only if \p writeGlobal is \c true.
+ * \param[in] gm_fractShiftsTable     Fractional shifts lookup table in the global memory.
+ * \param[in] gm_gridlineIndicesTable Gridline indices lookup table in the global memory.
+ * \param[out] sm_theta               Atom spline values in the local memory.
+ * \param[out] sm_dtheta              Derivatives of atom spline values in the local memory.
+ * \param[out] sm_gridlineIndices     Atom gridline indices in the local memory.
+ * \param[out] sm_fractCoords         Fractional coordinates in the local memory.
+ * \param[in]  itemIdx                SYCL thread ID.
+ */
+
+template<int order, int atomsPerBlock, int atomsPerWarp, bool writeSmDtheta, bool writeGlobal, int numGrids, int subGroupSize>
+static inline void calculateSplines(const int                         atomIndexOffset,
+                                    const Float3                      atomX,
+                                    const float                       atomCharge,
+                                    const gmx::IVec                   tablesOffsets,
+                                    const gmx::RVec                   realGridSizeFP,
+                                    const gmx::RVec                   currentRecipBox0,
+                                    const gmx::RVec                   currentRecipBox1,
+                                    const gmx::RVec                   currentRecipBox2,
+                                    cl::sycl::global_ptr<float>       gm_theta,
+                                    cl::sycl::global_ptr<float>       gm_dtheta,
+                                    cl::sycl::global_ptr<int>         gm_gridlineIndices,
+                                    const cl::sycl::global_ptr<float> gm_fractShiftsTable,
+                                    const cl::sycl::global_ptr<int>   gm_gridlineIndicesTable,
+                                    cl::sycl::local_ptr<float>        sm_theta,
+                                    cl::sycl::local_ptr<float>        sm_dtheta,
+                                    cl::sycl::local_ptr<int>          sm_gridlineIndices,
+                                    cl::sycl::local_ptr<float>        sm_fractCoords,
+                                    cl::sycl::nd_item<3>              itemIdx)
+{
+    static_assert(numGrids == 1 || numGrids == 2);
+    static_assert(numGrids == 1 || c_skipNeutralAtoms == false);
+
+    /* Thread index w.r.t. block */
+    const int threadLocalId = itemIdx.get_local_linear_id();
+    /* Warp index w.r.t. block - could probably be obtained easier? */
+    const int warpIndex = threadLocalId / subGroupSize;
+    /* Atom index w.r.t. warp - alternating 0 1 0 1 ... */
+    const int atomWarpIndex = itemIdx.get_local_id(0) % atomsPerWarp;
+    /* Atom index w.r.t. block/shared memory */
+    const int atomIndexLocal = warpIndex * atomsPerWarp + atomWarpIndex;
+
+    /* Spline contribution index in one dimension */
+    const int threadLocalIdXY =
+            (itemIdx.get_local_id(1) * itemIdx.get_group_range(2)) + itemIdx.get_local_id(2);
+    const int orderIndex = threadLocalIdXY / DIM;
+    /* Dimension index */
+    const int dimIndex = threadLocalIdXY % DIM;
+
+    /* Multi-purpose index of rvec/ivec atom data */
+    const int sharedMemoryIndex = atomIndexLocal * DIM + dimIndex;
+
+    float splineData[order];
+
+    const int localCheck = (dimIndex < DIM) && (orderIndex < 1);
+
+    /* we have 4 threads per atom, but can only use 3 here for the dimensions */
+    if (localCheck)
+    {
+        /* Indices interpolation */
+        if (orderIndex == 0)
+        {
+            int   tableIndex, tInt;
+            float n, t;
+            assert(atomIndexLocal < DIM * atomsPerBlock);
+            // Switch structure inherited from CUDA.
+            // TODO: Issue #4153: Direct indexing with dimIndex can be better with SYCL
+            switch (dimIndex)
+            {
+                case XX:
+                    tableIndex = tablesOffsets[XX];
+                    n          = realGridSizeFP[XX];
+                    t          = atomX[XX] * currentRecipBox0[XX] + atomX[YY] * currentRecipBox0[YY]
+                        + atomX[ZZ] * currentRecipBox0[ZZ];
+                    break;
+
+                case YY:
+                    tableIndex = tablesOffsets[YY];
+                    n          = realGridSizeFP[YY];
+                    t = atomX[YY] * currentRecipBox1[YY] + atomX[ZZ] * currentRecipBox1[ZZ];
+                    break;
+
+                case ZZ:
+                    tableIndex = tablesOffsets[ZZ];
+                    n          = realGridSizeFP[ZZ];
+                    t          = atomX[ZZ] * currentRecipBox2[ZZ];
+                    break;
+            }
+            const float shift = c_pmeMaxUnitcellShift;
+            /* Fractional coordinates along box vectors, adding a positive shift to ensure t is positive for triclinic boxes */
+            t    = (t + shift) * n;
+            tInt = static_cast<int>(t);
+            assert(sharedMemoryIndex < atomsPerBlock * DIM);
+            sm_fractCoords[sharedMemoryIndex] = t - tInt;
+            tableIndex += tInt;
+            assert(tInt >= 0);
+            assert(tInt < c_pmeNeighborUnitcellCount * n);
+
+            // TODO: Issue #4153: use shared table for both parameters to share the fetch, as index is always same.
+            sm_fractCoords[sharedMemoryIndex] += gm_fractShiftsTable[tableIndex];
+            sm_gridlineIndices[sharedMemoryIndex] = gm_gridlineIndicesTable[tableIndex];
+            if constexpr (writeGlobal)
+            {
+                gm_gridlineIndices[atomIndexOffset * DIM + sharedMemoryIndex] =
+                        sm_gridlineIndices[sharedMemoryIndex];
+            }
+        }
+
+        /* B-spline calculation */
+        const int chargeCheck = pmeGpuCheckAtomCharge(atomCharge);
+        /* With FEP (numGrids == 2), we might have 0 charge in state A, but !=0 in state B, so we always calculate splines */
+        if (numGrids == 2 || chargeCheck)
+        {
+            const float dr = sm_fractCoords[sharedMemoryIndex];
+            assertIsFinite(dr);
+
+            /* dr is relative offset from lower cell limit */
+            splineData[order - 1] = 0.0F;
+            splineData[1]         = dr;
+            splineData[0]         = 1.0F - dr;
+
+#pragma unroll
+            for (int k = 3; k < order; k++)
+            {
+                const float div   = 1.0F / (k - 1.0F);
+                splineData[k - 1] = div * dr * splineData[k - 2];
+#pragma unroll
+                for (int l = 1; l < (k - 1); l++)
+                {
+                    splineData[k - l - 1] =
+                            div * ((dr + l) * splineData[k - l - 2] + (k - l - dr) * splineData[k - l - 1]);
+                }
+                splineData[0] = div * (1.0F - dr) * splineData[0];
+            }
+
+            const int thetaIndexBase =
+                    getSplineParamIndexBase<order, atomsPerWarp>(warpIndex, atomWarpIndex);
+            const int thetaGlobalOffsetBase = atomIndexOffset * DIM * order;
+            /* only calculate dtheta if we are saving it to shared or global memory */
+            if constexpr (writeSmDtheta || writeGlobal)
+            {
+                /* Differentiation and storing the spline derivatives (dtheta) */
+#pragma unroll
+                for (int o = 0; o < order; o++)
+                {
+                    const int thetaIndex =
+                            getSplineParamIndex<order, atomsPerWarp>(thetaIndexBase, dimIndex, o);
+
+                    const float dtheta = ((o > 0) ? splineData[o - 1] : 0.0F) - splineData[o];
+                    assertIsFinite(dtheta);
+                    assert(thetaIndex < order * DIM * atomsPerBlock);
+                    if constexpr (writeSmDtheta)
+                    {
+                        sm_dtheta[thetaIndex] = dtheta;
+                    }
+                    if constexpr (writeGlobal)
+                    {
+                        const int thetaGlobalIndex  = thetaGlobalOffsetBase + thetaIndex;
+                        gm_dtheta[thetaGlobalIndex] = dtheta;
+                    }
+                }
+            }
+
+            const float div       = 1.0F / (order - 1.0F);
+            splineData[order - 1] = div * dr * splineData[order - 2];
+#pragma unroll
+            for (int k = 1; k < (order - 1); k++)
+            {
+                splineData[order - k - 1] = div
+                                            * ((dr + k) * splineData[order - k - 2]
+                                               + (order - k - dr) * splineData[order - k - 1]);
+            }
+            splineData[0] = div * (1.0F - dr) * splineData[0];
+
+            /* Storing the spline values (theta) */
+#pragma unroll
+            for (int o = 0; o < order; o++)
+            {
+                const int thetaIndex =
+                        getSplineParamIndex<order, atomsPerWarp>(thetaIndexBase, dimIndex, o);
+                assert(thetaIndex < order * DIM * atomsPerBlock);
+                sm_theta[thetaIndex] = splineData[o];
+                assertIsFinite(sm_theta[thetaIndex]);
+                if constexpr (writeGlobal)
+                {
+                    const int thetaGlobalIndex = thetaGlobalOffsetBase + thetaIndex;
+                    gm_theta[thetaGlobalIndex] = splineData[o];
+                }
+            }
+        }
+    }
+}
diff --git a/src/include/gromacs/ewald/pme_gpu_constants.h b/src/include/gromacs/ewald/pme_gpu_constants.h
new file mode 100644 (file)
index 0000000..d2503cb
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief This file defines the PME GPU compile-time constants/macros,
+ * used both in device and host code.
+ *
+ * As OpenCL C is not aware of constexpr, most of this file is
+ * forwarded to the OpenCL kernel compilation as defines with same
+ * names, for the sake of code similarity.
+ *
+ * \todo The values are currently common to both CUDA and OpenCL
+ * implementations, but should be reconsidered when we tune the OpenCL
+ * implementation. See Issue #2528.
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_GPU_CONSTANTS_H
+#define GMX_EWALD_PME_GPU_CONSTANTS_H
+
+#include "config.h"
+
+#if GMX_GPU_CUDA
+#    include "gromacs/gpu_utils/cuda_arch_utils.cuh" // for warp_size
+#endif
+
+/* General settings for PME GPU behaviour */
+
+/*! \brief
+ * false: Atoms with zero charges are processed by PME. Could introduce some overhead.
+ * true:  Atoms with zero charges are not processed by PME. Adds branching to the spread/gather.
+ *        Could be good for performance in specific systems with lots of neutral atoms.
+ * \todo Estimate performance differences.
+ */
+constexpr bool c_skipNeutralAtoms = false;
+
+/*! \brief
+ * Number of PME solve output floating point numbers.
+ * 6 for symmetric virial matrix + 1 for reciprocal energy.
+ */
+constexpr int c_virialAndEnergyCount = 7;
+
+
+/* Macros concerning the data layout */
+
+/*
+    Here is a current memory layout for the theta/dtheta B-spline float parameter arrays.
+    This is the data in global memory used both by spreading and gathering kernels (with same scheduling).
+    This example has PME order 4 and 2 particles per warp/data chunk.
+    Each particle has 16 threads assigned to it, each thread works on 4 non-sequential global grid contributions.
+
+    ----------------------------------------------------------------------------
+    particles 0, 1                                        | particles 2, 3     | ...
+    ----------------------------------------------------------------------------
+    order index 0           | index 1 | index 2 | index 3 | order index 0 .....
+    ----------------------------------------------------------------------------
+    tx0 tx1 ty0 ty1 tz0 tz1 | ..........
+    ----------------------------------------------------------------------------
+
+    Each data chunk for a single warp is 24 floats. This goes both for theta and dtheta.
+    24 = 2 particles per warp * order 4 * 3 dimensions. 48 floats (1.5 warp size) per warp in total.
+    I have also tried intertwining theta and theta in a single array (they are used in pairs in gathering stage anyway)
+    and it didn't seem to make a performance difference.
+
+    The spline indexing is isolated in the 2 inline functions:
+    getSplineParamIndexBase() return a base shared memory index corresponding to the atom in the block;
+    getSplineParamIndex() consumes its results and adds offsets for dimension and spline value index.
+
+    The corresponding defines follow.
+ */
+
+/*! \brief PME order parameter
+ *
+ *  Note that the GPU code, unlike the CPU, only supports order 4.
+ */
+constexpr int c_pmeGpuOrder = 4;
+
+/*! \brief The number of GPU threads used for computing spread/gather
+ * contributions of a single atom, which relates to the PME order.
+ *
+ * TODO: this assumption leads to minimum execution width of 16. See Issue #2516
+ */
+enum class ThreadsPerAtom : int
+{
+    /*! \brief Use a number of threads equal to the PME order (ie. 4)
+     *
+     * Only CUDA implements this. See Issue #2516 */
+    Order,
+    //! Use a number of threads equal to the square of the PME order (ie. 16)
+    OrderSquared,
+    //! Size of the enumeration
+    Count
+};
+
+/*
+ * The execution widths for PME GPU kernels, used both on host and device for correct scheduling.
+ * TODO: those were tuned for CUDA with assumption of warp size 32; specialize those for OpenCL
+ * (Issue #2528).
+ * As noted below, these are very approximate maximum sizes; in run time we might have to use
+ * smaller block/workgroup sizes, depending on device capabilities.
+ */
+
+//! Spreading max block width in warps picked among powers of 2 (2, 4, 8, 16) for max. occupancy and min. runtime in most cases
+constexpr int c_spreadMaxWarpsPerBlock = 8;
+
+//! Solving kernel max block width in warps picked among powers of 2 (2, 4, 8, 16) for max.
+//! occupancy and min. runtime (560Ti (CC2.1), 660Ti (CC3.0) and 750 (CC5.0)))
+constexpr int c_solveMaxWarpsPerBlock = 8;
+
+//! Gathering max block width in warps - picked empirically among 2, 4, 8, 16 for max. occupancy and min. runtime
+constexpr int c_gatherMaxWarpsPerBlock = 4;
+
+#if GMX_GPU_CUDA
+/* All the fields below are dependent on warp_size and should
+ * ideally be removed from the device-side code, as we have to
+ * do that for OpenCL already.
+ *
+ * They also express maximum desired block/workgroup sizes,
+ * while both with CUDA and OpenCL we have to treat the device
+ * runtime limitations gracefully as well.
+ */
+
+//! Spreading max block size in threads
+static constexpr int c_spreadMaxThreadsPerBlock = c_spreadMaxWarpsPerBlock * warp_size;
+
+//! Solving kernel max block size in threads
+static constexpr int c_solveMaxThreadsPerBlock = c_solveMaxWarpsPerBlock * warp_size;
+
+//! Gathering max block size in threads
+static constexpr int c_gatherMaxThreadsPerBlock = c_gatherMaxWarpsPerBlock * warp_size;
+//! Gathering min blocks per CUDA multiprocessor
+static constexpr int c_gatherMinBlocksPerMP = GMX_CUDA_MAX_THREADS_PER_MP / c_gatherMaxThreadsPerBlock;
+
+#endif // GMX_GPU_CUDA
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gpu_internal.h b/src/include/gromacs/ewald/pme_gpu_internal.h
new file mode 100644 (file)
index 0000000..7baa6bd
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief This file contains internal function definitions for performing the PME calculations on GPU.
+ * These are not meant to be exposed outside of the PME GPU code.
+ * As of now, their bodies are still in the common pme_gpu.cpp files.
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_GPU_INTERNAL_H
+#define GMX_EWALD_PME_GPU_INTERNAL_H
+
+#include "gromacs/fft/fft.h" // for the gmx_fft_direction enum
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gpu_macros.h" // for the GPU_FUNC_ macros
+
+#include "pme_gpu_types_host.h"
+#include "pme_output.h"
+
+class DeviceContext;
+struct DeviceInformation;
+class DeviceStream;
+class GpuEventSynchronizer;
+struct gmx_hw_info_t;
+struct gmx_gpu_opt_t;
+struct gmx_pme_t; // only used in pme_gpu_reinit
+struct gmx_wallcycle;
+class PmeAtomComm;
+enum class PmeForceOutputHandling;
+struct PmeGpu;
+class PmeGpuProgram;
+struct PmeGpuStaging;
+struct PmeGpuSettings;
+struct t_complex;
+
+#ifndef FEP_STATE_A
+//! Grid index of FEP state A (or unperturbed system)
+#    define FEP_STATE_A 0
+#endif
+#ifndef FEP_STATE_B
+//! Grid index of FEP state B
+#    define FEP_STATE_B 1
+#endif
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class MDLogger;
+} // namespace gmx
+
+//! Type of spline data
+enum class PmeSplineDataType
+{
+    Values,      // theta
+    Derivatives, // dtheta
+};               // TODO move this into new and shiny pme.h (pme-types.h?)
+
+//! PME grid dimension ordering (from major to minor)
+enum class GridOrdering
+{
+    YZX,
+    XYZ
+};
+
+/*! \libinternal \brief
+ * Returns the size of the block size requirement
+ *
+ * The GPU version of PME requires that the coordinates array have a
+ * size divisible by the returned number.
+ *
+ * \returns Number of atoms in a single GPU atom data chunk, which
+ * determines a minimum divisior of the size of the memory allocated.
+ */
+int pme_gpu_get_atom_data_block_size();
+
+/*! \libinternal \brief
+ * Synchronizes the current computation, waiting for the GPU kernels/transfers to finish.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_synchronize(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu)) GPU_FUNC_TERM;
+
+/*! \libinternal \brief
+ * Allocates the fixed size energy and virial buffer both on GPU and CPU.
+ *
+ * \param[in,out] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_alloc_energy_virial(PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Frees the energy and virial memory both on GPU and CPU.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_free_energy_virial(PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Clears the energy and virial memory on GPU with 0.
+ * Should be called at the end of PME computation which returned energy/virial.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_clear_energy_virial(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Reallocates and copies the pre-computed B-spline values to the GPU.
+ *
+ * \param[in,out] pmeGpu             The PME GPU structure.
+ * \param[in]     gridIndex          The index of the grid to use. 0 is Coulomb in the normal
+ *                                   state or FEP state A and 1 is Coulomb in FEP state B.
+ */
+void pme_gpu_realloc_and_copy_bspline_values(PmeGpu* pmeGpu, int gridIndex = 0);
+
+/*! \libinternal \brief
+ * Frees the pre-computed B-spline values on the GPU (and the transfer CPU buffers).
+ *
+ * \param[in] pmeGpu             The PME GPU structure.
+ */
+void pme_gpu_free_bspline_values(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Reallocates the GPU buffer for the PME forces.
+ *
+ * \param[in] pmeGpu             The PME GPU structure.
+ */
+void pme_gpu_realloc_forces(PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Frees the GPU buffer for the PME forces.
+ *
+ * \param[in] pmeGpu             The PME GPU structure.
+ */
+void pme_gpu_free_forces(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Copies the forces from the CPU buffer to the GPU (to reduce them with the PME GPU gathered
+ * forces). To be called e.g. after the bonded calculations.
+ *
+ * \param[in] pmeGpu             The PME GPU structure.
+ */
+void pme_gpu_copy_input_forces(PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Copies the forces from the GPU to the CPU buffer. To be called after the gathering stage.
+ *
+ * \param[in] pmeGpu             The PME GPU structure.
+ */
+void pme_gpu_copy_output_forces(PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Checks whether work in the PME GPU stream has completed.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ *
+ * \returns                     True if work in the PME stream has completed.
+ */
+bool pme_gpu_stream_query(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Reallocates the buffer on the GPU and copies the charges/coefficients from the CPU buffer.
+ * Clears the padded part if needed.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ * \param[in] h_coefficients    The input atom charges/coefficients.
+ * \param[in] gridIndex         The index of the grid to use. 0 is Coulomb in the normal
+ *                              state or FEP state A and 1 is Coulomb in FEP state B.
+ *
+ * Does not need to be done for every PME computation, only whenever the local charges change.
+ * (So, in the beginning of the run, or on DD step).
+ */
+void pme_gpu_realloc_and_copy_input_coefficients(const PmeGpu* pmeGpu,
+                                                 const float*  h_coefficients,
+                                                 int           gridIndex = 0);
+
+/*! \libinternal \brief
+ * Frees the charges/coefficients on the GPU.
+ *
+ * \param[in] pmeGpu             The PME GPU structure.
+ */
+void pme_gpu_free_coefficients(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Reallocates the buffers on the GPU and the host for the atoms spline data.
+ *
+ * \param[in,out] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_realloc_spline_data(PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Frees the buffers on the GPU for the atoms spline data.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_free_spline_data(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Reallocates the buffers on the GPU and the host for the particle gridline indices.
+ *
+ * \param[in,out] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_realloc_grid_indices(PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Frees the buffer on the GPU for the particle gridline indices.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_free_grid_indices(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Reallocates the real space grid and the complex reciprocal grid (if needed) on the GPU.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_realloc_grids(PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Frees the real space grid and the complex reciprocal grid (if needed) on the GPU.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_free_grids(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Clears the real space grid on the GPU.
+ * Should be called at the end of each computation.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_clear_grids(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Reallocates and copies the pre-computed fractional coordinates' shifts to the GPU.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_realloc_and_copy_fract_shifts(PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Frees the pre-computed fractional coordinates' shifts on the GPU.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_free_fract_shifts(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Copies the input real-space grid from the host to the GPU.
+ *
+ * \param[in] pmeGpu    The PME GPU structure.
+ * \param[in] h_grid    The host-side grid buffer.
+ * \param[in] gridIndex The index of the grid to use. 0 is Coulomb in the normal
+ *                      state or FEP state A and 1 is Coulomb in FEP state B.
+ */
+void pme_gpu_copy_input_gather_grid(const PmeGpu* pmeGpu, const float* h_grid, int gridIndex = 0);
+
+/*! \libinternal \brief
+ * Copies the output real-space grid from the GPU to the host.
+ *
+ * \param[in] pmeGpu    The PME GPU structure.
+ * \param[out] h_grid   The host-side grid buffer.
+ * \param[in] gridIndex The index of the grid to use. 0 is Coulomb in the normal
+ *                      state or FEP state A and 1 is Coulomb in FEP state B.
+ */
+void pme_gpu_copy_output_spread_grid(const PmeGpu* pmeGpu, float* h_grid, int gridIndex = 0);
+
+/*! \libinternal \brief
+ * Copies the spread output spline data and gridline indices from the GPU to the host.
+ *
+ * \param[in] pmeGpu    The PME GPU structure.
+ */
+void pme_gpu_copy_output_spread_atom_data(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Copies the gather input spline data and gridline indices from the host to the GPU.
+ *
+ * \param[in] pmeGpu    The PME GPU structure.
+ */
+void pme_gpu_copy_input_gather_atom_data(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Waits for the grid copying to the host-side buffer after spreading to finish.
+ *
+ * \param[in] pmeGpu  The PME GPU structure.
+ */
+void pme_gpu_sync_spread_grid(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Initializes the CUDA FFT structures.
+ *
+ * \param[in] pmeGpu  The PME GPU structure.
+ */
+void pme_gpu_reinit_3dfft(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Destroys the CUDA FFT structures.
+ *
+ * \param[in] pmeGpu  The PME GPU structure.
+ */
+void pme_gpu_destroy_3dfft(const PmeGpu* pmeGpu);
+
+/* The PME stages themselves */
+
+/*! \libinternal \brief
+ * A GPU spline computation and charge spreading function.
+ *
+ * \param[in]  pmeGpu                 The PME GPU structure.
+ * \param[in]  xReadyOnDevice         Event synchronizer indicating that the coordinates are ready in the device memory;
+ *                                    can be nullptr when invoked on a separate PME rank or from PME tests.
+ * \param[out] h_grids                The host-side grid buffers (used only if the result of the spread is expected on the host,
+ *                                    e.g. testing or host-side FFT)
+ * \param[in]  computeSplines         Should the computation of spline parameters and gridline indices be performed.
+ * \param[in]  spreadCharges          Should the charges/coefficients be spread on the grid.
+ * \param[in]  lambda                 The lambda value of the current system state.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_spread(const PmeGpu*         GPU_FUNC_ARGUMENT(pmeGpu),
+                                       GpuEventSynchronizer* GPU_FUNC_ARGUMENT(xReadyOnDevice),
+                                       float**               GPU_FUNC_ARGUMENT(h_grids),
+                                       bool                  GPU_FUNC_ARGUMENT(computeSplines),
+                                       bool                  GPU_FUNC_ARGUMENT(spreadCharges),
+                                       real GPU_FUNC_ARGUMENT(lambda)) GPU_FUNC_TERM;
+
+/*! \libinternal \brief
+ * 3D FFT R2C/C2R routine.
+ *
+ * \param[in]  pmeGpu          The PME GPU structure.
+ * \param[in]  direction       Transform direction (real-to-complex or complex-to-real)
+ * \param[in]  gridIndex       The index of the grid to use. 0 is Coulomb in the normal
+ *                             state or FEP state A and 1 is Coulomb in FEP state B.
+ */
+void pme_gpu_3dfft(const PmeGpu* pmeGpu, enum gmx_fft_direction direction, int gridIndex = 0);
+
+/*! \libinternal \brief
+ * A GPU Fourier space solving function.
+ *
+ * \param[in]     pmeGpu                  The PME GPU structure.
+ * \param[in]     gridIndex               The index of the grid to use. 0 is Coulomb in the normal
+ *                                        state or FEP state A and 1 is Coulomb in FEP state B.
+ * \param[in,out] h_grid                  The host-side input and output Fourier grid buffer (used only with testing or host-side FFT)
+ * \param[in]     gridOrdering            Specifies the dimenion ordering of the complex grid. TODO: store this information?
+ * \param[in]     computeEnergyAndVirial  Tells if the energy and virial computation should be performed.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_solve(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu),
+                                      int           GPU_FUNC_ARGUMENT(gridIndex),
+                                      t_complex*    GPU_FUNC_ARGUMENT(h_grid),
+                                      GridOrdering  GPU_FUNC_ARGUMENT(gridOrdering),
+                                      bool GPU_FUNC_ARGUMENT(computeEnergyAndVirial)) GPU_FUNC_TERM;
+
+/*! \libinternal \brief
+ * A GPU force gathering function.
+ *
+ * \param[in]     pmeGpu                   The PME GPU structure.
+ * \param[in]     h_grids                  The host-side grid buffer (used only in testing mode).
+ * \param[in]     lambda                   The lambda value to use.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_gather(PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu),
+                                       float** GPU_FUNC_ARGUMENT(h_grids),
+                                       float   GPU_FUNC_ARGUMENT(lambda)) GPU_FUNC_TERM;
+
+
+/*! \brief Sets the device pointer to coordinate data
+ * \param[in] pmeGpu         The PME GPU structure.
+ * \param[in] d_x            Pointer to coordinate data
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_set_kernelparam_coordinates(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu),
+                                                            DeviceBuffer<gmx::RVec> GPU_FUNC_ARGUMENT(d_x)) GPU_FUNC_TERM;
+
+/*! \brief Return pointer to device copy of force data.
+ * \param[in] pmeGpu         The PME GPU structure.
+ * \returns                  Pointer to force data
+ */
+GPU_FUNC_QUALIFIER DeviceBuffer<gmx::RVec> pme_gpu_get_kernelparam_forces(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu))
+        GPU_FUNC_TERM_WITH_RETURN(DeviceBuffer<gmx::RVec>{});
+
+/*! \brief Return pointer to the sync object triggered after the PME force calculation completion
+ * \param[in] pmeGpu         The PME GPU structure.
+ * \returns                  Pointer to sync object
+ */
+GPU_FUNC_QUALIFIER GpuEventSynchronizer* pme_gpu_get_forces_ready_synchronizer(
+        const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu)) GPU_FUNC_TERM_WITH_RETURN(nullptr);
+
+/*! \libinternal \brief
+ * Returns the PME GPU settings
+ *
+ * \param[in] pmeGpu         The PME GPU structure.
+ * \returns                  The settings for PME on GPU
+ */
+inline const PmeGpuSettings& pme_gpu_settings(const PmeGpu* pmeGpu)
+{
+    return pmeGpu->settings;
+}
+
+/*! \libinternal \brief
+ * Returns the PME GPU staging object
+ *
+ * \param[in] pmeGpu         The PME GPU structure.
+ * \returns                  The staging object for PME on GPU
+ */
+inline const PmeGpuStaging& pme_gpu_staging(const PmeGpu* pmeGpu)
+{
+    return pmeGpu->staging;
+}
+
+/*! \libinternal \brief
+ * Sets whether the PME module is running in testing mode
+ *
+ * \param[in] pmeGpu         The PME GPU structure.
+ * \param[in] testing        Whether testing mode is on.
+ */
+inline void pme_gpu_set_testing(PmeGpu* pmeGpu, bool testing)
+{
+    if (pmeGpu)
+    {
+        pmeGpu->settings.copyAllOutputs = testing;
+        pmeGpu->settings.transferKind = testing ? GpuApiCallBehavior::Sync : GpuApiCallBehavior::Async;
+    }
+}
+
+/* A block of C++ functions that live in pme_gpu_internal.cpp */
+
+/*! \libinternal \brief
+ * Returns the energy and virial GPU outputs, useful for testing.
+ *
+ * It is the caller's responsibility to be aware of whether the GPU
+ * handled the solve stage.
+ *
+ * \param[in] pme                The PME structure.
+ * \param[in] lambda             The lambda value to use when calculating the results.
+ * \param[out] output            Pointer to output where energy and virial should be stored.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_getEnergyAndVirial(const gmx_pme_t& GPU_FUNC_ARGUMENT(pme),
+                                                   float            GPU_FUNC_ARGUMENT(lambda),
+                                                   PmeOutput* GPU_FUNC_ARGUMENT(output)) GPU_FUNC_TERM;
+
+/*! \libinternal \brief
+ * Returns the GPU outputs (forces, energy and virial)
+ *
+ * \param[in] pme                     The PME structure.
+ * \param[in] computeEnergyAndVirial  Whether the energy and virial are being computed
+ * \param[in] lambdaQ            The Coulomb lambda to use when finalizing the output.
+ * \returns                           The output object.
+ */
+GPU_FUNC_QUALIFIER PmeOutput pme_gpu_getOutput(const gmx_pme_t& GPU_FUNC_ARGUMENT(pme),
+                                               bool GPU_FUNC_ARGUMENT(computeEnergyAndVirial),
+                                               real GPU_FUNC_ARGUMENT(lambdaQ))
+        GPU_FUNC_TERM_WITH_RETURN(PmeOutput{});
+
+/*! \libinternal \brief
+ * Updates the unit cell parameters. Does not check if update is necessary - that is done in pme_gpu_prepare_computation().
+ *
+ * \param[in] pmeGpu         The PME GPU structure.
+ * \param[in] box            The unit cell box.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_update_input_box(PmeGpu*      GPU_FUNC_ARGUMENT(pmeGpu),
+                                                 const matrix GPU_FUNC_ARGUMENT(box)) GPU_FUNC_TERM;
+
+/*! \libinternal \brief
+ * Finishes the PME GPU computation, waiting for the output forces and/or energy/virial to be copied to the host.
+ * If forces were computed, they will have arrived at the external host buffer provided to gather.
+ * If virial/energy were computed, they will have arrived into the internal staging buffer
+ * (even though that should have already happened before even launching the gather).
+ * Finally, cudaEvent_t based GPU timers get updated if enabled. They also need stream synchronization for correctness.
+ * Additionally, device-side buffers are cleared asynchronously for the next computation.
+ *
+ * \param[in] pmeGpu         The PME GPU structure.
+ */
+void pme_gpu_finish_computation(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Get the normal/padded grid dimensions of the real-space PME grid on GPU. Only used in tests.
+ *
+ * \param[in] pmeGpu             The PME GPU structure.
+ * \param[out] gridSize          Pointer to the grid dimensions to fill in.
+ * \param[out] paddedGridSize    Pointer to the padded grid dimensions to fill in.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_get_real_grid_sizes(const PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu),
+                                                    gmx::IVec*    GPU_FUNC_ARGUMENT(gridSize),
+                                                    gmx::IVec* GPU_FUNC_ARGUMENT(paddedGridSize)) GPU_FUNC_TERM;
+
+/*! \libinternal \brief
+ * (Re-)initializes the PME GPU data at the beginning of the run or on DLB.
+ *
+ * \param[in,out] pme            The PME structure.
+ * \param[in]     deviceContext  The GPU context.
+ * \param[in]     deviceStream   The GPU stream.
+ * \param[in,out] pmeGpuProgram  The handle to the program/kernel data created outside (e.g. in unit tests/runner)
+ *
+ * \throws gmx::NotImplementedError if this generally valid PME structure is not valid for GPU runs.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_reinit(gmx_pme_t*           GPU_FUNC_ARGUMENT(pme),
+                                       const DeviceContext* GPU_FUNC_ARGUMENT(deviceContext),
+                                       const DeviceStream*  GPU_FUNC_ARGUMENT(deviceStream),
+                                       const PmeGpuProgram* GPU_FUNC_ARGUMENT(pmeGpuProgram)) GPU_FUNC_TERM;
+
+/*! \libinternal \brief
+ * Destroys the PME GPU data at the end of the run.
+ *
+ * \param[in] pmeGpu     The PME GPU structure.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_destroy(PmeGpu* GPU_FUNC_ARGUMENT(pmeGpu)) GPU_FUNC_TERM;
+
+/*! \libinternal \brief
+ * Reallocates the local atoms data (charges, coordinates, etc.). Copies the charges to the GPU.
+ *
+ * \param[in] pmeGpu    The PME GPU structure.
+ * \param[in] nAtoms    The number of particles.
+ * \param[in] chargesA  The pointer to the host-side array of particle charges in the unperturbed state or FEP state A.
+ * \param[in] chargesB  The pointer to the host-side array of particle charges in FEP state B.
+ *
+ * This is a function that should only be called in the beginning of the run and on domain
+ * decomposition. Should be called before the pme_gpu_set_io_ranges.
+ */
+GPU_FUNC_QUALIFIER void pme_gpu_reinit_atoms(PmeGpu*     GPU_FUNC_ARGUMENT(pmeGpu),
+                                             int         GPU_FUNC_ARGUMENT(nAtoms),
+                                             const real* GPU_FUNC_ARGUMENT(chargesA),
+                                             const real* GPU_FUNC_ARGUMENT(chargesB) = nullptr) GPU_FUNC_TERM;
+
+/*! \brief \libinternal
+ * The PME GPU reinitialization function that is called both at the end of any PME computation and on any load balancing.
+ *
+ * This clears the device-side working buffers in preparation for new computation.
+ *
+ * \param[in] pmeGpu            The PME GPU structure.
+ */
+void pme_gpu_reinit_computation(const PmeGpu* pmeGpu);
+
+/*! \brief
+ * Blocks until PME GPU tasks are completed, and gets the output forces and virial/energy
+ * (if they were to be computed).
+ *
+ * \param[in]  pme                     The PME data structure.
+ * \param[in]  computeEnergyAndVirial  Tells if the energy and virial computation should be performed.
+ * \param[in]  lambdaQ                 The Coulomb lambda to use when calculating the results.
+ * \param[out] wcycle                  The wallclock counter.
+ * \return                             The output forces, energy and virial
+ */
+GPU_FUNC_QUALIFIER PmeOutput pme_gpu_wait_finish_task(gmx_pme_t* GPU_FUNC_ARGUMENT(pme),
+                                                      bool GPU_FUNC_ARGUMENT(computeEnergyAndVirial),
+                                                      real           GPU_FUNC_ARGUMENT(lambdaQ),
+                                                      gmx_wallcycle* GPU_FUNC_ARGUMENT(wcycle))
+        GPU_FUNC_TERM_WITH_RETURN(PmeOutput{});
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gpu_program.h b/src/include/gromacs/ewald/pme_gpu_program.h
new file mode 100644 (file)
index 0000000..f73bd4d
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief
+ * Declares PmeGpuProgram
+ * to store data derived from the GPU context or devices for
+ * PME, such as (compiled) kernel handles and the warp sizes
+ * they work with.
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_ewald
+ * \inlibraryapi
+ */
+
+#ifndef GMX_EWALD_PME_PME_GPU_PROGRAM_H
+#define GMX_EWALD_PME_PME_GPU_PROGRAM_H
+
+#include <memory>
+
+class DeviceContext;
+
+struct PmeGpuProgramImpl;
+struct DeviceInformation;
+
+/*! \libinternal
+ * \brief Stores PME data derived from the GPU context or devices.
+ *
+ * This includes compiled kernel handles and the warp sizes they
+ * work with.
+ */
+class PmeGpuProgram
+{
+public:
+    /*! \brief Construct a PME GPU program.
+     *
+     * \param[in] deviceContext  GPU context.
+     */
+    explicit PmeGpuProgram(const DeviceContext& deviceContext);
+    //! Destructor
+    ~PmeGpuProgram();
+
+    //! Return the warp size for which the kernels were compiled
+    int warpSize() const;
+
+    // TODO: design more getters for information inside, if needed for PME, and make this private?
+    //! Private impl class
+    std::unique_ptr<PmeGpuProgramImpl> impl_;
+};
+
+/*! \brief This is an owning handle for the compiled PME GPU kernels.
+ */
+using PmeGpuProgramStorage = std::unique_ptr<PmeGpuProgram>;
+
+/*! \brief
+ * Factory function used to build persistent PME GPU program for the device at once.
+ */
+PmeGpuProgramStorage buildPmeGpuProgram(const DeviceContext& /* deviceContext */);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gpu_program_impl.h b/src/include/gromacs/ewald/pme_gpu_program_impl.h
new file mode 100644 (file)
index 0000000..621a7b9
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares PmeGpuProgramImpl, which stores PME GPU (compiled) kernel handles.
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_ewald
+ */
+#ifndef GMX_EWALD_PME_PME_GPU_PROGRAM_IMPL_H
+#define GMX_EWALD_PME_PME_GPU_PROGRAM_IMPL_H
+
+#include "config.h"
+
+#include <memory>
+
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/utility/classhelpers.h"
+
+class ISyclKernelFunctor;
+class DeviceContext;
+struct DeviceInformation;
+
+/*! \internal
+ * \brief
+ * PME GPU persistent host program/kernel data, which should be initialized once for the whole execution.
+ *
+ * Primary purpose of this is to not recompile GPU kernels for each OpenCL unit test,
+ * while the relevant GPU context (e.g. cl_context) instance persists.
+ * In CUDA, this just assigns the kernel function pointers.
+ * This also implicitly relies on the fact that reasonable share of the kernels are always used.
+ * If there were more template parameters, even smaller share of all possible kernels would be used.
+ *
+ * \todo In future if we would need to react to either user input or
+ * auto-tuning to compile different kernels, then we might wish to
+ * revisit the number of kernels we pre-compile, and/or the management
+ * of their lifetime.
+ *
+ * This also doesn't manage cuFFT/clFFT kernels, which depend on the PME grid dimensions.
+ *
+ * TODO: pass cl_context to the constructor and not create it inside.
+ * See also Issue #2522.
+ */
+struct PmeGpuProgramImpl
+{
+    /*! \brief
+     * This is a handle to the GPU context, which is just a dummy in CUDA,
+     * but is created/destroyed by this class in OpenCL.
+     */
+    const DeviceContext& deviceContext_;
+
+    //! Conveniently all the PME kernels use the same single argument type
+#if GMX_GPU_CUDA
+    using PmeKernelHandle = void (*)(const struct PmeGpuCudaKernelParams);
+#elif GMX_GPU_OPENCL
+    using PmeKernelHandle = cl_kernel;
+#else
+    using PmeKernelHandle = ISyclKernelFunctor*;
+#endif
+
+    /*! \brief
+     * Maximum synchronous GPU thread group execution width.
+     * "Warp" is a CUDA term which we end up reusing in OpenCL kernels as well.
+     * For CUDA, this is a static value that comes from gromacs/gpu_utils/cuda_arch_utils.cuh;
+     * for OpenCL, we have to query it dynamically.
+     */
+    size_t warpSize_;
+
+    //@{
+    /**
+     * Spread/spline kernels are compiled only for order of 4.
+     * There are multiple versions of each kernel, paramaretized according to
+     *   Number of threads per atom. Using either order(4) or order*order (16) threads per atom is
+     * supported If the spline data is written in the spline/spread kernel and loaded in the gather
+     *   or recalculated in the gather.
+     * Spreading kernels also have hardcoded X/Y indices wrapping parameters,
+     * as a placeholder for implementing 1/2D decomposition.
+     * The kernels are templated separately for spreading on one grid (one or
+     * two sets of coefficients) or on two grids (required for energy and virial
+     * calculations).
+     */
+    size_t spreadWorkGroupSize;
+
+    PmeKernelHandle splineKernelSingle;
+    PmeKernelHandle splineKernelThPerAtom4Single;
+    PmeKernelHandle spreadKernelSingle;
+    PmeKernelHandle spreadKernelThPerAtom4Single;
+    PmeKernelHandle splineAndSpreadKernelSingle;
+    PmeKernelHandle splineAndSpreadKernelThPerAtom4Single;
+    PmeKernelHandle splineAndSpreadKernelWriteSplinesSingle;
+    PmeKernelHandle splineAndSpreadKernelWriteSplinesThPerAtom4Single;
+    PmeKernelHandle splineKernelDual;
+    PmeKernelHandle splineKernelThPerAtom4Dual;
+    PmeKernelHandle spreadKernelDual;
+    PmeKernelHandle spreadKernelThPerAtom4Dual;
+    PmeKernelHandle splineAndSpreadKernelDual;
+    PmeKernelHandle splineAndSpreadKernelThPerAtom4Dual;
+    PmeKernelHandle splineAndSpreadKernelWriteSplinesDual;
+    PmeKernelHandle splineAndSpreadKernelWriteSplinesThPerAtom4Dual;
+    //@}
+
+    //@{
+    /** Same for gather: hardcoded X/Y unwrap parameters, order of 4, plus
+     * it can either reduce with previous forces in the host buffer, or ignore them.
+     * Also similarly to the gather we can use either order(4) or order*order (16) threads per atom
+     * and either recalculate the splines or read the ones written by the spread
+     * The kernels are templated separately for using one or two grids (required for
+     * calculating energies and virial).
+     */
+    size_t gatherWorkGroupSize;
+
+    PmeKernelHandle gatherKernelSingle;
+    PmeKernelHandle gatherKernelThPerAtom4Single;
+    PmeKernelHandle gatherKernelReadSplinesSingle;
+    PmeKernelHandle gatherKernelReadSplinesThPerAtom4Single;
+    PmeKernelHandle gatherKernelDual;
+    PmeKernelHandle gatherKernelThPerAtom4Dual;
+    PmeKernelHandle gatherKernelReadSplinesDual;
+    PmeKernelHandle gatherKernelReadSplinesThPerAtom4Dual;
+    //@}
+
+    //@{
+    /** Solve kernel doesn't care about the interpolation order, but can optionally
+     * compute energy and virial, and supports XYZ and YZX grid orderings.
+     * The kernels are templated separately for grids in state A and B.
+     */
+    size_t solveMaxWorkGroupSize;
+
+    PmeKernelHandle solveYZXKernelA;
+    PmeKernelHandle solveXYZKernelA;
+    PmeKernelHandle solveYZXEnergyKernelA;
+    PmeKernelHandle solveXYZEnergyKernelA;
+    PmeKernelHandle solveYZXKernelB;
+    PmeKernelHandle solveXYZKernelB;
+    PmeKernelHandle solveYZXEnergyKernelB;
+    PmeKernelHandle solveXYZEnergyKernelB;
+    //@}
+
+    PmeGpuProgramImpl() = delete;
+    //! Constructor for the given device
+    explicit PmeGpuProgramImpl(const DeviceContext& deviceContext);
+    // NOLINTNEXTLINE(performance-trivially-destructible)
+    ~PmeGpuProgramImpl();
+    GMX_DISALLOW_COPY_AND_ASSIGN(PmeGpuProgramImpl);
+
+    //! Return the warp size for which the kernels were compiled
+    int warpSize() const { return warpSize_; }
+
+private:
+    // Compiles kernels, if supported. Called by the constructor.
+    void compileKernels(const DeviceInformation& deviceInfo);
+};
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gpu_settings.h b/src/include/gromacs/ewald/pme_gpu_settings.h
new file mode 100644 (file)
index 0000000..d6fa414
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Defines the PME GPU settings data structures.
+ * \todo Some renaming/refactoring, which does not impair the performance:
+ * -- PmeGpuSettings -> PmeGpuTasks
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_GPU_SETTINGS_H
+#define GMX_EWALD_PME_GPU_SETTINGS_H
+
+#include "gromacs/gpu_utils/gpu_utils.h" // for GpuApiCallBehavior
+
+enum class ThreadsPerAtom : int;
+
+/*! \internal \brief
+ * The PME GPU settings structure, included in the main PME GPU structure by value.
+ */
+struct PmeGpuSettings
+{
+    /* Permanent settings set on initialization */
+    /*! \brief A boolean which tells if the solving is performed on GPU. Currently always true */
+    bool performGPUSolve;
+    /*! \brief A boolean which tells if the gathering is performed on GPU. Currently always true */
+    bool performGPUGather;
+    /*! \brief A boolean which tells if the FFT is performed on GPU. Currently true for a single MPI rank. */
+    bool performGPUFFT;
+    /*! \brief A convenience boolean which tells if PME decomposition is used. */
+    bool useDecomposition;
+    /*! \brief True if PME forces are reduced on-GPU, false if reduction is done on the CPU;
+     *  in the former case transfer does not need to happen.
+     *
+     *  Note that this flag may change per-step.
+     */
+    bool useGpuForceReduction;
+
+    /*! \brief A boolean which tells if any PME GPU stage should copy all of its outputs to the
+     * host. Only intended to be used by the test framework.
+     */
+    bool copyAllOutputs;
+    /*! \brief An enum which tells whether most PME GPU D2H/H2D data transfers should be synchronous. */
+    GpuApiCallBehavior transferKind;
+    /*! \brief
+     *  Controls whether we use order (i.e. 4) threads per atom for the GPU
+     *  or order*order (i.e. 16) threads per atom.
+     *
+     *  Currently ThreadsPerAtom::Order is only supported by CUDA.
+     */
+    ThreadsPerAtom threadsPerAtom;
+    /*! \brief
+     * Currently only supported by CUDA.
+     * Controls if we should recalculate the splines in the gather or
+     * save the values in the spread and reload in the gather.
+     */
+    bool recalculateSplines;
+};
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gpu_staging.h b/src/include/gromacs/ewald/pme_gpu_staging.h
new file mode 100644 (file)
index 0000000..7439027
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Defines the host-side PME GPU data structures.
+ * \todo Some renaming/refactoring, which does not impair the performance:
+ * -- bringing the function names up to guidelines
+ * -- PmeGpuSettings -> PmeGpuTasks
+ * -- refining GPU notation application (#2053)
+ * -- renaming coefficients to charges (?)
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_GPU_STAGING_H
+#define GMX_EWALD_PME_GPU_STAGING_H
+
+#include <vector>
+
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/vectypes.h"
+
+#ifndef NUM_STATES
+//! Number of FEP states.
+#    define NUM_STATES 2
+#endif
+
+/*! \internal \brief
+ * The PME GPU intermediate buffers structure, included in the main PME GPU structure by value.
+ * Buffers are managed by the PME GPU module.
+ */
+struct PmeGpuStaging
+{
+    //! Host-side force buffer
+    gmx::PaddedHostVector<gmx::RVec> h_forces;
+
+    /*! \brief Virial and energy intermediate host-side buffer. Size is PME_GPU_VIRIAL_AND_ENERGY_COUNT. */
+    float* h_virialAndEnergy[NUM_STATES];
+    /*! \brief B-spline values intermediate host-side buffer. */
+    float* h_splineModuli[NUM_STATES];
+
+    /*! \brief Pointer to the host memory with B-spline values. Only used for host-side gather, or unit tests */
+    float* h_theta;
+    /*! \brief Pointer to the host memory with B-spline derivative values. Only used for host-side gather, or unit tests */
+    float* h_dtheta;
+    /*! \brief Pointer to the host memory with ivec atom gridline indices. Only used for host-side gather, or unit tests */
+    int* h_gridlineIndices;
+};
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gpu_timings.h b/src/include/gromacs/ewald/pme_gpu_timings.h
new file mode 100644 (file)
index 0000000..d574522
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Defines PME GPU timing functions.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_GPU_TIMINGS_H
+#define GMX_EWALD_PME_GPU_TIMINGS_H
+
+#include <cstddef>
+
+struct gmx_wallclock_gpu_pme_t;
+struct PmeGpu;
+
+enum class PmeStage : int;
+
+/*! \libinternal \brief
+ * Starts timing the certain PME GPU stage during a single computation (if timings are enabled).
+ *
+ * \param[in] pmeGpu         The PME GPU data structure.
+ * \param[in] pmeStageId     The PME GPU stage gtPME_ index from the enum in src/gromacs/timing/gpu_timing.h
+ */
+void pme_gpu_start_timing(const PmeGpu* pmeGpu, PmeStage pmeStageId);
+
+/*! \libinternal \brief
+ * Stops timing the certain PME GPU stage during a single computation (if timings are enabled).
+ *
+ * \param[in] pmeGpu         The PME GPU data structure.
+ * \param[in] pmeStageId     The PME GPU stage gtPME_ index from the enum in src/gromacs/timing/gpu_timing.h
+ */
+void pme_gpu_stop_timing(const PmeGpu* pmeGpu, PmeStage pmeStageId);
+
+/*! \brief
+ * Tells if CUDA-based performance tracking is enabled for PME.
+ *
+ * \param[in] pmeGpu         The PME GPU data structure.
+ * \returns                  True if timings are enabled, false otherwise.
+ */
+bool pme_gpu_timings_enabled(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Finalizes all the active PME GPU stage timings for the current computation. Should be called at the end of every computation.
+ *
+ * \param[in] pmeGpu         The PME GPU structure.
+ */
+void pme_gpu_update_timings(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Updates the internal list of active PME GPU stages (if timings are enabled).
+ *
+ * \param[in] pmeGpu         The PME GPU data structure.
+ */
+void pme_gpu_reinit_timings(const PmeGpu* pmeGpu);
+
+/*! \brief
+ * Resets the PME GPU timings. To be called at the reset MD step.
+ *
+ * \param[in] pmeGpu         The PME GPU structure.
+ */
+void pme_gpu_reset_timings(const PmeGpu* pmeGpu);
+
+/*! \libinternal \brief
+ * Copies the PME GPU timings to the gmx_wallclock_gpu_t structure (for log output). To be called at the run end.
+ *
+ * \param[in] pmeGpu         The PME GPU structure.
+ * \param[in] timings        The gmx_wallclock_gpu_pme_t structure.
+ */
+void pme_gpu_get_timings(const PmeGpu* pmeGpu, gmx_wallclock_gpu_pme_t* timings);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gpu_types.h b/src/include/gromacs/ewald/pme_gpu_types.h
new file mode 100644 (file)
index 0000000..e2c0673
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ * \brief Defines the PME GPU data structures
+ * (the GPU function parameters used both on host and device sides).
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_GPU_TYPES_H
+#define GMX_EWALD_PME_GPU_TYPES_H
+
+/*
+ * In OpenCL, the structures must be laid out on the host and device exactly the same way.
+ * If something is off, one might get an error CL_INVALID_ARG_SIZE if any structure's sizes don't
+ * match. What's worse, structures might be of same size but members might be aligned differently,
+ * resulting in wrong kernel results. The structures below are aligned manually.
+ * The pattern is ordering the members of structs from smallest to largest sizeof
+ * (arrays behave the same way as sequences of separate fields),
+ * as described in "The Lost Art of C Structure Packing".
+ *
+ * However, if the need arises at some point, they can all be aligned forcefully:
+ *
+ * #define GMX_GPU_ALIGNED __attribute__ ((aligned(8)))
+ * struct GMX_GPU_ALIGNED PmeGpuConstParams
+ * struct GMX_GPU_ALIGNED PmeGpuGridParams
+ * etc...
+ *
+ * One might also try __attribute__ ((packed)), but it doesn't work with DeviceBuffer,
+ * as it appears to not be POD.
+ */
+
+
+/*! \brief A workaround to hide DeviceBuffer template from OpenCL kernel compilation
+ * - to turn it into a dummy of the same size as host implementation of device buffer.
+ * As we only care about 64-bit, 8 bytes is fine.
+ * TODO: what we should be doing is providing separate device-side views of the same structures -
+ * then there would be no need for macro.
+ */
+#ifndef __OPENCL_C_VERSION__
+#    include "gromacs/gpu_utils/devicebuffer.h"
+#    define HIDE_FROM_OPENCL_COMPILER(x) x
+static_assert(sizeof(DeviceBuffer<float>) == 8,
+              "DeviceBuffer is defined as an 8 byte stub for OpenCL C");
+static_assert(sizeof(DeviceBuffer<int>) == 8,
+              "DeviceBuffer is defined as an 8 byte stub for OpenCL C");
+#else
+#    define HIDE_FROM_OPENCL_COMPILER(x) char8
+#endif
+
+#ifndef NUMFEPSTATES
+//! Number of FEP states.
+#    define NUMFEPSTATES 2
+#endif
+
+/* What follows is all the PME GPU function arguments,
+ * sorted into several device-side structures depending on the update rate.
+ * This is GPU agnostic (float3 replaced by float[3], etc.).
+ * The GPU-framework specifics (e.g. cudaTextureObject_t handles) are described
+ * in the larger structure PmeGpuCudaKernelParams in the pme.cuh.
+ */
+
+/*! \internal \brief
+ * A GPU data structure for storing the constant PME data.
+ * This only has to be initialized once.
+ */
+struct PmeGpuConstParams
+{
+    /*! \brief Electrostatics coefficient = c_one4PiEps0 / pme->epsilon_r */
+    float elFactor;
+    /*! \brief Virial and energy GPU array. Size is c_virialAndEnergyCount (7) floats.
+     * The element order is virxx, viryy, virzz, virxy, virxz, viryz, energy. */
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_virialAndEnergy[NUMFEPSTATES];
+};
+
+/*! \internal \brief
+ * A GPU data structure for storing the PME data related to the grid sizes and cut-off.
+ * This only has to be updated at every DD step.
+ */
+struct PmeGpuGridParams
+{
+    /*! \brief Ewald solving factor = (M_PI / pme->ewaldcoeff_q)^2 */
+    float ewaldFactor;
+
+    /* Grid sizes */
+    /*! \brief Real-space grid data dimensions. */
+    int realGridSize[DIM];
+    /*! \brief Real-space grid dimensions, only converted to floating point. */
+    float realGridSizeFP[DIM];
+    /*! \brief Real-space grid dimensions (padded). The padding as compared to realGridSize includes the (order - 1) overlap. */
+    int realGridSizePadded[DIM]; /* Is major dimension of this ever used in kernels? */
+    /*! \brief Fourier grid dimensions. This counts the complex numbers! */
+    int complexGridSize[DIM];
+    /*! \brief Fourier grid dimensions (padded). This counts the complex numbers! */
+    int complexGridSizePadded[DIM];
+
+    /*! \brief Offsets for X/Y/Z components of d_splineModuli */
+    int splineValuesOffset[DIM];
+    /*! \brief Offsets for X/Y/Z components of d_fractShiftsTable and d_gridlineIndicesTable */
+    int tablesOffsets[DIM];
+
+    /* Grid arrays */
+    /*! \brief Real space grid. */
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_realGrid[NUMFEPSTATES];
+    /*! \brief Complex grid - used in FFT/solve. If inplace cu/clFFT is used, then it is the same handle as realGrid. */
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_fourierGrid[NUMFEPSTATES];
+
+    /*! \brief Grid spline values as in pme->bsp_mod
+     * (laid out sequentially (XXX....XYYY......YZZZ.....Z))
+     */
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_splineModuli[NUMFEPSTATES];
+    /*! \brief Fractional shifts lookup table as in pme->fshx/fshy/fshz, laid out sequentially (XXX....XYYY......YZZZ.....Z) */
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_fractShiftsTable;
+    /*! \brief Gridline indices lookup table
+     * (modulo lookup table as in pme->nnx/nny/nnz, laid out sequentially (XXX....XYYY......YZZZ.....Z)) */
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<int>) d_gridlineIndicesTable;
+};
+
+/*! \internal \brief
+ * A GPU data structure for storing the PME data of the atoms, local to this process' domain
+ * partition. This only has to be updated every DD step.
+ */
+struct PmeGpuAtomParams
+{
+    /*! \brief Number of local atoms */
+    int nAtoms;
+    /*! \brief Global GPU memory array handle with input rvec atom coordinates.
+     * The coordinates themselves change and need to be copied to the GPU for every PME computation,
+     * but reallocation happens only at DD.
+     */
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<gmx::RVec>) d_coordinates;
+    /*! \brief Global GPU memory array handle with input atom charges in states A and B.
+     * The charges only need to be reallocated and copied to the GPU at DD step.
+     */
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_coefficients[NUMFEPSTATES];
+    /*! \brief Global GPU memory array handle with input/output rvec atom forces.
+     * The forces change and need to be copied from (and possibly to) the GPU for every PME
+     * computation, but reallocation happens only at DD.
+     */
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<gmx::RVec>) d_forces;
+    /*! \brief Global GPU memory array handle with ivec atom gridline indices.
+     * Computed on GPU in the spline calculation part.
+     */
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<int>) d_gridlineIndices;
+    /* B-spline parameters are computed entirely on GPU for every PME computation, not copied.
+     * Unless we want to try something like GPU spread + CPU gather?
+     */
+    /*! \brief Global GPU memory array handle with B-spline values */
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_theta;
+    /*! \brief Global GPU memory array handle with B-spline derivative values */
+    HIDE_FROM_OPENCL_COMPILER(DeviceBuffer<float>) d_dtheta;
+};
+
+/*! \internal \brief
+ * A GPU data structure for storing the PME data which might change for each new PME computation.
+ */
+struct PmeGpuDynamicParams
+{
+    /* The box parameters. The box only changes size with pressure coupling enabled. */
+    /*! \brief
+     * Reciprocal (inverted unit cell) box.
+     *
+     * The box is transposed as compared to the CPU pme->recipbox.
+     * Basically, spread uses matrix columns (while solve and gather use rows).
+     * This storage format might be not the most optimal since the box is always triangular so there are zeroes.
+     */
+    float recipBox[DIM][DIM];
+    /*! \brief The unit cell volume for solving. */
+    float boxVolume;
+
+    /*! \brief The current coefficient scaling value. */
+    float scale;
+};
+
+/*! \internal \brief
+ * A single structure encompassing all the PME data used in GPU kernels on device.
+ * To extend the list with platform-specific parameters, this can be inherited by the
+ * GPU framework-specific structure.
+ */
+struct PmeGpuKernelParamsBase
+{
+    /*! \brief Constant data that is set once. */
+    struct PmeGpuConstParams constants;
+    /*! \brief Data dependent on the grid size/cutoff. */
+    struct PmeGpuGridParams grid;
+    /*! \brief Data dependent on the DD and local atoms. */
+    struct PmeGpuAtomParams atoms;
+    /*! \brief Data that possibly changes for every new PME computation.
+     * This should be kept up-to-date by calling pme_gpu_prepare_computation(...)
+     * before launching spreading.
+     */
+    struct PmeGpuDynamicParams current;
+    /* These texture objects are only used in CUDA and are related to the grid size. */
+    /*! \brief Texture object for accessing grid.d_fractShiftsTable */
+    HIDE_FROM_OPENCL_COMPILER(DeviceTexture) fractShiftsTableTexture;
+    /*! \brief Texture object for accessing grid.d_gridlineIndicesTable */
+    HIDE_FROM_OPENCL_COMPILER(DeviceTexture) gridlineIndicesTableTexture;
+};
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gpu_types_host.h b/src/include/gromacs/ewald/pme_gpu_types_host.h
new file mode 100644 (file)
index 0000000..0c6d7bb
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Defines the host-side PME GPU data structures.
+ * \todo Some renaming/refactoring, which does not impair the performance:
+ * -- bringing the function names up to guidelines
+ * -- PmeGpuSettings -> PmeGpuTasks
+ * -- refining GPU notation application (#2053)
+ * -- renaming coefficients to charges (?)
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_GPU_TYPES_HOST_H
+#define GMX_EWALD_PME_GPU_TYPES_HOST_H
+
+#include "config.h"
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/ewald/pme.h"
+#include "gromacs/ewald/pme_gpu_program.h"
+#include "gromacs/gpu_utils/clfftinitializer.h"
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/vectypes.h"
+
+#include "pme_gpu_settings.h"
+#include "pme_gpu_staging.h"
+
+#if GMX_GPU
+struct PmeGpuSpecific;
+#else
+/*! \brief A dummy typedef for the GPU host data placeholder on non-GPU builds */
+typedef int PmeGpuSpecific;
+#endif
+
+#if GMX_GPU_CUDA
+struct PmeGpuCudaKernelParams;
+/*! \brief A typedef for including the GPU kernel arguments data by pointer */
+typedef PmeGpuCudaKernelParams PmeGpuKernelParams;
+#elif GMX_GPU_OPENCL || GMX_GPU_SYCL
+struct PmeGpuKernelParamsBase;
+/*! \brief A typedef for including the GPU kernel arguments data by pointer */
+typedef PmeGpuKernelParamsBase PmeGpuKernelParams;
+#else
+/*! \brief A dummy typedef for the GPU kernel arguments data placeholder on non-GPU builds */
+typedef int PmeGpuKernelParams;
+#endif
+
+struct DeviceInformation;
+
+/*! \internal \brief
+ * The PME GPU structure for all the data copied directly from the CPU PME structure.
+ * The copying is done when the CPU PME structure is already (re-)initialized
+ * (pme_gpu_reinit is called at the end of gmx_pme_init).
+ * All the variables here are named almost the same way as in gmx_pme_t.
+ * The types are different: pointers are replaced by vectors.
+ * TODO: use the shared data with the PME CPU.
+ * Included in the main PME GPU structure by value.
+ */
+struct PmeShared
+{
+    /*! \brief Grid count */
+    int ngrids;
+    /*! \brief Grid dimensions - nkx, nky, nkz */
+    int nk[DIM];
+    /*! \brief PME interpolation order */
+    int pme_order;
+    /*! \brief Ewald splitting coefficient for Coulomb */
+    real ewaldcoeff_q;
+    /*! \brief Electrostatics parameter */
+    real epsilon_r;
+    /*! \brief Gridline indices - nnx, nny, nnz */
+    std::vector<int> nn;
+    /*! \brief Fractional shifts - fshx, fshy, fshz */
+    std::vector<real> fsh;
+    /*! \brief Precomputed B-spline values */
+    std::vector<real> bsp_mod[DIM];
+    /*! \brief The PME codepath being taken */
+    PmeRunMode runMode;
+    /*! \brief  Whether PME execution is happening on a PME-only rank (from gmx_pme_t.bPPnode). */
+    bool isRankPmeOnly;
+    /*! \brief The box scaler based on inputrec - created in pme_init and managed by CPU structure */
+    class EwaldBoxZScaler* boxScaler;
+    /*! \brief The previous computation box to know if we even need to update the current box params.
+     * \todo Manage this on higher level.
+     * \todo Alternatively, when this structure is used by CPU PME code, make use of this field there as well.
+     */
+    matrix previousBox;
+};
+
+/*! \internal \brief
+ * The main PME GPU host structure, included in the PME CPU structure by pointer.
+ */
+struct PmeGpu
+{
+    /*! \brief The information copied once per reinit from the CPU structure. */
+    std::shared_ptr<PmeShared> common; // TODO: make the CPU structure use the same type
+
+    //! A handle to the program created by buildPmeGpuProgram()
+    const PmeGpuProgram* programHandle_;
+
+    //! Handle that ensures the clFFT library has been initialized once per process.
+    std::unique_ptr<gmx::ClfftInitializer> initializedClfftLibrary_;
+
+    /*! \brief The settings. */
+    PmeGpuSettings settings;
+
+    /*! \brief The host-side buffers.
+     * The device-side buffers are buried in kernelParams, but that will have to change.
+     */
+    PmeGpuStaging staging;
+
+    /*! \brief Number of local atoms, padded to be divisible by c_pmeAtomDataAlignment.
+     *
+     * Used only as a basic size for almost all the atom data allocations
+     * (spline parameter data is also aligned by PME_SPREADGATHER_PARTICLES_PER_WARP).
+     * kernelParams.atoms.nAtoms is the actual atom count to be used for most data copying.
+     *
+     * TODO: memory allocation/padding properties should be handled by
+     * something like a container
+     */
+    int nAtomsAlloc;
+
+    /*! \brief Kernel scheduling grid width limit in X - derived from deviceinfo compute capability in CUDA.
+     * Declared as very large int to make it useful in computations with type promotion, to avoid overflows.
+     * OpenCL seems to not have readily available global work size limit, so we just assign a large arbitrary constant to this instead.
+     * TODO: this should be in PmeGpuProgram(Impl)
+     */
+    std::intmax_t maxGridWidthX;
+
+    /*! \brief A single structure encompassing all the PME data used on GPU.
+     * Its value is the only argument to all the PME GPU kernels.
+     * \todo Test whether this should be copied to the constant GPU memory once for each computation
+     * (or even less often with no box updates) instead of being an argument.
+     */
+    std::shared_ptr<PmeGpuKernelParams> kernelParams;
+
+    /*! \brief The pointer to GPU-framework specific host-side data, such as CUDA streams and events. */
+    std::shared_ptr<PmeGpuSpecific> archSpecific; /* FIXME: make it an unique_ptr */
+};
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_gpu_types_host_impl.h b/src/include/gromacs/ewald/pme_gpu_types_host_impl.h
new file mode 100644 (file)
index 0000000..e1f7f17
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ * \brief Defines the host-side PME GPU data structure, which is dependent on the GPU types.
+ * It's included by pointer in the general PmeGpu host structure in pme_gpu_types_host.h.
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef PMEGPUTYPESHOSTIMPL_H
+#define PMEGPUTYPESHOSTIMPL_H
+
+#include "config.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+#include <array>
+#include <set>
+#include <vector>
+
+#if GMX_GPU_CUDA
+#    include "gromacs/gpu_utils/gpuregiontimer.cuh"
+#elif GMX_GPU_OPENCL
+#    include "gromacs/gpu_utils/gpuregiontimer_ocl.h"
+#elif GMX_GPU_SYCL
+#    include "gromacs/gpu_utils/gpuregiontimer_sycl.h"
+#endif
+
+#include "gromacs/gpu_utils/gpueventsynchronizer.h"
+
+#include "gromacs/fft/gpu_3dfft.h"
+#include "gromacs/timing/gpu_timing.h" // for gtPME_EVENT_COUNT
+
+#ifndef NUMFEPSTATES
+//! Number of FEP states.
+#    define NUMFEPSTATES 2
+#endif
+
+namespace gmx
+{
+class Gpu3dFft;
+} // namespace gmx
+
+/*! \internal \brief
+ * The main PME CUDA/OpenCL-specific host data structure, included in the PME GPU structure by the archSpecific pointer.
+ */
+struct PmeGpuSpecific
+{
+    /*! \brief Constructor
+     *
+     * \param[in] deviceContext  GPU device context
+     * \param[in] pmeStream      GPU pme stream.
+     */
+    PmeGpuSpecific(const DeviceContext& deviceContext, const DeviceStream& pmeStream) :
+        deviceContext_(deviceContext), pmeStream_(pmeStream)
+    {
+    }
+
+    /*! \brief
+     * A handle to the GPU context.
+     * TODO: this is currently extracted from the implementation of pmeGpu->programHandle_,
+     * but should be a constructor parameter to PmeGpu, as well as PmeGpuProgram,
+     * managed by high-level code.
+     */
+    const DeviceContext& deviceContext_;
+
+    /*! \brief The GPU stream where everything related to the PME happens. */
+    const DeviceStream& pmeStream_;
+
+    /* Synchronization events */
+    /*! \brief Triggered after the PME Force Calculations have been completed */
+    GpuEventSynchronizer pmeForcesReady;
+    /*! \brief Triggered after the grid has been copied to the host (after the spreading stage). */
+    GpuEventSynchronizer syncSpreadGridD2H;
+
+    /* Settings which are set at the start of the run */
+    /*! \brief A boolean which tells whether the complex and real grids for cu/clFFT are different or same. Currenty true. */
+    bool performOutOfPlaceFFT = false;
+    /*! \brief A boolean which tells if the GPU timing events are enabled.
+     *  False by default, can be enabled by setting the environment variable GMX_ENABLE_GPU_TIMING.
+     *  Note: will not be reliable when multiple GPU tasks are running concurrently on the same
+     * device context, as CUDA events on multiple streams are untrustworthy.
+     */
+    bool useTiming = false;
+
+    //! Vector of FFT setups
+    std::vector<std::unique_ptr<gmx::Gpu3dFft>> fftSetup;
+
+    //! All the timers one might use
+    gmx::EnumerationArray<PmeStage, GpuRegionTimer> timingEvents;
+
+    //! Indices of timingEvents actually used
+    std::set<PmeStage> activeTimers;
+
+    /* GPU arrays element counts (not the arrays sizes in bytes!).
+     * They might be larger than the actual meaningful data sizes.
+     * These are paired: the actual element count + the maximum element count that can fit in the current allocated memory.
+     * These integer pairs are mostly meaningful for the reallocateDeviceBuffer calls.
+     * As such, if DeviceBuffer is refactored into a class, they can be freely changed, too.
+     * The only exceptions are realGridSize and complexGridSize which are also used for grid clearing/copying.
+     * TODO: these should live in a clean buffered container type, and be refactored in the NB/cudautils as well.
+     */
+    /*! \brief The kernelParams.atoms.coordinates float element count (actual)*/
+    int coordinatesSize = 0;
+    /*! \brief The kernelParams.atoms.coordinates float element count (reserved) */
+    int coordinatesSizeAlloc = 0;
+    /*! \brief The kernelParams.atoms.forces float element count (actual) */
+    int forcesSize = 0;
+    /*! \brief The kernelParams.atoms.forces float element count (reserved) */
+    int forcesSizeAlloc = 0;
+    /*! \brief The kernelParams.atoms.gridlineIndices int element count (actual) */
+    int gridlineIndicesSize = 0;
+    /*! \brief The kernelParams.atoms.gridlineIndices int element count (reserved) */
+    int gridlineIndicesSizeAlloc = 0;
+    /*! \brief Both the kernelParams.atoms.theta and kernelParams.atoms.dtheta float element count (actual) */
+    int splineDataSize = 0;
+    /*! \brief Both the kernelParams.atoms.theta and kernelParams.atoms.dtheta float element count (reserved) */
+    int splineDataSizeAlloc = 0;
+    /*! \brief The kernelParams.atoms.coefficients float element count (actual) */
+    int coefficientsSize[NUMFEPSTATES] = { 0, 0 };
+    /*! \brief The kernelParams.atoms.coefficients float element count (reserved) */
+    int coefficientsCapacity[NUMFEPSTATES] = { 0, 0 };
+    /*! \brief The kernelParams.grid.splineValuesArray float element count (actual) */
+    int splineValuesSize[NUMFEPSTATES] = { 0, 0 };
+    /*! \brief The kernelParams.grid.splineValuesArray float element count (reserved) */
+    int splineValuesCapacity[NUMFEPSTATES] = { 0, 0 };
+    /*! \brief The kernelParams.grid.realGrid float element count (actual) */
+    int realGridSize[NUMFEPSTATES] = { 0, 0 };
+    /*! \brief The kernelParams.grid.realGrid float element count (reserved) */
+    int realGridCapacity[NUMFEPSTATES] = { 0, 0 };
+    /*! \brief The kernelParams.grid.fourierGrid float (not float2!) element count (actual) */
+    int complexGridSize[NUMFEPSTATES] = { 0, 0 };
+    /*! \brief The kernelParams.grid.fourierGrid float (not float2!) element count (reserved) */
+    int complexGridCapacity[NUMFEPSTATES] = { 0, 0 };
+};
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_grid.h b/src/include/gromacs/ewald/pme_grid.h
new file mode 100644 (file)
index 0000000..41f1453
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/* TODO find out what this file should be called */
+#ifndef GMX_EWALD_PME_GRID_H
+#define GMX_EWALD_PME_GRID_H
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_pme_t;
+
+/*! \brief
+ * We allow coordinates to be out the unit-cell by up to 2 box lengths,
+ * which might be needed along dimension x for a very skewed unit-cell.
+ */
+constexpr int c_pmeMaxUnitcellShift = 2;
+
+/*! \brief
+ * This affects the size of the lookup table of the modulo operation result,
+ * when working with PME local grid indices of the particles.
+ */
+constexpr int c_pmeNeighborUnitcellCount = 2 * c_pmeMaxUnitcellShift + 1;
+
+struct pmegrid_t;
+struct pmegrids_t;
+
+void gmx_sum_qgrid_dd(gmx_pme_t* pme, real* grid, int direction);
+
+int copy_pmegrid_to_fftgrid(const gmx_pme_t* pme, const real* pmegrid, real* fftgrid, int grid_index);
+
+int copy_fftgrid_to_pmegrid(gmx_pme_t* pme, const real* fftgrid, real* pmegrid, int grid_index, int nthread, int thread);
+
+void wrap_periodic_pmegrid(const gmx_pme_t* pme, real* pmegrid);
+
+void unwrap_periodic_pmegrid(gmx_pme_t* pme, real* pmegrid);
+
+void pmegrid_init(pmegrid_t* grid,
+                  int        cx,
+                  int        cy,
+                  int        cz,
+                  int        x0,
+                  int        y0,
+                  int        z0,
+                  int        x1,
+                  int        y1,
+                  int        z1,
+                  gmx_bool   set_alignment,
+                  int        pme_order,
+                  real*      ptr);
+
+void pmegrids_init(pmegrids_t* grids,
+                   int         nx,
+                   int         ny,
+                   int         nz,
+                   int         nz_base,
+                   int         pme_order,
+                   gmx_bool    bUseThreads,
+                   int         nthread,
+                   int         overlap_x,
+                   int         overlap_y);
+
+void pmegrids_destroy(pmegrids_t* grids);
+
+void make_gridindex_to_localindex(int n, int local_start, int local_range, int** global_to_local, real** fraction_shift);
+
+void set_grid_alignment(int* pmegrid_nz, int pme_order);
+
+void reuse_pmegrids(const pmegrids_t* oldgrid, pmegrids_t* newgrid);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_internal.h b/src/include/gromacs/ewald/pme_internal.h
new file mode 100644 (file)
index 0000000..fad4c16
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file contains function declarations necessary for
+ * computing energies and forces for the PME long-ranged part (Coulomb
+ * and LJ).
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_ewald
+ */
+
+/* TODO This file is a temporary holding area for stuff local to the
+ * PME code, before it acquires some more normal ewald/file.c and
+ * ewald/file.h structure.  In future clean up, get rid of this file,
+ * to build more normal. */
+
+#ifndef GMX_EWALD_PME_INTERNAL_H
+#define GMX_EWALD_PME_INTERNAL_H
+
+#include "config.h"
+
+#include <vector>
+
+#include "gromacs/math/gmxcomplex.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/defaultinitializationallocator.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/gmxmpi.h"
+
+#include "spline_vectors.h"
+
+//! A repeat of typedef from parallel_3dfft.h
+typedef struct gmx_parallel_3dfft* gmx_parallel_3dfft_t;
+
+struct t_commrec;
+struct t_inputrec;
+struct PmeGpu;
+class EwaldBoxZScaler;
+enum class PmeRunMode;
+enum class LongRangeVdW : int;
+
+//@{
+//! Grid indices for A state for charge and Lennard-Jones C6
+#define PME_GRID_QA 0
+#define PME_GRID_C6A 2
+//@}
+
+//@{
+/*! \brief Flags that indicate the number of PME grids in use */
+#define DO_Q 2           /* Electrostatic grids have index q<2 */
+#define DO_Q_AND_LJ 4    /* non-LB LJ grids have index 2 <= q < 4 */
+#define DO_Q_AND_LJ_LB 9 /* With LB rules we need a total of 2+7 grids */
+//@}
+
+/*! \brief Pascal triangle coefficients scaled with (1/2)^6 for LJ-PME with LB-rules */
+static const real lb_scale_factor[] = { 1.0 / 64,  6.0 / 64, 15.0 / 64, 20.0 / 64,
+                                        15.0 / 64, 6.0 / 64, 1.0 / 64 };
+
+/*! \brief Pascal triangle coefficients used in solve_pme_lj_yzx, only need to do 4 calculations due to symmetry */
+static const real lb_scale_factor_symm[] = { 2.0 / 64, 12.0 / 64, 30.0 / 64, 20.0 / 64 };
+
+/*! \brief We only define a maximum to be able to use local arrays without allocation.
+ * An order larger than 12 should never be needed, even for test cases.
+ * If needed it can be changed here.
+ */
+#define PME_ORDER_MAX 12
+
+
+/* Temporary suppression until these structs become opaque and don't live in
+ * a header that is included by other headers. Also, until then I have no
+ * idea what some of the names mean. */
+
+//! @cond Doxygen_Suppress
+
+/*! \brief Data structure for grid communication */
+struct pme_grid_comm_t
+{
+    int send_id; //!< Source rank id
+    int send_index0;
+    int send_nindex;
+    int recv_id; //!< Destination rank id
+    int recv_index0;
+    int recv_nindex;
+    int recv_size = 0; //!< Receive buffer width, used with OpenMP
+};
+
+/*! \brief Data structure for grid overlap communication in a single dimension */
+struct pme_overlap_t
+{
+    MPI_Comm                     mpi_comm;  //!< MPI communcator
+    int                          nnodes;    //!< Number of ranks
+    int                          nodeid;    //!< Unique rank identifcator
+    std::vector<int>             s2g0;      //!< The local interpolation grid start
+    std::vector<int>             s2g1;      //!< The local interpolation grid end
+    int                          send_size; //!< Send buffer width, used with OpenMP
+    std::vector<pme_grid_comm_t> comm_data; //!< All the individual communication data for each rank
+    std::vector<real>            sendbuf;   //!< Shared buffer for sending
+    std::vector<real>            recvbuf;   //!< Shared buffer for receiving
+};
+
+template<typename T>
+using AlignedVector = std::vector<T, gmx::AlignedAllocator<T>>;
+
+template<typename T>
+using FastVector = std::vector<T, gmx::DefaultInitializationAllocator<T>>;
+
+/*! \brief Data structure for organizing particle allocation to threads */
+struct AtomToThreadMap
+{
+    //! Cumulative counts of the number of particles per thread
+    int* n = nullptr;
+    //! Storage buffer for n
+    std::vector<int> nBuffer;
+    //! Particle indices ordered on thread index (n)
+    FastVector<int> i;
+};
+
+/*! \internal
+ * \brief Coefficients for theta or dtheta
+ */
+class SplineCoefficients
+{
+public:
+    //! Reallocate for use with up to nalloc coefficients
+    void realloc(int nalloc);
+
+    //! Pointers to the coefficient buffer for x, y, z
+    splinevec coefficients = { nullptr };
+
+private:
+    //! Storage for x coefficients
+    std::vector<real> bufferX_;
+    //! Storage for y coefficients
+    std::vector<real> bufferY_;
+    //! Storage for z coefficients, aligned for SIMD load
+    AlignedVector<real> bufferZ_;
+};
+
+/*! \brief Data structure for beta-spline interpolation */
+struct splinedata_t
+{
+    int                n = 0;
+    FastVector<int>    ind;
+    SplineCoefficients theta;
+    SplineCoefficients dtheta;
+    int                nalloc = 0;
+};
+
+/*! \brief PME slab MPI communication setup */
+struct SlabCommSetup
+{
+    //! The nodes to send x and q to with DD
+    int node_dest;
+    //! The nodes to receive x and q from with DD
+    int node_src;
+    //! Index for commnode into the buffers
+    int buf_index;
+    //! The number of atoms to receive
+    int rcount;
+};
+
+/*! \internal
+ * \brief Data structure for coordinating transfers between PME ranks along one dimension
+ *
+ * Also used for passing coordinates, coefficients and forces to and from PME routines.
+ */
+class PmeAtomComm
+{
+public:
+    //! Constructor, \p PmeMpiCommunicator is the communicator for this dimension
+    PmeAtomComm(MPI_Comm PmeMpiCommunicator, int numThreads, int pmeOrder, int dimIndex, bool doSpread);
+
+    //! Set the atom count and when necessary resizes atom buffers
+    void setNumAtoms(int numAtoms);
+
+    //! Returns the atom count
+    int numAtoms() const { return numAtoms_; }
+
+    //! Returns the number of atoms to send to each rank
+    gmx::ArrayRef<int> sendCount()
+    {
+        GMX_ASSERT(!count_thread.empty(), "Need at least one thread_count");
+        return count_thread[0];
+    }
+
+    //! The index of the dimension, 0=x, 1=y
+    int dimind = 0;
+    //! The number of slabs and ranks this dimension is decomposed over
+    int nslab = 1;
+    //! Our MPI rank index
+    int nodeid = 0;
+    //! Communicator for this dimension
+    MPI_Comm mpi_comm;
+
+    //! Communication setup for each slab, only present with nslab > 1
+    std::vector<SlabCommSetup> slabCommSetup;
+    //! The maximum communication distance counted in MPI ranks
+    int maxshift = 0;
+
+    //! The target slab index for each particle
+    FastVector<int> pd;
+    //! Target particle counts for each slab, for each thread
+    std::vector<std::vector<int>> count_thread;
+
+private:
+    //! The number of atoms
+    int numAtoms_ = 0;
+
+public:
+    //! The coordinates
+    gmx::ArrayRef<const gmx::RVec> x;
+    //! The coefficient, charges or LJ C6
+    gmx::ArrayRef<const real> coefficient;
+    //! The forces
+    gmx::ArrayRef<gmx::RVec> f;
+    //! Coordinate buffer, used only with nslab > 1
+    FastVector<gmx::RVec> xBuffer;
+    //! Coefficient buffer, used only with nslab > 1
+    FastVector<real> coefficientBuffer;
+    //! Force buffer, used only with nslab > 1
+    FastVector<gmx::RVec> fBuffer;
+    //! Tells whether these coordinates are used for spreading
+    bool bSpread;
+    //! The PME order
+    int pme_order;
+    //! The grid index per atom
+    FastVector<gmx::IVec> idx;
+    //! Fractional atom coordinates relative to the lower cell boundary
+    FastVector<gmx::RVec> fractx;
+
+    //! The number of threads to use in PME
+    int nthread;
+    //! Thread index for each atom
+    FastVector<int>              thread_idx;
+    std::vector<AtomToThreadMap> threadMap;
+    std::vector<splinedata_t>    spline;
+};
+
+/*! \brief Data structure for a single PME grid */
+struct pmegrid_t
+{
+    ivec  ci;     /* The spatial location of this grid         */
+    ivec  n;      /* The used size of *grid, including order-1 */
+    ivec  offset; /* The grid offset from the full node grid   */
+    int   order;  /* PME spreading order                       */
+    ivec  s;      /* The allocated size of *grid, s >= n       */
+    real* grid;   /* The grid local thread, size n             */
+};
+
+/*! \brief Data structures for PME grids */
+struct pmegrids_t
+{
+    pmegrid_t  grid;         /* The full node grid (non thread-local)            */
+    int        nthread;      /* The number of threads operating on this grid     */
+    ivec       nc;           /* The local spatial decomposition over the threads */
+    pmegrid_t* grid_th;      /* Array of grids for each thread                   */
+    real*      grid_all;     /* Allocated array for the grids in *grid_th        */
+    int*       g2t[DIM];     /* The grid to thread index                         */
+    ivec       nthread_comm; /* The number of threads to communicate with        */
+};
+
+/*! \brief Data structure for spline-interpolation working buffers */
+struct pme_spline_work;
+
+/*! \brief Data structure for working buffers */
+struct pme_solve_work_t;
+
+/*! \brief Master PME data structure */
+struct gmx_pme_t
+{                   //NOLINT(clang-analyzer-optin.performance.Padding)
+    int ndecompdim; /* The number of decomposition dimensions */
+    int nodeid;     /* Our nodeid in mpi->mpi_comm */
+    int nodeid_major;
+    int nodeid_minor;
+    int nnodes; /* The number of nodes doing PME */
+    int nnodes_major;
+    int nnodes_minor;
+
+    MPI_Comm mpi_comm;
+    MPI_Comm mpi_comm_d[2]; /* Indexed on dimension, 0=x, 1=y */
+#if GMX_MPI
+    MPI_Datatype rvec_mpi; /* the pme vector's MPI type */
+#endif
+
+    bool bUseThreads; /* Does any of the PME ranks have nthread>1 ?  */
+    int  nthread;     /* The number of threads doing PME on our rank */
+
+    bool bPPnode;   /* Node also does particle-particle forces */
+    bool doCoulomb; /* Apply PME to electrostatics */
+    bool doLJ;      /* Apply PME to Lennard-Jones r^-6 interactions */
+    bool bFEP;      /* Compute Free energy contribution */
+    bool bFEP_q;
+    bool bFEP_lj;
+    int  nkx, nky, nkz; /* Grid dimensions */
+    bool bP3M;          /* Do P3M: optimize the influence function */
+    int  pme_order;
+    real ewaldcoeff_q;  /* Ewald splitting coefficient for Coulomb */
+    real ewaldcoeff_lj; /* Ewald splitting coefficient for r^-6 */
+    real epsilon_r;
+
+
+    enum PmeRunMode runMode; /* Which codepath is the PME runner taking - CPU, GPU, mixed;
+                              * TODO: this is the information that should be owned by the task
+                              * scheduler, and ideally not be duplicated here.
+                              */
+
+    PmeGpu* gpu; /* A pointer to the GPU data.
+                  * TODO: this should be unique or a shared pointer.
+                  * Currently in practice there is a single gmx_pme_t instance while a code
+                  * is partially set up for many of them. The PME tuning calls gmx_pme_reinit()
+                  * which fully reinitializes the one and only PME structure anew while maybe
+                  * keeping the old grid buffers if they were already large enough.
+                  * This small choice should be made clear in the later refactoring -
+                  * do we store many PME objects for different grid sizes,
+                  * or a single PME object that handles different grid sizes gracefully.
+                  */
+
+
+    std::unique_ptr<EwaldBoxZScaler> boxScaler; /**< The scaling data Ewald uses with walls (set at pme_init constant for the entire run) */
+
+
+    LongRangeVdW ljpme_combination_rule; /* Type of combination rule in LJ-PME */
+
+    int ngrids; /* number of grids we maintain for pmegrid, (c)fftgrid and pfft_setups*/
+
+    std::array<pmegrids_t, DO_Q_AND_LJ_LB> pmegrid; /* Grids on which we do spreading/interpolation,
+                                                     * includes overlap Grid indices are ordered as
+                                                     * follows:
+                                                     * 0: Coloumb PME, state A
+                                                     * 1: Coloumb PME, state B
+                                                     * 2-8: LJ-PME
+                                                     * This can probably be done in a better way
+                                                     * but this simple hack works for now
+                                                     */
+
+    /* The PME coefficient spreading grid sizes/strides, includes pme_order-1 */
+    int pmegrid_nx, pmegrid_ny, pmegrid_nz;
+    /* pmegrid_nz might be larger than strictly necessary to ensure
+     * memory alignment, pmegrid_nz_base gives the real base size.
+     */
+    int pmegrid_nz_base;
+    /* The local PME grid starting indices */
+    int pmegrid_start_ix, pmegrid_start_iy, pmegrid_start_iz;
+
+    /* Work data for spreading and gathering */
+    pme_spline_work* spline_work;
+
+    real** fftgrid; /* Grids for FFT. With 1D FFT decomposition this can be a pointer */
+
+    t_complex** cfftgrid; /* Grids for complex FFT data */
+
+    gmx_parallel_3dfft_t* pfft_setup;
+
+    int * nnx, *nny, *nnz;
+    real *fshx, *fshy, *fshz;
+
+    std::vector<PmeAtomComm> atc; /* Indexed on decomposition index */
+    matrix                   recipbox;
+    real                     boxVolume;
+    splinevec                bsp_mod;
+    /* Buffers to store data for local atoms for L-B combination rule
+     * calculations in LJ-PME. lb_buf1 stores either the coefficients
+     * for spreading/gathering (in serial), or the C6 coefficient for
+     * local atoms (in parallel).  lb_buf2 is only used in parallel,
+     * and stores the sigma values for local atoms. */
+    FastVector<real> lb_buf1;
+    FastVector<real> lb_buf2;
+
+    std::array<pme_overlap_t, 2> overlap; /* Indexed on dimension, 0=x, 1=y */
+
+    /* Atom step for energy only calculation in gmx_pme_calc_energy() */
+    std::unique_ptr<PmeAtomComm> atc_energy;
+
+    /* Communication buffers */
+    rvec* bufv;       /* Communication buffer */
+    real* bufr;       /* Communication buffer */
+    int   buf_nalloc; /* The communication buffer size */
+
+    /* thread local work data for solve_pme */
+    struct pme_solve_work_t* solve_work;
+};
+
+//! @endcond
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_load_balancing.h b/src/include/gromacs/ewald/pme_load_balancing.h
new file mode 100644 (file)
index 0000000..38ba5c7
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file contains function declarations necessary for
+ * managing automatic load balance of PME calculations (Coulomb and
+ * LJ).
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_LOAD_BALANCING_H
+#define GMX_EWALD_PME_LOAD_BALANCING_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/timing/wallcycle.h"
+
+struct nonbonded_verlet_t;
+struct t_commrec;
+struct t_forcerec;
+struct t_inputrec;
+struct interaction_const_t;
+struct gmx_pme_t;
+class t_state;
+
+namespace gmx
+{
+class MDLogger;
+template<typename T>
+class ArrayRef;
+} // namespace gmx
+
+/*! \brief Object to manage PME load balancing */
+struct pme_load_balancing_t;
+
+/*! \brief Return whether PME load balancing is active */
+bool pme_loadbal_is_active(const pme_load_balancing_t* pme_lb);
+
+/*! \brief Initialize the PP-PME load balacing data and infrastructure
+ *
+ * Initialize the PP-PME load balacing data and infrastructure.
+ * The actual load balancing might start right away, later or never.
+ * The PME grid in pmedata is reused for smaller grids to lower the memory
+ * usage.
+ */
+void pme_loadbal_init(pme_load_balancing_t**     pme_lb_p,
+                      t_commrec*                 cr,
+                      const gmx::MDLogger&       mdlog,
+                      const t_inputrec&          ir,
+                      const matrix               box,
+                      const interaction_const_t& ic,
+                      const nonbonded_verlet_t&  nbv,
+                      gmx_pme_t*                 pmedata,
+                      gmx_bool                   bUseGPU);
+
+/*! \brief Process cycles and PME load balance when necessary
+ *
+ * Process the cycles measured over the last nstlist steps and then
+ * either continue balancing or check if we need to trigger balancing.
+ * Should be called after the WallCycleCounter::Step cycle counter has been stopped.
+ * Returns if the load balancing is printing to fp_err.
+ */
+void pme_loadbal_do(pme_load_balancing_t*          pme_lb,
+                    struct t_commrec*              cr,
+                    FILE*                          fp_err,
+                    FILE*                          fp_log,
+                    const gmx::MDLogger&           mdlog,
+                    const t_inputrec&              ir,
+                    t_forcerec*                    fr,
+                    const matrix                   box,
+                    gmx::ArrayRef<const gmx::RVec> x,
+                    gmx_wallcycle*                 wcycle,
+                    int64_t                        step,
+                    int64_t                        step_rel,
+                    gmx_bool*                      bPrinting,
+                    bool                           useGpuPmePpCommunication);
+
+/*! \brief Finish the PME load balancing and print the settings when fplog!=NULL */
+void pme_loadbal_done(pme_load_balancing_t* pme_lb, FILE* fplog, const gmx::MDLogger& mdlog, gmx_bool bNonBondedOnGPU);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_only.h b/src/include/gromacs/ewald/pme_only.h
new file mode 100644 (file)
index 0000000..150a5da
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file contains function declarations necessary for
+ * running on an MPI rank doing only PME long-ranged work.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_ONLY_H
+#define GMX_EWALD_PME_ONLY_H
+
+#include <string>
+
+#include "gromacs/timing/walltime_accounting.h"
+
+struct t_commrec;
+struct t_inputrec;
+struct t_nrnb;
+struct gmx_pme_t;
+struct gmx_wallcycle;
+
+enum class PmeRunMode;
+namespace gmx
+{
+class DeviceStreamManager;
+}
+
+/*! \brief Called on the nodes that do PME exclusively */
+int gmx_pmeonly(gmx_pme_t*                      pme,
+                const t_commrec*                cr,
+                t_nrnb*                         mynrnb,
+                gmx_wallcycle*                  wcycle,
+                gmx_walltime_accounting_t       walltime_accounting,
+                t_inputrec*                     ir,
+                PmeRunMode                      runMode,
+                bool                            useGpuPmePpCommunication,
+                const gmx::DeviceStreamManager* deviceStreamManager);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_output.h b/src/include/gromacs/ewald/pme_output.h
new file mode 100644 (file)
index 0000000..bc8a501
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Defines a struct useful for transferring the PME output
+ * values
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_OUTPUT_H
+#define GMX_EWALD_PME_OUTPUT_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+
+// TODO There's little value in computing the Coulomb and LJ virial
+// separately, so we should simplify that.
+// TODO The matrices might be best as a view, but not currently
+// possible. Use mdspan?
+struct PmeOutput
+{
+    //!< Host staging area for PME forces
+    gmx::ArrayRef<gmx::RVec> forces_;
+    //!< True if forces have been staged other false (when forces are reduced on the GPU).
+    bool haveForceOutput_ = false;
+    //!< Host staging area for PME coulomb energy
+    real coulombEnergy_ = 0;
+    //!< Host staging area for PME coulomb virial contributions
+    matrix coulombVirial_ = { { 0 } };
+    //!< Host staging area for PME coulomb dVdl.
+    real coulombDvdl_ = 0;
+    //!< Host staging area for PME LJ energy
+    real lennardJonesEnergy_ = 0;
+    //!< Host staging area for PME LJ virial contributions
+    matrix lennardJonesVirial_ = { { 0 } };
+    //!< Host staging area for PME LJ dVdl. (Not used)
+    real lennardJonesDvdl_ = 0;
+};
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_pp.h b/src/include/gromacs/ewald/pme_pp.h
new file mode 100644 (file)
index 0000000..6c84023
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file contains function declarations necessary for
+ * mananging the PP side of PME-only ranks.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_PP_H
+#define GMX_EWALD_PME_PP_H
+
+#include "gromacs/math/vectypes.h"
+
+struct gmx_wallcycle;
+struct interaction_const_t;
+struct t_commrec;
+struct t_forcerec;
+
+class GpuEventSynchronizer;
+
+namespace gmx
+{
+class ForceWithVirial;
+class PmePpCommGpu;
+template<typename>
+class ArrayRef;
+} // namespace gmx
+
+/*! \brief Send the charges and maxshift to out PME-only node. */
+void gmx_pme_send_parameters(const t_commrec*           cr,
+                             const interaction_const_t& interactionConst,
+                             bool                       bFreeEnergy_q,
+                             bool                       bFreeEnergy_lj,
+                             gmx::ArrayRef<const real>  chargeA,
+                             gmx::ArrayRef<const real>  chargeB,
+                             gmx::ArrayRef<const real>  sqrt_c6A,
+                             gmx::ArrayRef<const real>  sqrt_c6B,
+                             gmx::ArrayRef<const real>  sigmaA,
+                             gmx::ArrayRef<const real>  sigmaB,
+                             int                        maxshift_x,
+                             int                        maxshift_y);
+
+/*! \brief Send the coordinates to our PME-only node and request a PME calculation */
+void gmx_pme_send_coordinates(t_forcerec*                    fr,
+                              const t_commrec*               cr,
+                              const matrix                   box,
+                              gmx::ArrayRef<const gmx::RVec> x,
+                              real                           lambda_q,
+                              real                           lambda_lj,
+                              bool                           computeEnergyAndVirial,
+                              int64_t                        step,
+                              bool                           useGpuPmePpComms,
+                              bool                           reinitGpuPmePpComms,
+                              bool                           sendCoordinatesFromGpu,
+                              bool                           receiveForcesToGpu,
+                              GpuEventSynchronizer*          coordinatesReadyOnDeviceEvent,
+                              gmx_wallcycle*                 wcycle);
+
+/*! \brief Tell our PME-only node to finish */
+void gmx_pme_send_finish(const t_commrec* cr);
+
+/*! \brief Tell our PME-only node to reset all cycle and flop counters */
+void gmx_pme_send_resetcounters(const t_commrec* cr, int64_t step);
+
+/*! \brief PP nodes receive the long range forces from the PME nodes */
+void gmx_pme_receive_f(gmx::PmePpCommGpu*    pmePpCommGpu,
+                       const t_commrec*      cr,
+                       gmx::ForceWithVirial* forceWithVirial,
+                       real*                 energy_q,
+                       real*                 energy_lj,
+                       real*                 dvdlambda_q,
+                       real*                 dvdlambda_lj,
+                       bool                  useGpuPmePpComms,
+                       bool                  receivePmeForceToGpu,
+                       float*                pme_cycles);
+
+/*! \brief Tell our PME-only node to switch to a new grid size */
+void gmx_pme_send_switchgrid(const t_commrec* cr, ivec grid_size, real ewaldcoeff_q, real ewaldcoeff_lj);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_pp_comm_gpu.h b/src/include/gromacs/ewald/pme_pp_comm_gpu.h
new file mode 100644 (file)
index 0000000..ca7cdce
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declaration of GPU PME-PP Communication.
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ * \inlibraryapi
+ * \ingroup module_ewald
+ */
+#ifndef GMX_PME_PP_COMM_GPU_H
+#define GMX_PME_PP_COMM_GPU_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/gmxmpi.h"
+
+class DeviceContext;
+class DeviceStream;
+class GpuEventSynchronizer;
+
+namespace gmx
+{
+
+class DeviceStreamManager;
+
+/*! \libinternal
+
+ * \brief Manages communication related to GPU buffers between this
+ * PME rank and its PP rank. */
+class PmePpCommGpu
+{
+
+public:
+    /*! \brief Creates PME-PP GPU communication object
+     * \param[in] comm              Communicator used for simulation
+     * \param[in] pmeRank           Rank of PME task
+     * \param[in] pmeCpuForceBuffer Buffer for PME force in CPU memory
+     * \param[in] deviceContext     GPU context.
+     * \param[in] deviceStream      GPU stream.
+     */
+    PmePpCommGpu(MPI_Comm                comm,
+                 int                     pmeRank,
+                 std::vector<gmx::RVec>* pmeCpuForceBuffer,
+                 const DeviceContext&    deviceContext,
+                 const DeviceStream&     deviceStream);
+    ~PmePpCommGpu();
+
+    /*! \brief Perform steps required when buffer size changes
+     * \param[in]  size   Number of elements in buffer
+     */
+    void reinit(int size);
+
+    /*! \brief
+     * Pull data from PME GPU directly using CUDA Memory copy.
+     * \param[out] recvPtr  Buffer to receive PME force data
+     * \param[in] recvSize Number of elements to receive
+     * \param[in] recvPmeForceToGpu Whether receive is to GPU, otherwise CPU
+     */
+    void receiveForceFromPme(RVec* recvPtr, int recvSize, bool recvPmeForceToGpu);
+
+    /*! \brief Push coordinates buffer directly to GPU memory on PME task
+     * \param[in] sendPtr Buffer with coordinate data
+     * \param[in] sendSize Number of elements to send
+     * \param[in] coordinatesReadyOnDeviceEvent Event recorded when coordinates are available on device
+     */
+    void sendCoordinatesToPmeFromGpu(DeviceBuffer<RVec>    sendPtr,
+                                     int                   sendSize,
+                                     GpuEventSynchronizer* coordinatesReadyOnDeviceEvent);
+
+    /*! \brief Push coordinates buffer from host memory directly to GPU memory on PME task
+     * \param[in] sendPtr Buffer with coordinate data
+     * \param[in] sendSize Number of elements to send
+     * \param[in] coordinatesReadyOnDeviceEvent Event recorded when coordinates are available on device
+     */
+    void sendCoordinatesToPmeFromCpu(RVec*                 sendPtr,
+                                     int                   sendSize,
+                                     GpuEventSynchronizer* coordinatesReadyOnDeviceEvent);
+
+    /*! \brief
+     * Return pointer to buffer used for staging PME force on GPU
+     */
+    DeviceBuffer<gmx::RVec> getGpuForceStagingPtr();
+
+    /*! \brief
+     * Return pointer to event recorded when forces are ready
+     */
+    GpuEventSynchronizer* getForcesReadySynchronizer();
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_pp_comm_gpu_impl.h b/src/include/gromacs/ewald/pme_pp_comm_gpu_impl.h
new file mode 100644 (file)
index 0000000..f4ec921
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares CUDA implementation class for PME-PP communications
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ *
+ * \ingroup module_ewald
+ */
+#ifndef GMX_PME_PP_COMM_GPU_IMPL_H
+#define GMX_PME_PP_COMM_GPU_IMPL_H
+
+#include <atomic>
+
+#include "gromacs/ewald/pme_pp_comm_gpu.h"
+#include "gromacs/gpu_utils/gpueventsynchronizer.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/gmxmpi.h"
+
+namespace gmx
+{
+
+/*! \internal \brief Class with interfaces and data for CUDA version of PME-PP Communication */
+class PmePpCommGpu::Impl
+{
+
+public:
+    /*! \brief Creates PME-PP GPU communication object.
+     *
+     * \param[in] comm              Communicator used for simulation
+     * \param[in] pmeRank           Rank of PME task
+     * \param[in] pmeCpuForceBuffer Buffer for PME force in CPU memory
+     * \param[in] deviceContext     GPU context.
+     * \param[in] deviceStream      GPU stream.
+     */
+    Impl(MPI_Comm                comm,
+         int                     pmeRank,
+         std::vector<gmx::RVec>* pmeCpuForceBuffer,
+         const DeviceContext&    deviceContext,
+         const DeviceStream&     deviceStream);
+    ~Impl();
+
+    /*! \brief Perform steps required when buffer size changes
+     * \param[in]  size   Number of elements in buffer
+     */
+    void reinit(int size);
+
+    /*! \brief Pull force buffer directly from GPU memory on PME
+     * rank to either GPU or CPU memory on PP task using CUDA
+     * Memory copy or CUDA-aware MPI.
+     *
+     * recvPtr should be in GPU or CPU memory if recvPmeForceToGpu
+     * is true or false, respectively. If receiving to GPU, this
+     * method should be called before the local GPU buffer
+     * operations. If receiving to CPU it should be called
+     * before forces are reduced with the other force
+     * contributions on the CPU. It will automatically wait for
+     * remote PME force data to be ready.
+     *
+     * \param[out] recvPtr CPU buffer to receive PME force data
+     * \param[in] recvSize Number of elements to receive
+     * \param[in] receivePmeForceToGpu Whether receive is to GPU, otherwise CPU
+     */
+    void receiveForceFromPme(float3* recvPtr, int recvSize, bool receivePmeForceToGpu);
+
+    /*! \brief Push coordinates buffer directly to GPU memory on PME
+     * task, from either GPU or CPU memory on PP task using CUDA
+     * Memory copy or CUDA-aware MPI. If sending from GPU, this method should
+     * be called after the local GPU coordinate buffer operations.
+     * The remote PME task will automatically wait for data to be copied
+     * before commencing PME force calculations.
+     * \param[in] sendPtr Buffer with coordinate data
+     * \param[in] sendSize Number of elements to send
+     * \param[in] coordinatesReadyOnDeviceEvent Event recorded when coordinates are available on device
+     */
+    void sendCoordinatesToPme(float3* sendPtr, int sendSize, GpuEventSynchronizer* coordinatesReadyOnDeviceEvent);
+
+    /*! \brief
+     * Return pointer to buffer used for staging PME force on GPU
+     */
+    DeviceBuffer<Float3> getGpuForceStagingPtr();
+
+    /*! \brief
+     * Return pointer to event recorded when forces are ready
+     */
+    GpuEventSynchronizer* getForcesReadySynchronizer();
+
+private:
+    /*! \brief Pull force buffer directly from GPU memory on PME
+     * rank to either GPU or CPU memory on PP task using CUDA
+     * Memory copy. This method is used with Thread-MPI.
+     * \param[in] receivePmeForceToGpu Whether receive is to GPU, otherwise CPU
+     */
+    void receiveForceFromPmeCudaDirect(bool receivePmeForceToGpu);
+
+    /*! \brief Pull force buffer directly from GPU memory on PME
+     * rank to either GPU or CPU memory on PP task using CUDA-aware
+     * MPI. This method is used with process-MPI.
+     * \param[out] recvPtr CPU buffer to receive PME force data
+     * \param[in] recvSize Number of elements to receive
+     */
+    void receiveForceFromPmeCudaMpi(float3* recvPtr, int recvSize);
+
+    /*! \brief Push coordinates buffer directly to GPU memory on PME
+     * task, from either GPU or CPU memory on PP task using CUDA Memory copy.
+     * This method is used with Thread-MPI.
+     * \param[in] sendPtr Buffer with coordinate data
+     * \param[in] sendSize Number of elements to send
+     * \param[in] coordinatesReadyOnDeviceEvent Event recorded when coordinates are available on device
+     */
+    void sendCoordinatesToPmeCudaDirect(float3*               sendPtr,
+                                        int                   sendSize,
+                                        GpuEventSynchronizer* coordinatesReadyOnDeviceEvent);
+
+    /*! \brief Push coordinates buffer directly to GPU memory on PME
+     * task, from either GPU or CPU memory on PP task using CUDA-aware MPI.
+     * This method is used with process-MPI.
+     * \param[in] sendPtr Buffer with coordinate data
+     * \param[in] sendSize Number of elements to send
+     * \param[in] coordinatesReadyOnDeviceEvent Event recorded when coordinates are available on device
+     */
+    void sendCoordinatesToPmeCudaMpi(float3*               sendPtr,
+                                     int                   sendSize,
+                                     GpuEventSynchronizer* coordinatesReadyOnDeviceEvent);
+
+    //! GPU context handle (not used in CUDA)
+    const DeviceContext& deviceContext_;
+    //! Handle for CUDA stream used for the communication operations in this class
+    const DeviceStream& pmePpCommStream_;
+    //! Remote location of PME coordinate data buffer
+    float3* remotePmeXBuffer_ = nullptr;
+    //! communicator for simulation
+    MPI_Comm comm_;
+    //! Rank of PME task
+    int pmeRank_ = -1;
+    //! Buffer for PME force on CPU
+    std::vector<gmx::RVec>* pmeCpuForceBuffer_;
+    //! Buffer for staging PME force on GPU
+    DeviceBuffer<gmx::RVec> d_pmeForces_;
+    //! number of atoms in PME force staging array
+    int d_pmeForcesSize_ = -1;
+    //! number of atoms allocated in recvbuf array
+    int d_pmeForcesSizeAlloc_ = -1;
+    //! Event recorded when PME forces are ready on PME task
+    GpuEventSynchronizer forcesReadySynchronizer_;
+    //! Event recorded when coordinates have been transferred to PME task
+    GpuEventSynchronizer pmeCoordinatesSynchronizer_;
+    //! Event recorded by remote PME task when forces have been transferred
+    GpuEventSynchronizer* remotePmeForceSendEvent_;
+    //! Flag to track when remote PP event has been recorded, ready for enqueueing
+    volatile std::atomic<bool>* remotePmeForceSendEventRecorded_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_pp_communication.h b/src/include/gromacs/ewald/pme_pp_communication.h
new file mode 100644 (file)
index 0000000..184ae94
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file contains declarations and constants necessary for
+ * coordinating the communication for the offload of long-ranged PME
+ * work to separate MPI rank, for computing energies and forces
+ * (Coulomb and LJ).
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdlib/sighandler.h"
+#include "gromacs/utility/real.h"
+
+/*! \brief MPI Tags used to separate communication of different types of quantities */
+enum
+{
+    eCommType_ChargeA,
+    eCommType_ChargeB,
+    eCommType_SQRTC6A,
+    eCommType_SQRTC6B,
+    eCommType_SigmaA,
+    eCommType_SigmaB,
+    eCommType_NR,
+    eCommType_COORD,
+    eCommType_COORD_GPU,
+    eCommType_CNB
+};
+
+//@{
+/*! \brief Flags used to coordinate PP-PME communication and computation phases
+ *
+ * Some parts of the code(gmx_pme_send_q, gmx_pme_recv_q_x) assume
+ * that the six first flags are exactly in this order.
+ */
+
+#define PP_PME_CHARGE (1 << 0)
+#define PP_PME_CHARGEB (1 << 1)
+#define PP_PME_SQRTC6 (1 << 2)
+#define PP_PME_SQRTC6B (1 << 3)
+#define PP_PME_SIGMA (1 << 4)
+#define PP_PME_SIGMAB (1 << 5)
+#define PP_PME_COORD (1 << 6)
+#define PP_PME_ENER_VIR (1 << 9)
+#define PP_PME_FINISH (1 << 10)
+#define PP_PME_SWITCHGRID (1 << 11)
+#define PP_PME_RESETCOUNTERS (1 << 12)
+#define PP_PME_GPUCOMMS (1 << 13)
+// Whether PME forces are transferred directly to remote PP GPU memory in a specific step
+#define PP_PME_RECVFTOGPU (1 << 14)
+//@}
+
+/*! \brief Return values for gmx_pme_recv_q_x */
+enum
+{
+    pmerecvqxX,            /* calculate PME mesh interactions for new x    */
+    pmerecvqxFINISH,       /* the simulation should finish, we should quit */
+    pmerecvqxSWITCHGRID,   /* change the PME grid size                     */
+    pmerecvqxRESETCOUNTERS /* reset the cycle and flop counters            */
+};
+
+/*! \internal
+ * \brief Helper struct for PP-PME communication of parameters.
+ *
+ * The contents are communicated over MPI in memcpy style, so should
+ * remain suitable for that.
+ */
+struct gmx_pme_comm_n_box_t
+{
+    int          natoms;     /**< Number of atoms */
+    matrix       box;        /**< Box */
+    int          maxshift_x; /**< Maximum shift in x direction */
+    int          maxshift_y; /**< Maximum shift in y direction */
+    real         lambda_q;   /**< Free-energy lambda for electrostatics */
+    real         lambda_lj;  /**< Free-energy lambda for Lennard-Jones */
+    unsigned int flags;      /**< Control flags */
+    int64_t      step;       /**< MD integration step number */
+    //@{
+    /*! \brief Used in PME grid tuning */
+    ivec grid_size;
+    real ewaldcoeff_q;
+    real ewaldcoeff_lj;
+    //@}
+};
+
+/*! \internal
+ * \brief Helper struct for PP-PME communication of virial and energy.
+ *
+ * The contents are communicated over MPI in memcpy style, so should
+ * remain suitable for that.
+ */
+struct gmx_pme_comm_vir_ene_t
+{
+    //@{
+    /*! \brief Virial, energy, and derivative of potential w.r.t. lambda for charge and Lennard-Jones */
+    matrix vir_q;
+    matrix vir_lj;
+    real   energy_q;
+    real   energy_lj;
+    real   dvdlambda_q;
+    real   dvdlambda_lj;
+    //@}
+    float         cycles;    /**< Counter of CPU cycles used */
+    StopCondition stop_cond; /**< Flag used in responding to an external signal to terminate */
+};
diff --git a/src/include/gromacs/ewald/pme_redistribute.h b/src/include/gromacs/ewald/pme_redistribute.h
new file mode 100644 (file)
index 0000000..1b277a8
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief Declares functions for redistributing atoms over the PME domains
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_PME_REDISTRIBUTE_H
+#define GMX_EWALD_PME_REDISTRIBUTE_H
+
+#include "pme_internal.h"
+
+//! Redistributes forces along the dimension gives by \p atc
+void dd_pmeredist_f(struct gmx_pme_t* pme, PmeAtomComm* atc, gmx::ArrayRef<gmx::RVec> f, gmx_bool bAddF);
+
+//! Redistributes coefficients and when \p bFirst=true coordinates over MPI ranks
+void do_redist_pos_coeffs(struct gmx_pme_t*              pme,
+                          const t_commrec*               cr,
+                          gmx_bool                       bFirst,
+                          gmx::ArrayRef<const gmx::RVec> x,
+                          gmx::ArrayRef<const real>      data);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_simd.h b/src/include/gromacs/ewald/pme_simd.h
new file mode 100644 (file)
index 0000000..03c03df
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_EWALD_PME_SIMD_H
+#define GMX_EWALD_PME_SIMD_H
+
+/* Include the SIMD macro file and then check for support */
+#include "gromacs/simd/simd.h"
+
+/* Check if we have 4-wide SIMD macro support */
+#if GMX_SIMD4_HAVE_REAL
+/* Do PME spread and gather with 4-wide SIMD.
+ * NOTE: SIMD is only used with PME order 4 and 5 (which are the most common).
+ */
+#    define PME_SIMD4_SPREAD_GATHER
+
+#    if GMX_SIMD_HAVE_LOADU && GMX_SIMD_HAVE_STOREU
+/* With PME-order=4 on x86, unaligned load+store is slightly faster
+ * than doubling all SIMD operations when using aligned load+store.
+ */
+#        define PME_SIMD4_UNALIGNED
+#    endif
+#endif
+
+#ifdef PME_SIMD4_SPREAD_GATHER
+#    define SIMD4_ALIGNMENT (GMX_SIMD4_WIDTH * sizeof(real))
+#else
+/* We can use any alignment, apart from 0, so we use 4 reals */
+#    define SIMD4_ALIGNMENT (4 * sizeof(real))
+#endif
+
+/* Check if we can use SIMD with packs of 4 for gather with order 4 */
+#if GMX_SIMD_HAVE_4NSIMD_UTIL_REAL && GMX_SIMD_REAL_WIDTH <= 16
+#    define PME_4NSIMD_GATHER 1
+#else
+#    define PME_4NSIMD_GATHER 0
+#endif
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_simd4.h b/src/include/gromacs/ewald/pme_simd4.h
new file mode 100644 (file)
index 0000000..a2c8e3b
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/* This include file has code between ifdef's to make sure
+ * that this performance sensitive code is inlined
+ * and to remove conditionals and variable loop bounds at compile time.
+ */
+
+#ifdef PME_SPREAD_SIMD4_ORDER4
+/* Spread one charge with pme_order=4 with unaligned SIMD4 load+store.
+ * This code does not assume any memory alignment for the grid.
+ */
+{
+    using namespace gmx;
+    Simd4Real ty_S0(thy[0]);
+    Simd4Real ty_S1(thy[1]);
+    Simd4Real ty_S2(thy[2]);
+    Simd4Real ty_S3(thy[3]);
+    Simd4Real tz_S;
+    Simd4Real vx_S;
+    Simd4Real vx_tz_S;
+    Simd4Real sum_S0, sum_S1, sum_S2, sum_S3;
+    Simd4Real gri_S0, gri_S1, gri_S2, gri_S3;
+
+    /* With order 4 the z-spline is actually aligned */
+    tz_S = load4(thz);
+
+    for (ithx = 0; (ithx < 4); ithx++)
+    {
+        index_x = (i0 + ithx) * pny * pnz;
+        valx    = coefficient * thx[ithx];
+
+        vx_S = Simd4Real(valx);
+
+        vx_tz_S = vx_S * tz_S;
+
+        gri_S0 = load4U(grid + index_x + (j0 + 0) * pnz + k0);
+        gri_S1 = load4U(grid + index_x + (j0 + 1) * pnz + k0);
+        gri_S2 = load4U(grid + index_x + (j0 + 2) * pnz + k0);
+        gri_S3 = load4U(grid + index_x + (j0 + 3) * pnz + k0);
+
+        sum_S0 = fma(vx_tz_S, ty_S0, gri_S0);
+        sum_S1 = fma(vx_tz_S, ty_S1, gri_S1);
+        sum_S2 = fma(vx_tz_S, ty_S2, gri_S2);
+        sum_S3 = fma(vx_tz_S, ty_S3, gri_S3);
+
+        store4U(grid + index_x + (j0 + 0) * pnz + k0, sum_S0);
+        store4U(grid + index_x + (j0 + 1) * pnz + k0, sum_S1);
+        store4U(grid + index_x + (j0 + 2) * pnz + k0, sum_S2);
+        store4U(grid + index_x + (j0 + 3) * pnz + k0, sum_S3);
+    }
+}
+#    undef PME_SPREAD_SIMD4_ORDER4
+#endif
+
+
+#ifdef PME_SPREAD_SIMD4_ALIGNED
+/* This code assumes that the grid is allocated 4-real aligned
+ * and that pnz is a multiple of 4.
+ * This code supports pme_order <= 5.
+ */
+{
+    using namespace gmx;
+    int       offset;
+    int       index;
+    Simd4Real ty_S0(thy[0]);
+    Simd4Real ty_S1(thy[1]);
+    Simd4Real ty_S2(thy[2]);
+    Simd4Real ty_S3(thy[3]);
+    Simd4Real tz_S0;
+    Simd4Real tz_S1;
+    Simd4Real vx_S;
+    Simd4Real vx_tz_S0;
+    Simd4Real vx_tz_S1;
+    Simd4Real sum_S00, sum_S01, sum_S02, sum_S03;
+    Simd4Real sum_S10, sum_S11, sum_S12, sum_S13;
+    Simd4Real gri_S00, gri_S01, gri_S02, gri_S03;
+    Simd4Real gri_S10, gri_S11, gri_S12, gri_S13;
+#    if PME_ORDER == 5
+    Simd4Real ty_S4(thy[4]);
+    Simd4Real sum_S04;
+    Simd4Real sum_S14;
+    Simd4Real gri_S04;
+    Simd4Real gri_S14;
+#    endif
+
+    offset = k0 & 3;
+
+#    ifdef PME_SIMD4_UNALIGNED
+    tz_S0 = load4U(thz - offset);
+    tz_S1 = load4U(thz - offset + 4);
+#    else
+    {
+        int i;
+        /* Copy thz to an aligned buffer (unused buffer parts are masked) */
+        for (i = 0; i < PME_ORDER; i++)
+        {
+            thz_aligned[offset + i] = thz[i];
+        }
+        tz_S0 = load4(thz_aligned);
+        tz_S1 = load4(thz_aligned + 4);
+    }
+#    endif
+    tz_S0 = selectByMask(tz_S0, work->mask_S0[offset]);
+    tz_S1 = selectByMask(tz_S1, work->mask_S1[offset]);
+
+    for (ithx = 0; (ithx < PME_ORDER); ithx++)
+    {
+        index = (i0 + ithx) * pny * pnz + j0 * pnz + k0 - offset;
+        valx  = coefficient * thx[ithx];
+
+        vx_S = Simd4Real(valx);
+
+        vx_tz_S0 = vx_S * tz_S0;
+        vx_tz_S1 = vx_S * tz_S1;
+
+        gri_S00 = load4(grid + index + 0 * pnz);
+        gri_S01 = load4(grid + index + 1 * pnz);
+        gri_S02 = load4(grid + index + 2 * pnz);
+        gri_S03 = load4(grid + index + 3 * pnz);
+#    if PME_ORDER == 5
+        gri_S04 = load4(grid + index + 4 * pnz);
+#    endif
+        gri_S10 = load4(grid + index + 0 * pnz + 4);
+        gri_S11 = load4(grid + index + 1 * pnz + 4);
+        gri_S12 = load4(grid + index + 2 * pnz + 4);
+        gri_S13 = load4(grid + index + 3 * pnz + 4);
+#    if PME_ORDER == 5
+        gri_S14 = load4(grid + index + 4 * pnz + 4);
+#    endif
+
+        sum_S00 = fma(vx_tz_S0, ty_S0, gri_S00);
+        sum_S01 = fma(vx_tz_S0, ty_S1, gri_S01);
+        sum_S02 = fma(vx_tz_S0, ty_S2, gri_S02);
+        sum_S03 = fma(vx_tz_S0, ty_S3, gri_S03);
+#    if PME_ORDER == 5
+        sum_S04 = fma(vx_tz_S0, ty_S4, gri_S04);
+#    endif
+        sum_S10 = fma(vx_tz_S1, ty_S0, gri_S10);
+        sum_S11 = fma(vx_tz_S1, ty_S1, gri_S11);
+        sum_S12 = fma(vx_tz_S1, ty_S2, gri_S12);
+        sum_S13 = fma(vx_tz_S1, ty_S3, gri_S13);
+#    if PME_ORDER == 5
+        sum_S14 = fma(vx_tz_S1, ty_S4, gri_S14);
+#    endif
+
+        store4(grid + index + 0 * pnz, sum_S00);
+        store4(grid + index + 1 * pnz, sum_S01);
+        store4(grid + index + 2 * pnz, sum_S02);
+        store4(grid + index + 3 * pnz, sum_S03);
+#    if PME_ORDER == 5
+        store4(grid + index + 4 * pnz, sum_S04);
+#    endif
+        store4(grid + index + 0 * pnz + 4, sum_S10);
+        store4(grid + index + 1 * pnz + 4, sum_S11);
+        store4(grid + index + 2 * pnz + 4, sum_S12);
+        store4(grid + index + 3 * pnz + 4, sum_S13);
+#    if PME_ORDER == 5
+        store4(grid + index + 4 * pnz + 4, sum_S14);
+#    endif
+    }
+}
+#    undef PME_ORDER
+#    undef PME_SPREAD_SIMD4_ALIGNED
+#endif
diff --git a/src/include/gromacs/ewald/pme_solve.h b/src/include/gromacs/ewald/pme_solve.h
new file mode 100644 (file)
index 0000000..61da89d
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_EWALD_PME_SOLVE_H
+#define GMX_EWALD_PME_SOLVE_H
+
+#include "gromacs/math/gmxcomplex.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct pme_solve_work_t;
+struct gmx_pme_t;
+struct PmeOutput;
+
+/*! \brief Allocates array of work structures
+ *
+ * Note that work is the address of a pointer allocated by
+ * this function. Upon return it will point at
+ * an array of work structures.
+ */
+void pme_init_all_work(struct pme_solve_work_t** work, int nthread, int nkx);
+
+/*! \brief Frees array of work structures
+ *
+ * Frees work and sets it to NULL. */
+void pme_free_all_work(struct pme_solve_work_t** work, int nthread);
+
+/*! \brief Get energy and virial for electrostatics
+ *
+ * Note that work is an array of work structures
+ */
+void get_pme_ener_vir_q(pme_solve_work_t* work, int nthread, PmeOutput* output);
+
+/*! \brief Get energy and virial for L-J
+ *
+ * Note that work is an array of work structures
+ */
+void get_pme_ener_vir_lj(pme_solve_work_t* work, int nthread, PmeOutput* output);
+
+int solve_pme_yzx(const gmx_pme_t* pme, t_complex* grid, real vol, bool computeEnergyAndVirial, int nthread, int thread);
+
+int solve_pme_lj_yzx(const gmx_pme_t* pme,
+                     t_complex**      grid,
+                     gmx_bool         bLB,
+                     real             vol,
+                     bool             computeEnergyAndVirial,
+                     int              nthread,
+                     int              thread);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_spline_work.h b/src/include/gromacs/ewald/pme_spline_work.h
new file mode 100644 (file)
index 0000000..cef9cec
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_EWALD_PME_SPLINE_WORK_H
+#define GMX_EWALD_PME_SPLINE_WORK_H
+
+#include "gromacs/simd/simd.h"
+
+#include "pme_simd.h"
+
+struct pme_spline_work
+{
+#ifdef PME_SIMD4_SPREAD_GATHER
+    /* Masks for 4-wide SIMD aligned spreading and gathering */
+    gmx::Simd4Bool mask_S0[6], mask_S1[6];
+#else
+    int dummy; /* C89 requires that struct has at least one member */
+#endif
+};
+
+pme_spline_work* make_pme_spline_work(int order);
+
+void destroy_pme_spline_work(pme_spline_work* work);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_spread.h b/src/include/gromacs/ewald/pme_spread.h
new file mode 100644 (file)
index 0000000..93e5252
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_EWALD_PME_SPREAD_H
+#define GMX_EWALD_PME_SPREAD_H
+
+#include "gromacs/utility/real.h"
+
+#include "pme_internal.h"
+
+void spread_on_grid(const gmx_pme_t*  pme,
+                    PmeAtomComm*      atc,
+                    const pmegrids_t* grids,
+                    gmx_bool          bCalcSplines,
+                    gmx_bool          bSpread,
+                    real*             fftgrid,
+                    gmx_bool          bDoSplines,
+                    int               grid_index);
+
+#endif
diff --git a/src/include/gromacs/ewald/pme_spread_sycl.h b/src/include/gromacs/ewald/pme_spread_sycl.h
new file mode 100644 (file)
index 0000000..fdf65fb
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Implements PME GPU spline calculation and charge spreading in SYCL.
+ *  TODO: consider always pre-sorting particles (as in DD case).
+ *
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ */
+
+#include "gromacs/gpu_utils/gmxsycl.h"
+
+#include "gromacs/gpu_utils/syclutils.h"
+
+#include "pme_grid.h"
+#include "pme_gpu_types_host.h"
+
+struct PmeGpuGridParams;
+struct PmeGpuAtomParams;
+struct PmeGpuDynamicParams;
+
+template<int order, bool computeSplines, bool spreadCharges, bool wrapX, bool wrapY, int numGrids, bool writeGlobal, ThreadsPerAtom threadsPerAtom, int subGroupSize>
+class PmeSplineAndSpreadKernel : public ISyclKernelFunctor
+{
+public:
+    PmeSplineAndSpreadKernel();
+    void            setArg(size_t argIndex, void* arg) override;
+    cl::sycl::event launch(const KernelLaunchConfig& config, const DeviceStream& deviceStream) override;
+
+private:
+    PmeGpuGridParams*    gridParams_;
+    PmeGpuAtomParams*    atomParams_;
+    PmeGpuDynamicParams* dynamicParams_;
+    void                 reset();
+};
diff --git a/src/include/gromacs/ewald/spline_vectors.h b/src/include/gromacs/ewald/spline_vectors.h
new file mode 100644 (file)
index 0000000..24b0b55
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file declares a type useful for spline vectors
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_ewald
+ */
+
+#ifndef GMX_EWALD_SPLINE_VECTORS_H
+#define GMX_EWALD_SPLINE_VECTORS_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+/*! \brief Helper typedef for spline vectors */
+typedef real* splinevec[DIM];
+
+#endif
diff --git a/src/include/gromacs/ewald/tests/pmetestcommon.h b/src/include/gromacs/ewald/tests/pmetestcommon.h
new file mode 100644 (file)
index 0000000..6b1e6d1
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Describes common routines and types for PME tests.
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_ewald
+ */
+#ifndef GMX_EWALD_PME_TEST_COMMON_H
+#define GMX_EWALD_PME_TEST_COMMON_H
+
+#include <array>
+#include <map>
+#include <vector>
+
+#include "gromacs/ewald/pme.h"
+#include "gromacs/ewald/pme_gpu_internal.h"
+#include "gromacs/math/gmxcomplex.h"
+#include "gromacs/mdtypes/state_propagator_data_gpu.h"
+#include "gromacs/utility/unique_cptr.h"
+
+#include "testutils/test_device.h"
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+
+namespace test
+{
+
+//! Hardware code path being tested
+enum class CodePath : int
+{
+    //! CPU code path
+    CPU,
+    //! GPU code path
+    GPU,
+    //! Total number of code paths
+    Count
+};
+
+//! Return a string useful for human-readable messages describing a \c codePath.
+const char* codePathToString(CodePath codePath);
+
+// Convenience typedefs
+//! A safe pointer type for PME.
+typedef gmx::unique_cptr<gmx_pme_t, gmx_pme_destroy> PmeSafePointer;
+//! Charges
+typedef ArrayRef<const real> ChargesVector;
+//! Coordinates
+typedef std::vector<RVec> CoordinatesVector;
+//! Forces
+typedef ArrayRef<RVec> ForcesVector;
+//! Gridline indices
+typedef ArrayRef<const IVec> GridLineIndicesVector;
+/*! \brief Spline parameters (theta or dtheta).
+ * A reference to a single dimension's spline data; this means (atomCount * pmeOrder) values or derivatives.
+ */
+typedef ArrayRef<const real> SplineParamsDimVector;
+/*! \brief Spline parameters (theta or dtheta) in all 3 dimensions
+ */
+typedef std::array<SplineParamsDimVector, DIM> SplineParamsVector;
+
+//! Non-zero grid values for test input; keys are 3d indices (IVec)
+template<typename ValueType>
+using SparseGridValuesInput = std::map<IVec, ValueType>;
+//! Non-zero real grid values
+typedef SparseGridValuesInput<real> SparseRealGridValuesInput;
+//! Non-zero complex grid values
+typedef SparseGridValuesInput<t_complex> SparseComplexGridValuesInput;
+//! Non-zero grid values for test output; keys are string representations of the cells' 3d indices (IVec); this allows for better sorting.
+template<typename ValueType>
+using SparseGridValuesOutput = std::map<std::string, ValueType>;
+//! Non-zero real grid values
+typedef SparseGridValuesOutput<real> SparseRealGridValuesOutput;
+//! Non-zero complex grid values
+typedef SparseGridValuesOutput<t_complex> SparseComplexGridValuesOutput;
+//! TODO: make proper C++ matrix for the whole Gromacs, get rid of this
+typedef std::array<real, DIM * DIM> Matrix3x3;
+//! PME solver type
+enum class PmeSolveAlgorithm : int
+{
+    //! Coulomb electrostatics
+    Coulomb,
+    //! Lennard-Jones
+    LennardJones,
+    //! Total number of solvers
+    Count
+};
+
+// Misc.
+
+//! Tells if this generally valid PME input is supported for this mode
+bool pmeSupportsInputForMode(const gmx_hw_info_t& hwinfo, const t_inputrec* inputRec, CodePath mode);
+
+//! Spline moduli are computed in double precision, so they're very good in single precision
+constexpr int64_t c_splineModuliSinglePrecisionUlps = 1;
+/*! \brief For double precision checks, the recursive interpolation
+ * and use of trig functions in make_dft_mod require a lot more flops,
+ * and thus opportunity for deviation between implementations. */
+uint64_t getSplineModuliDoublePrecisionUlps(int splineOrder);
+
+// PME stages
+
+//! PME initialization
+PmeSafePointer pmeInitWrapper(const t_inputrec*    inputRec,
+                              CodePath             mode,
+                              const DeviceContext* deviceContext,
+                              const DeviceStream*  deviceStream,
+                              const PmeGpuProgram* pmeGpuProgram,
+                              const Matrix3x3&     box,
+                              real                 ewaldCoeff_q  = 1.0F,
+                              real                 ewaldCoeff_lj = 1.0F);
+
+//! Simple PME initialization based on inputrec only
+PmeSafePointer pmeInitEmpty(const t_inputrec* inputRec);
+
+//! Make a GPU state-propagator manager
+std::unique_ptr<StatePropagatorDataGpu> makeStatePropagatorDataGpu(const gmx_pme_t&     pme,
+                                                                   const DeviceContext* deviceContext,
+                                                                   const DeviceStream* deviceStream);
+//! PME initialization with atom data and system box
+void pmeInitAtoms(gmx_pme_t*               pme,
+                  StatePropagatorDataGpu*  stateGpu,
+                  CodePath                 mode,
+                  const CoordinatesVector& coordinates,
+                  const ChargesVector&     charges);
+//! PME spline computation and charge spreading
+void pmePerformSplineAndSpread(gmx_pme_t* pme, CodePath mode, bool computeSplines, bool spreadCharges);
+//! PME solving
+void pmePerformSolve(const gmx_pme_t*  pme,
+                     CodePath          mode,
+                     PmeSolveAlgorithm method,
+                     real              cellVolume,
+                     GridOrdering      gridOrdering,
+                     bool              computeEnergyAndVirial);
+//! PME force gathering
+void pmePerformGather(gmx_pme_t*    pme,
+                      CodePath      mode,
+                      ForcesVector& forces); //NOLINT(google-runtime-references)
+//! PME test finalization before fetching the outputs
+void pmeFinalizeTest(const gmx_pme_t* pme, CodePath mode);
+
+// PME state setters
+
+//! Setting atom spline values or derivatives to be used in spread/gather
+void pmeSetSplineData(const gmx_pme_t*             pme,
+                      CodePath                     mode,
+                      const SplineParamsDimVector& splineValues,
+                      PmeSplineDataType            type,
+                      int                          dimIndex);
+
+//! Setting gridline indices be used in spread/gather
+void pmeSetGridLineIndices(gmx_pme_t* pme, CodePath mode, const GridLineIndicesVector& gridLineIndices);
+//! Setting real grid to be used in gather
+void pmeSetRealGrid(const gmx_pme_t* pme, CodePath mode, const SparseRealGridValuesInput& gridValues);
+void pmeSetComplexGrid(const gmx_pme_t*                    pme,
+                       CodePath                            mode,
+                       GridOrdering                        gridOrdering,
+                       const SparseComplexGridValuesInput& gridValues);
+
+// PME state getters
+
+//! Getting the single dimension's spline values or derivatives
+SplineParamsDimVector pmeGetSplineData(const gmx_pme_t* pme, CodePath mode, PmeSplineDataType type, int dimIndex);
+//! Getting the gridline indices
+GridLineIndicesVector pmeGetGridlineIndices(const gmx_pme_t* pme, CodePath mode);
+//! Getting the real grid (spreading output of pmePerformSplineAndSpread())
+SparseRealGridValuesOutput pmeGetRealGrid(const gmx_pme_t* pme, CodePath mode);
+//! Getting the complex grid output of pmePerformSolve()
+SparseComplexGridValuesOutput pmeGetComplexGrid(const gmx_pme_t* pme, CodePath mode, GridOrdering gridOrdering);
+//! Getting the reciprocal energy and virial
+PmeOutput pmeGetReciprocalEnergyAndVirial(const gmx_pme_t* pme, CodePath mode, PmeSolveAlgorithm method);
+
+struct PmeTestHardwareContext
+{
+    //! Hardware path for the code being tested.
+    CodePath codePath_;
+    //! Returns a human-readable context description line
+    std::string description() const;
+    //! Pointer to the global test hardware device (if on GPU)
+    TestDevice* testDevice_ = nullptr;
+    //! PME GPU program if needed
+    PmeGpuProgramStorage pmeGpuProgram_ = nullptr;
+    // Constructor for CPU context
+    PmeTestHardwareContext();
+    // Constructor for GPU context
+    explicit PmeTestHardwareContext(TestDevice* testDevice);
+
+    //! Get the code path
+    CodePath codePath() const { return codePath_; }
+    //! Get the PME GPU program
+    const PmeGpuProgram* pmeGpuProgram() const
+    {
+        return codePath() == CodePath::GPU ? pmeGpuProgram_.get() : nullptr;
+    }
+
+    const DeviceContext* deviceContext() const
+    {
+        return codePath() == CodePath::GPU ? &testDevice_->deviceContext() : nullptr;
+    }
+
+    const DeviceStream* deviceStream() const
+    {
+        return codePath() == CodePath::GPU ? &testDevice_->deviceStream() : nullptr;
+    }
+
+    //! Activate the context (set the device)
+    void activate() const;
+};
+
+std::vector<std::unique_ptr<PmeTestHardwareContext>> createPmeTestHardwareContextList();
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_EWALD_PME_TEST_COMMON_H
diff --git a/src/include/gromacs/external/boost/stl_interfaces/fwd.hpp b/src/include/gromacs/external/boost/stl_interfaces/fwd.hpp
new file mode 100644 (file)
index 0000000..aa2f478
--- /dev/null
@@ -0,0 +1,98 @@
+// Copyright (C) 2019 T. Zachary Laine
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_STL_INTERFACES_FWD_HPP
+#define BOOST_STL_INTERFACES_FWD_HPP
+
+#include <iterator>
+
+#ifndef BOOST_STL_INTERFACES_DOXYGEN
+
+// It is not known whether intel has fixed this issue, but it was
+// observed to be present in icc 19.1.2.20200623. Update the version
+// check when it is fixed.
+#if defined(_MSC_VER) || defined(__GNUC__) && __GNUC__ < 8 || defined(__INTEL_COMPILER) && __INTEL_COMPILER < 1920
+#define BOOST_STL_INTERFACES_NO_HIDDEN_FRIEND_CONSTEXPR
+#define BOOST_STL_INTERFACES_HIDDEN_FRIEND_CONSTEXPR
+#else
+#define BOOST_STL_INTERFACES_HIDDEN_FRIEND_CONSTEXPR constexpr
+#endif
+
+#if defined(__GNUC__) && __GNUC__ < 9
+#define BOOST_STL_INTERFACES_CONCEPT concept bool
+#else
+#define BOOST_STL_INTERFACES_CONCEPT concept
+#endif
+
+#endif
+
+
+namespace boost { namespace stl_interfaces {
+    inline namespace v1 {
+
+        /** An enumeration used to indicate whether the underlying data have a
+            contiguous or discontiguous layout when instantiating
+            `view_interface` and `sequence_container_interface`. */
+        enum class element_layout : bool {
+            discontiguous = false,
+            contiguous = true
+        };
+
+        namespace v1_dtl {
+            template<typename... T>
+            using void_t = void;
+
+            template<typename Iter>
+            using iter_difference_t =
+                typename std::iterator_traits<Iter>::difference_type;
+
+            template<typename Range, typename = void>
+            struct iterator;
+            template<typename Range>
+            struct iterator<
+                Range,
+                void_t<decltype(std::declval<Range &>().begin())>>
+            {
+                using type = decltype(std::declval<Range &>().begin());
+            };
+            template<typename Range>
+            using iterator_t = typename iterator<Range>::type;
+
+            template<typename Range, typename = void>
+            struct sentinel;
+            template<typename Range>
+            struct sentinel<
+                Range,
+                void_t<decltype(std::declval<Range &>().end())>>
+            {
+                using type = decltype(std::declval<Range &>().end());
+            };
+            template<typename Range>
+            using sentinel_t = typename sentinel<Range>::type;
+
+            template<typename Range>
+            using range_difference_t = iter_difference_t<iterator_t<Range>>;
+
+            template<typename Range>
+            using common_range =
+                std::is_same<iterator_t<Range>, sentinel_t<Range>>;
+
+            template<typename Range, typename = void>
+            struct decrementable_sentinel : std::false_type
+            {
+            };
+            template<typename Range>
+            struct decrementable_sentinel<
+                Range,
+                void_t<decltype(--std::declval<sentinel_t<Range> &>())>>
+                : std::true_type
+            {
+            };
+        }
+
+    }
+}}
+
+#endif
diff --git a/src/include/gromacs/external/boost/stl_interfaces/iterator_interface.hpp b/src/include/gromacs/external/boost/stl_interfaces/iterator_interface.hpp
new file mode 100644 (file)
index 0000000..5f39619
--- /dev/null
@@ -0,0 +1,623 @@
+// Copyright (C) 2019 T. Zachary Laine
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+#ifndef BOOST_STL_INTERFACES_ITERATOR_INTERFACE_HPP
+#define BOOST_STL_INTERFACES_ITERATOR_INTERFACE_HPP
+
+#include "./fwd.hpp"
+
+#include <utility>
+#include <type_traits>
+#if defined(__cpp_lib_three_way_comparison)
+#include <compare>
+#endif
+
+
+namespace boost { namespace stl_interfaces {
+
+    /** A type for granting access to the private members of an iterator
+        derived from `iterator_interface`. */
+    struct access
+    {
+#ifndef BOOST_STL_INTERFACES_DOXYGEN
+
+        template<typename D>
+        static constexpr auto base(D & d) noexcept
+            -> decltype(d.base_reference())
+        {
+            return d.base_reference();
+        }
+        template<typename D>
+        static constexpr auto base(D const & d) noexcept
+            -> decltype(d.base_reference())
+        {
+            return d.base_reference();
+        }
+
+#endif
+    };
+
+    /** The return type of `operator->()` in a proxy iterator.
+
+        This template is used as the default `Pointer` template parameter in
+        the `proxy_iterator_interface` template alias.  Note that the use of
+        this template implies a copy or move of the underlying object of type
+        `T`. */
+    template<typename T>
+    struct proxy_arrow_result
+    {
+        constexpr proxy_arrow_result(T const & value) noexcept(
+            noexcept(T(value))) :
+            value_(value)
+        {}
+        constexpr proxy_arrow_result(T && value) noexcept(
+            noexcept(T(std::move(value)))) :
+            value_(std::move(value))
+        {}
+
+        constexpr T const * operator->() const noexcept { return &value_; }
+        constexpr T * operator->() noexcept { return &value_; }
+
+    private:
+        T value_;
+    };
+
+    namespace detail {
+        template<typename Pointer, typename T>
+        auto make_pointer(
+            T && value,
+            std::enable_if_t<std::is_pointer<Pointer>::value, int> = 0)
+            -> decltype(std::addressof(value))
+        {
+            return std::addressof(value);
+        }
+
+        template<typename Pointer, typename T>
+        auto make_pointer(
+            T && value,
+            std::enable_if_t<!std::is_pointer<Pointer>::value, int> = 0)
+        {
+            return Pointer(std::forward<T>(value));
+        }
+
+        template<typename IteratorConcept>
+        struct concept_category
+        {
+            using type = IteratorConcept;
+        };
+        template<typename IteratorConcept>
+        using concept_category_t =
+            typename concept_category<IteratorConcept>::type;
+
+        template<typename Pointer, typename IteratorConcept>
+        struct pointer
+        {
+            using type = Pointer;
+        };
+        template<typename Pointer>
+        struct pointer<Pointer, std::output_iterator_tag>
+        {
+            using type = void;
+        };
+        template<typename Pointer, typename IteratorConcept>
+        using pointer_t = typename pointer<Pointer, IteratorConcept>::type;
+
+        template<typename T, typename U>
+        using interoperable = std::integral_constant<
+            bool,
+            (std::is_convertible<T, U>::value ||
+             std::is_convertible<U, T>::value)>;
+
+        template<typename T, typename U>
+        using common_t =
+            std::conditional_t<std::is_convertible<T, U>::value, U, T>;
+
+        template<typename T>
+        using use_base = decltype(access::base(std::declval<T &>()));
+
+        template<typename... T>
+        using void_t = void;
+
+        template<
+            typename AlwaysVoid,
+            template<class...> class Template,
+            typename... Args>
+        struct detector : std::false_type
+        {
+        };
+
+        template<template<class...> class Template, typename... Args>
+        struct detector<void_t<Template<Args...>>, Template, Args...>
+            : std::true_type
+        {
+        };
+
+        template<
+            typename T,
+            typename U,
+            bool UseBase = detector<void, use_base, T>::value>
+        struct common_eq
+        {
+            static constexpr auto call(T lhs, U rhs)
+            {
+                return static_cast<common_t<T, U>>(lhs).derived() ==
+                       static_cast<common_t<T, U>>(rhs).derived();
+            }
+        };
+        template<typename T, typename U>
+        struct common_eq<T, U, true>
+        {
+            static constexpr auto call(T lhs, U rhs)
+            {
+                return access::base(lhs) == access::base(rhs);
+            }
+        };
+
+        template<typename T, typename U>
+        constexpr auto common_diff(T lhs, U rhs) noexcept(noexcept(
+            static_cast<common_t<T, U>>(lhs) -
+            static_cast<common_t<T, U>>(rhs)))
+            -> decltype(
+                static_cast<common_t<T, U>>(lhs) -
+                static_cast<common_t<T, U>>(rhs))
+        {
+            return static_cast<common_t<T, U>>(lhs) -
+                   static_cast<common_t<T, U>>(rhs);
+        }
+    }
+
+}}
+
+namespace boost { namespace stl_interfaces { inline namespace v1 {
+
+    /** A CRTP template that one may derive from to make defining iterators
+        easier.
+
+        The template parameter `D` for `iterator_interface` may be an
+        incomplete type.  Before any member of the resulting specialization of
+        `iterator_interface` other than special member functions is
+        referenced, `D` shall be complete, and model
+        `std::derived_from<iterator_interface<D>>`. */
+    template<
+        typename Derived,
+        typename IteratorConcept,
+        typename ValueType,
+        typename Reference = ValueType &,
+        typename Pointer = ValueType *,
+        typename DifferenceType = std::ptrdiff_t
+#ifndef BOOST_STL_INTERFACES_DOXYGEN
+        ,
+        typename E = std::enable_if_t<
+            std::is_class<Derived>::value &&
+            std::is_same<Derived, std::remove_cv_t<Derived>>::value>
+#endif
+        >
+    struct iterator_interface;
+
+    namespace v1_dtl {
+        template<typename Iterator, typename = void>
+        struct ra_iter : std::false_type
+        {
+        };
+        template<typename Iterator>
+        struct ra_iter<Iterator, void_t<typename Iterator::iterator_concept>>
+            : std::integral_constant<
+                  bool,
+                  std::is_base_of<
+                      std::random_access_iterator_tag,
+                      typename Iterator::iterator_concept>::value>
+        {
+        };
+
+        template<typename Iterator, typename DifferenceType, typename = void>
+        struct plus_eq : std::false_type
+        {
+        };
+        template<typename Iterator, typename DifferenceType>
+        struct plus_eq<
+            Iterator,
+            DifferenceType,
+            void_t<decltype(
+                std::declval<Iterator &>() += std::declval<DifferenceType>())>>
+            : std::true_type
+        {
+        };
+
+        template<
+            typename D,
+            typename IteratorConcept,
+            typename ValueType,
+            typename Reference,
+            typename Pointer,
+            typename DifferenceType>
+        void derived_iterator(iterator_interface<
+                              D,
+                              IteratorConcept,
+                              ValueType,
+                              Reference,
+                              Pointer,
+                              DifferenceType> const &);
+    }
+
+    template<
+        typename Derived,
+        typename IteratorConcept,
+        typename ValueType,
+        typename Reference,
+        typename Pointer,
+        typename DifferenceType
+#ifndef BOOST_STL_INTERFACES_DOXYGEN
+        ,
+        typename E
+#endif
+        >
+    struct iterator_interface
+    {
+#ifndef BOOST_STL_INTERFACES_DOXYGEN
+    private:
+        constexpr Derived & derived() noexcept
+        {
+            return static_cast<Derived &>(*this);
+        }
+        constexpr Derived const & derived() const noexcept
+        {
+            return static_cast<Derived const &>(*this);
+        }
+
+        template<typename T, typename U, bool UseBase>
+        friend struct detail::common_eq;
+#endif
+
+    public:
+        using iterator_concept = IteratorConcept;
+        using iterator_category = detail::concept_category_t<iterator_concept>;
+        using value_type = std::remove_const_t<ValueType>;
+        using reference = Reference;
+        using pointer = detail::pointer_t<Pointer, iterator_concept>;
+        using difference_type = DifferenceType;
+
+        template<typename D = Derived>
+        constexpr auto operator*() const
+            noexcept(noexcept(*access::base(std::declval<D const &>())))
+                -> decltype(*access::base(std::declval<D const &>()))
+        {
+            return *access::base(derived());
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator-> () const noexcept(
+            noexcept(detail::make_pointer<pointer>(*std::declval<D const &>())))
+            -> decltype(
+                detail::make_pointer<pointer>(*std::declval<D const &>()))
+        {
+            return detail::make_pointer<pointer>(*derived());
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator[](difference_type i) const noexcept(noexcept(
+            D(std::declval<D const &>()),
+            std::declval<D &>() += i,
+            *std::declval<D &>()))
+            -> decltype(std::declval<D &>() += i, *std::declval<D &>())
+        {
+            D retval = derived();
+            retval += i;
+            return *retval;
+        }
+
+        template<
+            typename D = Derived,
+            typename Enable =
+                std::enable_if_t<!v1_dtl::plus_eq<D, difference_type>::value>>
+        constexpr auto
+        operator++() noexcept(noexcept(++access::base(std::declval<D &>())))
+            -> decltype(++access::base(std::declval<D &>()))
+        {
+            return ++access::base(derived());
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator++() noexcept(
+            noexcept(std::declval<D &>() += difference_type(1)))
+            -> decltype(
+                std::declval<D &>() += difference_type(1), std::declval<D &>())
+        {
+            derived() += difference_type(1);
+            return derived();
+        }
+        template<typename D = Derived>
+        constexpr auto operator++(int)noexcept(
+            noexcept(D(std::declval<D &>()), ++std::declval<D &>()))
+            -> std::remove_reference_t<decltype(
+                D(std::declval<D &>()),
+                ++std::declval<D &>(),
+                std::declval<D &>())>
+        {
+            D retval = derived();
+            ++derived();
+            return retval;
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator+=(difference_type n) noexcept(
+            noexcept(access::base(std::declval<D &>()) += n))
+            -> decltype(access::base(std::declval<D &>()) += n)
+        {
+            return access::base(derived()) += n;
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator+(difference_type i) const
+            noexcept(noexcept(D(std::declval<D &>()), std::declval<D &>() += i))
+                -> std::remove_reference_t<decltype(
+                    D(std::declval<D &>()),
+                    std::declval<D &>() += i,
+                    std::declval<D &>())>
+        {
+            D retval = derived();
+            retval += i;
+            return retval;
+        }
+        friend BOOST_STL_INTERFACES_HIDDEN_FRIEND_CONSTEXPR Derived
+        operator+(difference_type i, Derived it) noexcept
+        {
+            return it + i;
+        }
+
+        template<
+            typename D = Derived,
+            typename Enable =
+                std::enable_if_t<!v1_dtl::plus_eq<D, difference_type>::value>>
+        constexpr auto
+        operator--() noexcept(noexcept(--access::base(std::declval<D &>())))
+            -> decltype(--access::base(std::declval<D &>()))
+        {
+            return --access::base(derived());
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator--() noexcept(noexcept(
+            D(std::declval<D &>()), std::declval<D &>() += -difference_type(1)))
+            -> decltype(
+                std::declval<D &>() += -difference_type(1), std::declval<D &>())
+        {
+            derived() += -difference_type(1);
+            return derived();
+        }
+        template<typename D = Derived>
+        constexpr auto operator--(int)noexcept(
+            noexcept(D(std::declval<D &>()), --std::declval<D &>()))
+            -> std::remove_reference_t<decltype(
+                D(std::declval<D &>()),
+                --std::declval<D &>(),
+                std::declval<D &>())>
+        {
+            D retval = derived();
+            --derived();
+            return retval;
+        }
+
+        template<typename D = Derived>
+        constexpr D & operator-=(difference_type i) noexcept
+        {
+            derived() += -i;
+            return derived();
+        }
+
+        template<typename D = Derived>
+        constexpr auto operator-(D other) const noexcept(noexcept(
+            access::base(std::declval<D const &>()) - access::base(other)))
+            -> decltype(
+                access::base(std::declval<D const &>()) - access::base(other))
+        {
+            return access::base(derived()) - access::base(other);
+        }
+
+        friend BOOST_STL_INTERFACES_HIDDEN_FRIEND_CONSTEXPR Derived
+        operator-(Derived it, difference_type i) noexcept
+        {
+            Derived retval = it;
+            retval += -i;
+            return retval;
+        }
+    };
+
+    /** Implementation of `operator==()`, implemented in terms of the iterator
+        underlying IteratorInterface, for all iterators derived from
+        `iterator_interface`, except those with an iterator category derived
+        from `std::random_access_iterator_tag`.  */
+    template<
+        typename IteratorInterface1,
+        typename IteratorInterface2,
+        typename Enable =
+            std::enable_if_t<!v1_dtl::ra_iter<IteratorInterface1>::value>>
+    constexpr auto
+    operator==(IteratorInterface1 lhs, IteratorInterface2 rhs) noexcept
+        -> decltype(
+            access::base(std::declval<IteratorInterface1 &>()) ==
+            access::base(std::declval<IteratorInterface2 &>()))
+    {
+        return access::base(lhs) == access::base(rhs);
+    }
+
+    /** Implementation of `operator==()` for all iterators derived from
+        `iterator_interface` that have an iterator category derived from
+        `std::random_access_iterator_tag`.  */
+    template<
+        typename IteratorInterface1,
+        typename IteratorInterface2,
+        typename Enable =
+            std::enable_if_t<v1_dtl::ra_iter<IteratorInterface1>::value>>
+    constexpr auto
+    operator==(IteratorInterface1 lhs, IteratorInterface2 rhs) noexcept(
+        noexcept(detail::common_diff(lhs, rhs)))
+        -> decltype(
+            v1_dtl::derived_iterator(lhs), detail::common_diff(lhs, rhs) == 0)
+    {
+        return detail::common_diff(lhs, rhs) == 0;
+    }
+
+    /** Implementation of `operator!=()` for all iterators derived from
+        `iterator_interface`.  */
+    template<typename IteratorInterface1, typename IteratorInterface2>
+    constexpr auto operator!=(
+        IteratorInterface1 lhs,
+        IteratorInterface2 rhs) noexcept(noexcept(!(lhs == rhs)))
+        -> decltype(v1_dtl::derived_iterator(lhs), !(lhs == rhs))
+    {
+        return !(lhs == rhs);
+    }
+
+    /** Implementation of `operator<()` for all iterators derived from
+        `iterator_interface` that have an iterator category derived from
+        `std::random_access_iterator_tag`.  */
+    template<typename IteratorInterface1, typename IteratorInterface2>
+    constexpr auto
+    operator<(IteratorInterface1 lhs, IteratorInterface2 rhs) noexcept(
+        noexcept(detail::common_diff(lhs, rhs)))
+        -> decltype(
+            v1_dtl::derived_iterator(lhs), detail::common_diff(lhs, rhs) < 0)
+    {
+        return detail::common_diff(lhs, rhs) < 0;
+    }
+
+    /** Implementation of `operator<=()` for all iterators derived from
+        `iterator_interface` that have an iterator category derived from
+        `std::random_access_iterator_tag`.  */
+    template<typename IteratorInterface1, typename IteratorInterface2>
+    constexpr auto
+    operator<=(IteratorInterface1 lhs, IteratorInterface2 rhs) noexcept(
+        noexcept(detail::common_diff(lhs, rhs)))
+        -> decltype(
+            v1_dtl::derived_iterator(lhs), detail::common_diff(lhs, rhs) <= 0)
+    {
+        return detail::common_diff(lhs, rhs) <= 0;
+    }
+
+    /** Implementation of `operator>()` for all iterators derived from
+        `iterator_interface` that have an iterator category derived from
+        `std::random_access_iterator_tag`.  */
+    template<typename IteratorInterface1, typename IteratorInterface2>
+    constexpr auto
+    operator>(IteratorInterface1 lhs, IteratorInterface2 rhs) noexcept(
+        noexcept(detail::common_diff(lhs, rhs)))
+        -> decltype(
+            v1_dtl::derived_iterator(lhs), detail::common_diff(lhs, rhs) > 0)
+    {
+        return detail::common_diff(lhs, rhs) > 0;
+    }
+
+    /** Implementation of `operator>=()` for all iterators derived from
+        `iterator_interface` that have an iterator category derived from
+        `std::random_access_iterator_tag`.  */
+    template<typename IteratorInterface1, typename IteratorInterface2>
+    constexpr auto
+    operator>=(IteratorInterface1 lhs, IteratorInterface2 rhs) noexcept(
+        noexcept(detail::common_diff(lhs, rhs)))
+        -> decltype(
+            v1_dtl::derived_iterator(lhs), detail::common_diff(lhs, rhs) >= 0)
+    {
+        return detail::common_diff(lhs, rhs) >= 0;
+    }
+
+
+    /** A template alias useful for defining proxy iterators.  \see
+        `iterator_interface`. */
+    template<
+        typename Derived,
+        typename IteratorConcept,
+        typename ValueType,
+        typename Reference = ValueType,
+        typename DifferenceType = std::ptrdiff_t>
+    using proxy_iterator_interface = iterator_interface<
+        Derived,
+        IteratorConcept,
+        ValueType,
+        Reference,
+        proxy_arrow_result<Reference>,
+        DifferenceType>;
+
+}}}
+
+#ifdef BOOST_STL_INTERFACES_DOXYGEN
+
+/** `static_asserts` that type `type` models concept `concept_name`.  This is
+    useful for checking that an iterator, view, etc. that you write using one
+    of the *`_interface` templates models the right C++ concept.
+
+    For example: `BOOST_STL_INTERFACES_STATIC_ASSERT_CONCEPT(my_iter,
+    std::input_iterator)`.
+
+    \note This macro expands to nothing when `__cpp_lib_concepts` is not
+    defined. */
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_CONCEPT(type, concept_name)
+
+/** `static_asserts` that the types of all typedefs in
+    `std::iterator_traits<iter>` match the remaining macro parameters.  This
+    is useful for checking that an iterator you write using
+    `iterator_interface` has the correct iterator traits.
+
+    For example: `BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS(my_iter,
+    std::input_iterator_tag, std::input_iterator_tag, int, int &, int *, std::ptrdiff_t)`.
+
+    \note This macro ignores the `concept` parameter when `__cpp_lib_concepts`
+    is not defined. */
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS(                    \
+    iter, category, concept, value_type, reference, pointer, difference_type)
+
+#else
+
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_CONCEPT_IMPL(              \
+    type, concept_name)                                                        \
+    static_assert(concept_name<type>, "");
+
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_CONCEPT(iter, concept_name)
+
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS_IMPL(               \
+    iter, category, value_t, ref, ptr, diff_t)                                 \
+    static_assert(                                                             \
+        std::is_same<                                                          \
+            typename std::iterator_traits<iter>::iterator_category,            \
+            category>::value,                                                  \
+        "");                                                                   \
+    static_assert(                                                             \
+        std::is_same<                                                          \
+            typename std::iterator_traits<iter>::value_type,                   \
+            value_t>::value,                                                   \
+        "");                                                                   \
+    static_assert(                                                             \
+        std::is_same<typename std::iterator_traits<iter>::reference, ref>::    \
+            value,                                                             \
+        "");                                                                   \
+    static_assert(                                                             \
+        std::is_same<typename std::iterator_traits<iter>::pointer, ptr>::      \
+            value,                                                             \
+        "");                                                                   \
+    static_assert(                                                             \
+        std::is_same<                                                          \
+            typename std::iterator_traits<iter>::difference_type,              \
+            diff_t>::value,                                                    \
+        "");
+
+#if 201703L < __cplusplus && defined(__cpp_lib_ranges)
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS(                    \
+    iter, category, concept, value_type, reference, pointer, difference_type)  \
+    static_assert(                                                             \
+        std::is_same<                                                          \
+            typename std::iterator_traits<iter>::iterator_concept,             \
+            concept>::value,                                                   \
+        "");                                                                   \
+    BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS_IMPL(                   \
+        iter, category, value_type, reference, pointer, difference_type)
+#else
+#define BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS(                    \
+    iter, category, concept, value_type, reference, pointer, difference_type)  \
+    BOOST_STL_INTERFACES_STATIC_ASSERT_ITERATOR_TRAITS_IMPL(                   \
+        iter, category, value_type, reference, pointer, difference_type)
+#endif
+
+#endif
+
+#endif
diff --git a/src/include/gromacs/external/clFFT/src/include/clAmdFft.h b/src/include/gromacs/external/clFFT/src/include/clAmdFft.h
new file mode 100644 (file)
index 0000000..848d076
--- /dev/null
@@ -0,0 +1,535 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+/*! @file clAmdFft.h
+ * /note clAmdFft.h is a deprecated header file.  
+ * This header is provided to help projects that were written with the older clAmdFft codebase, to help them 
+ * port to the new API at their own schedule.  It will not be maintained or updated, and will be removed after 
+ * a reasonable amount of time has passed.  All new code should be written against clFFT.h.  
+ * Older projects should migrate to the new header at their earliest convenience.
+ */
+
+#pragma once
+#if !defined( CLAMDFFT_DOTH )
+#define CLAMDFFT_DOTH
+
+#include "clFFT.h"
+
+/* The following header defines a fixed version number as this header is deprecated and won't be updated */
+#include "clAmdFft.version.h"
+
+/*     In general, you can not use namespaces for strict C compliance, so we prefix our public accessible names
+ *     with the string clAmdFft
+ */
+
+/*     All functions will return pre-defined error codes, and will NOT throw exceptions to the caller
+ */
+
+/*!  @brief clAmdFft error codes definition, incorporating OpenCL error definitions
+ *
+ *   This enumeration is a superset of the OpenCL error codes.  For example, CL_OUT_OF_HOST_MEMORY,
+ *   which is defined in cl.h is aliased as CLFFT_OUT_OF_HOST_MEMORY.  The set of basic OpenCL
+ *   error codes is extended to add extra values specific to the clAmdFft package.
+ */
+typedef enum clfftStatus_ clAmdFftStatus;
+
+/*!  @brief The dimension of the input and output buffers that will be fed into all FFT transforms */
+typedef enum clfftDim_ clAmdFftDim;
+
+/*!  @brief These are the expected layouts of the buffers */
+typedef enum clfftLayout_ clAmdFftLayout;
+
+/*!  @brief This is the expected precision of each FFT.
+ */
+typedef enum clfftPrecision_ clAmdFftPrecision;
+
+/*!  @brief What is the expected direction of each FFT, time or the frequency domains */
+typedef enum clfftDirection_ clAmdFftDirection;
+
+/*!  @brief Are the input buffers overwritten with the results */
+typedef enum clfftResultLocation_ clAmdFftResultLocation;
+
+/*! @brief This determines whether the result is returned in original order. It is valid only for
+dimensions greater than 1. */
+typedef enum clfftResultTransposed_ clAmdFftResultTransposed;
+
+/*! @brief Data structure that can be passed to clAmdFftSetup() to control the behavior of the FFT runtime
+ *  @details This structure contains values that can be initialized before instantiation of the FFT runtime
+ *  with ::clAmdFftSetup().  To initialize this structure, pass a pointer to a user struct to ::clAmdFftInitSetupData( ),
+ *  which will clear the structure and set the version member variables to the current values.
+ */
+typedef struct clfftSetupData_ clAmdFftSetupData;
+
+/*!  @brief An abstract handle to the object that represents the state of the FFT(s) */
+typedef clfftPlanHandle clAmdFftPlanHandle;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       /*! @brief Initialize an clAmdFftSetupData struct for the client
+        *  @details clAmdFftSetupData is passed to clAmdFftSetup to control behavior of the FFT runtime
+        *  @param[out] setupData Data structure is cleared, initialized with version information and default values
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftInitSetupData( clAmdFftSetupData* setupData )
+       {
+               return clfftInitSetupData( setupData );
+       }
+
+       /*! @brief Initialize internal FFT resources.
+        *  @details AMD's FFT implementation caches kernels, programs and buffers for its internal use.
+        *  @param[in] setupData Data structure that can be passed into the setup routine to control FFT generation behavior
+        *      and debug functionality
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftSetup( const clAmdFftSetupData* setupData )
+       {
+               return clfftSetup( setupData );
+       }
+
+       /*! @brief Release all internal resources.
+        *  @details Call when client is done with this FFT library, allowing the library to destroy all resources it has cached
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftTeardown( )
+       {
+               return clfftTeardown( );
+       }
+
+       /*! @brief Query the FFT library for version information
+        *  @details Return the major, minor and patch version numbers associated with this FFT library
+        *  @param[out] major Major functionality change
+        *  @param[out] minor Minor functionality change
+        *  @param[out] patch Bug fixes, documentation changes, no new features introduced
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftGetVersion( cl_uint* major, cl_uint* minor, cl_uint* patch )
+       {
+               return clfftGetVersion( major, minor, patch );
+       }
+
+       /*! @brief Create a plan object initialized entirely with default values.
+        *  @details A plan is a repository of state for calculating FFT's.  Allows the runtime to pre-calculate kernels, programs
+        *      and buffers and associate them with buffers of specified dimensions.
+        *  @param[out] plHandle Handle to the newly created plan
+        *  @param[in] context Client is responsible for providing an OpenCL context for the plan
+        *  @param[in] dim The dimensionality of the FFT transform; describes how many elements are in the array
+        *  @param[in] clLengths An array of lengths, of size 'dim'.  Each value describes the length of additional dimensions
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftCreateDefaultPlan( clAmdFftPlanHandle* plHandle, cl_context context, const clAmdFftDim dim,
+                                                               const size_t* clLengths )
+       {
+               return clfftCreateDefaultPlan( plHandle, context, dim, clLengths );
+       }
+
+       /*! @brief Create a copy of an existing plan.
+        *  @details This API allows a client to create a new plan based upon an existing plan.  This is a convenience function
+        *  provided for quickly creating plans that are similar, but may differ slightly.
+        *  @param[out] out_plHandle Handle to the newly created plan that is based on in_plHandle
+        *  @param[in] new_context Client is responsible for providing a new context for the new plan
+        *  @param[in] in_plHandle Handle to a plan to be copied, previously created
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftCopyPlan( clAmdFftPlanHandle* out_plHandle, cl_context new_context, clAmdFftPlanHandle in_plHandle )
+       {
+               return clfftCopyPlan( out_plHandle, new_context, in_plHandle );
+       }
+
+       /*! @brief Prepare the plan for execution.
+        *  @details After all plan parameters are set, the client has the option of 'baking' the plan, which tells the runtime that
+        *  no more changes to the plan's parameters are expected, and the OpenCL kernels should be compiled.  This optional function
+        *  allows the client application to perform this function when the application is being initialized instead of on the first
+        *  execution.
+        *  At this point, the clAmdFft runtime will apply all implimented optimizations, possibly including
+        *  running kernel experiments on the devices in the plan context.
+        *  <p>  Users should assume that this function will take a long time to execute.  If a plan is not baked before being executed,
+        *  users should assume that the first call to clAmdFftEnqueueTransform will take a long time to execute.
+        *  <p>  If any significant parameter of a plan is changed after the plan is baked (by a subsequent call to one of
+        *  the clAmdFftSetPlan____ functions), that will not be considered an error.  Instead, the plan will revert back to
+        *  the unbaked state, discarding the benefits of the baking operation.
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] numQueues Number of command queues in commQueueFFT; 0 is a valid value, in which case client does not want
+        *      the runtime to run load experiments and only pre-calculate state information
+        *  @param[in] commQueueFFT An array of cl_command_queues created by the client; the command queues must be a proper subset of
+        *      the devices included in the plan context
+        *  @param[in] pfn_notify A function pointer to a notification routine. The notification routine is a callback function that
+        *  an application can register and which will be called when the program executable has been built (successfully or unsuccessfully).
+        *  Currently, this parameter MUST be NULL or nullptr.
+        *  @param[in] user_data Passed as an argument when pfn_notify is called.
+        *  Currently, this parameter MUST be NULL or nullptr.
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftBakePlan( clAmdFftPlanHandle plHandle, cl_uint numQueues, cl_command_queue* commQueueFFT,
+                                                       void (CL_CALLBACK *pfn_notify)(clAmdFftPlanHandle plHandle, void *user_data), void* user_data )
+       {
+               return clfftBakePlan( plHandle, numQueues, commQueueFFT, pfn_notify, user_data );
+       }
+
+       /*! @brief Release the resources of a plan.
+        *  @details A plan may include kernels, programs and buffers associated with it that consume memory.  When a plan
+        *  is not needed anymore, the client should release the plan.
+        *  @param[in,out] plHandle Handle to a plan previously created
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftDestroyPlan( clAmdFftPlanHandle* plHandle )
+       {
+               return clfftDestroyPlan( plHandle );
+       }
+
+       /*! @brief Retrieve the OpenCL context of a previously created plan.
+        *  @details User should pass a reference to an cl_context variable, which will be changed to point to a
+        *  context set in the specified plan.
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[out] context Reference to user allocated cl_context, which will point to context set in plan
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftGetPlanContext( const clAmdFftPlanHandle plHandle, cl_context* context )
+       {
+               return clfftGetPlanContext( plHandle, context );
+       }
+
+       /*! @brief Retrieve the floating point precision of the FFT data
+        *  @details User should pass a reference to an clAmdFftPrecision variable, which will be set to the
+        *  precision of the FFT complex data in the plan.
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[out] precision Reference to user clAmdFftPrecision enum
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftGetPlanPrecision( const clAmdFftPlanHandle plHandle, clAmdFftPrecision* precision )
+       {
+               return clfftGetPlanPrecision( plHandle, precision );
+       }
+
+       /*! @brief Set the floating point precision of the FFT data
+        *  @details Set the plan property which will be the precision of the FFT complex data in the plan.
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] precision Reference to user clAmdFftPrecision enum
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftSetPlanPrecision( clAmdFftPlanHandle plHandle, clAmdFftPrecision precision )
+       {
+               return clfftSetPlanPrecision( plHandle, precision );
+       }
+
+       /*! @brief Retrieve the scaling factor that should be applied to the FFT data
+        *  @details User should pass a reference to an cl_float variable, which will be set to the
+        *  floating point scaling factor that will be multiplied across the FFT data.
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] dir Which direction does the scaling factor apply to
+        *  @param[out] scale Reference to user cl_float variable
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftGetPlanScale( const clAmdFftPlanHandle plHandle, clAmdFftDirection dir, cl_float* scale )
+       {
+               return clfftGetPlanScale( plHandle, dir, scale );
+       }
+
+       /*! @brief Set the scaling factor that should be applied to the FFT data
+        *  @details Set the plan property which will be the floating point scaling factor that will be
+        *  multiplied across the FFT data.
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] dir Which direction does the scaling factor apply to
+        *  @param[in] scale Reference to user cl_float variable
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftSetPlanScale( clAmdFftPlanHandle plHandle, clAmdFftDirection dir, cl_float scale )
+       {
+               return clfftSetPlanScale( plHandle, dir, scale );
+       }
+
+       /*! @brief Retrieve the number of discrete arrays that this plan can handle concurrently
+        *  @details User should pass a reference to an cl_uint variable, which will be set to the
+        *  number of discrete arrays (1D or 2D) that will be batched together for this plan
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[out] batchSize How many discrete number of FFT's are to be performed
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftGetPlanBatchSize( const clAmdFftPlanHandle plHandle, size_t* batchSize )
+       {
+               return clfftGetPlanBatchSize( plHandle, batchSize );
+       }
+
+       /*! @brief Set the number of discrete arrays that this plan can handle concurrently
+        *  @details Set the plan property which will be set to the number of discrete arrays (1D or 2D)
+        *  that will be batched together for this plan
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] batchSize How many discrete number of FFT's are to be performed
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftSetPlanBatchSize( clAmdFftPlanHandle plHandle, size_t batchSize )
+       {
+               return clfftSetPlanBatchSize( plHandle, batchSize );
+       }
+
+       /*! @brief Retrieve the dimensionality of FFT's to be transformed in the plan
+        *  @details Queries a plan object and retrieves the dimensionality that the plan is set for.  A size is returned to
+        *  help the client allocate the proper storage to hold the dimensions in a further call to clAmdFftGetPlanLength
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[out] dim The dimensionality of the FFT's to be transformed
+        *  @param[out] size Value used to allocate an array to hold the FFT dimensions.
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftGetPlanDim( const clAmdFftPlanHandle plHandle, clAmdFftDim* dim, cl_uint* size )
+       {
+               return clfftGetPlanDim( plHandle, dim, size );
+       }
+
+       /*! @brief Set the dimensionality of FFT's to be transformed by the plan
+        *  @details Set the dimensionality of FFT's to be transformed by the plan
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] dim The dimensionality of the FFT's to be transformed
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftSetPlanDim( clAmdFftPlanHandle plHandle, const clAmdFftDim dim )
+       {
+               return clfftSetPlanDim( plHandle, dim );
+       }
+
+       /*! @brief Retrieve the length of each dimension of the FFT
+        *  @details User should pass a reference to a size_t array, which will be set to the
+        *  length of each discrete dimension of the FFT
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] dim The dimension of the length parameters; describes how many elements are in the array
+        *  @param[out] clLengths An array of lengths, of size 'dim'.  Each array value describes the length of each dimension
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftGetPlanLength( const clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clLengths )
+       {
+               return clfftGetPlanLength( plHandle, dim, clLengths );
+       }
+
+       /*! @brief Set the length of each dimension of the FFT
+        *  @details Set the plan property which will be the length of each discrete dimension of the FFT
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] dim The dimension of the length parameters; describes how many elements are in the array
+        *  @param[in] clLengths An array of lengths, of size 'dim'.  Each value describes the length of additional dimensions
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftSetPlanLength( clAmdFftPlanHandle plHandle, const clAmdFftDim dim, const size_t* clLengths )
+       {
+               return clfftSetPlanLength( plHandle, dim, clLengths );
+       }
+
+       /*! @brief Retrieve the distance between consecutive elements for input buffers in a dimension.
+        *  @details Depending on how the dimension is set in the plan (for 2D or 3D FFT's), strideY or strideZ can be safely
+        *  ignored
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] dim The dimension of the stride parameters; describes how many elements are in the array
+        *  @param[out] clStrides An array of strides, of size 'dim'.
+        */
+       __inline clAmdFftStatus clAmdFftGetPlanInStride( const clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clStrides )
+       {
+               return clfftGetPlanInStride( plHandle, dim, clStrides );
+       }
+
+       /*! @brief Set the distance between consecutive elements for input buffers in a dimension.
+        *  @details Set the plan properties which will be the distance between elements in a given dimension
+        *  (units are in terms of clAmdFftPrecision)
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] dim The dimension of the stride parameters; describes how many elements are in the array
+        *  @param[in] clStrides An array of strides, of size 'dim'. Usually strideX=1 so that successive elements in the first dimension are stored contiguously.
+        *      Typically strideY=LenX, strideZ=LenX*LenY such that successive elements in the second and third dimensions are stored in packed format.
+        *  See  @ref DistanceStridesandPitches for details.
+        */
+       __inline clAmdFftStatus clAmdFftSetPlanInStride( clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clStrides )
+       {
+               return clfftSetPlanInStride( plHandle, dim, clStrides );
+       }
+
+       /*! @brief Retrieve the distance between consecutive elements for output buffers in a dimension.
+        *  @details Depending on how the dimension is set in the plan (for 2D or 3D FFT's), strideY or strideZ can be safely
+        *  ignored
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] dim The dimension of the stride parameters; describes how many elements are in the array
+        *  @param[out] clStrides An array of strides, of size 'dim'.
+        */
+       __inline clAmdFftStatus clAmdFftGetPlanOutStride( const clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clStrides )
+       {
+               return clfftGetPlanOutStride( plHandle, dim, clStrides );
+       }
+
+       /*! @brief Set the distance between consecutive elements for output buffers in a dimension.
+        *  @details Set the plan properties which will be the distance between elements in a given dimension
+        *  (units are in terms of clAmdFftPrecision)
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] dim The dimension of the stride parameters; describes how many elements are in the array
+        *  @param[in] clStrides An array of strides, of size 'dim'.  Usually strideX=1 so that successive elements in the first dimension are stored contiguously.
+        *      Typically strideY=LenX, strideZ=LenX*LenY such that successive elements in the second and third dimensions are stored in packed format.
+        *  @sa clAmdFftSetPlanInStride
+        */
+       __inline clAmdFftStatus clAmdFftSetPlanOutStride( clAmdFftPlanHandle plHandle, const clAmdFftDim dim, size_t* clStrides )
+       {
+               return clfftSetPlanOutStride( plHandle, dim, clStrides );
+       }
+
+       /*! @brief Retrieve the distance between Array objects
+        *  @details Pitch is the distance between each discrete array object in an FFT array. This is only used
+        *  for 'array' dimensions in clAmdFftDim; see clAmdFftSetPlanDimension (units are in terms of clAmdFftPrecision)
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[out] iDist The distance between the beginning elements of the discrete array objects in memory on input.
+        *  For contiguous arrays in memory, iDist=(strideX*strideY*strideZ)
+        *  @param[out] oDist The distance between the beginning elements of the discrete array objects in memory on output.
+        *  For contiguous arrays in memory, oDist=(strideX*strideY*strideZ)
+        */
+       __inline clAmdFftStatus clAmdFftGetPlanDistance( const clAmdFftPlanHandle plHandle, size_t* iDist, size_t* oDist )
+       {
+               return clfftGetPlanDistance( plHandle, iDist, oDist );
+       }
+
+       /*! @brief Set the distance between Array objects
+        *  @details Pitch is the distance between each discrete array object in an FFT array. This is only used
+        *  for 'array' dimensions in clAmdFftDim; see clAmdFftSetPlanDimension (units are in terms of clAmdFftPrecision)
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[out] iDist The distance between the beginning elements of the discrete array objects in memory on input.
+        *  For contiguous arrays in memory, iDist=(strideX*strideY*strideZ)
+        *  @param[out] oDist The distance between the beginning elements of the discrete array objects in memory on output.
+        *  For contiguous arrays in memory, oDist=(strideX*strideY*strideZ)
+        */
+       __inline clAmdFftStatus clAmdFftSetPlanDistance( clAmdFftPlanHandle plHandle, size_t iDist, size_t oDist )
+       {
+               return clfftSetPlanDistance( plHandle, iDist, oDist );
+       }
+
+       /*! @brief Retrieve the expected layout of the input and output buffers
+        *  @details Output buffers can be filled with either hermitian or complex numbers.  Complex numbers can be stored
+        *  in various layouts; this informs the FFT engine what layout to produce on output
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[out] iLayout Indicates how the input buffers are laid out in memory
+        *  @param[out] oLayout Indicates how the output buffers are laid out in memory
+        */
+       __inline clAmdFftStatus clAmdFftGetLayout( const clAmdFftPlanHandle plHandle, clAmdFftLayout* iLayout, clAmdFftLayout* oLayout )
+       {
+               return clfftGetLayout( plHandle, iLayout, oLayout );
+       }
+
+       /*! @brief Set the expected layout of the input and output buffers
+        *  @details Output buffers can be filled with either hermitian or complex numbers.  Complex numbers can be stored
+        *  in various layouts; this informs the FFT engine what layout to produce on output
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] iLayout Indicates how the input buffers are laid out in memory
+        *  @param[in] oLayout Indicates how the output buffers are laid out in memory
+        */
+       __inline clAmdFftStatus clAmdFftSetLayout( clAmdFftPlanHandle plHandle, clAmdFftLayout iLayout, clAmdFftLayout oLayout )
+       {
+               return clfftSetLayout( plHandle, iLayout, oLayout );
+       }
+
+       /*! @brief Retrieve whether the input buffers are going to be overwritten with results
+        *  @details If the setting is to do an in-place transform, the input buffers are overwritten with the results of the
+        *  transform.  If the setting is for out-of-place transforms, the engine knows to look for separate output buffers
+        *  on the Enqueue call.
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[out] placeness Tells the FFT engine to clobber the input buffers or to expect output buffers for results
+        */
+       __inline clAmdFftStatus clAmdFftGetResultLocation( const clAmdFftPlanHandle plHandle, clAmdFftResultLocation* placeness )
+       {
+               return clfftGetResultLocation( plHandle, placeness );
+       }
+
+       /*! @brief Set whether the input buffers are going to be overwritten with results
+        *  @details If the setting is to do an in-place transform, the input buffers are overwritten with the results of the
+        *  transform.  If the setting is for out-of-place transforms, the engine knows to look for separate output buffers
+        *  on the Enqueue call.
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] placeness Tells the FFT engine to clobber the input buffers or to expect output buffers for results
+        */
+       __inline clAmdFftStatus clAmdFftSetResultLocation( clAmdFftPlanHandle plHandle, clAmdFftResultLocation placeness )
+       {
+               return clfftSetResultLocation( plHandle, placeness );
+       }
+
+       /*! @brief Retrieve the final transpose setting of a muti-dimensional FFT
+        *  @details A multi-dimensional FFT typically transposes the data several times during calculation.  If the client
+        *  does not care about the final transpose to put data back in proper dimension, the final transpose can be skipped
+        *  for possible speed improvements
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[out] transposed Parameter specifies whether the final transpose can be skipped
+        */
+       __inline clAmdFftStatus clAmdFftGetPlanTransposeResult( const clAmdFftPlanHandle plHandle, clAmdFftResultTransposed * transposed )
+       {
+               return clfftGetPlanTransposeResult( plHandle, transposed );
+       }
+
+       /*! @brief Set the final transpose setting of a muti-dimensional FFT
+        *  @details A multi-dimensional FFT typically transposes the data several times during calculation.  If the client
+        *  does not care about the final transpose to put data back in proper dimension, the final transpose can be skipped
+        *  for possible speed improvements
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] transposed Parameter specifies whether the final transpose can be skipped
+        */
+       __inline clAmdFftStatus clAmdFftSetPlanTransposeResult( clAmdFftPlanHandle plHandle, clAmdFftResultTransposed transposed )
+       {
+               return clfftSetPlanTransposeResult( plHandle, transposed );
+       }
+
+       /*! @brief Get buffer size (in bytes), which may be needed internally for an intermediate buffer
+        *  @details Very large FFT transforms may need multiple passes, and the operation would need a temporary buffer to hold
+        *  intermediate results. This function is only valid after the plan is baked, otherwise an invalid operation error
+        *  is returned. If buffersize returns as 0, the runtime needs no temporary buffer.
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[out] buffersize Size in bytes for intermediate buffer
+        */
+       __inline clAmdFftStatus clAmdFftGetTmpBufSize( const clAmdFftPlanHandle plHandle, size_t* buffersize )
+       {
+               return clfftGetTmpBufSize( plHandle, buffersize );
+       }
+
+       /*! @brief Enqueue an FFT transform operation, and return immediately (non-blocking)
+        *  @details This transform API is the function that actually computes the FFT transfrom. It is non-blocking as it
+        *  only enqueues the OpenCL kernels for execution. The synchronization step has to be managed by the user.
+        *  @param[in] plHandle Handle to a plan previously created
+        *  @param[in] dir Forwards or backwards transform
+        *  @param[in] numQueuesAndEvents Number of command queues in commQueues; number of expected events to be returned in outEvents
+        *  @param[in] commQueues An array of cl_command_queues created by the client; the command queues must be a proper subset of
+        *      the devices included in the plan context
+        *  @param[in] numWaitEvents Specify the number of elements in the eventWaitList array
+        *  @param[in] waitEvents Events that this transform should wait to complete before executing on the device
+        *  @param[out] outEvents The runtime fills this array with events corresponding 1 to 1 with the input command queues passed
+        *      in commQueues.  This parameter can be NULL or nullptr, in which case client is not interested in receiving notifications
+        *      when transforms are finished, otherwise if not NULL the client is responsible for allocating this array, with at least
+        *      as many elements as specified in numQueuesAndEvents.
+        *  @param[in] inputBuffers An array of cl_mem objects that contain data for processing by the FFT runtime.  If the transform
+        *  is in place, the FFT results will overwrite the input buffers
+        *  @param[out] outputBuffers An array of cl_mem objects that will store the results of out of place transforms.  If the transform
+        *  is in place, this parameter may be NULL or nullptr.  It is completely ignored
+        *  @param[in] tmpBuffer A cl_mem object that is reserved as a temporary buffer for FFT processing. If clTmpBuffers is NULL or nullptr,
+        *  and the runtime needs temporary storage, an internal temporary buffer will be created on the fly managed by the runtime.
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       __inline clAmdFftStatus clAmdFftEnqueueTransform(
+                                                                                               clAmdFftPlanHandle plHandle,
+                                                                                               clAmdFftDirection dir,
+                                                                                               cl_uint numQueuesAndEvents,
+                                                                                               cl_command_queue* commQueues,
+                                                                                               cl_uint numWaitEvents,
+                                                                                               const cl_event* waitEvents,
+                                                                                               cl_event* outEvents,
+                                                                                               cl_mem* inputBuffers,
+                                                                                               cl_mem* outputBuffers,
+                                                                                               cl_mem tmpBuffer
+                                                                                               )
+       {
+               return clfftEnqueueTransform( plHandle, dir, numQueuesAndEvents, commQueues, numWaitEvents, waitEvents, outEvents, 
+                       inputBuffers, outputBuffers, tmpBuffer );
+       }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/include/gromacs/external/clFFT/src/include/clAmdFft.version.h b/src/include/gromacs/external/clFFT/src/include/clAmdFft.version.h
new file mode 100644 (file)
index 0000000..ec9ef54
--- /dev/null
@@ -0,0 +1,29 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+/*! @file clAmdFft.version.h
+ * /note clAmdFft.version.h is a deprecated header file.  
+ * This header is provided to help projects that were written with the older clAmdFft codebase, to help them 
+ * port to the new API at their own schedule.  It will not be maintained or updated, and will be removed after 
+ * a reasonable amount of time has passed.  All new code should be written against clFFT.h.  
+ * Older projects should migrate to the new header at their earliest convenience.
+ */
+
+/* the configured version and settings for clFFT
+ */
+#define clAmdFftVersionMajor 2
+#define clAmdFftVersionMinor 0
+#define clAmdFftVersionPatch 0
diff --git a/src/include/gromacs/external/clFFT/src/include/clFFT.h b/src/include/gromacs/external/clFFT/src/include/clFFT.h
new file mode 100644 (file)
index 0000000..cb68425
--- /dev/null
@@ -0,0 +1,601 @@
+/* ************************************************************************
+ * Copyright 2013-2015 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+/*! @file clFFT.h
+ * clFFT.h defines all the public interfaces and types that are used by clFFT clients
+ * This is the only public header file that should be consumed by clFFT clients.  It is written to adhere to native "C"
+ * interfaces to make clFFT library as portable as possible; it should be callable from C, C++, .NET and Fortran,
+ * either with the proper linking or using wrapper classes.
+ *
+ */
+
+#pragma once
+#if !defined( CLFFT_H )
+#define CLFFT_H
+
+#if defined(__APPLE__) || defined(__MACOSX)
+       #include <OpenCL/cl.h>
+#else
+       #include <CL/cl.h>
+#endif
+
+#include "clFFT.version.h"
+
+/*! This preprocessor definition is the standard way to export APIs
+ *  from a DLL simpler. All files within this DLL are compiled with the CLFFT_EXPORTS
+ *  symbol defined on the command line. This symbol must not be defined on any project
+ *  that uses this DLL. This ensures source files of any other project that include this file see
+ *  clfft functions as being imported from a DLL, whereas the DLL sees symbols
+ *  defined with this macro as being exported.
+ */
+#if defined( _WIN32 )
+       #if !defined( __cplusplus )
+               #define inline __inline
+       #endif
+
+    #if defined( CLFFT_STATIC )
+        #define CLFFTAPI
+    #elif defined( CLFFT_EXPORTS )
+        #define CLFFTAPI __declspec( dllexport )
+    #else
+        #define CLFFTAPI __declspec( dllimport )
+    #endif
+#else
+       #define CLFFTAPI
+#endif
+
+/*     In general, you cannot use namespaces for strict C compliance, so we prefix our public accessible names
+ *     with the string clfft
+ */
+
+/*     All functions return pre-defined error codes, and do NOT throw exceptions to the caller.
+ */
+
+/*!  @brief clfft error codes definition(incorporating OpenCL error definitions)
+ *
+ *   This enumeration is a superset of the OpenCL error codes.  For example, CL_OUT_OF_HOST_MEMORY,
+ *   which is defined in cl.h is aliased as CLFFT_OUT_OF_HOST_MEMORY.  The set of basic OpenCL
+ *   error codes is extended to add extra values specific to the clfft package.
+ */
+enum clfftStatus_
+{
+       CLFFT_INVALID_GLOBAL_WORK_SIZE                  = CL_INVALID_GLOBAL_WORK_SIZE,
+       CLFFT_INVALID_MIP_LEVEL                                 = CL_INVALID_MIP_LEVEL,
+       CLFFT_INVALID_BUFFER_SIZE                               = CL_INVALID_BUFFER_SIZE,
+       CLFFT_INVALID_GL_OBJECT                                 = CL_INVALID_GL_OBJECT,
+       CLFFT_INVALID_OPERATION                                 = CL_INVALID_OPERATION,
+       CLFFT_INVALID_EVENT                                             = CL_INVALID_EVENT,
+       CLFFT_INVALID_EVENT_WAIT_LIST                   = CL_INVALID_EVENT_WAIT_LIST,
+       CLFFT_INVALID_GLOBAL_OFFSET                             = CL_INVALID_GLOBAL_OFFSET,
+       CLFFT_INVALID_WORK_ITEM_SIZE                    = CL_INVALID_WORK_ITEM_SIZE,
+       CLFFT_INVALID_WORK_GROUP_SIZE                   = CL_INVALID_WORK_GROUP_SIZE,
+       CLFFT_INVALID_WORK_DIMENSION                    = CL_INVALID_WORK_DIMENSION,
+       CLFFT_INVALID_KERNEL_ARGS                               = CL_INVALID_KERNEL_ARGS,
+       CLFFT_INVALID_ARG_SIZE                                  = CL_INVALID_ARG_SIZE,
+       CLFFT_INVALID_ARG_VALUE                                 = CL_INVALID_ARG_VALUE,
+       CLFFT_INVALID_ARG_INDEX                                 = CL_INVALID_ARG_INDEX,
+       CLFFT_INVALID_KERNEL                                    = CL_INVALID_KERNEL,
+       CLFFT_INVALID_KERNEL_DEFINITION                 = CL_INVALID_KERNEL_DEFINITION,
+       CLFFT_INVALID_KERNEL_NAME                               = CL_INVALID_KERNEL_NAME,
+       CLFFT_INVALID_PROGRAM_EXECUTABLE                = CL_INVALID_PROGRAM_EXECUTABLE,
+       CLFFT_INVALID_PROGRAM                                   = CL_INVALID_PROGRAM,
+       CLFFT_INVALID_BUILD_OPTIONS                             = CL_INVALID_BUILD_OPTIONS,
+       CLFFT_INVALID_BINARY                                    = CL_INVALID_BINARY,
+       CLFFT_INVALID_SAMPLER                                   = CL_INVALID_SAMPLER,
+       CLFFT_INVALID_IMAGE_SIZE                                = CL_INVALID_IMAGE_SIZE,
+       CLFFT_INVALID_IMAGE_FORMAT_DESCRIPTOR   = CL_INVALID_IMAGE_FORMAT_DESCRIPTOR,
+       CLFFT_INVALID_MEM_OBJECT                                = CL_INVALID_MEM_OBJECT,
+       CLFFT_INVALID_HOST_PTR                                  = CL_INVALID_HOST_PTR,
+       CLFFT_INVALID_COMMAND_QUEUE                             = CL_INVALID_COMMAND_QUEUE,
+       CLFFT_INVALID_QUEUE_PROPERTIES                  = CL_INVALID_QUEUE_PROPERTIES,
+       CLFFT_INVALID_CONTEXT                                   = CL_INVALID_CONTEXT,
+       CLFFT_INVALID_DEVICE                                    = CL_INVALID_DEVICE,
+       CLFFT_INVALID_PLATFORM                                  = CL_INVALID_PLATFORM,
+       CLFFT_INVALID_DEVICE_TYPE                               = CL_INVALID_DEVICE_TYPE,
+       CLFFT_INVALID_VALUE                                             = CL_INVALID_VALUE,
+       CLFFT_MAP_FAILURE                                               = CL_MAP_FAILURE,
+       CLFFT_BUILD_PROGRAM_FAILURE                             = CL_BUILD_PROGRAM_FAILURE,
+       CLFFT_IMAGE_FORMAT_NOT_SUPPORTED                = CL_IMAGE_FORMAT_NOT_SUPPORTED,
+       CLFFT_IMAGE_FORMAT_MISMATCH                             = CL_IMAGE_FORMAT_MISMATCH,
+       CLFFT_MEM_COPY_OVERLAP                                  = CL_MEM_COPY_OVERLAP,
+       CLFFT_PROFILING_INFO_NOT_AVAILABLE              = CL_PROFILING_INFO_NOT_AVAILABLE,
+       CLFFT_OUT_OF_HOST_MEMORY                                = CL_OUT_OF_HOST_MEMORY,
+       CLFFT_OUT_OF_RESOURCES                                  = CL_OUT_OF_RESOURCES,
+       CLFFT_MEM_OBJECT_ALLOCATION_FAILURE             = CL_MEM_OBJECT_ALLOCATION_FAILURE,
+       CLFFT_COMPILER_NOT_AVAILABLE                    = CL_COMPILER_NOT_AVAILABLE,
+       CLFFT_DEVICE_NOT_AVAILABLE                              = CL_DEVICE_NOT_AVAILABLE,
+       CLFFT_DEVICE_NOT_FOUND                                  = CL_DEVICE_NOT_FOUND,
+       CLFFT_SUCCESS                                                   = CL_SUCCESS,
+       //-------------------------- Extended status codes for clfft ----------------------------------------
+       CLFFT_BUGCHECK =  4*1024,       /*!< Bugcheck. */
+       CLFFT_NOTIMPLEMENTED,           /*!< Functionality is not implemented yet. */
+       CLFFT_TRANSPOSED_NOTIMPLEMENTED, /*!< Transposed functionality is not implemented for this transformation. */
+       CLFFT_FILE_NOT_FOUND,           /*!< Tried to open an existing file on the host system, but failed. */
+       CLFFT_FILE_CREATE_FAILURE,      /*!< Tried to create a file on the host system, but failed. */
+       CLFFT_VERSION_MISMATCH,         /*!< Version conflict between client and library. */
+       CLFFT_INVALID_PLAN,                     /*!< Requested plan could not be found. */
+       CLFFT_DEVICE_NO_DOUBLE,         /*!< Double precision not supported on this device. */
+       CLFFT_DEVICE_MISMATCH,          /*!< Attempt to run on a device using a plan baked for a different device. */
+       CLFFT_ENDSTATUS                         /* The last value of the enum, and marks the length of clfftStatus. */
+};
+typedef enum clfftStatus_ clfftStatus;
+
+/*!  @brief The dimension of the input and output buffers that is fed into all FFT transforms */
+typedef enum clfftDim_
+{
+       CLFFT_1D                = 1,            /*!< 1 Dimensional FFT transform (default). */
+       CLFFT_2D,                                       /*!< 2 Dimensional FFT transform. */
+       CLFFT_3D,                                       /*!< 3 Dimensional FFT transform. */
+       ENDDIMENSION                    /*!< The last value of the enum, and marks the length of clfftDim. */
+} clfftDim;
+
+/*!  @brief Specify the expected layouts of the buffers */
+typedef enum clfftLayout_
+{
+       CLFFT_COMPLEX_INTERLEAVED       = 1,    /*!< An array of complex numbers, with real and imaginary components together (default). */
+       CLFFT_COMPLEX_PLANAR,                           /*!< Separate arrays of real components and imaginary components. */
+       CLFFT_HERMITIAN_INTERLEAVED,            /*!< Compressed form of complex numbers; complex-conjugates are not stored, real and imaginary components are stored in the same array. */
+       CLFFT_HERMITIAN_PLANAR,                         /*!< Compressed form of complex numbers; complex-conjugates are not stored, real and imaginary components are stored in separate arrays. */
+       CLFFT_REAL,                                                     /*!< An array of real numbers, with no corresponding imaginary components. */
+       ENDLAYOUT                       /*!< The last value of the enum, and marks the length of clfftLayout. */
+} clfftLayout;
+
+/*!  @brief Specify the expected precision of each FFT.
+ */
+typedef enum clfftPrecision_
+{
+       CLFFT_SINGLE    = 1,    /*!< An array of complex numbers, with real and imaginary components saved as floats (default). */
+       CLFFT_DOUBLE,                   /*!< An array of complex numbers, with real and imaginary components saved as doubles. */
+       CLFFT_SINGLE_FAST,              /*!< Faster implementation preferred. */
+       CLFFT_DOUBLE_FAST,              /*!< Faster implementation preferred. */
+       ENDPRECISION    /*!< The last value of the enum, and marks the length of clfftPrecision. */
+} clfftPrecision;
+
+/*!  @brief Specify the expected direction of each FFT, time or the frequency domains */
+typedef enum clfftDirection_
+{
+       CLFFT_FORWARD   = -1,           /*!< FFT transform from time to frequency domain. */
+       CLFFT_BACKWARD  = 1,            /*!< FFT transform from frequency to time domain. */
+       CLFFT_MINUS             = -1,           /*!< Alias for the forward transform. */
+       CLFFT_PLUS              = 1,            /*!< Alias for the backward transform. */
+       ENDDIRECTION                    /*!< The last value of the enum, and marks the length of clfftDirection. */
+} clfftDirection;
+
+/*!  @brief Specify wheter the input buffers are overwritten with results */
+typedef enum clfftResultLocation_
+{
+       CLFFT_INPLACE           = 1,            /*!< Input and output buffers are the same (default). */
+       CLFFT_OUTOFPLACE,                               /*!< Input and output buffers are separate. */
+       ENDPLACE                                /*!< The last value of the enum, and marks the length of clfftPlaceness. */
+} clfftResultLocation;
+
+/*! @brief Determines whether the result is returned in original order. It is valid only for
+dimensions greater than 1. */
+typedef enum clfftResultTransposed_ {
+       CLFFT_NOTRANSPOSE = 1,          /*!< The result is returned in the original order (default) */
+       CLFFT_TRANSPOSED,                       /*!< The result is transposed where transpose kernel is supported (possibly faster) */
+       ENDTRANSPOSED                   /*!< The last value of the enum, and marks the length of clfftResultTransposed */
+} clfftResultTransposed;
+
+/*!    BitMasks to be used with clfftSetupData.debugFlags */
+#define CLFFT_DUMP_PROGRAMS 0x1
+
+/*! @brief Data structure that can be passed to clfftSetup() to control the behavior of the FFT runtime
+ *  @details This structure contains values that can be initialized before instantiation of the FFT runtime
+ *  with ::clfftSetup().  To initialize this structure, pass a pointer to a user struct to ::clfftInitSetupData( ),
+ *  which clears the structure and sets the version member variables to the current values.
+ */
+struct clfftSetupData_
+{
+       cl_uint major;          /*!< Major version number of the project; signifies possible major API changes. */
+       cl_uint minor;          /*!< Minor version number of the project; minor API changes that can break backward compatibility. */
+       cl_uint patch;          /*!< Patch version number of the project; always incrementing number, signifies change over time. */
+
+       /*!     Bitwise flags that control the behavior of library debug logic. */
+       cl_ulong debugFlags;  /*! This must be set to zero, except when debugging the clfft library.
+                              *  <p> debugFlags can be set to CLFFT_DUMP_PROGRAMS, in which case the dynamically generated OpenCL kernels are
+                              *  written to text files in the current working directory.  These files have a *.cl suffix.
+                              */
+};
+typedef struct clfftSetupData_ clfftSetupData;
+
+/*! @brief Type of Callback function.
+*/
+typedef enum clfftCallbackType_
+{
+       PRECALLBACK,    /*!< Callback function is invoked only once for every point of input at the beginning of FFT transform. */
+       POSTCALLBACK    /*!< Callback function is invoked only once for every point of output at the end of FFT transform. */
+}clfftCallbackType;
+
+/*!  @brief An abstract handle to the object that represents the state of the FFT(s) */
+typedef size_t clfftPlanHandle;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       /*! @brief Initialize a clfftSetupData struct for the client
+        *  @details clfftSetupData is passed to clfftSetup to control behavior of the FFT runtime.
+        *  @param[out] setupData Data structure is cleared and initialized with version information and default values
+        *  @return Enum describes the error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus clfftInitSetupData( clfftSetupData* setupData );
+
+
+       /*! @brief Initialize the internal FFT resources.
+        *  @details The internal resources include FFT implementation caches kernels, programs, and buffers.
+        *  @param[in] setupData Data structure that is passed into the setup routine to control FFT generation behavior
+        *      and debug functionality
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftSetup( const clfftSetupData* setupData );
+
+       /*! @brief Release all internal resources.
+        *  @details Called when client is done with the FFT library, allowing the library to destroy all resources it has cached
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftTeardown( );
+
+       /*! @brief Query the FFT library for version information
+        *  @details Returns the major, minor and patch version numbers associated with the FFT library
+        *  @param[out] major Major functionality change
+        *  @param[out] minor Minor functionality change
+        *  @param[out] patch Bug fixes, documentation changes, no new features introduced
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftGetVersion( cl_uint* major, cl_uint* minor, cl_uint* patch );
+
+       /*! @brief Create a plan object initialized entirely with default values.
+        *  @details A plan is a repository of state for calculating FFT's.  Allows the runtime to pre-calculate kernels, programs
+        *      and buffers and associate them with buffers of specified dimensions.
+        *  @param[out] plHandle Handle to the newly created plan
+        *  @param[in] context Client is responsible for providing an OpenCL context for the plan
+        *  @param[in] dim Dimensionality of the FFT transform; describes how many elements are in the array
+        *  @param[in] clLengths An array of length of size 'dim';  each array value describes the length of each dimension
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftCreateDefaultPlan( clfftPlanHandle* plHandle, cl_context context, const clfftDim dim,
+                                                               const size_t* clLengths );
+
+       /*! @brief Create a copy of an existing plan.
+        *  @details This API allows a client to create a new plan based upon an existing plan.  This function can be used to
+        *  quickly create plans that are similar, but may differ slightly.
+        *  @param[out] out_plHandle Handle to the newly created plan that is based on in_plHandle
+        *  @param[in] new_context Client is responsible for providing a new context for the new plan
+        *  @param[in] in_plHandle Handle to a previously created plan that is to be copied
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftCopyPlan( clfftPlanHandle* out_plHandle, cl_context new_context, clfftPlanHandle in_plHandle );
+
+       /*! @brief Prepare the plan for execution.
+        *  @details After all plan parameters are set, the client has the option of 'baking' the plan, which informs the runtime that
+        *  no more change to the parameters of the plan is expected, and the OpenCL kernels can be compiled.  This optional function
+        *  allows the client application to perform the OpenCL kernel compilation when the application is initialized instead of during the first
+        *  execution.
+        *  At this point, the clfft runtime applies all implimented optimizations, including
+        *  running kernel experiments on the devices in the plan context.
+        *  <p>  This function takes a long time to execute. If a plan is not baked before being executed,
+        *  the first call to clfftEnqueueTransform takes a long time to execute.
+        *  <p>  If any significant parameter of a plan is changed after the plan is baked (by a subsequent call to any one of
+        *  the functions that has the prefix "clfftSetPlan"), it is not considered an error.  Instead, the plan reverts back to
+        *  the unbaked state, discarding the benefits of the baking operation.
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] numQueues Number of command queues in commQueueFFT; 0 is a valid value, in which case the client does not want
+        *      the runtime to run load experiments and only pre-calculate state information
+        *  @param[in] commQueueFFT An array of cl_command_queues created by the client; the command queues must be a proper subset of
+        *      the devices included in the plan context
+        *  @param[in] pfn_notify A function pointer to a notification routine. The notification routine is a callback function that
+        *  an application can register and is called when the program executable is built (successfully or unsuccessfully).
+        *  Currently, this parameter MUST be NULL or nullptr.
+        *  @param[in] user_data Passed as an argument when pfn_notify is called.
+        *  Currently, this parameter MUST be NULL or nullptr.
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftBakePlan( clfftPlanHandle plHandle, cl_uint numQueues, cl_command_queue* commQueueFFT,
+                                                       void (CL_CALLBACK *pfn_notify)(clfftPlanHandle plHandle, void *user_data), void* user_data );
+
+       /*! @brief Release the resources of a plan.
+        *  @details A plan may include resources, such as kernels, programs, and buffers that consume memory.  When a plan
+        *  is no more needed, the client must release the plan.
+        *  @param[in,out] plHandle Handle to a previously created plan
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftDestroyPlan( clfftPlanHandle* plHandle );
+
+       /*! @brief Retrieve the OpenCL context of a previously created plan.
+        *  @details The user must pass a reference to a cl_context variable, which is modified to point to a
+        *  context set in the specified plan.
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[out] context Reference to the user allocated cl_context, which points to context set in the plan
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftGetPlanContext( const clfftPlanHandle plHandle, cl_context* context );
+
+       /*! @brief Retrieve the floating point precision of the FFT data
+        *  @details The user must pass a reference to a clfftPrecision variable, which is set to the
+        *  precision of the FFT complex data in the plan.
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[out] precision Reference to the user clfftPrecision enum
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftGetPlanPrecision( const clfftPlanHandle plHandle, clfftPrecision* precision );
+
+       /*! @brief Set the floating point precision of the FFT data
+        *  @details Sets the floating point precision of the FFT complex data in the plan.
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] precision Reference to the user clfftPrecision enum
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftSetPlanPrecision( clfftPlanHandle plHandle, clfftPrecision precision );
+
+       /*! @brief Retrieve the scaling factor that is applied to the FFT data
+        *  @details The user must pass a reference to a cl_float variable, which is set to the
+        *  floating point scaling factor that is multiplied across the FFT data.
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] dir Direction of the applied scaling factor
+        *  @param[out] scale Reference to the user cl_float variable
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftGetPlanScale( const clfftPlanHandle plHandle, clfftDirection dir, cl_float* scale );
+
+       /*! @brief Set the scaling factor that is applied to the FFT data
+        *  @details Sets the floating point scaling factor that is
+        *  multiplied across the FFT data.
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] dir Direction of the applied scaling factor
+        *  @param[in] scale Reference to the user cl_float variable
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftSetPlanScale( clfftPlanHandle plHandle, clfftDirection dir, cl_float scale );
+
+       /*! @brief Retrieve the number of discrete arrays that the plan can concurrently handle
+        *  @details The user must pass a reference to a cl_uint variable, which is set to the
+        *  number of discrete arrays (1D or 2D) that is batched together for the plan
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[out] batchSize Number of discrete FFTs performed
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftGetPlanBatchSize( const clfftPlanHandle plHandle, size_t* batchSize );
+
+       /*! @brief Set the number of discrete arrays that the plan can concurrently handle
+        *  @details Sets the plan property which sets the number of discrete arrays (1D or 2D)
+        *  that is batched together for the plan
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] batchSize Number of discrete FFTs performed
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftSetPlanBatchSize( clfftPlanHandle plHandle, size_t batchSize );
+
+       /*! @brief Retrieve the dimensionality of the data that is transformed
+        *  @details Queries a plan object and retrieves the value of the dimensionality that the plan is set for.  A size is returned to
+        *  help the client allocate sufficient storage to hold the dimensions in a further call to clfftGetPlanLength
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[out] dim The dimensionality of the FFT to be transformed
+        *  @param[out] size Value to allocate an array to hold the FFT dimensions.
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftGetPlanDim( const clfftPlanHandle plHandle, clfftDim* dim, cl_uint* size );
+
+       /*! @brief Set the dimensionality of the data that is transformed
+        *  @details Set the dimensionality of the data that is transformed by the plan
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] dim The dimensionality of the FFT to be transformed
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftSetPlanDim( clfftPlanHandle plHandle, const clfftDim dim );
+
+       /*! @brief Retrieve the length of each dimension of the FFT
+        *  @details The user must pass a reference to a size_t array, which is set to the
+        *  length of each discrete dimension of the FFT
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] dim Dimension of the FFT; describes how many elements are in the clLengths array
+        *  @param[out] clLengths An array of length of size 'dim';  each array value describes the length of each dimension
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftGetPlanLength( const clfftPlanHandle plHandle, const clfftDim dim, size_t* clLengths );
+
+       /*! @brief Set the length of each dimension of the FFT
+        *  @details Sets the plan property which is the length of each discrete dimension of the FFT
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] dim The dimension of the FFT; describes how many elements are in the clLengths array
+        *  @param[in] clLengths An array of length of size 'dim';  each array value describes the length of each dimension
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftSetPlanLength( clfftPlanHandle plHandle, const clfftDim dim, const size_t* clLengths );
+
+       /*! @brief Retrieve the distance between consecutive elements of input buffers in each dimension.
+        *  @details Depending on how the dimension is set in the plan (for 2D or 3D FFT), strideY or strideZ can be safely
+        *  ignored
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] dim The dimension of the stride parameters; provides the number of elements in the array
+        *  @param[out] clStrides An array of strides, of size 'dim'.
+        */
+       CLFFTAPI clfftStatus    clfftGetPlanInStride( const clfftPlanHandle plHandle, const clfftDim dim, size_t* clStrides );
+
+       /*! @brief Set the distance between consecutive elements of input buffers in each dimension.
+        *  @details Set the plan properties which is the distance between elements in all dimensions of the input buffer
+        *  (units are in terms of clfftPrecision)
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] dim The dimension of the stride parameters; provides the number of elements in the clStrides array
+        *  @param[in] clStrides An array of strides of size 'dim'. Usually, strideX=1 so that successive elements in the first dimension are stored contiguously.
+        *      Typically, strideY=LenX and strideZ=LenX*LenY with the successive elements in the second and third dimensions stored in packed format.
+        *  See  @ref DistanceStridesandPitches for details.
+        */
+       CLFFTAPI clfftStatus    clfftSetPlanInStride( clfftPlanHandle plHandle, const clfftDim dim, size_t* clStrides );
+
+       /*! @brief Retrieve the distance between consecutive elements of output buffers in each dimension.
+        *  @details Depending on how the dimension is set in the plan (for 2D or 3D FFT), strideY or strideZ can be safely
+        *  ignored
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] dim The dimension of the stride parameters; provides the number of elements in the clStrides array
+        *  @param[out] clStrides An array of strides, of size 'dim'.
+        */
+       CLFFTAPI clfftStatus    clfftGetPlanOutStride( const clfftPlanHandle plHandle, const clfftDim dim, size_t* clStrides );
+
+       /*! @brief Set the distance between consecutive elements of output buffers in a dimension.
+        *  @details Sets the plan properties which is the distance between elements in all dimensions of the output buffer
+        *  (units are in terms of clfftPrecision)
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] dim The dimension of the stride parameters; provides the number of elements in the clStrides array
+        *  @param[in] clStrides An array of strides of size 'dim'.  Usually, strideX=1 so that successive elements in the first dimension are stored contiguously.
+        *      Typically, strideY=LenX and strideZ=LenX*LenY cause the successive elements in the second and third dimensions be stored in packed format.
+        *  @sa clfftSetPlanInStride
+        */
+       CLFFTAPI clfftStatus    clfftSetPlanOutStride( clfftPlanHandle plHandle, const clfftDim dim, size_t* clStrides );
+
+       /*! @brief Retrieve the distance between array objects
+        *  @details Pitch is the distance between each discrete array object in an FFT array. This is only used
+        *  for 'array' dimensions in clfftDim; see clfftSetPlanDimension (units are in terms of clfftPrecision)
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[out] iDist The distance between the beginning elements of the discrete array objects in input buffer.
+        *  For contiguous arrays in memory, iDist=(strideX*strideY*strideZ)
+        *  @param[out] oDist The distance between the beginning elements of the discrete array objects in output buffer.
+        *  For contiguous arrays in memory, oDist=(strideX*strideY*strideZ)
+        */
+       CLFFTAPI clfftStatus    clfftGetPlanDistance( const clfftPlanHandle plHandle, size_t* iDist, size_t* oDist );
+
+       /*! @brief Set the distance between array objects
+        *  @details Pitch is the distance between each discrete array object in an FFT array. This is only used
+        *  for 'array' dimensions in clfftDim; see clfftSetPlanDimension (units are in terms of clfftPrecision)
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[out] iDist The distance between the beginning elements of the discrete array objects in input buffer.
+        *  For contiguous arrays in memory, iDist=(strideX*strideY*strideZ)
+        *  @param[out] oDist The distance between the beginning elements of the discrete array objects in output buffer.
+        *  For contiguous arrays in memory, oDist=(strideX*strideY*strideZ)
+        */
+       CLFFTAPI clfftStatus    clfftSetPlanDistance( clfftPlanHandle plHandle, size_t iDist, size_t oDist );
+
+       /*! @brief Retrieve the expected layout of the input and output buffers
+        *  @details Input and output buffers can be filled with either Hermitian, complex, or real numbers.  Complex numbers are stored
+        *  in various layouts; this function retrieves the layouts used by input and output
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[out] iLayout Indicates how the input buffers are laid out in memory
+        *  @param[out] oLayout Indicates how the output buffers are laid out in memory
+        */
+       CLFFTAPI clfftStatus    clfftGetLayout( const clfftPlanHandle plHandle, clfftLayout* iLayout, clfftLayout* oLayout );
+
+       /*! @brief Set the expected layout of the input and output buffers
+        *  @details Input and output buffers can be filled with either Hermitian, complex, or real numbers.  Complex numbers can be stored
+        *  in various layouts; this function informs the library what layouts to use for input and output
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] iLayout Indicates how the input buffers are laid out in memory
+        *  @param[in] oLayout Indicates how the output buffers are laid out in memory
+        */
+       CLFFTAPI clfftStatus    clfftSetLayout( clfftPlanHandle plHandle, clfftLayout iLayout, clfftLayout oLayout );
+
+       /*! @brief Retrieve whether the input buffers are to be overwritten with results
+        *  @details If the setting performs an in-place transform, the input buffers are overwritten with the results of the
+        *  transform.  If the setting performs an out-of-place transforms, the library looks for separate output buffers
+        *  on the Enqueue call.
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[out] placeness Informs the library to either overwrite the input buffers with results or to write them in separate output buffers
+        */
+       CLFFTAPI clfftStatus    clfftGetResultLocation( const clfftPlanHandle plHandle, clfftResultLocation* placeness );
+
+       /*! @brief Set whether the input buffers are to be overwritten with results
+        *  @details If the setting performs an in-place transform, the input buffers are overwritten with the results of the
+        *  transform.  If the setting performs an out-of-place transforms, the library looks for separate output buffers
+        *  on the Enqueue call.
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] placeness Informs the library to either overwrite the input buffers with results or to write them in separate output buffers
+        */
+       CLFFTAPI clfftStatus    clfftSetResultLocation( clfftPlanHandle plHandle, clfftResultLocation placeness );
+
+       /*! @brief Retrieve the final transpose setting of a multi-dimensional FFT
+        *  @details A multi-dimensional FFT transposes the data several times during calculation. If the client
+        *  does not care about the final transpose, to put data back in proper dimension, the final transpose can be skipped
+        *  to improve speed
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[out] transposed Specifies whether the final transpose can be skipped
+        */
+       CLFFTAPI clfftStatus    clfftGetPlanTransposeResult( const clfftPlanHandle plHandle, clfftResultTransposed * transposed );
+
+       /*! @brief Set the final transpose setting of a multi-dimensional FFT
+        *  @details A multi-dimensional FFT transposes the data several times during calculation.  If the client
+        *  does not care about the final transpose, to put data back in proper dimension, the final transpose can be skipped
+        *  to improve speed
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] transposed Specifies whether the final transpose can be skipped
+        */
+       CLFFTAPI clfftStatus    clfftSetPlanTransposeResult( clfftPlanHandle plHandle, clfftResultTransposed transposed );
+
+
+       /*! @brief Get buffer size (in bytes), which may be needed internally for an intermediate buffer
+        *  @details Very large FFT transforms may need multiple passes, and the operation needs a temporary buffer to hold
+        *  intermediate results. This function is only valid after the plan is baked, otherwise, an invalid operation error
+        *  is returned. If the returned buffersize is 0, the runtime needs no temporary buffer.
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[out] buffersize Size in bytes for intermediate buffer
+        */
+       CLFFTAPI clfftStatus clfftGetTmpBufSize( const clfftPlanHandle plHandle, size_t* buffersize );
+
+       /*! @brief Register the callback parameters
+        *  @details Client can provide a callback function to do custom processing while reading input data and/or
+        *  writing output data. The callback function is provided as a string.
+        *  clFFT library incorporates the callback function string into the main FFT kernel. This function is used
+        *  by client to set the necessary parameters for callback
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] funcName Callback function name
+        *  @param[in] funcString Callback function in string form
+        *  @param[in] localMemSize Optional - Size (bytes) of the local memory used by callback function; pass 0 if no local memory is used
+        *  @param[in] callbackType Type of callback - Pre-Callback or Post-Callback
+        *  @param[in] userdata Supplementary data if any used by callback function
+        *  @param[in] numUserdataBuffers Number of userdata buffers
+        */
+       CLFFTAPI clfftStatus clfftSetPlanCallback(clfftPlanHandle plHandle, const char* funcName, const char* funcString,
+                                                                               int localMemSize, clfftCallbackType callbackType, cl_mem *userdata, int numUserdataBuffers);
+
+
+       /*! @brief Enqueue an FFT transform operation, and return immediately (non-blocking)
+        *  @details This transform API function computes the FFT transform. It is non-blocking as it
+        *  only enqueues the OpenCL kernels for execution. The synchronization step must be managed by the user.
+        *  @param[in] plHandle Handle to a previously created plan
+        *  @param[in] dir Forward or backward transform
+        *  @param[in] numQueuesAndEvents Number of command queues in commQueues; number of expected events to be returned in outEvents
+        *  @param[in] commQueues An array of cl_command_queues created by the client; the command queues must be a proper subset of
+        *      the devices included in the OpenCL context associated with the plan
+        *  @param[in] numWaitEvents Specify the number of elements in the eventWaitList array
+        *  @param[in] waitEvents Events for which the transform waits to complete before executing on the device
+        *  @param[out] outEvents The runtime fills this array with events corresponding one to one with the input command queues passed
+        *      in commQueues.  This parameter can have the value NULL or nullptr. When the value is NULL, the client is not interested in receiving notifications
+        *      when transforms are finished, otherwise, (if not NULL) the client is responsible for allocating this array with at least
+        *      as many elements as specified in numQueuesAndEvents.
+        *  @param[in] inputBuffers An array of cl_mem objects that contain data for processing by the FFT runtime. If the transform
+        *  is in-place, the FFT results overwrite the input buffers
+        *  @param[out] outputBuffers An array of cl_mem objects that store the results of out-of-place transforms. If the transform
+        *  is in-place, this parameter may be NULL or nullptr and is completely ignored
+        *  @param[in] tmpBuffer A cl_mem object that is reserved as a temporary buffer for FFT processing. If clTmpBuffers is NULL or nullptr,
+        *  and the library needs temporary storage, an internal temporary buffer is created on the fly managed by the library.
+        *  @return Enum describing error condition; superset of OpenCL error codes
+        */
+       CLFFTAPI clfftStatus    clfftEnqueueTransform(
+                                                                                               clfftPlanHandle plHandle,
+                                                                                               clfftDirection dir,
+                                                                                               cl_uint numQueuesAndEvents,
+                                                                                               cl_command_queue* commQueues,
+                                                                                               cl_uint numWaitEvents,
+                                                                                               const cl_event* waitEvents,
+                                                                                               cl_event* outEvents,
+                                                                                               cl_mem* inputBuffers,
+                                                                                               cl_mem* outputBuffers,
+                                                                                               cl_mem tmpBuffer
+                                                                                               );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/include/gromacs/external/clFFT/src/include/clFFT.version.h b/src/include/gromacs/external/clFFT/src/include/clFFT.version.h
new file mode 100644 (file)
index 0000000..6c04664
--- /dev/null
@@ -0,0 +1,24 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+/* the configured version and settings for clFFT
+ */
+#define clfftVersionMajor 2
+#define clfftVersionMinor 14
+#define clfftVersionPatch 0
+
+#define CLFFT_STATIC 1
diff --git a/src/include/gromacs/external/clFFT/src/include/convenienceFunctions.h b/src/include/gromacs/external/clFFT/src/include/convenienceFunctions.h
new file mode 100644 (file)
index 0000000..e32bd3f
--- /dev/null
@@ -0,0 +1,28 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+/*****************************************************/
+template< typename T >
+unsigned int float_as_hex( T a ) {
+       return *(unsigned int*)&a;
+}
+
+/*****************************************************/
+template< typename T >
+T hex_as_float( unsigned int a ) {
+       return *(T*)&a;
+}
\ No newline at end of file
diff --git a/src/include/gromacs/external/clFFT/src/include/sharedLibrary.h b/src/include/gromacs/external/clFFT/src/include/sharedLibrary.h
new file mode 100644 (file)
index 0000000..961dbca
--- /dev/null
@@ -0,0 +1,108 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+#ifndef _SHAREDLIBRARY_H_
+#define _SHAREDLIBRARY_H_
+#include <string>
+
+//     _WIN32 is defined for both 32 & 64 bit environments
+#if defined( _WIN32 )
+       #define WIN32_LEAN_AND_MEAN                     // Exclude rarely-used stuff from Windows headers
+       // Windows Header Files:
+       #include <windows.h>
+#else
+       #include <dlfcn.h>
+#endif
+
+inline void* LoadSharedLibrary( std::string unixPrefix, std::string libraryName, bool quiet )
+{
+#if defined( _WIN32 )
+       libraryName += ".dll";
+
+       //      HMODULE is actually the load address; function returns NULL if it cannot find the shared library
+       HMODULE fileHandle      = ::LoadLibraryExA( libraryName.c_str( ), NULL, NULL );
+#elif defined(__linux__) || defined(__GNU__) || (defined(__FreeBSD_kernel__) && defined(__GLIBC__))
+        tstring linuxName = unixPrefix;
+       linuxName += libraryName += ".so";
+       void* fileHandle = ::dlopen( linuxName.c_str( ), RTLD_NOW );
+       if( !quiet && !fileHandle )
+       {
+               std::cerr << ::dlerror( ) << std::endl;
+       }
+#elif defined(__APPLE__)
+  tstring appleName = unixPrefix;
+  appleName += libraryName += ".dylib";
+  void* fileHandle = ::dlopen( appleName.c_str( ), RTLD_NOW );
+  if( !quiet && !fileHandle )
+  {
+          std::cerr << ::dlerror( ) << std::endl;
+  }
+#elif defined(__FreeBSD__)
+        tstring freebsdName = unixPrefix;
+        freebsdName += libraryName += ".so";
+        void* fileHandle = ::dlopen( freebsdName.c_str( ), RTLD_NOW );
+        if( !quiet && !fileHandle )
+        {
+                std::cerr << ::dlerror( ) << std::endl;
+        }
+#else
+        #error "unsupported platform"
+#endif
+
+       return fileHandle;
+}
+
+//     If the function succeeds, the return value is nonzero.
+//     If the function fails, the return value is zero.
+inline int FreeSharedLibrary( void*& libHandle )
+{
+       int result      = 0;
+
+#if defined( _WIN32 )
+       if( libHandle != 0 )
+               result = ::FreeLibrary( reinterpret_cast< HMODULE >( libHandle ) );
+#else
+       if( libHandle != 0 )
+               result = ( ::dlclose( libHandle ) == 0 );
+#endif
+
+       libHandle       = NULL;
+
+       return result;
+}
+
+//     This takes a shared module handle returned from LoadSharedLibrary, and a text string of a symbol
+//     to load from the module, and returns a pointer to that symbol.  If the symbol is not found, NULL
+//     is returned.  If the module handle is NULL, NULL is returned.
+inline void* LoadFunctionAddr( void* libHandle, std::string funcName )
+{
+       if( libHandle == NULL )
+               return NULL;
+
+#if defined( _WIN32 )
+       HMODULE fileHandle = reinterpret_cast< HMODULE >( libHandle );
+
+       void* pFunc     = reinterpret_cast< void* >( ::GetProcAddress( fileHandle, funcName.c_str( ) ) );
+#else
+       void* pFunc = ::dlsym( libHandle, funcName.c_str( ) );
+#endif
+
+       return pFunc;
+}
+
+#endif // _SHAREDLIBRARY_H_
diff --git a/src/include/gromacs/external/clFFT/src/include/stdafx.h b/src/include/gromacs/external/clFFT/src/include/stdafx.h
new file mode 100644 (file)
index 0000000..4ab26bf
--- /dev/null
@@ -0,0 +1,52 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <iomanip>
+#include <cstring>
+#include <memory>
+#include <vector>
+#include <valarray>
+#include <cstring>
+#include <stdarg.h>
+#include <assert.h>
+#include <complex>
+
+//     _WIN32 is defined for both 32 & 64 bit environments
+#if defined( _WIN32 )
+       #include <tchar.h>
+       #include "targetver.h"
+
+#if !defined( NOMINMAX )
+       #define NOMINMAX
+#endif
+
+    #define WIN32_LEAN_AND_MEAN                        // Exclude rarely-used stuff from Windows headers
+       // Windows Header Files:
+       #include <windows.h>
+#endif
diff --git a/src/include/gromacs/external/clFFT/src/include/targetver.h b/src/include/gromacs/external/clFFT/src/include/targetver.h
new file mode 100644 (file)
index 0000000..7c05692
--- /dev/null
@@ -0,0 +1,25 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>
diff --git a/src/include/gromacs/external/clFFT/src/include/unicode.compatibility.h b/src/include/gromacs/external/clFFT/src/include/unicode.compatibility.h
new file mode 100644 (file)
index 0000000..56a365f
--- /dev/null
@@ -0,0 +1,59 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+#if !defined( amd_unicode_h )
+#define amd_unicode_h
+
+//     Typedefs to support unicode and ansii compilation
+#if defined( _UNICODE )
+       typedef std::wstring            tstring;
+       typedef std::wstringstream      tstringstream;
+       typedef std::wifstream          tifstream;
+       typedef std::wofstream          tofstream;
+       typedef std::wfstream           tfstream;
+       static std::wostream&   tout    = std::wcout;
+       static std::wostream&   terr    = std::wcerr;
+#else
+       typedef std::string tstring;
+       typedef std::stringstream tstringstream;
+       typedef std::ifstream           tifstream;
+       typedef std::ofstream           tofstream;
+       typedef std::fstream            tfstream;
+       static std::ostream&    tout    = std::cout;
+       static std::ostream&    terr    = std::cerr;
+#endif
+
+//     These macros help linux cope with the conventions of windows tchar.h file
+#if defined( _WIN32 )
+       #include <tchar.h>
+       #include <windows.h>
+#else
+       #if defined( __GNUC__ )
+               typedef char TCHAR;
+               typedef char _TCHAR;
+               #define _tmain main
+
+               #if defined( UNICODE )
+                       #define _T(x)   L ## x
+               #else
+                       #define _T(x)   x
+               #endif
+       #endif
+#endif
+
+#endif
diff --git a/src/include/gromacs/external/clFFT/src/library/action.h b/src/include/gromacs/external/clFFT/src/library/action.h
new file mode 100644 (file)
index 0000000..aecbe58
--- /dev/null
@@ -0,0 +1,314 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+#if !defined( AMD_CLFFT_action_H )
+#define AMD_CLFFT_action_H
+
+#include "plan.h"
+
+
+//
+// FFTCopyAction
+//
+// Base class for every Copy action for the FFT.
+// Currently do nothing special. The kernel generation and compilation occurs
+// by the subclass FFTGeneratedCopyAction
+// 
+class FFTCopyAction : public FFTAction
+{
+public:
+    FFTCopyAction(clfftPlanHandle plHandle, FFTPlan * plan, cl_command_queue queue, clfftStatus & err);
+
+    clfftGenerators getGenerator() { return Copy; }
+};
+
+
+//
+// FFTStockhamAction
+//
+// Base class for every Stockham action for the FFT.
+// Currently do nothing special. The kernel generation and compilation occurs
+// by the subclasses FFTGeneratedStockhamAction or FFTStaticStockhamAction
+// 
+class FFTStockhamAction : public FFTAction
+{
+public:
+    FFTStockhamAction(clfftPlanHandle plHandle, FFTPlan * plan, cl_command_queue queue, clfftStatus & err);
+
+    clfftGenerators getGenerator() { return Stockham; }
+};
+
+
+
+
+//
+// FFTTransposeGCNAction
+//
+// Base class for every TransposeGCN action for the FFT.
+// Currently do nothing special. The kernel generation and compilation occurs
+// by the subclass FFTGeneratedTransposeGCNAction
+// 
+class FFTTransposeGCNAction : public FFTAction
+{
+public:
+    FFTTransposeGCNAction(clfftPlanHandle plHandle, FFTPlan * plan, cl_command_queue queue, clfftStatus & err);
+
+    clfftGenerators getGenerator() { return Transpose_GCN; }
+};
+
+//
+// FFTTransposeSquareAction
+//
+// Base class for every TransposeSquare action for the FFT.
+// Currently do nothing special. The kernel generation and compilation occurs
+// by the subclass FFTGeneratedTransposeSquareAction
+// 
+class FFTTransposeSquareAction : public FFTAction
+{
+public:
+    FFTTransposeSquareAction(clfftPlanHandle plHandle, FFTPlan * plan, cl_command_queue queue, clfftStatus & err);
+
+    clfftGenerators getGenerator() { return Transpose_SQUARE; }
+};
+
+//
+// FFTTransposeNonSquareAction
+//
+// Base class for every TransposeSquare action for the FFT.
+// Currently do nothing special. The kernel generation and compilation occurs
+// by the subclass FFTGeneratedTransposeSquareAction
+// 
+class FFTTransposeNonSquareAction : public FFTAction
+{
+public:
+    FFTTransposeNonSquareAction(clfftPlanHandle plHandle, FFTPlan * plan, cl_command_queue queue, clfftStatus & err);
+
+    clfftGenerators getGenerator() { return Transpose_NONSQUARE; }
+};
+
+//
+// FFTGeneratedCopyAction
+//
+// Implements a Copy action for the FFT
+// Its signature is represented by FFTKernelGenKeyParams structure
+// 
+// This class implements:
+//  - the generation of the kernel string
+//  - the build of the kernel
+//
+// The structure FFTKernelGenKeyParams is used to characterize and generate
+// the appropriate copy kernel. That structure is used for the signature of
+// this action. It is common to Stockham, copy and transpose methods. For
+// convenience, this structure is used for every FFTGenerated*Action class,
+// but in practice the copy action only use a few information of that
+// structure, so a proper structure should be used instead.
+//
+class FFTGeneratedCopyAction : public FFTCopyAction
+{
+public:
+    FFTGeneratedCopyAction(clfftPlanHandle plHandle, FFTPlan * plan, cl_command_queue queue, clfftStatus & err);
+
+    typedef FFTKernelSignature<FFTKernelGenKeyParams, FFT_DEFAULT_COPY_ACTION> Signature;
+
+private:
+    Signature signature;
+
+    clfftStatus generateKernel  (FFTRepo& fftRepo, const cl_command_queue commQueueFFT );
+    clfftStatus getWorkSizes    (std::vector<size_t> & globalws, std::vector<size_t> & localws);
+    clfftStatus initParams      ();
+
+    bool buildForwardKernel();
+    bool buildBackwardKernel();
+
+public:
+
+    virtual const Signature * getSignatureData()
+    {
+        return &this->signature;
+    }
+};
+
+
+//
+// FFTGeneratedStockhamAction
+//
+// Represents a Stockham action for the FFT. This class implements the former
+// mechanism of kernel generation and compilation for Stockham method.
+// 
+// This class implements:
+//  - the generation of the kernel string
+//  - the build of the kernel
+//
+// The structure FFTKernelGenKeyParams is used to characterize and generate
+// the appropriate kernel. That structure is used for the signature of this
+// action. For convenience, this structure is used for every
+// FFTGenerated*Action class, but a "Stockham-specific" version of that
+// structure should be used instead.
+//
+class FFTGeneratedStockhamAction : public FFTStockhamAction
+{
+public:
+    FFTGeneratedStockhamAction(clfftPlanHandle plHandle, FFTPlan * plan, cl_command_queue queue, clfftStatus & err);
+    
+    typedef FFTKernelSignature<FFTKernelGenKeyParams, FFT_DEFAULT_STOCKHAM_ACTION> Signature;
+
+private:
+    Signature signature;
+
+    clfftStatus generateKernel  (FFTRepo& fftRepo, const cl_command_queue commQueueFFT );
+    clfftStatus getWorkSizes    (std::vector<size_t> & globalws, std::vector<size_t> & localws);
+    clfftStatus initParams      ();
+
+    bool buildForwardKernel();
+    bool buildBackwardKernel();
+
+public:
+
+    virtual const Signature * getSignatureData()
+    {
+        return &this->signature;
+    }
+};
+
+
+
+
+// FFTGeneratedTransposeGCNAction
+//
+// Implements a TransposeGCN action for the FFT
+// Its signature is represented by FFTKernelGenKeyParams structure
+// 
+// This class implements:
+//  - the generation of the kernel string
+//  - the build of the kernel
+// 
+// The structure FFTKernelGenKeyParams is used to characterize and generate
+// the appropriate transpose kernel. That structure is used for the signature of
+// this action. It is common to Stockham, copy and transpose methods. For
+// convenience, this structure is used for every FFTGenerated*Action class,
+// but in practice the transpose action only use a few information of that
+// structure, so a proper structure should be used instead.
+//
+class FFTGeneratedTransposeGCNAction : public FFTTransposeGCNAction
+{
+public:
+    FFTGeneratedTransposeGCNAction(clfftPlanHandle plHandle, FFTPlan * plan, cl_command_queue queue, clfftStatus & err);
+
+    typedef FFTKernelSignature<FFTKernelGenKeyParams, FFT_DEFAULT_TRANSPOSE_ACTION> Signature;
+
+private:
+    Signature signature;
+
+    clfftStatus generateKernel  (FFTRepo& fftRepo, const cl_command_queue commQueueFFT );
+    clfftStatus getWorkSizes    (std::vector<size_t> & globalws, std::vector<size_t> & localws);
+    clfftStatus initParams      ();
+
+    bool buildForwardKernel();
+    bool buildBackwardKernel();
+
+public:
+
+    virtual const Signature * getSignatureData()
+    {
+        return &this->signature;
+    }
+};
+
+
+// FFTGeneratedTransposeSquareAction
+//
+// Implements a TransposeSquare action for the FFT
+// Its signature is represented by FFTKernelGenKeyParams structure
+// 
+// This class implements:
+//  - the generation of the kernel string
+//  - the build of the kernel
+// 
+// The structure FFTKernelGenKeyParams is used to characterize and generate
+// the appropriate transpose kernel. That structure is used for the signature of
+// this action. It is common to Stockham, copy and transpose methods. For
+// convenience, this structure is used for every FFTGenerated*Action class,
+// but in practice the transpose action only use a few information of that
+// structure, so a proper structure should be used instead.
+//
+class FFTGeneratedTransposeSquareAction : public FFTTransposeSquareAction
+{
+public:
+    FFTGeneratedTransposeSquareAction(clfftPlanHandle plHandle, FFTPlan * plan, cl_command_queue queue, clfftStatus & err);
+
+    typedef FFTKernelSignature<FFTKernelGenKeyParams, FFT_DEFAULT_TRANSPOSE_ACTION> Signature;
+
+private:
+    Signature signature;
+
+    clfftStatus generateKernel  (FFTRepo& fftRepo, const cl_command_queue commQueueFFT );
+    clfftStatus getWorkSizes    (std::vector<size_t> & globalws, std::vector<size_t> & localws);
+    clfftStatus initParams      ();
+
+    bool buildForwardKernel();
+    bool buildBackwardKernel();
+
+public:
+
+    virtual const Signature * getSignatureData()
+    {
+        return &this->signature;
+    }
+};
+
+// FFTGeneratedTransposeNonSquareAction
+//
+// Implements a TransposeSquare action for the FFT
+// Its signature is represented by FFTKernelGenKeyParams structure
+// 
+// This class implements:
+//  - the generation of the kernel string
+//  - the build of the kernel
+// 
+// The structure FFTKernelGenKeyParams is used to characterize and generate
+// the appropriate transpose kernel. That structure is used for the signature of
+// this action. It is common to Stockham, copy and transpose methods. For
+// convenience, this structure is used for every FFTGenerated*Action class,
+// but in practice the transpose action only use a few information of that
+// structure, so a proper structure should be used instead.
+//
+class FFTGeneratedTransposeNonSquareAction : public FFTTransposeNonSquareAction
+{
+public:
+    FFTGeneratedTransposeNonSquareAction(clfftPlanHandle plHandle, FFTPlan * plan, cl_command_queue queue, clfftStatus & err);
+
+    typedef FFTKernelSignature<FFTKernelGenKeyParams, FFT_DEFAULT_TRANSPOSE_ACTION> Signature;
+
+private:
+    Signature signature;
+
+    clfftStatus generateKernel(FFTRepo& fftRepo, const cl_command_queue commQueueFFT);
+    clfftStatus getWorkSizes(std::vector<size_t> & globalws, std::vector<size_t> & localws);
+    clfftStatus initParams();
+
+    bool buildForwardKernel();
+    bool buildBackwardKernel();
+
+public:
+
+    virtual const Signature * getSignatureData()
+    {
+        return &this->signature;
+    }
+};
+#endif // AMD_CLFFT_action_H
diff --git a/src/include/gromacs/external/clFFT/src/library/action.transpose.h b/src/include/gromacs/external/clFFT/src/library/action.transpose.h
new file mode 100644 (file)
index 0000000..532707a
--- /dev/null
@@ -0,0 +1,25 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+#pragma once
+#if !defined( AMD_CLFFT_action_transpose_H )
+#define AMD_CLFFT_action_transpose_H
+#include "private.h"
+#include "repo.h"
+#include "plan.h"
+
+#endif
+
diff --git a/src/include/gromacs/external/clFFT/src/library/fft_binary_lookup.h b/src/include/gromacs/external/clFFT/src/library/fft_binary_lookup.h
new file mode 100644 (file)
index 0000000..19081f3
--- /dev/null
@@ -0,0 +1,277 @@
+/* ************************************************************************
+ * Copyright 2014 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+#ifndef __CLFFT_CLFFT_BINARY_LOOKUP__
+#define __CLFFT_CLFFT_BINARY_LOOKUP__
+
+#if defined(__APPLE__) || defined(__MACOSX)
+#include <OpenCL/cl.h>
+#else
+#include <CL/cl.h>
+#endif
+
+#include <string>
+#include <vector>
+
+#include "generator.h"
+#include "plan.h"
+
+//
+// FFTBinaryLookup defines an API to manage the kernel cache on the disk
+//
+// The FFTBinaryLookup object provides methods to:
+//  * check if a cache file exists on the disk or not
+//  * fill-up the signature to characterize the program beeing built on the disk
+//  * build a cl_program from a string kernel or from a binary
+// 
+// A cache entry is a file stored on the disk which contains 3 sections:
+//  * A header section (providing information about file structure)
+//  * The binary contained in the cl_program
+//  * A signature which provides additionnal informations about the kernel
+//    and allows to characterize the kernel in the disk cache
+//
+// The environment variable CLFFT_CACHE_PATH defines the location of the
+// cache on the disk. If the variable CLFFT_CACHE_PATH is not defined, no
+// cache file is written on the disk, but the cl_program can be built and
+// remains on memory
+//
+// Concerning multithreading, the policy is that every thread build the
+// cl_program from the source, but only the first one writes it on the
+// disk. Other threads continue with the cl_program in memory.
+//
+// A typical cache query shall be composed of the following steps:
+//
+//  (1) Create a local instance of FFTBinaryLookup 
+//
+//  (2) Specify the additional characteristics (i.e. variants) of the
+//      requested program. Those information combined with the program
+//      name and the OpenCL context and device shall form a unique
+//      signature for the binary program.
+// 
+//  (3) Perform the effective search by calling the 'found' method
+// 
+//  (4) if the search was successfull then cl_program can be retreived 
+//      by a call to the 'getProgram' method
+// 
+//  (5) if the search was not successfull then a cl_program 
+//      must be created  and populated in the cache by a call 
+//      to the 'setProgram' method.
+// 
+//  (6) Destroy the FFTBinaryLookup local instance.
+// 
+// For instance, that could be 
+//
+//     cl_program program  ;
+//   
+//     The program name is part of the signature and shall be unique 
+//     const char * program_name = "... my unique program name ... " ;
+//   
+//     FFTBinaryLookup bl(context, device, program_name);
+//   
+//     //  Specify additionnal information used to build a
+//     //  signature signature for that cache entry 
+//                  
+//     bl.variantInt( vectorSize );
+//     bl.variantInt( hasBorder );
+//     ... 
+//   
+//     // Perform the query 
+//     if ( bl.found() ) 
+//     {
+//        //   Success! use the cl_program retreived from the cache
+//        program = bl.getProgram();
+//     }
+//     else 
+//     {
+//        //   Failure! we need to build the program ourself
+//        program = build_the_program(context,device,vectorSize,...) ; 
+//        //   Inform the lookup object of the program
+//        bl.setProgram(program);  
+//        //   And populate the cache
+//        bl.populateCache() 
+//     }
+// 
+// Remark: The members buildFromSource, buildFromBinary etc are utility 
+//         functions that can be used to build the cl_program from either 
+//         sources or binary (e.g. SPIR). Their use is optionnal. 
+//
+//
+class FFTBinaryLookup
+ {
+public:
+    // Constructor
+    // \param ctxt the context for which the cl_program should be built
+    // \param device the device for which the cl_program should be built
+    // \param kernel_name the kernel identifier
+    FFTBinaryLookup(const clfftGenerators gen, const clfftPlanHandle plHandle, cl_context ctxt, cl_device_id device);
+    ~FFTBinaryLookup();
+
+    // Methods to fill up the signature of the cache entry
+    void variantInt(int num);
+    void variantDouble(double num);
+    void variantCompileOptions(const std::string & opts);
+    void variantRaw(const void * data, size_t bytes);
+
+    // Indicates whether or not the cache entry was found on the disk
+    // If the cache entry was found and is complete on the disk, its content
+    // is loaded
+    // \return true if a cache entry was found, false else
+    bool found();
+
+    // Build a cl_program from the source code and init attributes
+    // of the current structure
+    // so that the program can be accessed with the getProgram method
+    // Write the file to the cache
+    cl_int buildFromSource(const char * source);
+
+    // Build a cl_program from the source code and init attributes
+    // so that the program can be accessed with the getProgram method
+    // Write the file to the cache
+    cl_int buildFromBinary(const void * data, 
+                           size_t len);
+
+    // Returns the cl_program built from binary or loaded from disk
+    cl_program getProgram();
+
+    // Set the current m_program to the given program
+    void setProgram(cl_program program, const char * source);
+
+    // Build a cl_program from a text
+    static cl_program buildProgramFromSource(const char * filename,
+                                             cl_context context,
+                                             cl_device_id device,
+                                             cl_int & err,
+                                             const char * options = 0);
+
+    // Build a cl_program from binary
+    static cl_program buildProgramFromBinary(const char * data,
+                                             size_t data_size,
+                                             cl_context context,
+                                             cl_device_id device,
+                                             cl_int & err,
+                                             const char * options = 0);
+
+    // Initialize the whole cache file information (magic_key, header and program)
+    // and dump on the disk
+    cl_int populateCache();
+
+private:
+
+    // Serialize variants and compute the checksum to load the file from cache
+    void finalizeVariant();
+
+    // Build a cl_program from the source code and init attributes
+    // so that the program can be accessed with the getProgram method
+    // Do not write the file to the cache
+    cl_int buildFromLoadedBinary(const void * data, 
+                                 size_t len);
+
+    // Try to retrieve the header of the cache file
+    // Returns: ok if the header sections was successfully loaded, false else
+    bool loadHeader(std::ifstream &file, size_t length);
+
+    // Try to retrieve the cl_program and its signature in file
+    // Returns: ok if the binary and signature sections were successfully loaded, false else
+    bool loadBinaryAndSignature(std::ifstream &file);
+
+    // Try to create a file associated to the current program/variant in the cache folder
+    // Returns true if the file was successfully opened and loaded, false else
+    bool tryLoadCacheFile();
+
+    // Dump the file on the disk with the name stored in this->m_cache_entry_name
+    cl_int writeCacheFile(std::vector<unsigned char*> &data);
+
+    // Retrieve device name, device vendor and driver number by calling
+    // clGetDeviceInfo
+    cl_int retrieveDeviceAndDriverInfo();
+
+    // Cache entry name 
+    std::string m_cache_entry_name;
+
+    // Path for the cache entry name
+    std::string m_path;
+
+    // Header structure of a cache entry
+    typedef struct Header_
+    {
+        char magic_key[4]; // = |C|L|F|\0, useful to know that we are loading a clfft cache entry
+        size_t whole_file_size; // the whole file of the size to know if the current file is complete or not
+        size_t header_size; // = sizeof(Header)
+        size_t binary_size; // kernel binary size
+        size_t signature_size; // variant information
+    } Header;
+
+    Header m_header;
+
+    cl_context   m_context;
+    cl_device_id m_device;
+
+    cl_program   m_program;
+
+    std::string  m_source;
+
+    unsigned char * m_binary;
+    char *          m_signature;
+
+    enum VariantKind
+    {
+        INT,
+        DOUBLE,
+        STRING,
+        DATA
+    };
+
+    struct Variant
+    {
+        Variant();
+        Variant(VariantKind kind, char * data, size_t size);
+               Variant(const Variant &obj);
+               Variant &operator=(const Variant &obj);
+
+
+        ~Variant();
+
+        VariantKind m_kind;
+        size_t      m_size;
+        char *      m_data;
+        
+    };
+
+    // Cache entry, useful to abstract Windows and linux 
+    // cache entry file descriptor
+    struct CacheEntry
+    {
+        CacheEntry(const std::string & filename);
+        bool exclusive_create();
+        void close();
+        bool successful_creation();
+
+    private:
+        std::string m_filename;
+        bool        m_successful_creation;
+        void *      m_handle;
+    };
+
+    // Variants
+    std::vector<Variant> m_variants;
+
+    // Indicates whether the cache should be used or not
+    bool m_cache_enabled;
+};
+
+#undef SIZE
+
+#endif
diff --git a/src/include/gromacs/external/clFFT/src/library/generator.h b/src/include/gromacs/external/clFFT/src/library/generator.h
new file mode 100644 (file)
index 0000000..fbf5cce
--- /dev/null
@@ -0,0 +1,33 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+#if !defined( AMD_CLFFT_generator_H )
+#define AMD_CLFFT_generator_H
+
+//     Enum to help provide descriptive names to array indices, when indexing into our various vectors
+enum clfftGenerators
+{
+    Stockham, // Using the Stockham autosort frameworks
+    Transpose_GCN,
+    Transpose_SQUARE,
+    Transpose_NONSQUARE,
+    Copy,
+    ENDGENERATORS                      ///< This value will always be last, and marks the length of clfftGenerators
+};
+
+#endif
diff --git a/src/include/gromacs/external/clFFT/src/library/generator.stockham.h b/src/include/gromacs/external/clFFT/src/library/generator.stockham.h
new file mode 100644 (file)
index 0000000..df5a37c
--- /dev/null
@@ -0,0 +1,2101 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+#ifdef _MSC_VER
+#pragma warning(disable : 4996)
+#endif
+
+#pragma once
+#if !defined( AMD_CLFFT_generator_stockham_H )
+#define AMD_CLFFT_generator_stockham_H
+#include <stdio.h>
+#include "private.h"
+#include "repo.h"
+#include "plan.h"
+
+typedef union {
+       cl_float f;
+       cl_uint  u;
+       cl_int   i;
+} cb_t;
+
+namespace StockhamGenerator
+{
+       // Precision
+       enum Precision
+       {
+               P_SINGLE,
+               P_DOUBLE,
+       };
+
+       template <Precision PR>
+       inline size_t PrecisionWidth()
+       {
+               switch(PR)
+               {
+               case P_SINGLE:  return 1;
+               case P_DOUBLE:  return 2;
+               default:                assert(false); return 1;
+               }
+       }
+
+       template <Precision PR>
+       inline std::string ClPragma()
+       {
+               switch(PR)
+               {
+               case P_SINGLE:  return "";
+               case P_DOUBLE:  return  "\n#ifdef cl_khr_fp64\n"
+                                                               "#pragma OPENCL EXTENSION cl_khr_fp64 : enable\n"
+                                                               "#else\n"
+                                                               "#pragma OPENCL EXTENSION cl_amd_fp64 : enable\n"
+                                                               "#endif\n\n";
+               default:                assert(false); return "";
+               }
+       }
+
+       // Convert unsigned integers to string
+       inline std::string SztToStr(size_t i)
+       {
+               std::stringstream ss;
+               ss << i;
+               return ss.str();
+       }
+
+       inline std::string FloatToStr(double f)
+       {
+               std::stringstream ss;
+               ss.imbue(std::locale("C"));
+               ss.precision(16);
+               ss << std::scientific << f;
+               return ss.str();
+       }
+
+
+       //      Find the smallest power of 2 that is >= n; return its power of 2 factor
+       //      e.g., CeilPo2 (7) returns 3 : (2^3 >= 7)
+       inline size_t CeilPo2 (size_t n)
+       {
+               size_t v = 1, t = 0;
+               while(v < n)
+               {
+                       v <<= 1;
+                       t++;
+               }
+
+               return t;
+       }
+
+       inline size_t FloorPo2 (size_t n)
+       //      return the largest power of 2 that is <= n.
+       //      e.g., FloorPo2 (7) returns 4.
+       // *** TODO use x86 BSR instruction, using compiler intrinsics.
+       {
+               size_t tmp;
+               while (0 != (tmp = n & (n-1)))
+                       n = tmp;
+               return n;
+       }
+
+       typedef std::pair<std::string,std::string> stringpair;
+       inline stringpair ComplexMul(const char *type, const char * a, const char * b, bool forward = true)
+       {
+               stringpair result;
+               result.first = "(";
+               result.first += type;
+               result.first += ") ((";
+               result.first += a;
+               result.first += ".x * ";
+               result.first += b;
+               result.first += (forward ? ".x - " : ".x + ");
+               result.first += a;
+               result.first += ".y * ";
+               result.first += b;
+               result.first += ".y),";
+               result.second = "(";
+               result.second += a;
+               result.second += ".y * ";
+               result.second += b;
+               result.second += (forward ? ".x + " : ".x - ");
+               result.second += a;
+               result.second += ".x * ";
+               result.second += b;
+               result.second += ".y))";
+               return result;
+       }
+
+
+       // Register data base types
+       template <Precision PR>
+       inline std::string RegBaseType(size_t count)
+       {
+               switch(PR)
+               {
+               case P_SINGLE:
+                       switch(count)
+                       {
+                       case 1: return "float";
+                       case 2: return "float2";
+                       case 4: return "float4";
+                       default: assert(false); return "";
+                       }
+                       break;
+               case P_DOUBLE:
+                       switch(count)
+                       {
+                       case 1: return "double";
+                       case 2: return "double2";
+                       case 4: return "double4";
+                       default: assert(false); return "";
+                       }
+                       break;
+               default:
+                       assert(false); return "";
+               }
+       }
+
+       template <Precision PR>
+       inline std::string FloatSuffix()
+       {
+               // Suffix for constants
+               std::string sfx;
+               switch(PR)
+               {
+               case P_SINGLE: sfx = "f"; break;
+               case P_DOUBLE: sfx = "";  break;
+               default: assert(false);
+               }
+
+               return sfx;
+       }
+
+       inline std::string ButterflyName(size_t radix, size_t count, bool fwd)
+       {
+               std::string str;
+               if(fwd) str += "Fwd";
+               else    str += "Inv";
+               str += "Rad"; str += SztToStr(radix);
+               str += "B"; str += SztToStr(count);
+               return str;
+       }
+
+       inline std::string PassName(size_t pos, bool fwd)
+       {
+               std::string str;
+               if(fwd) str += "Fwd";
+               else    str += "Inv";
+               str += "Pass"; str += SztToStr(pos);
+               return str;
+       }
+
+       inline std::string TwTableName()
+       {
+               return "twiddles";
+       }
+
+       inline std::string TwTableLargeName()
+       {
+               return "twiddle_dee";
+       }
+
+       inline std::string TwTableLargeFunc()
+       {
+               return "TW3step";
+       }
+
+       // Twiddle factors table for large N
+       // used in 3-step algorithm
+    class TwiddleTableLarge
+    {
+        size_t N; // length
+               size_t X, Y;
+               size_t tableSize;
+               double *wc, *ws; // cosine, sine arrays
+
+       public:
+               TwiddleTableLarge(size_t length) : N(length)
+               {
+                       X = size_t(1) << ARBITRARY::TWIDDLE_DEE;
+                       Y = DivRoundingUp<size_t> (CeilPo2(N), ARBITRARY::TWIDDLE_DEE);
+                       tableSize = X * Y;
+
+                       // Allocate memory for the tables
+                       wc = new double[tableSize];
+                       ws = new double[tableSize];
+               }
+
+               ~TwiddleTableLarge()
+               {
+                       // Free
+                       delete[] wc;
+                       delete[] ws;
+               }
+
+               template <Precision PR>
+               void GenerateTwiddleTable(std::string &twStr)
+               {
+                       const double TWO_PI = -6.283185307179586476925286766559;
+
+                       // Generate the table
+                       size_t nt = 0;
+                       double phi = TWO_PI / double (N);
+                       for (size_t iY = 0; iY < Y; ++iY)
+                       {
+                               size_t i = size_t(1) << (iY * ARBITRARY::TWIDDLE_DEE);
+                               for (size_t iX = 0; iX < X; ++iX)
+                               {
+                                       size_t j = i * iX;
+
+                                       double c = cos(phi * (double)j);
+                                       double s = sin(phi * (double)j);
+
+                                       //if (fabs(c) < 1.0E-12)        c = 0.0;
+                                       //if (fabs(s) < 1.0E-12)        s = 0.0;
+
+                                       wc[nt]   = c;
+                                       ws[nt++] = s;
+                               }
+                       }
+
+                       std::string sfx = FloatSuffix<PR>();
+
+                       // Stringize the table
+                       std::stringstream ss;
+                       ss.imbue(std::locale("C"));
+                       ss.precision(34);
+                       ss << std::scientific;
+                       nt = 0;
+
+                       ss << "\n __constant ";
+                       ss << RegBaseType<PR>(2);
+                       ss << " " << TwTableLargeName();
+                       ss << "[" << Y << "][" << X << "] = {\n";
+                       for (size_t iY = 0; iY < Y; ++iY)
+                       {
+                               ss << "{ ";
+                               for (size_t iX = 0; iX < X; ++iX)
+                               {
+                                       ss << "("; ss << RegBaseType<PR>(2); ss << ")(";
+                                       ss << wc[nt] << sfx << ", ";
+                                       ss << ws[nt++] << sfx << "),\n";
+                               }
+                               ss << " },\n";
+                       }
+                       ss << "};\n\n";
+
+                       // Twiddle calc function
+                       ss << "__attribute__((always_inline)) ";
+                       ss << RegBaseType<PR>(2);
+                       ss << "\n" << TwTableLargeFunc() << "(size_t u)\n{\n";
+
+                       ss << "\t" "size_t j = u & " << unsigned(X-1) << ";\n";
+                       ss << "\t" ; ss << RegBaseType<PR>(2); ss << " result = ";
+                       ss << TwTableLargeName();
+                       ss << "[0][j];\n";
+
+                       for (size_t iY = 1; iY < Y; ++iY)
+                       {
+                               std::string phasor = TwTableLargeName();
+                               phasor += "[";
+                               phasor += SztToStr(iY);
+                               phasor += "][j]";
+
+                               stringpair product = ComplexMul((RegBaseType<PR>(2)).c_str(), "result", phasor.c_str());
+
+                               ss << "\t" "u >>= " << unsigned (ARBITRARY::TWIDDLE_DEE) << ";\n";
+                               ss << "\t" "j = u & " << unsigned(X-1) << ";\n";
+                               ss << "\t" "result = " << product.first << "\n";
+                               ss << "\t" "\t" << product.second <<";\n";
+                       }
+                       ss << "\t" "return result;\n}\n\n";
+
+                       twStr += ss.str();
+               }
+       };
+
+       // FFT butterfly
+    template <Precision PR>
+    class Butterfly
+    {
+               size_t radix;           // Base radix
+        size_t count;       // Number of basic butterflies, valid values: 1,2,4
+               bool fwd;                       // FFT direction
+               bool cReg;                      // registers are complex numbers, .x (real), .y(imag)
+
+               size_t BitReverse (size_t n, size_t N) const
+               {
+                       return (N < 2) ? n : (BitReverse (n >> 1, N >> 1) | ((n & 1) != 0 ? (N >> 1) : 0));
+               }
+
+               void GenerateButterflyStr(std::string &bflyStr) const
+               {
+                       std::string regType = cReg ? RegBaseType<PR>(2) : RegBaseType<PR>(count);
+
+                       // Function attribute
+                       bflyStr += "__attribute__((always_inline)) void \n";
+
+                       // Function name
+                       bflyStr += ButterflyName(radix, count, fwd);
+
+                       // Function Arguments
+                       bflyStr += "(";
+                       for(size_t i=0;;i++)
+                       {
+                               if(cReg)
+                               {
+                                       bflyStr += regType; bflyStr += " *R";
+                                       if(radix & (radix-1))   bflyStr += SztToStr(i);
+                                       else                                    bflyStr += SztToStr(BitReverse(i,radix));
+                               }
+                               else
+                               {
+                                       bflyStr += regType; bflyStr += " *R"; bflyStr += SztToStr(i); bflyStr += ", ";  // real arguments
+                                       bflyStr += regType; bflyStr += " *I"; bflyStr += SztToStr(i);                                   // imaginary arguments
+                               }
+
+                               if(i == radix-1)
+                               {
+                                       bflyStr += ")";
+                                       break;
+                               }
+                               else
+                               {
+                                       bflyStr += ", ";
+                               }
+                       }
+
+                       bflyStr += "\n{\n\n";
+
+
+                       // Temporary variables
+                       // Allocate temporary variables if we are not using complex registers (cReg = 0) or if cReg is true, then
+                       // allocate temporary variables only for non power-of-2 radices
+                       if (!( (radix == 7 && cReg) || (radix == 11 && cReg) || (radix == 13 && cReg) ))
+                       {
+                       if( (radix & (radix-1)) || (!cReg) )
+                       {
+                               bflyStr += "\t";
+                               if(cReg)
+                                       bflyStr += RegBaseType<PR>(1);
+                               else
+                                       bflyStr += regType;
+
+                               for(size_t i=0;;i++)
+                               {
+                                       bflyStr += " TR"; bflyStr += SztToStr(i); bflyStr += ",";       // real arguments
+                                       bflyStr += " TI"; bflyStr += SztToStr(i);                                       // imaginary arguments
+
+                                       if(i == radix-1)
+                                       {
+                                               bflyStr += ";";
+                                               break;
+                                       }
+                                       else
+                                       {
+                                               bflyStr += ",";
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               bflyStr += "\t";
+                               bflyStr += RegBaseType<PR>(2);
+                               bflyStr += " T;";
+                               }
+                       }
+
+
+                       bflyStr += "\n\n\t";
+
+                       // Butterfly for different radices
+                       switch(radix)
+                       {
+                       case 2:
+                               {
+                                       if(cReg)
+                                       {
+                                               bflyStr +=
+                                               "(*R1) = (*R0) - (*R1);\n\t"
+                                               "(*R0) = 2.0f * (*R0) - (*R1);\n\t";
+                                       }
+                                       else
+                                       {
+                                               bflyStr +=
+                                               "TR0 = (*R0) + (*R1);\n\t"
+                                               "TI0 = (*I0) + (*I1);\n\t"
+                                               "TR1 = (*R0) - (*R1);\n\t"
+                                               "TI1 = (*I0) - (*I1);\n\t";
+                                       }
+
+                               } break;
+                       case 3:
+                               {
+                                       if(fwd)
+                                       {
+                                               if(cReg)
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = (*R0).x + (*R1).x + (*R2).x;\n\t"
+                                                       "TR1 = ((*R0).x - C3QA*((*R1).x + (*R2).x)) + C3QB*((*R1).y - (*R2).y);\n\t"
+                                                       "TR2 = ((*R0).x - C3QA*((*R1).x + (*R2).x)) - C3QB*((*R1).y - (*R2).y);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = (*R0).y + (*R1).y + (*R2).y;\n\t"
+                                                       "TI1 = ((*R0).y - C3QA*((*R1).y + (*R2).y)) - C3QB*((*R1).x - (*R2).x);\n\t"
+                                                       "TI2 = ((*R0).y - C3QA*((*R1).y + (*R2).y)) + C3QB*((*R1).x - (*R2).x);\n\t";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = *R0 + *R1 + *R2;\n\t"
+                                                       "TR1 = (*R0 - C3QA*(*R1 + *R2)) + C3QB*(*I1 - *I2);\n\t"
+                                                       "TR2 = (*R0 - C3QA*(*R1 + *R2)) - C3QB*(*I1 - *I2);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = *I0 + *I1 + *I2;\n\t"
+                                                       "TI1 = (*I0 - C3QA*(*I1 + *I2)) - C3QB*(*R1 - *R2);\n\t"
+                                                       "TI2 = (*I0 - C3QA*(*I1 + *I2)) + C3QB*(*R1 - *R2);\n\t";
+                                               }
+                                       }
+                                       else
+                                       {
+                                               if(cReg)
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = (*R0).x + (*R1).x + (*R2).x;\n\t"
+                                                       "TR1 = ((*R0).x - C3QA*((*R1).x + (*R2).x)) - C3QB*((*R1).y - (*R2).y);\n\t"
+                                                       "TR2 = ((*R0).x - C3QA*((*R1).x + (*R2).x)) + C3QB*((*R1).y - (*R2).y);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = (*R0).y + (*R1).y + (*R2).y;\n\t"
+                                                       "TI1 = ((*R0).y - C3QA*((*R1).y + (*R2).y)) + C3QB*((*R1).x - (*R2).x);\n\t"
+                                                       "TI2 = ((*R0).y - C3QA*((*R1).y + (*R2).y)) - C3QB*((*R1).x - (*R2).x);\n\t";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = *R0 + *R1 + *R2;\n\t"
+                                                       "TR1 = (*R0 - C3QA*(*R1 + *R2)) - C3QB*(*I1 - *I2);\n\t"
+                                                       "TR2 = (*R0 - C3QA*(*R1 + *R2)) + C3QB*(*I1 - *I2);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = *I0 + *I1 + *I2;\n\t"
+                                                       "TI1 = (*I0 - C3QA*(*I1 + *I2)) + C3QB*(*R1 - *R2);\n\t"
+                                                       "TI2 = (*I0 - C3QA*(*I1 + *I2)) - C3QB*(*R1 - *R2);\n\t";
+                                               }
+                                       }
+                               } break;
+                       case 4:
+                               {
+                                       if(fwd)
+                                       {
+                                               if(cReg)
+                                               {
+                                                       bflyStr +=
+                                                       "(*R1) = (*R0) - (*R1);\n\t"
+                                                       "(*R0) = 2.0f * (*R0) - (*R1);\n\t"
+                                                       "(*R3) = (*R2) - (*R3);\n\t"
+                                                       "(*R2) = 2.0f * (*R2) - (*R3);\n\t"
+                                                       "\n\t"
+                                                       "(*R2) = (*R0) - (*R2);\n\t"
+                                                       "(*R0) = 2.0f * (*R0) - (*R2);\n\t"
+                                                       "(*R3) = (*R1) + (fvect2)(-(*R3).y, (*R3).x);\n\t"
+                                                       "(*R1) = 2.0f * (*R1) - (*R3);\n\t";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = (*R0) + (*R2) + (*R1) + (*R3);\n\t"
+                                                       "TR1 = (*R0) - (*R2) + (*I1) - (*I3);\n\t"
+                                                       "TR2 = (*R0) + (*R2) - (*R1) - (*R3);\n\t"
+                                                       "TR3 = (*R0) - (*R2) - (*I1) + (*I3);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = (*I0) + (*I2) + (*I1) + (*I3);\n\t"
+                                                       "TI1 = (*I0) - (*I2) - (*R1) + (*R3);\n\t"
+                                                       "TI2 = (*I0) + (*I2) - (*I1) - (*I3);\n\t"
+                                                       "TI3 = (*I0) - (*I2) + (*R1) - (*R3);\n\t";
+                                               }
+                                       }
+                                       else
+                                       {
+                                               if(cReg)
+                                               {
+                                                       bflyStr +=
+                                                       "(*R1) = (*R0) - (*R1);\n\t"
+                                                       "(*R0) = 2.0f * (*R0) - (*R1);\n\t"
+                                                       "(*R3) = (*R2) - (*R3);\n\t"
+                                                       "(*R2) = 2.0f * (*R2) - (*R3);\n\t"
+                                                       "\n\t"
+                                                       "(*R2) = (*R0) - (*R2);\n\t"
+                                                       "(*R0) = 2.0f * (*R0) - (*R2);\n\t"
+                                                       "(*R3) = (*R1) + (fvect2)((*R3).y, -(*R3).x);\n\t"
+                                                       "(*R1) = 2.0f * (*R1) - (*R3);\n\t";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = (*R0) + (*R2) + (*R1) + (*R3);\n\t"
+                                                       "TR1 = (*R0) - (*R2) - (*I1) + (*I3);\n\t"
+                                                       "TR2 = (*R0) + (*R2) - (*R1) - (*R3);\n\t"
+                                                       "TR3 = (*R0) - (*R2) + (*I1) - (*I3);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = (*I0) + (*I2) + (*I1) + (*I3);\n\t"
+                                                       "TI1 = (*I0) - (*I2) + (*R1) - (*R3);\n\t"
+                                                       "TI2 = (*I0) + (*I2) - (*I1) - (*I3);\n\t"
+                                                       "TI3 = (*I0) - (*I2) - (*R1) + (*R3);\n\t";
+                                               }
+                                       }
+                               } break;
+                       case 5:
+                               {
+                                       if(fwd)
+                                       {
+                                               if(cReg)
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = (*R0).x + (*R1).x + (*R2).x + (*R3).x + (*R4).x;\n\t"
+                                                       "TR1 = ((*R0).x - C5QC*((*R2).x + (*R3).x)) + C5QB*((*R1).y - (*R4).y) + C5QD*((*R2).y - (*R3).y) + C5QA*(((*R1).x - (*R2).x) + ((*R4).x - (*R3).x));\n\t"
+                                                       "TR4 = ((*R0).x - C5QC*((*R2).x + (*R3).x)) - C5QB*((*R1).y - (*R4).y) - C5QD*((*R2).y - (*R3).y) + C5QA*(((*R1).x - (*R2).x) + ((*R4).x - (*R3).x));\n\t"
+                                                       "TR2 = ((*R0).x - C5QC*((*R1).x + (*R4).x)) - C5QB*((*R2).y - (*R3).y) + C5QD*((*R1).y - (*R4).y) + C5QA*(((*R2).x - (*R1).x) + ((*R3).x - (*R4).x));\n\t"
+                                                       "TR3 = ((*R0).x - C5QC*((*R1).x + (*R4).x)) + C5QB*((*R2).y - (*R3).y) - C5QD*((*R1).y - (*R4).y) + C5QA*(((*R2).x - (*R1).x) + ((*R3).x - (*R4).x));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = (*R0).y + (*R1).y + (*R2).y + (*R3).y + (*R4).y;\n\t"
+                                                       "TI1 = ((*R0).y - C5QC*((*R2).y + (*R3).y)) - C5QB*((*R1).x - (*R4).x) - C5QD*((*R2).x - (*R3).x) + C5QA*(((*R1).y - (*R2).y) + ((*R4).y - (*R3).y));\n\t"
+                                                       "TI4 = ((*R0).y - C5QC*((*R2).y + (*R3).y)) + C5QB*((*R1).x - (*R4).x) + C5QD*((*R2).x - (*R3).x) + C5QA*(((*R1).y - (*R2).y) + ((*R4).y - (*R3).y));\n\t"
+                                                       "TI2 = ((*R0).y - C5QC*((*R1).y + (*R4).y)) + C5QB*((*R2).x - (*R3).x) - C5QD*((*R1).x - (*R4).x) + C5QA*(((*R2).y - (*R1).y) + ((*R3).y - (*R4).y));\n\t"
+                                                       "TI3 = ((*R0).y - C5QC*((*R1).y + (*R4).y)) - C5QB*((*R2).x - (*R3).x) + C5QD*((*R1).x - (*R4).x) + C5QA*(((*R2).y - (*R1).y) + ((*R3).y - (*R4).y));\n\t";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = *R0 + *R1 + *R2 + *R3 + *R4;\n\t"
+                                                       "TR1 = (*R0 - C5QC*(*R2 + *R3)) + C5QB*(*I1 - *I4) + C5QD*(*I2 - *I3) + C5QA*((*R1 - *R2) + (*R4 - *R3));\n\t"
+                                                       "TR4 = (*R0 - C5QC*(*R2 + *R3)) - C5QB*(*I1 - *I4) - C5QD*(*I2 - *I3) + C5QA*((*R1 - *R2) + (*R4 - *R3));\n\t"
+                                                       "TR2 = (*R0 - C5QC*(*R1 + *R4)) - C5QB*(*I2 - *I3) + C5QD*(*I1 - *I4) + C5QA*((*R2 - *R1) + (*R3 - *R4));\n\t"
+                                                       "TR3 = (*R0 - C5QC*(*R1 + *R4)) + C5QB*(*I2 - *I3) - C5QD*(*I1 - *I4) + C5QA*((*R2 - *R1) + (*R3 - *R4));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = *I0 + *I1 + *I2 + *I3 + *I4;\n\t"
+                                                       "TI1 = (*I0 - C5QC*(*I2 + *I3)) - C5QB*(*R1 - *R4) - C5QD*(*R2 - *R3) + C5QA*((*I1 - *I2) + (*I4 - *I3));\n\t"
+                                                       "TI4 = (*I0 - C5QC*(*I2 + *I3)) + C5QB*(*R1 - *R4) + C5QD*(*R2 - *R3) + C5QA*((*I1 - *I2) + (*I4 - *I3));\n\t"
+                                                       "TI2 = (*I0 - C5QC*(*I1 + *I4)) + C5QB*(*R2 - *R3) - C5QD*(*R1 - *R4) + C5QA*((*I2 - *I1) + (*I3 - *I4));\n\t"
+                                                       "TI3 = (*I0 - C5QC*(*I1 + *I4)) - C5QB*(*R2 - *R3) + C5QD*(*R1 - *R4) + C5QA*((*I2 - *I1) + (*I3 - *I4));\n\t";
+                                               }
+                                       }
+                                       else
+                                       {
+                                               if(cReg)
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = (*R0).x + (*R1).x + (*R2).x + (*R3).x + (*R4).x;\n\t"
+                                                       "TR1 = ((*R0).x - C5QC*((*R2).x + (*R3).x)) - C5QB*((*R1).y - (*R4).y) - C5QD*((*R2).y - (*R3).y) + C5QA*(((*R1).x - (*R2).x) + ((*R4).x - (*R3).x));\n\t"
+                                                       "TR4 = ((*R0).x - C5QC*((*R2).x + (*R3).x)) + C5QB*((*R1).y - (*R4).y) + C5QD*((*R2).y - (*R3).y) + C5QA*(((*R1).x - (*R2).x) + ((*R4).x - (*R3).x));\n\t"
+                                                       "TR2 = ((*R0).x - C5QC*((*R1).x + (*R4).x)) + C5QB*((*R2).y - (*R3).y) - C5QD*((*R1).y - (*R4).y) + C5QA*(((*R2).x - (*R1).x) + ((*R3).x - (*R4).x));\n\t"
+                                                       "TR3 = ((*R0).x - C5QC*((*R1).x + (*R4).x)) - C5QB*((*R2).y - (*R3).y) + C5QD*((*R1).y - (*R4).y) + C5QA*(((*R2).x - (*R1).x) + ((*R3).x - (*R4).x));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = (*R0).y + (*R1).y + (*R2).y + (*R3).y + (*R4).y;\n\t"
+                                                       "TI1 = ((*R0).y - C5QC*((*R2).y + (*R3).y)) + C5QB*((*R1).x - (*R4).x) + C5QD*((*R2).x - (*R3).x) + C5QA*(((*R1).y - (*R2).y) + ((*R4).y - (*R3).y));\n\t"
+                                                       "TI4 = ((*R0).y - C5QC*((*R2).y + (*R3).y)) - C5QB*((*R1).x - (*R4).x) - C5QD*((*R2).x - (*R3).x) + C5QA*(((*R1).y - (*R2).y) + ((*R4).y - (*R3).y));\n\t"
+                                                       "TI2 = ((*R0).y - C5QC*((*R1).y + (*R4).y)) - C5QB*((*R2).x - (*R3).x) + C5QD*((*R1).x - (*R4).x) + C5QA*(((*R2).y - (*R1).y) + ((*R3).y - (*R4).y));\n\t"
+                                                       "TI3 = ((*R0).y - C5QC*((*R1).y + (*R4).y)) + C5QB*((*R2).x - (*R3).x) - C5QD*((*R1).x - (*R4).x) + C5QA*(((*R2).y - (*R1).y) + ((*R3).y - (*R4).y));\n\t";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = *R0 + *R1 + *R2 + *R3 + *R4;\n\t"
+                                                       "TR1 = (*R0 - C5QC*(*R2 + *R3)) - C5QB*(*I1 - *I4) - C5QD*(*I2 - *I3) + C5QA*((*R1 - *R2) + (*R4 - *R3));\n\t"
+                                                       "TR4 = (*R0 - C5QC*(*R2 + *R3)) + C5QB*(*I1 - *I4) + C5QD*(*I2 - *I3) + C5QA*((*R1 - *R2) + (*R4 - *R3));\n\t"
+                                                       "TR2 = (*R0 - C5QC*(*R1 + *R4)) + C5QB*(*I2 - *I3) - C5QD*(*I1 - *I4) + C5QA*((*R2 - *R1) + (*R3 - *R4));\n\t"
+                                                       "TR3 = (*R0 - C5QC*(*R1 + *R4)) - C5QB*(*I2 - *I3) + C5QD*(*I1 - *I4) + C5QA*((*R2 - *R1) + (*R3 - *R4));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = *I0 + *I1 + *I2 + *I3 + *I4;\n\t"
+                                                       "TI1 = (*I0 - C5QC*(*I2 + *I3)) + C5QB*(*R1 - *R4) + C5QD*(*R2 - *R3) + C5QA*((*I1 - *I2) + (*I4 - *I3));\n\t"
+                                                       "TI4 = (*I0 - C5QC*(*I2 + *I3)) - C5QB*(*R1 - *R4) - C5QD*(*R2 - *R3) + C5QA*((*I1 - *I2) + (*I4 - *I3));\n\t"
+                                                       "TI2 = (*I0 - C5QC*(*I1 + *I4)) - C5QB*(*R2 - *R3) + C5QD*(*R1 - *R4) + C5QA*((*I2 - *I1) + (*I3 - *I4));\n\t"
+                                                       "TI3 = (*I0 - C5QC*(*I1 + *I4)) + C5QB*(*R2 - *R3) - C5QD*(*R1 - *R4) + C5QA*((*I2 - *I1) + (*I3 - *I4));\n\t";
+                                               }
+                                       }
+                               } break;
+                       case 6:
+                               {
+                                       if(fwd)
+                                       {
+                                               if(cReg)
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = (*R0).x + (*R2).x + (*R4).x;\n\t"
+                                                       "TR2 = ((*R0).x - C3QA*((*R2).x + (*R4).x)) + C3QB*((*R2).y - (*R4).y);\n\t"
+                                                       "TR4 = ((*R0).x - C3QA*((*R2).x + (*R4).x)) - C3QB*((*R2).y - (*R4).y);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = (*R0).y + (*R2).y + (*R4).y;\n\t"
+                                                       "TI2 = ((*R0).y - C3QA*((*R2).y + (*R4).y)) - C3QB*((*R2).x - (*R4).x);\n\t"
+                                                       "TI4 = ((*R0).y - C3QA*((*R2).y + (*R4).y)) + C3QB*((*R2).x - (*R4).x);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TR1 = (*R1).x + (*R3).x + (*R5).x;\n\t"
+                                                       "TR3 = ((*R1).x - C3QA*((*R3).x + (*R5).x)) + C3QB*((*R3).y - (*R5).y);\n\t"
+                                                       "TR5 = ((*R1).x - C3QA*((*R3).x + (*R5).x)) - C3QB*((*R3).y - (*R5).y);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI1 = (*R1).y + (*R3).y + (*R5).y;\n\t"
+                                                       "TI3 = ((*R1).y - C3QA*((*R3).y + (*R5).y)) - C3QB*((*R3).x - (*R5).x);\n\t"
+                                                       "TI5 = ((*R1).y - C3QA*((*R3).y + (*R5).y)) + C3QB*((*R3).x - (*R5).x);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R0).x = TR0 + TR1;\n\t"
+                                                       "(*R1).x = TR2 + ( C3QA*TR3 + C3QB*TI3);\n\t"
+                                                       "(*R2).x = TR4 + (-C3QA*TR5 + C3QB*TI5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R0).y = TI0 + TI1;\n\t"
+                                                       "(*R1).y = TI2 + (-C3QB*TR3 + C3QA*TI3);\n\t"
+                                                       "(*R2).y = TI4 + (-C3QB*TR5 - C3QA*TI5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R3).x = TR0 - TR1;\n\t"
+                                                       "(*R4).x = TR2 - ( C3QA*TR3 + C3QB*TI3);\n\t"
+                                                       "(*R5).x = TR4 - (-C3QA*TR5 + C3QB*TI5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R3).y = TI0 - TI1;\n\t"
+                                                       "(*R4).y = TI2 - (-C3QB*TR3 + C3QA*TI3);\n\t"
+                                                       "(*R5).y = TI4 - (-C3QB*TR5 - C3QA*TI5);\n\t";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = *R0 + *R2 + *R4;\n\t"
+                                                       "TR2 = (*R0 - C3QA*(*R2 + *R4)) + C3QB*(*I2 - *I4);\n\t"
+                                                       "TR4 = (*R0 - C3QA*(*R2 + *R4)) - C3QB*(*I2 - *I4);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = *I0 + *I2 + *I4;\n\t"
+                                                       "TI2 = (*I0 - C3QA*(*I2 + *I4)) - C3QB*(*R2 - *R4);\n\t"
+                                                       "TI4 = (*I0 - C3QA*(*I2 + *I4)) + C3QB*(*R2 - *R4);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TR1 = *R1 + *R3 + *R5;\n\t"
+                                                       "TR3 = (*R1 - C3QA*(*R3 + *R5)) + C3QB*(*I3 - *I5);\n\t"
+                                                       "TR5 = (*R1 - C3QA*(*R3 + *R5)) - C3QB*(*I3 - *I5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI1 = *I1 + *I3 + *I5;\n\t"
+                                                       "TI3 = (*I1 - C3QA*(*I3 + *I5)) - C3QB*(*R3 - *R5);\n\t"
+                                                       "TI5 = (*I1 - C3QA*(*I3 + *I5)) + C3QB*(*R3 - *R5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R0) = TR0 + TR1;\n\t"
+                                                       "(*R1) = TR2 + ( C3QA*TR3 + C3QB*TI3);\n\t"
+                                                       "(*R2) = TR4 + (-C3QA*TR5 + C3QB*TI5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*I0) = TI0 + TI1;\n\t"
+                                                       "(*I1) = TI2 + (-C3QB*TR3 + C3QA*TI3);\n\t"
+                                                       "(*I2) = TI4 + (-C3QB*TR5 - C3QA*TI5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R3) = TR0 - TR1;\n\t"
+                                                       "(*R4) = TR2 - ( C3QA*TR3 + C3QB*TI3);\n\t"
+                                                       "(*R5) = TR4 - (-C3QA*TR5 + C3QB*TI5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*I3) = TI0 - TI1;\n\t"
+                                                       "(*I4) = TI2 - (-C3QB*TR3 + C3QA*TI3);\n\t"
+                                                       "(*I5) = TI4 - (-C3QB*TR5 - C3QA*TI5);\n\t";
+                                               }
+                                       }
+                                       else
+                                       {
+                                               if(cReg)
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = (*R0).x + (*R2).x + (*R4).x;\n\t"
+                                                       "TR2 = ((*R0).x - C3QA*((*R2).x + (*R4).x)) - C3QB*((*R2).y - (*R4).y);\n\t"
+                                                       "TR4 = ((*R0).x - C3QA*((*R2).x + (*R4).x)) + C3QB*((*R2).y - (*R4).y);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = (*R0).y + (*R2).y + (*R4).y;\n\t"
+                                                       "TI2 = ((*R0).y - C3QA*((*R2).y + (*R4).y)) + C3QB*((*R2).x - (*R4).x);\n\t"
+                                                       "TI4 = ((*R0).y - C3QA*((*R2).y + (*R4).y)) - C3QB*((*R2).x - (*R4).x);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TR1 = (*R1).x + (*R3).x + (*R5).x;\n\t"
+                                                       "TR3 = ((*R1).x - C3QA*((*R3).x + (*R5).x)) - C3QB*((*R3).y - (*R5).y);\n\t"
+                                                       "TR5 = ((*R1).x - C3QA*((*R3).x + (*R5).x)) + C3QB*((*R3).y - (*R5).y);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI1 = (*R1).y + (*R3).y + (*R5).y;\n\t"
+                                                       "TI3 = ((*R1).y - C3QA*((*R3).y + (*R5).y)) + C3QB*((*R3).x - (*R5).x);\n\t"
+                                                       "TI5 = ((*R1).y - C3QA*((*R3).y + (*R5).y)) - C3QB*((*R3).x - (*R5).x);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R0).x = TR0 + TR1;\n\t"
+                                                       "(*R1).x = TR2 + ( C3QA*TR3 - C3QB*TI3);\n\t"
+                                                       "(*R2).x = TR4 + (-C3QA*TR5 - C3QB*TI5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R0).y = TI0 + TI1;\n\t"
+                                                       "(*R1).y = TI2 + ( C3QB*TR3 + C3QA*TI3);\n\t"
+                                                       "(*R2).y = TI4 + ( C3QB*TR5 - C3QA*TI5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R3).x = TR0 - TR1;\n\t"
+                                                       "(*R4).x = TR2 - ( C3QA*TR3 - C3QB*TI3);\n\t"
+                                                       "(*R5).x = TR4 - (-C3QA*TR5 - C3QB*TI5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R3).y = TI0 - TI1;\n\t"
+                                                       "(*R4).y = TI2 - ( C3QB*TR3 + C3QA*TI3);\n\t"
+                                                       "(*R5).y = TI4 - ( C3QB*TR5 - C3QA*TI5);\n\t";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = *R0 + *R2 + *R4;\n\t"
+                                                       "TR2 = (*R0 - C3QA*(*R2 + *R4)) - C3QB*(*I2 - *I4);\n\t"
+                                                       "TR4 = (*R0 - C3QA*(*R2 + *R4)) + C3QB*(*I2 - *I4);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = *I0 + *I2 + *I4;\n\t"
+                                                       "TI2 = (*I0 - C3QA*(*I2 + *I4)) + C3QB*(*R2 - *R4);\n\t"
+                                                       "TI4 = (*I0 - C3QA*(*I2 + *I4)) - C3QB*(*R2 - *R4);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TR1 = *R1 + *R3 + *R5;\n\t"
+                                                       "TR3 = (*R1 - C3QA*(*R3 + *R5)) - C3QB*(*I3 - *I5);\n\t"
+                                                       "TR5 = (*R1 - C3QA*(*R3 + *R5)) + C3QB*(*I3 - *I5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI1 = *I1 + *I3 + *I5;\n\t"
+                                                       "TI3 = (*I1 - C3QA*(*I3 + *I5)) + C3QB*(*R3 - *R5);\n\t"
+                                                       "TI5 = (*I1 - C3QA*(*I3 + *I5)) - C3QB*(*R3 - *R5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R0) = TR0 + TR1;\n\t"
+                                                       "(*R1) = TR2 + ( C3QA*TR3 - C3QB*TI3);\n\t"
+                                                       "(*R2) = TR4 + (-C3QA*TR5 - C3QB*TI5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*I0) = TI0 + TI1;\n\t"
+                                                       "(*I1) = TI2 + ( C3QB*TR3 + C3QA*TI3);\n\t"
+                                                       "(*I2) = TI4 + ( C3QB*TR5 - C3QA*TI5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R3) = TR0 - TR1;\n\t"
+                                                       "(*R4) = TR2 - ( C3QA*TR3 - C3QB*TI3);\n\t"
+                                                       "(*R5) = TR4 - (-C3QA*TR5 - C3QB*TI5);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*I3) = TI0 - TI1;\n\t"
+                                                       "(*I4) = TI2 - ( C3QB*TR3 + C3QA*TI3);\n\t"
+                                                       "(*I5) = TI4 - ( C3QB*TR5 - C3QA*TI5);\n\t";
+                                               }
+                                       }
+                               } break;
+                       case 7:
+                               {
+                                       static const char *C7SFR = "\
+                                       /*FFT7 Forward Real */ \n\
+                                       \n\
+                                               pr0 = *R1 + *R6; \n\
+                                               pi0 = *I1 + *I6; \n\
+                                               pr1 = *R1 - *R6; \n\
+                                               pi1 = *I1 - *I6; \n\
+                                               pr2 = *R2 + *R5; \n\
+                                               pi2 = *I2 + *I5; \n\
+                                               pr3 = *R2 - *R5; \n\
+                                               pi3 = *I2 - *I5; \n\
+                                               pr4 = *R4 + *R3; \n\
+                                               pi4 = *I4 + *I3; \n\
+                                               pr5 = *R4 - *R3; \n\
+                                               pi5 = *I4 - *I3; \n\
+                                       \n\
+                                               pr6 = pr2 + pr0; \n\
+                                               pi6 = pi2 + pi0; \n\
+                                               qr4 = pr2 - pr0; \n\
+                                               qi4 = pi2 - pi0; \n\
+                                               qr2 = pr0 - pr4; \n\
+                                               qi2 = pi0 - pi4; \n\
+                                               qr3 = pr4 - pr2; \n\
+                                               qi3 = pi4 - pi2; \n\
+                                               pr7 = pr5 + pr3; \n\
+                                               pi7 = pi5 + pi3; \n\
+                                               qr7 = pr5 - pr3; \n\
+                                               qi7 = pi5 - pi3; \n\
+                                               qr6 = pr1 - pr5; \n\
+                                               qi6 = pi1 - pi5; \n\
+                                               qr8 = pr3 - pr1; \n\
+                                               qi8 = pi3 - pi1; \n\
+                                               qr1 = pr6 + pr4; \n\
+                                               qi1 = pi6 + pi4; \n\
+                                               qr5 = pr7 + pr1; \n\
+                                               qi5 = pi7 + pi1; \n\
+                                               qr0 = *R0 + qr1; \n\
+                                               qi0 = *I0 + qi1; \n\
+                                       \n\
+                                               qr1 *= C7Q1; \n\
+                                               qi1 *= C7Q1; \n\
+                                               qr2 *= C7Q2; \n\
+                                               qi2 *= C7Q2; \n\
+                                               qr3 *= C7Q3; \n\
+                                               qi3 *= C7Q3; \n\
+                                               qr4 *= C7Q4; \n\
+                                               qi4 *= C7Q4; \n\
+                                       \n\
+                                               qr5 *= (C7Q5); \n\
+                                               qi5 *= (C7Q5); \n\
+                                               qr6 *= (C7Q6); \n\
+                                               qi6 *= (C7Q6); \n\
+                                               qr7 *= (C7Q7); \n\
+                                               qi7 *= (C7Q7); \n\
+                                               qr8 *= (C7Q8); \n\
+                                               qi8 *= (C7Q8); \n\
+                                       \n\
+                                               pr0 =  qr0 + qr1; \n\
+                                               pi0 =  qi0 + qi1; \n\
+                                               pr1 =  qr2 + qr3; \n\
+                                               pi1 =  qi2 + qi3; \n\
+                                               pr2 =  qr4 - qr3; \n\
+                                               pi2 =  qi4 - qi3; \n\
+                                               pr3 = -qr2 - qr4; \n\
+                                               pi3 = -qi2 - qi4; \n\
+                                               pr4 =  qr6 + qr7; \n\
+                                               pi4 =  qi6 + qi7; \n\
+                                               pr5 =  qr8 - qr7; \n\
+                                               pi5 =  qi8 - qi7; \n\
+                                               pr6 = -qr8 - qr6; \n\
+                                               pi6 = -qi8 - qi6; \n\
+                                               pr7 =  pr0 + pr1; \n\
+                                               pi7 =  pi0 + pi1; \n\
+                                               pr8 =  pr0 + pr2; \n\
+                                               pi8 =  pi0 + pi2; \n\
+                                               pr9 =  pr0 + pr3; \n\
+                                               pi9 =  pi0 + pi3; \n\
+                                               qr6 =  pr4 + qr5; \n\
+                                               qi6 =  pi4 + qi5; \n\
+                                               qr7 =  pr5 + qr5; \n\
+                                               qi7 =  pi5 + qi5; \n\
+                                               qr8 =  pr6 + qr5; \n\
+                                               qi8 =  pi6 + qi5; \n\
+                                       \n\
+                                               TR0 = qr0; TI0 = qi0; \n\
+                                               TR1 = pr7 + qi6; \n\
+                                               TI1 = pi7 - qr6; \n\
+                                               TR2 = pr9 + qi8; \n\
+                                               TI2 = pi9 - qr8; \n\
+                                               TR3 = pr8 - qi7; \n\
+                                               TI3 = pi8 + qr7; \n\
+                                               TR4 = pr8 + qi7; \n\
+                                               TI4 = pi8 - qr7; \n\
+                                               TR5 = pr9 - qi8; \n\
+                                               TI5 = pi9 + qr8; \n\
+                                               TR6 = pr7 - qi6; \n\
+                                               TI6 = pi7 + qr6; \n\
+                                       ";
+
+                                       static const char *C7SBR = "\
+                                       /*FFT7 Backward Real */ \n\
+                                       \n\
+                                               pr0 = *R1 + *R6; \n\
+                                               pi0 = *I1 + *I6; \n\
+                                               pr1 = *R1 - *R6; \n\
+                                               pi1 = *I1 - *I6; \n\
+                                               pr2 = *R2 + *R5; \n\
+                                               pi2 = *I2 + *I5; \n\
+                                               pr3 = *R2 - *R5; \n\
+                                               pi3 = *I2 - *I5; \n\
+                                               pr4 = *R4 + *R3; \n\
+                                               pi4 = *I4 + *I3; \n\
+                                               pr5 = *R4 - *R3; \n\
+                                               pi5 = *I4 - *I3; \n\
+                                       \n\
+                                               pr6 = pr2 + pr0; \n\
+                                               pi6 = pi2 + pi0; \n\
+                                               qr4 = pr2 - pr0; \n\
+                                               qi4 = pi2 - pi0; \n\
+                                               qr2 = pr0 - pr4; \n\
+                                               qi2 = pi0 - pi4; \n\
+                                               qr3 = pr4 - pr2; \n\
+                                               qi3 = pi4 - pi2; \n\
+                                               pr7 = pr5 + pr3; \n\
+                                               pi7 = pi5 + pi3; \n\
+                                               qr7 = pr5 - pr3; \n\
+                                               qi7 = pi5 - pi3; \n\
+                                               qr6 = pr1 - pr5; \n\
+                                               qi6 = pi1 - pi5; \n\
+                                               qr8 = pr3 - pr1; \n\
+                                               qi8 = pi3 - pi1; \n\
+                                               qr1 = pr6 + pr4; \n\
+                                               qi1 = pi6 + pi4; \n\
+                                               qr5 = pr7 + pr1; \n\
+                                               qi5 = pi7 + pi1; \n\
+                                               qr0 = *R0 + qr1; \n\
+                                               qi0 = *I0 + qi1; \n\
+                                       \n\
+                                               qr1 *= C7Q1; \n\
+                                               qi1 *= C7Q1; \n\
+                                               qr2 *= C7Q2; \n\
+                                               qi2 *= C7Q2; \n\
+                                               qr3 *= C7Q3; \n\
+                                               qi3 *= C7Q3; \n\
+                                               qr4 *= C7Q4; \n\
+                                               qi4 *= C7Q4; \n\
+                                       \n\
+                                               qr5 *= -(C7Q5); \n\
+                                               qi5 *= -(C7Q5); \n\
+                                               qr6 *= -(C7Q6); \n\
+                                               qi6 *= -(C7Q6); \n\
+                                               qr7 *= -(C7Q7); \n\
+                                               qi7 *= -(C7Q7); \n\
+                                               qr8 *= -(C7Q8); \n\
+                                               qi8 *= -(C7Q8); \n\
+                                       \n\
+                                               pr0 =  qr0 + qr1; \n\
+                                               pi0 =  qi0 + qi1; \n\
+                                               pr1 =  qr2 + qr3; \n\
+                                               pi1 =  qi2 + qi3; \n\
+                                               pr2 =  qr4 - qr3; \n\
+                                               pi2 =  qi4 - qi3; \n\
+                                               pr3 = -qr2 - qr4; \n\
+                                               pi3 = -qi2 - qi4; \n\
+                                               pr4 =  qr6 + qr7; \n\
+                                               pi4 =  qi6 + qi7; \n\
+                                               pr5 =  qr8 - qr7; \n\
+                                               pi5 =  qi8 - qi7; \n\
+                                               pr6 = -qr8 - qr6; \n\
+                                               pi6 = -qi8 - qi6; \n\
+                                               pr7 =  pr0 + pr1; \n\
+                                               pi7 =  pi0 + pi1; \n\
+                                               pr8 =  pr0 + pr2; \n\
+                                               pi8 =  pi0 + pi2; \n\
+                                               pr9 =  pr0 + pr3; \n\
+                                               pi9 =  pi0 + pi3; \n\
+                                               qr6 =  pr4 + qr5; \n\
+                                               qi6 =  pi4 + qi5; \n\
+                                               qr7 =  pr5 + qr5; \n\
+                                               qi7 =  pi5 + qi5; \n\
+                                               qr8 =  pr6 + qr5; \n\
+                                               qi8 =  pi6 + qi5; \n\
+                                       \n\
+                                               TR0 = qr0; TI0 = qi0; \n\
+                                               TR1 = pr7 + qi6; \n\
+                                               TI1 = pi7 - qr6; \n\
+                                               TR2 = pr9 + qi8; \n\
+                                               TI2 = pi9 - qr8; \n\
+                                               TR3 = pr8 - qi7; \n\
+                                               TI3 = pi8 + qr7; \n\
+                                               TR4 = pr8 + qi7; \n\
+                                               TI4 = pi8 - qr7; \n\
+                                               TR5 = pr9 - qi8; \n\
+                                               TI5 = pi9 + qr8; \n\
+                                               TR6 = pr7 - qi6; \n\
+                                               TI6 = pi7 + qr6; \n\
+                                       ";
+
+                                       static const char *C7SFC = "\
+                                       /*FFT7 Forward Complex */ \n\
+                                       \n\
+                                               p0 = *R1 + *R6; \n\
+                                               p1 = *R1 - *R6; \n\
+                                               p2 = *R2 + *R5; \n\
+                                               p3 = *R2 - *R5; \n\
+                                               p4 = *R4 + *R3; \n\
+                                               p5 = *R4 - *R3; \n\
+                                       \n\
+                                               p6 = p2 + p0; \n\
+                                               q4 = p2 - p0; \n\
+                                               q2 = p0 - p4; \n\
+                                               q3 = p4 - p2; \n\
+                                               p7 = p5 + p3; \n\
+                                               q7 = p5 - p3; \n\
+                                               q6 = p1 - p5; \n\
+                                               q8 = p3 - p1; \n\
+                                               q1 = p6 + p4; \n\
+                                               q5 = p7 + p1; \n\
+                                               q0 = *R0 + q1; \n\
+                                       \n\
+                                               q1 *= C7Q1; \n\
+                                               q2 *= C7Q2; \n\
+                                               q3 *= C7Q3; \n\
+                                               q4 *= C7Q4; \n\
+                                       \n\
+                                               q5 *= (C7Q5); \n\
+                                               q6 *= (C7Q6); \n\
+                                               q7 *= (C7Q7); \n\
+                                               q8 *= (C7Q8); \n\
+                                       \n\
+                                               p0 = q0 + q1; \n\
+                                               p1 = q2 + q3; \n\
+                                               p2 = q4 - q3; \n\
+                                               p3 = -q2 - q4; \n\
+                                               p4 = q6 + q7; \n\
+                                               p5 = q8 - q7; \n\
+                                               p6 = -q8 - q6; \n\
+                                               p7 = p0 + p1; \n\
+                                               p8 = p0 + p2; \n\
+                                               p9 = p0 + p3; \n\
+                                               q6 = p4 + q5; \n\
+                                               q7 = p5 + q5; \n\
+                                               q8 = p6 + q5; \n\
+                                       \n\
+                                               *R0 = q0; \n\
+                                               (*R1).x = p7.x + q6.y; \n\
+                                               (*R1).y = p7.y - q6.x; \n\
+                                               (*R2).x = p9.x + q8.y; \n\
+                                               (*R2).y = p9.y - q8.x; \n\
+                                               (*R3).x = p8.x - q7.y; \n\
+                                               (*R3).y = p8.y + q7.x; \n\
+                                               (*R4).x = p8.x + q7.y; \n\
+                                               (*R4).y = p8.y - q7.x; \n\
+                                               (*R5).x = p9.x - q8.y; \n\
+                                               (*R5).y = p9.y + q8.x; \n\
+                                               (*R6).x = p7.x - q6.y; \n\
+                                               (*R6).y = p7.y + q6.x; \n\
+                                       ";
+
+                                       static const char *C7SBC = "\
+                                       /*FFT7 Backward Complex */ \n\
+                                       \n\
+                                               p0 = *R1 + *R6; \n\
+                                               p1 = *R1 - *R6; \n\
+                                               p2 = *R2 + *R5; \n\
+                                               p3 = *R2 - *R5; \n\
+                                               p4 = *R4 + *R3; \n\
+                                               p5 = *R4 - *R3; \n\
+                                       \n\
+                                               p6 = p2 + p0; \n\
+                                               q4 = p2 - p0; \n\
+                                               q2 = p0 - p4; \n\
+                                               q3 = p4 - p2; \n\
+                                               p7 = p5 + p3; \n\
+                                               q7 = p5 - p3; \n\
+                                               q6 = p1 - p5; \n\
+                                               q8 = p3 - p1; \n\
+                                               q1 = p6 + p4; \n\
+                                               q5 = p7 + p1; \n\
+                                               q0 = *R0 + q1; \n\
+                                       \n\
+                                               q1 *= C7Q1; \n\
+                                               q2 *= C7Q2; \n\
+                                               q3 *= C7Q3; \n\
+                                               q4 *= C7Q4; \n\
+                                       \n\
+                                               q5 *= -(C7Q5); \n\
+                                               q6 *= -(C7Q6); \n\
+                                               q7 *= -(C7Q7); \n\
+                                               q8 *= -(C7Q8); \n\
+                                       \n\
+                                               p0 = q0 + q1; \n\
+                                               p1 = q2 + q3; \n\
+                                               p2 = q4 - q3; \n\
+                                               p3 = -q2 - q4; \n\
+                                               p4 = q6 + q7; \n\
+                                               p5 = q8 - q7; \n\
+                                               p6 = -q8 - q6; \n\
+                                               p7 = p0 + p1; \n\
+                                               p8 = p0 + p2; \n\
+                                               p9 = p0 + p3; \n\
+                                               q6 = p4 + q5; \n\
+                                               q7 = p5 + q5; \n\
+                                               q8 = p6 + q5; \n\
+                                       \n\
+                                               *R0 = q0; \n\
+                                               (*R1).x = p7.x + q6.y; \n\
+                                               (*R1).y = p7.y - q6.x; \n\
+                                               (*R2).x = p9.x + q8.y; \n\
+                                               (*R2).y = p9.y - q8.x; \n\
+                                               (*R3).x = p8.x - q7.y; \n\
+                                               (*R3).y = p8.y + q7.x; \n\
+                                               (*R4).x = p8.x + q7.y; \n\
+                                               (*R4).y = p8.y - q7.x; \n\
+                                               (*R5).x = p9.x - q8.y; \n\
+                                               (*R5).y = p9.y + q8.x; \n\
+                                               (*R6).x = p7.x - q6.y; \n\
+                                               (*R6).y = p7.y + q6.x; \n\
+                                       ";
+
+
+
+                                       if (!cReg) {
+                                               for (size_t i = 0; i < 10; i++)
+                                                       bflyStr += regType + " pr" + SztToStr(i) + ", pi" + SztToStr(i) + ";\n\t";
+                                               for (size_t i = 0; i < 9; i++)
+                                                       bflyStr += regType + " qr" + SztToStr(i) + ", qi" + SztToStr(i) + ";\n\t";
+
+                                               if (fwd)
+                                                       bflyStr += C7SFR;
+                                               else
+                                                       bflyStr += C7SBR;
+                                       } else {
+                                               for (size_t i = 0; i < 10; i++)
+                                                       bflyStr += regType + " p" + SztToStr(i) + ";\n\t";
+                                               for (size_t i = 0; i < 9; i++)
+                                                       bflyStr += regType + " q" + SztToStr(i) + ";\n\t";
+                                               if (fwd)
+                                                       bflyStr += C7SFC;
+                                               else
+                                                       bflyStr += C7SBC;
+                                       }
+                               }
+                               break;
+
+                       case 8:
+                               {
+                                       if(fwd)
+                                       {
+                                               if(cReg)
+                                               {
+                                                       bflyStr +=
+                                                       "(*R1) = (*R0) - (*R1);\n\t"
+                                                       "(*R0) = 2.0f * (*R0) - (*R1);\n\t"
+                                                       "(*R3) = (*R2) - (*R3);\n\t"
+                                                       "(*R2) = 2.0f * (*R2) - (*R3);\n\t"
+                                                       "(*R5) = (*R4) - (*R5);\n\t"
+                                                       "(*R4) = 2.0f * (*R4) - (*R5);\n\t"
+                                                       "(*R7) = (*R6) - (*R7);\n\t"
+                                                       "(*R6) = 2.0f * (*R6) - (*R7);\n\t"
+                                                       "\n\t"
+                                                       "(*R2) = (*R0) - (*R2);\n\t"
+                                                       "(*R0) = 2.0f * (*R0) - (*R2);\n\t"
+                                                       "(*R3) = (*R1) + (fvect2)(-(*R3).y, (*R3).x);\n\t"
+                                                       "(*R1) = 2.0f * (*R1) - (*R3);\n\t"
+                                                       "(*R6) = (*R4) - (*R6);\n\t"
+                                                       "(*R4) = 2.0f * (*R4) - (*R6);\n\t"
+                                                       "(*R7) = (*R5) + (fvect2)(-(*R7).y, (*R7).x);\n\t"
+                                                       "(*R5) = 2.0f * (*R5) - (*R7);\n\t"
+                                                       "\n\t"
+                                                       "(*R4) = (*R0) - (*R4);\n\t"
+                                                       "(*R0) = 2.0f * (*R0) - (*R4);\n\t"
+                                                       "(*R5) = ((*R1) - C8Q * (*R5)) - C8Q * (fvect2)((*R5).y, -(*R5).x);\n\t"
+                                                       "(*R1) = 2.0f * (*R1) - (*R5);\n\t"
+                                                       "(*R6) = (*R2) + (fvect2)(-(*R6).y, (*R6).x);\n\t"
+                                                       "(*R2) = 2.0f * (*R2) - (*R6);\n\t"
+                                                       "(*R7) = ((*R3) + C8Q * (*R7)) - C8Q * (fvect2)((*R7).y, -(*R7).x);\n\t"
+                                                       "(*R3) = 2.0f * (*R3) - (*R7);\n\t";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = (*R0) + (*R4) + (*R2) + (*R6) +     (*R1)             +     (*R3)             +     (*R5)             +     (*R7)            ;\n\t"
+                                                       "TR1 = (*R0) - (*R4) + (*I2) - (*I6) + C8Q*(*R1) + C8Q*(*I1) - C8Q*(*R3) + C8Q*(*I3) - C8Q*(*R5) - C8Q*(*I5) + C8Q*(*R7) - C8Q*(*I7);\n\t"
+                                                       "TR2 = (*R0) + (*R4) - (*R2) - (*R6)             +     (*I1)             -     (*I3)             +     (*I5)             -     (*I7);\n\t"
+                                                       "TR3 = (*R0) - (*R4) - (*I2) + (*I6) - C8Q*(*R1) + C8Q*(*I1) + C8Q*(*R3) + C8Q*(*I3) + C8Q*(*R5) - C8Q*(*I5) - C8Q*(*R7) - C8Q*(*I7);\n\t"
+                                                       "TR4 = (*R0) + (*R4) + (*R2) + (*R6) -     (*R1)             -     (*R3)             -     (*R5)             -     (*R7)            ;\n\t"
+                                                       "TR5 = (*R0) - (*R4) + (*I2) - (*I6) - C8Q*(*R1) - C8Q*(*I1) + C8Q*(*R3) - C8Q*(*I3) + C8Q*(*R5) + C8Q*(*I5) - C8Q*(*R7) + C8Q*(*I7);\n\t"
+                                                       "TR6 = (*R0) + (*R4) - (*R2) - (*R6)             -    (*I1)              +     (*I3)             -     (*I5)             +     (*I7);\n\t"
+                                                       "TR7 = (*R0) - (*R4) - (*I2) + (*I6) + C8Q*(*R1) - C8Q*(*I1) - C8Q*(*R3) - C8Q*(*I3) - C8Q*(*R5) + C8Q*(*I5) + C8Q*(*R7) + C8Q*(*I7);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = (*I0) + (*I4) + (*I2) + (*I6)             +     (*I1)             +     (*I3)             +     (*I5)             +     (*I7);\n\t"
+                                                       "TI1 = (*I0) - (*I4) - (*R2) + (*R6) - C8Q*(*R1) + C8Q*(*I1) - C8Q*(*R3) - C8Q*(*I3) + C8Q*(*R5) - C8Q*(*I5) + C8Q*(*R7) + C8Q*(*I7);\n\t"
+                                                       "TI2 = (*I0) + (*I4) - (*I2) - (*I6) -     (*R1)             +     (*R3)             -     (*R5)             +     (*R7)            ;\n\t"
+                                                       "TI3 = (*I0) - (*I4) + (*R2) - (*R6) - C8Q*(*R1) - C8Q*(*I1) - C8Q*(*R3) + C8Q*(*I3) + C8Q*(*R5) + C8Q*(*I5) + C8Q*(*R7) - C8Q*(*I7);\n\t"
+                                                       "TI4 = (*I0) + (*I4) + (*I2) + (*I6)             -    (*I1)              -     (*I3)             -     (*I5)             -     (*I7);\n\t"
+                                                       "TI5 = (*I0) - (*I4) - (*R2) + (*R6) + C8Q*(*R1) - C8Q*(*I1) + C8Q*(*R3) + C8Q*(*I3) - C8Q*(*R5) + C8Q*(*I5) - C8Q*(*R7) - C8Q*(*I7);\n\t"
+                                                       "TI6 = (*I0) + (*I4) - (*I2) - (*I6) +     (*R1)             -     (*R3)             +     (*R5)             -     (*R7)            ;\n\t"
+                                                       "TI7 = (*I0) - (*I4) + (*R2) - (*R6) + C8Q*(*R1) + C8Q*(*I1) + C8Q*(*R3) - C8Q*(*I3) - C8Q*(*R5) - C8Q*(*I5) - C8Q*(*R7) + C8Q*(*I7);\n\t";
+                                               }
+                                       }
+                                       else
+                                       {
+                                               if(cReg)
+                                               {
+                                                       bflyStr +=
+                                                       "(*R1) = (*R0) - (*R1);\n\t"
+                                                       "(*R0) = 2.0f * (*R0) - (*R1);\n\t"
+                                                       "(*R3) = (*R2) - (*R3);\n\t"
+                                                       "(*R2) = 2.0f * (*R2) - (*R3);\n\t"
+                                                       "(*R5) = (*R4) - (*R5);\n\t"
+                                                       "(*R4) = 2.0f * (*R4) - (*R5);\n\t"
+                                                       "(*R7) = (*R6) - (*R7);\n\t"
+                                                       "(*R6) = 2.0f * (*R6) - (*R7);\n\t"
+                                                       "\n\t"
+                                                       "(*R2) = (*R0) - (*R2);\n\t"
+                                                       "(*R0) = 2.0f * (*R0) - (*R2);\n\t"
+                                                       "(*R3) = (*R1) + (fvect2)((*R3).y, -(*R3).x);\n\t"
+                                                       "(*R1) = 2.0f * (*R1) - (*R3);\n\t"
+                                                       "(*R6) = (*R4) - (*R6);\n\t"
+                                                       "(*R4) = 2.0f * (*R4) - (*R6);\n\t"
+                                                       "(*R7) = (*R5) + (fvect2)((*R7).y, -(*R7).x);\n\t"
+                                                       "(*R5) = 2.0f * (*R5) - (*R7);\n\t"
+                                                       "\n\t"
+                                                       "(*R4) = (*R0) - (*R4);\n\t"
+                                                       "(*R0) = 2.0f * (*R0) - (*R4);\n\t"
+                                                       "(*R5) = ((*R1) - C8Q * (*R5)) + C8Q * (fvect2)((*R5).y, -(*R5).x);\n\t"
+                                                       "(*R1) = 2.0f * (*R1) - (*R5);\n\t"
+                                                       "(*R6) = (*R2) + (fvect2)((*R6).y, -(*R6).x);\n\t"
+                                                       "(*R2) = 2.0f * (*R2) - (*R6);\n\t"
+                                                       "(*R7) = ((*R3) + C8Q * (*R7)) + C8Q * (fvect2)((*R7).y, -(*R7).x);\n\t"
+                                                       "(*R3) = 2.0f * (*R3) - (*R7);\n\t";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = (*R0) + (*R4) + (*R2) + (*R6) +     (*R1)             +     (*R3)             +     (*R5)             +     (*R7)            ;\n\t"
+                                                       "TR1 = (*R0) - (*R4) - (*I2) + (*I6) + C8Q*(*R1) - C8Q*(*I1) - C8Q*(*R3) - C8Q*(*I3) - C8Q*(*R5) + C8Q*(*I5) + C8Q*(*R7) + C8Q*(*I7);\n\t"
+                                                       "TR2 = (*R0) + (*R4) - (*R2) - (*R6)             -     (*I1)             +     (*I3)             -     (*I5)             +     (*I7);\n\t"
+                                                       "TR3 = (*R0) - (*R4) + (*I2) - (*I6) - C8Q*(*R1) - C8Q*(*I1) + C8Q*(*R3) - C8Q*(*I3) + C8Q*(*R5) + C8Q*(*I5) - C8Q*(*R7) + C8Q*(*I7);\n\t"
+                                                       "TR4 = (*R0) + (*R4) + (*R2) + (*R6) -     (*R1)             -    (*R3)              -     (*R5)             -     (*R7)            ;\n\t"
+                                                       "TR5 = (*R0) - (*R4) - (*I2) + (*I6) - C8Q*(*R1) + C8Q*(*I1) + C8Q*(*R3) + C8Q*(*I3) + C8Q*(*R5) - C8Q*(*I5) - C8Q*(*R7) - C8Q*(*I7);\n\t"
+                                                       "TR6 = (*R0) + (*R4) - (*R2) - (*R6)             +     (*I1)             -     (*I3)             +     (*I5)             -     (*I7);\n\t"
+                                                       "TR7 = (*R0) - (*R4) + (*I2) - (*I6) + C8Q*(*R1) + C8Q*(*I1) - C8Q*(*R3) + C8Q*(*I3) - C8Q*(*R5) - C8Q*(*I5) + C8Q*(*R7) - C8Q*(*I7);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = (*I0) + (*I4) + (*I2) + (*I6)             +     (*I1)             +    (*I3)              +     (*I5)             +     (*I7);\n\t"
+                                                       "TI1 = (*I0) - (*I4) + (*R2) - (*R6) + C8Q*(*R1) + C8Q*(*I1) + C8Q*(*R3) - C8Q*(*I3) - C8Q*(*R5) - C8Q*(*I5) - C8Q*(*R7) + C8Q*(*I7);\n\t"
+                                                       "TI2 = (*I0) + (*I4) - (*I2) - (*I6) +     (*R1)             -     (*R3)             +     (*R5)             -     (*R7)            ;\n\t"
+                                                       "TI3 = (*I0) - (*I4) - (*R2) + (*R6) + C8Q*(*R1) - C8Q*(*I1) + C8Q*(*R3) + C8Q*(*I3) - C8Q*(*R5) + C8Q*(*I5) - C8Q*(*R7) - C8Q*(*I7);\n\t"
+                                                       "TI4 = (*I0) + (*I4) + (*I2) + (*I6)             -     (*I1)             -     (*I3)             -     (*I5)             -     (*I7);\n\t"
+                                                       "TI5 = (*I0) - (*I4) + (*R2) - (*R6) - C8Q*(*R1) - C8Q*(*I1) - C8Q*(*R3) + C8Q*(*I3) + C8Q*(*R5) + C8Q*(*I5) + C8Q*(*R7) - C8Q*(*I7);\n\t"
+                                                       "TI6 = (*I0) + (*I4) - (*I2) - (*I6) -     (*R1)             +     (*R3)             -     (*R5)             +     (*R7)            ;\n\t"
+                                                       "TI7 = (*I0) - (*I4) - (*R2) + (*R6) - C8Q*(*R1) + C8Q*(*I1) - C8Q*(*R3) - C8Q*(*I3) + C8Q*(*R5) - C8Q*(*I5) + C8Q*(*R7) + C8Q*(*I7);\n\t";
+                                               }
+                                       }
+                               } break;
+                       case 10:
+                               {
+                                       if(fwd)
+                                       {
+                                               if(cReg)
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = (*R0).x + (*R2).x + (*R4).x + (*R6).x + (*R8).x;\n\t"
+                                                       "TR2 = ((*R0).x - C5QC*((*R4).x + (*R6).x)) + C5QB*((*R2).y - (*R8).y) + C5QD*((*R4).y - (*R6).y) + C5QA*(((*R2).x - (*R4).x) + ((*R8).x - (*R6).x));\n\t"
+                                                       "TR8 = ((*R0).x - C5QC*((*R4).x + (*R6).x)) - C5QB*((*R2).y - (*R8).y) - C5QD*((*R4).y - (*R6).y) + C5QA*(((*R2).x - (*R4).x) + ((*R8).x - (*R6).x));\n\t"
+                                                       "TR4 = ((*R0).x - C5QC*((*R2).x + (*R8).x)) - C5QB*((*R4).y - (*R6).y) + C5QD*((*R2).y - (*R8).y) + C5QA*(((*R4).x - (*R2).x) + ((*R6).x - (*R8).x));\n\t"
+                                                       "TR6 = ((*R0).x - C5QC*((*R2).x + (*R8).x)) + C5QB*((*R4).y - (*R6).y) - C5QD*((*R2).y - (*R8).y) + C5QA*(((*R4).x - (*R2).x) + ((*R6).x - (*R8).x));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = (*R0).y + (*R2).y + (*R4).y + (*R6).y + (*R8).y;\n\t"
+                                                       "TI2 = ((*R0).y - C5QC*((*R4).y + (*R6).y)) - C5QB*((*R2).x - (*R8).x) - C5QD*((*R4).x - (*R6).x) + C5QA*(((*R2).y - (*R4).y) + ((*R8).y - (*R6).y));\n\t"
+                                                       "TI8 = ((*R0).y - C5QC*((*R4).y + (*R6).y)) + C5QB*((*R2).x - (*R8).x) + C5QD*((*R4).x - (*R6).x) + C5QA*(((*R2).y - (*R4).y) + ((*R8).y - (*R6).y));\n\t"
+                                                       "TI4 = ((*R0).y - C5QC*((*R2).y + (*R8).y)) + C5QB*((*R4).x - (*R6).x) - C5QD*((*R2).x - (*R8).x) + C5QA*(((*R4).y - (*R2).y) + ((*R6).y - (*R8).y));\n\t"
+                                                       "TI6 = ((*R0).y - C5QC*((*R2).y + (*R8).y)) - C5QB*((*R4).x - (*R6).x) + C5QD*((*R2).x - (*R8).x) + C5QA*(((*R4).y - (*R2).y) + ((*R6).y - (*R8).y));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TR1 = (*R1).x + (*R3).x + (*R5).x + (*R7).x + (*R9).x;\n\t"
+                                                       "TR3 = ((*R1).x - C5QC*((*R5).x + (*R7).x)) + C5QB*((*R3).y - (*R9).y) + C5QD*((*R5).y - (*R7).y) + C5QA*(((*R3).x - (*R5).x) + ((*R9).x - (*R7).x));\n\t"
+                                                       "TR9 = ((*R1).x - C5QC*((*R5).x + (*R7).x)) - C5QB*((*R3).y - (*R9).y) - C5QD*((*R5).y - (*R7).y) + C5QA*(((*R3).x - (*R5).x) + ((*R9).x - (*R7).x));\n\t"
+                                                       "TR5 = ((*R1).x - C5QC*((*R3).x + (*R9).x)) - C5QB*((*R5).y - (*R7).y) + C5QD*((*R3).y - (*R9).y) + C5QA*(((*R5).x - (*R3).x) + ((*R7).x - (*R9).x));\n\t"
+                                                       "TR7 = ((*R1).x - C5QC*((*R3).x + (*R9).x)) + C5QB*((*R5).y - (*R7).y) - C5QD*((*R3).y - (*R9).y) + C5QA*(((*R5).x - (*R3).x) + ((*R7).x - (*R9).x));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI1 = (*R1).y + (*R3).y + (*R5).y + (*R7).y + (*R9).y;\n\t"
+                                                       "TI3 = ((*R1).y - C5QC*((*R5).y + (*R7).y)) - C5QB*((*R3).x - (*R9).x) - C5QD*((*R5).x - (*R7).x) + C5QA*(((*R3).y - (*R5).y) + ((*R9).y - (*R7).y));\n\t"
+                                                       "TI9 = ((*R1).y - C5QC*((*R5).y + (*R7).y)) + C5QB*((*R3).x - (*R9).x) + C5QD*((*R5).x - (*R7).x) + C5QA*(((*R3).y - (*R5).y) + ((*R9).y - (*R7).y));\n\t"
+                                                       "TI5 = ((*R1).y - C5QC*((*R3).y + (*R9).y)) + C5QB*((*R5).x - (*R7).x) - C5QD*((*R3).x - (*R9).x) + C5QA*(((*R5).y - (*R3).y) + ((*R7).y - (*R9).y));\n\t"
+                                                       "TI7 = ((*R1).y - C5QC*((*R3).y + (*R9).y)) - C5QB*((*R5).x - (*R7).x) + C5QD*((*R3).x - (*R9).x) + C5QA*(((*R5).y - (*R3).y) + ((*R7).y - (*R9).y));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R0).x = TR0 + TR1;\n\t"
+                                                       "(*R1).x = TR2 + ( C5QE*TR3 + C5QD*TI3);\n\t"
+                                                       "(*R2).x = TR4 + ( C5QA*TR5 + C5QB*TI5);\n\t"
+                                                       "(*R3).x = TR6 + (-C5QA*TR7 + C5QB*TI7);\n\t"
+                                                       "(*R4).x = TR8 + (-C5QE*TR9 + C5QD*TI9);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R0).y = TI0 + TI1;\n\t"
+                                                       "(*R1).y = TI2 + (-C5QD*TR3 + C5QE*TI3);\n\t"
+                                                       "(*R2).y = TI4 + (-C5QB*TR5 + C5QA*TI5);\n\t"
+                                                       "(*R3).y = TI6 + (-C5QB*TR7 - C5QA*TI7);\n\t"
+                                                       "(*R4).y = TI8 + (-C5QD*TR9 - C5QE*TI9);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R5).x = TR0 - TR1;\n\t"
+                                                       "(*R6).x = TR2 - ( C5QE*TR3 + C5QD*TI3);\n\t"
+                                                       "(*R7).x = TR4 - ( C5QA*TR5 + C5QB*TI5);\n\t"
+                                                       "(*R8).x = TR6 - (-C5QA*TR7 + C5QB*TI7);\n\t"
+                                                       "(*R9).x = TR8 - (-C5QE*TR9 + C5QD*TI9);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R5).y = TI0 - TI1;\n\t"
+                                                       "(*R6).y = TI2 - (-C5QD*TR3 + C5QE*TI3);\n\t"
+                                                       "(*R7).y = TI4 - (-C5QB*TR5 + C5QA*TI5);\n\t"
+                                                       "(*R8).y = TI6 - (-C5QB*TR7 - C5QA*TI7);\n\t"
+                                                       "(*R9).y = TI8 - (-C5QD*TR9 - C5QE*TI9);\n\t";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = *R0 + *R2 + *R4 + *R6 + *R8;\n\t"
+                                                       "TR2 = (*R0 - C5QC*(*R4 + *R6)) + C5QB*(*I2 - *I8) + C5QD*(*I4 - *I6) + C5QA*((*R2 - *R4) + (*R8 - *R6));\n\t"
+                                                       "TR8 = (*R0 - C5QC*(*R4 + *R6)) - C5QB*(*I2 - *I8) - C5QD*(*I4 - *I6) + C5QA*((*R2 - *R4) + (*R8 - *R6));\n\t"
+                                                       "TR4 = (*R0 - C5QC*(*R2 + *R8)) - C5QB*(*I4 - *I6) + C5QD*(*I2 - *I8) + C5QA*((*R4 - *R2) + (*R6 - *R8));\n\t"
+                                                       "TR6 = (*R0 - C5QC*(*R2 + *R8)) + C5QB*(*I4 - *I6) - C5QD*(*I2 - *I8) + C5QA*((*R4 - *R2) + (*R6 - *R8));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = *I0 + *I2 + *I4 + *I6 + *I8;\n\t"
+                                                       "TI2 = (*I0 - C5QC*(*I4 + *I6)) - C5QB*(*R2 - *R8) - C5QD*(*R4 - *R6) + C5QA*((*I2 - *I4) + (*I8 - *I6));\n\t"
+                                                       "TI8 = (*I0 - C5QC*(*I4 + *I6)) + C5QB*(*R2 - *R8) + C5QD*(*R4 - *R6) + C5QA*((*I2 - *I4) + (*I8 - *I6));\n\t"
+                                                       "TI4 = (*I0 - C5QC*(*I2 + *I8)) + C5QB*(*R4 - *R6) - C5QD*(*R2 - *R8) + C5QA*((*I4 - *I2) + (*I6 - *I8));\n\t"
+                                                       "TI6 = (*I0 - C5QC*(*I2 + *I8)) - C5QB*(*R4 - *R6) + C5QD*(*R2 - *R8) + C5QA*((*I4 - *I2) + (*I6 - *I8));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TR1 = *R1 + *R3 + *R5 + *R7 + *R9;\n\t"
+                                                       "TR3 = (*R1 - C5QC*(*R5 + *R7)) + C5QB*(*I3 - *I9) + C5QD*(*I5 - *I7) + C5QA*((*R3 - *R5) + (*R9 - *R7));\n\t"
+                                                       "TR9 = (*R1 - C5QC*(*R5 + *R7)) - C5QB*(*I3 - *I9) - C5QD*(*I5 - *I7) + C5QA*((*R3 - *R5) + (*R9 - *R7));\n\t"
+                                                       "TR5 = (*R1 - C5QC*(*R3 + *R9)) - C5QB*(*I5 - *I7) + C5QD*(*I3 - *I9) + C5QA*((*R5 - *R3) + (*R7 - *R9));\n\t"
+                                                       "TR7 = (*R1 - C5QC*(*R3 + *R9)) + C5QB*(*I5 - *I7) - C5QD*(*I3 - *I9) + C5QA*((*R5 - *R3) + (*R7 - *R9));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI1 = *I1 + *I3 + *I5 + *I7 + *I9;\n\t"
+                                                       "TI3 = (*I1 - C5QC*(*I5 + *I7)) - C5QB*(*R3 - *R9) - C5QD*(*R5 - *R7) + C5QA*((*I3 - *I5) + (*I9 - *I7));\n\t"
+                                                       "TI9 = (*I1 - C5QC*(*I5 + *I7)) + C5QB*(*R3 - *R9) + C5QD*(*R5 - *R7) + C5QA*((*I3 - *I5) + (*I9 - *I7));\n\t"
+                                                       "TI5 = (*I1 - C5QC*(*I3 + *I9)) + C5QB*(*R5 - *R7) - C5QD*(*R3 - *R9) + C5QA*((*I5 - *I3) + (*I7 - *I9));\n\t"
+                                                       "TI7 = (*I1 - C5QC*(*I3 + *I9)) - C5QB*(*R5 - *R7) + C5QD*(*R3 - *R9) + C5QA*((*I5 - *I3) + (*I7 - *I9));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R0) = TR0 + TR1;\n\t"
+                                                       "(*R1) = TR2 + ( C5QE*TR3 + C5QD*TI3);\n\t"
+                                                       "(*R2) = TR4 + ( C5QA*TR5 + C5QB*TI5);\n\t"
+                                                       "(*R3) = TR6 + (-C5QA*TR7 + C5QB*TI7);\n\t"
+                                                       "(*R4) = TR8 + (-C5QE*TR9 + C5QD*TI9);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*I0) = TI0 + TI1;\n\t"
+                                                       "(*I1) = TI2 + (-C5QD*TR3 + C5QE*TI3);\n\t"
+                                                       "(*I2) = TI4 + (-C5QB*TR5 + C5QA*TI5);\n\t"
+                                                       "(*I3) = TI6 + (-C5QB*TR7 - C5QA*TI7);\n\t"
+                                                       "(*I4) = TI8 + (-C5QD*TR9 - C5QE*TI9);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R5) = TR0 - TR1;\n\t"
+                                                       "(*R6) = TR2 - ( C5QE*TR3 + C5QD*TI3);\n\t"
+                                                       "(*R7) = TR4 - ( C5QA*TR5 + C5QB*TI5);\n\t"
+                                                       "(*R8) = TR6 - (-C5QA*TR7 + C5QB*TI7);\n\t"
+                                                       "(*R9) = TR8 - (-C5QE*TR9 + C5QD*TI9);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*I5) = TI0 - TI1;\n\t"
+                                                       "(*I6) = TI2 - (-C5QD*TR3 + C5QE*TI3);\n\t"
+                                                       "(*I7) = TI4 - (-C5QB*TR5 + C5QA*TI5);\n\t"
+                                                       "(*I8) = TI6 - (-C5QB*TR7 - C5QA*TI7);\n\t"
+                                                       "(*I9) = TI8 - (-C5QD*TR9 - C5QE*TI9);\n\t";
+                                               }
+                                       }
+                                       else
+                                       {
+                                               if(cReg)
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = (*R0).x + (*R2).x + (*R4).x + (*R6).x + (*R8).x;\n\t"
+                                                       "TR2 = ((*R0).x - C5QC*((*R4).x + (*R6).x)) - C5QB*((*R2).y - (*R8).y) - C5QD*((*R4).y - (*R6).y) + C5QA*(((*R2).x - (*R4).x) + ((*R8).x - (*R6).x));\n\t"
+                                                       "TR8 = ((*R0).x - C5QC*((*R4).x + (*R6).x)) + C5QB*((*R2).y - (*R8).y) + C5QD*((*R4).y - (*R6).y) + C5QA*(((*R2).x - (*R4).x) + ((*R8).x - (*R6).x));\n\t"
+                                                       "TR4 = ((*R0).x - C5QC*((*R2).x + (*R8).x)) + C5QB*((*R4).y - (*R6).y) - C5QD*((*R2).y - (*R8).y) + C5QA*(((*R4).x - (*R2).x) + ((*R6).x - (*R8).x));\n\t"
+                                                       "TR6 = ((*R0).x - C5QC*((*R2).x + (*R8).x)) - C5QB*((*R4).y - (*R6).y) + C5QD*((*R2).y - (*R8).y) + C5QA*(((*R4).x - (*R2).x) + ((*R6).x - (*R8).x));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = (*R0).y + (*R2).y + (*R4).y + (*R6).y + (*R8).y;\n\t"
+                                                       "TI2 = ((*R0).y - C5QC*((*R4).y + (*R6).y)) + C5QB*((*R2).x - (*R8).x) + C5QD*((*R4).x - (*R6).x) + C5QA*(((*R2).y - (*R4).y) + ((*R8).y - (*R6).y));\n\t"
+                                                       "TI8 = ((*R0).y - C5QC*((*R4).y + (*R6).y)) - C5QB*((*R2).x - (*R8).x) - C5QD*((*R4).x - (*R6).x) + C5QA*(((*R2).y - (*R4).y) + ((*R8).y - (*R6).y));\n\t"
+                                                       "TI4 = ((*R0).y - C5QC*((*R2).y + (*R8).y)) - C5QB*((*R4).x - (*R6).x) + C5QD*((*R2).x - (*R8).x) + C5QA*(((*R4).y - (*R2).y) + ((*R6).y - (*R8).y));\n\t"
+                                                       "TI6 = ((*R0).y - C5QC*((*R2).y + (*R8).y)) + C5QB*((*R4).x - (*R6).x) - C5QD*((*R2).x - (*R8).x) + C5QA*(((*R4).y - (*R2).y) + ((*R6).y - (*R8).y));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TR1 = (*R1).x + (*R3).x + (*R5).x + (*R7).x + (*R9).x;\n\t"
+                                                       "TR3 = ((*R1).x - C5QC*((*R5).x + (*R7).x)) - C5QB*((*R3).y - (*R9).y) - C5QD*((*R5).y - (*R7).y) + C5QA*(((*R3).x - (*R5).x) + ((*R9).x - (*R7).x));\n\t"
+                                                       "TR9 = ((*R1).x - C5QC*((*R5).x + (*R7).x)) + C5QB*((*R3).y - (*R9).y) + C5QD*((*R5).y - (*R7).y) + C5QA*(((*R3).x - (*R5).x) + ((*R9).x - (*R7).x));\n\t"
+                                                       "TR5 = ((*R1).x - C5QC*((*R3).x + (*R9).x)) + C5QB*((*R5).y - (*R7).y) - C5QD*((*R3).y - (*R9).y) + C5QA*(((*R5).x - (*R3).x) + ((*R7).x - (*R9).x));\n\t"
+                                                       "TR7 = ((*R1).x - C5QC*((*R3).x + (*R9).x)) - C5QB*((*R5).y - (*R7).y) + C5QD*((*R3).y - (*R9).y) + C5QA*(((*R5).x - (*R3).x) + ((*R7).x - (*R9).x));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI1 = (*R1).y + (*R3).y + (*R5).y + (*R7).y + (*R9).y;\n\t"
+                                                       "TI3 = ((*R1).y - C5QC*((*R5).y + (*R7).y)) + C5QB*((*R3).x - (*R9).x) + C5QD*((*R5).x - (*R7).x) + C5QA*(((*R3).y - (*R5).y) + ((*R9).y - (*R7).y));\n\t"
+                                                       "TI9 = ((*R1).y - C5QC*((*R5).y + (*R7).y)) - C5QB*((*R3).x - (*R9).x) - C5QD*((*R5).x - (*R7).x) + C5QA*(((*R3).y - (*R5).y) + ((*R9).y - (*R7).y));\n\t"
+                                                       "TI5 = ((*R1).y - C5QC*((*R3).y + (*R9).y)) - C5QB*((*R5).x - (*R7).x) + C5QD*((*R3).x - (*R9).x) + C5QA*(((*R5).y - (*R3).y) + ((*R7).y - (*R9).y));\n\t"
+                                                       "TI7 = ((*R1).y - C5QC*((*R3).y + (*R9).y)) + C5QB*((*R5).x - (*R7).x) - C5QD*((*R3).x - (*R9).x) + C5QA*(((*R5).y - (*R3).y) + ((*R7).y - (*R9).y));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R0).x = TR0 + TR1;\n\t"
+                                                       "(*R1).x = TR2 + ( C5QE*TR3 - C5QD*TI3);\n\t"
+                                                       "(*R2).x = TR4 + ( C5QA*TR5 - C5QB*TI5);\n\t"
+                                                       "(*R3).x = TR6 + (-C5QA*TR7 - C5QB*TI7);\n\t"
+                                                       "(*R4).x = TR8 + (-C5QE*TR9 - C5QD*TI9);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R0).y = TI0 + TI1;\n\t"
+                                                       "(*R1).y = TI2 + ( C5QD*TR3 + C5QE*TI3);\n\t"
+                                                       "(*R2).y = TI4 + ( C5QB*TR5 + C5QA*TI5);\n\t"
+                                                       "(*R3).y = TI6 + ( C5QB*TR7 - C5QA*TI7);\n\t"
+                                                       "(*R4).y = TI8 + ( C5QD*TR9 - C5QE*TI9);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R5).x = TR0 - TR1;\n\t"
+                                                       "(*R6).x = TR2 - ( C5QE*TR3 - C5QD*TI3);\n\t"
+                                                       "(*R7).x = TR4 - ( C5QA*TR5 - C5QB*TI5);\n\t"
+                                                       "(*R8).x = TR6 - (-C5QA*TR7 - C5QB*TI7);\n\t"
+                                                       "(*R9).x = TR8 - (-C5QE*TR9 - C5QD*TI9);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R5).y = TI0 - TI1;\n\t"
+                                                       "(*R6).y = TI2 - ( C5QD*TR3 + C5QE*TI3);\n\t"
+                                                       "(*R7).y = TI4 - ( C5QB*TR5 + C5QA*TI5);\n\t"
+                                                       "(*R8).y = TI6 - ( C5QB*TR7 - C5QA*TI7);\n\t"
+                                                       "(*R9).y = TI8 - ( C5QD*TR9 - C5QE*TI9);\n\t";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr +=
+                                                       "TR0 = *R0 + *R2 + *R4 + *R6 + *R8;\n\t"
+                                                       "TR2 = (*R0 - C5QC*(*R4 + *R6)) - C5QB*(*I2 - *I8) - C5QD*(*I4 - *I6) + C5QA*((*R2 - *R4) + (*R8 - *R6));\n\t"
+                                                       "TR8 = (*R0 - C5QC*(*R4 + *R6)) + C5QB*(*I2 - *I8) + C5QD*(*I4 - *I6) + C5QA*((*R2 - *R4) + (*R8 - *R6));\n\t"
+                                                       "TR4 = (*R0 - C5QC*(*R2 + *R8)) + C5QB*(*I4 - *I6) - C5QD*(*I2 - *I8) + C5QA*((*R4 - *R2) + (*R6 - *R8));\n\t"
+                                                       "TR6 = (*R0 - C5QC*(*R2 + *R8)) - C5QB*(*I4 - *I6) + C5QD*(*I2 - *I8) + C5QA*((*R4 - *R2) + (*R6 - *R8));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI0 = *I0 + *I2 + *I4 + *I6 + *I8;\n\t"
+                                                       "TI2 = (*I0 - C5QC*(*I4 + *I6)) + C5QB*(*R2 - *R8) + C5QD*(*R4 - *R6) + C5QA*((*I2 - *I4) + (*I8 - *I6));\n\t"
+                                                       "TI8 = (*I0 - C5QC*(*I4 + *I6)) - C5QB*(*R2 - *R8) - C5QD*(*R4 - *R6) + C5QA*((*I2 - *I4) + (*I8 - *I6));\n\t"
+                                                       "TI4 = (*I0 - C5QC*(*I2 + *I8)) - C5QB*(*R4 - *R6) + C5QD*(*R2 - *R8) + C5QA*((*I4 - *I2) + (*I6 - *I8));\n\t"
+                                                       "TI6 = (*I0 - C5QC*(*I2 + *I8)) + C5QB*(*R4 - *R6) - C5QD*(*R2 - *R8) + C5QA*((*I4 - *I2) + (*I6 - *I8));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TR1 = *R1 + *R3 + *R5 + *R7 + *R9;\n\t"
+                                                       "TR3 = (*R1 - C5QC*(*R5 + *R7)) - C5QB*(*I3 - *I9) - C5QD*(*I5 - *I7) + C5QA*((*R3 - *R5) + (*R9 - *R7));\n\t"
+                                                       "TR9 = (*R1 - C5QC*(*R5 + *R7)) + C5QB*(*I3 - *I9) + C5QD*(*I5 - *I7) + C5QA*((*R3 - *R5) + (*R9 - *R7));\n\t"
+                                                       "TR5 = (*R1 - C5QC*(*R3 + *R9)) + C5QB*(*I5 - *I7) - C5QD*(*I3 - *I9) + C5QA*((*R5 - *R3) + (*R7 - *R9));\n\t"
+                                                       "TR7 = (*R1 - C5QC*(*R3 + *R9)) - C5QB*(*I5 - *I7) + C5QD*(*I3 - *I9) + C5QA*((*R5 - *R3) + (*R7 - *R9));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "TI1 = *I1 + *I3 + *I5 + *I7 + *I9;\n\t"
+                                                       "TI3 = (*I1 - C5QC*(*I5 + *I7)) + C5QB*(*R3 - *R9) + C5QD*(*R5 - *R7) + C5QA*((*I3 - *I5) + (*I9 - *I7));\n\t"
+                                                       "TI9 = (*I1 - C5QC*(*I5 + *I7)) - C5QB*(*R3 - *R9) - C5QD*(*R5 - *R7) + C5QA*((*I3 - *I5) + (*I9 - *I7));\n\t"
+                                                       "TI5 = (*I1 - C5QC*(*I3 + *I9)) - C5QB*(*R5 - *R7) + C5QD*(*R3 - *R9) + C5QA*((*I5 - *I3) + (*I7 - *I9));\n\t"
+                                                       "TI7 = (*I1 - C5QC*(*I3 + *I9)) + C5QB*(*R5 - *R7) - C5QD*(*R3 - *R9) + C5QA*((*I5 - *I3) + (*I7 - *I9));\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R0) = TR0 + TR1;\n\t"
+                                                       "(*R1) = TR2 + ( C5QE*TR3 - C5QD*TI3);\n\t"
+                                                       "(*R2) = TR4 + ( C5QA*TR5 - C5QB*TI5);\n\t"
+                                                       "(*R3) = TR6 + (-C5QA*TR7 - C5QB*TI7);\n\t"
+                                                       "(*R4) = TR8 + (-C5QE*TR9 - C5QD*TI9);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*I0) = TI0 + TI1;\n\t"
+                                                       "(*I1) = TI2 + ( C5QD*TR3 + C5QE*TI3);\n\t"
+                                                       "(*I2) = TI4 + ( C5QB*TR5 + C5QA*TI5);\n\t"
+                                                       "(*I3) = TI6 + ( C5QB*TR7 - C5QA*TI7);\n\t"
+                                                       "(*I4) = TI8 + ( C5QD*TR9 - C5QE*TI9);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*R5) = TR0 - TR1;\n\t"
+                                                       "(*R6) = TR2 - ( C5QE*TR3 - C5QD*TI3);\n\t"
+                                                       "(*R7) = TR4 - ( C5QA*TR5 - C5QB*TI5);\n\t"
+                                                       "(*R8) = TR6 - (-C5QA*TR7 - C5QB*TI7);\n\t"
+                                                       "(*R9) = TR8 - (-C5QE*TR9 - C5QD*TI9);\n\t";
+
+                                                       bflyStr += "\n\t";
+
+                                                       bflyStr +=
+                                                       "(*I5) = TI0 - TI1;\n\t"
+                                                       "(*I6) = TI2 - ( C5QD*TR3 + C5QE*TI3);\n\t"
+                                                       "(*I7) = TI4 - ( C5QB*TR5 + C5QA*TI5);\n\t"
+                                                       "(*I8) = TI6 - ( C5QB*TR7 - C5QA*TI7);\n\t"
+                                                       "(*I9) = TI8 - ( C5QD*TR9 - C5QE*TI9);\n\t";
+                                               }
+                                       }
+                               } break;
+                       case 11:
+                               {
+                                       static const char *radix11str = " \
+                                               fptype p0, p1, p2, p3, p4, p5, p6, p7, p8, p9; \n\
+                                               p0 = ((*R1).x - (*R10).x)*dir; \n\
+                                               p1 = (*R1).x + (*R10).x; \n\
+                                               p2 = ((*R5).x - (*R6).x)*dir; \n\
+                                               p3 = (*R5).x + (*R6).x; \n\
+                                               p4 = ((*R2).x - (*R9).x)*dir; \n\
+                                               p5 = (*R2).x + (*R9).x; \n\
+                                               p6 = ((*R3).x - (*R8).x)*dir; \n\
+                                               p7 = (*R3).x + (*R8).x; \n\
+                                               p8 = (*R4).x + (*R7).x; \n\
+                                               p9 = ((*R4).x - (*R7).x)*dir; \n\
+                                               \n\
+                                               fptype r0, r1, r2, r3, r4, r5, r6, r7, r8, r9; \n\
+                                               r0 = p4 - p0 * b11_9; \n\
+                                               r1 = p0 + p2 * b11_9; \n\
+                                               r2 = p2 + p6 * b11_9; \n\
+                                               r3 = p6 + p9 * b11_9; \n\
+                                               r4 = p9 - p4 * b11_9; \n\
+                                               r5 = p7 - p1 * b11_8; \n\
+                                               r6 = p5 - p7 * b11_8; \n\
+                                               r7 = p1 - p8 * b11_8; \n\
+                                               r8 = p3 - p5 * b11_8; \n\
+                                               r9 = p8 - p3 * b11_8; \n\
+                                               \n\
+                                               fptype s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; \n\
+                                               s0 = p6 - r0 * b11_6; \n\
+                                               s1 = p9 + r1 * b11_6; \n\
+                                               s2 = p4 - r2 * b11_6; \n\
+                                               s3 = p0 + r3 * b11_6; \n\
+                                               s4 = p2 + r4 * b11_6; \n\
+                                               s5 = p3 - r5 * b11_7; \n\
+                                               s6 = p8 - r6 * b11_7; \n\
+                                               s7 = p5 - r7 * b11_7; \n\
+                                               s8 = p1 - r8 * b11_7; \n\
+                                               s9 = p7 - r9 * b11_7; \n\
+                                               \n\
+                                               fptype p10, p11, p12, p13, p14, p15, p16, p17, p18, p19; \n\
+                                               p10 = ((*R10).y - (*R1).y)*dir; \n\
+                                               p11 = (*R1).y + (*R10).y; \n\
+                                               p12 = ((*R9).y - (*R2).y)*dir; \n\
+                                               p13 = (*R2).y + (*R9).y; \n\
+                                               p14 = ((*R8).y - (*R3).y)*dir; \n\
+                                               p15 = (*R3).y + (*R8).y; \n\
+                                               p16 = ((*R7).y - (*R4).y)*dir; \n\
+                                               p17 = (*R4).y + (*R7).y; \n\
+                                               p18 = ((*R6).y - (*R5).y)*dir; \n\
+                                               p19 = (*R5).y + (*R6).y; \n\
+                                               \n\
+                                               fptype r10, r11, r12, r13, r14, r15, r16, r17, r18, r19; \n\
+                                               r10 = p12 - p10 * b11_9; \n\
+                                               r11 = p16 - p12 * b11_9; \n\
+                                               r12 = p18 + p14 * b11_9; \n\
+                                               r13 = p14 + p16 * b11_9; \n\
+                                               r14 = p10 + p18 * b11_9; \n\
+                                               r15 = p15 - p11 * b11_8; \n\
+                                               r16 = p19 - p13 * b11_8; \n\
+                                               r17 = p13 - p15 * b11_8; \n\
+                                               r18 = p11 - p17 * b11_8; \n\
+                                               r19 = p17 - p19 * b11_8; \n\
+                                               \n\
+                                               fptype s10, s11, s12, s13, s14, s15, s16, s17, s18, s19; \n\
+                                               s10 = p14 - r10 * b11_6; \n\
+                                               s11 = p18 + r11 * b11_6; \n\
+                                               s12 = p12 - r12 * b11_6; \n\
+                                               s13 = p10 + r13 * b11_6; \n\
+                                               s14 = p16 + r14 * b11_6; \n\
+                                               s15 = p19 - r15 * b11_7; \n\
+                                               s16 = p11 - r16 * b11_7; \n\
+                                               s17 = p17 - r17 * b11_7; \n\
+                                               s18 = p13 - r18 * b11_7; \n\
+                                               s19 = p15 - r19 * b11_7; \n\
+                                               \n\
+                                               fptype v0, v1, v2, v3, v4, v5, v6, v7, v8, v9; \n\
+                                               fptype v10, v11, v12, v13, v14, v15, v16, v17, v18, v19; \n\
+                                               v0 = p9 - s0 * b11_4; \n\
+                                               v1 = p4 + s1 * b11_4; \n\
+                                               v2 = p0 + s2 * b11_4; \n\
+                                               v3 = p2 - s3 * b11_4; \n\
+                                               v4 = p6 - s4 * b11_4; \n\
+                                               v5 = p8 - s5 * b11_5; \n\
+                                               v6 = p1 - s6 * b11_5; \n\
+                                               v7 = p3 - s7 * b11_5; \n\
+                                               v8 = p7 - s8 * b11_5; \n\
+                                               v9 = p5 - s9 * b11_5; \n\
+                                               v10 = p16 - s10 * b11_4; \n\
+                                               v11 = p14 - s11 * b11_4; \n\
+                                               v12 = p10 + s12 * b11_4; \n\
+                                               v13 = p18 - s13 * b11_4; \n\
+                                               v14 = p12 + s14 * b11_4; \n\
+                                               v15 = p17 - s15 * b11_5; \n\
+                                               v16 = p15 - s16 * b11_5; \n\
+                                               v17 = p11 - s17 * b11_5; \n\
+                                               v18 = p19 - s18 * b11_5; \n\
+                                               v19 = p13 - s19 * b11_5; \n\
+                                               \n\
+                                               fptype w0, w1, w2, w3, w4, w5, w6, w7, w8, w9; \n\
+                                               fptype w10, w11, w12, w13, w14, w15, w16, w17, w18, w19; \n\
+                                               w0 = p2 - v0 * b11_2; \n\
+                                               w1 = p6 + v1 * b11_2; \n\
+                                               w2 = p9 - v2 * b11_2; \n\
+                                               w3 = p4 + v3 * b11_2; \n\
+                                               w4 = p0 - v4 * b11_2; \n\
+                                               w5 = p5 - v5 * b11_3; \n\
+                                               w6 = p3 - v6 * b11_3; \n\
+                                               w7 = p7 - v7 * b11_3; \n\
+                                               w8 = p8 - v8 * b11_3; \n\
+                                               w9 = p1 - v9 * b11_3; \n\
+                                               w10 = p18 - v10 * b11_2; \n\
+                                               w11 = p10 - v11 * b11_2; \n\
+                                               w12 = p16 - v12 * b11_2; \n\
+                                               w13 = p12 + v13 * b11_2; \n\
+                                               w14 = p14 + v14 * b11_2; \n\
+                                               w15 = p13 - v15 * b11_3; \n\
+                                               w16 = p17 - v16 * b11_3; \n\
+                                               w17 = p19 - v17 * b11_3; \n\
+                                               w18 = p15 - v18 * b11_3; \n\
+                                               w19 = p11 - v19 * b11_3; \n\
+                                               \n\
+                                               fptype z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; \n\
+                                               z0 = (*R0).x - w5 * b11_1; \n\
+                                               z1 = (*R0).x - w6 * b11_1; \n\
+                                               z2 = (*R0).x - w7 * b11_1; \n\
+                                               z3 = (*R0).x - w8 * b11_1; \n\
+                                               z4 = (*R0).x - w9 * b11_1; \n\
+                                               z5 = (*R0).y - w15 * b11_1; \n\
+                                               z6 = (*R0).y - w16 * b11_1; \n\
+                                               z7 = (*R0).y - w17 * b11_1; \n\
+                                               z8 = (*R0).y - w18 * b11_1; \n\
+                                               z9 = (*R0).y - w19 * b11_1; \n\
+                                               \n\
+                                               (*R0).x = (*R0).x + p1 + p3 + p5 + p7 + p8; \n\
+                                               (*R0).y = (*R0).y + p11 + p13 + p15 + p17 + p19; \n\
+                                               (*R1).x = z1 + w14* b11_0; \n\
+                                               (*R1).y = z7 + w1* b11_0; \n\
+                                               (*R2).x = z2 - w12* b11_0; \n\
+                                               (*R2).y = z8 - w2* b11_0; \n\
+                                               (*R3).x = z0 + w11* b11_0; \n\
+                                               (*R3).y = z5 + w4* b11_0; \n\
+                                               (*R4).x = z3 - w13* b11_0; \n\
+                                               (*R4).y = z6 - w3* b11_0; \n\
+                                               (*R5).x = z4 + w10* b11_0; \n\
+                                               (*R5).y = z9 + w0* b11_0; \n\
+                                               (*R6).x = z4 - w10* b11_0; \n\
+                                               (*R6).y = z9 - w0* b11_0; \n\
+                                               (*R7).x = z3 + w13* b11_0; \n\
+                                               (*R7).y = z6 + w3* b11_0; \n\
+                                               (*R8).x = z0 - w11* b11_0; \n\
+                                               (*R8).y = z5 - w4* b11_0; \n\
+                                               (*R9).x = z2 + w12* b11_0; \n\
+                                               (*R9).y = z8 + w2* b11_0; \n\
+                                               (*R10).x = z1 - w14* b11_0; \n\
+                                               (*R10).y = z7 - w1* b11_0; \n";
+
+                                       if (fwd)
+                                       {
+                                               bflyStr += "fptype dir = -1;\n\n";
+                                       }
+                                       else
+                                       {
+                                               bflyStr += "fptype dir = 1;\n\n";
+                                       }
+
+                                       bflyStr += radix11str;
+
+                               } break;
+                       case 13:
+                               {
+
+                                       static const char *radix13str = " \
+                                               fptype p0, p1, p2, p3, p4, p5, p6, p7, p8, p9;\n\
+                                               p0 = (*R7).x - (*R2).x;\n\
+                                               p1 = (*R7).x + (*R2).x;\n\
+                                               p2 = (*R8).x - (*R5).x;\n\
+                                               p3 = (*R8).x + (*R5).x;\n\
+                                               p4 = (*R9).x - (*R3).x;\n\
+                                               p5 = (*R3).x + (*R9).x;\n\
+                                               p6 = (*R10).x + (*R4).x;\n\
+                                               p7 = (*R10).x - (*R4).x;\n\
+                                               p8 = (*R11).x + (*R6).x;\n\
+                                               p9 = (*R11).x - (*R6).x;\n\
+                                               \n\
+                                               fptype p10, p11, p12, p13, p14, p15, p16, p17, p18, p19;\n\
+                                               p10 = (*R12).x + p6;\n\
+                                               p11 = (*R1).x + p5;\n\
+                                               p12 = p8 - p1;\n\
+                                               p13 = p8 + p1;\n\
+                                               p14 = p9 + p0;\n\
+                                               p15 = p9 - p0;\n\
+                                               p16 = p7 - p4;\n\
+                                               p17 = p4 + p7;\n\
+                                               p18 = p11 + p10;\n\
+                                               p19 = p11 - p10;\n\
+                                               \n\
+                                               fptype s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11;\n\
+                                               s0 = p3 + p13;\n\
+                                               s1 = p2 + p14;\n\
+                                               s2 = p16 - p15;\n\
+                                               s3 = p16 + p15;\n\
+                                               s4 = -(*R12).x + p6 * b13_17;\n\
+                                               s5 =   (*R1).x - p5 * b13_17;\n\
+                                               s6 = s5 - s4;\n\
+                                               s7 = s5 + s4;\n\
+                                               s8 = p18 + s0;\n\
+                                               s9 = p18 - s0;\n\
+                                               fptype c2 = p3 - p13 * b13_17;\n\
+                                               s10 = s6 - c2;\n\
+                                               s11 = s6 + c2;\n\
+                                               \n\
+                                               fptype r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11;\n\
+                                               r0 = (*R7).y + (*R2).y;\n\
+                                               r1 = (*R7).y - (*R2).y;\n\
+                                               r2 = (*R8).y + (*R5).y;\n\
+                                               r3 = (*R8).y - (*R5).y;\n\
+                                               r4 = (*R9).y - (*R3).y;\n\
+                                               r5 = (*R3).y + (*R9).y;\n\
+                                               r6 = (*R10).y + (*R4).y;\n\
+                                               r7 = (*R10).y - (*R4).y;\n\
+                                               r8 = (*R11).y - (*R6).y;\n\
+                                               r9 = (*R11).y + (*R6).y;\n\
+                                               r10 = (*R12).y + r6;\n\
+                                               r11 = (*R1).y + r5;\n\
+                                               \n\
+                                               fptype m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10;\n\
+                                               fptype m11, m12, m13, m14, m15, m16, m17, m18, m19, m20;\n\
+                                               m0 = r4 + r7;\n\
+                                               m1 = r7 - r4;\n\
+                                               m2 = r8 - r1;\n\
+                                               m3 = r8 + r1;\n\
+                                               m4 = r9 + r0;\n\
+                                               m5 = r9 - r0;\n\
+                                               m6 = r11 + r10;\n\
+                                               m7 = r11 - r10;\n\
+                                               m8 = m1 - m2;\n\
+                                               m9 = m1 + m2;\n\
+                                               m10 = r3 + m3;\n\
+                                               m11 = r2 + m4;\n\
+                                               m12 = m6 - m11;\n\
+                                               m13 = m6 + m11;\n\
+                                               \n\
+                                               m14 =  (*R1).y - r5 * b13_17;\n\
+                                               m15 = -(*R12).y + r6 * b13_17;\n\
+                                               m16 =  r2      - m4 * b13_17;\n\
+                                               \n\
+                                               m17 = m14 + m15;\n\
+                                               m18 = m14 - m15;\n\
+                                               m19 = m18 + m16;\n\
+                                               m20 = m18 - m16;\n\
+                                               \n\
+                                               fptype c0, c1, c3, c4, c5, c6, c7, c8, c9;\n\
+                                               fptype c10, c11, c12, c13, c14, c15, c16, c17, c18, c19;\n\
+                                               fptype c20, c21, c22, c23, c24;\n\
+                                               c0  =  s7 - p12 * b13_3;\n\
+                                               c1  =  s7 + p12 * b13_3;\n\
+                                               c3  =  p2 - p14 * b13_17;\n\
+                                               c4  =  s1 - p19 * b13_18;\n\
+                                               c5  = p19 + s1 * b13_18;\n\
+                                               c6  = s10 - s2 * b13_15;\n\
+                                               c7  = s11 - s3 * b13_22;\n\
+                                               c8  = (*R0).x - s8 * b13_23;\n\
+                                               c9  =  s2 + s10 * b13_7;\n\
+                                               c10 =  s3 + s11 * b13_19;\n\
+                                               c11 =  r3 - m3 * b13_17;\n\
+                                               c12 = m17 - m5 * b13_3;\n\
+                                               c13 = m17 + m5 * b13_3;\n\
+                                               c14 = m10 - m7 * b13_18;\n\
+                                               c15 = m20 - m8 * b13_15;\n\
+                                               c16 = m19 - m9 * b13_22;\n\
+                                               c17 =  m7 + m10 * b13_18;\n\
+                                               c18 = (*R0).y- m13 * b13_23;\n\
+                                               c19 =  m9 + m19 * b13_19;\n\
+                                               c20 =  m8 + m20 * b13_7;\n\
+                                               c21 =  c3 + p17 * b13_3;\n\
+                                               c22 =  c3 - p17 * b13_3;\n\
+                                               c23 = c11 + m0 * b13_3;\n\
+                                               c24 = c11 - m0 * b13_3;\n\
+                                               \n\
+                                               fptype d0, d1, d2, d3, d4, d5, d6, d7, d8, d9;\n\
+                                               fptype d10, d11, d12, d13, d14, d15, d16, d17, d18, d19;\n\
+                                               d0  = c22 +  c0 * b13_8;\n\
+                                               d1  =  c0 - c22 * b13_8;\n\
+                                               d2  = c21 +  c1 * b13_24;\n\
+                                               d3  =  c1 - c21 * b13_24;\n\
+                                               d4  =  s9 -  c6 * b13_4;\n\
+                                               d5  =  c6 +  s9 * b13_10;\n\
+                                               d6  =  c7 +  c9 * b13_6;\n\
+                                               d7  =  c7 -  c9 * b13_6;\n\
+                                               d8  =  c8 - c10 * b13_21;\n\
+                                               d9  =  c8 + c10 * b13_16;\n\
+                                               d10 = c24 + c12 * b13_8;\n\
+                                               d11 = c12 - c24 * b13_8;\n\
+                                               d12 = c23 + c13 * b13_24;\n\
+                                               d13 = c13 - c23 * b13_24;\n\
+                                               d14 = m12 - c15 * b13_4;\n\
+                                               d15 = c15 + m12 * b13_10;\n\
+                                               d16 = c18 + c19 * b13_16;\n\
+                                               d17 = c18 - c19 * b13_21;\n\
+                                               d18 = c16 - c20 * b13_6;\n\
+                                               d19 = c16 + c20 * b13_6;\n\
+                                               \n\
+                                               fptype e0, e1, e2, e3, e4, e5, e6, e7, e8, e9;\n\
+                                               fptype e10, e11, e12, e13, e14, e15;\n\
+                                               e0  = d2  +  d0 * b13_5;\n\
+                                               e1  = d2  -  d0 * b13_5;\n\
+                                               e2  = d3  -  d1 * b13_5;\n\
+                                               e3  = d3  +  d1 * b13_5;\n\
+                                               e4  = d8  -  d4 * b13_20;\n\
+                                               e5  = d8  +  d4 * b13_20;\n\
+                                               e6  = d9  +  d5 * b13_14;\n\
+                                               e7  = d9  -  d5 * b13_14;\n\
+                                               e8  = d12 + d10 * b13_5;\n\
+                                               e9  = d12 - d10 * b13_5;\n\
+                                               e10 = d13 - d11 * b13_5;\n\
+                                               e11 = d13 + d11 * b13_5;\n\
+                                               e12 = d16 + d15 * b13_14;\n\
+                                               e13 = d16 - d15 * b13_14;\n\
+                                               e14 = d17 + d14 * b13_20;\n\
+                                               e15 = d17 - d14 * b13_20;\n\
+                                               \n\
+                                               fptype f0, f1, f2, f3, f4, f5, f6, f7, f8, f9;\n\
+                                               fptype f10, f11, f12, f13, f14, f15, f16, f17, f18, f19;\n\
+                                               fptype f20, f21, f22, f23;\n\
+                                               f0  = c17 - e10 * b13_12;\n\
+                                               f1  = e10 + c17 * b13_1;\n\
+                                               f2  = e9  + c14 * b13_1;\n\
+                                               f3  = c14 -  e9 * b13_12;\n\
+                                               f4  = e11 + dir * d7 * b13_0;\n\
+                                               f5  = e11 - dir * d7 * b13_0;\n\
+                                               f6  = e5  + dir * f3 * b13_11;\n\
+                                               f7  = e5  - dir * f3 * b13_11;\n\
+                                               f8  = e4  + dir * e8 * b13_13;\n\
+                                               f9  = e4  - dir * e8 * b13_13;\n\
+                                               f10 = f0  - dir * d6 * b13_2;\n\
+                                               f11 = f0  + dir * d6 * b13_2;\n\
+                                               f12 = e1  +  c4 * b13_1;\n\
+                                               f13 = c4  -  e1 * b13_12;\n\
+                                               f14 = c5  -  e2 * b13_12;\n\
+                                               f15 = e2  +  c5 * b13_1;\n\
+                                               f16 = f14 + dir * d19 * b13_2;\n\
+                                               f17 = f14 - dir * d19 * b13_2;\n\
+                                               f18 = e15 - dir *  e0 * b13_13;\n\
+                                               f19 = e15 + dir *  e0 * b13_13;\n\
+                                               f20 = e14 - dir * f13 * b13_11;\n\
+                                               f21 = e14 + dir * f13 * b13_11;\n\
+                                               f22 = e3  - dir * d18 * b13_0;\n\
+                                               f23 = e3  + dir * d18 * b13_0;\n\
+                                               \n\
+                                               (*R0).x  = (*R0).x + s8;\n\
+                                               (*R0).y  = (*R0).y + m13;\n\
+                                               (*R1).x  =  e6 +  f2 * dir * b13_9 ;\n\
+                                               (*R1).y  = e12 - f12 * dir * b13_9 ;\n\
+                                               (*R2).x  =  f9 - f10 * dir * b13_11;\n\
+                                               (*R2).y  = f19 + f16 * dir * b13_11;\n\
+                                               (*R3).x  =  f6 -  f5 * dir * b13_13;\n\
+                                               (*R3).y  = f20 + f23 * dir * b13_13;\n\
+                                               (*R4).x  =  f7 -  f4 * dir * b13_13;\n\
+                                               (*R4).y  = f21 + f22 * dir * b13_13;\n\
+                                               (*R5).x  =  e7 -  f1 * dir * b13_9 ;\n\
+                                               (*R5).y  = e13 + f15 * dir * b13_9 ;\n\
+                                               (*R6).x  =  f8 - f11 * dir * b13_11;\n\
+                                               (*R6).y  = f18 + f17 * dir * b13_11;\n\
+                                               (*R7).x  =  f9 + f10 * dir * b13_11;\n\
+                                               (*R7).y  = f19 - f16 * dir * b13_11;\n\
+                                               (*R8).x  =  e7 +  f1 * dir * b13_9 ;\n\
+                                               (*R8).y  = e13 - f15 * dir * b13_9 ;\n\
+                                               (*R9).x  =  f6 +  f5 * dir * b13_13;\n\
+                                               (*R9).y  = f20 - f23 * dir * b13_13;\n\
+                                               (*R10).x =  f7 +  f4 * dir * b13_13;\n\
+                                               (*R10).y = f21 - f22 * dir * b13_13;\n\
+                                               (*R11).x =  f8 + f11 * dir * b13_11;\n\
+                                               (*R11).y = f18 - f17 * dir * b13_11;\n\
+                                               (*R12).x =  e6 -  f2 * dir * b13_9 ;\n\
+                                               (*R12).y = e12 + f12 * dir * b13_9 ;\n";
+
+                                               if (fwd)
+                                               {
+                                                       bflyStr += "fptype dir = -1;\n\n";
+                                               }
+                                               else
+                                               {
+                                                       bflyStr += "fptype dir = 1;\n\n";
+                                               }
+
+                                               bflyStr += radix13str;
+
+                               } break;
+
+                       default:
+                               assert(false);
+                       }
+
+                       bflyStr += "\n\t";
+
+                       // Assign results
+                       if( (radix & (radix-1)) || (!cReg) )
+                       {
+                               if( (radix != 10) && (radix != 6) )
+                               {
+                               for(size_t i=0; i<radix;i++)
+                               {
+                                       if(cReg)
+                                       {
+                                               if ( (radix != 7) && (radix != 11) && (radix != 13) )
+                                               {
+                                               bflyStr += "((*R"; bflyStr += SztToStr(i); bflyStr += ").x) = TR"; bflyStr += SztToStr(i); bflyStr += "; ";
+                                               bflyStr += "((*R"; bflyStr += SztToStr(i); bflyStr += ").y) = TI"; bflyStr += SztToStr(i); bflyStr += ";\n\t";
+                                               }
+                                       }
+                                       else
+                                       {
+                                               bflyStr += "(*R"; bflyStr += SztToStr(i); bflyStr += ") = TR"; bflyStr += SztToStr(i); bflyStr += "; ";
+                                               bflyStr += "(*I"; bflyStr += SztToStr(i); bflyStr += ") = TI"; bflyStr += SztToStr(i); bflyStr += ";\n\t";
+                                       }
+                               }
+                               }
+                       }
+                       else
+                       {
+                               for(size_t i=0; i<radix;i++)
+                               {
+                                       size_t j = BitReverse(i, radix);
+
+                                       if(i < j)
+                                       {
+                                               bflyStr += "T = (*R"; bflyStr += SztToStr(i); bflyStr += "); (*R";
+                                               bflyStr += SztToStr(i); bflyStr += ") = (*R"; bflyStr += SztToStr(j); bflyStr += "); (*R";
+                                               bflyStr += SztToStr(j); bflyStr += ") = T;\n\t";
+                                       }
+                               }
+                       }
+
+                       bflyStr += "\n}\n";
+               }
+
+       public:
+               Butterfly(size_t radixVal, size_t countVal, bool fwdVal, bool cRegVal) : radix(radixVal), count(countVal), fwd(fwdVal), cReg(cRegVal) {}
+
+               void GenerateButterfly(std::string &bflyStr) const
+               {
+                       assert(count <= 4);
+                       if(count > 0)
+                               GenerateButterflyStr(bflyStr);
+               }
+    };
+
+};
+
+#endif
+
diff --git a/src/include/gromacs/external/clFFT/src/library/generator.transpose.gcn.h b/src/include/gromacs/external/clFFT/src/library/generator.transpose.gcn.h
new file mode 100644 (file)
index 0000000..12ad701
--- /dev/null
@@ -0,0 +1,25 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+#pragma once
+#if !defined( AMD_CLFFT_generator_transpose_H )
+#define AMD_CLFFT_generator_transpose_H
+#include "private.h"
+#include "repo.h"
+#include "plan.h"
+
+#endif
+
diff --git a/src/include/gromacs/external/clFFT/src/library/generator.transpose.h b/src/include/gromacs/external/clFFT/src/library/generator.transpose.h
new file mode 100644 (file)
index 0000000..d00b657
--- /dev/null
@@ -0,0 +1,65 @@
+/* ************************************************************************
+* Copyright 2016 Advanced Micro Devices, Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+* ************************************************************************/
+
+#pragma once
+#if !defined( AMD_CLFFT_GENERATOR_TRANSPOSE_HEADER )
+#define AMD_CLFFT_GENERATOR_TRANSPOSE_HEADER
+#include <iomanip>
+#include "private.h"
+#include "repo.h"
+#include "plan.h"
+#include "generator.stockham.h"
+#include "action.h"
+
+#define AVAIL_MEM_SIZE 32768 
+
+inline std::stringstream& clKernWrite(std::stringstream& rhs, const size_t tabIndex)
+{
+       rhs << std::setw(tabIndex) << "";
+       return rhs;
+}
+
+namespace clfft_transpose_generator
+{
+//generate transepose kernel with sqaure 2d matrix of row major with arbitrary batch size
+/*
+Below is a matrix(row major) containing three sqaure sub matrix along column
+The transpose will be done within each sub matrix.
+[M0
+M1
+M2]
+*/
+clfftStatus genTransposeKernelBatched(const FFTGeneratedTransposeSquareAction::Signature & params, std::string& strKernel, const size_t& lwSize, const size_t reShapeFactor);
+
+//generate transpose kernel with square 2d matrix of row major with blocks along the leading dimension
+//aka leading dimension batched
+/*
+Below is a matrix(row major) contaning three square sub matrix along row
+[M0 M2 M2]
+*/
+clfftStatus genTransposeKernelLeadingDimensionBatched(const FFTGeneratedTransposeNonSquareAction::Signature & params, std::string& strKernel, const size_t& lwSize, const size_t reShapeFactor);
+
+//swap lines. This kind of kernels are using with combination of square transpose kernels to perform nonsqaure transpose 1:2 ratio
+clfftStatus genSwapKernel(const FFTGeneratedTransposeNonSquareAction::Signature & params, std::string& strKernel, std::string& KernelFuncName, const size_t& lwSize, const size_t reShapeFactor);
+
+clfftStatus genSwapKernelGeneral(const FFTGeneratedTransposeNonSquareAction::Signature & params, std::string& strKernel, std::string& KernelFuncName, const size_t& lwSize, const size_t reShapeFactor);
+
+void get_cycles(size_t *cycle_map, size_t num_reduced_row, size_t num_reduced_col);
+
+void permutation_calculation(size_t m, size_t n, std::vector<std::vector<size_t> > &permutationVec);
+}//end of namespace clfft_transpose_generator
+
+#endif
\ No newline at end of file
diff --git a/src/include/gromacs/external/clFFT/src/library/lock.h b/src/include/gromacs/external/clFFT/src/library/lock.h
new file mode 100644 (file)
index 0000000..49c95ac
--- /dev/null
@@ -0,0 +1,248 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+#if !defined( CLFFT_lock_H )
+#define CLFFT_lock_H
+
+#if defined( _WIN32 )
+       #include <windows.h>
+#else
+       #include <pthread.h>
+#endif
+
+#include "private.h"
+
+#if defined( _WIN32 )
+
+//     lockRAII provides an abstraction for the concept of a mutex; it wraps all  mutex functions in generic methods
+//     On windows, the mutex is implemented as a CRITICAL_SECTION, as this is the fastest intraprocess mutex
+//     available.
+//     The template argument 'debugPrint' activates debugging information, but if not active the compiler optimizes
+//     the print statements out
+template< bool debugPrint >
+class lockRAII
+{
+       CRITICAL_SECTION cs;
+       tstring                 csName;
+       tstringstream   tstream;
+
+       //      Does not make sense to create a copy of a lock object; private method
+       lockRAII( const lockRAII& rhs ): csName( rhs.csName )
+       {
+               tstream << std::hex << std::showbase;
+               ::InitializeCriticalSection( &cs );
+       }
+
+       public:
+               lockRAII( )
+               {
+                       tstream << std::hex << std::showbase;
+                       ::InitializeCriticalSection( &cs );
+               }
+
+               lockRAII( const tstring& name ): csName( name )
+               {
+                       tstream << std::hex << std::showbase;
+                       ::InitializeCriticalSection( &cs );
+               }
+
+               ~lockRAII( )
+               {
+                       ::DeleteCriticalSection( &cs );
+               }
+
+               tstring& getName( )
+               {
+                       return csName;
+               }
+
+               void setName( const tstring& name )
+               {
+                       csName  = name;
+               }
+
+               void enter( )
+               {
+                       if( debugPrint )
+                       {
+                               tstream.str( _T( "" ) );
+                               tstream << _T( "Attempting CRITICAL_SECTION( " ) << csName << _T( " )" ) << std::endl;
+                               tout << tstream.str( );
+                       }
+
+                       ::EnterCriticalSection( &cs );
+
+                       if( debugPrint )
+                       {
+                               tstream.str( _T( "" ) );
+                               tstream << _T( "Acquired CRITICAL_SECTION( " ) << csName << _T( " )" ) << std::endl;
+                               tstream << _T( "\tOwningThread( " ) << cs.OwningThread << _T( " )" ) << std::endl;
+                               tstream << _T( "\tLockcount( " ) << cs.LockCount << _T( " )" ) << std::endl;
+                               tstream << _T( "\tRecursionCount( " ) << cs.RecursionCount << _T( " )" ) << std::endl;
+                               tout << tstream.str( );
+                       }
+               }
+
+               void leave( )
+               {
+                       if( debugPrint )
+                       {
+                               tstream.str( _T( "" ) );
+                               tstream << _T( "Releasing CRITICAL_SECTION( " ) << csName << _T( " )" ) << std::endl;
+                               tstream << _T( "\tOwningThread( " ) << cs.OwningThread << _T( " )" ) << std::endl;
+                               tstream << _T( "\tLockcount( " ) << cs.LockCount << _T( " )" ) << std::endl;
+                               tstream << _T( "\tRecursionCount( " ) << cs.RecursionCount << _T( " )" ) << std::endl << std::endl;
+                               tout << tstream.str( );
+                       }
+
+                       ::LeaveCriticalSection( &cs );
+               }
+};
+
+#else
+//     lockRAII provides an abstraction for the concept of a mutex; it wraps all  mutex functions in generic methods
+//     Linux implementation not done yet
+//     The template argument 'debugPrint' activates debugging information, but if not active the compiler optimizes
+//     the print statements out
+template< bool debugPrint >
+class lockRAII
+{
+       pthread_mutex_t mutex;
+       pthread_mutexattr_t mAttr;
+       tstring                 mutexName;
+       tstringstream   tstream;
+
+       //      Does not make sense to create a copy of a lock object; private method
+       lockRAII( const lockRAII& rhs ): mutexName( rhs.mutexName )
+       {
+               tstream << std::hex << std::showbase;
+       }
+
+       public:
+               lockRAII( )
+               {
+                       tstream << std::hex << std::showbase;
+                       pthread_mutexattr_init( &mAttr );
+                       pthread_mutexattr_settype( &mAttr, PTHREAD_MUTEX_RECURSIVE );
+                       pthread_mutex_init( &mutex, &mAttr );
+               }
+
+               lockRAII( const tstring& name ): mutexName( name )
+               {
+                       tstream << std::hex << std::showbase;
+                       pthread_mutexattr_init( &mAttr );
+                       pthread_mutexattr_settype( &mAttr, PTHREAD_MUTEX_RECURSIVE );
+                       pthread_mutex_init( &mutex, &mAttr );
+               }
+
+               ~lockRAII( )
+               {
+                       pthread_mutex_destroy( &mutex );
+                       pthread_mutexattr_destroy( &mAttr );
+               }
+
+               tstring& getName( )
+               {
+                       return mutexName;
+               }
+
+               void setName( const tstring& name )
+               {
+                       mutexName       = name;
+               }
+
+               void enter( )
+               {
+                       if( debugPrint )
+                       {
+                               tstream.str( _T( "" ) );
+                               tstream << _T( "Attempting pthread_mutex_t( " ) << mutexName << _T( " )" ) << std::endl;
+                               tout << tstream.str( );
+                       }
+
+                       ::pthread_mutex_lock( &mutex );
+
+                       if( debugPrint )
+                       {
+                               tstream.str( _T( "" ) );
+                               tstream << _T( "Acquired pthread_mutex_t( " ) << mutexName << _T( " )" ) << std::endl;
+                               //tstream << _T( "\tOwningThread( " ) << mutex.OwningThread << _T( " )" ) << std::endl;
+                               //tstream << _T( "\tLockcount( " ) << mutex.LockCount << _T( " )" ) << std::endl;
+                               //tstream << _T( "\tRecursionCount( " ) << mutex.RecursionCount << _T( " )" ) << std::endl;
+                               tout << tstream.str( );
+                       }
+               }
+
+               void leave( )
+               {
+                       if( debugPrint )
+                       {
+                               tstream.str( _T( "" ) );
+                               tstream << _T( "Releasing pthread_mutex_t( " ) << mutexName << _T( " )" ) << std::endl;
+                               //tstream << _T( "\tOwningThread( " ) << mutex.OwningThread << _T( " )" ) << std::endl;
+                               //tstream << _T( "\tLockcount( " ) << mutex.LockCount << _T( " )" ) << std::endl;
+                               //tstream << _T( "\tRecursionCount( " ) << mutex.RecursionCount << _T( " )" ) << std::endl << std::endl;
+                               tout << tstream.str( );
+                       }
+
+                       ::pthread_mutex_unlock( &mutex );
+               }
+};
+#endif
+
+//     Class used to make sure that we enter and leave critical sections in pairs
+//     The template logic logs our CRITICAL_SECTION actions; if the template parameter is false,
+//     the branch is constant and the compiler will optimize the branch out
+template< bool debugPrint >
+class scopedLock
+{
+       lockRAII< debugPrint >* sLock;
+       tstring                 sLockName;
+       tstringstream   tstream;
+
+       public:
+               scopedLock( lockRAII< debugPrint >& lock, const tstring& name ): sLock( &lock ), sLockName( name )
+               {
+                       if( debugPrint )
+                       {
+                               tstream.str( _T( "" ) );
+                               tstream << _T( "Entering scopedLock( " ) << sLockName << _T( " )" ) << std::endl << std::endl;
+                               tout << tstream.str( );
+                       }
+
+                       sLock->enter( );
+               }
+
+               ~scopedLock( )
+               {
+                       sLock->leave( );
+
+                       if( debugPrint )
+                       {
+                               tstream.str( _T( "" ) );
+                               tstream << _T( "Left scopedLock( " ) << sLockName << _T( " )" ) << std::endl << std::endl;
+                               tout << tstream.str( );
+                       }
+               }
+};
+
+//     Convenience macro to enable/disable debugging print statements
+#define lockRAII lockRAII< false >
+#define scopedLock scopedLock< false >
+
+#endif // CLFFT_lock_H
diff --git a/src/include/gromacs/external/clFFT/src/library/mainpage.h b/src/include/gromacs/external/clFFT/src/library/mainpage.h
new file mode 100644 (file)
index 0000000..d4aca71
--- /dev/null
@@ -0,0 +1,486 @@
+/* ************************************************************************
+ * Copyright 2013-2015 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+/*! @file mainpage.h
+
+This file contains all documentation, no code, in the form of comment text.  It provides
+chapter 1 of the documentation that is produced with doxygen. Chapter 1 includes the title page, installation instructions, and prose on the nature of FFT and their use in our library.
+
+@mainpage OpenCL Fast Fourier Transforms (FFTs)
+
+The clFFT library is an OpenCL library implementation of discrete Fast Fourier Transforms. The library:
+@li provides a fast and accurate platform for calculating discrete FFTs.
+@li works on CPU or GPU backends.
+@li supports in-place or out-of-place transforms.
+@li supports 1D, 2D, and 3D transforms with a batch size that can be greater than or equal to 1.
+@li supports planar (real and complex components are stored in separate arrays) and interleaved (real and complex components are stored as a pair in the same array) formats.
+@li supports lengths that are any combination of powers of 2, 3, 5, and 7.
+@li supports single and double precision floating point formats.
+
+
+@section IntroFFT Introduction to clFFT
+
+The FFT is an implementation of the Discrete Fourier Transform (DFT) that makes use of symmetries in the FFT definition to reduce the mathematical intensity required from O(\f$N^2\f$) to O(\f$ N \log N\f$) when the sequence length, *N*, is the product of small prime factors.  Currently, there is no standard API for FFT routines. Hardware vendors usually provide a set of high-performance FFTs optimized for their systems: no two vendors employ the same interfaces for their FFT routines. clFFT provides a set of FFT routines that are optimized for AMD graphics processors, and that are also functional across CPU and other compute devices.
+
+@subsection SupportRadix Supported radices
+clFFT supports transform sizes that are powers of 2, 3, 5, and 7. This means that the vector lengths can be  a combination of powers of two, three, five, and seven; examples include \f$2^7, 2^1*3^1, 3^2*5^4, 2^2*3^3*5^5\f$, up to the limit that the device can support.
+
+@subsection SizeLimit Transform size limits
+Currently, there is an upper bound on the transform size that the library can support for certain transforms. This limit is \f$2^{24}\f$ for real 1D single precision and \f$2^{22}\f$ for real 1D double precision.
+
+@subsection EnumDim Dimensionality
+clFFT currently supports FFTs of (up to) three dimensions, given by the enum @ref clfftDim. This enum
+is a required parameter of @ref clfftCreateDefaultPlan() to create an initial plan, where a plan is the collection of (almost) all the parameters needed to specify an FFT computation. For more information about clFFT plans, see the section \ref clFFTPlans.
+Depending on the dimensionality that the client requests, clFFT uses the following formulations to compute the DFT:
+
+@li For a 1D complex DFT
+\f[
+{\tilde{x}}_j = {{1}\over{scale}}\sum_{k=0}^{n-1}x_k\exp\left({\pm i}{{2\pi jk}\over{n}}\right)\hbox{ for } j=0,1,\ldots,n-1
+\f]
+where, \f$x_k\f$ are the complex data to be transformed, \f$\tilde{x}_j\f$ are the transformed data, and the sign \f$\pm\f$ determines the direction of the transform: \f$-\f$ for forward and \f$+\f$ for backward. Note that you must provide the scaling factor.  By default, the scale is set to 1 for forward transforms, and \f${{1}\over{N}}\f$ for backward transforms, where *N* is the size of transform.
+
+@li For a 2D complex DFT
+\f[
+{\tilde{x}}_{jk} = {{1}\over{scale}}\sum_{q=0}^{m-1}\sum_{r=0}^{n-1}x_{rq}\exp\left({\pm i} {{2\pi jr}\over{n}}\right)\exp\left({\pm i}{{2\pi kq}\over{m}}\right)
+\f]
+for \f$j=0,1,\ldots,n-1\hbox{ and } k=0,1,\ldots,m-1\f$, where, \f$x_{rq}\f$ are the complex data to be transformed, \f$\tilde{x}_{jk}\f$ are the transformed data, and the sign \f$\pm\f$ determines the direction of the transform.  By default, the scale is set to 1 for forwards transforms and \f${{1}\over{MN}}\f$ for backwards transforms, where *M* and *N* are the 2D size of the transform.
+
+@li For a 3D complex DFT
+\f[
+\tilde{x}_{jkl} = {{1}\over{scale}}\sum_{s=0}^{p-1}\sum_{q=0}^{m-1}\sum_{r=0}^{n-1}
+x_{rqs}\exp\left({\pm i} {{2\pi jr}\over{n}}\right)\exp\left({\pm i}{{2\pi kq}\over{m}}\right)\exp\left({\pm i}{{2\pi ls}\over{p}}\right)
+\f]
+for \f$j=0,1,\ldots,n-1\hbox{ and } k=0,1,\ldots,m-1\hbox{ and } l=0,1,\ldots,p-1\f$, where \f$x_{rqs}\f$ are the complex data to be transformed, \f$\tilde{x}_{jkl}\f$ are the transformed data, and the sign \f$\pm\f$ determines the direction of the transform. By default, the scale is set to 1 for forward transforms and \f${{1}\over{MNP}}\f$ for backward transforms, where *M*, *N*, and *P* are the 3D size of the transform.
+
+@subsection InitLibrary Setup and Teardown of clFFT
+clFFT is initialized by the API @ref clfftSetup(), which must be called before any other API of
+clFFT. This allows the library to create resources needed to manage the plans that you create and
+destroy. This API also takes a structure @ref clfftInitSetupData() that is initialized by the
+client to control the behavior of the library.
+
+After you use the library, the @ref clfftTeardown() method must be called. This function instructs clFFT to release all resources allocated internally, and resets acquired references to any OpenCL objects.
+
+@subsection ThreadSafety Thread safety
+The clFFT API is designed to be thread-safe. It is safe to create plans from multiple threads and to
+destroy those plans in separate threads. Multiple threads can call @ref clfftEnqueueTransform() to place work in a command queue at the same time. clFFT does not provide a single-threaded version of the library. The overhead of the synchronization mechanisms inside a clFFT thread-safe is expected to be minor.
+
+Currently, you must manage the multi-device operation. You can create OpenCL contexts that are
+associated with multiple devices, but clFFT only uses a single device from that context to transform
+the data. You can manage a multi-device operation by creating multiple contexts, in which each
+context contains a different device; you are responsible for scheduling and partitioning the work
+across multiple devices and contexts.
+
+@subsection MajorFormat Row major formats
+clFFT expects all multi-dimensional input passed to it to be in row-major format. This is compatible
+with C-based languages. However, clFFT is very flexible in the organization of the input and output data, and it accepts input data by letting you specify a stride for each dimension. This feature can be used to process data in column major arrays and other non-contiguous data formats. See @ref clfftSetPlanInStride() and 
+@ref clfftSetPlanOutStride().
+
+@subsection Object OpenCL object creation
+Your application must allocate and manage OpenCL objects, such as contexts,  *cl_mem* buffers and command queues. All the clFFT interfaces that interact with OpenCL objects take those objects as references through the API.
+Specifically, the plan creation function @ref clfftCreateDefaultPlan() takes an OpenCL context as a parameter reference, increments the reference count on that object, and keeps the object alive until the corresponding plan is destroyed by the call @ref clfftDestroyPlan().
+
+@subsection FlushQueue Flushing of command queues
+The clFFT API operates asynchronously; with the exception of thread safety locking with multiple 
+threads, all APIs return immediately. Specifically, the @ref clfftEnqueueTransform() API does not
+explicitly flush the command queues that are passed by reference to it. It pushes the transform work onto the command queues and returns the modified queues to the client. The client is free to issue its own blocking logic by using OpenCL synchronization mechanisms or push further work onto the queue to continue processing.
+
+@subsection EnvVariables Environment variables
+The clFFT library looks for definition of two environment variables: CLFFT_CACHE_PATH and CLFFT_REQUEST_LIB_NOMEMALLOC. 
+If the variable CLFFT_CACHE_PATH is defined, the library caches OpenCL binaries. This enables a subsequent run of the application with the same type of transforms to avoid the expensive compilation step. Instead, the stored binaries are loaded and executed. The CLFFT_CACHE_PATH must point to a folder location where the library can store binaries. 
+The other variable CLFFT_REQUEST_LIB_NOMEMALLOC when defined, requests the library to do all computations in-place and avoid allocating extra device memory whenever possible. This feature is experimental and currently works only for certain types of transforms  when the library decomposes the input into square matrices or rectangular matrices with dimensions in the ratio 1:2. Currently, it works for 1D complex transforms of size of powers of 2. 
+
+@section clFFTPlans clFFT plans
+A plan is the collection of (almost) all the parameters needed to specify an FFT computation.
+A clFFT plan includes the following parameters:
+<ul>
+<li> The OpenCL context that executes the transform
+<li> Dimension of the transform (1D, 2D or 3D)
+<li> Length or extent of data in each dimension
+<li> Number of datasets that are transformed
+<li> Precision of the data
+<li> Scaling factor to the transformed data
+<li> In-place or Out-of-place transform
+<li> Format of the input data - interleaved, planar or real
+<li> Format of the output data - interleaved, planar or real
+</ul>
+
+The clFFT plan does not include the following parameters:
+<ul>
+<li> The OpenCL handles to the input and output data buffers.
+<li> The OpenCL handle to a temporary scratch buffer (if needed).
+<li> Direction of execution of the transform (forward or reverse transform).
+</ul>
+These parameters are specified when the plan is executed.
+
+@subsection Default Default plan values
+
+When a new plan is created by calling @ref clfftCreateDefaultPlan(), its parameters are initialized as
+follows:
+
+<ul>
+<li> Dimensions: as provided by the caller
+<li> Lengths: as provided by the caller
+<li> Batch size: 1
+<li> Precision: *CLFFT_SINGLE*
+<li> Scaling factors:
+    <ol>
+    <li> for the forward transform, the default is 1.0, or no scale factor is applied
+    <li> for the reverse transform, the default is 1.0 / P, where P is the product of the FFT lengths
+    </ol>
+<li> Location: *CLFFT_INPLACE*
+<li> Input layout: *CLFFT_COMPLEX_INTERLEAVED*
+<li> Input strides: the strides of a multidimensional array of the lengths specified, where the data is
+compactly stored using the row-major convention
+<li> Output layout: *CLFFT_COMPLEX_INTERLEAVED*
+<li> Output strides: same as input strides
+</ul>
+
+Writing client programs that depend on these initial values is <b> not </b> recommended.
+
+@subsection EnumLayout Supported memory layouts
+There are two main types of Discrete Fourier Transform (DFT) in clFFT:
+<ol>
+<li> Transformation of complex data - clFFT supports the following two layouts to store complex numbers:
+<ul>
+  <li> Planar format - where the real and imaginary components are kept in separate arrays: \n
+          Buffer1: **RRRRR**  \n
+          Buffer2: **IIIII**
+  <li> Interleaved format - where the real and imaginary components are stored as contiguous pairs:  \n
+          Buffer1: **RIRIRIRIRIRI**
+</ul>
+<li> Transformation of real to complex data and vice versa - clFFT provides enums to define these formats.
+For transforms involving real data, there are two possibilities:
+<ul>
+<li> Real data being subject to forward FFT transform that results in complex data.
+<li> Complex data being subject to backward FFT transform that results in
+real data. See the section \ref RealFFT.
+</ul>
+</ol>
+
+@subsubsection DistanceStridesandPitches Strides and Distances
+For one-dimensional data, if clStrides[0] = strideX = 1, successive elements in the first dimension are stored contiguously in memory. If strideX is an integral value greater than 1, gaps in memory exist between each element of the vectors.
+For multi-dimensional data, if clStrides[1] = strideY = LenX for 2 dimensional data and clStrides[2] = strideZ = LenX*LenY for 3 dimensional data, no gaps exist in memory between each element, and all vectors are stored tightly packed in memory. Here, LenX, LenY, and LenZ denote the transform lengths clLengths[0], clLengths[1], and clLengths[2], respectively, which are used to set up the plan.
+
+By specifying non-default strides, it is possible to process either row-major or column-major arrays. Data can be extracted from arrays of structures. Almost any regular data storage pattern can be accommodated.
+
+Distance is the amount of memory that exists between corresponding elements in an FFT primitive in a batch. Distance is measured in units of the FFT primitive; complex data measures in complex units, and real data measures in real units. Stride between tightly packed elements is 1 in either case. Typically, one can measure the distance between any two elements in a batch primitive, be it 1D, 2D, or 3D data. For tightly packed data, the distance between FFT primitives is the size of the FFT primitive, such that dist=LenX for 1D data, dist=LenX*LenY for 2D data, and dist=LenX*LenY*LenZ for 3D data. It is possible to set the distance of a plan to be less than the size of the FFT vector; most often 1 for this case. When computing a batch of 1D FFT vectors, if distance == 1, and strideX == length(vector), a transposed output is produced for a batch of 1D vectors. You must verify that the distance and strides are valid (not intersecting); if not valid, undefined results may occur.
+A simple example would be to perform a 1D length 4096 on each row of an array of 1024 rows x 4096 columns of values stored in a column-major array, such as a FORTRAN program might provide. (This would be equivalent to a C or C++ program that has an array of 4096 rows x 1024 columns stored in a row-major manner, on which you want to perform a 1-D length 4096 transform on each column.) In this case, specify the strides as [1024, 1].
+
+A more complex example would be to compute a 2D FFT for each 64 x 64 subtile of the grid that has an input buffer with a raster grid of 1024 x 1024 monochrome pixel values. Specifying strides allows you to treat each horizontal band of 1024 x 64 pixels as an array of 16 64 x 64 matrixes, and process an entire band with a single call @ref clfftEnqueueTransform(). (Specifying strides is not quite flexible enough to transform the entire grid of this example with a single kernel execution.) It is possible to create a Plan to compute arrays of 64 x 64 2D FFTs, then specify three strides: [1, 1024, 64]. The first stride, 1, indicates that the rows of each matrix are stored consecutively; the second stride, 1024, gives the distance between rows, and the third stride, 64, defines the distance between two matrices. Then call @ref clfftEnqueueTransform() 16 times – once for each horizontal band of pixels.
+
+@subsection EnumPrecision Supported precisions in clFFT
+Both *CLFFT_SINGLE* and *CLFFT_DOUBLE* precisions are supported by the library for all supported radices. For both these enums the math functions of the host computer are used to produce the sine and cosine tables that are used by the OpenCL kernel.
+Both *CLFFT_SINGLE_FAST* and *CLFFT_DOUBLE_FAST* generate faster kernels with reduced accuracy, but are disabled in the current build.
+See @ref clfftPrecision, @ref clfftSetPlanPrecision(), and @ref clfftGetPlanPrecision().
+
+@subsection FftDirection clfftDirection
+For complex transforms, the direction of the transform is not baked into the plan; the same plan can be used to specify both forward and backward transforms. To specify the direction, @ref clfftDirection is passed as a parameter into @ref clfftEnqueueTransform(). 
+For real transforms, the  input and output layouts of the plan determine the direction.
+
+@subsection EnumResultLocation In-place and out-of-place transforms
+The clFFT API supports both in-place and out-of-place transforms. With in-place transforms, only the input buffers are provided to the @ref clfftEnqueueTransform() API, and the resulting data is written in the same buffer, overwriting the input data. With out-of-place transforms, distinct output buffers are provided to the @ref clfftEnqueueTransform() API, and the input data is preserved. 
+In-place transforms require that the *cl_mem* objects created by the client application, have both read and write permissions. This is given in the nature of the in-place algorithm. Out-of-place transforms require that the destination buffers have read and write permissions, but input buffers can still be created with read-only permissions. This is a clFFT requirement because internally the algorithms may go back and forth between the destination buffers and internally allocated temp buffers. For out-of-place transforms, clFFT never writes back to input buffers.
+
+@subsection clFFTEff Batches
+The efficiency of clFFT is improved by utilizing transforms in batches. Sending as much data as possible in a single transform call leverages the parallel compute capabilities of OpenCL devices (and GPU devices in particular), and minimizes the penalty of transfer overhead. It is best to think of an OpenCL device as a high-throughput, high-latency device. Using a networking analogy as an example, this approach is similar to having a massively high-bandwidth pipe with very high ping response times. If the client is ready to send data to the device for compute, it should be sent in as few API calls as possible and this can be done by batching. clFFT plans have a parameter @ref clfftSetPlanBatchSize() to describe the number of transforms being batched, and another parameter @ref clfftSetPlanDistance() to describe how those batches are laid out and spaced in memory. 1D, 2D, or 3D transforms can be batched.
+
+@section Outline  Using clFFT in a client application
+
+To perform FFT calculations using clFFT, the client program must perform the following tasks:
+<ul>
+       <li> Initialize the library by calling @ref clfftSetup(). </li>
+       <li> For each distinct type of FFT needed:
+       <ol>
+                <li> Create an FFT Plan object. 
+                               To create an FFT Plan object, do either of the following. 
+               <ul>
+                       <li>Call the factory function @ref clfftCreateDefaultPlan() and specify the value 
+                              of the most fundamental parameters, such as plHandle, context, dim, and 
+                              clLengths, while other parameters assume default values.  The 
+                              OpenCL context must be provided when the plan is created; it cannot be 
+                              changed. </li> <br /> Or
+                       <li>Call @ref clfftCopyPlan(). </li>
+               </ul>
+                               Note: In both the cases, the function returns an opaque handle to the plan 
+                       object.
+    <li> Complete the specification of all the Plan parameters by calling various parameter-setting 
+            functions that have the prefix *clfftSet*. </li>
+         <li> Optionally, "bake" or finalize the plan by calling @ref clfftBakePlan() function. This signals 
+                 the library the end of the specification phase, and causes it to generate and compile the 
+                         exact OpenCL kernels that perform the specified FFT on the provided OpenCL device.
+                 At this point, all performance-enhancing optimizations are applied, possibly including 
+                        executing benchmark kernels on the OpenCL device context to maximize the runtime 
+                        performance. <br /> <br />
+Although the previous step is optional, it is recommended to use it so that you can
+have control on when to do this work. Usually, this time consuming step is done when the application is initialized. If you do not call @ref clfftBakePlan(), this work is done during the first call of @ref clfftEnqueueTransform().
+               </li>
+       </ol>
+
+       <li> Execute the OpenCL FFT kernels as many times as needed. </li>
+       <ol>
+               <li>  Call @ref clfftEnqueueTransform(). At this point, specify whether you want to 
+                        execute a forward or reverse transform; also, provide the OpenCL *cl_mem* 
+                        handles for the input buffer(s), output buffer(s)--unless you want the transformed 
+                        data to overwrite the input buffers, and (optionally) scratch buffer.
+                        @ref clfftEnqueueTransform() performs one or more calls to the OpenCL function 
+                         clEnqueueNDRangeKernel. Like clEnqueueNDRangeKernel, @ref 
+                                       clfftEnqueueTransform() is a non-blocking call. The commands to execute the FFT 
+                                       compute kernel(s) are added to the OpenCL context queue to be executed 
+                         asynchronously.
+                        An OpenCL event handle is returned to the caller. If multiple NDRangeKernel 
+                        operations are queued, the final event handle is returned.
+               </li>
+               <li>  Add any application OpenCL tasks to the OpenCL context queue. For example, if the                         next step in the application process is to apply a filter to the transformed data, the 
+                       application calls clEnqueueNDRangeKernel, and specifies its output buffer(s) as the  
+                       input to the filter kernel, and provides the transform's event handle to ensure 
+                       proper synchronization. </li>
+               <li>  If the application accessed the transformed data directly, it calls one of the OpenCL 
+                       functions for synchronizing the host computer execution with the OpenCL device 
+                       (for example: clFinish()). </li>
+       </ol>
+       <li> Terminate the library by calling @ref clfftTeardown().
+</ul>
+
+@section RealFFT  FFTs of real data
+
+When real data is subject to DFT transformation, the resulting complex output data follows a special property. About half of the output is redundant because they are complex conjugates of the other half. This is called the Hermitian redundancy. So, for space and performance considerations, it is only necessary to store the non-redundant part of the data. Most FFT libraries use this property to offer specific storage layouts for FFTs involving real data. clFFT provides three enumerated types to deal with real data FFTs:
+<ul>
+       <li> *CLFFT_REAL*
+       <li> *CLFFT_HERMITIAN_INTERLEAVED*
+       <li> *CLFFT_HERMITIAN_PLANAR*
+</ul>
+
+The *CLFFT_REAL* enum specifies that the data is purely real. This can be used to feed real input or get back real output. The *CLFFT_HERMITIAN_INTERLEAVED* and *CLFFT_HERMITIAN_PLANAR* enums are similar to the corresponding full complex enums in the way they store real and imaginary components, but store only about half of the complex output. Client applications can do just a forward transform and analyze the output or they can process the output and do a backward transform to get back real data. This is illustrated in the following figure.
+
+@image html realfft_fwdinv.jpg "Forward and Backward Transform Processes"
+
+Let us consider a 1D real FFT of length N. The full output looks as shown in following figure.
+
+@image html realfft_1dlen.jpg "1D Real FFT of Length N"
+
+Here, C* denotes the complex conjugate. Since the values at indices greater than N/2 can be deduced from the first half of the array, clFFT stores data only up to the index N/2. This means that the output contains only 1 + N/2 complex elements, where the division N/2 is rounded down. Examples for even
+and odd lengths are given below.
+
+Example for N = 8 is shown in following figure.
+
+@image html realfft_ex_n8.jpg "Example for N = 8"
+
+Example for N = 7 is shown in following figure.
+
+@image html realfft_ex_n7.jpg "Example for N = 7"
+
+
+For length 8, only (1 + 8/2) = 5 of the output complex numbers are stored, with the index ranging from 0 through 4. Similarly for length 7, only (1 + 7/2) = 4 of the output complex numbers are stored, with the index ranging from 0 through 3.
+For 2D and 3D FFTs, the FFT length along the least dimension is used to compute the (1 + N/2) value. This is because the FFT along the least dimension is computed first and is logically a real-to-hermitian transform. The FFTs along other dimensions are computed afterwards; they are simply 'complex-to-complex' transforms. For example, assuming clLengths[2] is used to set up a 2D real FFT, let N1 = clLengths[1], and N0 = clLengths[0]. The output FFT has N1*(1 + N0/2) complex elements. Similarly, for a 3D FFT with clLengths[3] and N2 = clLengths[2], N1 = clLengths[1], and N0 = clLengths[0], the output has N2*N1*(1 + N0/2) complex elements.
+
+@subsection RealModes Supported Modes
+
+Out-of-place transforms:
+
+<ul>
+       <li> *CLFFT_REAL* to *CLFFT_HERMITIAN_INTERLEAVED*
+       <li> *CLFFT_REAL* to *CLFFT_HERMITIAN_PLANAR*
+       <li> *CLFFT_HERMITIAN_INTERLEAVED* to *CLFFT_REAL*
+       <li> *CLFFT_ CLFFT_HERMITIAN_PLANAR* to *CLFFT_REAL*
+</ul>
+
+In-place transforms:
+
+<ul>
+       <li> *CLFFT_REAL* to *CLFFT_HERMITIAN_INTERLEAVED*
+       <li> *CLFFT_HERMITIAN_INTERLEAVED* to *CLFFT_REAL*
+</ul>
+
+@subsection ExplicitStrides Setting strides
+
+The library currently <b> requires you to explicitly set input and output strides for real transforms.</b> See the following examples to understand what values to use for input and output strides under different scenarios. These examples show typical usages, but you can allocate the buffers and layout data according to your need.
+
+@subsection RealExamples Examples
+
+The following pages provide figures and examples to explain in detail the real FFT features of this library.
+
+@image html realfft_expl_01.jpg "1D FFT - Real to Hermitian"
+@image html realfft_expl_02.jpg "1D FFT - Real to Hermitian, Example 1"
+@image html realfft_expl_03.jpg "1D FFT - Real to Hermitian, Example 2"
+@image html realfft_expl_04.jpg "1D FFT - Real to Hermitian, Example 3"
+@image html realfft_expl_05.jpg "1D FFT - Hermitian to Real"
+@image html realfft_expl_06.jpg "1D FFT - Hermitian to Real, Example"
+@image html realfft_expl_07.jpg "2D FFT - Real to Hermitian In Place"
+@image html realfft_expl_08.jpg "2D FFT - Real to Hermitian, Example"
+
+@section Callbacks  clFFT Callbacks
+
+The callback feature of clFFT has the ability to invoke user provided OpenCL™ inline functions to pre-process or post-process data from within the FFT kernel. The inline OpenCL callback function is passed as a string to the library. It is then incorporated into the generated FFT kernel. This eliminates the need for an additional kernel launch to carry out the pre/post processing tasks, thus improving overall performance.
+There are 2 types of callback; Pre-callback and Post-callback. Pre-callback invokes user callback function to perform custom  pre-processing of the input data before FFT is executed. Post-callback invokes user callback function to perform custom post-processing of the output data after FFT is executed.
+
+@subsection CallbackWorkflow Callback Workflow
+
+The workflow of FFT execution using callback feature of clFFT is as follows:
+
+<ol>
+       <li> Create the clFFT Plan and initialize the standard clFFT parameters.
+       <li> Use @ref clfftSetPlanCallback() API to register the callback function with library
+               @code
+               clfftStatus clfftSetPlanCallback(clfftPlanHandle plHandle,
+                                                                                       const char* funcName,
+                                                                                       const char* funcString,
+                                                                                       int localMemSize,
+                                                                                       clfftCallbackType callbackType,
+                                                                                       void *userdata,
+                                                                                       int numUserdataBuffers)
+               @endcode
+               The library uses the arguments passed to this API, including callback function string, to stitch the callback code      into the generated FFT kernel. The arguments for clfftSetPlanCallback are
+               <ul>
+                       <li> clFFT plan handle
+                       <li> Name of the callback function
+                       <li> Callback function as character array; the character array can include custom datatype declaration if any custom type is used by callback function
+                       <li> Size of local memory requested by callback, if any, in bytes
+                       <li> Type of callback: ‘PRECALLBACK’ or ‘POSTCALLBACK’; this is an enumerator
+                       <li> Supplementary user data, if any, used by callback function
+                       <li> Number of user data buffers; the library currently supports only one user data buffer per callback registration
+               </ul>
+               Multiple callback registration calls to the same type of callback result in overwriting the previously registered callback function
+       <li> Invoke Bake Plan step
+       Library inserts the callback code into the main FFT kernel during bake plan and compiles it. If there are any compilation errors caused by syntax or incompatible callback function prototype, the library reports failure.
+       <li> Enqueue clFFT transform
+</ol>
+
+The caller is responsible to provide a callback function that matches the function prototype based on the type of
+callback(pre/post), type of transform(real/complex) and whether LDS is used. The bake plan step checks the function prototype.
+
+@subsection CallbackFunctionPrototype Callback Function Prototypes
+
+clFFT expects the callback function to be of a specific prototype depending on the type of callback(pre/post), type of transform(real/complex) and whether LDS is used. These are as follows:
+
+@subsubsection PrecallbackProtyotype Pre-callback Prototypes
+
+ FFT Type                               | Function Prototype
+----------------------------------------| -------------
+C2C/C2R – Interleaved Single Precision  | Without LDS <br />float2  <precallback_func>  (  __global void *input, uint inoffset, __global void *userdata) <br /> With LDS <br />float2  <precallback_func>  (  __global void *input, uint inoffset, __global void *userdata, __local void *localmem)
+C2C/C2R – Interleaved Double Precision  | Without LDS <br />double2  <precallback_func>  (  __global void *input, uint inoffset, __global void *userdata) <br /> With LDS <br />double2  <precallback_func>  (  __global void *input, uint inoffset, __global void *userdata, __local void *localmem)
+C2C – Planar Single Precision                        | Without LDS <br />float2  <precallback_func>  (  __global void *inputRe, __global void *inputIm, uint inoffset, __global void *userdata)<br /> With LDS <br />float2  <precallback_func>  (  __global void *inputRe, __global void *inputIm, int inoffset, __global void *userdata, __local void *localmem)
+C2C – Planar Double Precision                        | Without LDS <br />double2  <precallback_func>  (  __global void *inputRe, __global void *inputIm, uint inoffset, __global void *userdata)<br /> With LDS <br />double2  <precallback_func>  (  __global void *inputRe, __global void *inputIm, uint inoffset, __global void *userdata, __local void *localmem)
+R2C Single Precision                                   | Without LDS <br />float  <precallback_func>   (  __global void *input, uint inoffset, __global void *userdata)<br /> With LDS <br />float  <precallback_func>   (  __global void *input, uint inoffset, __global void *userdata, __local void *localmem)
+R2C Double Precision                                   | Without LDS <br />double  <precallback_func>   (  __global void *input, uint inoffset, __global void *userdata)<br /> With LDS <br />double  <precallback_func>   (  __global void *input, uint inoffset, __global void *userdata, __local void *localmem)
+
+
+Parameters
+<ul>
+       <li> \c input : The base pointer of the input buffer for R2C and Interleaved C2C/C2R transforms
+       <li> \c inputRe : The base pointer of the “Real” input buffer for Planar C2C transforms
+       <li> \c inputIm : The base pointer of the “Imaginary” part input buffer for Planar C2C transforms
+       <li> \c inoffset : Index of the current element  of the input buffer from the start
+       <li> \c userdata : Buffer containing optional caller specified data. The userdata pointer is useful
+       for passing any supplementary data to the callback function. For example, buffer having convolution
+       filter data or any scalar value. The userdata can be of any custom data type/structure, in which case,
+       you have to declare the custom data type and include it along with the callback function string. </li>
+       <li> \c localmem : Pointer to local memory. This memory is allocated by library based on the size you specify
+       and is subjected to local memory availability. </li>
+</ul>
+
+For Planar C2C, the return type of callback is a vector (float2/double2) that contains the   Real
+and Imaginary elements as computed in the callback.
+
+@subsubsection PostcallbackProtyotype Post-callback Prototypes
+
+ FFT Type                               | Function Prototype
+----------------------------------------| ------------------
+C2C/R2C – Interleaved Single Precision  | Without LDS <br />void  <postcallback_func> ( __global void *output, uint outoffset, __global void *userdata, float2 fftoutput) <br /> With LDS <br />void  <postcallback_func> ( __global void *output, uint outoffset, __global void *userdata, float2 fftoutput, __local void *localmem)
+C2C/R2C – Interleaved Double Precision  | Without LDS <br />void  <postcallback_func> ( __global void *output, uint outoffset, __global void *userdata, double2 fftoutput) <br /> With LDS <br />void  <postcallback_func> ( __global void *output, uint outoffset, __global void *userdata, double2 fftoutput, __local void *localmem)
+C2C/R2C – Planar Single Precision            | Without LDS <br />void  <postcallback_func> ( __global void *outputRe, __global void *outputIm, uint outoffset, __global void *userdata, float fftoutputRe, float fftoutputIm) <br /> With LDS <br />void  <postcallback_func> ( __global void *outputRe, __global void *outputIm, uint outoffset, __global void *userdata, float fftoutputRe, float fftoutputIm, __local void *localmem)
+C2C/R2C – Planar Double Precision            | Without LDS <br />void  <postcallback_func> ( __global void *outputRe, __global void *outputIm, uint outoffset, __global void *userdata, double fftoutputRe, double fftoutputIm) <br /> With LDS <br />void  <postcallback_func> ( __global void *outputRe, __global void *outputIm, uint outoffset, __global void *userdata, double fftoutputRe, double fftoutputIm, __local void *localmem)
+C2R Single Precision                                   | Without LDS <br />void  <postcallback_func> ( __global void *output, uint outoffset, __global void *userdata, float fftoutput) <br /> With LDS <br />void  <postcallback_func> ( __global void *output, uint outoffset, __global void *userdata, float fftoutput, __local void *localmem)
+C2R Double Precision                                   | Without LDS <br />void  <postcallback_func> ( __global void *output, uint outoffset, __global void *userdata, double fftoutput) <br /> With LDS <br />void  <postcallback_func> ( __global void *output, uint outoffset, __global void *userdata, double fftoutput, __local void *localmem)
+
+
+Parameters
+<ul>
+       <li> \c output  : The base pointer of the output buffer for C2R and Interleaved R2C/C2C transforms
+       <li> \c outputRe : The base pointer of the “Real” output buffer for Planar R2C/C2C transforms
+       <li> \c outputIm : The base pointer of the “Imaginary” part output buffer for Planar R2C/C2C transforms
+       <li> \c outoffset : Index of the current element  of the output buffer from the start
+       <li> \c userdata : Buffer containing optional caller specified data. The userdata pointer is useful
+       for passing any supplementary data to the callback function. For example, buffer having convolution
+       filter data or any scalar value. The userdata can be of any custom data type/structure, in which case,
+       you have to declare the custom data type and include it along with the callback function string. </li>
+       <li> \c fftoutput : The result computed by clFFT for the element corresponding to outoffset argument
+       <li> \c localmem : Pointer to local memory. This memory is allocated by library based on the size you specify
+       and is subject to local memory availability. </li>
+</ul>
+
+@subsection SampleCallbackCode Sample Callback Code
+
+@code
+//**************************************************************************
+//* Step 1 : Store the pre and post callback function in a string.
+//**************************************************************************
+const char* precallbackstr = "float2 pre_mulval(__global void* input, \n
+                                  uint inoffset,                      \n
+                                  __global void* userdata,            \n
+                                  __local void* localmem)             \n
+                               {                                                            \n
+                               float scalar = *((__global float*)userdata + inoffset);      \n
+                               float2 ret = *((__global float2*)input + inoffset) * scalar; \n
+                               return ret;                                                  \n
+                               }                                                            \n";
+
+const char* postcallbackstr = "void post_mulval(__global void* output, \n
+                                  uint outoffset,                      \n
+                                  __global void* userdata,             \n
+                                                                 float2 fftoutput,                    \n
+                                  __local void* localmem)              \n
+                               {                                                      \n
+                               float scalar = *((__global float*)userdata + outoffset);      \n
+                               *((__global float2*)output + outoffset) = fftoutput * scalar; \n
+                               }                                                             \n";
+                               
+//**************************************************************************
+//* Step 2 : Initialize arguments, if any, required by the callback.
+//**************************************************************************
+int h_preuserdata[N] = {  };
+cl_mem preuserdata = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(float) * N,  (void*)h_preuserdata, NULL);
+
+int h_postuserdata[N] = {  };
+cl_mem postuserdata = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(float) * N,  (void*)h_postuserdata, NULL);
+
+//**************************************************************************
+//* Step 3 : Register the callback.
+//**************************************************************************
+
+status = clfftSetPlanCallback(plan_handle, "pre_mulval", precallbackstr, 0, PRECALLBACK, &preuserdata, 1);
+
+status = clfftSetPlanCallback(plan_handle, "post_mulval", postcallbackstr, 0, POSTCALLBACK, &postuserdata, 1);
+
+//**************************************************************************
+//* Step 4 : Bake plan and enqueue transform.
+//**************************************************************************
+status = clfftBakePlan( plan_handle, 1, &queue, NULL, NULL );
+
+status = clfftEnqueueTransform( plan_handle, dir, 1, &queue, 0, NULL, &outEvent,
+                       &input_buffers[ 0 ], buffersOut, clMedBuffer );
+@endcode
+
+@subsection CallbackNotes Important Notes on Callback
+
+<ol>
+       <li> The caller is responsible to provide a callback function in string form that matches the function prototype based on the type of callback, type of transform(real/complex), and whether LDS is used.
+       <li> clFFT considers the value returned by the pre-callback function as the new value of the input at the index corresponding to the *inoffset* argument.
+       <li> Callback function can request for local memory for its own use. If the requested amount of local memory is available on the device, clFFT passes a pointer to the local memory when it invokes the callback function.
+       <li> clFFT may invoke the FFT kernels several times depending on the input parameters. However, the pre-callback function provided by the caller is invoked only once for each point in the input. Similarly, it calls the post-callback function only once for each point in the output.
+       <li> If clFFT is implementing a given FFT in multiple phases, it calls the pre-callback function only from the first phase kernel. Similarly, it calls the post-callback function only from the last phase kernel.
+</ol>
+
+ */
diff --git a/src/include/gromacs/external/clFFT/src/library/md5sum.h b/src/include/gromacs/external/clFFT/src/library/md5sum.h
new file mode 100644 (file)
index 0000000..2b93d21
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001.  No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See md5.c for more information.
+ */
+
+#ifndef _MD5_SUM_H
+#define _MD5_SUM_H
+
+#ifdef HAVE_OPENSSL
+#include <openssl/md5.h>
+#else
+
+/* Any 32-bit or wider unsigned integer data type will do */
+typedef unsigned int MD5_u32plus;
+
+typedef struct {
+       MD5_u32plus lo, hi;
+       MD5_u32plus a, b, c, d;
+       unsigned char buffer[64];
+       MD5_u32plus block[16];
+} MD5_CTX;
+
+extern void MD5_Init(MD5_CTX *ctx);
+extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
+extern void MD5_Final(unsigned char *result, MD5_CTX *ctx);
+
+#endif // HAVE_OPENSSL
+
+void md5sum (const void * data, unsigned long size, char * md5sum);
+
+#endif // _MD5_SUM_H
diff --git a/src/include/gromacs/external/clFFT/src/library/plan.h b/src/include/gromacs/external/clFFT/src/library/plan.h
new file mode 100644 (file)
index 0000000..8368e5f
--- /dev/null
@@ -0,0 +1,628 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+#if !defined( AMD_CLFFT_plan_H )
+#define AMD_CLFFT_plan_H
+#include <cstring>
+#include "private.h"
+#include "lock.h"
+#include "generator.h"
+
+std::string getKernelName(const clfftGenerators gen, const clfftPlanHandle plHandle, bool withPlHandle);
+
+namespace ARBITRARY {
+       // TODO:  These arbitrary parameters should be tuned for the type of GPU
+       //      being used.  These values are probably OK for Radeon 58xx and 68xx.
+       enum {
+               MAX_DIMS  = 3,
+                       //  The clEnqueuNDRangeKernel accepts a multi-dimensional domain array.
+                       //  The # of dimensions is arbitrary, but limited by the OpenCL implementation
+                       //  usually to 3 dimensions (CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS).
+                       //  The kernel generator also assumes a limit on the # of dimensions.
+
+               SIMD_WIDTH = 64,
+                       //  Workgroup size.  This is the # of work items that share
+                       //  local data storage (LDS).  This # is best for Evergreen gpus,
+                       //  but might change in the future.
+
+               LDS_BANK_BITS = 5,
+               LDS_BANK_SIZE = (1 << LDS_BANK_BITS),
+               LDS_PADDING   = false,//true,
+                       //  On AMD hardware, the low-order bits of the local_id enumerate
+                       //  the work items that access LDS in parallel.  Ideally, we will
+                       //  pad our LDS arrays so that these work items access different banks
+                       //  of the LDS.
+                       //  2 ** LDS_BANK_BITS is the number of LDS banks.
+                       //  If LDS_PADDING is non-zero, the kernel generator should pad the
+                       //  LDS arrays to reduce or eliminate bank conflicts.
+
+               LDS_FRACTION_IDEAL = 6,    // i.e., 1/6th
+               LDS_FRACTION_MAX   = 4,    // i.e., 1/4
+                       //  For best performance, each workgroup should use 1/IDEAL'th the amount of LDS
+                       //  revealed by clGetDeviceInfo (.. CL_DEVICE_LOCAL_MEM_SIZE, ...)
+                       //  However, we can use up to 1/MAX'th of LDS per workgroup when necessary to
+                       //  perform the FFT in a single pass instead of multiple passes.
+                       //  This tuning parameter is a good value for Evergreen gpus,
+                       //  but might change in the future.
+
+               LDS_COMPLEX = false,
+                       //  This is the default value for FFTKernelGenKeyParams::fft_LdsComplex.
+                       //  The generated kernels require so many bytes of LDS for each single precision
+                       //..complex number in the vector.
+                       //  If LDS_COMPLEX, then we declare an LDS array of complex numbers (8 bytes each)
+                       //  and swap data between workitems with a single barrier.
+                       //  If ! LDS_COMPLEX, then we declare an LDS array or scalar numbers (4 bytes each)
+                       //  and swap data between workitems in two phases, with extra barriers.
+                       //  The former approach uses fewer instructions and barriers;
+                       //  The latter uses half as much LDS space, so twice as many wavefronts can be run
+                       //  in parallel.
+
+               TWIDDLE_DEE = 8,
+                       //  number of bits per row of matrix.
+       };
+
+};
+
+
+enum BlockComputeType
+{
+       BCT_C2C,        // Column to column
+       BCT_C2R,        // Column to row
+       BCT_R2C,        // Row to column
+};
+
+
+//NonSquareKernelType
+enum NonSquareTransposeKernelType
+{
+    NON_SQUARE_TRANS_PARENT,
+    NON_SQUARE_TRANS_TRANSPOSE_BATCHED_LEADING,
+    NON_SQUARE_TRANS_TRANSPOSE_BATCHED,
+    NON_SQUARE_TRANS_SWAP
+};
+
+/*
+There are three ways of conducting inplace transpose with 1:2 (or 2:1) dimension ratio.
+A. first conduct line swapping kernels for the whole non square matrix
+then conduct batched square transpose along column dim (a 'real' batched transpose)
+B. first conduct batched square transpose along column dim (a 'real' batched transpose)
+then conduct line swapping kernels for the whole non square matrix (for 2:1 case)
+C. first conduct batched square transpose along leading dim (row dim)
+then conduct line swapping kernels for the whole non square matrix
+Note that the twiddle computation has to go at the begining of the first kernel or the end of the second kernel
+
+if leading dimension is bigger, it makes more sense (faster) to swap line first and then conduct batched square transpose
+if leading dimension is smaller, it makes more sense (faster) to conduct batched transpose and then swap lines.
+*/
+enum NON_SQUARE_KERNEL_ORDER
+{
+       NOT_A_TRANSPOSE,
+       SWAP_AND_TRANSPOSE, // A.
+       TRANSPOSE_AND_SWAP, // B.
+       TRANSPOSE_LEADING_AND_SWAP, // C.
+};
+
+#define CLFFT_CB_SIZE 32
+#define CLFFT_MAX_INTERNAL_DIM 16
+
+/*! @brief Data structure to store the callback function string and other metadata passed by client 
+*  @details Client sets the callback function and other required parameters through clfftSetPlanCallback() 
+*  in order to register the callback function. The library populates these values into this data structure
+*/ 
+typedef struct clfftCallbackParam_
+{
+       int localMemSize;                       /*!< optional local memory size if needed by callback */
+       const char* funcname;           /*!< callback function name */
+       const char* funcstring;         /*!< callback function in string form */
+}clfftCallbackParam;
+
+struct FFTKernelGenKeyParams {
+       /*
+        *      This structure distills a subset of the fftPlan data,
+        *      including all information that is used to generate the OpenCL kernel.
+        *      This structure can be used as a key to reusing kernels that have already
+        *      been compiled.
+        */
+       size_t                   fft_DataDim;       // Dimensionality of the data
+       size_t                   fft_N[CLFFT_MAX_INTERNAL_DIM];          // [0] is FFT size, e.g. 1024
+                                                   // This must be <= size of LDS!
+       size_t                   fft_inStride [CLFFT_MAX_INTERNAL_DIM];  // input strides
+       size_t                   fft_outStride[CLFFT_MAX_INTERNAL_DIM];  // output strides
+
+       clfftResultLocation   fft_placeness;
+       clfftLayout           fft_inputLayout;
+       clfftLayout           fft_outputLayout;
+       clfftPrecision        fft_precision;
+       double                   fft_fwdScale;
+       double                   fft_backScale;
+
+       size_t                   fft_SIMD;          // Assume this SIMD/workgroup size
+       size_t                   fft_LDSsize;       // Limit the use of LDS to this many bytes.
+       size_t                   fft_R;             // # of complex values to keep in working registers
+                                                   // SIMD size * R must be <= size of LDS!
+
+       size_t                                   fft_MaxWorkGroupSize; // Limit for work group size
+
+       bool                     fft_3StepTwiddle;  // This is one pass of the "3-step" algorithm;
+                                                   // so extra twiddles are applied on output.
+       bool                                     fft_twiddleFront;      // do twiddle scaling at the beginning pass
+
+       bool                                     fft_realSpecial;       // this is the flag to control the special case step (4th step)
+                                                   // in the 5-step real 1D large breakdown
+       size_t                                   fft_realSpecial_Nr;
+
+       bool                     fft_RCsimple; 
+
+       bool                                     transOutHorizontal;    // tiles traverse the output buffer in horizontal direction
+
+       bool                                     blockCompute;
+       BlockComputeType                 blockComputeType;
+       size_t                                   blockSIMD;
+       size_t                                   blockLDS;
+    
+       NonSquareTransposeKernelType      nonSquareKernelType;
+       // sometimes non square matrix are broken down into a number of
+       // square matrix during inplace transpose
+       // let's call this number transposeMiniBatchSize
+       // no user of the library should set its value
+       size_t transposeMiniBatchSize;
+       // transposeBatchSize is the number of batchs times transposeMiniBatchSzie
+       // no user of the library should set its value
+       size_t transposeBatchSize;
+       // no user of the library should set its value 
+       NON_SQUARE_KERNEL_ORDER nonSquareKernelOrder;
+
+       bool fft_hasPreCallback;
+       clfftCallbackParam fft_preCallback;
+
+       bool fft_hasPostCallback;
+       clfftCallbackParam fft_postCallback;
+
+       cl_ulong   limit_LocalMemSize;
+
+       // Default constructor
+       FFTKernelGenKeyParams()
+       {
+               fft_DataDim = 0;
+               for(int i=0; i<CLFFT_MAX_INTERNAL_DIM; i++)
+               {
+                       fft_N[i] = 0;
+                       fft_inStride[i] = 0;
+                       fft_outStride[i] = 0;
+               }
+
+               fft_placeness = CLFFT_OUTOFPLACE;
+               fft_inputLayout = CLFFT_COMPLEX_INTERLEAVED;
+               fft_outputLayout = CLFFT_COMPLEX_INTERLEAVED;
+               fft_precision = CLFFT_SINGLE;
+               fft_fwdScale = fft_backScale = 0.0;
+               fft_SIMD = 0;
+               fft_LDSsize = 0;
+               fft_R = 0;
+               fft_MaxWorkGroupSize = 0;
+               fft_3StepTwiddle = false;
+               fft_twiddleFront = false;
+
+               transOutHorizontal = false;
+
+               fft_realSpecial = false;
+               fft_realSpecial_Nr = 0;
+
+               fft_RCsimple = false;
+
+               blockCompute = false;
+               blockComputeType = BCT_C2C;
+               blockSIMD = 0;
+               blockLDS = 0;
+        nonSquareKernelType = NON_SQUARE_TRANS_PARENT;
+               transposeMiniBatchSize = 1;
+               transposeBatchSize = 1;
+               fft_hasPreCallback = false;
+               fft_hasPostCallback = false;
+               limit_LocalMemSize = 0;
+       }
+};
+
+
+//     Sorting operator for struct FFTKernelGenKeyParams, such that it can be used in a map
+bool operator<( const FFTKernelGenKeyParams& lhs, const FFTKernelGenKeyParams& rhs);
+
+class  FFTPlan;
+class   FFTRepo;
+
+// Action ID
+enum FFTActionImplID
+{
+    FFT_DEFAULT_STOCKHAM_ACTION,
+    FFT_DEFAULT_TRANSPOSE_ACTION,
+    FFT_DEFAULT_COPY_ACTION,
+    FFT_STATIC_STOCKHAM_ACTION
+};
+
+
+// 
+// FFTKernelSignatureHeader
+// 
+// This structure is a wrapper for the FFTKernelSignature.  
+// It stores the signature size and the action ID. This ensure that every 
+// FFTKernelSignature (even with an empty DATA) is unique
+// 
+// This class is used as the return type of FFTAction::getSignatureData()
+// 
+struct FFTKernelSignatureHeader
+{
+    int datasize;
+    FFTActionImplID id;
+
+    //clfftLayout           fft_inputLayout;
+    //clfftLayout           fft_outputLayout;
+
+    FFTKernelSignatureHeader(int size_, FFTActionImplID id_)
+    {
+        // Set to 0 the whole signature structure
+        ::memset(this, 0, size_);
+        datasize = size_;
+        id = id_;
+    }
+};
+
+// 
+// FFTKernelSignature
+// 
+// This struct represents the signature of an action. An action signature 
+// stores (by inheritage):
+//  - the action ID
+//  - its signature data size
+//  - a set of bytes caracterizes a FFT action
+// 
+// This template class FFTKernelSignature provides a simple mechanism to
+// build a proper signature (see also in src/library/repo.h) from any POD type.
+//  
+// It is used as a key in the different cache mecanisms (binary cache and
+// dynamic cache managed by FFTRepo)
+// 
+template <typename DATA, FFTActionImplID ID>
+struct FFTKernelSignature : public FFTKernelSignatureHeader, public DATA
+{
+    FFTKernelSignature()
+        : FFTKernelSignatureHeader(sizeof(FFTKernelSignature<DATA, ID>), ID)
+    {
+    }
+};
+
+
+
+// 
+// FFTAction is the base class for all actions used to implement FFT computes
+// 
+// An action basically implements some OpenCL related actions, for instance:
+//  - Generating a kernel source code from kernel characteristics
+//  - Computing the work-group local sizes according to a kernel
+//  - Enqueuing arguments to the kernel call
+// 
+// Kernels generated and compiled by an action are stored in the different
+// cache mechanism (see repo.h for the dynamic cache and fft_binary_lookup.h
+// for disk cache for more information). Each cache entry can be obtained from
+// the action signature which is set of byte characterizing the action itself.
+// 
+// At the moment, FFTAction only implements the enqueue method which is
+// inherited by every action subclasses. But it may change in the time. There
+// are no clear rules and the choices made until now are still subject to
+// change.
+// 
+class FFTAction
+{
+public:
+    FFTAction(FFTPlan * plan, clfftStatus & err);
+
+    virtual clfftStatus enqueue(clfftPlanHandle plHandle,
+                                clfftDirection dir,
+                                cl_uint numQueuesAndEvents,
+                                cl_command_queue* commQueues,
+                                cl_uint numWaitEvents,
+                                const cl_event* waitEvents,
+                                cl_event* outEvents,
+                                cl_mem* clInputBuffers,
+                                cl_mem* clOutputBuffers);
+
+protected:
+
+    virtual clfftGenerators                getGenerator() = 0;
+
+    clfftStatus                            compileKernels  ( const cl_command_queue commQueueFFT, const clfftPlanHandle plHandle, FFTPlan* fftPlan);
+    clfftStatus                            writeKernel     ( const clfftPlanHandle plHandle, const clfftGenerators gen, const FFTKernelSignatureHeader* data, const cl_context& context, const cl_device_id &device);
+
+    virtual clfftStatus                    generateKernel  ( FFTRepo & fftRepo, const cl_command_queue commQueueFFT) = 0;
+    virtual clfftStatus                    getWorkSizes    ( std::vector<size_t> & globalws, std::vector<size_t> & localws) = 0;
+
+    virtual const FFTKernelSignatureHeader * getSignatureData() = 0;
+
+    FFTPlan * plan;
+
+private:
+
+    clfftStatus selectBufferArguments(FFTPlan * plan,
+                                      cl_mem* clInputBuffers,
+                                      cl_mem* clOutputBuffers,
+                                      std::vector< cl_mem > &inputBuff,
+                                      std::vector< cl_mem > &outputBuff);
+
+    virtual bool buildForwardKernel() = 0;
+    virtual bool buildBackwardKernel() = 0;
+};
+
+
+//     The "envelope" is a set of limits imposed by the hardware
+//     This will depend on the GPU(s) in the OpenCL context.
+//     If there are multiple devices, this should be the least
+//     common denominators.
+//
+struct FFTEnvelope {
+       cl_ulong   limit_LocalMemSize;
+                  //  this is the minimum of CL_DEVICE_LOCAL_MEM_SIZE
+       size_t     limit_Dimensions;
+                  //  this is the minimum of CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS
+       size_t     limit_Size[8];
+                  //  these are the minimima of CL_DEVICE_MAX_WORK_ITEM_SIZES[0..n]
+       size_t     limit_WorkGroupSize;
+                  //  this is the minimum of CL_DEVICE_MAX_WORK_GROUP_SIZE
+
+       // ??  CL_KERNEL_PREFERRED_WORK_GROUP_SIZE_MULTIPLE
+
+       FFTEnvelope ()
+       :       limit_LocalMemSize (0)
+       ,       limit_Dimensions (0)
+       ,       limit_WorkGroupSize (0)
+       {
+               ::memset( &limit_Size, 0, sizeof( limit_Size ) );
+       }
+};
+
+
+//     This class contains objects that are specific to a particular FFT transform, and the data herein is useful
+//     for us to know ahead of transform time such that we can optimize for these settings
+class  FFTPlan
+{
+
+public:
+
+       bool baked;
+
+       //      Properties provided by the user.
+       clfftDim             dim;
+       clfftLayout          inputLayout;
+       clfftLayout          outputLayout;
+       clfftResultLocation  placeness;
+       clfftResultTransposed transposed;
+       clfftPrecision       precision;
+       cl_context              context;
+       double                  forwardScale, backwardScale;
+       size_t                  iDist, oDist;
+       size_t                  batchsize;
+
+       // Note the device passed to BakePlan, assuming we are baking for one device
+       // TODO, change this logic for handling multiple GPUs/devices
+       cl_device_id bakeDevice;
+
+       // Disabling devices member, plan has 1-on-1 mapping with single device as identified by bakeDevice
+       //      Devices that the user specified in the context passed to the create function
+       // std::vector< cl_device_id > devices;
+
+       //      Length of the FFT in each dimension
+       std::vector< size_t >   length;
+
+       //      Stride of the FFT in each dimension
+       std::vector< size_t >   inStride, outStride;
+
+       //      Hardware Limits
+       FFTEnvelope                 envelope;
+
+
+       // Reserved copy for large 1d, 2d, and 3d plan
+       size_t tmpBufSize;
+       cl_mem intBuffer;
+       bool libCreatedIntBuffer;
+
+       // for RC copies
+       size_t  tmpBufSizeRC;
+       cl_mem  intBufferRC;
+
+       // for C-to-R transforms that are OUTOFPLACE
+       // we need this because the user supplied output buffer is not big enough
+       // to hold intermediate results for any problem other than normal 1D
+       size_t  tmpBufSizeC2R;
+       cl_mem  intBufferC2R;
+
+
+       size_t  large1D;
+       bool    large2D;
+       bool    twiddleFront;
+
+       clfftPlanHandle planX;
+       clfftPlanHandle planY;
+       clfftPlanHandle planZ;
+
+       bool transflag;
+       bool transOutHorizontal;
+       clfftPlanHandle planTX;
+       clfftPlanHandle planTY;
+       clfftPlanHandle planTZ; //reserve for 3D transpose
+
+       clfftPlanHandle planRCcopy;
+       clfftPlanHandle planCopy;
+
+       // Plan resources
+       //
+       cl_mem const_buffer;
+
+       // Generator type
+       clfftGenerators gen;
+
+
+       // Real-Complex simple flag
+       // if this is set we do real to-and-from full complex using simple algorithm
+       // where imaginary of input is set to zero in forward and imaginary not written in backward
+       bool RCsimple;
+
+       // Real FFT special flag
+       // if this is set it means we are doing the 4th step in the 5-step real FFT breakdown algorithm
+       bool realSpecial;
+       
+       size_t realSpecial_Nr; // this value stores the logical column height (N0) of matrix in the 4th step
+                              // length[1] should be 1 + N0/2
+
+       // User created plan
+       bool userPlan;
+
+
+       // Allocate no extra memory
+       bool allOpsInplace;
+
+       // flag to indicate transpose placeness in 2D breakdown
+       bool transpose_in_2d_inplace;
+
+
+       // A flag to say that blocked FFTs are going to be performed
+       // It can only be one of these: column to row, row to column or column to column
+       // row to row is just the normal case where blocking is not needed
+       bool blockCompute;
+       BlockComputeType blockComputeType;
+
+       bool hasPreCallback;
+       bool hasPostCallback;
+
+       clfftCallbackParam preCallback;
+       clfftCallbackParam postCallbackParam;
+
+       cl_mem precallUserData;
+       cl_mem postcallUserData;
+
+    clfftPlanHandle plHandle;
+
+    // The action
+    FFTAction * action;
+
+    NonSquareTransposeKernelType nonSquareKernelType;
+       // sometimes non square matrix are broken down into a number of
+       // square matrix during inplace transpose
+       // let's call this number transposeMiniBatchSize
+       // no user of the library should set its value
+       size_t transposeMiniBatchSize;
+       NON_SQUARE_KERNEL_ORDER nonSquareKernelOrder;
+
+       FFTPlan ()
+       :       baked (false)
+       ,       dim (CLFFT_1D)
+       ,       inputLayout (CLFFT_COMPLEX_INTERLEAVED)
+       ,       outputLayout (CLFFT_COMPLEX_INTERLEAVED)
+       ,       placeness (CLFFT_INPLACE)
+       ,   transposed (CLFFT_NOTRANSPOSE)
+       ,       precision (CLFFT_SINGLE)
+       ,       context (NULL)
+       ,       forwardScale (1.0)
+       ,       backwardScale (1.0)
+       ,       iDist( 1 ), oDist( 1 )
+       ,       batchsize (1)
+       ,   tmpBufSize (0)
+       ,       intBuffer( NULL )
+       ,       libCreatedIntBuffer(false)
+       ,       tmpBufSizeRC (0)
+       ,       intBufferRC( NULL )
+       ,       tmpBufSizeC2R (0)
+       ,       intBufferC2R( NULL )
+       ,   large1D(0)
+       ,   large2D(false)
+       ,       twiddleFront(false)
+       ,   planX( 0 )
+       ,   planY( 0 )
+       ,   planZ( 0 )
+       ,   transflag(false)
+       ,       transOutHorizontal(false)
+       ,       RCsimple(false)
+       ,       realSpecial(false)
+       ,       realSpecial_Nr(0)
+       ,       userPlan(false)
+       ,       allOpsInplace(false)
+       ,       transpose_in_2d_inplace(false)
+       ,       blockCompute(false)
+       ,       blockComputeType(BCT_C2C)
+       ,   planTX( 0 )
+       ,   planTY( 0 )
+       ,   planTZ( 0 )
+       ,       planRCcopy(0)
+       ,       planCopy(0)
+       ,       const_buffer( NULL )
+       ,       gen(Stockham)
+    ,   action(0)
+    ,   nonSquareKernelType(NON_SQUARE_TRANS_PARENT)
+       ,   transposeMiniBatchSize(1)
+       ,   nonSquareKernelOrder(NOT_A_TRANSPOSE)
+    ,   plHandle(0)
+       ,   hasPreCallback(false)
+       ,   hasPostCallback(false)
+       {
+       };
+
+
+       size_t ElementSize() const;
+
+       clfftStatus AllocateBuffers ();
+       clfftStatus ReleaseBuffers ();
+
+       clfftStatus GetMax1DLength (size_t *longest ) const;
+
+       clfftStatus ConstructAndEnqueueConstantBuffers( cl_command_queue* commQueueFFT );
+
+       clfftStatus GetEnvelope (const FFTEnvelope **) const;
+       clfftStatus SetEnvelope ();
+
+       clfftStatus GetMax1DLengthStockham (size_t *longest ) const;
+
+       ~FFTPlan ()
+       {
+               ReleaseBuffers ();
+
+               if (action != NULL)
+               {
+                       delete action;
+                       action = 0;
+               }
+       }
+};
+
+static bool Is1DPossible(size_t length, size_t large1DThreshold)
+{
+       if (length > large1DThreshold)
+               return false;
+
+       if ( (length%7 == 0) && (length%5 == 0) && (length%3 == 0) )
+               return false;
+
+       // radix 11 & 2 is ok, anything else we cannot do in 1 kernel
+       if ( (length % 11 == 0) && ((length % 13 == 0) || (length % 7 == 0) || (length % 5 == 0) || (length % 3 == 0)) )
+               return false;
+       
+       // radix 13 & 2 is ok, anything else we cannot do in 1 kernel
+       if ( (length % 13 == 0) && ((length % 11 == 0) || (length % 7 == 0) || (length % 5 == 0) || (length % 3 == 0)) )
+               return false;
+
+       return true;
+}
+
+#endif // AMD_CLFFT_plan_H
+
diff --git a/src/include/gromacs/external/clFFT/src/library/private.h b/src/include/gromacs/external/clFFT/src/library/private.h
new file mode 100644 (file)
index 0000000..48f2fa5
--- /dev/null
@@ -0,0 +1,374 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+#if !defined( CLFFT_private_H )
+#define CLFFT_private_H
+
+#include <vector>
+#include <string>
+#include <locale>
+#include <iostream>
+#include <sstream>
+#include <stdexcept>
+#include <cassert>
+#include "../include/clFFT.h"
+#include "../include/unicode.compatibility.h"
+
+#if defined(_MSC_VER)
+       //      Microsoft Visual C++ compiler
+       //
+#define SPRINTF(_buffer, _count, _format,...)                        \
+       _snprintf_s (_buffer, _count, _TRUNCATE, _format, __VA_ARGS__)
+#elif defined( __GNUC__ )
+       //      Gnu G++
+       //
+#define SPRINTF(_buffer, _count, _format,...)                   \
+       {       size_t len = (_count)-1;                                \
+               snprintf (_buffer, len, _format,__VA_ARGS__);           \
+               _buffer[len] = 0;                                       \
+       }
+#else
+#error Unknown/unsupported C++ compiler.
+#endif
+
+//     Creating a portable defintion of countof
+//  This excludes mingw compilers; mingw32 does not have _countof
+#if defined( _MSC_VER )
+       #define countOf _countof
+#else
+       #define countOf( arr ) ( sizeof( arr ) / sizeof( arr[ 0 ] ) )
+#endif
+
+// This excludes mingw compilers; mingw32 does not have <intrin.h>
+#if defined( _MSC_VER )
+       #include <intrin.h>
+
+       #if defined( _WIN64 )
+               inline void BSF( unsigned long* index, size_t& mask )
+               {
+                       _BitScanForward64( index, mask );
+               }
+
+               inline size_t AtomicAdd( volatile size_t* value, size_t op )
+               {
+                       return _InterlockedExchangeAdd64( reinterpret_cast< volatile __int64* >( value ), op );
+               }
+       #else
+               inline void BSF( unsigned long* index, size_t& mask )
+               {
+                       _BitScanForward( index, mask );
+               }
+
+               inline size_t AtomicAdd( volatile size_t* value, size_t op )
+               {
+                       return _InterlockedExchangeAdd( reinterpret_cast< volatile long* >( value ), op );
+               }
+       #endif
+#elif defined( __GNUC__ )
+       inline void BSF( unsigned long * index, size_t & mask )
+       {
+               *index = __builtin_ctz( mask );
+       }
+
+       inline size_t AtomicAdd( volatile size_t* value, size_t op )
+       {
+               return __sync_fetch_and_add( value, op );
+       }
+#endif
+
+void clfftInitRequestLibNoMemAlloc();
+bool clfftGetRequestLibNoMemAlloc();
+
+void clfftInitBinaryCache();
+
+//     This header file is not visible to clients, and contains internal structures and functions for use
+//     by the FFT library.  Since this header is private to this implementation, there is no need to keep
+//     strict C compliance.
+
+//     Enum to help provide descriptive names to array indices, when indexing into our various vectors
+enum clfftDim_Index
+{
+       DimX,                           ///< 1 Dimension
+       DimY,                           ///< 2 Dimension
+       DimZ,                           ///< 3 Dimension
+       DimW,                           ///< 4th Dimension
+       ENDDIMINDEX                     ///< This value will always be last, and marks the length of clfftDim_Index
+};
+
+template< typename FileStreamType, typename StringType >
+class tofstreamRAII
+{
+       FileStreamType  outFile;
+       StringType              fileName;
+
+       public:
+               tofstreamRAII( const StringType& name ): fileName( name )
+               {
+                       outFile.open( fileName.c_str( ) );
+               }
+
+               ~tofstreamRAII( )
+               {
+                       outFile.close( );
+               }
+
+               StringType& getName( )
+               {
+                       return fileName;
+               }
+
+               void setName( const StringType& name )
+               {
+                       fileName = name;
+               }
+
+               FileStreamType& get( )
+               {
+                       return outFile;
+               }
+};
+
+//(currently) true if length is a power of 2,3,5,7,11,13
+inline bool IsASupportedLength( size_t length )
+{
+       while( length > 1 )
+       {
+               if( length % 2 == 0 )
+                       length /= 2;
+               else if( length % 3 == 0 )
+                       length /= 3;
+               else if( length % 5 == 0 )
+                       length /= 5;
+               else if( length % 7 == 0 )
+                       length /= 7;
+               else if (length % 11 == 0)
+                       length /= 11;
+               else if (length % 13 == 0)
+                       length /= 13;
+               else
+                       return false;
+       }
+       return true;
+}
+
+inline tstring clfftErrorStatusAsString( const cl_int& status )
+{
+       switch( status )
+       {
+               case CLFFT_INVALID_GLOBAL_WORK_SIZE:
+                       return _T( "CLFFT_INVALID_GLOBAL_WORK_SIZE" );
+               case CLFFT_INVALID_MIP_LEVEL:
+                       return _T( "CLFFT_INVALID_MIP_LEVEL" );
+               case CLFFT_INVALID_BUFFER_SIZE:
+                       return _T( "CLFFT_INVALID_BUFFER_SIZE" );
+               case CLFFT_INVALID_GL_OBJECT:
+                       return _T( "CLFFT_INVALID_GL_OBJECT" );
+               case CLFFT_INVALID_OPERATION:
+                       return _T( "CLFFT_INVALID_OPERATION" );
+               case CLFFT_INVALID_EVENT:
+                       return _T( "CLFFT_INVALID_EVENT" );
+               case CLFFT_INVALID_EVENT_WAIT_LIST:
+                       return _T( "CLFFT_INVALID_EVENT_WAIT_LIST" );
+               case CLFFT_INVALID_GLOBAL_OFFSET:
+                       return _T( "CLFFT_INVALID_GLOBAL_OFFSET" );
+               case CLFFT_INVALID_WORK_ITEM_SIZE:
+                       return _T( "CLFFT_INVALID_WORK_ITEM_SIZE" );
+               case CLFFT_INVALID_WORK_GROUP_SIZE:
+                       return _T( "CLFFT_INVALID_WORK_GROUP_SIZE" );
+               case CLFFT_INVALID_WORK_DIMENSION:
+                       return _T( "CLFFT_INVALID_WORK_DIMENSION" );
+               case CLFFT_INVALID_KERNEL_ARGS:
+                       return _T( "CLFFT_INVALID_KERNEL_ARGS" );
+               case CLFFT_INVALID_ARG_SIZE:
+                       return _T( "CLFFT_INVALID_ARG_SIZE" );
+               case CLFFT_INVALID_ARG_VALUE:
+                       return _T( "CLFFT_INVALID_ARG_VALUE" );
+               case CLFFT_INVALID_ARG_INDEX:
+                       return _T( "CLFFT_INVALID_ARG_INDEX" );
+               case CLFFT_INVALID_KERNEL:
+                       return _T( "CLFFT_INVALID_KERNEL" );
+               case CLFFT_INVALID_KERNEL_DEFINITION:
+                       return _T( "CLFFT_INVALID_KERNEL_DEFINITION" );
+               case CLFFT_INVALID_KERNEL_NAME:
+                       return _T( "CLFFT_INVALID_KERNEL_NAME" );
+               case CLFFT_INVALID_PROGRAM_EXECUTABLE:
+                       return _T( "CLFFT_INVALID_PROGRAM_EXECUTABLE" );
+               case CLFFT_INVALID_PROGRAM:
+                       return _T( "CLFFT_INVALID_PROGRAM" );
+               case CLFFT_INVALID_BUILD_OPTIONS:
+                       return _T( "CLFFT_INVALID_BUILD_OPTIONS" );
+               case CLFFT_INVALID_BINARY:
+                       return _T( "CLFFT_INVALID_BINARY" );
+               case CLFFT_INVALID_SAMPLER:
+                       return _T( "CLFFT_INVALID_SAMPLER" );
+               case CLFFT_INVALID_IMAGE_SIZE:
+                       return _T( "CLFFT_INVALID_IMAGE_SIZE" );
+               case CLFFT_INVALID_IMAGE_FORMAT_DESCRIPTOR:
+                       return _T( "CLFFT_INVALID_IMAGE_FORMAT_DESCRIPTOR" );
+               case CLFFT_INVALID_MEM_OBJECT:
+                       return _T( "CLFFT_INVALID_MEM_OBJECT" );
+               case CLFFT_INVALID_HOST_PTR:
+                       return _T( "CLFFT_INVALID_HOST_PTR" );
+               case CLFFT_INVALID_COMMAND_QUEUE:
+                       return _T( "CLFFT_INVALID_COMMAND_QUEUE" );
+               case CLFFT_INVALID_QUEUE_PROPERTIES:
+                       return _T( "CLFFT_INVALID_QUEUE_PROPERTIES" );
+               case CLFFT_INVALID_CONTEXT:
+                       return _T( "CLFFT_INVALID_CONTEXT" );
+               case CLFFT_INVALID_DEVICE:
+                       return _T( "CLFFT_INVALID_DEVICE" );
+               case CLFFT_INVALID_PLATFORM:
+                       return _T( "CLFFT_INVALID_PLATFORM" );
+               case CLFFT_INVALID_DEVICE_TYPE:
+                       return _T( "CLFFT_INVALID_DEVICE_TYPE" );
+               case CLFFT_INVALID_VALUE:
+                       return _T( "CLFFT_INVALID_VALUE" );
+               case CLFFT_MAP_FAILURE:
+                       return _T( "CLFFT_MAP_FAILURE" );
+               case CLFFT_BUILD_PROGRAM_FAILURE:
+                       return _T( "CLFFT_BUILD_PROGRAM_FAILURE" );
+               case CLFFT_IMAGE_FORMAT_NOT_SUPPORTED:
+                       return _T( "CLFFT_IMAGE_FORMAT_NOT_SUPPORTED" );
+               case CLFFT_IMAGE_FORMAT_MISMATCH:
+                       return _T( "CLFFT_IMAGE_FORMAT_MISMATCH" );
+               case CLFFT_MEM_COPY_OVERLAP:
+                       return _T( "CLFFT_MEM_COPY_OVERLAP" );
+               case CLFFT_PROFILING_INFO_NOT_AVAILABLE:
+                       return _T( "CLFFT_PROFILING_INFO_NOT_AVAILABLE" );
+               case CLFFT_OUT_OF_HOST_MEMORY:
+                       return _T( "CLFFT_OUT_OF_HOST_MEMORY" );
+               case CLFFT_OUT_OF_RESOURCES:
+                       return _T( "CLFFT_OUT_OF_RESOURCES" );
+               case CLFFT_MEM_OBJECT_ALLOCATION_FAILURE:
+                       return _T( "CLFFT_MEM_OBJECT_ALLOCATION_FAILURE" );
+               case CLFFT_COMPILER_NOT_AVAILABLE:
+                       return _T( "CLFFT_COMPILER_NOT_AVAILABLE" );
+               case CLFFT_DEVICE_NOT_AVAILABLE:
+                       return _T( "CLFFT_DEVICE_NOT_AVAILABLE" );
+               case CLFFT_DEVICE_NOT_FOUND:
+                       return _T( "CLFFT_DEVICE_NOT_FOUND" );
+               case CLFFT_SUCCESS:
+                       return _T( "CLFFT_SUCCESS" );
+               case CLFFT_NOTIMPLEMENTED:
+                       return _T( "CLFFT_NOTIMPLEMENTED" );
+               case CLFFT_FILE_NOT_FOUND:
+                       return _T( "CLFFT_FILE_NOT_FOUND" );
+               case CLFFT_FILE_CREATE_FAILURE:
+                       return _T( "CLFFT_FILE_CREATE_FAILURE" );
+               case CLFFT_VERSION_MISMATCH:
+                       return _T( "CLFFT_VERSION_MISMATCH" );
+               case CLFFT_INVALID_PLAN:
+                       return _T( "CLFFT_INVALID_PLAN" );
+               default:
+                       return _T( "Error code not defined" );
+               break;
+       }
+}
+
+//     This is used to either wrap an OpenCL function call, or to explicitly check a variable for an OpenCL error condition.
+//     If an error occurs, we issue a return statement to exit the calling function.
+#if defined( _DEBUG )
+
+#define OPENCL_V( fn, msg ) \
+{ \
+       clfftStatus vclStatus = static_cast< clfftStatus >( fn ); \
+       switch( vclStatus ) \
+       { \
+               case    CL_SUCCESS:             /**< No error */ \
+                       break; \
+               default: \
+               { \
+                       terr << _T( "OPENCL_V< " ); \
+                       terr << clfftErrorStatusAsString( vclStatus ); \
+                       terr << _T( " > (" )<< static_cast<unsigned>( __LINE__ ) << _T( "): " ); \
+                       terr << msg << std::endl; \
+                       return  vclStatus; \
+               } \
+       } \
+}
+
+#else
+
+#define OPENCL_V( fn, msg ) \
+{ \
+       clfftStatus vclStatus = static_cast< clfftStatus >( fn ); \
+       switch( vclStatus ) \
+       { \
+               case    CL_SUCCESS:             /**< No error */ \
+                       break; \
+               default: \
+               { \
+                       return  vclStatus; \
+               } \
+       } \
+}
+#endif
+
+static inline bool IsPo2 (size_t u) {
+       return (u != 0) &&  (0 == (u & (u-1)));
+}
+
+template<typename T>
+static inline T DivRoundingUp (T a, T b) {
+       return (a + (b-1)) / b;
+}
+
+static inline size_t BitScanF (size_t n) {
+       assert (n != 0);
+       unsigned long tmp = 0;
+       BSF (& tmp, n);
+       return (size_t) tmp;
+}
+
+#define ARG_CHECK(_proposition)        \
+{ bool btmp = (_proposition);  assert (btmp); if (! btmp)      return CLFFT_INVALID_ARG_VALUE; }
+
+#define BUG_CHECK(_proposition)        \
+       { bool btmp = (_proposition);   assert (btmp); if (! btmp)      return CLFFT_BUGCHECK; }
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+CLFFTAPI clfftStatus clfftLocalMemSize( const clfftPlanHandle plHandle, cl_ulong* local_mem_size );
+
+/*! @brief Save to disk a file containing the contents of a baked plan.
+*  @details A plan is a repository of state for calculating FFT's. Saves the details for a plan to allow the user
+*      to easily recreate a plan and execute it without having to first build the kernel.
+*  @param[in] plHandle Handle to the plan to be written to disk
+*  @param[in] filename The desired name of the output file for the stored plan
+*  @return Enum describing error condition; superset of OpenCL error codes
+*/
+CLFFTAPI clfftStatus   clfftWritePlanToDisk( clfftPlanHandle plHandle, const char* filename );
+
+/*! @brief Read from disk a file containing the contents of a baked plan.
+*  @details A plan is a repository of state for calculating FFT's. Reads the details for a plan from a file on disk and duplicates
+*      the plan in the provided plan handle.
+*  @param[out] plHandle Handle to the plan to be set to details from the file
+*  @param[in] filename The name of the file containing the stored plan
+*  @return Enum describing error condition; superset of OpenCL error codes
+*/
+CLFFTAPI clfftStatus   clfftReadPlanFromDisk( clfftPlanHandle plHandle, const char* filename );
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/include/gromacs/external/clFFT/src/library/repo.h b/src/include/gromacs/external/clFFT/src/library/repo.h
new file mode 100644 (file)
index 0000000..bb81f11
--- /dev/null
@@ -0,0 +1,234 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+#if !defined( CLFFT_repo_H )
+#define CLFFT_repo_H
+#include <map>
+#include "private.h"
+#include "plan.h"
+#include "lock.h"
+
+#include "../statTimer/statisticalTimer.GPU.h"
+
+
+
+
+
+//     This class contains objects that we wish to retain between individual calls into the FFT interface.
+//     These objects will be shared across different individual FFT plans, and we wish to keep only one
+//     copy of these programs, objects and events.  When the client decides that they either want to reset
+//     the library or release all resources, this Repo will release all acquired resources and clean itself
+//     up as much as it can.  It is implemented as a Singleton object.
+class  FFTRepo
+{
+
+    struct FFTRepoKey
+    {
+        clfftGenerators gen;
+        const FFTKernelSignatureHeader * data;
+        cl_context context;
+               cl_device_id device;
+               bool dataIsPrivate;
+
+        FFTRepoKey(clfftGenerators gen_, const FFTKernelSignatureHeader * data_, cl_context context_, cl_device_id device_)
+            : gen(gen_), data(data_), context(context_), device(device_), dataIsPrivate(false)
+        {
+
+        }
+
+        void privatizeData()
+        {
+            char * tmp = new char[data->datasize];
+            ::memcpy(tmp, data, data->datasize);
+            this->data = (FFTKernelSignatureHeader*) tmp;
+                       dataIsPrivate = true;
+        }
+
+        void deleteData()
+        {
+            if ( dataIsPrivate && (this->data != NULL) )
+            {
+                               char *tmp = (char *)(this->data);
+                delete[] tmp;
+                               this->data = 0;
+            }            
+        }
+
+        bool operator<(const FFTRepoKey & b) const
+        {
+            const FFTRepoKey & a = *this;
+
+            if (a.gen != b.gen)
+            {
+                return a.gen < b.gen;
+            }
+            else if (a.data->datasize != b.data->datasize)
+            {
+                return a.data->datasize < b.data->datasize;
+            }
+            else if (a.context != b.context)
+            {
+                return a.context < b.context;
+            }
+                       else if (a.device != b.device)
+                       {
+                               return a.device < b.device;
+                       }
+            else
+            {
+                return ::memcmp(a.data, b.data, a.data->datasize) < 0;
+            }
+        }
+    };
+
+
+       //      Structure containing all the data we need to remember for a specific invokation of a kernel
+       //      generator
+       struct fftRepoValue {
+               std::string ProgramString;
+               std::string EntryPoint_fwd;
+               std::string EntryPoint_back;
+               cl_program  clProgram;
+
+               fftRepoValue ()
+               :       clProgram (NULL)
+               {}
+       };
+
+       //      Map structure to map parameters that a generator uses to a specific set of kernels that the generator
+       //      has created
+       //typedef std::pair< clfftGenerators, FFTKernelGenKeyParams > fftRepoKey;
+
+       typedef std::map< FFTRepoKey, fftRepoValue > fftRepoType;
+       typedef fftRepoType::iterator fftRepo_iterator;
+
+
+
+       fftRepoType     mapFFTs;
+
+       struct fftKernels {
+               cl_kernel kernel_fwd;
+               cl_kernel kernel_back;
+               lockRAII* kernel_fwd_lock;
+               lockRAII* kernel_back_lock;
+
+               fftKernels ()
+               :       kernel_fwd (NULL)
+               ,       kernel_back (NULL)
+               ,       kernel_fwd_lock(NULL)
+               ,       kernel_back_lock(NULL)
+               {}
+       };
+
+       typedef std::map< cl_program, fftKernels > mapKernelType;
+       typedef mapKernelType::iterator Kernel_iterator;
+       mapKernelType mapKernels;
+
+       //      All plans that the user creates over the course of using the library are stored here.
+       //      Plans can be arbitrarily created and destroyed at anytime by the user, in arbitrary order, so vector
+       //      does not seem appropriate, so a map was chosen because of the O(log N) search properties
+       //      A lock object is created for each plan, such that any getter/setter can lock the 'plan' object before
+       //      reading/writing its values.  The lock object is kept seperate from the plan object so that the lock
+       //      object can be held the entire time a plan is getting destroyed in clfftDestroyPlan.
+       typedef std::pair< FFTPlan*, lockRAII* > repoPlansValue;
+       typedef std::map< clfftPlanHandle, repoPlansValue > repoPlansType;
+       repoPlansType repoPlans;
+
+       //      Static count of how many plans we have generated; always incrementing during the life of the library
+       //      This is used as a unique identifier for plans
+       static size_t planCount;
+
+       // Private constructor to stop explicit instantiation
+       FFTRepo( )
+       {}
+
+       // Private copy constructor to stop implicit instantiation
+       FFTRepo( const FFTRepo& );
+
+       // Private operator= to assure only 1 copy of singleton
+       FFTRepo& operator=( const FFTRepo& );
+
+       ~FFTRepo( )
+       {
+               //      NOTE:  We can't release resources in our destructor because as a static object, the order of destruction of static objects
+               //      is not guaranteed, and openCL might already have cleaned itself up.  When clFFT tries to free its resources, an access
+               //      violation could occur.
+               //releaseResources( );
+
+               //      We should at least print out a warning message to the user if we are in our destructor and we still have resources
+               //      bound.  This should give the user a clue to remember to call clfftTeardown( )
+               if( (!mapKernels.empty( )) || (!mapFFTs.empty( )) )
+               {
+                       terr << _T( "Warning:  Program terminating, but clFFT resources not freed." ) << std::endl;
+                       terr << _T( "Please consider explicitly calling clfftTeardown( )." ) << std::endl;
+               }
+       };
+
+public:
+       //      Used to make the FFTRepo struct thread safe; STL is not thread safe by default
+       //      Optimally, we could use a lock object per STL struct, as two different STL structures
+       //      can be modified at the same time, but a single lock object is easier and performance should
+       //      still be good. This is implemented as a function returning a static local reference to
+       //      assert that the lock must be instantiated before the result can be used.
+       static lockRAII& lockRepo()
+       {
+               //      Static initialization of the repo lock variable
+               static lockRAII lock(_T("FFTRepo"));
+               return lock;
+       }
+
+       //      Our runtime library can instrument kernel timings with a GPU timer available in a shared module
+       //      Handle/Address of the dynamic module that contains timers
+       static void* timerHandle;
+
+       //      Pointer to the timer class queried from the timer shared library
+       static GpuStatTimer* pStatTimer;
+
+       //      Global debug flags that the FFT engine can check to control various debug logic
+       clfftSetupData setupData;
+
+       //      Everybody who wants to access the Repo calls this function to get a repo reference
+       static FFTRepo& getInstance( )
+       {
+               static FFTRepo fftRepo;
+               return  fftRepo;
+       };
+
+       clfftStatus releaseResources( );
+
+       clfftStatus setProgramCode( const clfftGenerators gen, const FFTKernelSignatureHeader * data, const std::string& kernel, const cl_device_id &device, const cl_context& planContext );
+       clfftStatus getProgramCode( const clfftGenerators gen, const FFTKernelSignatureHeader * data, std::string& kernel, const cl_device_id &device, const cl_context& planContext );
+
+       clfftStatus setProgramEntryPoints( const clfftGenerators gen, const FFTKernelSignatureHeader * data, const char * kernel_fwd, const char * kernel_back, const cl_device_id &device, const cl_context& planContext );
+       clfftStatus getProgramEntryPoint( const clfftGenerators gen, const FFTKernelSignatureHeader * data, clfftDirection dir, std::string& kernel , const cl_device_id &device, const cl_context& planContext );
+
+       clfftStatus setclProgram( const clfftGenerators gen, const FFTKernelSignatureHeader * data, const cl_program& prog, const cl_device_id &device, const cl_context& planContext );
+       clfftStatus getclProgram( const clfftGenerators gen, const FFTKernelSignatureHeader * data, cl_program& prog, const cl_device_id &device, const cl_context& planContext );
+
+       clfftStatus setclKernel ( cl_program prog, clfftDirection dir, const cl_kernel& kernel );
+       clfftStatus getclKernel ( cl_program prog, clfftDirection dir, cl_kernel& kernel, lockRAII*& kernelLock);
+
+       clfftStatus createPlan( clfftPlanHandle* plHandle, FFTPlan*& fftPlan );
+       clfftStatus getPlan( clfftPlanHandle plHandle, FFTPlan*& fftPlan, lockRAII*& planLock );
+       clfftStatus deletePlan( clfftPlanHandle* plHandle );
+  
+
+};
+
+#endif
+
diff --git a/src/include/gromacs/external/clFFT/src/statTimer/statisticalTimer.CPU.h b/src/include/gromacs/external/clFFT/src/statTimer/statisticalTimer.CPU.h
new file mode 100644 (file)
index 0000000..7601493
--- /dev/null
@@ -0,0 +1,173 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+#ifndef _STATISTICALTIMER_CPU_H_
+#define _STATISTICALTIMER_CPU_H_
+#include <iosfwd>
+#include <vector>
+#include <algorithm>
+#ifdef __FreeBSD__
+#include <sys/timespec.h>
+#endif
+#include "statisticalTimer.h"
+
+/**
+ * \file clfft.StatisticalTimer.CPU.h
+ * \brief A timer class that provides a cross platform timer for use
+ * in timing code progress with a high degree of accuracy.
+ *     This class is implemented entirely in the header, to facilitate inclusion into multiple
+ *     projects without needing to compile an object file for each project.
+ */
+
+class CpuStatTimer : public baseStatTimer
+{
+       //      Private typedefs
+       typedef std::vector< cl_ulong > clkVector;
+       typedef std::pair< std::string, cl_uint > labelPair;
+       typedef std::vector< labelPair > stringVector;
+
+       //      In order to calculate statistics <std. dev.>, we need to keep a history of our timings
+       stringVector    labelID;
+       clkVector       clkStart;
+       std::vector< clkVector >        clkTicks;
+
+       //      How many clockticks in a second
+       cl_ulong        clkFrequency;
+
+       //      For linux; the resolution of a high-precision timer
+       //  Mingw32 does not define timespec; can use windows timers
+#if !defined( _WIN32 )
+       timespec res;
+#endif
+
+       //      Saved sizes for our vectors, used in Reset() to reallocate vectors
+       clkVector::size_type    nEvents, nSamples;
+
+       //      This setting controls whether the Timer should convert samples into time by dividing by the
+       //      clock frequency
+       bool normalize;
+
+       /**
+        * \fn StatisticalTimer()
+        * \brief Constructor for StatisticalTimer that initializes the class
+        *      This is private so that user code cannot create their own instantiation.  Instead, you
+        *      must go through getInstance( ) to get a reference to the class.
+        */
+       CpuStatTimer( );
+
+       /**
+        * \fn ~StatisticalTimer()
+        * \brief Destructor for StatisticalTimer that cleans up the class
+        */
+       ~CpuStatTimer( );
+
+       /**
+        * \fn StatisticalTimer(const StatisticalTimer& )
+        * \brief Copy constructors do not make sense for a singleton, disallow copies
+        */
+       CpuStatTimer( const CpuStatTimer& );
+
+       /**
+        * \fn operator=( const StatisticalTimer& )
+        * \brief Assignment operator does not make sense for a singleton, disallow assignments
+        */
+       CpuStatTimer& operator=( const CpuStatTimer& );
+
+       friend std::ostream& operator<<( std::ostream& os, const CpuStatTimer& s );
+
+       /**
+        * \fn void AddSample( const size_t id, const cl_ulong n )
+        * \brief Explicitely add a timing sample into the class
+        */
+       void AddSample( const size_t id, const cl_ulong n );
+
+       //      Calculate the average/mean of data for a given event
+       cl_double       getMean( size_t id ) const;
+
+       //      Calculate the variance of data for a given event
+       //      Variance - average of the squared differences between data points and the mean
+       cl_double       getVariance( size_t id ) const;
+
+       //      Sqrt of variance, also in units of the original data
+       cl_double       getStdDev( size_t id ) const;
+
+       /**
+        * \fn double getAverageTime(size_t id) const
+        * \return Return the arithmetic mean of all the samples that have been saved
+        */
+       cl_double getAverageTime( size_t id ) const;
+
+       /**
+        * \fn double getMinimumTime(size_t id) const
+        * \return Return the arithmetic min of all the samples that have been saved
+        */
+       cl_double getMinimumTime( size_t id ) const;
+
+public:
+       /**
+        * \fn getInstance()
+        * \brief This returns a reference to the singleton timer.  Guarantees only 1 timer class is ever
+        *      instantiated within a compilable executable.
+        */
+       static CpuStatTimer& getInstance( );
+
+       /**
+        * \fn void Start( size_t id )
+        * \brief Start the timer
+        * \sa Stop(), Reset()
+        */
+       void Start( size_t id );
+
+       /**
+        * \fn void Stop( size_t id )
+        * \brief Stop the timer
+        * \sa Start(), Reset()
+        */
+       void Stop( size_t id );
+
+       /**
+        * \fn void Reset(void)
+        * \brief Reset the timer to 0
+        * \sa Start(), Stop()
+        */
+       void Clear( );
+
+       /**
+        * \fn void Reset(void)
+        * \brief Reset the timer to 0
+        * \sa Start(), Stop()
+        */
+       void Reset( );
+
+       void Reserve( size_t nEvents, size_t nSamples );
+
+       size_t getUniqueID( const std::string& label, cl_uint groupID );
+
+       //      Calculate the average/mean of data for a given event
+       void    setNormalize( bool norm );
+
+       void Print( );
+
+       //      Using the stdDev of the entire population (of an id), eliminate those samples that fall
+       //      outside some specified multiple of the stdDev.  This assumes that the population
+       //      form a gaussian curve.
+       size_t  pruneOutliers( cl_double multiple );
+       std::vector< size_t > pruneOutliers( size_t id , cl_double multiple );
+};
+
+#endif // _STATISTICALTIMER_CPU_H_
diff --git a/src/include/gromacs/external/clFFT/src/statTimer/statisticalTimer.GPU.h b/src/include/gromacs/external/clFFT/src/statTimer/statisticalTimer.GPU.h
new file mode 100644 (file)
index 0000000..5b65ffc
--- /dev/null
@@ -0,0 +1,256 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+#ifndef _STATISTICALTIMER_GPU_H_
+#define _STATISTICALTIMER_GPU_H_
+#include <iosfwd>
+#include <vector>
+#include <algorithm>
+#include <cmath>
+#include "statisticalTimer.h"
+#include "../library/plan.h"
+
+/**
+ * \file clfft.StatisticalTimer.GPU.h
+ * \brief A timer class that provides a cross platform timer for use
+ * in timing code progress with a high degree of accuracy.
+ *     This class is implemented entirely in the header, to facilitate inclusion into multiple
+ *     projects without needing to compile an object file for each project.
+ */
+
+struct StatData
+{
+       cl_kernel kernel;
+       cl_ulong deltaNanoSec;
+       double doubleNanoSec;
+       size_t batchSize;
+       clfftDim dim;
+       clfftPlanHandle plHandle;
+       clfftPlanHandle planX;
+       clfftPlanHandle planY;
+       clfftPlanHandle planZ;
+       clfftPlanHandle planTX;
+       clfftPlanHandle planTY;
+       clfftPlanHandle planTZ;
+
+       clfftPlanHandle planRCcopy;
+       clfftPlanHandle planCopy;
+
+       clfftGenerators gen;
+
+       std::vector< size_t > lengths;
+       std::vector< size_t > inStride;
+       std::vector< size_t > outStride;
+       size_t iDist;
+       size_t oDist;
+       clfftResultLocation placeness;
+       std::vector< size_t > enqueueLocalWorkSize;
+       std::vector< size_t > enqueueWorkSize;
+       std::vector< cl_event > outEvents;
+
+       StatData( ): deltaNanoSec( 0 )
+       {}
+
+       StatData( clfftPlanHandle id, FFTPlan* plan, cl_kernel kern, cl_uint nEv, cl_event* Ev,
+               const std::vector< size_t >& gWorkSize, const std::vector< size_t >& lWorkSize):
+               deltaNanoSec( 0 ), kernel( kern ), batchSize( plan->batchsize ), dim( plan->dim ),
+               plHandle( id ), planX( plan->planX ), planY( plan->planY ), planZ( plan->planZ ),
+               planTX( plan->planTX ), planTY( plan->planTY ), planTZ( plan->planTZ ),
+               planRCcopy( plan->planRCcopy ), planCopy( plan->planCopy ), gen(plan->gen),
+               inStride( plan->inStride ), outStride( plan->outStride ), iDist( plan->iDist ), oDist( plan->oDist ),
+               lengths( plan->length ), enqueueWorkSize( gWorkSize ), enqueueLocalWorkSize( lWorkSize ), placeness( plan->placeness )
+       {
+               for( cl_uint e = 0; e < nEv; ++e )
+               {
+                       outEvents.push_back( Ev[ e ] );
+               }
+       }
+
+       double calcFlops( )
+       {
+               size_t  fftLength = 0;
+               size_t  dimIndex = 0;
+
+               if( dim == CLFFT_1D )
+               {
+                       fftLength       = lengths.at( 0 );
+                       dimIndex        = 1;
+               }
+               else if( dim == CLFFT_2D )
+               {
+                       fftLength       = lengths.at( 0 ) * lengths.at( 1 );
+                       dimIndex        = 2;
+               }
+               else if( dim == CLFFT_3D )
+               {
+                       fftLength       = lengths.at( 0 ) * lengths.at( 1 ) * lengths.at( 2 );
+                       dimIndex        = 3;
+               }
+
+               size_t cumulativeBatch = 1;
+               for( ; dimIndex < lengths.size(); ++dimIndex )
+               {
+                       cumulativeBatch *= std::max< size_t >( 1, lengths[ dimIndex ] );
+               }
+               cumulativeBatch *= batchSize;
+
+               double flops    = cumulativeBatch * 5 * fftLength * ( log( static_cast< double >( fftLength ) ) / log( 2.0 ) );
+
+               return flops;
+       }
+
+};
+
+//     Sorting operator for struct StatData, such that it can be used in a map
+bool operator<( const StatData& lhs, const StatData& rhs);
+
+class GpuStatTimer : public baseStatTimer
+{
+       //      Typedefs to handle the data that we store
+       typedef std::vector< StatData > StatDataVec;
+       typedef std::vector< StatDataVec > PerEnqueueVec;
+
+       //      In order to calculate statistics <std. dev.>, we need to keep a history of our timings
+       std::vector< PerEnqueueVec > timerData;
+
+       //      Typedefs to handle the identifiers we use for our timers
+       typedef std::pair< std::string, cl_uint > idPair;
+       typedef std::vector< idPair > idVector;
+       idVector labelID;
+
+       //      Between each Start/Stop pair, we need to count how many AddSamples were made.
+       size_t currSample, currRecord;
+
+       //      Saved sizes for our vectors, used in Reset() to reallocate vectors
+       StatDataVec::size_type  nEvents, nSamples;
+       size_t currID;
+
+       /**
+        * \fn GpuStatTimer()
+        * \brief Constructor for StatisticalTimer that initializes the class
+        *      This is private so that user code cannot create their own instantiation.  Instead, you
+        *      must go through getInstance( ) to get a reference to the class.
+        */
+       GpuStatTimer( );
+
+       /**
+        * \fn ~GpuStatTimer()
+        * \brief Destructor for StatisticalTimer that cleans up the class
+        */
+       ~GpuStatTimer( );
+
+       /**
+        * \fn GpuStatTimer(const StatisticalTimer& )
+        * \brief Copy constructors do not make sense for a singleton, disallow copies
+        */
+       GpuStatTimer( const GpuStatTimer& );
+
+       /**
+        * \fn operator=( const StatisticalTimer& )
+        * \brief Assignment operator does not make sense for a singleton, disallow assignments
+        */
+       GpuStatTimer& operator=( const GpuStatTimer& );
+
+       friend std::ostream& operator<<( std::ostream& os, const GpuStatTimer& s );
+
+       //      Calculate the average/mean of data for a given event
+       std::vector< StatData > getMean( size_t id );
+
+       //      Calculate the variance of data for a given event
+       //      Variance - average of the squared differences between data points and the mean
+       std::vector< StatData > getVariance( size_t id );
+
+       //      Sqrt of variance, also in units of the original data
+       std::vector< StatData > getStdDev( size_t id );
+
+       /**
+        * \fn double getAverageTime(size_t id) const
+        * \return Return the arithmetic mean of all the samples that have been saved
+        */
+       std::vector< StatData > getAverageTime( size_t id );
+
+       /**
+        * \fn double getMinimumTime(size_t id) const
+        * \return Return the arithmetic min of all the samples that have been saved
+        */
+       std::vector< StatData > getMinimumTime( size_t id );
+
+       void queryOpenCL( size_t id );
+
+       void ReleaseEvents();
+
+public:
+       /**
+        * \fn getInstance()
+        * \brief This returns a reference to the singleton timer.  Guarantees only 1 timer class is ever
+        *      instantiated within a compilable executable.
+        */
+       static GpuStatTimer& getInstance( );
+
+       /**
+        * \fn void Start( size_t id )
+        * \brief Start the timer
+        * \sa Stop(), Reset()
+        */
+       void Start( size_t id );
+
+       /**
+        * \fn void Stop( size_t id )
+        * \brief Stop the timer
+        * \sa Start(), Reset()
+        */
+       void Stop( size_t id );
+
+       /**
+        * \fn void AddSample( const cl_event ev )
+        * \brief Explicitely add a timing sample into the class
+        */
+       virtual void AddSample( clfftPlanHandle plHandle, FFTPlan* plan, cl_kernel kern, cl_uint numQueuesAndEvents, cl_event* ev,
+               const std::vector< size_t >& gWorkSize, const std::vector< size_t >& lWorkSize );
+
+       /**
+        * \fn void Reset(void)
+        * \brief Reset the timer to 0
+        * \sa Start(), Stop()
+        */
+       void Clear( );
+
+       /**
+        * \fn void Reset(void)
+        * \brief Reset the timer to 0
+        * \sa Start(), Stop()
+        */
+       void Reset( );
+
+       void Reserve( size_t nEvents, size_t nSamples );
+
+       size_t getUniqueID( const std::string& label, cl_uint groupID );
+
+       //      Calculate the average/mean of data for a given event
+       void    setNormalize( bool norm );
+
+       void Print( );
+
+       //      Using the stdDev of the entire population (of an id), eliminate those samples that fall
+       //      outside some specified multiple of the stdDev.  This assumes that the population
+       //      form a gaussian curve.
+       size_t  pruneOutliers( cl_double multiple );
+       std::vector< size_t > pruneOutliers( size_t id , cl_double multiple );
+};
+
+#endif // _STATISTICALTIMER_GPU_H_
diff --git a/src/include/gromacs/external/clFFT/src/statTimer/statisticalTimer.extern.h b/src/include/gromacs/external/clFFT/src/statTimer/statisticalTimer.extern.h
new file mode 100644 (file)
index 0000000..84999d6
--- /dev/null
@@ -0,0 +1,73 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+#ifndef _STATISTICALTIMER_EXTERN_H_
+#define _STATISTICALTIMER_EXTERN_H_
+#include "../include/clFFT.h"
+#include "statisticalTimer.h"
+
+/**
+ * \file clfft.StatisticalTimer.extern.h
+ * \brief A timer class that provides a cross platform timer for use
+ * in timing code progress with a high degree of accuracy.
+ *     This class is implemented entirely in the header, to facilitate inclusion into multiple
+ *     projects without needing to compile an object file for each project.
+ */
+
+// The following ifdef block is the standard way of creating macros which make exporting
+// from a DLL simpler. All files within this DLL are compiled with the STATTIMER_EXPORTS
+// symbol defined on the command line. this symbol should not be defined on any project
+// that uses this DLL. This way any other project whose source files include this file see
+// STATTIMER_API functions as being imported from a DLL, whereas this DLL sees symbols
+// defined with this macro as being exported.
+#if defined( _WIN32 )
+       #if !defined( __cplusplus )
+               #define inline __inline
+       #endif
+
+    #if defined( CLFFT_STATIC )
+        #define STATTIMER_API
+    #elif defined( STATTIMER_EXPORTS )
+        #define STATTIMER_API __declspec( dllexport )
+    #else
+        #define STATTIMER_API __declspec( dllimport )
+    #endif
+#else
+       #define STATTIMER_API
+#endif
+
+//     The type of timer to be returned from ::getStatTimer( )
+typedef enum clfftTimerType_
+{
+       CLFFT_GPU                       = 1,
+       CLFFT_CPU,
+} clfftTimerType;
+
+//     Table of typedef definitions for all exported functions from this shared module.
+//     Clients of this module can use these typedefs to help create function pointers
+//     that can be initialized to point to the functions exported from this module.
+typedef baseStatTimer* (*PFGETSTATTIMER)( const clfftTimerType type );
+
+       /**
+       * \fn getInstance()
+       * \brief This returns a reference to the singleton timer.  Guarantees only 1 timer class is ever
+       *       instantiated within a compilable executable.
+       */
+extern "C" STATTIMER_API baseStatTimer* getStatTimer( const clfftTimerType type );
+
+#endif // _STATISTICALTIMER_EXTERN_H_
diff --git a/src/include/gromacs/external/clFFT/src/statTimer/statisticalTimer.h b/src/include/gromacs/external/clFFT/src/statTimer/statisticalTimer.h
new file mode 100644 (file)
index 0000000..58c03b7
--- /dev/null
@@ -0,0 +1,106 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+#pragma once
+#ifndef _STATISTICALTIMER_H_
+#define _STATISTICALTIMER_H_
+#include <vector>
+#include <functional>
+#include <string>
+
+#include "../include/clFFT.h"
+
+/**
+ * \file clfft.StatisticalTimer.h
+ * \brief A timer class that provides a cross platform timer for use
+ * in timing code progress with a high degree of accuracy.
+ *     This class is implemented entirely in the header, to facilitate inclusion into multiple
+ *     projects without needing to compile an object file for each project.
+ */
+
+//     Definition of a functor object that is passed by reference into the Print statement
+//     of the timing class.
+//     Functor object to help with accumulating values in vectors
+template< typename A, typename R >
+class flopsFunc: public std::unary_function< A, R >
+{
+public:
+       virtual typename std::unary_function<A, R>::result_type operator( )( ) = 0;
+};
+
+/**
+ * \class StatisticalTimer
+ * \brief Counter that provides a fairly accurate timing mechanism for both
+ * windows and linux. This timer is used extensively in all the samples.
+ */
+class baseStatTimer
+{
+protected:
+       /**
+        * \fn ~baseStatTimer()
+        * \brief Destructor for StatisticalTimer that cleans up the class
+        */
+       virtual ~baseStatTimer( ){ };
+
+//     friend std::ostream& operator<<( std::ostream& os, const baseStatTimer& s );
+
+public:
+       /**
+        * \fn void Start( sTimerID id )
+        * \brief Start the timer
+        * \sa Stop(), Reset()
+        */
+       virtual void Start( size_t id ) = 0;
+
+       /**
+        * \fn void Stop( size_t id )
+        * \brief Stop the timer
+        * \sa Start(), Reset()
+        */
+       virtual void Stop( size_t id ) = 0;
+
+       /**
+        * \fn void Reset(void)
+        * \brief Reset the timer to 0
+        * \sa Start(), Stop()
+        */
+       virtual void Clear( ) = 0;
+
+       /**
+        * \fn void Reset(void)
+        * \brief Reset the timer to 0
+        * \sa Start(), Stop()
+        */
+       virtual void Reset( ) = 0;
+
+       virtual void Reserve( size_t nEvents, size_t nSamples ) = 0;
+
+       virtual size_t getUniqueID( const std::string& label, cl_uint groupID ) = 0;
+
+       //      Calculate the average/mean of data for a given event
+       virtual void    setNormalize( bool norm ) = 0;
+
+       virtual void Print( ) = 0;
+
+       //      Using the stdDev of the entire population (of an id), eliminate those samples that fall
+       //      outside some specified multiple of the stdDev.  This assumes that the population
+       //      form a gaussian curve.
+       virtual size_t  pruneOutliers( cl_double multiple ) = 0;
+       virtual std::vector< size_t > pruneOutliers( size_t id , cl_double multiple ) = 0;
+};
+
+#endif // _STATISTICALTIMER_H_
diff --git a/src/include/gromacs/external/clFFT/src/statTimer/stdafx.h b/src/include/gromacs/external/clFFT/src/statTimer/stdafx.h
new file mode 100644 (file)
index 0000000..774fef7
--- /dev/null
@@ -0,0 +1,51 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#define _CRT_SECURE_NO_WARNINGS
+
+//#include <iostream>
+//#include <sstream>
+//#include <fstream>
+//#include <iomanip>
+//#include <cstring>
+//#include <memory>
+#include <vector>
+//#include <cstring>
+//#include <stdarg.h>
+#include <assert.h>
+//#include <complex>
+
+//     _WIN32 is defined for both 32 & 64 bit environments
+#if defined( _WIN32 )
+//     #include <tchar.h>
+       #include "targetver.h"
+
+#if !defined( NOMINMAX )
+       #define NOMINMAX
+#endif
+
+       #define WIN32_LEAN_AND_MEAN                     // Exclude rarely-used stuff from Windows headers
+       // Windows Header Files:
+       #include <windows.h>
+#endif
diff --git a/src/include/gromacs/external/clFFT/src/statTimer/targetver.h b/src/include/gromacs/external/clFFT/src/statTimer/targetver.h
new file mode 100644 (file)
index 0000000..dafe714
--- /dev/null
@@ -0,0 +1,24 @@
+/* ************************************************************************
+ * Copyright 2013 Advanced Micro Devices, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ************************************************************************/
+
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>
diff --git a/src/include/gromacs/external/fftpack/fftpack.h b/src/include/gromacs/external/fftpack/fftpack.h
new file mode 100644 (file)
index 0000000..92d024d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+
+ *                This source code is part of
+ * 
+ *                 G   R   O   M   A   C   S
+ * 
+ *          GROningen MAchine for Chemical Simulations
+ * 
+ * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2012, The GROMACS development team,
+ * check out http://www.gromacs.org for more information.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * If you want to redistribute modifications, please consider that
+ * scientific software is very special. Version control is crucial -
+ * bugs must be traceable. We will be happy to consider code for
+ * inclusion in the official distribution, but derived work must not
+ * be called official GROMACS. Details are found in the README & COPYING
+ * files - if they are missing, get the official version at www.gromacs.org.
+ * 
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the papers on the package - you can find them in the top README file.
+ * 
+ * For more info, check our website at http://www.gromacs.org
+ * 
+ * And Hey:
+ * Groningen Machine for Chemical Simulation
+
+ ************************************************************/
+
+#ifndef _fftpack_h
+#define _fftpack_h
+
+#if GMX_DOUBLE
+#define Treal double
+#else
+#define Treal float
+#endif
+
+    void fftpack_cffti1(int n, Treal wa[], int ifac[]);
+    void fftpack_cfftf1(int n, Treal c[], Treal ch[], const Treal wa[], const int ifac[], int isign);
+    void fftpack_rffti1(int n, Treal wa[], int ifac[]);
+    void fftpack_rfftf1(int n, Treal c[], Treal ch[], const Treal wa[], const int ifac[]);
+    void fftpack_rfftb1(int n, Treal c[], Treal ch[], const Treal wa[], const int ifac[]);
+
+#endif
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-actions.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-actions.h
new file mode 100644 (file)
index 0000000..0046aaf
--- /dev/null
@@ -0,0 +1,1685 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// The ACTION* family of macros can be used in a namespace scope to
+// define custom actions easily.  The syntax:
+//
+//   ACTION(name) { statements; }
+//
+// will define an action with the given name that executes the
+// statements.  The value returned by the statements will be used as
+// the return value of the action.  Inside the statements, you can
+// refer to the K-th (0-based) argument of the mock function by
+// 'argK', and refer to its type by 'argK_type'.  For example:
+//
+//   ACTION(IncrementArg1) {
+//     arg1_type temp = arg1;
+//     return ++(*temp);
+//   }
+//
+// allows you to write
+//
+//   ...WillOnce(IncrementArg1());
+//
+// You can also refer to the entire argument tuple and its type by
+// 'args' and 'args_type', and refer to the mock function type and its
+// return type by 'function_type' and 'return_type'.
+//
+// Note that you don't need to specify the types of the mock function
+// arguments.  However rest assured that your code is still type-safe:
+// you'll get a compiler error if *arg1 doesn't support the ++
+// operator, or if the type of ++(*arg1) isn't compatible with the
+// mock function's return type, for example.
+//
+// Sometimes you'll want to parameterize the action.   For that you can use
+// another macro:
+//
+//   ACTION_P(name, param_name) { statements; }
+//
+// For example:
+//
+//   ACTION_P(Add, n) { return arg0 + n; }
+//
+// will allow you to write:
+//
+//   ...WillOnce(Add(5));
+//
+// Note that you don't need to provide the type of the parameter
+// either.  If you need to reference the type of a parameter named
+// 'foo', you can write 'foo_type'.  For example, in the body of
+// ACTION_P(Add, n) above, you can write 'n_type' to refer to the type
+// of 'n'.
+//
+// We also provide ACTION_P2, ACTION_P3, ..., up to ACTION_P10 to support
+// multi-parameter actions.
+//
+// For the purpose of typing, you can view
+//
+//   ACTION_Pk(Foo, p1, ..., pk) { ... }
+//
+// as shorthand for
+//
+//   template <typename p1_type, ..., typename pk_type>
+//   FooActionPk<p1_type, ..., pk_type> Foo(p1_type p1, ..., pk_type pk) { ... }
+//
+// In particular, you can provide the template type arguments
+// explicitly when invoking Foo(), as in Foo<long, bool>(5, false);
+// although usually you can rely on the compiler to infer the types
+// for you automatically.  You can assign the result of expression
+// Foo(p1, ..., pk) to a variable of type FooActionPk<p1_type, ...,
+// pk_type>.  This can be useful when composing actions.
+//
+// You can also overload actions with different numbers of parameters:
+//
+//   ACTION_P(Plus, a) { ... }
+//   ACTION_P2(Plus, a, b) { ... }
+//
+// While it's tempting to always use the ACTION* macros when defining
+// a new action, you should also consider implementing ActionInterface
+// or using MakePolymorphicAction() instead, especially if you need to
+// use the action a lot.  While these approaches require more work,
+// they give you more control on the types of the mock function
+// arguments and the action parameters, which in general leads to
+// better compiler error messages that pay off in the long run.  They
+// also allow overloading actions based on parameter types (as opposed
+// to just based on the number of parameters).
+//
+// CAVEAT:
+//
+// ACTION*() can only be used in a namespace scope as templates cannot be
+// declared inside of a local class.
+// Users can, however, define any local functors (e.g. a lambda) that
+// can be used as actions.
+//
+// MORE INFORMATION:
+//
+// To learn more about using these macros, please search for 'ACTION' on
+// https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
+
+#ifndef _WIN32_WCE
+# include <errno.h>
+#endif
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "gmock/internal/gmock-internal-utils.h"
+#include "gmock/internal/gmock-port.h"
+#include "gmock/internal/gmock-pp.h"
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4100)
+#endif
+
+namespace testing {
+
+// To implement an action Foo, define:
+//   1. a class FooAction that implements the ActionInterface interface, and
+//   2. a factory function that creates an Action object from a
+//      const FooAction*.
+//
+// The two-level delegation design follows that of Matcher, providing
+// consistency for extension developers.  It also eases ownership
+// management as Action objects can now be copied like plain values.
+
+namespace internal {
+
+// BuiltInDefaultValueGetter<T, true>::Get() returns a
+// default-constructed T value.  BuiltInDefaultValueGetter<T,
+// false>::Get() crashes with an error.
+//
+// This primary template is used when kDefaultConstructible is true.
+template <typename T, bool kDefaultConstructible>
+struct BuiltInDefaultValueGetter {
+  static T Get() { return T(); }
+};
+template <typename T>
+struct BuiltInDefaultValueGetter<T, false> {
+  static T Get() {
+    Assert(false, __FILE__, __LINE__,
+           "Default action undefined for the function return type.");
+    return internal::Invalid<T>();
+    // The above statement will never be reached, but is required in
+    // order for this function to compile.
+  }
+};
+
+// BuiltInDefaultValue<T>::Get() returns the "built-in" default value
+// for type T, which is NULL when T is a raw pointer type, 0 when T is
+// a numeric type, false when T is bool, or "" when T is string or
+// std::string.  In addition, in C++11 and above, it turns a
+// default-constructed T value if T is default constructible.  For any
+// other type T, the built-in default T value is undefined, and the
+// function will abort the process.
+template <typename T>
+class BuiltInDefaultValue {
+ public:
+  // This function returns true if and only if type T has a built-in default
+  // value.
+  static bool Exists() {
+    return ::std::is_default_constructible<T>::value;
+  }
+
+  static T Get() {
+    return BuiltInDefaultValueGetter<
+        T, ::std::is_default_constructible<T>::value>::Get();
+  }
+};
+
+// This partial specialization says that we use the same built-in
+// default value for T and const T.
+template <typename T>
+class BuiltInDefaultValue<const T> {
+ public:
+  static bool Exists() { return BuiltInDefaultValue<T>::Exists(); }
+  static T Get() { return BuiltInDefaultValue<T>::Get(); }
+};
+
+// This partial specialization defines the default values for pointer
+// types.
+template <typename T>
+class BuiltInDefaultValue<T*> {
+ public:
+  static bool Exists() { return true; }
+  static T* Get() { return nullptr; }
+};
+
+// The following specializations define the default values for
+// specific types we care about.
+#define GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(type, value) \
+  template <> \
+  class BuiltInDefaultValue<type> { \
+   public: \
+    static bool Exists() { return true; } \
+    static type Get() { return value; } \
+  }
+
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(void, );  // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(::std::string, "");
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(bool, false);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned char, '\0');
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed char, '\0');
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(char, '\0');
+
+// There's no need for a default action for signed wchar_t, as that
+// type is the same as wchar_t for gcc, and invalid for MSVC.
+//
+// There's also no need for a default action for unsigned wchar_t, as
+// that type is the same as unsigned int for gcc, and invalid for
+// MSVC.
+#if GMOCK_WCHAR_T_IS_NATIVE_
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(wchar_t, 0U);  // NOLINT
+#endif
+
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned short, 0U);  // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed short, 0);     // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned int, 0U);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed int, 0);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long, 0UL);  // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long, 0L);     // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(unsigned long long, 0);  // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(signed long long, 0);  // NOLINT
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(float, 0);
+GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_(double, 0);
+
+#undef GMOCK_DEFINE_DEFAULT_ACTION_FOR_RETURN_TYPE_
+
+// Simple two-arg form of std::disjunction.
+template <typename P, typename Q>
+using disjunction = typename ::std::conditional<P::value, P, Q>::type;
+
+}  // namespace internal
+
+// When an unexpected function call is encountered, Google Mock will
+// let it return a default value if the user has specified one for its
+// return type, or if the return type has a built-in default value;
+// otherwise Google Mock won't know what value to return and will have
+// to abort the process.
+//
+// The DefaultValue<T> class allows a user to specify the
+// default value for a type T that is both copyable and publicly
+// destructible (i.e. anything that can be used as a function return
+// type).  The usage is:
+//
+//   // Sets the default value for type T to be foo.
+//   DefaultValue<T>::Set(foo);
+template <typename T>
+class DefaultValue {
+ public:
+  // Sets the default value for type T; requires T to be
+  // copy-constructable and have a public destructor.
+  static void Set(T x) {
+    delete producer_;
+    producer_ = new FixedValueProducer(x);
+  }
+
+  // Provides a factory function to be called to generate the default value.
+  // This method can be used even if T is only move-constructible, but it is not
+  // limited to that case.
+  typedef T (*FactoryFunction)();
+  static void SetFactory(FactoryFunction factory) {
+    delete producer_;
+    producer_ = new FactoryValueProducer(factory);
+  }
+
+  // Unsets the default value for type T.
+  static void Clear() {
+    delete producer_;
+    producer_ = nullptr;
+  }
+
+  // Returns true if and only if the user has set the default value for type T.
+  static bool IsSet() { return producer_ != nullptr; }
+
+  // Returns true if T has a default return value set by the user or there
+  // exists a built-in default value.
+  static bool Exists() {
+    return IsSet() || internal::BuiltInDefaultValue<T>::Exists();
+  }
+
+  // Returns the default value for type T if the user has set one;
+  // otherwise returns the built-in default value. Requires that Exists()
+  // is true, which ensures that the return value is well-defined.
+  static T Get() {
+    return producer_ == nullptr ? internal::BuiltInDefaultValue<T>::Get()
+                                : producer_->Produce();
+  }
+
+ private:
+  class ValueProducer {
+   public:
+    virtual ~ValueProducer() {}
+    virtual T Produce() = 0;
+  };
+
+  class FixedValueProducer : public ValueProducer {
+   public:
+    explicit FixedValueProducer(T value) : value_(value) {}
+    T Produce() override { return value_; }
+
+   private:
+    const T value_;
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(FixedValueProducer);
+  };
+
+  class FactoryValueProducer : public ValueProducer {
+   public:
+    explicit FactoryValueProducer(FactoryFunction factory)
+        : factory_(factory) {}
+    T Produce() override { return factory_(); }
+
+   private:
+    const FactoryFunction factory_;
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(FactoryValueProducer);
+  };
+
+  static ValueProducer* producer_;
+};
+
+// This partial specialization allows a user to set default values for
+// reference types.
+template <typename T>
+class DefaultValue<T&> {
+ public:
+  // Sets the default value for type T&.
+  static void Set(T& x) {  // NOLINT
+    address_ = &x;
+  }
+
+  // Unsets the default value for type T&.
+  static void Clear() { address_ = nullptr; }
+
+  // Returns true if and only if the user has set the default value for type T&.
+  static bool IsSet() { return address_ != nullptr; }
+
+  // Returns true if T has a default return value set by the user or there
+  // exists a built-in default value.
+  static bool Exists() {
+    return IsSet() || internal::BuiltInDefaultValue<T&>::Exists();
+  }
+
+  // Returns the default value for type T& if the user has set one;
+  // otherwise returns the built-in default value if there is one;
+  // otherwise aborts the process.
+  static T& Get() {
+    return address_ == nullptr ? internal::BuiltInDefaultValue<T&>::Get()
+                               : *address_;
+  }
+
+ private:
+  static T* address_;
+};
+
+// This specialization allows DefaultValue<void>::Get() to
+// compile.
+template <>
+class DefaultValue<void> {
+ public:
+  static bool Exists() { return true; }
+  static void Get() {}
+};
+
+// Points to the user-set default value for type T.
+template <typename T>
+typename DefaultValue<T>::ValueProducer* DefaultValue<T>::producer_ = nullptr;
+
+// Points to the user-set default value for type T&.
+template <typename T>
+T* DefaultValue<T&>::address_ = nullptr;
+
+// Implement this interface to define an action for function type F.
+template <typename F>
+class ActionInterface {
+ public:
+  typedef typename internal::Function<F>::Result Result;
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  ActionInterface() {}
+  virtual ~ActionInterface() {}
+
+  // Performs the action.  This method is not const, as in general an
+  // action can have side effects and be stateful.  For example, a
+  // get-the-next-element-from-the-collection action will need to
+  // remember the current element.
+  virtual Result Perform(const ArgumentTuple& args) = 0;
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionInterface);
+};
+
+// An Action<F> is a copyable and IMMUTABLE (except by assignment)
+// object that represents an action to be taken when a mock function
+// of type F is called.  The implementation of Action<T> is just a
+// std::shared_ptr to const ActionInterface<T>. Don't inherit from Action!
+// You can view an object implementing ActionInterface<F> as a
+// concrete action (including its current state), and an Action<F>
+// object as a handle to it.
+template <typename F>
+class Action {
+  // Adapter class to allow constructing Action from a legacy ActionInterface.
+  // New code should create Actions from functors instead.
+  struct ActionAdapter {
+    // Adapter must be copyable to satisfy std::function requirements.
+    ::std::shared_ptr<ActionInterface<F>> impl_;
+
+    template <typename... Args>
+    typename internal::Function<F>::Result operator()(Args&&... args) {
+      return impl_->Perform(
+          ::std::forward_as_tuple(::std::forward<Args>(args)...));
+    }
+  };
+
+  template <typename G>
+  using IsCompatibleFunctor = std::is_constructible<std::function<F>, G>;
+
+ public:
+  typedef typename internal::Function<F>::Result Result;
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+  // Constructs a null Action.  Needed for storing Action objects in
+  // STL containers.
+  Action() {}
+
+  // Construct an Action from a specified callable.
+  // This cannot take std::function directly, because then Action would not be
+  // directly constructible from lambda (it would require two conversions).
+  template <
+      typename G,
+      typename = typename std::enable_if<internal::disjunction<
+          IsCompatibleFunctor<G>, std::is_constructible<std::function<Result()>,
+                                                        G>>::value>::type>
+  Action(G&& fun) {  // NOLINT
+    Init(::std::forward<G>(fun), IsCompatibleFunctor<G>());
+  }
+
+  // Constructs an Action from its implementation.
+  explicit Action(ActionInterface<F>* impl)
+      : fun_(ActionAdapter{::std::shared_ptr<ActionInterface<F>>(impl)}) {}
+
+  // This constructor allows us to turn an Action<Func> object into an
+  // Action<F>, as long as F's arguments can be implicitly converted
+  // to Func's and Func's return type can be implicitly converted to F's.
+  template <typename Func>
+  explicit Action(const Action<Func>& action) : fun_(action.fun_) {}
+
+  // Returns true if and only if this is the DoDefault() action.
+  bool IsDoDefault() const { return fun_ == nullptr; }
+
+  // Performs the action.  Note that this method is const even though
+  // the corresponding method in ActionInterface is not.  The reason
+  // is that a const Action<F> means that it cannot be re-bound to
+  // another concrete action, not that the concrete action it binds to
+  // cannot change state.  (Think of the difference between a const
+  // pointer and a pointer to const.)
+  Result Perform(ArgumentTuple args) const {
+    if (IsDoDefault()) {
+      internal::IllegalDoDefault(__FILE__, __LINE__);
+    }
+    return internal::Apply(fun_, ::std::move(args));
+  }
+
+ private:
+  template <typename G>
+  friend class Action;
+
+  template <typename G>
+  void Init(G&& g, ::std::true_type) {
+    fun_ = ::std::forward<G>(g);
+  }
+
+  template <typename G>
+  void Init(G&& g, ::std::false_type) {
+    fun_ = IgnoreArgs<typename ::std::decay<G>::type>{::std::forward<G>(g)};
+  }
+
+  template <typename FunctionImpl>
+  struct IgnoreArgs {
+    template <typename... Args>
+    Result operator()(const Args&...) const {
+      return function_impl();
+    }
+
+    FunctionImpl function_impl;
+  };
+
+  // fun_ is an empty function if and only if this is the DoDefault() action.
+  ::std::function<F> fun_;
+};
+
+// The PolymorphicAction class template makes it easy to implement a
+// polymorphic action (i.e. an action that can be used in mock
+// functions of than one type, e.g. Return()).
+//
+// To define a polymorphic action, a user first provides a COPYABLE
+// implementation class that has a Perform() method template:
+//
+//   class FooAction {
+//    public:
+//     template <typename Result, typename ArgumentTuple>
+//     Result Perform(const ArgumentTuple& args) const {
+//       // Processes the arguments and returns a result, using
+//       // std::get<N>(args) to get the N-th (0-based) argument in the tuple.
+//     }
+//     ...
+//   };
+//
+// Then the user creates the polymorphic action using
+// MakePolymorphicAction(object) where object has type FooAction.  See
+// the definition of Return(void) and SetArgumentPointee<N>(value) for
+// complete examples.
+template <typename Impl>
+class PolymorphicAction {
+ public:
+  explicit PolymorphicAction(const Impl& impl) : impl_(impl) {}
+
+  template <typename F>
+  operator Action<F>() const {
+    return Action<F>(new MonomorphicImpl<F>(impl_));
+  }
+
+ private:
+  template <typename F>
+  class MonomorphicImpl : public ActionInterface<F> {
+   public:
+    typedef typename internal::Function<F>::Result Result;
+    typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+    explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
+
+    Result Perform(const ArgumentTuple& args) override {
+      return impl_.template Perform<Result>(args);
+    }
+
+   private:
+    Impl impl_;
+  };
+
+  Impl impl_;
+};
+
+// Creates an Action from its implementation and returns it.  The
+// created Action object owns the implementation.
+template <typename F>
+Action<F> MakeAction(ActionInterface<F>* impl) {
+  return Action<F>(impl);
+}
+
+// Creates a polymorphic action from its implementation.  This is
+// easier to use than the PolymorphicAction<Impl> constructor as it
+// doesn't require you to explicitly write the template argument, e.g.
+//
+//   MakePolymorphicAction(foo);
+// vs
+//   PolymorphicAction<TypeOfFoo>(foo);
+template <typename Impl>
+inline PolymorphicAction<Impl> MakePolymorphicAction(const Impl& impl) {
+  return PolymorphicAction<Impl>(impl);
+}
+
+namespace internal {
+
+// Helper struct to specialize ReturnAction to execute a move instead of a copy
+// on return. Useful for move-only types, but could be used on any type.
+template <typename T>
+struct ByMoveWrapper {
+  explicit ByMoveWrapper(T value) : payload(std::move(value)) {}
+  T payload;
+};
+
+// Implements the polymorphic Return(x) action, which can be used in
+// any function that returns the type of x, regardless of the argument
+// types.
+//
+// Note: The value passed into Return must be converted into
+// Function<F>::Result when this action is cast to Action<F> rather than
+// when that action is performed. This is important in scenarios like
+//
+// MOCK_METHOD1(Method, T(U));
+// ...
+// {
+//   Foo foo;
+//   X x(&foo);
+//   EXPECT_CALL(mock, Method(_)).WillOnce(Return(x));
+// }
+//
+// In the example above the variable x holds reference to foo which leaves
+// scope and gets destroyed.  If copying X just copies a reference to foo,
+// that copy will be left with a hanging reference.  If conversion to T
+// makes a copy of foo, the above code is safe. To support that scenario, we
+// need to make sure that the type conversion happens inside the EXPECT_CALL
+// statement, and conversion of the result of Return to Action<T(U)> is a
+// good place for that.
+//
+// The real life example of the above scenario happens when an invocation
+// of gtl::Container() is passed into Return.
+//
+template <typename R>
+class ReturnAction {
+ public:
+  // Constructs a ReturnAction object from the value to be returned.
+  // 'value' is passed by value instead of by const reference in order
+  // to allow Return("string literal") to compile.
+  explicit ReturnAction(R value) : value_(new R(std::move(value))) {}
+
+  // This template type conversion operator allows Return(x) to be
+  // used in ANY function that returns x's type.
+  template <typename F>
+  operator Action<F>() const {  // NOLINT
+    // Assert statement belongs here because this is the best place to verify
+    // conditions on F. It produces the clearest error messages
+    // in most compilers.
+    // Impl really belongs in this scope as a local class but can't
+    // because MSVC produces duplicate symbols in different translation units
+    // in this case. Until MS fixes that bug we put Impl into the class scope
+    // and put the typedef both here (for use in assert statement) and
+    // in the Impl class. But both definitions must be the same.
+    typedef typename Function<F>::Result Result;
+    GTEST_COMPILE_ASSERT_(
+        !std::is_reference<Result>::value,
+        use_ReturnRef_instead_of_Return_to_return_a_reference);
+    static_assert(!std::is_void<Result>::value,
+                  "Can't use Return() on an action expected to return `void`.");
+    return Action<F>(new Impl<R, F>(value_));
+  }
+
+ private:
+  // Implements the Return(x) action for a particular function type F.
+  template <typename R_, typename F>
+  class Impl : public ActionInterface<F> {
+   public:
+    typedef typename Function<F>::Result Result;
+    typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+    // The implicit cast is necessary when Result has more than one
+    // single-argument constructor (e.g. Result is std::vector<int>) and R
+    // has a type conversion operator template.  In that case, value_(value)
+    // won't compile as the compiler doesn't known which constructor of
+    // Result to call.  ImplicitCast_ forces the compiler to convert R to
+    // Result without considering explicit constructors, thus resolving the
+    // ambiguity. value_ is then initialized using its copy constructor.
+    explicit Impl(const std::shared_ptr<R>& value)
+        : value_before_cast_(*value),
+          value_(ImplicitCast_<Result>(value_before_cast_)) {}
+
+    Result Perform(const ArgumentTuple&) override { return value_; }
+
+   private:
+    GTEST_COMPILE_ASSERT_(!std::is_reference<Result>::value,
+                          Result_cannot_be_a_reference_type);
+    // We save the value before casting just in case it is being cast to a
+    // wrapper type.
+    R value_before_cast_;
+    Result value_;
+
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(Impl);
+  };
+
+  // Partially specialize for ByMoveWrapper. This version of ReturnAction will
+  // move its contents instead.
+  template <typename R_, typename F>
+  class Impl<ByMoveWrapper<R_>, F> : public ActionInterface<F> {
+   public:
+    typedef typename Function<F>::Result Result;
+    typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+    explicit Impl(const std::shared_ptr<R>& wrapper)
+        : performed_(false), wrapper_(wrapper) {}
+
+    Result Perform(const ArgumentTuple&) override {
+      GTEST_CHECK_(!performed_)
+          << "A ByMove() action should only be performed once.";
+      performed_ = true;
+      return std::move(wrapper_->payload);
+    }
+
+   private:
+    bool performed_;
+    const std::shared_ptr<R> wrapper_;
+  };
+
+  const std::shared_ptr<R> value_;
+};
+
+// Implements the ReturnNull() action.
+class ReturnNullAction {
+ public:
+  // Allows ReturnNull() to be used in any pointer-returning function. In C++11
+  // this is enforced by returning nullptr, and in non-C++11 by asserting a
+  // pointer type on compile time.
+  template <typename Result, typename ArgumentTuple>
+  static Result Perform(const ArgumentTuple&) {
+    return nullptr;
+  }
+};
+
+// Implements the Return() action.
+class ReturnVoidAction {
+ public:
+  // Allows Return() to be used in any void-returning function.
+  template <typename Result, typename ArgumentTuple>
+  static void Perform(const ArgumentTuple&) {
+    static_assert(std::is_void<Result>::value, "Result should be void.");
+  }
+};
+
+// Implements the polymorphic ReturnRef(x) action, which can be used
+// in any function that returns a reference to the type of x,
+// regardless of the argument types.
+template <typename T>
+class ReturnRefAction {
+ public:
+  // Constructs a ReturnRefAction object from the reference to be returned.
+  explicit ReturnRefAction(T& ref) : ref_(ref) {}  // NOLINT
+
+  // This template type conversion operator allows ReturnRef(x) to be
+  // used in ANY function that returns a reference to x's type.
+  template <typename F>
+  operator Action<F>() const {
+    typedef typename Function<F>::Result Result;
+    // Asserts that the function return type is a reference.  This
+    // catches the user error of using ReturnRef(x) when Return(x)
+    // should be used, and generates some helpful error message.
+    GTEST_COMPILE_ASSERT_(std::is_reference<Result>::value,
+                          use_Return_instead_of_ReturnRef_to_return_a_value);
+    return Action<F>(new Impl<F>(ref_));
+  }
+
+ private:
+  // Implements the ReturnRef(x) action for a particular function type F.
+  template <typename F>
+  class Impl : public ActionInterface<F> {
+   public:
+    typedef typename Function<F>::Result Result;
+    typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+    explicit Impl(T& ref) : ref_(ref) {}  // NOLINT
+
+    Result Perform(const ArgumentTuple&) override { return ref_; }
+
+   private:
+    T& ref_;
+  };
+
+  T& ref_;
+};
+
+// Implements the polymorphic ReturnRefOfCopy(x) action, which can be
+// used in any function that returns a reference to the type of x,
+// regardless of the argument types.
+template <typename T>
+class ReturnRefOfCopyAction {
+ public:
+  // Constructs a ReturnRefOfCopyAction object from the reference to
+  // be returned.
+  explicit ReturnRefOfCopyAction(const T& value) : value_(value) {}  // NOLINT
+
+  // This template type conversion operator allows ReturnRefOfCopy(x) to be
+  // used in ANY function that returns a reference to x's type.
+  template <typename F>
+  operator Action<F>() const {
+    typedef typename Function<F>::Result Result;
+    // Asserts that the function return type is a reference.  This
+    // catches the user error of using ReturnRefOfCopy(x) when Return(x)
+    // should be used, and generates some helpful error message.
+    GTEST_COMPILE_ASSERT_(
+        std::is_reference<Result>::value,
+        use_Return_instead_of_ReturnRefOfCopy_to_return_a_value);
+    return Action<F>(new Impl<F>(value_));
+  }
+
+ private:
+  // Implements the ReturnRefOfCopy(x) action for a particular function type F.
+  template <typename F>
+  class Impl : public ActionInterface<F> {
+   public:
+    typedef typename Function<F>::Result Result;
+    typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+
+    explicit Impl(const T& value) : value_(value) {}  // NOLINT
+
+    Result Perform(const ArgumentTuple&) override { return value_; }
+
+   private:
+    T value_;
+  };
+
+  const T value_;
+};
+
+// Implements the polymorphic ReturnRoundRobin(v) action, which can be
+// used in any function that returns the element_type of v.
+template <typename T>
+class ReturnRoundRobinAction {
+ public:
+  explicit ReturnRoundRobinAction(std::vector<T> values) {
+    GTEST_CHECK_(!values.empty())
+        << "ReturnRoundRobin requires at least one element.";
+    state_->values = std::move(values);
+  }
+
+  template <typename... Args>
+  T operator()(Args&&...) const {
+     return state_->Next();
+  }
+
+ private:
+  struct State {
+    T Next() {
+      T ret_val = values[i++];
+      if (i == values.size()) i = 0;
+      return ret_val;
+    }
+
+    std::vector<T> values;
+    size_t i = 0;
+  };
+  std::shared_ptr<State> state_ = std::make_shared<State>();
+};
+
+// Implements the polymorphic DoDefault() action.
+class DoDefaultAction {
+ public:
+  // This template type conversion operator allows DoDefault() to be
+  // used in any function.
+  template <typename F>
+  operator Action<F>() const { return Action<F>(); }  // NOLINT
+};
+
+// Implements the Assign action to set a given pointer referent to a
+// particular value.
+template <typename T1, typename T2>
+class AssignAction {
+ public:
+  AssignAction(T1* ptr, T2 value) : ptr_(ptr), value_(value) {}
+
+  template <typename Result, typename ArgumentTuple>
+  void Perform(const ArgumentTuple& /* args */) const {
+    *ptr_ = value_;
+  }
+
+ private:
+  T1* const ptr_;
+  const T2 value_;
+};
+
+#if !GTEST_OS_WINDOWS_MOBILE
+
+// Implements the SetErrnoAndReturn action to simulate return from
+// various system calls and libc functions.
+template <typename T>
+class SetErrnoAndReturnAction {
+ public:
+  SetErrnoAndReturnAction(int errno_value, T result)
+      : errno_(errno_value),
+        result_(result) {}
+  template <typename Result, typename ArgumentTuple>
+  Result Perform(const ArgumentTuple& /* args */) const {
+    errno = errno_;
+    return result_;
+  }
+
+ private:
+  const int errno_;
+  const T result_;
+};
+
+#endif  // !GTEST_OS_WINDOWS_MOBILE
+
+// Implements the SetArgumentPointee<N>(x) action for any function
+// whose N-th argument (0-based) is a pointer to x's type.
+template <size_t N, typename A, typename = void>
+struct SetArgumentPointeeAction {
+  A value;
+
+  template <typename... Args>
+  void operator()(const Args&... args) const {
+    *::std::get<N>(std::tie(args...)) = value;
+  }
+};
+
+// Implements the Invoke(object_ptr, &Class::Method) action.
+template <class Class, typename MethodPtr>
+struct InvokeMethodAction {
+  Class* const obj_ptr;
+  const MethodPtr method_ptr;
+
+  template <typename... Args>
+  auto operator()(Args&&... args) const
+      -> decltype((obj_ptr->*method_ptr)(std::forward<Args>(args)...)) {
+    return (obj_ptr->*method_ptr)(std::forward<Args>(args)...);
+  }
+};
+
+// Implements the InvokeWithoutArgs(f) action.  The template argument
+// FunctionImpl is the implementation type of f, which can be either a
+// function pointer or a functor.  InvokeWithoutArgs(f) can be used as an
+// Action<F> as long as f's type is compatible with F.
+template <typename FunctionImpl>
+struct InvokeWithoutArgsAction {
+  FunctionImpl function_impl;
+
+  // Allows InvokeWithoutArgs(f) to be used as any action whose type is
+  // compatible with f.
+  template <typename... Args>
+  auto operator()(const Args&...) -> decltype(function_impl()) {
+    return function_impl();
+  }
+};
+
+// Implements the InvokeWithoutArgs(object_ptr, &Class::Method) action.
+template <class Class, typename MethodPtr>
+struct InvokeMethodWithoutArgsAction {
+  Class* const obj_ptr;
+  const MethodPtr method_ptr;
+
+  using ReturnType =
+      decltype((std::declval<Class*>()->*std::declval<MethodPtr>())());
+
+  template <typename... Args>
+  ReturnType operator()(const Args&...) const {
+    return (obj_ptr->*method_ptr)();
+  }
+};
+
+// Implements the IgnoreResult(action) action.
+template <typename A>
+class IgnoreResultAction {
+ public:
+  explicit IgnoreResultAction(const A& action) : action_(action) {}
+
+  template <typename F>
+  operator Action<F>() const {
+    // Assert statement belongs here because this is the best place to verify
+    // conditions on F. It produces the clearest error messages
+    // in most compilers.
+    // Impl really belongs in this scope as a local class but can't
+    // because MSVC produces duplicate symbols in different translation units
+    // in this case. Until MS fixes that bug we put Impl into the class scope
+    // and put the typedef both here (for use in assert statement) and
+    // in the Impl class. But both definitions must be the same.
+    typedef typename internal::Function<F>::Result Result;
+
+    // Asserts at compile time that F returns void.
+    static_assert(std::is_void<Result>::value, "Result type should be void.");
+
+    return Action<F>(new Impl<F>(action_));
+  }
+
+ private:
+  template <typename F>
+  class Impl : public ActionInterface<F> {
+   public:
+    typedef typename internal::Function<F>::Result Result;
+    typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+
+    explicit Impl(const A& action) : action_(action) {}
+
+    void Perform(const ArgumentTuple& args) override {
+      // Performs the action and ignores its result.
+      action_.Perform(args);
+    }
+
+   private:
+    // Type OriginalFunction is the same as F except that its return
+    // type is IgnoredValue.
+    typedef typename internal::Function<F>::MakeResultIgnoredValue
+        OriginalFunction;
+
+    const Action<OriginalFunction> action_;
+  };
+
+  const A action_;
+};
+
+template <typename InnerAction, size_t... I>
+struct WithArgsAction {
+  InnerAction action;
+
+  // The inner action could be anything convertible to Action<X>.
+  // We use the conversion operator to detect the signature of the inner Action.
+  template <typename R, typename... Args>
+  operator Action<R(Args...)>() const {  // NOLINT
+    using TupleType = std::tuple<Args...>;
+    Action<R(typename std::tuple_element<I, TupleType>::type...)>
+        converted(action);
+
+    return [converted](Args... args) -> R {
+      return converted.Perform(std::forward_as_tuple(
+        std::get<I>(std::forward_as_tuple(std::forward<Args>(args)...))...));
+    };
+  }
+};
+
+template <typename... Actions>
+struct DoAllAction {
+ private:
+  template <typename T>
+  using NonFinalType =
+      typename std::conditional<std::is_scalar<T>::value, T, const T&>::type;
+
+  template <typename ActionT, size_t... I>
+  std::vector<ActionT> Convert(IndexSequence<I...>) const {
+    return {ActionT(std::get<I>(actions))...};
+  }
+
+ public:
+  std::tuple<Actions...> actions;
+
+  template <typename R, typename... Args>
+  operator Action<R(Args...)>() const {  // NOLINT
+    struct Op {
+      std::vector<Action<void(NonFinalType<Args>...)>> converted;
+      Action<R(Args...)> last;
+      R operator()(Args... args) const {
+        auto tuple_args = std::forward_as_tuple(std::forward<Args>(args)...);
+        for (auto& a : converted) {
+          a.Perform(tuple_args);
+        }
+        return last.Perform(std::move(tuple_args));
+      }
+    };
+    return Op{Convert<Action<void(NonFinalType<Args>...)>>(
+                  MakeIndexSequence<sizeof...(Actions) - 1>()),
+              std::get<sizeof...(Actions) - 1>(actions)};
+  }
+};
+
+template <typename T, typename... Params>
+struct ReturnNewAction {
+  T* operator()() const {
+    return internal::Apply(
+        [](const Params&... unpacked_params) {
+          return new T(unpacked_params...);
+        },
+        params);
+  }
+  std::tuple<Params...> params;
+};
+
+template <size_t k>
+struct ReturnArgAction {
+  template <typename... Args>
+  auto operator()(const Args&... args) const ->
+      typename std::tuple_element<k, std::tuple<Args...>>::type {
+    return std::get<k>(std::tie(args...));
+  }
+};
+
+template <size_t k, typename Ptr>
+struct SaveArgAction {
+  Ptr pointer;
+
+  template <typename... Args>
+  void operator()(const Args&... args) const {
+    *pointer = std::get<k>(std::tie(args...));
+  }
+};
+
+template <size_t k, typename Ptr>
+struct SaveArgPointeeAction {
+  Ptr pointer;
+
+  template <typename... Args>
+  void operator()(const Args&... args) const {
+    *pointer = *std::get<k>(std::tie(args...));
+  }
+};
+
+template <size_t k, typename T>
+struct SetArgRefereeAction {
+  T value;
+
+  template <typename... Args>
+  void operator()(Args&&... args) const {
+    using argk_type =
+        typename ::std::tuple_element<k, std::tuple<Args...>>::type;
+    static_assert(std::is_lvalue_reference<argk_type>::value,
+                  "Argument must be a reference type.");
+    std::get<k>(std::tie(args...)) = value;
+  }
+};
+
+template <size_t k, typename I1, typename I2>
+struct SetArrayArgumentAction {
+  I1 first;
+  I2 last;
+
+  template <typename... Args>
+  void operator()(const Args&... args) const {
+    auto value = std::get<k>(std::tie(args...));
+    for (auto it = first; it != last; ++it, (void)++value) {
+      *value = *it;
+    }
+  }
+};
+
+template <size_t k>
+struct DeleteArgAction {
+  template <typename... Args>
+  void operator()(const Args&... args) const {
+    delete std::get<k>(std::tie(args...));
+  }
+};
+
+template <typename Ptr>
+struct ReturnPointeeAction {
+  Ptr pointer;
+  template <typename... Args>
+  auto operator()(const Args&...) const -> decltype(*pointer) {
+    return *pointer;
+  }
+};
+
+#if GTEST_HAS_EXCEPTIONS
+template <typename T>
+struct ThrowAction {
+  T exception;
+  // We use a conversion operator to adapt to any return type.
+  template <typename R, typename... Args>
+  operator Action<R(Args...)>() const {  // NOLINT
+    T copy = exception;
+    return [copy](Args...) -> R { throw copy; };
+  }
+};
+#endif  // GTEST_HAS_EXCEPTIONS
+
+}  // namespace internal
+
+// An Unused object can be implicitly constructed from ANY value.
+// This is handy when defining actions that ignore some or all of the
+// mock function arguments.  For example, given
+//
+//   MOCK_METHOD3(Foo, double(const string& label, double x, double y));
+//   MOCK_METHOD3(Bar, double(int index, double x, double y));
+//
+// instead of
+//
+//   double DistanceToOriginWithLabel(const string& label, double x, double y) {
+//     return sqrt(x*x + y*y);
+//   }
+//   double DistanceToOriginWithIndex(int index, double x, double y) {
+//     return sqrt(x*x + y*y);
+//   }
+//   ...
+//   EXPECT_CALL(mock, Foo("abc", _, _))
+//       .WillOnce(Invoke(DistanceToOriginWithLabel));
+//   EXPECT_CALL(mock, Bar(5, _, _))
+//       .WillOnce(Invoke(DistanceToOriginWithIndex));
+//
+// you could write
+//
+//   // We can declare any uninteresting argument as Unused.
+//   double DistanceToOrigin(Unused, double x, double y) {
+//     return sqrt(x*x + y*y);
+//   }
+//   ...
+//   EXPECT_CALL(mock, Foo("abc", _, _)).WillOnce(Invoke(DistanceToOrigin));
+//   EXPECT_CALL(mock, Bar(5, _, _)).WillOnce(Invoke(DistanceToOrigin));
+typedef internal::IgnoredValue Unused;
+
+// Creates an action that does actions a1, a2, ..., sequentially in
+// each invocation. All but the last action will have a readonly view of the
+// arguments.
+template <typename... Action>
+internal::DoAllAction<typename std::decay<Action>::type...> DoAll(
+    Action&&... action) {
+  return {std::forward_as_tuple(std::forward<Action>(action)...)};
+}
+
+// WithArg<k>(an_action) creates an action that passes the k-th
+// (0-based) argument of the mock function to an_action and performs
+// it.  It adapts an action accepting one argument to one that accepts
+// multiple arguments.  For convenience, we also provide
+// WithArgs<k>(an_action) (defined below) as a synonym.
+template <size_t k, typename InnerAction>
+internal::WithArgsAction<typename std::decay<InnerAction>::type, k>
+WithArg(InnerAction&& action) {
+  return {std::forward<InnerAction>(action)};
+}
+
+// WithArgs<N1, N2, ..., Nk>(an_action) creates an action that passes
+// the selected arguments of the mock function to an_action and
+// performs it.  It serves as an adaptor between actions with
+// different argument lists.
+template <size_t k, size_t... ks, typename InnerAction>
+internal::WithArgsAction<typename std::decay<InnerAction>::type, k, ks...>
+WithArgs(InnerAction&& action) {
+  return {std::forward<InnerAction>(action)};
+}
+
+// WithoutArgs(inner_action) can be used in a mock function with a
+// non-empty argument list to perform inner_action, which takes no
+// argument.  In other words, it adapts an action accepting no
+// argument to one that accepts (and ignores) arguments.
+template <typename InnerAction>
+internal::WithArgsAction<typename std::decay<InnerAction>::type>
+WithoutArgs(InnerAction&& action) {
+  return {std::forward<InnerAction>(action)};
+}
+
+// Creates an action that returns 'value'.  'value' is passed by value
+// instead of const reference - otherwise Return("string literal")
+// will trigger a compiler error about using array as initializer.
+template <typename R>
+internal::ReturnAction<R> Return(R value) {
+  return internal::ReturnAction<R>(std::move(value));
+}
+
+// Creates an action that returns NULL.
+inline PolymorphicAction<internal::ReturnNullAction> ReturnNull() {
+  return MakePolymorphicAction(internal::ReturnNullAction());
+}
+
+// Creates an action that returns from a void function.
+inline PolymorphicAction<internal::ReturnVoidAction> Return() {
+  return MakePolymorphicAction(internal::ReturnVoidAction());
+}
+
+// Creates an action that returns the reference to a variable.
+template <typename R>
+inline internal::ReturnRefAction<R> ReturnRef(R& x) {  // NOLINT
+  return internal::ReturnRefAction<R>(x);
+}
+
+// Prevent using ReturnRef on reference to temporary.
+template <typename R, R* = nullptr>
+internal::ReturnRefAction<R> ReturnRef(R&&) = delete;
+
+// Creates an action that returns the reference to a copy of the
+// argument.  The copy is created when the action is constructed and
+// lives as long as the action.
+template <typename R>
+inline internal::ReturnRefOfCopyAction<R> ReturnRefOfCopy(const R& x) {
+  return internal::ReturnRefOfCopyAction<R>(x);
+}
+
+// Modifies the parent action (a Return() action) to perform a move of the
+// argument instead of a copy.
+// Return(ByMove()) actions can only be executed once and will assert this
+// invariant.
+template <typename R>
+internal::ByMoveWrapper<R> ByMove(R x) {
+  return internal::ByMoveWrapper<R>(std::move(x));
+}
+
+// Creates an action that returns an element of `vals`. Calling this action will
+// repeatedly return the next value from `vals` until it reaches the end and
+// will restart from the beginning.
+template <typename T>
+internal::ReturnRoundRobinAction<T> ReturnRoundRobin(std::vector<T> vals) {
+  return internal::ReturnRoundRobinAction<T>(std::move(vals));
+}
+
+// Creates an action that returns an element of `vals`. Calling this action will
+// repeatedly return the next value from `vals` until it reaches the end and
+// will restart from the beginning.
+template <typename T>
+internal::ReturnRoundRobinAction<T> ReturnRoundRobin(
+    std::initializer_list<T> vals) {
+  return internal::ReturnRoundRobinAction<T>(std::vector<T>(vals));
+}
+
+// Creates an action that does the default action for the give mock function.
+inline internal::DoDefaultAction DoDefault() {
+  return internal::DoDefaultAction();
+}
+
+// Creates an action that sets the variable pointed by the N-th
+// (0-based) function argument to 'value'.
+template <size_t N, typename T>
+internal::SetArgumentPointeeAction<N, T> SetArgPointee(T value) {
+  return {std::move(value)};
+}
+
+// The following version is DEPRECATED.
+template <size_t N, typename T>
+internal::SetArgumentPointeeAction<N, T> SetArgumentPointee(T value) {
+  return {std::move(value)};
+}
+
+// Creates an action that sets a pointer referent to a given value.
+template <typename T1, typename T2>
+PolymorphicAction<internal::AssignAction<T1, T2> > Assign(T1* ptr, T2 val) {
+  return MakePolymorphicAction(internal::AssignAction<T1, T2>(ptr, val));
+}
+
+#if !GTEST_OS_WINDOWS_MOBILE
+
+// Creates an action that sets errno and returns the appropriate error.
+template <typename T>
+PolymorphicAction<internal::SetErrnoAndReturnAction<T> >
+SetErrnoAndReturn(int errval, T result) {
+  return MakePolymorphicAction(
+      internal::SetErrnoAndReturnAction<T>(errval, result));
+}
+
+#endif  // !GTEST_OS_WINDOWS_MOBILE
+
+// Various overloads for Invoke().
+
+// Legacy function.
+// Actions can now be implicitly constructed from callables. No need to create
+// wrapper objects.
+// This function exists for backwards compatibility.
+template <typename FunctionImpl>
+typename std::decay<FunctionImpl>::type Invoke(FunctionImpl&& function_impl) {
+  return std::forward<FunctionImpl>(function_impl);
+}
+
+// Creates an action that invokes the given method on the given object
+// with the mock function's arguments.
+template <class Class, typename MethodPtr>
+internal::InvokeMethodAction<Class, MethodPtr> Invoke(Class* obj_ptr,
+                                                      MethodPtr method_ptr) {
+  return {obj_ptr, method_ptr};
+}
+
+// Creates an action that invokes 'function_impl' with no argument.
+template <typename FunctionImpl>
+internal::InvokeWithoutArgsAction<typename std::decay<FunctionImpl>::type>
+InvokeWithoutArgs(FunctionImpl function_impl) {
+  return {std::move(function_impl)};
+}
+
+// Creates an action that invokes the given method on the given object
+// with no argument.
+template <class Class, typename MethodPtr>
+internal::InvokeMethodWithoutArgsAction<Class, MethodPtr> InvokeWithoutArgs(
+    Class* obj_ptr, MethodPtr method_ptr) {
+  return {obj_ptr, method_ptr};
+}
+
+// Creates an action that performs an_action and throws away its
+// result.  In other words, it changes the return type of an_action to
+// void.  an_action MUST NOT return void, or the code won't compile.
+template <typename A>
+inline internal::IgnoreResultAction<A> IgnoreResult(const A& an_action) {
+  return internal::IgnoreResultAction<A>(an_action);
+}
+
+// Creates a reference wrapper for the given L-value.  If necessary,
+// you can explicitly specify the type of the reference.  For example,
+// suppose 'derived' is an object of type Derived, ByRef(derived)
+// would wrap a Derived&.  If you want to wrap a const Base& instead,
+// where Base is a base class of Derived, just write:
+//
+//   ByRef<const Base>(derived)
+//
+// N.B. ByRef is redundant with std::ref, std::cref and std::reference_wrapper.
+// However, it may still be used for consistency with ByMove().
+template <typename T>
+inline ::std::reference_wrapper<T> ByRef(T& l_value) {  // NOLINT
+  return ::std::reference_wrapper<T>(l_value);
+}
+
+// The ReturnNew<T>(a1, a2, ..., a_k) action returns a pointer to a new
+// instance of type T, constructed on the heap with constructor arguments
+// a1, a2, ..., and a_k. The caller assumes ownership of the returned value.
+template <typename T, typename... Params>
+internal::ReturnNewAction<T, typename std::decay<Params>::type...> ReturnNew(
+    Params&&... params) {
+  return {std::forward_as_tuple(std::forward<Params>(params)...)};
+}
+
+// Action ReturnArg<k>() returns the k-th argument of the mock function.
+template <size_t k>
+internal::ReturnArgAction<k> ReturnArg() {
+  return {};
+}
+
+// Action SaveArg<k>(pointer) saves the k-th (0-based) argument of the
+// mock function to *pointer.
+template <size_t k, typename Ptr>
+internal::SaveArgAction<k, Ptr> SaveArg(Ptr pointer) {
+  return {pointer};
+}
+
+// Action SaveArgPointee<k>(pointer) saves the value pointed to
+// by the k-th (0-based) argument of the mock function to *pointer.
+template <size_t k, typename Ptr>
+internal::SaveArgPointeeAction<k, Ptr> SaveArgPointee(Ptr pointer) {
+  return {pointer};
+}
+
+// Action SetArgReferee<k>(value) assigns 'value' to the variable
+// referenced by the k-th (0-based) argument of the mock function.
+template <size_t k, typename T>
+internal::SetArgRefereeAction<k, typename std::decay<T>::type> SetArgReferee(
+    T&& value) {
+  return {std::forward<T>(value)};
+}
+
+// Action SetArrayArgument<k>(first, last) copies the elements in
+// source range [first, last) to the array pointed to by the k-th
+// (0-based) argument, which can be either a pointer or an
+// iterator. The action does not take ownership of the elements in the
+// source range.
+template <size_t k, typename I1, typename I2>
+internal::SetArrayArgumentAction<k, I1, I2> SetArrayArgument(I1 first,
+                                                             I2 last) {
+  return {first, last};
+}
+
+// Action DeleteArg<k>() deletes the k-th (0-based) argument of the mock
+// function.
+template <size_t k>
+internal::DeleteArgAction<k> DeleteArg() {
+  return {};
+}
+
+// This action returns the value pointed to by 'pointer'.
+template <typename Ptr>
+internal::ReturnPointeeAction<Ptr> ReturnPointee(Ptr pointer) {
+  return {pointer};
+}
+
+// Action Throw(exception) can be used in a mock function of any type
+// to throw the given exception.  Any copyable value can be thrown.
+#if GTEST_HAS_EXCEPTIONS
+template <typename T>
+internal::ThrowAction<typename std::decay<T>::type> Throw(T&& exception) {
+  return {std::forward<T>(exception)};
+}
+#endif  // GTEST_HAS_EXCEPTIONS
+
+namespace internal {
+
+// A macro from the ACTION* family (defined later in gmock-generated-actions.h)
+// defines an action that can be used in a mock function.  Typically,
+// these actions only care about a subset of the arguments of the mock
+// function.  For example, if such an action only uses the second
+// argument, it can be used in any mock function that takes >= 2
+// arguments where the type of the second argument is compatible.
+//
+// Therefore, the action implementation must be prepared to take more
+// arguments than it needs.  The ExcessiveArg type is used to
+// represent those excessive arguments.  In order to keep the compiler
+// error messages tractable, we define it in the testing namespace
+// instead of testing::internal.  However, this is an INTERNAL TYPE
+// and subject to change without notice, so a user MUST NOT USE THIS
+// TYPE DIRECTLY.
+struct ExcessiveArg {};
+
+// Builds an implementation of an Action<> for some particular signature, using
+// a class defined by an ACTION* macro.
+template <typename F, typename Impl> struct ActionImpl;
+
+template <typename Impl>
+struct ImplBase {
+  struct Holder {
+    // Allows each copy of the Action<> to get to the Impl.
+    explicit operator const Impl&() const { return *ptr; }
+    std::shared_ptr<Impl> ptr;
+  };
+  using type = typename std::conditional<std::is_constructible<Impl>::value,
+                                         Impl, Holder>::type;
+};
+
+template <typename R, typename... Args, typename Impl>
+struct ActionImpl<R(Args...), Impl> : ImplBase<Impl>::type {
+  using Base = typename ImplBase<Impl>::type;
+  using function_type = R(Args...);
+  using args_type = std::tuple<Args...>;
+
+  ActionImpl() = default;  // Only defined if appropriate for Base.
+  explicit ActionImpl(std::shared_ptr<Impl> impl) : Base{std::move(impl)} { }
+
+  R operator()(Args&&... arg) const {
+    static constexpr size_t kMaxArgs =
+        sizeof...(Args) <= 10 ? sizeof...(Args) : 10;
+    return Apply(MakeIndexSequence<kMaxArgs>{},
+                 MakeIndexSequence<10 - kMaxArgs>{},
+                 args_type{std::forward<Args>(arg)...});
+  }
+
+  template <std::size_t... arg_id, std::size_t... excess_id>
+  R Apply(IndexSequence<arg_id...>, IndexSequence<excess_id...>,
+          const args_type& args) const {
+    // Impl need not be specific to the signature of action being implemented;
+    // only the implementing function body needs to have all of the specific
+    // types instantiated.  Up to 10 of the args that are provided by the
+    // args_type get passed, followed by a dummy of unspecified type for the
+    // remainder up to 10 explicit args.
+    static constexpr ExcessiveArg kExcessArg{};
+    return static_cast<const Impl&>(*this).template gmock_PerformImpl<
+        /*function_type=*/function_type, /*return_type=*/R,
+        /*args_type=*/args_type,
+        /*argN_type=*/typename std::tuple_element<arg_id, args_type>::type...>(
+        /*args=*/args, std::get<arg_id>(args)...,
+        ((void)excess_id, kExcessArg)...);
+  }
+};
+
+// Stores a default-constructed Impl as part of the Action<>'s
+// std::function<>. The Impl should be trivial to copy.
+template <typename F, typename Impl>
+::testing::Action<F> MakeAction() {
+  return ::testing::Action<F>(ActionImpl<F, Impl>());
+}
+
+// Stores just the one given instance of Impl.
+template <typename F, typename Impl>
+::testing::Action<F> MakeAction(std::shared_ptr<Impl> impl) {
+  return ::testing::Action<F>(ActionImpl<F, Impl>(std::move(impl)));
+}
+
+#define GMOCK_INTERNAL_ARG_UNUSED(i, data, el) \
+  , const arg##i##_type& arg##i GTEST_ATTRIBUTE_UNUSED_
+#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_           \
+  const args_type& args GTEST_ATTRIBUTE_UNUSED_ GMOCK_PP_REPEAT( \
+      GMOCK_INTERNAL_ARG_UNUSED, , 10)
+
+#define GMOCK_INTERNAL_ARG(i, data, el) , const arg##i##_type& arg##i
+#define GMOCK_ACTION_ARG_TYPES_AND_NAMES_ \
+  const args_type& args GMOCK_PP_REPEAT(GMOCK_INTERNAL_ARG, , 10)
+
+#define GMOCK_INTERNAL_TEMPLATE_ARG(i, data, el) , typename arg##i##_type
+#define GMOCK_ACTION_TEMPLATE_ARGS_NAMES_ \
+  GMOCK_PP_TAIL(GMOCK_PP_REPEAT(GMOCK_INTERNAL_TEMPLATE_ARG, , 10))
+
+#define GMOCK_INTERNAL_TYPENAME_PARAM(i, data, param) , typename param##_type
+#define GMOCK_ACTION_TYPENAME_PARAMS_(params) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPENAME_PARAM, , params))
+
+#define GMOCK_INTERNAL_TYPE_PARAM(i, data, param) , param##_type
+#define GMOCK_ACTION_TYPE_PARAMS_(params) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPE_PARAM, , params))
+
+#define GMOCK_INTERNAL_TYPE_GVALUE_PARAM(i, data, param) \
+  , param##_type gmock_p##i
+#define GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_TYPE_GVALUE_PARAM, , params))
+
+#define GMOCK_INTERNAL_GVALUE_PARAM(i, data, param) \
+  , std::forward<param##_type>(gmock_p##i)
+#define GMOCK_ACTION_GVALUE_PARAMS_(params) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GVALUE_PARAM, , params))
+
+#define GMOCK_INTERNAL_INIT_PARAM(i, data, param) \
+  , param(::std::forward<param##_type>(gmock_p##i))
+#define GMOCK_ACTION_INIT_PARAMS_(params) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_INIT_PARAM, , params))
+
+#define GMOCK_INTERNAL_FIELD_PARAM(i, data, param) param##_type param;
+#define GMOCK_ACTION_FIELD_PARAMS_(params) \
+  GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_FIELD_PARAM, , params)
+
+#define GMOCK_INTERNAL_ACTION(name, full_name, params)                        \
+  template <GMOCK_ACTION_TYPENAME_PARAMS_(params)>                            \
+  class full_name {                                                           \
+   public:                                                                    \
+    explicit full_name(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params))              \
+        : impl_(std::make_shared<gmock_Impl>(                                 \
+                GMOCK_ACTION_GVALUE_PARAMS_(params))) { }                     \
+    full_name(const full_name&) = default;                                    \
+    full_name(full_name&&) noexcept = default;                                \
+    template <typename F>                                                     \
+    operator ::testing::Action<F>() const {                                   \
+      return ::testing::internal::MakeAction<F>(impl_);                       \
+    }                                                                         \
+   private:                                                                   \
+    class gmock_Impl {                                                        \
+     public:                                                                  \
+      explicit gmock_Impl(GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params))           \
+          : GMOCK_ACTION_INIT_PARAMS_(params) {}                              \
+      template <typename function_type, typename return_type,                 \
+                typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>        \
+      return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \
+      GMOCK_ACTION_FIELD_PARAMS_(params)                                      \
+    };                                                                        \
+    std::shared_ptr<const gmock_Impl> impl_;                                  \
+  };                                                                          \
+  template <GMOCK_ACTION_TYPENAME_PARAMS_(params)>                            \
+  inline full_name<GMOCK_ACTION_TYPE_PARAMS_(params)> name(                   \
+      GMOCK_ACTION_TYPE_GVALUE_PARAMS_(params)) {                             \
+    return full_name<GMOCK_ACTION_TYPE_PARAMS_(params)>(                      \
+        GMOCK_ACTION_GVALUE_PARAMS_(params));                                 \
+  }                                                                           \
+  template <GMOCK_ACTION_TYPENAME_PARAMS_(params)>                            \
+  template <typename function_type, typename return_type, typename args_type, \
+            GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>                                \
+  return_type full_name<GMOCK_ACTION_TYPE_PARAMS_(params)>::gmock_Impl::      \
+  gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+}  // namespace internal
+
+// Similar to GMOCK_INTERNAL_ACTION, but no bound parameters are stored.
+#define ACTION(name)                                                          \
+  class name##Action {                                                        \
+   public:                                                                    \
+   explicit name##Action() noexcept {}                                        \
+   name##Action(const name##Action&) noexcept {}                              \
+    template <typename F>                                                     \
+    operator ::testing::Action<F>() const {                                   \
+      return ::testing::internal::MakeAction<F, gmock_Impl>();                \
+    }                                                                         \
+   private:                                                                   \
+    class gmock_Impl {                                                        \
+     public:                                                                  \
+      template <typename function_type, typename return_type,                 \
+                typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>        \
+      return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const; \
+    };                                                                        \
+  };                                                                          \
+  inline name##Action name() GTEST_MUST_USE_RESULT_;                          \
+  inline name##Action name() { return name##Action(); }                       \
+  template <typename function_type, typename return_type, typename args_type, \
+            GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>                                \
+  return_type name##Action::gmock_Impl::gmock_PerformImpl(                    \
+      GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+#define ACTION_P(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP, (__VA_ARGS__))
+
+#define ACTION_P2(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP2, (__VA_ARGS__))
+
+#define ACTION_P3(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP3, (__VA_ARGS__))
+
+#define ACTION_P4(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP4, (__VA_ARGS__))
+
+#define ACTION_P5(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP5, (__VA_ARGS__))
+
+#define ACTION_P6(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP6, (__VA_ARGS__))
+
+#define ACTION_P7(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP7, (__VA_ARGS__))
+
+#define ACTION_P8(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP8, (__VA_ARGS__))
+
+#define ACTION_P9(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP9, (__VA_ARGS__))
+
+#define ACTION_P10(name, ...) \
+  GMOCK_INTERNAL_ACTION(name, name##ActionP10, (__VA_ARGS__))
+
+}  // namespace testing
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_ACTIONS_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-cardinalities.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-cardinalities.h
new file mode 100644 (file)
index 0000000..c45fd64
--- /dev/null
@@ -0,0 +1,155 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some commonly used cardinalities.  More
+// cardinalities can be defined by the user implementing the
+// CardinalityInterface interface if necessary.
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
+
+#include <limits.h>
+#include <memory>
+#include <ostream>  // NOLINT
+#include "gmock/internal/gmock-port.h"
+#include "gtest/gtest.h"
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
+namespace testing {
+
+// To implement a cardinality Foo, define:
+//   1. a class FooCardinality that implements the
+//      CardinalityInterface interface, and
+//   2. a factory function that creates a Cardinality object from a
+//      const FooCardinality*.
+//
+// The two-level delegation design follows that of Matcher, providing
+// consistency for extension developers.  It also eases ownership
+// management as Cardinality objects can now be copied like plain values.
+
+// The implementation of a cardinality.
+class CardinalityInterface {
+ public:
+  virtual ~CardinalityInterface() {}
+
+  // Conservative estimate on the lower/upper bound of the number of
+  // calls allowed.
+  virtual int ConservativeLowerBound() const { return 0; }
+  virtual int ConservativeUpperBound() const { return INT_MAX; }
+
+  // Returns true if and only if call_count calls will satisfy this
+  // cardinality.
+  virtual bool IsSatisfiedByCallCount(int call_count) const = 0;
+
+  // Returns true if and only if call_count calls will saturate this
+  // cardinality.
+  virtual bool IsSaturatedByCallCount(int call_count) const = 0;
+
+  // Describes self to an ostream.
+  virtual void DescribeTo(::std::ostream* os) const = 0;
+};
+
+// A Cardinality is a copyable and IMMUTABLE (except by assignment)
+// object that specifies how many times a mock function is expected to
+// be called.  The implementation of Cardinality is just a std::shared_ptr
+// to const CardinalityInterface. Don't inherit from Cardinality!
+class GTEST_API_ Cardinality {
+ public:
+  // Constructs a null cardinality.  Needed for storing Cardinality
+  // objects in STL containers.
+  Cardinality() {}
+
+  // Constructs a Cardinality from its implementation.
+  explicit Cardinality(const CardinalityInterface* impl) : impl_(impl) {}
+
+  // Conservative estimate on the lower/upper bound of the number of
+  // calls allowed.
+  int ConservativeLowerBound() const { return impl_->ConservativeLowerBound(); }
+  int ConservativeUpperBound() const { return impl_->ConservativeUpperBound(); }
+
+  // Returns true if and only if call_count calls will satisfy this
+  // cardinality.
+  bool IsSatisfiedByCallCount(int call_count) const {
+    return impl_->IsSatisfiedByCallCount(call_count);
+  }
+
+  // Returns true if and only if call_count calls will saturate this
+  // cardinality.
+  bool IsSaturatedByCallCount(int call_count) const {
+    return impl_->IsSaturatedByCallCount(call_count);
+  }
+
+  // Returns true if and only if call_count calls will over-saturate this
+  // cardinality, i.e. exceed the maximum number of allowed calls.
+  bool IsOverSaturatedByCallCount(int call_count) const {
+    return impl_->IsSaturatedByCallCount(call_count) &&
+        !impl_->IsSatisfiedByCallCount(call_count);
+  }
+
+  // Describes self to an ostream
+  void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); }
+
+  // Describes the given actual call count to an ostream.
+  static void DescribeActualCallCountTo(int actual_call_count,
+                                        ::std::ostream* os);
+
+ private:
+  std::shared_ptr<const CardinalityInterface> impl_;
+};
+
+// Creates a cardinality that allows at least n calls.
+GTEST_API_ Cardinality AtLeast(int n);
+
+// Creates a cardinality that allows at most n calls.
+GTEST_API_ Cardinality AtMost(int n);
+
+// Creates a cardinality that allows any number of calls.
+GTEST_API_ Cardinality AnyNumber();
+
+// Creates a cardinality that allows between min and max calls.
+GTEST_API_ Cardinality Between(int min, int max);
+
+// Creates a cardinality that allows exactly n calls.
+GTEST_API_ Cardinality Exactly(int n);
+
+// Creates a cardinality from its implementation.
+inline Cardinality MakeCardinality(const CardinalityInterface* c) {
+  return Cardinality(c);
+}
+
+}  // namespace testing
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_CARDINALITIES_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-function-mocker.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-function-mocker.h
new file mode 100644 (file)
index 0000000..36bab78
--- /dev/null
@@ -0,0 +1,477 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements MOCK_METHOD.
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_  // NOLINT
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_  // NOLINT
+
+#include <type_traits>  // IWYU pragma: keep
+#include <utility>      // IWYU pragma: keep
+
+#include "gmock/gmock-spec-builders.h"
+#include "gmock/internal/gmock-internal-utils.h"
+#include "gmock/internal/gmock-pp.h"
+
+namespace testing {
+namespace internal {
+template <typename T>
+using identity_t = T;
+
+template <typename Pattern>
+struct ThisRefAdjuster {
+  template <typename T>
+  using AdjustT = typename std::conditional<
+      std::is_const<typename std::remove_reference<Pattern>::type>::value,
+      typename std::conditional<std::is_lvalue_reference<Pattern>::value,
+                                const T&, const T&&>::type,
+      typename std::conditional<std::is_lvalue_reference<Pattern>::value, T&,
+                                T&&>::type>::type;
+
+  template <typename MockType>
+  static AdjustT<MockType> Adjust(const MockType& mock) {
+    return static_cast<AdjustT<MockType>>(const_cast<MockType&>(mock));
+  }
+};
+
+}  // namespace internal
+
+// The style guide prohibits "using" statements in a namespace scope
+// inside a header file.  However, the FunctionMocker class template
+// is meant to be defined in the ::testing namespace.  The following
+// line is just a trick for working around a bug in MSVC 8.0, which
+// cannot handle it if we define FunctionMocker in ::testing.
+using internal::FunctionMocker;
+}  // namespace testing
+
+#define MOCK_METHOD(...) \
+  GMOCK_PP_VARIADIC_CALL(GMOCK_INTERNAL_MOCK_METHOD_ARG_, __VA_ARGS__)
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_1(...) \
+  GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__)
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_2(...) \
+  GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__)
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_3(_Ret, _MethodName, _Args) \
+  GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, ())
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_4(_Ret, _MethodName, _Args, _Spec)     \
+  GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Args);                                   \
+  GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Spec);                                   \
+  GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE(                                      \
+      GMOCK_PP_NARG0 _Args, GMOCK_INTERNAL_SIGNATURE(_Ret, _Args));           \
+  GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec)                                     \
+  GMOCK_INTERNAL_MOCK_METHOD_IMPL(                                            \
+      GMOCK_PP_NARG0 _Args, _MethodName, GMOCK_INTERNAL_HAS_CONST(_Spec),     \
+      GMOCK_INTERNAL_HAS_OVERRIDE(_Spec), GMOCK_INTERNAL_HAS_FINAL(_Spec),    \
+      GMOCK_INTERNAL_GET_NOEXCEPT_SPEC(_Spec),                                \
+      GMOCK_INTERNAL_GET_CALLTYPE(_Spec), GMOCK_INTERNAL_GET_REF_SPEC(_Spec), \
+      (GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)))
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_5(...) \
+  GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__)
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_6(...) \
+  GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__)
+
+#define GMOCK_INTERNAL_MOCK_METHOD_ARG_7(...) \
+  GMOCK_INTERNAL_WRONG_ARITY(__VA_ARGS__)
+
+#define GMOCK_INTERNAL_WRONG_ARITY(...)                                      \
+  static_assert(                                                             \
+      false,                                                                 \
+      "MOCK_METHOD must be called with 3 or 4 arguments. _Ret, "             \
+      "_MethodName, _Args and optionally _Spec. _Args and _Spec must be "    \
+      "enclosed in parentheses. If _Ret is a type with unprotected commas, " \
+      "it must also be enclosed in parentheses.")
+
+#define GMOCK_INTERNAL_ASSERT_PARENTHESIS(_Tuple) \
+  static_assert(                                  \
+      GMOCK_PP_IS_ENCLOSED_PARENS(_Tuple),        \
+      GMOCK_PP_STRINGIZE(_Tuple) " should be enclosed in parentheses.")
+
+#define GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE(_N, ...)                 \
+  static_assert(                                                       \
+      std::is_function<__VA_ARGS__>::value,                            \
+      "Signature must be a function type, maybe return type contains " \
+      "unprotected comma.");                                           \
+  static_assert(                                                       \
+      ::testing::tuple_size<typename ::testing::internal::Function<    \
+              __VA_ARGS__>::ArgumentTuple>::value == _N,               \
+      "This method does not take " GMOCK_PP_STRINGIZE(                 \
+          _N) " arguments. Parenthesize all types with unprotected commas.")
+
+#define GMOCK_INTERNAL_ASSERT_VALID_SPEC(_Spec) \
+  GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT, ~, _Spec)
+
+#define GMOCK_INTERNAL_MOCK_METHOD_IMPL(_N, _MethodName, _Constness,           \
+                                        _Override, _Final, _NoexceptSpec,      \
+                                        _CallType, _RefSpec, _Signature)       \
+  typename ::testing::internal::Function<GMOCK_PP_REMOVE_PARENS(               \
+      _Signature)>::Result                                                     \
+  GMOCK_INTERNAL_EXPAND(_CallType)                                             \
+      _MethodName(GMOCK_PP_REPEAT(GMOCK_INTERNAL_PARAMETER, _Signature, _N))   \
+          GMOCK_PP_IF(_Constness, const, ) _RefSpec _NoexceptSpec              \
+          GMOCK_PP_IF(_Override, override, ) GMOCK_PP_IF(_Final, final, ) {    \
+    GMOCK_MOCKER_(_N, _Constness, _MethodName)                                 \
+        .SetOwnerAndName(this, #_MethodName);                                  \
+    return GMOCK_MOCKER_(_N, _Constness, _MethodName)                          \
+        .Invoke(GMOCK_PP_REPEAT(GMOCK_INTERNAL_FORWARD_ARG, _Signature, _N));  \
+  }                                                                            \
+  ::testing::MockSpec<GMOCK_PP_REMOVE_PARENS(_Signature)> gmock_##_MethodName( \
+      GMOCK_PP_REPEAT(GMOCK_INTERNAL_MATCHER_PARAMETER, _Signature, _N))       \
+      GMOCK_PP_IF(_Constness, const, ) _RefSpec {                              \
+    GMOCK_MOCKER_(_N, _Constness, _MethodName).RegisterOwner(this);            \
+    return GMOCK_MOCKER_(_N, _Constness, _MethodName)                          \
+        .With(GMOCK_PP_REPEAT(GMOCK_INTERNAL_MATCHER_ARGUMENT, , _N));         \
+  }                                                                            \
+  ::testing::MockSpec<GMOCK_PP_REMOVE_PARENS(_Signature)> gmock_##_MethodName( \
+      const ::testing::internal::WithoutMatchers&,                             \
+      GMOCK_PP_IF(_Constness, const, )::testing::internal::Function<           \
+          GMOCK_PP_REMOVE_PARENS(_Signature)>*) const _RefSpec _NoexceptSpec { \
+    return ::testing::internal::ThisRefAdjuster<GMOCK_PP_IF(                   \
+        _Constness, const, ) int _RefSpec>::Adjust(*this)                      \
+        .gmock_##_MethodName(GMOCK_PP_REPEAT(                                  \
+            GMOCK_INTERNAL_A_MATCHER_ARGUMENT, _Signature, _N));               \
+  }                                                                            \
+  mutable ::testing::FunctionMocker<GMOCK_PP_REMOVE_PARENS(_Signature)>        \
+      GMOCK_MOCKER_(_N, _Constness, _MethodName)
+
+#define GMOCK_INTERNAL_EXPAND(...) __VA_ARGS__
+
+// Five Valid modifiers.
+#define GMOCK_INTERNAL_HAS_CONST(_Tuple) \
+  GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_CONST, ~, _Tuple))
+
+#define GMOCK_INTERNAL_HAS_OVERRIDE(_Tuple) \
+  GMOCK_PP_HAS_COMMA(                       \
+      GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_OVERRIDE, ~, _Tuple))
+
+#define GMOCK_INTERNAL_HAS_FINAL(_Tuple) \
+  GMOCK_PP_HAS_COMMA(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_DETECT_FINAL, ~, _Tuple))
+
+#define GMOCK_INTERNAL_GET_NOEXCEPT_SPEC(_Tuple) \
+  GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_NOEXCEPT_SPEC_IF_NOEXCEPT, ~, _Tuple)
+
+#define GMOCK_INTERNAL_NOEXCEPT_SPEC_IF_NOEXCEPT(_i, _, _elem)          \
+  GMOCK_PP_IF(                                                          \
+      GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)), \
+      _elem, )
+
+#define GMOCK_INTERNAL_GET_REF_SPEC(_Tuple) \
+  GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_REF_SPEC_IF_REF, ~, _Tuple)
+
+#define GMOCK_INTERNAL_REF_SPEC_IF_REF(_i, _, _elem)                       \
+  GMOCK_PP_IF(GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)), \
+              GMOCK_PP_CAT(GMOCK_INTERNAL_UNPACK_, _elem), )
+
+#define GMOCK_INTERNAL_GET_CALLTYPE(_Tuple) \
+  GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_CALLTYPE_IMPL, ~, _Tuple)
+
+#define GMOCK_INTERNAL_ASSERT_VALID_SPEC_ELEMENT(_i, _, _elem)            \
+  static_assert(                                                          \
+      (GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem)) +    \
+       GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem)) + \
+       GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem)) +    \
+       GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem)) + \
+       GMOCK_PP_HAS_COMMA(GMOCK_INTERNAL_DETECT_REF(_i, _, _elem)) +      \
+       GMOCK_INTERNAL_IS_CALLTYPE(_elem)) == 1,                           \
+      GMOCK_PP_STRINGIZE(                                                 \
+          _elem) " cannot be recognized as a valid specification modifier.");
+
+// Modifiers implementation.
+#define GMOCK_INTERNAL_DETECT_CONST(_i, _, _elem) \
+  GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_CONST_I_, _elem)
+
+#define GMOCK_INTERNAL_DETECT_CONST_I_const ,
+
+#define GMOCK_INTERNAL_DETECT_OVERRIDE(_i, _, _elem) \
+  GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_OVERRIDE_I_, _elem)
+
+#define GMOCK_INTERNAL_DETECT_OVERRIDE_I_override ,
+
+#define GMOCK_INTERNAL_DETECT_FINAL(_i, _, _elem) \
+  GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_FINAL_I_, _elem)
+
+#define GMOCK_INTERNAL_DETECT_FINAL_I_final ,
+
+#define GMOCK_INTERNAL_DETECT_NOEXCEPT(_i, _, _elem) \
+  GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_NOEXCEPT_I_, _elem)
+
+#define GMOCK_INTERNAL_DETECT_NOEXCEPT_I_noexcept ,
+
+#define GMOCK_INTERNAL_DETECT_REF(_i, _, _elem) \
+  GMOCK_PP_CAT(GMOCK_INTERNAL_DETECT_REF_I_, _elem)
+
+#define GMOCK_INTERNAL_DETECT_REF_I_ref ,
+
+#define GMOCK_INTERNAL_UNPACK_ref(x) x
+
+#define GMOCK_INTERNAL_GET_CALLTYPE_IMPL(_i, _, _elem)           \
+  GMOCK_PP_IF(GMOCK_INTERNAL_IS_CALLTYPE(_elem),                 \
+              GMOCK_INTERNAL_GET_VALUE_CALLTYPE, GMOCK_PP_EMPTY) \
+  (_elem)
+
+// TODO(iserna): GMOCK_INTERNAL_IS_CALLTYPE and
+// GMOCK_INTERNAL_GET_VALUE_CALLTYPE needed more expansions to work on windows
+// maybe they can be simplified somehow.
+#define GMOCK_INTERNAL_IS_CALLTYPE(_arg) \
+  GMOCK_INTERNAL_IS_CALLTYPE_I(          \
+      GMOCK_PP_CAT(GMOCK_INTERNAL_IS_CALLTYPE_HELPER_, _arg))
+#define GMOCK_INTERNAL_IS_CALLTYPE_I(_arg) GMOCK_PP_IS_ENCLOSED_PARENS(_arg)
+
+#define GMOCK_INTERNAL_GET_VALUE_CALLTYPE(_arg) \
+  GMOCK_INTERNAL_GET_VALUE_CALLTYPE_I(          \
+      GMOCK_PP_CAT(GMOCK_INTERNAL_IS_CALLTYPE_HELPER_, _arg))
+#define GMOCK_INTERNAL_GET_VALUE_CALLTYPE_I(_arg) \
+  GMOCK_PP_IDENTITY _arg
+
+#define GMOCK_INTERNAL_IS_CALLTYPE_HELPER_Calltype
+
+// Note: The use of `identity_t` here allows _Ret to represent return types that
+// would normally need to be specified in a different way. For example, a method
+// returning a function pointer must be written as
+//
+// fn_ptr_return_t (*method(method_args_t...))(fn_ptr_args_t...)
+//
+// But we only support placing the return type at the beginning. To handle this,
+// we wrap all calls in identity_t, so that a declaration will be expanded to
+//
+// identity_t<fn_ptr_return_t (*)(fn_ptr_args_t...)> method(method_args_t...)
+//
+// This allows us to work around the syntactic oddities of function/method
+// types.
+#define GMOCK_INTERNAL_SIGNATURE(_Ret, _Args)                                 \
+  ::testing::internal::identity_t<GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(_Ret), \
+                                              GMOCK_PP_REMOVE_PARENS,         \
+                                              GMOCK_PP_IDENTITY)(_Ret)>(      \
+      GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_GET_TYPE, _, _Args))
+
+#define GMOCK_INTERNAL_GET_TYPE(_i, _, _elem)                          \
+  GMOCK_PP_COMMA_IF(_i)                                                \
+  GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(_elem), GMOCK_PP_REMOVE_PARENS, \
+              GMOCK_PP_IDENTITY)                                       \
+  (_elem)
+
+#define GMOCK_INTERNAL_PARAMETER(_i, _Signature, _)            \
+  GMOCK_PP_COMMA_IF(_i)                                        \
+  GMOCK_INTERNAL_ARG_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature)) \
+  gmock_a##_i
+
+#define GMOCK_INTERNAL_FORWARD_ARG(_i, _Signature, _) \
+  GMOCK_PP_COMMA_IF(_i)                               \
+  ::std::forward<GMOCK_INTERNAL_ARG_O(                \
+      _i, GMOCK_PP_REMOVE_PARENS(_Signature))>(gmock_a##_i)
+
+#define GMOCK_INTERNAL_MATCHER_PARAMETER(_i, _Signature, _)        \
+  GMOCK_PP_COMMA_IF(_i)                                            \
+  GMOCK_INTERNAL_MATCHER_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature)) \
+  gmock_a##_i
+
+#define GMOCK_INTERNAL_MATCHER_ARGUMENT(_i, _1, _2) \
+  GMOCK_PP_COMMA_IF(_i)                             \
+  gmock_a##_i
+
+#define GMOCK_INTERNAL_A_MATCHER_ARGUMENT(_i, _Signature, _) \
+  GMOCK_PP_COMMA_IF(_i)                                      \
+  ::testing::A<GMOCK_INTERNAL_ARG_O(_i, GMOCK_PP_REMOVE_PARENS(_Signature))>()
+
+#define GMOCK_INTERNAL_ARG_O(_i, ...) \
+  typename ::testing::internal::Function<__VA_ARGS__>::template Arg<_i>::type
+
+#define GMOCK_INTERNAL_MATCHER_O(_i, ...)                          \
+  const ::testing::Matcher<typename ::testing::internal::Function< \
+      __VA_ARGS__>::template Arg<_i>::type>&
+
+#define MOCK_METHOD0(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 0, __VA_ARGS__)
+#define MOCK_METHOD1(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 1, __VA_ARGS__)
+#define MOCK_METHOD2(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 2, __VA_ARGS__)
+#define MOCK_METHOD3(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 3, __VA_ARGS__)
+#define MOCK_METHOD4(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 4, __VA_ARGS__)
+#define MOCK_METHOD5(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 5, __VA_ARGS__)
+#define MOCK_METHOD6(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 6, __VA_ARGS__)
+#define MOCK_METHOD7(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 7, __VA_ARGS__)
+#define MOCK_METHOD8(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 8, __VA_ARGS__)
+#define MOCK_METHOD9(m, ...) GMOCK_INTERNAL_MOCK_METHODN(, , m, 9, __VA_ARGS__)
+#define MOCK_METHOD10(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, , m, 10, __VA_ARGS__)
+
+#define MOCK_CONST_METHOD0(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 0, __VA_ARGS__)
+#define MOCK_CONST_METHOD1(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 1, __VA_ARGS__)
+#define MOCK_CONST_METHOD2(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 2, __VA_ARGS__)
+#define MOCK_CONST_METHOD3(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 3, __VA_ARGS__)
+#define MOCK_CONST_METHOD4(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 4, __VA_ARGS__)
+#define MOCK_CONST_METHOD5(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 5, __VA_ARGS__)
+#define MOCK_CONST_METHOD6(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 6, __VA_ARGS__)
+#define MOCK_CONST_METHOD7(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 7, __VA_ARGS__)
+#define MOCK_CONST_METHOD8(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 8, __VA_ARGS__)
+#define MOCK_CONST_METHOD9(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 9, __VA_ARGS__)
+#define MOCK_CONST_METHOD10(m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, , m, 10, __VA_ARGS__)
+
+#define MOCK_METHOD0_T(m, ...) MOCK_METHOD0(m, __VA_ARGS__)
+#define MOCK_METHOD1_T(m, ...) MOCK_METHOD1(m, __VA_ARGS__)
+#define MOCK_METHOD2_T(m, ...) MOCK_METHOD2(m, __VA_ARGS__)
+#define MOCK_METHOD3_T(m, ...) MOCK_METHOD3(m, __VA_ARGS__)
+#define MOCK_METHOD4_T(m, ...) MOCK_METHOD4(m, __VA_ARGS__)
+#define MOCK_METHOD5_T(m, ...) MOCK_METHOD5(m, __VA_ARGS__)
+#define MOCK_METHOD6_T(m, ...) MOCK_METHOD6(m, __VA_ARGS__)
+#define MOCK_METHOD7_T(m, ...) MOCK_METHOD7(m, __VA_ARGS__)
+#define MOCK_METHOD8_T(m, ...) MOCK_METHOD8(m, __VA_ARGS__)
+#define MOCK_METHOD9_T(m, ...) MOCK_METHOD9(m, __VA_ARGS__)
+#define MOCK_METHOD10_T(m, ...) MOCK_METHOD10(m, __VA_ARGS__)
+
+#define MOCK_CONST_METHOD0_T(m, ...) MOCK_CONST_METHOD0(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD1_T(m, ...) MOCK_CONST_METHOD1(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD2_T(m, ...) MOCK_CONST_METHOD2(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD3_T(m, ...) MOCK_CONST_METHOD3(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD4_T(m, ...) MOCK_CONST_METHOD4(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD5_T(m, ...) MOCK_CONST_METHOD5(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD6_T(m, ...) MOCK_CONST_METHOD6(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD7_T(m, ...) MOCK_CONST_METHOD7(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD8_T(m, ...) MOCK_CONST_METHOD8(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD9_T(m, ...) MOCK_CONST_METHOD9(m, __VA_ARGS__)
+#define MOCK_CONST_METHOD10_T(m, ...) MOCK_CONST_METHOD10(m, __VA_ARGS__)
+
+#define MOCK_METHOD0_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 0, __VA_ARGS__)
+#define MOCK_METHOD1_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 1, __VA_ARGS__)
+#define MOCK_METHOD2_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 2, __VA_ARGS__)
+#define MOCK_METHOD3_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 3, __VA_ARGS__)
+#define MOCK_METHOD4_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 4, __VA_ARGS__)
+#define MOCK_METHOD5_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 5, __VA_ARGS__)
+#define MOCK_METHOD6_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 6, __VA_ARGS__)
+#define MOCK_METHOD7_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 7, __VA_ARGS__)
+#define MOCK_METHOD8_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 8, __VA_ARGS__)
+#define MOCK_METHOD9_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 9, __VA_ARGS__)
+#define MOCK_METHOD10_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(, ct, m, 10, __VA_ARGS__)
+
+#define MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 0, __VA_ARGS__)
+#define MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 1, __VA_ARGS__)
+#define MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 2, __VA_ARGS__)
+#define MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 3, __VA_ARGS__)
+#define MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 4, __VA_ARGS__)
+#define MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 5, __VA_ARGS__)
+#define MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 6, __VA_ARGS__)
+#define MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 7, __VA_ARGS__)
+#define MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 8, __VA_ARGS__)
+#define MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 9, __VA_ARGS__)
+#define MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, ...) \
+  GMOCK_INTERNAL_MOCK_METHODN(const, ct, m, 10, __VA_ARGS__)
+
+#define MOCK_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD0_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD1_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD2_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD3_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD4_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD5_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD6_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD7_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD8_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD9_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_METHOD10_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+
+#define MOCK_CONST_METHOD0_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD0_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD1_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD1_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD2_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD2_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD3_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD3_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD4_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD4_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD5_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD5_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD6_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD6_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD7_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD7_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD8_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD8_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD9_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD9_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+#define MOCK_CONST_METHOD10_T_WITH_CALLTYPE(ct, m, ...) \
+  MOCK_CONST_METHOD10_WITH_CALLTYPE(ct, m, __VA_ARGS__)
+
+#define GMOCK_INTERNAL_MOCK_METHODN(constness, ct, Method, args_num, ...) \
+  GMOCK_INTERNAL_ASSERT_VALID_SIGNATURE(                                  \
+      args_num, ::testing::internal::identity_t<__VA_ARGS__>);            \
+  GMOCK_INTERNAL_MOCK_METHOD_IMPL(                                        \
+      args_num, Method, GMOCK_PP_NARG0(constness), 0, 0, , ct, ,          \
+      (::testing::internal::identity_t<__VA_ARGS__>))
+
+#define GMOCK_MOCKER_(arity, constness, Method) \
+  GTEST_CONCAT_TOKEN_(gmock##constness##arity##_##Method##_, __LINE__)
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_FUNCTION_MOCKER_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-matchers.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-matchers.h
new file mode 100644 (file)
index 0000000..dc213a0
--- /dev/null
@@ -0,0 +1,5547 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// The MATCHER* family of macros can be used in a namespace scope to
+// define custom matchers easily.
+//
+// Basic Usage
+// ===========
+//
+// The syntax
+//
+//   MATCHER(name, description_string) { statements; }
+//
+// defines a matcher with the given name that executes the statements,
+// which must return a bool to indicate if the match succeeds.  Inside
+// the statements, you can refer to the value being matched by 'arg',
+// and refer to its type by 'arg_type'.
+//
+// The description string documents what the matcher does, and is used
+// to generate the failure message when the match fails.  Since a
+// MATCHER() is usually defined in a header file shared by multiple
+// C++ source files, we require the description to be a C-string
+// literal to avoid possible side effects.  It can be empty, in which
+// case we'll use the sequence of words in the matcher name as the
+// description.
+//
+// For example:
+//
+//   MATCHER(IsEven, "") { return (arg % 2) == 0; }
+//
+// allows you to write
+//
+//   // Expects mock_foo.Bar(n) to be called where n is even.
+//   EXPECT_CALL(mock_foo, Bar(IsEven()));
+//
+// or,
+//
+//   // Verifies that the value of some_expression is even.
+//   EXPECT_THAT(some_expression, IsEven());
+//
+// If the above assertion fails, it will print something like:
+//
+//   Value of: some_expression
+//   Expected: is even
+//     Actual: 7
+//
+// where the description "is even" is automatically calculated from the
+// matcher name IsEven.
+//
+// Argument Type
+// =============
+//
+// Note that the type of the value being matched (arg_type) is
+// determined by the context in which you use the matcher and is
+// supplied to you by the compiler, so you don't need to worry about
+// declaring it (nor can you).  This allows the matcher to be
+// polymorphic.  For example, IsEven() can be used to match any type
+// where the value of "(arg % 2) == 0" can be implicitly converted to
+// a bool.  In the "Bar(IsEven())" example above, if method Bar()
+// takes an int, 'arg_type' will be int; if it takes an unsigned long,
+// 'arg_type' will be unsigned long; and so on.
+//
+// Parameterizing Matchers
+// =======================
+//
+// Sometimes you'll want to parameterize the matcher.  For that you
+// can use another macro:
+//
+//   MATCHER_P(name, param_name, description_string) { statements; }
+//
+// For example:
+//
+//   MATCHER_P(HasAbsoluteValue, value, "") { return abs(arg) == value; }
+//
+// will allow you to write:
+//
+//   EXPECT_THAT(Blah("a"), HasAbsoluteValue(n));
+//
+// which may lead to this message (assuming n is 10):
+//
+//   Value of: Blah("a")
+//   Expected: has absolute value 10
+//     Actual: -9
+//
+// Note that both the matcher description and its parameter are
+// printed, making the message human-friendly.
+//
+// In the matcher definition body, you can write 'foo_type' to
+// reference the type of a parameter named 'foo'.  For example, in the
+// body of MATCHER_P(HasAbsoluteValue, value) above, you can write
+// 'value_type' to refer to the type of 'value'.
+//
+// We also provide MATCHER_P2, MATCHER_P3, ..., up to MATCHER_P$n to
+// support multi-parameter matchers.
+//
+// Describing Parameterized Matchers
+// =================================
+//
+// The last argument to MATCHER*() is a string-typed expression.  The
+// expression can reference all of the matcher's parameters and a
+// special bool-typed variable named 'negation'.  When 'negation' is
+// false, the expression should evaluate to the matcher's description;
+// otherwise it should evaluate to the description of the negation of
+// the matcher.  For example,
+//
+//   using testing::PrintToString;
+//
+//   MATCHER_P2(InClosedRange, low, hi,
+//       std::string(negation ? "is not" : "is") + " in range [" +
+//       PrintToString(low) + ", " + PrintToString(hi) + "]") {
+//     return low <= arg && arg <= hi;
+//   }
+//   ...
+//   EXPECT_THAT(3, InClosedRange(4, 6));
+//   EXPECT_THAT(3, Not(InClosedRange(2, 4)));
+//
+// would generate two failures that contain the text:
+//
+//   Expected: is in range [4, 6]
+//   ...
+//   Expected: is not in range [2, 4]
+//
+// If you specify "" as the description, the failure message will
+// contain the sequence of words in the matcher name followed by the
+// parameter values printed as a tuple.  For example,
+//
+//   MATCHER_P2(InClosedRange, low, hi, "") { ... }
+//   ...
+//   EXPECT_THAT(3, InClosedRange(4, 6));
+//   EXPECT_THAT(3, Not(InClosedRange(2, 4)));
+//
+// would generate two failures that contain the text:
+//
+//   Expected: in closed range (4, 6)
+//   ...
+//   Expected: not (in closed range (2, 4))
+//
+// Types of Matcher Parameters
+// ===========================
+//
+// For the purpose of typing, you can view
+//
+//   MATCHER_Pk(Foo, p1, ..., pk, description_string) { ... }
+//
+// as shorthand for
+//
+//   template <typename p1_type, ..., typename pk_type>
+//   FooMatcherPk<p1_type, ..., pk_type>
+//   Foo(p1_type p1, ..., pk_type pk) { ... }
+//
+// When you write Foo(v1, ..., vk), the compiler infers the types of
+// the parameters v1, ..., and vk for you.  If you are not happy with
+// the result of the type inference, you can specify the types by
+// explicitly instantiating the template, as in Foo<long, bool>(5,
+// false).  As said earlier, you don't get to (or need to) specify
+// 'arg_type' as that's determined by the context in which the matcher
+// is used.  You can assign the result of expression Foo(p1, ..., pk)
+// to a variable of type FooMatcherPk<p1_type, ..., pk_type>.  This
+// can be useful when composing matchers.
+//
+// While you can instantiate a matcher template with reference types,
+// passing the parameters by pointer usually makes your code more
+// readable.  If, however, you still want to pass a parameter by
+// reference, be aware that in the failure message generated by the
+// matcher you will see the value of the referenced object but not its
+// address.
+//
+// Explaining Match Results
+// ========================
+//
+// Sometimes the matcher description alone isn't enough to explain why
+// the match has failed or succeeded.  For example, when expecting a
+// long string, it can be very helpful to also print the diff between
+// the expected string and the actual one.  To achieve that, you can
+// optionally stream additional information to a special variable
+// named result_listener, whose type is a pointer to class
+// MatchResultListener:
+//
+//   MATCHER_P(EqualsLongString, str, "") {
+//     if (arg == str) return true;
+//
+//     *result_listener << "the difference: "
+///                     << DiffStrings(str, arg);
+//     return false;
+//   }
+//
+// Overloading Matchers
+// ====================
+//
+// You can overload matchers with different numbers of parameters:
+//
+//   MATCHER_P(Blah, a, description_string1) { ... }
+//   MATCHER_P2(Blah, a, b, description_string2) { ... }
+//
+// Caveats
+// =======
+//
+// When defining a new matcher, you should also consider implementing
+// MatcherInterface or using MakePolymorphicMatcher().  These
+// approaches require more work than the MATCHER* macros, but also
+// give you more control on the types of the value being matched and
+// the matcher parameters, which may leads to better compiler error
+// messages when the matcher is used wrong.  They also allow
+// overloading matchers based on parameter types (as opposed to just
+// based on the number of parameters).
+//
+// MATCHER*() can only be used in a namespace scope as templates cannot be
+// declared inside of a local class.
+//
+// More Information
+// ================
+//
+// To learn more about using these macros, please search for 'MATCHER'
+// on
+// https://github.com/google/googletest/blob/master/docs/gmock_cook_book.md
+//
+// This file also implements some commonly used argument matchers.  More
+// matchers can be defined by the user implementing the
+// MatcherInterface<T> interface if necessary.
+//
+// See googletest/include/gtest/gtest-matchers.h for the definition of class
+// Matcher, class MatcherInterface, and others.
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
+
+#include <algorithm>
+#include <cmath>
+#include <initializer_list>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <ostream>  // NOLINT
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "gmock/internal/gmock-internal-utils.h"
+#include "gmock/internal/gmock-port.h"
+#include "gmock/internal/gmock-pp.h"
+#include "gtest/gtest.h"
+
+// MSVC warning C5046 is new as of VS2017 version 15.8.
+#if defined(_MSC_VER) && _MSC_VER >= 1915
+#define GMOCK_MAYBE_5046_ 5046
+#else
+#define GMOCK_MAYBE_5046_
+#endif
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(
+    4251 GMOCK_MAYBE_5046_ /* class A needs to have dll-interface to be used by
+                              clients of class B */
+    /* Symbol involving type with internal linkage not defined */)
+
+namespace testing {
+
+// To implement a matcher Foo for type T, define:
+//   1. a class FooMatcherImpl that implements the
+//      MatcherInterface<T> interface, and
+//   2. a factory function that creates a Matcher<T> object from a
+//      FooMatcherImpl*.
+//
+// The two-level delegation design makes it possible to allow a user
+// to write "v" instead of "Eq(v)" where a Matcher is expected, which
+// is impossible if we pass matchers by pointers.  It also eases
+// ownership management as Matcher objects can now be copied like
+// plain values.
+
+// A match result listener that stores the explanation in a string.
+class StringMatchResultListener : public MatchResultListener {
+ public:
+  StringMatchResultListener() : MatchResultListener(&ss_) {}
+
+  // Returns the explanation accumulated so far.
+  std::string str() const { return ss_.str(); }
+
+  // Clears the explanation accumulated so far.
+  void Clear() { ss_.str(""); }
+
+ private:
+  ::std::stringstream ss_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(StringMatchResultListener);
+};
+
+// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION
+// and MUST NOT BE USED IN USER CODE!!!
+namespace internal {
+
+// The MatcherCastImpl class template is a helper for implementing
+// MatcherCast().  We need this helper in order to partially
+// specialize the implementation of MatcherCast() (C++ allows
+// class/struct templates to be partially specialized, but not
+// function templates.).
+
+// This general version is used when MatcherCast()'s argument is a
+// polymorphic matcher (i.e. something that can be converted to a
+// Matcher but is not one yet; for example, Eq(value)) or a value (for
+// example, "hello").
+template <typename T, typename M>
+class MatcherCastImpl {
+ public:
+  static Matcher<T> Cast(const M& polymorphic_matcher_or_value) {
+    // M can be a polymorphic matcher, in which case we want to use
+    // its conversion operator to create Matcher<T>.  Or it can be a value
+    // that should be passed to the Matcher<T>'s constructor.
+    //
+    // We can't call Matcher<T>(polymorphic_matcher_or_value) when M is a
+    // polymorphic matcher because it'll be ambiguous if T has an implicit
+    // constructor from M (this usually happens when T has an implicit
+    // constructor from any type).
+    //
+    // It won't work to unconditionally implicit_cast
+    // polymorphic_matcher_or_value to Matcher<T> because it won't trigger
+    // a user-defined conversion from M to T if one exists (assuming M is
+    // a value).
+    return CastImpl(polymorphic_matcher_or_value,
+                    std::is_convertible<M, Matcher<T>>{},
+                    std::is_convertible<M, T>{});
+  }
+
+ private:
+  template <bool Ignore>
+  static Matcher<T> CastImpl(const M& polymorphic_matcher_or_value,
+                             std::true_type /* convertible_to_matcher */,
+                             std::integral_constant<bool, Ignore>) {
+    // M is implicitly convertible to Matcher<T>, which means that either
+    // M is a polymorphic matcher or Matcher<T> has an implicit constructor
+    // from M.  In both cases using the implicit conversion will produce a
+    // matcher.
+    //
+    // Even if T has an implicit constructor from M, it won't be called because
+    // creating Matcher<T> would require a chain of two user-defined conversions
+    // (first to create T from M and then to create Matcher<T> from T).
+    return polymorphic_matcher_or_value;
+  }
+
+  // M can't be implicitly converted to Matcher<T>, so M isn't a polymorphic
+  // matcher. It's a value of a type implicitly convertible to T. Use direct
+  // initialization to create a matcher.
+  static Matcher<T> CastImpl(const M& value,
+                             std::false_type /* convertible_to_matcher */,
+                             std::true_type /* convertible_to_T */) {
+    return Matcher<T>(ImplicitCast_<T>(value));
+  }
+
+  // M can't be implicitly converted to either Matcher<T> or T. Attempt to use
+  // polymorphic matcher Eq(value) in this case.
+  //
+  // Note that we first attempt to perform an implicit cast on the value and
+  // only fall back to the polymorphic Eq() matcher afterwards because the
+  // latter calls bool operator==(const Lhs& lhs, const Rhs& rhs) in the end
+  // which might be undefined even when Rhs is implicitly convertible to Lhs
+  // (e.g. std::pair<const int, int> vs. std::pair<int, int>).
+  //
+  // We don't define this method inline as we need the declaration of Eq().
+  static Matcher<T> CastImpl(const M& value,
+                             std::false_type /* convertible_to_matcher */,
+                             std::false_type /* convertible_to_T */);
+};
+
+// This more specialized version is used when MatcherCast()'s argument
+// is already a Matcher.  This only compiles when type T can be
+// statically converted to type U.
+template <typename T, typename U>
+class MatcherCastImpl<T, Matcher<U> > {
+ public:
+  static Matcher<T> Cast(const Matcher<U>& source_matcher) {
+    return Matcher<T>(new Impl(source_matcher));
+  }
+
+ private:
+  class Impl : public MatcherInterface<T> {
+   public:
+    explicit Impl(const Matcher<U>& source_matcher)
+        : source_matcher_(source_matcher) {}
+
+    // We delegate the matching logic to the source matcher.
+    bool MatchAndExplain(T x, MatchResultListener* listener) const override {
+      using FromType = typename std::remove_cv<typename std::remove_pointer<
+          typename std::remove_reference<T>::type>::type>::type;
+      using ToType = typename std::remove_cv<typename std::remove_pointer<
+          typename std::remove_reference<U>::type>::type>::type;
+      // Do not allow implicitly converting base*/& to derived*/&.
+      static_assert(
+          // Do not trigger if only one of them is a pointer. That implies a
+          // regular conversion and not a down_cast.
+          (std::is_pointer<typename std::remove_reference<T>::type>::value !=
+           std::is_pointer<typename std::remove_reference<U>::type>::value) ||
+              std::is_same<FromType, ToType>::value ||
+              !std::is_base_of<FromType, ToType>::value,
+          "Can't implicitly convert from <base> to <derived>");
+
+      // Do the cast to `U` explicitly if necessary.
+      // Otherwise, let implicit conversions do the trick.
+      using CastType =
+          typename std::conditional<std::is_convertible<T&, const U&>::value,
+                                    T&, U>::type;
+
+      return source_matcher_.MatchAndExplain(static_cast<CastType>(x),
+                                             listener);
+    }
+
+    void DescribeTo(::std::ostream* os) const override {
+      source_matcher_.DescribeTo(os);
+    }
+
+    void DescribeNegationTo(::std::ostream* os) const override {
+      source_matcher_.DescribeNegationTo(os);
+    }
+
+   private:
+    const Matcher<U> source_matcher_;
+  };
+};
+
+// This even more specialized version is used for efficiently casting
+// a matcher to its own type.
+template <typename T>
+class MatcherCastImpl<T, Matcher<T> > {
+ public:
+  static Matcher<T> Cast(const Matcher<T>& matcher) { return matcher; }
+};
+
+// Template specialization for parameterless Matcher.
+template <typename Derived>
+class MatcherBaseImpl {
+ public:
+  MatcherBaseImpl() = default;
+
+  template <typename T>
+  operator ::testing::Matcher<T>() const {  // NOLINT(runtime/explicit)
+    return ::testing::Matcher<T>(new
+                                 typename Derived::template gmock_Impl<T>());
+  }
+};
+
+// Template specialization for Matcher with parameters.
+template <template <typename...> class Derived, typename... Ts>
+class MatcherBaseImpl<Derived<Ts...>> {
+ public:
+  // Mark the constructor explicit for single argument T to avoid implicit
+  // conversions.
+  template <typename E = std::enable_if<sizeof...(Ts) == 1>,
+            typename E::type* = nullptr>
+  explicit MatcherBaseImpl(Ts... params)
+      : params_(std::forward<Ts>(params)...) {}
+  template <typename E = std::enable_if<sizeof...(Ts) != 1>,
+            typename = typename E::type>
+  MatcherBaseImpl(Ts... params)  // NOLINT
+      : params_(std::forward<Ts>(params)...) {}
+
+  template <typename F>
+  operator ::testing::Matcher<F>() const {  // NOLINT(runtime/explicit)
+    return Apply<F>(MakeIndexSequence<sizeof...(Ts)>{});
+  }
+
+ private:
+  template <typename F, std::size_t... tuple_ids>
+  ::testing::Matcher<F> Apply(IndexSequence<tuple_ids...>) const {
+    return ::testing::Matcher<F>(
+        new typename Derived<Ts...>::template gmock_Impl<F>(
+            std::get<tuple_ids>(params_)...));
+  }
+
+  const std::tuple<Ts...> params_;
+};
+
+}  // namespace internal
+
+// In order to be safe and clear, casting between different matcher
+// types is done explicitly via MatcherCast<T>(m), which takes a
+// matcher m and returns a Matcher<T>.  It compiles only when T can be
+// statically converted to the argument type of m.
+template <typename T, typename M>
+inline Matcher<T> MatcherCast(const M& matcher) {
+  return internal::MatcherCastImpl<T, M>::Cast(matcher);
+}
+
+// This overload handles polymorphic matchers and values only since
+// monomorphic matchers are handled by the next one.
+template <typename T, typename M>
+inline Matcher<T> SafeMatcherCast(const M& polymorphic_matcher_or_value) {
+  return MatcherCast<T>(polymorphic_matcher_or_value);
+}
+
+// This overload handles monomorphic matchers.
+//
+// In general, if type T can be implicitly converted to type U, we can
+// safely convert a Matcher<U> to a Matcher<T> (i.e. Matcher is
+// contravariant): just keep a copy of the original Matcher<U>, convert the
+// argument from type T to U, and then pass it to the underlying Matcher<U>.
+// The only exception is when U is a reference and T is not, as the
+// underlying Matcher<U> may be interested in the argument's address, which
+// is not preserved in the conversion from T to U.
+template <typename T, typename U>
+inline Matcher<T> SafeMatcherCast(const Matcher<U>& matcher) {
+  // Enforce that T can be implicitly converted to U.
+  static_assert(std::is_convertible<const T&, const U&>::value,
+                "T must be implicitly convertible to U");
+  // Enforce that we are not converting a non-reference type T to a reference
+  // type U.
+  GTEST_COMPILE_ASSERT_(
+      std::is_reference<T>::value || !std::is_reference<U>::value,
+      cannot_convert_non_reference_arg_to_reference);
+  // In case both T and U are arithmetic types, enforce that the
+  // conversion is not lossy.
+  typedef GTEST_REMOVE_REFERENCE_AND_CONST_(T) RawT;
+  typedef GTEST_REMOVE_REFERENCE_AND_CONST_(U) RawU;
+  constexpr bool kTIsOther = GMOCK_KIND_OF_(RawT) == internal::kOther;
+  constexpr bool kUIsOther = GMOCK_KIND_OF_(RawU) == internal::kOther;
+  GTEST_COMPILE_ASSERT_(
+      kTIsOther || kUIsOther ||
+      (internal::LosslessArithmeticConvertible<RawT, RawU>::value),
+      conversion_of_arithmetic_types_must_be_lossless);
+  return MatcherCast<T>(matcher);
+}
+
+// A<T>() returns a matcher that matches any value of type T.
+template <typename T>
+Matcher<T> A();
+
+// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION
+// and MUST NOT BE USED IN USER CODE!!!
+namespace internal {
+
+// If the explanation is not empty, prints it to the ostream.
+inline void PrintIfNotEmpty(const std::string& explanation,
+                            ::std::ostream* os) {
+  if (explanation != "" && os != nullptr) {
+    *os << ", " << explanation;
+  }
+}
+
+// Returns true if the given type name is easy to read by a human.
+// This is used to decide whether printing the type of a value might
+// be helpful.
+inline bool IsReadableTypeName(const std::string& type_name) {
+  // We consider a type name readable if it's short or doesn't contain
+  // a template or function type.
+  return (type_name.length() <= 20 ||
+          type_name.find_first_of("<(") == std::string::npos);
+}
+
+// Matches the value against the given matcher, prints the value and explains
+// the match result to the listener. Returns the match result.
+// 'listener' must not be NULL.
+// Value cannot be passed by const reference, because some matchers take a
+// non-const argument.
+template <typename Value, typename T>
+bool MatchPrintAndExplain(Value& value, const Matcher<T>& matcher,
+                          MatchResultListener* listener) {
+  if (!listener->IsInterested()) {
+    // If the listener is not interested, we do not need to construct the
+    // inner explanation.
+    return matcher.Matches(value);
+  }
+
+  StringMatchResultListener inner_listener;
+  const bool match = matcher.MatchAndExplain(value, &inner_listener);
+
+  UniversalPrint(value, listener->stream());
+#if GTEST_HAS_RTTI
+  const std::string& type_name = GetTypeName<Value>();
+  if (IsReadableTypeName(type_name))
+    *listener->stream() << " (of type " << type_name << ")";
+#endif
+  PrintIfNotEmpty(inner_listener.str(), listener->stream());
+
+  return match;
+}
+
+// An internal helper class for doing compile-time loop on a tuple's
+// fields.
+template <size_t N>
+class TuplePrefix {
+ public:
+  // TuplePrefix<N>::Matches(matcher_tuple, value_tuple) returns true
+  // if and only if the first N fields of matcher_tuple matches
+  // the first N fields of value_tuple, respectively.
+  template <typename MatcherTuple, typename ValueTuple>
+  static bool Matches(const MatcherTuple& matcher_tuple,
+                      const ValueTuple& value_tuple) {
+    return TuplePrefix<N - 1>::Matches(matcher_tuple, value_tuple) &&
+           std::get<N - 1>(matcher_tuple).Matches(std::get<N - 1>(value_tuple));
+  }
+
+  // TuplePrefix<N>::ExplainMatchFailuresTo(matchers, values, os)
+  // describes failures in matching the first N fields of matchers
+  // against the first N fields of values.  If there is no failure,
+  // nothing will be streamed to os.
+  template <typename MatcherTuple, typename ValueTuple>
+  static void ExplainMatchFailuresTo(const MatcherTuple& matchers,
+                                     const ValueTuple& values,
+                                     ::std::ostream* os) {
+    // First, describes failures in the first N - 1 fields.
+    TuplePrefix<N - 1>::ExplainMatchFailuresTo(matchers, values, os);
+
+    // Then describes the failure (if any) in the (N - 1)-th (0-based)
+    // field.
+    typename std::tuple_element<N - 1, MatcherTuple>::type matcher =
+        std::get<N - 1>(matchers);
+    typedef typename std::tuple_element<N - 1, ValueTuple>::type Value;
+    const Value& value = std::get<N - 1>(values);
+    StringMatchResultListener listener;
+    if (!matcher.MatchAndExplain(value, &listener)) {
+      *os << "  Expected arg #" << N - 1 << ": ";
+      std::get<N - 1>(matchers).DescribeTo(os);
+      *os << "\n           Actual: ";
+      // We remove the reference in type Value to prevent the
+      // universal printer from printing the address of value, which
+      // isn't interesting to the user most of the time.  The
+      // matcher's MatchAndExplain() method handles the case when
+      // the address is interesting.
+      internal::UniversalPrint(value, os);
+      PrintIfNotEmpty(listener.str(), os);
+      *os << "\n";
+    }
+  }
+};
+
+// The base case.
+template <>
+class TuplePrefix<0> {
+ public:
+  template <typename MatcherTuple, typename ValueTuple>
+  static bool Matches(const MatcherTuple& /* matcher_tuple */,
+                      const ValueTuple& /* value_tuple */) {
+    return true;
+  }
+
+  template <typename MatcherTuple, typename ValueTuple>
+  static void ExplainMatchFailuresTo(const MatcherTuple& /* matchers */,
+                                     const ValueTuple& /* values */,
+                                     ::std::ostream* /* os */) {}
+};
+
+// TupleMatches(matcher_tuple, value_tuple) returns true if and only if
+// all matchers in matcher_tuple match the corresponding fields in
+// value_tuple.  It is a compiler error if matcher_tuple and
+// value_tuple have different number of fields or incompatible field
+// types.
+template <typename MatcherTuple, typename ValueTuple>
+bool TupleMatches(const MatcherTuple& matcher_tuple,
+                  const ValueTuple& value_tuple) {
+  // Makes sure that matcher_tuple and value_tuple have the same
+  // number of fields.
+  GTEST_COMPILE_ASSERT_(std::tuple_size<MatcherTuple>::value ==
+                            std::tuple_size<ValueTuple>::value,
+                        matcher_and_value_have_different_numbers_of_fields);
+  return TuplePrefix<std::tuple_size<ValueTuple>::value>::Matches(matcher_tuple,
+                                                                  value_tuple);
+}
+
+// Describes failures in matching matchers against values.  If there
+// is no failure, nothing will be streamed to os.
+template <typename MatcherTuple, typename ValueTuple>
+void ExplainMatchFailureTupleTo(const MatcherTuple& matchers,
+                                const ValueTuple& values,
+                                ::std::ostream* os) {
+  TuplePrefix<std::tuple_size<MatcherTuple>::value>::ExplainMatchFailuresTo(
+      matchers, values, os);
+}
+
+// TransformTupleValues and its helper.
+//
+// TransformTupleValuesHelper hides the internal machinery that
+// TransformTupleValues uses to implement a tuple traversal.
+template <typename Tuple, typename Func, typename OutIter>
+class TransformTupleValuesHelper {
+ private:
+  typedef ::std::tuple_size<Tuple> TupleSize;
+
+ public:
+  // For each member of tuple 't', taken in order, evaluates '*out++ = f(t)'.
+  // Returns the final value of 'out' in case the caller needs it.
+  static OutIter Run(Func f, const Tuple& t, OutIter out) {
+    return IterateOverTuple<Tuple, TupleSize::value>()(f, t, out);
+  }
+
+ private:
+  template <typename Tup, size_t kRemainingSize>
+  struct IterateOverTuple {
+    OutIter operator() (Func f, const Tup& t, OutIter out) const {
+      *out++ = f(::std::get<TupleSize::value - kRemainingSize>(t));
+      return IterateOverTuple<Tup, kRemainingSize - 1>()(f, t, out);
+    }
+  };
+  template <typename Tup>
+  struct IterateOverTuple<Tup, 0> {
+    OutIter operator() (Func /* f */, const Tup& /* t */, OutIter out) const {
+      return out;
+    }
+  };
+};
+
+// Successively invokes 'f(element)' on each element of the tuple 't',
+// appending each result to the 'out' iterator. Returns the final value
+// of 'out'.
+template <typename Tuple, typename Func, typename OutIter>
+OutIter TransformTupleValues(Func f, const Tuple& t, OutIter out) {
+  return TransformTupleValuesHelper<Tuple, Func, OutIter>::Run(f, t, out);
+}
+
+// Implements _, a matcher that matches any value of any
+// type.  This is a polymorphic matcher, so we need a template type
+// conversion operator to make it appearing as a Matcher<T> for any
+// type T.
+class AnythingMatcher {
+ public:
+  using is_gtest_matcher = void;
+
+  template <typename T>
+  bool MatchAndExplain(const T& /* x */, std::ostream* /* listener */) const {
+    return true;
+  }
+  void DescribeTo(std::ostream* os) const { *os << "is anything"; }
+  void DescribeNegationTo(::std::ostream* os) const {
+    // This is mostly for completeness' sake, as it's not very useful
+    // to write Not(A<bool>()).  However we cannot completely rule out
+    // such a possibility, and it doesn't hurt to be prepared.
+    *os << "never matches";
+  }
+};
+
+// Implements the polymorphic IsNull() matcher, which matches any raw or smart
+// pointer that is NULL.
+class IsNullMatcher {
+ public:
+  template <typename Pointer>
+  bool MatchAndExplain(const Pointer& p,
+                       MatchResultListener* /* listener */) const {
+    return p == nullptr;
+  }
+
+  void DescribeTo(::std::ostream* os) const { *os << "is NULL"; }
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "isn't NULL";
+  }
+};
+
+// Implements the polymorphic NotNull() matcher, which matches any raw or smart
+// pointer that is not NULL.
+class NotNullMatcher {
+ public:
+  template <typename Pointer>
+  bool MatchAndExplain(const Pointer& p,
+                       MatchResultListener* /* listener */) const {
+    return p != nullptr;
+  }
+
+  void DescribeTo(::std::ostream* os) const { *os << "isn't NULL"; }
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "is NULL";
+  }
+};
+
+// Ref(variable) matches any argument that is a reference to
+// 'variable'.  This matcher is polymorphic as it can match any
+// super type of the type of 'variable'.
+//
+// The RefMatcher template class implements Ref(variable).  It can
+// only be instantiated with a reference type.  This prevents a user
+// from mistakenly using Ref(x) to match a non-reference function
+// argument.  For example, the following will righteously cause a
+// compiler error:
+//
+//   int n;
+//   Matcher<int> m1 = Ref(n);   // This won't compile.
+//   Matcher<int&> m2 = Ref(n);  // This will compile.
+template <typename T>
+class RefMatcher;
+
+template <typename T>
+class RefMatcher<T&> {
+  // Google Mock is a generic framework and thus needs to support
+  // mocking any function types, including those that take non-const
+  // reference arguments.  Therefore the template parameter T (and
+  // Super below) can be instantiated to either a const type or a
+  // non-const type.
+ public:
+  // RefMatcher() takes a T& instead of const T&, as we want the
+  // compiler to catch using Ref(const_value) as a matcher for a
+  // non-const reference.
+  explicit RefMatcher(T& x) : object_(x) {}  // NOLINT
+
+  template <typename Super>
+  operator Matcher<Super&>() const {
+    // By passing object_ (type T&) to Impl(), which expects a Super&,
+    // we make sure that Super is a super type of T.  In particular,
+    // this catches using Ref(const_value) as a matcher for a
+    // non-const reference, as you cannot implicitly convert a const
+    // reference to a non-const reference.
+    return MakeMatcher(new Impl<Super>(object_));
+  }
+
+ private:
+  template <typename Super>
+  class Impl : public MatcherInterface<Super&> {
+   public:
+    explicit Impl(Super& x) : object_(x) {}  // NOLINT
+
+    // MatchAndExplain() takes a Super& (as opposed to const Super&)
+    // in order to match the interface MatcherInterface<Super&>.
+    bool MatchAndExplain(Super& x,
+                         MatchResultListener* listener) const override {
+      *listener << "which is located @" << static_cast<const void*>(&x);
+      return &x == &object_;
+    }
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "references the variable ";
+      UniversalPrinter<Super&>::Print(object_, os);
+    }
+
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "does not reference the variable ";
+      UniversalPrinter<Super&>::Print(object_, os);
+    }
+
+   private:
+    const Super& object_;
+  };
+
+  T& object_;
+};
+
+// Polymorphic helper functions for narrow and wide string matchers.
+inline bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs) {
+  return String::CaseInsensitiveCStringEquals(lhs, rhs);
+}
+
+inline bool CaseInsensitiveCStringEquals(const wchar_t* lhs,
+                                         const wchar_t* rhs) {
+  return String::CaseInsensitiveWideCStringEquals(lhs, rhs);
+}
+
+// String comparison for narrow or wide strings that can have embedded NUL
+// characters.
+template <typename StringType>
+bool CaseInsensitiveStringEquals(const StringType& s1,
+                                 const StringType& s2) {
+  // Are the heads equal?
+  if (!CaseInsensitiveCStringEquals(s1.c_str(), s2.c_str())) {
+    return false;
+  }
+
+  // Skip the equal heads.
+  const typename StringType::value_type nul = 0;
+  const size_t i1 = s1.find(nul), i2 = s2.find(nul);
+
+  // Are we at the end of either s1 or s2?
+  if (i1 == StringType::npos || i2 == StringType::npos) {
+    return i1 == i2;
+  }
+
+  // Are the tails equal?
+  return CaseInsensitiveStringEquals(s1.substr(i1 + 1), s2.substr(i2 + 1));
+}
+
+// String matchers.
+
+// Implements equality-based string matchers like StrEq, StrCaseNe, and etc.
+template <typename StringType>
+class StrEqualityMatcher {
+ public:
+  StrEqualityMatcher(StringType str, bool expect_eq, bool case_sensitive)
+      : string_(std::move(str)),
+        expect_eq_(expect_eq),
+        case_sensitive_(case_sensitive) {}
+
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  bool MatchAndExplain(const internal::StringView& s,
+                       MatchResultListener* listener) const {
+    // This should fail to compile if StringView is used with wide
+    // strings.
+    const StringType& str = std::string(s);
+    return MatchAndExplain(str, listener);
+  }
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
+
+  // Accepts pointer types, particularly:
+  //   const char*
+  //   char*
+  //   const wchar_t*
+  //   wchar_t*
+  template <typename CharType>
+  bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
+    if (s == nullptr) {
+      return !expect_eq_;
+    }
+    return MatchAndExplain(StringType(s), listener);
+  }
+
+  // Matches anything that can convert to StringType.
+  //
+  // This is a template, not just a plain function with const StringType&,
+  // because StringView has some interfering non-explicit constructors.
+  template <typename MatcheeStringType>
+  bool MatchAndExplain(const MatcheeStringType& s,
+                       MatchResultListener* /* listener */) const {
+    const StringType s2(s);
+    const bool eq = case_sensitive_ ? s2 == string_ :
+        CaseInsensitiveStringEquals(s2, string_);
+    return expect_eq_ == eq;
+  }
+
+  void DescribeTo(::std::ostream* os) const {
+    DescribeToHelper(expect_eq_, os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    DescribeToHelper(!expect_eq_, os);
+  }
+
+ private:
+  void DescribeToHelper(bool expect_eq, ::std::ostream* os) const {
+    *os << (expect_eq ? "is " : "isn't ");
+    *os << "equal to ";
+    if (!case_sensitive_) {
+      *os << "(ignoring case) ";
+    }
+    UniversalPrint(string_, os);
+  }
+
+  const StringType string_;
+  const bool expect_eq_;
+  const bool case_sensitive_;
+};
+
+// Implements the polymorphic HasSubstr(substring) matcher, which
+// can be used as a Matcher<T> as long as T can be converted to a
+// string.
+template <typename StringType>
+class HasSubstrMatcher {
+ public:
+  explicit HasSubstrMatcher(const StringType& substring)
+      : substring_(substring) {}
+
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  bool MatchAndExplain(const internal::StringView& s,
+                       MatchResultListener* listener) const {
+    // This should fail to compile if StringView is used with wide
+    // strings.
+    const StringType& str = std::string(s);
+    return MatchAndExplain(str, listener);
+  }
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
+
+  // Accepts pointer types, particularly:
+  //   const char*
+  //   char*
+  //   const wchar_t*
+  //   wchar_t*
+  template <typename CharType>
+  bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
+    return s != nullptr && MatchAndExplain(StringType(s), listener);
+  }
+
+  // Matches anything that can convert to StringType.
+  //
+  // This is a template, not just a plain function with const StringType&,
+  // because StringView has some interfering non-explicit constructors.
+  template <typename MatcheeStringType>
+  bool MatchAndExplain(const MatcheeStringType& s,
+                       MatchResultListener* /* listener */) const {
+    return StringType(s).find(substring_) != StringType::npos;
+  }
+
+  // Describes what this matcher matches.
+  void DescribeTo(::std::ostream* os) const {
+    *os << "has substring ";
+    UniversalPrint(substring_, os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "has no substring ";
+    UniversalPrint(substring_, os);
+  }
+
+ private:
+  const StringType substring_;
+};
+
+// Implements the polymorphic StartsWith(substring) matcher, which
+// can be used as a Matcher<T> as long as T can be converted to a
+// string.
+template <typename StringType>
+class StartsWithMatcher {
+ public:
+  explicit StartsWithMatcher(const StringType& prefix) : prefix_(prefix) {
+  }
+
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  bool MatchAndExplain(const internal::StringView& s,
+                       MatchResultListener* listener) const {
+    // This should fail to compile if StringView is used with wide
+    // strings.
+    const StringType& str = std::string(s);
+    return MatchAndExplain(str, listener);
+  }
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
+
+  // Accepts pointer types, particularly:
+  //   const char*
+  //   char*
+  //   const wchar_t*
+  //   wchar_t*
+  template <typename CharType>
+  bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
+    return s != nullptr && MatchAndExplain(StringType(s), listener);
+  }
+
+  // Matches anything that can convert to StringType.
+  //
+  // This is a template, not just a plain function with const StringType&,
+  // because StringView has some interfering non-explicit constructors.
+  template <typename MatcheeStringType>
+  bool MatchAndExplain(const MatcheeStringType& s,
+                       MatchResultListener* /* listener */) const {
+    const StringType& s2(s);
+    return s2.length() >= prefix_.length() &&
+        s2.substr(0, prefix_.length()) == prefix_;
+  }
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << "starts with ";
+    UniversalPrint(prefix_, os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "doesn't start with ";
+    UniversalPrint(prefix_, os);
+  }
+
+ private:
+  const StringType prefix_;
+};
+
+// Implements the polymorphic EndsWith(substring) matcher, which
+// can be used as a Matcher<T> as long as T can be converted to a
+// string.
+template <typename StringType>
+class EndsWithMatcher {
+ public:
+  explicit EndsWithMatcher(const StringType& suffix) : suffix_(suffix) {}
+
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  bool MatchAndExplain(const internal::StringView& s,
+                       MatchResultListener* listener) const {
+    // This should fail to compile if StringView is used with wide
+    // strings.
+    const StringType& str = std::string(s);
+    return MatchAndExplain(str, listener);
+  }
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
+
+  // Accepts pointer types, particularly:
+  //   const char*
+  //   char*
+  //   const wchar_t*
+  //   wchar_t*
+  template <typename CharType>
+  bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
+    return s != nullptr && MatchAndExplain(StringType(s), listener);
+  }
+
+  // Matches anything that can convert to StringType.
+  //
+  // This is a template, not just a plain function with const StringType&,
+  // because StringView has some interfering non-explicit constructors.
+  template <typename MatcheeStringType>
+  bool MatchAndExplain(const MatcheeStringType& s,
+                       MatchResultListener* /* listener */) const {
+    const StringType& s2(s);
+    return s2.length() >= suffix_.length() &&
+        s2.substr(s2.length() - suffix_.length()) == suffix_;
+  }
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << "ends with ";
+    UniversalPrint(suffix_, os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "doesn't end with ";
+    UniversalPrint(suffix_, os);
+  }
+
+ private:
+  const StringType suffix_;
+};
+
+// Implements a matcher that compares the two fields of a 2-tuple
+// using one of the ==, <=, <, etc, operators.  The two fields being
+// compared don't have to have the same type.
+//
+// The matcher defined here is polymorphic (for example, Eq() can be
+// used to match a std::tuple<int, short>, a std::tuple<const long&, double>,
+// etc).  Therefore we use a template type conversion operator in the
+// implementation.
+template <typename D, typename Op>
+class PairMatchBase {
+ public:
+  template <typename T1, typename T2>
+  operator Matcher<::std::tuple<T1, T2>>() const {
+    return Matcher<::std::tuple<T1, T2>>(new Impl<const ::std::tuple<T1, T2>&>);
+  }
+  template <typename T1, typename T2>
+  operator Matcher<const ::std::tuple<T1, T2>&>() const {
+    return MakeMatcher(new Impl<const ::std::tuple<T1, T2>&>);
+  }
+
+ private:
+  static ::std::ostream& GetDesc(::std::ostream& os) {  // NOLINT
+    return os << D::Desc();
+  }
+
+  template <typename Tuple>
+  class Impl : public MatcherInterface<Tuple> {
+   public:
+    bool MatchAndExplain(Tuple args,
+                         MatchResultListener* /* listener */) const override {
+      return Op()(::std::get<0>(args), ::std::get<1>(args));
+    }
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "are " << GetDesc;
+    }
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "aren't " << GetDesc;
+    }
+  };
+};
+
+class Eq2Matcher : public PairMatchBase<Eq2Matcher, AnyEq> {
+ public:
+  static const char* Desc() { return "an equal pair"; }
+};
+class Ne2Matcher : public PairMatchBase<Ne2Matcher, AnyNe> {
+ public:
+  static const char* Desc() { return "an unequal pair"; }
+};
+class Lt2Matcher : public PairMatchBase<Lt2Matcher, AnyLt> {
+ public:
+  static const char* Desc() { return "a pair where the first < the second"; }
+};
+class Gt2Matcher : public PairMatchBase<Gt2Matcher, AnyGt> {
+ public:
+  static const char* Desc() { return "a pair where the first > the second"; }
+};
+class Le2Matcher : public PairMatchBase<Le2Matcher, AnyLe> {
+ public:
+  static const char* Desc() { return "a pair where the first <= the second"; }
+};
+class Ge2Matcher : public PairMatchBase<Ge2Matcher, AnyGe> {
+ public:
+  static const char* Desc() { return "a pair where the first >= the second"; }
+};
+
+// Implements the Not(...) matcher for a particular argument type T.
+// We do not nest it inside the NotMatcher class template, as that
+// will prevent different instantiations of NotMatcher from sharing
+// the same NotMatcherImpl<T> class.
+template <typename T>
+class NotMatcherImpl : public MatcherInterface<const T&> {
+ public:
+  explicit NotMatcherImpl(const Matcher<T>& matcher)
+      : matcher_(matcher) {}
+
+  bool MatchAndExplain(const T& x,
+                       MatchResultListener* listener) const override {
+    return !matcher_.MatchAndExplain(x, listener);
+  }
+
+  void DescribeTo(::std::ostream* os) const override {
+    matcher_.DescribeNegationTo(os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const override {
+    matcher_.DescribeTo(os);
+  }
+
+ private:
+  const Matcher<T> matcher_;
+};
+
+// Implements the Not(m) matcher, which matches a value that doesn't
+// match matcher m.
+template <typename InnerMatcher>
+class NotMatcher {
+ public:
+  explicit NotMatcher(InnerMatcher matcher) : matcher_(matcher) {}
+
+  // This template type conversion operator allows Not(m) to be used
+  // to match any type m can match.
+  template <typename T>
+  operator Matcher<T>() const {
+    return Matcher<T>(new NotMatcherImpl<T>(SafeMatcherCast<T>(matcher_)));
+  }
+
+ private:
+  InnerMatcher matcher_;
+};
+
+// Implements the AllOf(m1, m2) matcher for a particular argument type
+// T. We do not nest it inside the BothOfMatcher class template, as
+// that will prevent different instantiations of BothOfMatcher from
+// sharing the same BothOfMatcherImpl<T> class.
+template <typename T>
+class AllOfMatcherImpl : public MatcherInterface<const T&> {
+ public:
+  explicit AllOfMatcherImpl(std::vector<Matcher<T> > matchers)
+      : matchers_(std::move(matchers)) {}
+
+  void DescribeTo(::std::ostream* os) const override {
+    *os << "(";
+    for (size_t i = 0; i < matchers_.size(); ++i) {
+      if (i != 0) *os << ") and (";
+      matchers_[i].DescribeTo(os);
+    }
+    *os << ")";
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const override {
+    *os << "(";
+    for (size_t i = 0; i < matchers_.size(); ++i) {
+      if (i != 0) *os << ") or (";
+      matchers_[i].DescribeNegationTo(os);
+    }
+    *os << ")";
+  }
+
+  bool MatchAndExplain(const T& x,
+                       MatchResultListener* listener) const override {
+    // If either matcher1_ or matcher2_ doesn't match x, we only need
+    // to explain why one of them fails.
+    std::string all_match_result;
+
+    for (size_t i = 0; i < matchers_.size(); ++i) {
+      StringMatchResultListener slistener;
+      if (matchers_[i].MatchAndExplain(x, &slistener)) {
+        if (all_match_result.empty()) {
+          all_match_result = slistener.str();
+        } else {
+          std::string result = slistener.str();
+          if (!result.empty()) {
+            all_match_result += ", and ";
+            all_match_result += result;
+          }
+        }
+      } else {
+        *listener << slistener.str();
+        return false;
+      }
+    }
+
+    // Otherwise we need to explain why *both* of them match.
+    *listener << all_match_result;
+    return true;
+  }
+
+ private:
+  const std::vector<Matcher<T> > matchers_;
+};
+
+// VariadicMatcher is used for the variadic implementation of
+// AllOf(m_1, m_2, ...) and AnyOf(m_1, m_2, ...).
+// CombiningMatcher<T> is used to recursively combine the provided matchers
+// (of type Args...).
+template <template <typename T> class CombiningMatcher, typename... Args>
+class VariadicMatcher {
+ public:
+  VariadicMatcher(const Args&... matchers)  // NOLINT
+      : matchers_(matchers...) {
+    static_assert(sizeof...(Args) > 0, "Must have at least one matcher.");
+  }
+
+  VariadicMatcher(const VariadicMatcher&) = default;
+  VariadicMatcher& operator=(const VariadicMatcher&) = delete;
+
+  // This template type conversion operator allows an
+  // VariadicMatcher<Matcher1, Matcher2...> object to match any type that
+  // all of the provided matchers (Matcher1, Matcher2, ...) can match.
+  template <typename T>
+  operator Matcher<T>() const {
+    std::vector<Matcher<T> > values;
+    CreateVariadicMatcher<T>(&values, std::integral_constant<size_t, 0>());
+    return Matcher<T>(new CombiningMatcher<T>(std::move(values)));
+  }
+
+ private:
+  template <typename T, size_t I>
+  void CreateVariadicMatcher(std::vector<Matcher<T> >* values,
+                             std::integral_constant<size_t, I>) const {
+    values->push_back(SafeMatcherCast<T>(std::get<I>(matchers_)));
+    CreateVariadicMatcher<T>(values, std::integral_constant<size_t, I + 1>());
+  }
+
+  template <typename T>
+  void CreateVariadicMatcher(
+      std::vector<Matcher<T> >*,
+      std::integral_constant<size_t, sizeof...(Args)>) const {}
+
+  std::tuple<Args...> matchers_;
+};
+
+template <typename... Args>
+using AllOfMatcher = VariadicMatcher<AllOfMatcherImpl, Args...>;
+
+// Implements the AnyOf(m1, m2) matcher for a particular argument type
+// T.  We do not nest it inside the AnyOfMatcher class template, as
+// that will prevent different instantiations of AnyOfMatcher from
+// sharing the same EitherOfMatcherImpl<T> class.
+template <typename T>
+class AnyOfMatcherImpl : public MatcherInterface<const T&> {
+ public:
+  explicit AnyOfMatcherImpl(std::vector<Matcher<T> > matchers)
+      : matchers_(std::move(matchers)) {}
+
+  void DescribeTo(::std::ostream* os) const override {
+    *os << "(";
+    for (size_t i = 0; i < matchers_.size(); ++i) {
+      if (i != 0) *os << ") or (";
+      matchers_[i].DescribeTo(os);
+    }
+    *os << ")";
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const override {
+    *os << "(";
+    for (size_t i = 0; i < matchers_.size(); ++i) {
+      if (i != 0) *os << ") and (";
+      matchers_[i].DescribeNegationTo(os);
+    }
+    *os << ")";
+  }
+
+  bool MatchAndExplain(const T& x,
+                       MatchResultListener* listener) const override {
+    std::string no_match_result;
+
+    // If either matcher1_ or matcher2_ matches x, we just need to
+    // explain why *one* of them matches.
+    for (size_t i = 0; i < matchers_.size(); ++i) {
+      StringMatchResultListener slistener;
+      if (matchers_[i].MatchAndExplain(x, &slistener)) {
+        *listener << slistener.str();
+        return true;
+      } else {
+        if (no_match_result.empty()) {
+          no_match_result = slistener.str();
+        } else {
+          std::string result = slistener.str();
+          if (!result.empty()) {
+            no_match_result += ", and ";
+            no_match_result += result;
+          }
+        }
+      }
+    }
+
+    // Otherwise we need to explain why *both* of them fail.
+    *listener << no_match_result;
+    return false;
+  }
+
+ private:
+  const std::vector<Matcher<T> > matchers_;
+};
+
+// AnyOfMatcher is used for the variadic implementation of AnyOf(m_1, m_2, ...).
+template <typename... Args>
+using AnyOfMatcher = VariadicMatcher<AnyOfMatcherImpl, Args...>;
+
+// ConditionalMatcher is the implementation of Conditional(cond, m1, m2)
+template <typename MatcherTrue, typename MatcherFalse>
+class ConditionalMatcher {
+ public:
+  ConditionalMatcher(bool condition, MatcherTrue matcher_true,
+                     MatcherFalse matcher_false)
+      : condition_(condition),
+        matcher_true_(std::move(matcher_true)),
+        matcher_false_(std::move(matcher_false)) {}
+
+  template <typename T>
+  operator Matcher<T>() const {  // NOLINT(runtime/explicit)
+    return condition_ ? SafeMatcherCast<T>(matcher_true_)
+                      : SafeMatcherCast<T>(matcher_false_);
+  }
+
+ private:
+  bool condition_;
+  MatcherTrue matcher_true_;
+  MatcherFalse matcher_false_;
+
+  GTEST_DISALLOW_ASSIGN_(ConditionalMatcher);
+};
+
+// Wrapper for implementation of Any/AllOfArray().
+template <template <class> class MatcherImpl, typename T>
+class SomeOfArrayMatcher {
+ public:
+  // Constructs the matcher from a sequence of element values or
+  // element matchers.
+  template <typename Iter>
+  SomeOfArrayMatcher(Iter first, Iter last) : matchers_(first, last) {}
+
+  template <typename U>
+  operator Matcher<U>() const {  // NOLINT
+    using RawU = typename std::decay<U>::type;
+    std::vector<Matcher<RawU>> matchers;
+    for (const auto& matcher : matchers_) {
+      matchers.push_back(MatcherCast<RawU>(matcher));
+    }
+    return Matcher<U>(new MatcherImpl<RawU>(std::move(matchers)));
+  }
+
+ private:
+  const ::std::vector<T> matchers_;
+};
+
+template <typename T>
+using AllOfArrayMatcher = SomeOfArrayMatcher<AllOfMatcherImpl, T>;
+
+template <typename T>
+using AnyOfArrayMatcher = SomeOfArrayMatcher<AnyOfMatcherImpl, T>;
+
+// Used for implementing Truly(pred), which turns a predicate into a
+// matcher.
+template <typename Predicate>
+class TrulyMatcher {
+ public:
+  explicit TrulyMatcher(Predicate pred) : predicate_(pred) {}
+
+  // This method template allows Truly(pred) to be used as a matcher
+  // for type T where T is the argument type of predicate 'pred'.  The
+  // argument is passed by reference as the predicate may be
+  // interested in the address of the argument.
+  template <typename T>
+  bool MatchAndExplain(T& x,  // NOLINT
+                       MatchResultListener* listener) const {
+    // Without the if-statement, MSVC sometimes warns about converting
+    // a value to bool (warning 4800).
+    //
+    // We cannot write 'return !!predicate_(x);' as that doesn't work
+    // when predicate_(x) returns a class convertible to bool but
+    // having no operator!().
+    if (predicate_(x))
+      return true;
+    *listener << "didn't satisfy the given predicate";
+    return false;
+  }
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << "satisfies the given predicate";
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "doesn't satisfy the given predicate";
+  }
+
+ private:
+  Predicate predicate_;
+};
+
+// Used for implementing Matches(matcher), which turns a matcher into
+// a predicate.
+template <typename M>
+class MatcherAsPredicate {
+ public:
+  explicit MatcherAsPredicate(M matcher) : matcher_(matcher) {}
+
+  // This template operator() allows Matches(m) to be used as a
+  // predicate on type T where m is a matcher on type T.
+  //
+  // The argument x is passed by reference instead of by value, as
+  // some matcher may be interested in its address (e.g. as in
+  // Matches(Ref(n))(x)).
+  template <typename T>
+  bool operator()(const T& x) const {
+    // We let matcher_ commit to a particular type here instead of
+    // when the MatcherAsPredicate object was constructed.  This
+    // allows us to write Matches(m) where m is a polymorphic matcher
+    // (e.g. Eq(5)).
+    //
+    // If we write Matcher<T>(matcher_).Matches(x) here, it won't
+    // compile when matcher_ has type Matcher<const T&>; if we write
+    // Matcher<const T&>(matcher_).Matches(x) here, it won't compile
+    // when matcher_ has type Matcher<T>; if we just write
+    // matcher_.Matches(x), it won't compile when matcher_ is
+    // polymorphic, e.g. Eq(5).
+    //
+    // MatcherCast<const T&>() is necessary for making the code work
+    // in all of the above situations.
+    return MatcherCast<const T&>(matcher_).Matches(x);
+  }
+
+ private:
+  M matcher_;
+};
+
+// For implementing ASSERT_THAT() and EXPECT_THAT().  The template
+// argument M must be a type that can be converted to a matcher.
+template <typename M>
+class PredicateFormatterFromMatcher {
+ public:
+  explicit PredicateFormatterFromMatcher(M m) : matcher_(std::move(m)) {}
+
+  // This template () operator allows a PredicateFormatterFromMatcher
+  // object to act as a predicate-formatter suitable for using with
+  // Google Test's EXPECT_PRED_FORMAT1() macro.
+  template <typename T>
+  AssertionResult operator()(const char* value_text, const T& x) const {
+#ifndef __clang_analyzer__
+    // We convert matcher_ to a Matcher<const T&> *now* instead of
+    // when the PredicateFormatterFromMatcher object was constructed,
+    // as matcher_ may be polymorphic (e.g. NotNull()) and we won't
+    // know which type to instantiate it to until we actually see the
+    // type of x here.
+    //
+    // We write SafeMatcherCast<const T&>(matcher_) instead of
+    // Matcher<const T&>(matcher_), as the latter won't compile when
+    // matcher_ has type Matcher<T> (e.g. An<int>()).
+    // We don't write MatcherCast<const T&> either, as that allows
+    // potentially unsafe downcasting of the matcher argument.
+    const Matcher<const T&> matcher = SafeMatcherCast<const T&>(matcher_);
+
+    // The expected path here is that the matcher should match (i.e. that most
+    // tests pass) so optimize for this case.
+    if (matcher.Matches(x)) {
+      return AssertionSuccess();
+    }
+
+    ::std::stringstream ss;
+    ss << "Value of: " << value_text << "\n"
+       << "Expected: ";
+    matcher.DescribeTo(&ss);
+
+    // Rerun the matcher to "PrintAndExplain" the failure.
+    StringMatchResultListener listener;
+    if (MatchPrintAndExplain(x, matcher, &listener)) {
+      ss << "\n  The matcher failed on the initial attempt; but passed when "
+            "rerun to generate the explanation.";
+    }
+    ss << "\n  Actual: " << listener.str();
+    return AssertionFailure() << ss.str();
+#else
+    return AssertionSuccess();
+#endif
+  }
+
+ private:
+  const M matcher_;
+};
+
+// A helper function for converting a matcher to a predicate-formatter
+// without the user needing to explicitly write the type.  This is
+// used for implementing ASSERT_THAT() and EXPECT_THAT().
+// Implementation detail: 'matcher' is received by-value to force decaying.
+template <typename M>
+inline PredicateFormatterFromMatcher<M>
+MakePredicateFormatterFromMatcher(M matcher) {
+  return PredicateFormatterFromMatcher<M>(std::move(matcher));
+}
+
+// Implements the polymorphic IsNan() matcher, which matches any floating type
+// value that is Nan.
+class IsNanMatcher {
+ public:
+  template <typename FloatType>
+  bool MatchAndExplain(const FloatType& f,
+                       MatchResultListener* /* listener */) const {
+    return (::std::isnan)(f);
+  }
+
+  void DescribeTo(::std::ostream* os) const { *os << "is NaN"; }
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "isn't NaN";
+  }
+};
+
+// Implements the polymorphic floating point equality matcher, which matches
+// two float values using ULP-based approximation or, optionally, a
+// user-specified epsilon.  The template is meant to be instantiated with
+// FloatType being either float or double.
+template <typename FloatType>
+class FloatingEqMatcher {
+ public:
+  // Constructor for FloatingEqMatcher.
+  // The matcher's input will be compared with expected.  The matcher treats two
+  // NANs as equal if nan_eq_nan is true.  Otherwise, under IEEE standards,
+  // equality comparisons between NANs will always return false.  We specify a
+  // negative max_abs_error_ term to indicate that ULP-based approximation will
+  // be used for comparison.
+  FloatingEqMatcher(FloatType expected, bool nan_eq_nan) :
+    expected_(expected), nan_eq_nan_(nan_eq_nan), max_abs_error_(-1) {
+  }
+
+  // Constructor that supports a user-specified max_abs_error that will be used
+  // for comparison instead of ULP-based approximation.  The max absolute
+  // should be non-negative.
+  FloatingEqMatcher(FloatType expected, bool nan_eq_nan,
+                    FloatType max_abs_error)
+      : expected_(expected),
+        nan_eq_nan_(nan_eq_nan),
+        max_abs_error_(max_abs_error) {
+    GTEST_CHECK_(max_abs_error >= 0)
+        << ", where max_abs_error is" << max_abs_error;
+  }
+
+  // Implements floating point equality matcher as a Matcher<T>.
+  template <typename T>
+  class Impl : public MatcherInterface<T> {
+   public:
+    Impl(FloatType expected, bool nan_eq_nan, FloatType max_abs_error)
+        : expected_(expected),
+          nan_eq_nan_(nan_eq_nan),
+          max_abs_error_(max_abs_error) {}
+
+    bool MatchAndExplain(T value,
+                         MatchResultListener* listener) const override {
+      const FloatingPoint<FloatType> actual(value), expected(expected_);
+
+      // Compares NaNs first, if nan_eq_nan_ is true.
+      if (actual.is_nan() || expected.is_nan()) {
+        if (actual.is_nan() && expected.is_nan()) {
+          return nan_eq_nan_;
+        }
+        // One is nan; the other is not nan.
+        return false;
+      }
+      if (HasMaxAbsError()) {
+        // We perform an equality check so that inf will match inf, regardless
+        // of error bounds.  If the result of value - expected_ would result in
+        // overflow or if either value is inf, the default result is infinity,
+        // which should only match if max_abs_error_ is also infinity.
+        if (value == expected_) {
+          return true;
+        }
+
+        const FloatType diff = value - expected_;
+        if (::std::fabs(diff) <= max_abs_error_) {
+          return true;
+        }
+
+        if (listener->IsInterested()) {
+          *listener << "which is " << diff << " from " << expected_;
+        }
+        return false;
+      } else {
+        return actual.AlmostEquals(expected);
+      }
+    }
+
+    void DescribeTo(::std::ostream* os) const override {
+      // os->precision() returns the previously set precision, which we
+      // store to restore the ostream to its original configuration
+      // after outputting.
+      const ::std::streamsize old_precision = os->precision(
+          ::std::numeric_limits<FloatType>::digits10 + 2);
+      if (FloatingPoint<FloatType>(expected_).is_nan()) {
+        if (nan_eq_nan_) {
+          *os << "is NaN";
+        } else {
+          *os << "never matches";
+        }
+      } else {
+        *os << "is approximately " << expected_;
+        if (HasMaxAbsError()) {
+          *os << " (absolute error <= " << max_abs_error_ << ")";
+        }
+      }
+      os->precision(old_precision);
+    }
+
+    void DescribeNegationTo(::std::ostream* os) const override {
+      // As before, get original precision.
+      const ::std::streamsize old_precision = os->precision(
+          ::std::numeric_limits<FloatType>::digits10 + 2);
+      if (FloatingPoint<FloatType>(expected_).is_nan()) {
+        if (nan_eq_nan_) {
+          *os << "isn't NaN";
+        } else {
+          *os << "is anything";
+        }
+      } else {
+        *os << "isn't approximately " << expected_;
+        if (HasMaxAbsError()) {
+          *os << " (absolute error > " << max_abs_error_ << ")";
+        }
+      }
+      // Restore original precision.
+      os->precision(old_precision);
+    }
+
+   private:
+    bool HasMaxAbsError() const {
+      return max_abs_error_ >= 0;
+    }
+
+    const FloatType expected_;
+    const bool nan_eq_nan_;
+    // max_abs_error will be used for value comparison when >= 0.
+    const FloatType max_abs_error_;
+  };
+
+  // The following 3 type conversion operators allow FloatEq(expected) and
+  // NanSensitiveFloatEq(expected) to be used as a Matcher<float>, a
+  // Matcher<const float&>, or a Matcher<float&>, but nothing else.
+  operator Matcher<FloatType>() const {
+    return MakeMatcher(
+        new Impl<FloatType>(expected_, nan_eq_nan_, max_abs_error_));
+  }
+
+  operator Matcher<const FloatType&>() const {
+    return MakeMatcher(
+        new Impl<const FloatType&>(expected_, nan_eq_nan_, max_abs_error_));
+  }
+
+  operator Matcher<FloatType&>() const {
+    return MakeMatcher(
+        new Impl<FloatType&>(expected_, nan_eq_nan_, max_abs_error_));
+  }
+
+ private:
+  const FloatType expected_;
+  const bool nan_eq_nan_;
+  // max_abs_error will be used for value comparison when >= 0.
+  const FloatType max_abs_error_;
+};
+
+// A 2-tuple ("binary") wrapper around FloatingEqMatcher:
+// FloatingEq2Matcher() matches (x, y) by matching FloatingEqMatcher(x, false)
+// against y, and FloatingEq2Matcher(e) matches FloatingEqMatcher(x, false, e)
+// against y. The former implements "Eq", the latter "Near". At present, there
+// is no version that compares NaNs as equal.
+template <typename FloatType>
+class FloatingEq2Matcher {
+ public:
+  FloatingEq2Matcher() { Init(-1, false); }
+
+  explicit FloatingEq2Matcher(bool nan_eq_nan) { Init(-1, nan_eq_nan); }
+
+  explicit FloatingEq2Matcher(FloatType max_abs_error) {
+    Init(max_abs_error, false);
+  }
+
+  FloatingEq2Matcher(FloatType max_abs_error, bool nan_eq_nan) {
+    Init(max_abs_error, nan_eq_nan);
+  }
+
+  template <typename T1, typename T2>
+  operator Matcher<::std::tuple<T1, T2>>() const {
+    return MakeMatcher(
+        new Impl<::std::tuple<T1, T2>>(max_abs_error_, nan_eq_nan_));
+  }
+  template <typename T1, typename T2>
+  operator Matcher<const ::std::tuple<T1, T2>&>() const {
+    return MakeMatcher(
+        new Impl<const ::std::tuple<T1, T2>&>(max_abs_error_, nan_eq_nan_));
+  }
+
+ private:
+  static ::std::ostream& GetDesc(::std::ostream& os) {  // NOLINT
+    return os << "an almost-equal pair";
+  }
+
+  template <typename Tuple>
+  class Impl : public MatcherInterface<Tuple> {
+   public:
+    Impl(FloatType max_abs_error, bool nan_eq_nan) :
+        max_abs_error_(max_abs_error),
+        nan_eq_nan_(nan_eq_nan) {}
+
+    bool MatchAndExplain(Tuple args,
+                         MatchResultListener* listener) const override {
+      if (max_abs_error_ == -1) {
+        FloatingEqMatcher<FloatType> fm(::std::get<0>(args), nan_eq_nan_);
+        return static_cast<Matcher<FloatType>>(fm).MatchAndExplain(
+            ::std::get<1>(args), listener);
+      } else {
+        FloatingEqMatcher<FloatType> fm(::std::get<0>(args), nan_eq_nan_,
+                                        max_abs_error_);
+        return static_cast<Matcher<FloatType>>(fm).MatchAndExplain(
+            ::std::get<1>(args), listener);
+      }
+    }
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "are " << GetDesc;
+    }
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "aren't " << GetDesc;
+    }
+
+   private:
+    FloatType max_abs_error_;
+    const bool nan_eq_nan_;
+  };
+
+  void Init(FloatType max_abs_error_val, bool nan_eq_nan_val) {
+    max_abs_error_ = max_abs_error_val;
+    nan_eq_nan_ = nan_eq_nan_val;
+  }
+  FloatType max_abs_error_;
+  bool nan_eq_nan_;
+};
+
+// Implements the Pointee(m) matcher for matching a pointer whose
+// pointee matches matcher m.  The pointer can be either raw or smart.
+template <typename InnerMatcher>
+class PointeeMatcher {
+ public:
+  explicit PointeeMatcher(const InnerMatcher& matcher) : matcher_(matcher) {}
+
+  // This type conversion operator template allows Pointee(m) to be
+  // used as a matcher for any pointer type whose pointee type is
+  // compatible with the inner matcher, where type Pointer can be
+  // either a raw pointer or a smart pointer.
+  //
+  // The reason we do this instead of relying on
+  // MakePolymorphicMatcher() is that the latter is not flexible
+  // enough for implementing the DescribeTo() method of Pointee().
+  template <typename Pointer>
+  operator Matcher<Pointer>() const {
+    return Matcher<Pointer>(new Impl<const Pointer&>(matcher_));
+  }
+
+ private:
+  // The monomorphic implementation that works for a particular pointer type.
+  template <typename Pointer>
+  class Impl : public MatcherInterface<Pointer> {
+   public:
+    using Pointee =
+        typename std::pointer_traits<GTEST_REMOVE_REFERENCE_AND_CONST_(
+            Pointer)>::element_type;
+
+    explicit Impl(const InnerMatcher& matcher)
+        : matcher_(MatcherCast<const Pointee&>(matcher)) {}
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "points to a value that ";
+      matcher_.DescribeTo(os);
+    }
+
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "does not point to a value that ";
+      matcher_.DescribeTo(os);
+    }
+
+    bool MatchAndExplain(Pointer pointer,
+                         MatchResultListener* listener) const override {
+      if (GetRawPointer(pointer) == nullptr) return false;
+
+      *listener << "which points to ";
+      return MatchPrintAndExplain(*pointer, matcher_, listener);
+    }
+
+   private:
+    const Matcher<const Pointee&> matcher_;
+  };
+
+  const InnerMatcher matcher_;
+};
+
+// Implements the Pointer(m) matcher
+// Implements the Pointer(m) matcher for matching a pointer that matches matcher
+// m.  The pointer can be either raw or smart, and will match `m` against the
+// raw pointer.
+template <typename InnerMatcher>
+class PointerMatcher {
+ public:
+  explicit PointerMatcher(const InnerMatcher& matcher) : matcher_(matcher) {}
+
+  // This type conversion operator template allows Pointer(m) to be
+  // used as a matcher for any pointer type whose pointer type is
+  // compatible with the inner matcher, where type PointerType can be
+  // either a raw pointer or a smart pointer.
+  //
+  // The reason we do this instead of relying on
+  // MakePolymorphicMatcher() is that the latter is not flexible
+  // enough for implementing the DescribeTo() method of Pointer().
+  template <typename PointerType>
+  operator Matcher<PointerType>() const {  // NOLINT
+    return Matcher<PointerType>(new Impl<const PointerType&>(matcher_));
+  }
+
+ private:
+  // The monomorphic implementation that works for a particular pointer type.
+  template <typename PointerType>
+  class Impl : public MatcherInterface<PointerType> {
+   public:
+    using Pointer =
+        const typename std::pointer_traits<GTEST_REMOVE_REFERENCE_AND_CONST_(
+            PointerType)>::element_type*;
+
+    explicit Impl(const InnerMatcher& matcher)
+        : matcher_(MatcherCast<Pointer>(matcher)) {}
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "is a pointer that ";
+      matcher_.DescribeTo(os);
+    }
+
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "is not a pointer that ";
+      matcher_.DescribeTo(os);
+    }
+
+    bool MatchAndExplain(PointerType pointer,
+                         MatchResultListener* listener) const override {
+      *listener << "which is a pointer that ";
+      Pointer p = GetRawPointer(pointer);
+      return MatchPrintAndExplain(p, matcher_, listener);
+    }
+
+   private:
+    Matcher<Pointer> matcher_;
+  };
+
+  const InnerMatcher matcher_;
+};
+
+#if GTEST_HAS_RTTI
+// Implements the WhenDynamicCastTo<T>(m) matcher that matches a pointer or
+// reference that matches inner_matcher when dynamic_cast<T> is applied.
+// The result of dynamic_cast<To> is forwarded to the inner matcher.
+// If To is a pointer and the cast fails, the inner matcher will receive NULL.
+// If To is a reference and the cast fails, this matcher returns false
+// immediately.
+template <typename To>
+class WhenDynamicCastToMatcherBase {
+ public:
+  explicit WhenDynamicCastToMatcherBase(const Matcher<To>& matcher)
+      : matcher_(matcher) {}
+
+  void DescribeTo(::std::ostream* os) const {
+    GetCastTypeDescription(os);
+    matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    GetCastTypeDescription(os);
+    matcher_.DescribeNegationTo(os);
+  }
+
+ protected:
+  const Matcher<To> matcher_;
+
+  static std::string GetToName() {
+    return GetTypeName<To>();
+  }
+
+ private:
+  static void GetCastTypeDescription(::std::ostream* os) {
+    *os << "when dynamic_cast to " << GetToName() << ", ";
+  }
+};
+
+// Primary template.
+// To is a pointer. Cast and forward the result.
+template <typename To>
+class WhenDynamicCastToMatcher : public WhenDynamicCastToMatcherBase<To> {
+ public:
+  explicit WhenDynamicCastToMatcher(const Matcher<To>& matcher)
+      : WhenDynamicCastToMatcherBase<To>(matcher) {}
+
+  template <typename From>
+  bool MatchAndExplain(From from, MatchResultListener* listener) const {
+    To to = dynamic_cast<To>(from);
+    return MatchPrintAndExplain(to, this->matcher_, listener);
+  }
+};
+
+// Specialize for references.
+// In this case we return false if the dynamic_cast fails.
+template <typename To>
+class WhenDynamicCastToMatcher<To&> : public WhenDynamicCastToMatcherBase<To&> {
+ public:
+  explicit WhenDynamicCastToMatcher(const Matcher<To&>& matcher)
+      : WhenDynamicCastToMatcherBase<To&>(matcher) {}
+
+  template <typename From>
+  bool MatchAndExplain(From& from, MatchResultListener* listener) const {
+    // We don't want an std::bad_cast here, so do the cast with pointers.
+    To* to = dynamic_cast<To*>(&from);
+    if (to == nullptr) {
+      *listener << "which cannot be dynamic_cast to " << this->GetToName();
+      return false;
+    }
+    return MatchPrintAndExplain(*to, this->matcher_, listener);
+  }
+};
+#endif  // GTEST_HAS_RTTI
+
+// Implements the Field() matcher for matching a field (i.e. member
+// variable) of an object.
+template <typename Class, typename FieldType>
+class FieldMatcher {
+ public:
+  FieldMatcher(FieldType Class::*field,
+               const Matcher<const FieldType&>& matcher)
+      : field_(field), matcher_(matcher), whose_field_("whose given field ") {}
+
+  FieldMatcher(const std::string& field_name, FieldType Class::*field,
+               const Matcher<const FieldType&>& matcher)
+      : field_(field),
+        matcher_(matcher),
+        whose_field_("whose field `" + field_name + "` ") {}
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << "is an object " << whose_field_;
+    matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "is an object " << whose_field_;
+    matcher_.DescribeNegationTo(os);
+  }
+
+  template <typename T>
+  bool MatchAndExplain(const T& value, MatchResultListener* listener) const {
+    // FIXME: The dispatch on std::is_pointer was introduced as a workaround for
+    // a compiler bug, and can now be removed.
+    return MatchAndExplainImpl(
+        typename std::is_pointer<typename std::remove_const<T>::type>::type(),
+        value, listener);
+  }
+
+ private:
+  bool MatchAndExplainImpl(std::false_type /* is_not_pointer */,
+                           const Class& obj,
+                           MatchResultListener* listener) const {
+    *listener << whose_field_ << "is ";
+    return MatchPrintAndExplain(obj.*field_, matcher_, listener);
+  }
+
+  bool MatchAndExplainImpl(std::true_type /* is_pointer */, const Class* p,
+                           MatchResultListener* listener) const {
+    if (p == nullptr) return false;
+
+    *listener << "which points to an object ";
+    // Since *p has a field, it must be a class/struct/union type and
+    // thus cannot be a pointer.  Therefore we pass false_type() as
+    // the first argument.
+    return MatchAndExplainImpl(std::false_type(), *p, listener);
+  }
+
+  const FieldType Class::*field_;
+  const Matcher<const FieldType&> matcher_;
+
+  // Contains either "whose given field " if the name of the field is unknown
+  // or "whose field `name_of_field` " if the name is known.
+  const std::string whose_field_;
+};
+
+// Implements the Property() matcher for matching a property
+// (i.e. return value of a getter method) of an object.
+//
+// Property is a const-qualified member function of Class returning
+// PropertyType.
+template <typename Class, typename PropertyType, typename Property>
+class PropertyMatcher {
+ public:
+  typedef const PropertyType& RefToConstProperty;
+
+  PropertyMatcher(Property property, const Matcher<RefToConstProperty>& matcher)
+      : property_(property),
+        matcher_(matcher),
+        whose_property_("whose given property ") {}
+
+  PropertyMatcher(const std::string& property_name, Property property,
+                  const Matcher<RefToConstProperty>& matcher)
+      : property_(property),
+        matcher_(matcher),
+        whose_property_("whose property `" + property_name + "` ") {}
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << "is an object " << whose_property_;
+    matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "is an object " << whose_property_;
+    matcher_.DescribeNegationTo(os);
+  }
+
+  template <typename T>
+  bool MatchAndExplain(const T&value, MatchResultListener* listener) const {
+    return MatchAndExplainImpl(
+        typename std::is_pointer<typename std::remove_const<T>::type>::type(),
+        value, listener);
+  }
+
+ private:
+  bool MatchAndExplainImpl(std::false_type /* is_not_pointer */,
+                           const Class& obj,
+                           MatchResultListener* listener) const {
+    *listener << whose_property_ << "is ";
+    // Cannot pass the return value (for example, int) to MatchPrintAndExplain,
+    // which takes a non-const reference as argument.
+    RefToConstProperty result = (obj.*property_)();
+    return MatchPrintAndExplain(result, matcher_, listener);
+  }
+
+  bool MatchAndExplainImpl(std::true_type /* is_pointer */, const Class* p,
+                           MatchResultListener* listener) const {
+    if (p == nullptr) return false;
+
+    *listener << "which points to an object ";
+    // Since *p has a property method, it must be a class/struct/union
+    // type and thus cannot be a pointer.  Therefore we pass
+    // false_type() as the first argument.
+    return MatchAndExplainImpl(std::false_type(), *p, listener);
+  }
+
+  Property property_;
+  const Matcher<RefToConstProperty> matcher_;
+
+  // Contains either "whose given property " if the name of the property is
+  // unknown or "whose property `name_of_property` " if the name is known.
+  const std::string whose_property_;
+};
+
+// Type traits specifying various features of different functors for ResultOf.
+// The default template specifies features for functor objects.
+template <typename Functor>
+struct CallableTraits {
+  typedef Functor StorageType;
+
+  static void CheckIsValid(Functor /* functor */) {}
+
+  template <typename T>
+  static auto Invoke(Functor f, const T& arg) -> decltype(f(arg)) {
+    return f(arg);
+  }
+};
+
+// Specialization for function pointers.
+template <typename ArgType, typename ResType>
+struct CallableTraits<ResType(*)(ArgType)> {
+  typedef ResType ResultType;
+  typedef ResType(*StorageType)(ArgType);
+
+  static void CheckIsValid(ResType(*f)(ArgType)) {
+    GTEST_CHECK_(f != nullptr)
+        << "NULL function pointer is passed into ResultOf().";
+  }
+  template <typename T>
+  static ResType Invoke(ResType(*f)(ArgType), T arg) {
+    return (*f)(arg);
+  }
+};
+
+// Implements the ResultOf() matcher for matching a return value of a
+// unary function of an object.
+template <typename Callable, typename InnerMatcher>
+class ResultOfMatcher {
+ public:
+  ResultOfMatcher(Callable callable, InnerMatcher matcher)
+      : callable_(std::move(callable)), matcher_(std::move(matcher)) {
+    CallableTraits<Callable>::CheckIsValid(callable_);
+  }
+
+  template <typename T>
+  operator Matcher<T>() const {
+    return Matcher<T>(new Impl<const T&>(callable_, matcher_));
+  }
+
+ private:
+  typedef typename CallableTraits<Callable>::StorageType CallableStorageType;
+
+  template <typename T>
+  class Impl : public MatcherInterface<T> {
+    using ResultType = decltype(CallableTraits<Callable>::template Invoke<T>(
+        std::declval<CallableStorageType>(), std::declval<T>()));
+
+   public:
+    template <typename M>
+    Impl(const CallableStorageType& callable, const M& matcher)
+        : callable_(callable), matcher_(MatcherCast<ResultType>(matcher)) {}
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "is mapped by the given callable to a value that ";
+      matcher_.DescribeTo(os);
+    }
+
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "is mapped by the given callable to a value that ";
+      matcher_.DescribeNegationTo(os);
+    }
+
+    bool MatchAndExplain(T obj, MatchResultListener* listener) const override {
+      *listener << "which is mapped by the given callable to ";
+      // Cannot pass the return value directly to MatchPrintAndExplain, which
+      // takes a non-const reference as argument.
+      // Also, specifying template argument explicitly is needed because T could
+      // be a non-const reference (e.g. Matcher<Uncopyable&>).
+      ResultType result =
+          CallableTraits<Callable>::template Invoke<T>(callable_, obj);
+      return MatchPrintAndExplain(result, matcher_, listener);
+    }
+
+   private:
+    // Functors often define operator() as non-const method even though
+    // they are actually stateless. But we need to use them even when
+    // 'this' is a const pointer. It's the user's responsibility not to
+    // use stateful callables with ResultOf(), which doesn't guarantee
+    // how many times the callable will be invoked.
+    mutable CallableStorageType callable_;
+    const Matcher<ResultType> matcher_;
+  };  // class Impl
+
+  const CallableStorageType callable_;
+  const InnerMatcher matcher_;
+};
+
+// Implements a matcher that checks the size of an STL-style container.
+template <typename SizeMatcher>
+class SizeIsMatcher {
+ public:
+  explicit SizeIsMatcher(const SizeMatcher& size_matcher)
+       : size_matcher_(size_matcher) {
+  }
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    return Matcher<Container>(new Impl<const Container&>(size_matcher_));
+  }
+
+  template <typename Container>
+  class Impl : public MatcherInterface<Container> {
+   public:
+    using SizeType = decltype(std::declval<Container>().size());
+    explicit Impl(const SizeMatcher& size_matcher)
+        : size_matcher_(MatcherCast<SizeType>(size_matcher)) {}
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "size ";
+      size_matcher_.DescribeTo(os);
+    }
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "size ";
+      size_matcher_.DescribeNegationTo(os);
+    }
+
+    bool MatchAndExplain(Container container,
+                         MatchResultListener* listener) const override {
+      SizeType size = container.size();
+      StringMatchResultListener size_listener;
+      const bool result = size_matcher_.MatchAndExplain(size, &size_listener);
+      *listener
+          << "whose size " << size << (result ? " matches" : " doesn't match");
+      PrintIfNotEmpty(size_listener.str(), listener->stream());
+      return result;
+    }
+
+   private:
+    const Matcher<SizeType> size_matcher_;
+  };
+
+ private:
+  const SizeMatcher size_matcher_;
+};
+
+// Implements a matcher that checks the begin()..end() distance of an STL-style
+// container.
+template <typename DistanceMatcher>
+class BeginEndDistanceIsMatcher {
+ public:
+  explicit BeginEndDistanceIsMatcher(const DistanceMatcher& distance_matcher)
+      : distance_matcher_(distance_matcher) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    return Matcher<Container>(new Impl<const Container&>(distance_matcher_));
+  }
+
+  template <typename Container>
+  class Impl : public MatcherInterface<Container> {
+   public:
+    typedef internal::StlContainerView<
+        GTEST_REMOVE_REFERENCE_AND_CONST_(Container)> ContainerView;
+    typedef typename std::iterator_traits<
+        typename ContainerView::type::const_iterator>::difference_type
+        DistanceType;
+    explicit Impl(const DistanceMatcher& distance_matcher)
+        : distance_matcher_(MatcherCast<DistanceType>(distance_matcher)) {}
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "distance between begin() and end() ";
+      distance_matcher_.DescribeTo(os);
+    }
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "distance between begin() and end() ";
+      distance_matcher_.DescribeNegationTo(os);
+    }
+
+    bool MatchAndExplain(Container container,
+                         MatchResultListener* listener) const override {
+      using std::begin;
+      using std::end;
+      DistanceType distance = std::distance(begin(container), end(container));
+      StringMatchResultListener distance_listener;
+      const bool result =
+          distance_matcher_.MatchAndExplain(distance, &distance_listener);
+      *listener << "whose distance between begin() and end() " << distance
+                << (result ? " matches" : " doesn't match");
+      PrintIfNotEmpty(distance_listener.str(), listener->stream());
+      return result;
+    }
+
+   private:
+    const Matcher<DistanceType> distance_matcher_;
+  };
+
+ private:
+  const DistanceMatcher distance_matcher_;
+};
+
+// Implements an equality matcher for any STL-style container whose elements
+// support ==. This matcher is like Eq(), but its failure explanations provide
+// more detailed information that is useful when the container is used as a set.
+// The failure message reports elements that are in one of the operands but not
+// the other. The failure messages do not report duplicate or out-of-order
+// elements in the containers (which don't properly matter to sets, but can
+// occur if the containers are vectors or lists, for example).
+//
+// Uses the container's const_iterator, value_type, operator ==,
+// begin(), and end().
+template <typename Container>
+class ContainerEqMatcher {
+ public:
+  typedef internal::StlContainerView<Container> View;
+  typedef typename View::type StlContainer;
+  typedef typename View::const_reference StlContainerReference;
+
+  static_assert(!std::is_const<Container>::value,
+                "Container type must not be const");
+  static_assert(!std::is_reference<Container>::value,
+                "Container type must not be a reference");
+
+  // We make a copy of expected in case the elements in it are modified
+  // after this matcher is created.
+  explicit ContainerEqMatcher(const Container& expected)
+      : expected_(View::Copy(expected)) {}
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << "equals ";
+    UniversalPrint(expected_, os);
+  }
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "does not equal ";
+    UniversalPrint(expected_, os);
+  }
+
+  template <typename LhsContainer>
+  bool MatchAndExplain(const LhsContainer& lhs,
+                       MatchResultListener* listener) const {
+    typedef internal::StlContainerView<
+        typename std::remove_const<LhsContainer>::type>
+        LhsView;
+    typedef typename LhsView::type LhsStlContainer;
+    StlContainerReference lhs_stl_container = LhsView::ConstReference(lhs);
+    if (lhs_stl_container == expected_)
+      return true;
+
+    ::std::ostream* const os = listener->stream();
+    if (os != nullptr) {
+      // Something is different. Check for extra values first.
+      bool printed_header = false;
+      for (typename LhsStlContainer::const_iterator it =
+               lhs_stl_container.begin();
+           it != lhs_stl_container.end(); ++it) {
+        if (internal::ArrayAwareFind(expected_.begin(), expected_.end(), *it) ==
+            expected_.end()) {
+          if (printed_header) {
+            *os << ", ";
+          } else {
+            *os << "which has these unexpected elements: ";
+            printed_header = true;
+          }
+          UniversalPrint(*it, os);
+        }
+      }
+
+      // Now check for missing values.
+      bool printed_header2 = false;
+      for (typename StlContainer::const_iterator it = expected_.begin();
+           it != expected_.end(); ++it) {
+        if (internal::ArrayAwareFind(
+                lhs_stl_container.begin(), lhs_stl_container.end(), *it) ==
+            lhs_stl_container.end()) {
+          if (printed_header2) {
+            *os << ", ";
+          } else {
+            *os << (printed_header ? ",\nand" : "which")
+                << " doesn't have these expected elements: ";
+            printed_header2 = true;
+          }
+          UniversalPrint(*it, os);
+        }
+      }
+    }
+
+    return false;
+  }
+
+ private:
+  const StlContainer expected_;
+};
+
+// A comparator functor that uses the < operator to compare two values.
+struct LessComparator {
+  template <typename T, typename U>
+  bool operator()(const T& lhs, const U& rhs) const { return lhs < rhs; }
+};
+
+// Implements WhenSortedBy(comparator, container_matcher).
+template <typename Comparator, typename ContainerMatcher>
+class WhenSortedByMatcher {
+ public:
+  WhenSortedByMatcher(const Comparator& comparator,
+                      const ContainerMatcher& matcher)
+      : comparator_(comparator), matcher_(matcher) {}
+
+  template <typename LhsContainer>
+  operator Matcher<LhsContainer>() const {
+    return MakeMatcher(new Impl<LhsContainer>(comparator_, matcher_));
+  }
+
+  template <typename LhsContainer>
+  class Impl : public MatcherInterface<LhsContainer> {
+   public:
+    typedef internal::StlContainerView<
+         GTEST_REMOVE_REFERENCE_AND_CONST_(LhsContainer)> LhsView;
+    typedef typename LhsView::type LhsStlContainer;
+    typedef typename LhsView::const_reference LhsStlContainerReference;
+    // Transforms std::pair<const Key, Value> into std::pair<Key, Value>
+    // so that we can match associative containers.
+    typedef typename RemoveConstFromKey<
+        typename LhsStlContainer::value_type>::type LhsValue;
+
+    Impl(const Comparator& comparator, const ContainerMatcher& matcher)
+        : comparator_(comparator), matcher_(matcher) {}
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "(when sorted) ";
+      matcher_.DescribeTo(os);
+    }
+
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "(when sorted) ";
+      matcher_.DescribeNegationTo(os);
+    }
+
+    bool MatchAndExplain(LhsContainer lhs,
+                         MatchResultListener* listener) const override {
+      LhsStlContainerReference lhs_stl_container = LhsView::ConstReference(lhs);
+      ::std::vector<LhsValue> sorted_container(lhs_stl_container.begin(),
+                                               lhs_stl_container.end());
+      ::std::sort(
+           sorted_container.begin(), sorted_container.end(), comparator_);
+
+      if (!listener->IsInterested()) {
+        // If the listener is not interested, we do not need to
+        // construct the inner explanation.
+        return matcher_.Matches(sorted_container);
+      }
+
+      *listener << "which is ";
+      UniversalPrint(sorted_container, listener->stream());
+      *listener << " when sorted";
+
+      StringMatchResultListener inner_listener;
+      const bool match = matcher_.MatchAndExplain(sorted_container,
+                                                  &inner_listener);
+      PrintIfNotEmpty(inner_listener.str(), listener->stream());
+      return match;
+    }
+
+   private:
+    const Comparator comparator_;
+    const Matcher<const ::std::vector<LhsValue>&> matcher_;
+
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(Impl);
+  };
+
+ private:
+  const Comparator comparator_;
+  const ContainerMatcher matcher_;
+};
+
+// Implements Pointwise(tuple_matcher, rhs_container).  tuple_matcher
+// must be able to be safely cast to Matcher<std::tuple<const T1&, const
+// T2&> >, where T1 and T2 are the types of elements in the LHS
+// container and the RHS container respectively.
+template <typename TupleMatcher, typename RhsContainer>
+class PointwiseMatcher {
+  GTEST_COMPILE_ASSERT_(
+      !IsHashTable<GTEST_REMOVE_REFERENCE_AND_CONST_(RhsContainer)>::value,
+      use_UnorderedPointwise_with_hash_tables);
+
+ public:
+  typedef internal::StlContainerView<RhsContainer> RhsView;
+  typedef typename RhsView::type RhsStlContainer;
+  typedef typename RhsStlContainer::value_type RhsValue;
+
+  static_assert(!std::is_const<RhsContainer>::value,
+                "RhsContainer type must not be const");
+  static_assert(!std::is_reference<RhsContainer>::value,
+                "RhsContainer type must not be a reference");
+
+  // Like ContainerEq, we make a copy of rhs in case the elements in
+  // it are modified after this matcher is created.
+  PointwiseMatcher(const TupleMatcher& tuple_matcher, const RhsContainer& rhs)
+      : tuple_matcher_(tuple_matcher), rhs_(RhsView::Copy(rhs)) {}
+
+  template <typename LhsContainer>
+  operator Matcher<LhsContainer>() const {
+    GTEST_COMPILE_ASSERT_(
+        !IsHashTable<GTEST_REMOVE_REFERENCE_AND_CONST_(LhsContainer)>::value,
+        use_UnorderedPointwise_with_hash_tables);
+
+    return Matcher<LhsContainer>(
+        new Impl<const LhsContainer&>(tuple_matcher_, rhs_));
+  }
+
+  template <typename LhsContainer>
+  class Impl : public MatcherInterface<LhsContainer> {
+   public:
+    typedef internal::StlContainerView<
+         GTEST_REMOVE_REFERENCE_AND_CONST_(LhsContainer)> LhsView;
+    typedef typename LhsView::type LhsStlContainer;
+    typedef typename LhsView::const_reference LhsStlContainerReference;
+    typedef typename LhsStlContainer::value_type LhsValue;
+    // We pass the LHS value and the RHS value to the inner matcher by
+    // reference, as they may be expensive to copy.  We must use tuple
+    // instead of pair here, as a pair cannot hold references (C++ 98,
+    // 20.2.2 [lib.pairs]).
+    typedef ::std::tuple<const LhsValue&, const RhsValue&> InnerMatcherArg;
+
+    Impl(const TupleMatcher& tuple_matcher, const RhsStlContainer& rhs)
+        // mono_tuple_matcher_ holds a monomorphic version of the tuple matcher.
+        : mono_tuple_matcher_(SafeMatcherCast<InnerMatcherArg>(tuple_matcher)),
+          rhs_(rhs) {}
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "contains " << rhs_.size()
+          << " values, where each value and its corresponding value in ";
+      UniversalPrinter<RhsStlContainer>::Print(rhs_, os);
+      *os << " ";
+      mono_tuple_matcher_.DescribeTo(os);
+    }
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "doesn't contain exactly " << rhs_.size()
+          << " values, or contains a value x at some index i"
+          << " where x and the i-th value of ";
+      UniversalPrint(rhs_, os);
+      *os << " ";
+      mono_tuple_matcher_.DescribeNegationTo(os);
+    }
+
+    bool MatchAndExplain(LhsContainer lhs,
+                         MatchResultListener* listener) const override {
+      LhsStlContainerReference lhs_stl_container = LhsView::ConstReference(lhs);
+      const size_t actual_size = lhs_stl_container.size();
+      if (actual_size != rhs_.size()) {
+        *listener << "which contains " << actual_size << " values";
+        return false;
+      }
+
+      typename LhsStlContainer::const_iterator left = lhs_stl_container.begin();
+      typename RhsStlContainer::const_iterator right = rhs_.begin();
+      for (size_t i = 0; i != actual_size; ++i, ++left, ++right) {
+        if (listener->IsInterested()) {
+          StringMatchResultListener inner_listener;
+          // Create InnerMatcherArg as a temporarily object to avoid it outlives
+          // *left and *right. Dereference or the conversion to `const T&` may
+          // return temp objects, e.g. for vector<bool>.
+          if (!mono_tuple_matcher_.MatchAndExplain(
+                  InnerMatcherArg(ImplicitCast_<const LhsValue&>(*left),
+                                  ImplicitCast_<const RhsValue&>(*right)),
+                  &inner_listener)) {
+            *listener << "where the value pair (";
+            UniversalPrint(*left, listener->stream());
+            *listener << ", ";
+            UniversalPrint(*right, listener->stream());
+            *listener << ") at index #" << i << " don't match";
+            PrintIfNotEmpty(inner_listener.str(), listener->stream());
+            return false;
+          }
+        } else {
+          if (!mono_tuple_matcher_.Matches(
+                  InnerMatcherArg(ImplicitCast_<const LhsValue&>(*left),
+                                  ImplicitCast_<const RhsValue&>(*right))))
+            return false;
+        }
+      }
+
+      return true;
+    }
+
+   private:
+    const Matcher<InnerMatcherArg> mono_tuple_matcher_;
+    const RhsStlContainer rhs_;
+  };
+
+ private:
+  const TupleMatcher tuple_matcher_;
+  const RhsStlContainer rhs_;
+};
+
+// Holds the logic common to ContainsMatcherImpl and EachMatcherImpl.
+template <typename Container>
+class QuantifierMatcherImpl : public MatcherInterface<Container> {
+ public:
+  typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
+  typedef StlContainerView<RawContainer> View;
+  typedef typename View::type StlContainer;
+  typedef typename View::const_reference StlContainerReference;
+  typedef typename StlContainer::value_type Element;
+
+  template <typename InnerMatcher>
+  explicit QuantifierMatcherImpl(InnerMatcher inner_matcher)
+      : inner_matcher_(
+           testing::SafeMatcherCast<const Element&>(inner_matcher)) {}
+
+  // Checks whether:
+  // * All elements in the container match, if all_elements_should_match.
+  // * Any element in the container matches, if !all_elements_should_match.
+  bool MatchAndExplainImpl(bool all_elements_should_match,
+                           Container container,
+                           MatchResultListener* listener) const {
+    StlContainerReference stl_container = View::ConstReference(container);
+    size_t i = 0;
+    for (typename StlContainer::const_iterator it = stl_container.begin();
+         it != stl_container.end(); ++it, ++i) {
+      StringMatchResultListener inner_listener;
+      const bool matches = inner_matcher_.MatchAndExplain(*it, &inner_listener);
+
+      if (matches != all_elements_should_match) {
+        *listener << "whose element #" << i
+                  << (matches ? " matches" : " doesn't match");
+        PrintIfNotEmpty(inner_listener.str(), listener->stream());
+        return !all_elements_should_match;
+      }
+    }
+    return all_elements_should_match;
+  }
+
+  bool MatchAndExplainImpl(const Matcher<size_t>& count_matcher,
+                           Container container,
+                           MatchResultListener* listener) const {
+    StlContainerReference stl_container = View::ConstReference(container);
+    size_t i = 0;
+    std::vector<size_t> match_elements;
+    for (auto it = stl_container.begin(); it != stl_container.end();
+         ++it, ++i) {
+      StringMatchResultListener inner_listener;
+      const bool matches = inner_matcher_.MatchAndExplain(*it, &inner_listener);
+      if (matches) {
+        match_elements.push_back(i);
+      }
+    }
+    if (listener->IsInterested()) {
+      if (match_elements.empty()) {
+        *listener << "has no element that matches";
+      } else if (match_elements.size() == 1) {
+        *listener << "whose element #" << match_elements[0] << " matches";
+      } else {
+        *listener << "whose elements (";
+        std::string sep = "";
+        for (size_t e : match_elements) {
+          *listener << sep << e;
+          sep = ", ";
+        }
+        *listener << ") match";
+      }
+    }
+    StringMatchResultListener count_listener;
+    if (count_matcher.MatchAndExplain(match_elements.size(), &count_listener)) {
+      *listener << " and whose match quantity of " << match_elements.size()
+                << " matches";
+      PrintIfNotEmpty(count_listener.str(), listener->stream());
+      return true;
+    } else {
+      if (match_elements.empty()) {
+        *listener << " and";
+      } else {
+        *listener << " but";
+      }
+      *listener << " whose match quantity of " << match_elements.size()
+                << " does not match";
+      PrintIfNotEmpty(count_listener.str(), listener->stream());
+      return false;
+    }
+  }
+
+ protected:
+  const Matcher<const Element&> inner_matcher_;
+};
+
+// Implements Contains(element_matcher) for the given argument type Container.
+// Symmetric to EachMatcherImpl.
+template <typename Container>
+class ContainsMatcherImpl : public QuantifierMatcherImpl<Container> {
+ public:
+  template <typename InnerMatcher>
+  explicit ContainsMatcherImpl(InnerMatcher inner_matcher)
+      : QuantifierMatcherImpl<Container>(inner_matcher) {}
+
+  // Describes what this matcher does.
+  void DescribeTo(::std::ostream* os) const override {
+    *os << "contains at least one element that ";
+    this->inner_matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const override {
+    *os << "doesn't contain any element that ";
+    this->inner_matcher_.DescribeTo(os);
+  }
+
+  bool MatchAndExplain(Container container,
+                       MatchResultListener* listener) const override {
+    return this->MatchAndExplainImpl(false, container, listener);
+  }
+};
+
+// Implements Each(element_matcher) for the given argument type Container.
+// Symmetric to ContainsMatcherImpl.
+template <typename Container>
+class EachMatcherImpl : public QuantifierMatcherImpl<Container> {
+ public:
+  template <typename InnerMatcher>
+  explicit EachMatcherImpl(InnerMatcher inner_matcher)
+      : QuantifierMatcherImpl<Container>(inner_matcher) {}
+
+  // Describes what this matcher does.
+  void DescribeTo(::std::ostream* os) const override {
+    *os << "only contains elements that ";
+    this->inner_matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const override {
+    *os << "contains some element that ";
+    this->inner_matcher_.DescribeNegationTo(os);
+  }
+
+  bool MatchAndExplain(Container container,
+                       MatchResultListener* listener) const override {
+    return this->MatchAndExplainImpl(true, container, listener);
+  }
+};
+
+// Implements Contains(element_matcher).Times(n) for the given argument type
+// Container.
+template <typename Container>
+class ContainsTimesMatcherImpl : public QuantifierMatcherImpl<Container> {
+ public:
+  template <typename InnerMatcher>
+  explicit ContainsTimesMatcherImpl(InnerMatcher inner_matcher,
+                                    Matcher<size_t> count_matcher)
+      : QuantifierMatcherImpl<Container>(inner_matcher),
+        count_matcher_(std::move(count_matcher)) {}
+
+  void DescribeTo(::std::ostream* os) const override {
+    *os << "quantity of elements that match ";
+    this->inner_matcher_.DescribeTo(os);
+    *os << " ";
+    count_matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const override {
+    *os << "quantity of elements that match ";
+    this->inner_matcher_.DescribeTo(os);
+    *os << " ";
+    count_matcher_.DescribeNegationTo(os);
+  }
+
+  bool MatchAndExplain(Container container,
+                       MatchResultListener* listener) const override {
+    return this->MatchAndExplainImpl(count_matcher_, container, listener);
+  }
+
+ private:
+  const Matcher<size_t> count_matcher_;
+};
+
+// Implements polymorphic Contains(element_matcher).Times(n).
+template <typename M>
+class ContainsTimesMatcher {
+ public:
+  explicit ContainsTimesMatcher(M m, Matcher<size_t> count_matcher)
+      : inner_matcher_(m), count_matcher_(std::move(count_matcher)) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {  // NOLINT
+    return Matcher<Container>(new ContainsTimesMatcherImpl<const Container&>(
+        inner_matcher_, count_matcher_));
+  }
+
+ private:
+  const M inner_matcher_;
+  const Matcher<size_t> count_matcher_;
+};
+
+// Implements polymorphic Contains(element_matcher).
+template <typename M>
+class ContainsMatcher {
+ public:
+  explicit ContainsMatcher(M m) : inner_matcher_(m) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {  // NOLINT
+    return Matcher<Container>(
+        new ContainsMatcherImpl<const Container&>(inner_matcher_));
+  }
+
+  ContainsTimesMatcher<M> Times(Matcher<size_t> count_matcher) const {
+    return ContainsTimesMatcher<M>(inner_matcher_, std::move(count_matcher));
+  }
+
+ private:
+  const M inner_matcher_;
+};
+
+// Implements polymorphic Each(element_matcher).
+template <typename M>
+class EachMatcher {
+ public:
+  explicit EachMatcher(M m) : inner_matcher_(m) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {  // NOLINT
+    return Matcher<Container>(
+        new EachMatcherImpl<const Container&>(inner_matcher_));
+  }
+
+ private:
+  const M inner_matcher_;
+};
+
+struct Rank1 {};
+struct Rank0 : Rank1 {};
+
+namespace pair_getters {
+using std::get;
+template <typename T>
+auto First(T& x, Rank1) -> decltype(get<0>(x)) {  // NOLINT
+  return get<0>(x);
+}
+template <typename T>
+auto First(T& x, Rank0) -> decltype((x.first)) {  // NOLINT
+  return x.first;
+}
+
+template <typename T>
+auto Second(T& x, Rank1) -> decltype(get<1>(x)) {  // NOLINT
+  return get<1>(x);
+}
+template <typename T>
+auto Second(T& x, Rank0) -> decltype((x.second)) {  // NOLINT
+  return x.second;
+}
+}  // namespace pair_getters
+
+// Implements Key(inner_matcher) for the given argument pair type.
+// Key(inner_matcher) matches an std::pair whose 'first' field matches
+// inner_matcher.  For example, Contains(Key(Ge(5))) can be used to match an
+// std::map that contains at least one element whose key is >= 5.
+template <typename PairType>
+class KeyMatcherImpl : public MatcherInterface<PairType> {
+ public:
+  typedef GTEST_REMOVE_REFERENCE_AND_CONST_(PairType) RawPairType;
+  typedef typename RawPairType::first_type KeyType;
+
+  template <typename InnerMatcher>
+  explicit KeyMatcherImpl(InnerMatcher inner_matcher)
+      : inner_matcher_(
+          testing::SafeMatcherCast<const KeyType&>(inner_matcher)) {
+  }
+
+  // Returns true if and only if 'key_value.first' (the key) matches the inner
+  // matcher.
+  bool MatchAndExplain(PairType key_value,
+                       MatchResultListener* listener) const override {
+    StringMatchResultListener inner_listener;
+    const bool match = inner_matcher_.MatchAndExplain(
+        pair_getters::First(key_value, Rank0()), &inner_listener);
+    const std::string explanation = inner_listener.str();
+    if (explanation != "") {
+      *listener << "whose first field is a value " << explanation;
+    }
+    return match;
+  }
+
+  // Describes what this matcher does.
+  void DescribeTo(::std::ostream* os) const override {
+    *os << "has a key that ";
+    inner_matcher_.DescribeTo(os);
+  }
+
+  // Describes what the negation of this matcher does.
+  void DescribeNegationTo(::std::ostream* os) const override {
+    *os << "doesn't have a key that ";
+    inner_matcher_.DescribeTo(os);
+  }
+
+ private:
+  const Matcher<const KeyType&> inner_matcher_;
+};
+
+// Implements polymorphic Key(matcher_for_key).
+template <typename M>
+class KeyMatcher {
+ public:
+  explicit KeyMatcher(M m) : matcher_for_key_(m) {}
+
+  template <typename PairType>
+  operator Matcher<PairType>() const {
+    return Matcher<PairType>(
+        new KeyMatcherImpl<const PairType&>(matcher_for_key_));
+  }
+
+ private:
+  const M matcher_for_key_;
+};
+
+// Implements polymorphic Address(matcher_for_address).
+template <typename InnerMatcher>
+class AddressMatcher {
+ public:
+  explicit AddressMatcher(InnerMatcher m) : matcher_(m) {}
+
+  template <typename Type>
+  operator Matcher<Type>() const {  // NOLINT
+    return Matcher<Type>(new Impl<const Type&>(matcher_));
+  }
+
+ private:
+  // The monomorphic implementation that works for a particular object type.
+  template <typename Type>
+  class Impl : public MatcherInterface<Type> {
+   public:
+    using Address = const GTEST_REMOVE_REFERENCE_AND_CONST_(Type) *;
+    explicit Impl(const InnerMatcher& matcher)
+        : matcher_(MatcherCast<Address>(matcher)) {}
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "has address that ";
+      matcher_.DescribeTo(os);
+    }
+
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "does not have address that ";
+      matcher_.DescribeTo(os);
+    }
+
+    bool MatchAndExplain(Type object,
+                         MatchResultListener* listener) const override {
+      *listener << "which has address ";
+      Address address = std::addressof(object);
+      return MatchPrintAndExplain(address, matcher_, listener);
+    }
+
+   private:
+    const Matcher<Address> matcher_;
+  };
+  const InnerMatcher matcher_;
+};
+
+// Implements Pair(first_matcher, second_matcher) for the given argument pair
+// type with its two matchers. See Pair() function below.
+template <typename PairType>
+class PairMatcherImpl : public MatcherInterface<PairType> {
+ public:
+  typedef GTEST_REMOVE_REFERENCE_AND_CONST_(PairType) RawPairType;
+  typedef typename RawPairType::first_type FirstType;
+  typedef typename RawPairType::second_type SecondType;
+
+  template <typename FirstMatcher, typename SecondMatcher>
+  PairMatcherImpl(FirstMatcher first_matcher, SecondMatcher second_matcher)
+      : first_matcher_(
+            testing::SafeMatcherCast<const FirstType&>(first_matcher)),
+        second_matcher_(
+            testing::SafeMatcherCast<const SecondType&>(second_matcher)) {
+  }
+
+  // Describes what this matcher does.
+  void DescribeTo(::std::ostream* os) const override {
+    *os << "has a first field that ";
+    first_matcher_.DescribeTo(os);
+    *os << ", and has a second field that ";
+    second_matcher_.DescribeTo(os);
+  }
+
+  // Describes what the negation of this matcher does.
+  void DescribeNegationTo(::std::ostream* os) const override {
+    *os << "has a first field that ";
+    first_matcher_.DescribeNegationTo(os);
+    *os << ", or has a second field that ";
+    second_matcher_.DescribeNegationTo(os);
+  }
+
+  // Returns true if and only if 'a_pair.first' matches first_matcher and
+  // 'a_pair.second' matches second_matcher.
+  bool MatchAndExplain(PairType a_pair,
+                       MatchResultListener* listener) const override {
+    if (!listener->IsInterested()) {
+      // If the listener is not interested, we don't need to construct the
+      // explanation.
+      return first_matcher_.Matches(pair_getters::First(a_pair, Rank0())) &&
+             second_matcher_.Matches(pair_getters::Second(a_pair, Rank0()));
+    }
+    StringMatchResultListener first_inner_listener;
+    if (!first_matcher_.MatchAndExplain(pair_getters::First(a_pair, Rank0()),
+                                        &first_inner_listener)) {
+      *listener << "whose first field does not match";
+      PrintIfNotEmpty(first_inner_listener.str(), listener->stream());
+      return false;
+    }
+    StringMatchResultListener second_inner_listener;
+    if (!second_matcher_.MatchAndExplain(pair_getters::Second(a_pair, Rank0()),
+                                         &second_inner_listener)) {
+      *listener << "whose second field does not match";
+      PrintIfNotEmpty(second_inner_listener.str(), listener->stream());
+      return false;
+    }
+    ExplainSuccess(first_inner_listener.str(), second_inner_listener.str(),
+                   listener);
+    return true;
+  }
+
+ private:
+  void ExplainSuccess(const std::string& first_explanation,
+                      const std::string& second_explanation,
+                      MatchResultListener* listener) const {
+    *listener << "whose both fields match";
+    if (first_explanation != "") {
+      *listener << ", where the first field is a value " << first_explanation;
+    }
+    if (second_explanation != "") {
+      *listener << ", ";
+      if (first_explanation != "") {
+        *listener << "and ";
+      } else {
+        *listener << "where ";
+      }
+      *listener << "the second field is a value " << second_explanation;
+    }
+  }
+
+  const Matcher<const FirstType&> first_matcher_;
+  const Matcher<const SecondType&> second_matcher_;
+};
+
+// Implements polymorphic Pair(first_matcher, second_matcher).
+template <typename FirstMatcher, typename SecondMatcher>
+class PairMatcher {
+ public:
+  PairMatcher(FirstMatcher first_matcher, SecondMatcher second_matcher)
+      : first_matcher_(first_matcher), second_matcher_(second_matcher) {}
+
+  template <typename PairType>
+  operator Matcher<PairType> () const {
+    return Matcher<PairType>(
+        new PairMatcherImpl<const PairType&>(first_matcher_, second_matcher_));
+  }
+
+ private:
+  const FirstMatcher first_matcher_;
+  const SecondMatcher second_matcher_;
+};
+
+template <typename T, size_t... I>
+auto UnpackStructImpl(const T& t, IndexSequence<I...>, int)
+    -> decltype(std::tie(get<I>(t)...)) {
+  static_assert(std::tuple_size<T>::value == sizeof...(I),
+                "Number of arguments doesn't match the number of fields.");
+  return std::tie(get<I>(t)...);
+}
+
+#if defined(__cpp_structured_bindings) && __cpp_structured_bindings >= 201606
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<1>, char) {
+  const auto& [a] = t;
+  return std::tie(a);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<2>, char) {
+  const auto& [a, b] = t;
+  return std::tie(a, b);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<3>, char) {
+  const auto& [a, b, c] = t;
+  return std::tie(a, b, c);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<4>, char) {
+  const auto& [a, b, c, d] = t;
+  return std::tie(a, b, c, d);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<5>, char) {
+  const auto& [a, b, c, d, e] = t;
+  return std::tie(a, b, c, d, e);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<6>, char) {
+  const auto& [a, b, c, d, e, f] = t;
+  return std::tie(a, b, c, d, e, f);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<7>, char) {
+  const auto& [a, b, c, d, e, f, g] = t;
+  return std::tie(a, b, c, d, e, f, g);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<8>, char) {
+  const auto& [a, b, c, d, e, f, g, h] = t;
+  return std::tie(a, b, c, d, e, f, g, h);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<9>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<10>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<11>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j, k] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j, k);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<12>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j, k, l] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<13>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<14>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<15>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
+}
+template <typename T>
+auto UnpackStructImpl(const T& t, MakeIndexSequence<16>, char) {
+  const auto& [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p] = t;
+  return std::tie(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p);
+}
+#endif  // defined(__cpp_structured_bindings)
+
+template <size_t I, typename T>
+auto UnpackStruct(const T& t)
+    -> decltype((UnpackStructImpl)(t, MakeIndexSequence<I>{}, 0)) {
+  return (UnpackStructImpl)(t, MakeIndexSequence<I>{}, 0);
+}
+
+// Helper function to do comma folding in C++11.
+// The array ensures left-to-right order of evaluation.
+// Usage: VariadicExpand({expr...});
+template <typename T, size_t N>
+void VariadicExpand(const T (&)[N]) {}
+
+template <typename Struct, typename StructSize>
+class FieldsAreMatcherImpl;
+
+template <typename Struct, size_t... I>
+class FieldsAreMatcherImpl<Struct, IndexSequence<I...>>
+    : public MatcherInterface<Struct> {
+  using UnpackedType =
+      decltype(UnpackStruct<sizeof...(I)>(std::declval<const Struct&>()));
+  using MatchersType = std::tuple<
+      Matcher<const typename std::tuple_element<I, UnpackedType>::type&>...>;
+
+ public:
+  template <typename Inner>
+  explicit FieldsAreMatcherImpl(const Inner& matchers)
+      : matchers_(testing::SafeMatcherCast<
+                  const typename std::tuple_element<I, UnpackedType>::type&>(
+            std::get<I>(matchers))...) {}
+
+  void DescribeTo(::std::ostream* os) const override {
+    const char* separator = "";
+    VariadicExpand(
+        {(*os << separator << "has field #" << I << " that ",
+          std::get<I>(matchers_).DescribeTo(os), separator = ", and ")...});
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const override {
+    const char* separator = "";
+    VariadicExpand({(*os << separator << "has field #" << I << " that ",
+                     std::get<I>(matchers_).DescribeNegationTo(os),
+                     separator = ", or ")...});
+  }
+
+  bool MatchAndExplain(Struct t, MatchResultListener* listener) const override {
+    return MatchInternal((UnpackStruct<sizeof...(I)>)(t), listener);
+  }
+
+ private:
+  bool MatchInternal(UnpackedType tuple, MatchResultListener* listener) const {
+    if (!listener->IsInterested()) {
+      // If the listener is not interested, we don't need to construct the
+      // explanation.
+      bool good = true;
+      VariadicExpand({good = good && std::get<I>(matchers_).Matches(
+                                         std::get<I>(tuple))...});
+      return good;
+    }
+
+    size_t failed_pos = ~size_t{};
+
+    std::vector<StringMatchResultListener> inner_listener(sizeof...(I));
+
+    VariadicExpand(
+        {failed_pos == ~size_t{} && !std::get<I>(matchers_).MatchAndExplain(
+                                        std::get<I>(tuple), &inner_listener[I])
+             ? failed_pos = I
+             : 0 ...});
+    if (failed_pos != ~size_t{}) {
+      *listener << "whose field #" << failed_pos << " does not match";
+      PrintIfNotEmpty(inner_listener[failed_pos].str(), listener->stream());
+      return false;
+    }
+
+    *listener << "whose all elements match";
+    const char* separator = ", where";
+    for (size_t index = 0; index < sizeof...(I); ++index) {
+      const std::string str = inner_listener[index].str();
+      if (!str.empty()) {
+        *listener << separator << " field #" << index << " is a value " << str;
+        separator = ", and";
+      }
+    }
+
+    return true;
+  }
+
+  MatchersType matchers_;
+};
+
+template <typename... Inner>
+class FieldsAreMatcher {
+ public:
+  explicit FieldsAreMatcher(Inner... inner) : matchers_(std::move(inner)...) {}
+
+  template <typename Struct>
+  operator Matcher<Struct>() const {  // NOLINT
+    return Matcher<Struct>(
+        new FieldsAreMatcherImpl<const Struct&, IndexSequenceFor<Inner...>>(
+            matchers_));
+  }
+
+ private:
+  std::tuple<Inner...> matchers_;
+};
+
+// Implements ElementsAre() and ElementsAreArray().
+template <typename Container>
+class ElementsAreMatcherImpl : public MatcherInterface<Container> {
+ public:
+  typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
+  typedef internal::StlContainerView<RawContainer> View;
+  typedef typename View::type StlContainer;
+  typedef typename View::const_reference StlContainerReference;
+  typedef typename StlContainer::value_type Element;
+
+  // Constructs the matcher from a sequence of element values or
+  // element matchers.
+  template <typename InputIter>
+  ElementsAreMatcherImpl(InputIter first, InputIter last) {
+    while (first != last) {
+      matchers_.push_back(MatcherCast<const Element&>(*first++));
+    }
+  }
+
+  // Describes what this matcher does.
+  void DescribeTo(::std::ostream* os) const override {
+    if (count() == 0) {
+      *os << "is empty";
+    } else if (count() == 1) {
+      *os << "has 1 element that ";
+      matchers_[0].DescribeTo(os);
+    } else {
+      *os << "has " << Elements(count()) << " where\n";
+      for (size_t i = 0; i != count(); ++i) {
+        *os << "element #" << i << " ";
+        matchers_[i].DescribeTo(os);
+        if (i + 1 < count()) {
+          *os << ",\n";
+        }
+      }
+    }
+  }
+
+  // Describes what the negation of this matcher does.
+  void DescribeNegationTo(::std::ostream* os) const override {
+    if (count() == 0) {
+      *os << "isn't empty";
+      return;
+    }
+
+    *os << "doesn't have " << Elements(count()) << ", or\n";
+    for (size_t i = 0; i != count(); ++i) {
+      *os << "element #" << i << " ";
+      matchers_[i].DescribeNegationTo(os);
+      if (i + 1 < count()) {
+        *os << ", or\n";
+      }
+    }
+  }
+
+  bool MatchAndExplain(Container container,
+                       MatchResultListener* listener) const override {
+    // To work with stream-like "containers", we must only walk
+    // through the elements in one pass.
+
+    const bool listener_interested = listener->IsInterested();
+
+    // explanations[i] is the explanation of the element at index i.
+    ::std::vector<std::string> explanations(count());
+    StlContainerReference stl_container = View::ConstReference(container);
+    typename StlContainer::const_iterator it = stl_container.begin();
+    size_t exam_pos = 0;
+    bool mismatch_found = false;  // Have we found a mismatched element yet?
+
+    // Go through the elements and matchers in pairs, until we reach
+    // the end of either the elements or the matchers, or until we find a
+    // mismatch.
+    for (; it != stl_container.end() && exam_pos != count(); ++it, ++exam_pos) {
+      bool match;  // Does the current element match the current matcher?
+      if (listener_interested) {
+        StringMatchResultListener s;
+        match = matchers_[exam_pos].MatchAndExplain(*it, &s);
+        explanations[exam_pos] = s.str();
+      } else {
+        match = matchers_[exam_pos].Matches(*it);
+      }
+
+      if (!match) {
+        mismatch_found = true;
+        break;
+      }
+    }
+    // If mismatch_found is true, 'exam_pos' is the index of the mismatch.
+
+    // Find how many elements the actual container has.  We avoid
+    // calling size() s.t. this code works for stream-like "containers"
+    // that don't define size().
+    size_t actual_count = exam_pos;
+    for (; it != stl_container.end(); ++it) {
+      ++actual_count;
+    }
+
+    if (actual_count != count()) {
+      // The element count doesn't match.  If the container is empty,
+      // there's no need to explain anything as Google Mock already
+      // prints the empty container.  Otherwise we just need to show
+      // how many elements there actually are.
+      if (listener_interested && (actual_count != 0)) {
+        *listener << "which has " << Elements(actual_count);
+      }
+      return false;
+    }
+
+    if (mismatch_found) {
+      // The element count matches, but the exam_pos-th element doesn't match.
+      if (listener_interested) {
+        *listener << "whose element #" << exam_pos << " doesn't match";
+        PrintIfNotEmpty(explanations[exam_pos], listener->stream());
+      }
+      return false;
+    }
+
+    // Every element matches its expectation.  We need to explain why
+    // (the obvious ones can be skipped).
+    if (listener_interested) {
+      bool reason_printed = false;
+      for (size_t i = 0; i != count(); ++i) {
+        const std::string& s = explanations[i];
+        if (!s.empty()) {
+          if (reason_printed) {
+            *listener << ",\nand ";
+          }
+          *listener << "whose element #" << i << " matches, " << s;
+          reason_printed = true;
+        }
+      }
+    }
+    return true;
+  }
+
+ private:
+  static Message Elements(size_t count) {
+    return Message() << count << (count == 1 ? " element" : " elements");
+  }
+
+  size_t count() const { return matchers_.size(); }
+
+  ::std::vector<Matcher<const Element&> > matchers_;
+};
+
+// Connectivity matrix of (elements X matchers), in element-major order.
+// Initially, there are no edges.
+// Use NextGraph() to iterate over all possible edge configurations.
+// Use Randomize() to generate a random edge configuration.
+class GTEST_API_ MatchMatrix {
+ public:
+  MatchMatrix(size_t num_elements, size_t num_matchers)
+      : num_elements_(num_elements),
+        num_matchers_(num_matchers),
+        matched_(num_elements_* num_matchers_, 0) {
+  }
+
+  size_t LhsSize() const { return num_elements_; }
+  size_t RhsSize() const { return num_matchers_; }
+  bool HasEdge(size_t ilhs, size_t irhs) const {
+    return matched_[SpaceIndex(ilhs, irhs)] == 1;
+  }
+  void SetEdge(size_t ilhs, size_t irhs, bool b) {
+    matched_[SpaceIndex(ilhs, irhs)] = b ? 1 : 0;
+  }
+
+  // Treating the connectivity matrix as a (LhsSize()*RhsSize())-bit number,
+  // adds 1 to that number; returns false if incrementing the graph left it
+  // empty.
+  bool NextGraph();
+
+  void Randomize();
+
+  std::string DebugString() const;
+
+ private:
+  size_t SpaceIndex(size_t ilhs, size_t irhs) const {
+    return ilhs * num_matchers_ + irhs;
+  }
+
+  size_t num_elements_;
+  size_t num_matchers_;
+
+  // Each element is a char interpreted as bool. They are stored as a
+  // flattened array in lhs-major order, use 'SpaceIndex()' to translate
+  // a (ilhs, irhs) matrix coordinate into an offset.
+  ::std::vector<char> matched_;
+};
+
+typedef ::std::pair<size_t, size_t> ElementMatcherPair;
+typedef ::std::vector<ElementMatcherPair> ElementMatcherPairs;
+
+// Returns a maximum bipartite matching for the specified graph 'g'.
+// The matching is represented as a vector of {element, matcher} pairs.
+GTEST_API_ ElementMatcherPairs
+FindMaxBipartiteMatching(const MatchMatrix& g);
+
+struct UnorderedMatcherRequire {
+  enum Flags {
+    Superset = 1 << 0,
+    Subset = 1 << 1,
+    ExactMatch = Superset | Subset,
+  };
+};
+
+// Untyped base class for implementing UnorderedElementsAre.  By
+// putting logic that's not specific to the element type here, we
+// reduce binary bloat and increase compilation speed.
+class GTEST_API_ UnorderedElementsAreMatcherImplBase {
+ protected:
+  explicit UnorderedElementsAreMatcherImplBase(
+      UnorderedMatcherRequire::Flags matcher_flags)
+      : match_flags_(matcher_flags) {}
+
+  // A vector of matcher describers, one for each element matcher.
+  // Does not own the describers (and thus can be used only when the
+  // element matchers are alive).
+  typedef ::std::vector<const MatcherDescriberInterface*> MatcherDescriberVec;
+
+  // Describes this UnorderedElementsAre matcher.
+  void DescribeToImpl(::std::ostream* os) const;
+
+  // Describes the negation of this UnorderedElementsAre matcher.
+  void DescribeNegationToImpl(::std::ostream* os) const;
+
+  bool VerifyMatchMatrix(const ::std::vector<std::string>& element_printouts,
+                         const MatchMatrix& matrix,
+                         MatchResultListener* listener) const;
+
+  bool FindPairing(const MatchMatrix& matrix,
+                   MatchResultListener* listener) const;
+
+  MatcherDescriberVec& matcher_describers() {
+    return matcher_describers_;
+  }
+
+  static Message Elements(size_t n) {
+    return Message() << n << " element" << (n == 1 ? "" : "s");
+  }
+
+  UnorderedMatcherRequire::Flags match_flags() const { return match_flags_; }
+
+ private:
+  UnorderedMatcherRequire::Flags match_flags_;
+  MatcherDescriberVec matcher_describers_;
+};
+
+// Implements UnorderedElementsAre, UnorderedElementsAreArray, IsSubsetOf, and
+// IsSupersetOf.
+template <typename Container>
+class UnorderedElementsAreMatcherImpl
+    : public MatcherInterface<Container>,
+      public UnorderedElementsAreMatcherImplBase {
+ public:
+  typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
+  typedef internal::StlContainerView<RawContainer> View;
+  typedef typename View::type StlContainer;
+  typedef typename View::const_reference StlContainerReference;
+  typedef typename StlContainer::const_iterator StlContainerConstIterator;
+  typedef typename StlContainer::value_type Element;
+
+  template <typename InputIter>
+  UnorderedElementsAreMatcherImpl(UnorderedMatcherRequire::Flags matcher_flags,
+                                  InputIter first, InputIter last)
+      : UnorderedElementsAreMatcherImplBase(matcher_flags) {
+    for (; first != last; ++first) {
+      matchers_.push_back(MatcherCast<const Element&>(*first));
+    }
+    for (const auto& m : matchers_) {
+      matcher_describers().push_back(m.GetDescriber());
+    }
+  }
+
+  // Describes what this matcher does.
+  void DescribeTo(::std::ostream* os) const override {
+    return UnorderedElementsAreMatcherImplBase::DescribeToImpl(os);
+  }
+
+  // Describes what the negation of this matcher does.
+  void DescribeNegationTo(::std::ostream* os) const override {
+    return UnorderedElementsAreMatcherImplBase::DescribeNegationToImpl(os);
+  }
+
+  bool MatchAndExplain(Container container,
+                       MatchResultListener* listener) const override {
+    StlContainerReference stl_container = View::ConstReference(container);
+    ::std::vector<std::string> element_printouts;
+    MatchMatrix matrix =
+        AnalyzeElements(stl_container.begin(), stl_container.end(),
+                        &element_printouts, listener);
+
+    if (matrix.LhsSize() == 0 && matrix.RhsSize() == 0) {
+      return true;
+    }
+
+    if (match_flags() == UnorderedMatcherRequire::ExactMatch) {
+      if (matrix.LhsSize() != matrix.RhsSize()) {
+        // The element count doesn't match.  If the container is empty,
+        // there's no need to explain anything as Google Mock already
+        // prints the empty container. Otherwise we just need to show
+        // how many elements there actually are.
+        if (matrix.LhsSize() != 0 && listener->IsInterested()) {
+          *listener << "which has " << Elements(matrix.LhsSize());
+        }
+        return false;
+      }
+    }
+
+    return VerifyMatchMatrix(element_printouts, matrix, listener) &&
+           FindPairing(matrix, listener);
+  }
+
+ private:
+  template <typename ElementIter>
+  MatchMatrix AnalyzeElements(ElementIter elem_first, ElementIter elem_last,
+                              ::std::vector<std::string>* element_printouts,
+                              MatchResultListener* listener) const {
+    element_printouts->clear();
+    ::std::vector<char> did_match;
+    size_t num_elements = 0;
+    DummyMatchResultListener dummy;
+    for (; elem_first != elem_last; ++num_elements, ++elem_first) {
+      if (listener->IsInterested()) {
+        element_printouts->push_back(PrintToString(*elem_first));
+      }
+      for (size_t irhs = 0; irhs != matchers_.size(); ++irhs) {
+        did_match.push_back(
+            matchers_[irhs].MatchAndExplain(*elem_first, &dummy));
+      }
+    }
+
+    MatchMatrix matrix(num_elements, matchers_.size());
+    ::std::vector<char>::const_iterator did_match_iter = did_match.begin();
+    for (size_t ilhs = 0; ilhs != num_elements; ++ilhs) {
+      for (size_t irhs = 0; irhs != matchers_.size(); ++irhs) {
+        matrix.SetEdge(ilhs, irhs, *did_match_iter++ != 0);
+      }
+    }
+    return matrix;
+  }
+
+  ::std::vector<Matcher<const Element&> > matchers_;
+};
+
+// Functor for use in TransformTuple.
+// Performs MatcherCast<Target> on an input argument of any type.
+template <typename Target>
+struct CastAndAppendTransform {
+  template <typename Arg>
+  Matcher<Target> operator()(const Arg& a) const {
+    return MatcherCast<Target>(a);
+  }
+};
+
+// Implements UnorderedElementsAre.
+template <typename MatcherTuple>
+class UnorderedElementsAreMatcher {
+ public:
+  explicit UnorderedElementsAreMatcher(const MatcherTuple& args)
+      : matchers_(args) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type View;
+    typedef typename View::value_type Element;
+    typedef ::std::vector<Matcher<const Element&> > MatcherVec;
+    MatcherVec matchers;
+    matchers.reserve(::std::tuple_size<MatcherTuple>::value);
+    TransformTupleValues(CastAndAppendTransform<const Element&>(), matchers_,
+                         ::std::back_inserter(matchers));
+    return Matcher<Container>(
+        new UnorderedElementsAreMatcherImpl<const Container&>(
+            UnorderedMatcherRequire::ExactMatch, matchers.begin(),
+            matchers.end()));
+  }
+
+ private:
+  const MatcherTuple matchers_;
+};
+
+// Implements ElementsAre.
+template <typename MatcherTuple>
+class ElementsAreMatcher {
+ public:
+  explicit ElementsAreMatcher(const MatcherTuple& args) : matchers_(args) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    GTEST_COMPILE_ASSERT_(
+        !IsHashTable<GTEST_REMOVE_REFERENCE_AND_CONST_(Container)>::value ||
+            ::std::tuple_size<MatcherTuple>::value < 2,
+        use_UnorderedElementsAre_with_hash_tables);
+
+    typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Container) RawContainer;
+    typedef typename internal::StlContainerView<RawContainer>::type View;
+    typedef typename View::value_type Element;
+    typedef ::std::vector<Matcher<const Element&> > MatcherVec;
+    MatcherVec matchers;
+    matchers.reserve(::std::tuple_size<MatcherTuple>::value);
+    TransformTupleValues(CastAndAppendTransform<const Element&>(), matchers_,
+                         ::std::back_inserter(matchers));
+    return Matcher<Container>(new ElementsAreMatcherImpl<const Container&>(
+        matchers.begin(), matchers.end()));
+  }
+
+ private:
+  const MatcherTuple matchers_;
+};
+
+// Implements UnorderedElementsAreArray(), IsSubsetOf(), and IsSupersetOf().
+template <typename T>
+class UnorderedElementsAreArrayMatcher {
+ public:
+  template <typename Iter>
+  UnorderedElementsAreArrayMatcher(UnorderedMatcherRequire::Flags match_flags,
+                                   Iter first, Iter last)
+      : match_flags_(match_flags), matchers_(first, last) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    return Matcher<Container>(
+        new UnorderedElementsAreMatcherImpl<const Container&>(
+            match_flags_, matchers_.begin(), matchers_.end()));
+  }
+
+ private:
+  UnorderedMatcherRequire::Flags match_flags_;
+  ::std::vector<T> matchers_;
+};
+
+// Implements ElementsAreArray().
+template <typename T>
+class ElementsAreArrayMatcher {
+ public:
+  template <typename Iter>
+  ElementsAreArrayMatcher(Iter first, Iter last) : matchers_(first, last) {}
+
+  template <typename Container>
+  operator Matcher<Container>() const {
+    GTEST_COMPILE_ASSERT_(
+        !IsHashTable<GTEST_REMOVE_REFERENCE_AND_CONST_(Container)>::value,
+        use_UnorderedElementsAreArray_with_hash_tables);
+
+    return Matcher<Container>(new ElementsAreMatcherImpl<const Container&>(
+        matchers_.begin(), matchers_.end()));
+  }
+
+ private:
+  const ::std::vector<T> matchers_;
+};
+
+// Given a 2-tuple matcher tm of type Tuple2Matcher and a value second
+// of type Second, BoundSecondMatcher<Tuple2Matcher, Second>(tm,
+// second) is a polymorphic matcher that matches a value x if and only if
+// tm matches tuple (x, second).  Useful for implementing
+// UnorderedPointwise() in terms of UnorderedElementsAreArray().
+//
+// BoundSecondMatcher is copyable and assignable, as we need to put
+// instances of this class in a vector when implementing
+// UnorderedPointwise().
+template <typename Tuple2Matcher, typename Second>
+class BoundSecondMatcher {
+ public:
+  BoundSecondMatcher(const Tuple2Matcher& tm, const Second& second)
+      : tuple2_matcher_(tm), second_value_(second) {}
+
+  BoundSecondMatcher(const BoundSecondMatcher& other) = default;
+
+  template <typename T>
+  operator Matcher<T>() const {
+    return MakeMatcher(new Impl<T>(tuple2_matcher_, second_value_));
+  }
+
+  // We have to define this for UnorderedPointwise() to compile in
+  // C++98 mode, as it puts BoundSecondMatcher instances in a vector,
+  // which requires the elements to be assignable in C++98.  The
+  // compiler cannot generate the operator= for us, as Tuple2Matcher
+  // and Second may not be assignable.
+  //
+  // However, this should never be called, so the implementation just
+  // need to assert.
+  void operator=(const BoundSecondMatcher& /*rhs*/) {
+    GTEST_LOG_(FATAL) << "BoundSecondMatcher should never be assigned.";
+  }
+
+ private:
+  template <typename T>
+  class Impl : public MatcherInterface<T> {
+   public:
+    typedef ::std::tuple<T, Second> ArgTuple;
+
+    Impl(const Tuple2Matcher& tm, const Second& second)
+        : mono_tuple2_matcher_(SafeMatcherCast<const ArgTuple&>(tm)),
+          second_value_(second) {}
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "and ";
+      UniversalPrint(second_value_, os);
+      *os << " ";
+      mono_tuple2_matcher_.DescribeTo(os);
+    }
+
+    bool MatchAndExplain(T x, MatchResultListener* listener) const override {
+      return mono_tuple2_matcher_.MatchAndExplain(ArgTuple(x, second_value_),
+                                                  listener);
+    }
+
+   private:
+    const Matcher<const ArgTuple&> mono_tuple2_matcher_;
+    const Second second_value_;
+  };
+
+  const Tuple2Matcher tuple2_matcher_;
+  const Second second_value_;
+};
+
+// Given a 2-tuple matcher tm and a value second,
+// MatcherBindSecond(tm, second) returns a matcher that matches a
+// value x if and only if tm matches tuple (x, second).  Useful for
+// implementing UnorderedPointwise() in terms of UnorderedElementsAreArray().
+template <typename Tuple2Matcher, typename Second>
+BoundSecondMatcher<Tuple2Matcher, Second> MatcherBindSecond(
+    const Tuple2Matcher& tm, const Second& second) {
+  return BoundSecondMatcher<Tuple2Matcher, Second>(tm, second);
+}
+
+// Returns the description for a matcher defined using the MATCHER*()
+// macro where the user-supplied description string is "", if
+// 'negation' is false; otherwise returns the description of the
+// negation of the matcher.  'param_values' contains a list of strings
+// that are the print-out of the matcher's parameters.
+GTEST_API_ std::string FormatMatcherDescription(bool negation,
+                                                const char* matcher_name,
+                                                const Strings& param_values);
+
+// Implements a matcher that checks the value of a optional<> type variable.
+template <typename ValueMatcher>
+class OptionalMatcher {
+ public:
+  explicit OptionalMatcher(const ValueMatcher& value_matcher)
+      : value_matcher_(value_matcher) {}
+
+  template <typename Optional>
+  operator Matcher<Optional>() const {
+    return Matcher<Optional>(new Impl<const Optional&>(value_matcher_));
+  }
+
+  template <typename Optional>
+  class Impl : public MatcherInterface<Optional> {
+   public:
+    typedef GTEST_REMOVE_REFERENCE_AND_CONST_(Optional) OptionalView;
+    typedef typename OptionalView::value_type ValueType;
+    explicit Impl(const ValueMatcher& value_matcher)
+        : value_matcher_(MatcherCast<ValueType>(value_matcher)) {}
+
+    void DescribeTo(::std::ostream* os) const override {
+      *os << "value ";
+      value_matcher_.DescribeTo(os);
+    }
+
+    void DescribeNegationTo(::std::ostream* os) const override {
+      *os << "value ";
+      value_matcher_.DescribeNegationTo(os);
+    }
+
+    bool MatchAndExplain(Optional optional,
+                         MatchResultListener* listener) const override {
+      if (!optional) {
+        *listener << "which is not engaged";
+        return false;
+      }
+      const ValueType& value = *optional;
+      StringMatchResultListener value_listener;
+      const bool match = value_matcher_.MatchAndExplain(value, &value_listener);
+      *listener << "whose value " << PrintToString(value)
+                << (match ? " matches" : " doesn't match");
+      PrintIfNotEmpty(value_listener.str(), listener->stream());
+      return match;
+    }
+
+   private:
+    const Matcher<ValueType> value_matcher_;
+  };
+
+ private:
+  const ValueMatcher value_matcher_;
+};
+
+namespace variant_matcher {
+// Overloads to allow VariantMatcher to do proper ADL lookup.
+template <typename T>
+void holds_alternative() {}
+template <typename T>
+void get() {}
+
+// Implements a matcher that checks the value of a variant<> type variable.
+template <typename T>
+class VariantMatcher {
+ public:
+  explicit VariantMatcher(::testing::Matcher<const T&> matcher)
+      : matcher_(std::move(matcher)) {}
+
+  template <typename Variant>
+  bool MatchAndExplain(const Variant& value,
+                       ::testing::MatchResultListener* listener) const {
+    using std::get;
+    if (!listener->IsInterested()) {
+      return holds_alternative<T>(value) && matcher_.Matches(get<T>(value));
+    }
+
+    if (!holds_alternative<T>(value)) {
+      *listener << "whose value is not of type '" << GetTypeName() << "'";
+      return false;
+    }
+
+    const T& elem = get<T>(value);
+    StringMatchResultListener elem_listener;
+    const bool match = matcher_.MatchAndExplain(elem, &elem_listener);
+    *listener << "whose value " << PrintToString(elem)
+              << (match ? " matches" : " doesn't match");
+    PrintIfNotEmpty(elem_listener.str(), listener->stream());
+    return match;
+  }
+
+  void DescribeTo(std::ostream* os) const {
+    *os << "is a variant<> with value of type '" << GetTypeName()
+        << "' and the value ";
+    matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(std::ostream* os) const {
+    *os << "is a variant<> with value of type other than '" << GetTypeName()
+        << "' or the value ";
+    matcher_.DescribeNegationTo(os);
+  }
+
+ private:
+  static std::string GetTypeName() {
+#if GTEST_HAS_RTTI
+    GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(
+        return internal::GetTypeName<T>());
+#endif
+    return "the element type";
+  }
+
+  const ::testing::Matcher<const T&> matcher_;
+};
+
+}  // namespace variant_matcher
+
+namespace any_cast_matcher {
+
+// Overloads to allow AnyCastMatcher to do proper ADL lookup.
+template <typename T>
+void any_cast() {}
+
+// Implements a matcher that any_casts the value.
+template <typename T>
+class AnyCastMatcher {
+ public:
+  explicit AnyCastMatcher(const ::testing::Matcher<const T&>& matcher)
+      : matcher_(matcher) {}
+
+  template <typename AnyType>
+  bool MatchAndExplain(const AnyType& value,
+                       ::testing::MatchResultListener* listener) const {
+    if (!listener->IsInterested()) {
+      const T* ptr = any_cast<T>(&value);
+      return ptr != nullptr && matcher_.Matches(*ptr);
+    }
+
+    const T* elem = any_cast<T>(&value);
+    if (elem == nullptr) {
+      *listener << "whose value is not of type '" << GetTypeName() << "'";
+      return false;
+    }
+
+    StringMatchResultListener elem_listener;
+    const bool match = matcher_.MatchAndExplain(*elem, &elem_listener);
+    *listener << "whose value " << PrintToString(*elem)
+              << (match ? " matches" : " doesn't match");
+    PrintIfNotEmpty(elem_listener.str(), listener->stream());
+    return match;
+  }
+
+  void DescribeTo(std::ostream* os) const {
+    *os << "is an 'any' type with value of type '" << GetTypeName()
+        << "' and the value ";
+    matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(std::ostream* os) const {
+    *os << "is an 'any' type with value of type other than '" << GetTypeName()
+        << "' or the value ";
+    matcher_.DescribeNegationTo(os);
+  }
+
+ private:
+  static std::string GetTypeName() {
+#if GTEST_HAS_RTTI
+    GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(
+        return internal::GetTypeName<T>());
+#endif
+    return "the element type";
+  }
+
+  const ::testing::Matcher<const T&> matcher_;
+};
+
+}  // namespace any_cast_matcher
+
+// Implements the Args() matcher.
+template <class ArgsTuple, size_t... k>
+class ArgsMatcherImpl : public MatcherInterface<ArgsTuple> {
+ public:
+  using RawArgsTuple = typename std::decay<ArgsTuple>::type;
+  using SelectedArgs =
+      std::tuple<typename std::tuple_element<k, RawArgsTuple>::type...>;
+  using MonomorphicInnerMatcher = Matcher<const SelectedArgs&>;
+
+  template <typename InnerMatcher>
+  explicit ArgsMatcherImpl(const InnerMatcher& inner_matcher)
+      : inner_matcher_(SafeMatcherCast<const SelectedArgs&>(inner_matcher)) {}
+
+  bool MatchAndExplain(ArgsTuple args,
+                       MatchResultListener* listener) const override {
+    // Workaround spurious C4100 on MSVC<=15.7 when k is empty.
+    (void)args;
+    const SelectedArgs& selected_args =
+        std::forward_as_tuple(std::get<k>(args)...);
+    if (!listener->IsInterested()) return inner_matcher_.Matches(selected_args);
+
+    PrintIndices(listener->stream());
+    *listener << "are " << PrintToString(selected_args);
+
+    StringMatchResultListener inner_listener;
+    const bool match =
+        inner_matcher_.MatchAndExplain(selected_args, &inner_listener);
+    PrintIfNotEmpty(inner_listener.str(), listener->stream());
+    return match;
+  }
+
+  void DescribeTo(::std::ostream* os) const override {
+    *os << "are a tuple ";
+    PrintIndices(os);
+    inner_matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const override {
+    *os << "are a tuple ";
+    PrintIndices(os);
+    inner_matcher_.DescribeNegationTo(os);
+  }
+
+ private:
+  // Prints the indices of the selected fields.
+  static void PrintIndices(::std::ostream* os) {
+    *os << "whose fields (";
+    const char* sep = "";
+    // Workaround spurious C4189 on MSVC<=15.7 when k is empty.
+    (void)sep;
+    const char* dummy[] = {"", (*os << sep << "#" << k, sep = ", ")...};
+    (void)dummy;
+    *os << ") ";
+  }
+
+  MonomorphicInnerMatcher inner_matcher_;
+};
+
+template <class InnerMatcher, size_t... k>
+class ArgsMatcher {
+ public:
+  explicit ArgsMatcher(InnerMatcher inner_matcher)
+      : inner_matcher_(std::move(inner_matcher)) {}
+
+  template <typename ArgsTuple>
+  operator Matcher<ArgsTuple>() const {  // NOLINT
+    return MakeMatcher(new ArgsMatcherImpl<ArgsTuple, k...>(inner_matcher_));
+  }
+
+ private:
+  InnerMatcher inner_matcher_;
+};
+
+}  // namespace internal
+
+// ElementsAreArray(iterator_first, iterator_last)
+// ElementsAreArray(pointer, count)
+// ElementsAreArray(array)
+// ElementsAreArray(container)
+// ElementsAreArray({ e1, e2, ..., en })
+//
+// The ElementsAreArray() functions are like ElementsAre(...), except
+// that they are given a homogeneous sequence rather than taking each
+// element as a function argument. The sequence can be specified as an
+// array, a pointer and count, a vector, an initializer list, or an
+// STL iterator range. In each of these cases, the underlying sequence
+// can be either a sequence of values or a sequence of matchers.
+//
+// All forms of ElementsAreArray() make a copy of the input matcher sequence.
+
+template <typename Iter>
+inline internal::ElementsAreArrayMatcher<
+    typename ::std::iterator_traits<Iter>::value_type>
+ElementsAreArray(Iter first, Iter last) {
+  typedef typename ::std::iterator_traits<Iter>::value_type T;
+  return internal::ElementsAreArrayMatcher<T>(first, last);
+}
+
+template <typename T>
+inline auto ElementsAreArray(const T* pointer, size_t count)
+    -> decltype(ElementsAreArray(pointer, pointer + count)) {
+  return ElementsAreArray(pointer, pointer + count);
+}
+
+template <typename T, size_t N>
+inline auto ElementsAreArray(const T (&array)[N])
+    -> decltype(ElementsAreArray(array, N)) {
+  return ElementsAreArray(array, N);
+}
+
+template <typename Container>
+inline auto ElementsAreArray(const Container& container)
+    -> decltype(ElementsAreArray(container.begin(), container.end())) {
+  return ElementsAreArray(container.begin(), container.end());
+}
+
+template <typename T>
+inline auto ElementsAreArray(::std::initializer_list<T> xs)
+    -> decltype(ElementsAreArray(xs.begin(), xs.end())) {
+  return ElementsAreArray(xs.begin(), xs.end());
+}
+
+// UnorderedElementsAreArray(iterator_first, iterator_last)
+// UnorderedElementsAreArray(pointer, count)
+// UnorderedElementsAreArray(array)
+// UnorderedElementsAreArray(container)
+// UnorderedElementsAreArray({ e1, e2, ..., en })
+//
+// UnorderedElementsAreArray() verifies that a bijective mapping onto a
+// collection of matchers exists.
+//
+// The matchers can be specified as an array, a pointer and count, a container,
+// an initializer list, or an STL iterator range. In each of these cases, the
+// underlying matchers can be either values or matchers.
+
+template <typename Iter>
+inline internal::UnorderedElementsAreArrayMatcher<
+    typename ::std::iterator_traits<Iter>::value_type>
+UnorderedElementsAreArray(Iter first, Iter last) {
+  typedef typename ::std::iterator_traits<Iter>::value_type T;
+  return internal::UnorderedElementsAreArrayMatcher<T>(
+      internal::UnorderedMatcherRequire::ExactMatch, first, last);
+}
+
+template <typename T>
+inline internal::UnorderedElementsAreArrayMatcher<T>
+UnorderedElementsAreArray(const T* pointer, size_t count) {
+  return UnorderedElementsAreArray(pointer, pointer + count);
+}
+
+template <typename T, size_t N>
+inline internal::UnorderedElementsAreArrayMatcher<T>
+UnorderedElementsAreArray(const T (&array)[N]) {
+  return UnorderedElementsAreArray(array, N);
+}
+
+template <typename Container>
+inline internal::UnorderedElementsAreArrayMatcher<
+    typename Container::value_type>
+UnorderedElementsAreArray(const Container& container) {
+  return UnorderedElementsAreArray(container.begin(), container.end());
+}
+
+template <typename T>
+inline internal::UnorderedElementsAreArrayMatcher<T>
+UnorderedElementsAreArray(::std::initializer_list<T> xs) {
+  return UnorderedElementsAreArray(xs.begin(), xs.end());
+}
+
+// _ is a matcher that matches anything of any type.
+//
+// This definition is fine as:
+//
+//   1. The C++ standard permits using the name _ in a namespace that
+//      is not the global namespace or ::std.
+//   2. The AnythingMatcher class has no data member or constructor,
+//      so it's OK to create global variables of this type.
+//   3. c-style has approved of using _ in this case.
+const internal::AnythingMatcher _ = {};
+// Creates a matcher that matches any value of the given type T.
+template <typename T>
+inline Matcher<T> A() {
+  return _;
+}
+
+// Creates a matcher that matches any value of the given type T.
+template <typename T>
+inline Matcher<T> An() {
+  return _;
+}
+
+template <typename T, typename M>
+Matcher<T> internal::MatcherCastImpl<T, M>::CastImpl(
+    const M& value, std::false_type /* convertible_to_matcher */,
+    std::false_type /* convertible_to_T */) {
+  return Eq(value);
+}
+
+// Creates a polymorphic matcher that matches any NULL pointer.
+inline PolymorphicMatcher<internal::IsNullMatcher > IsNull() {
+  return MakePolymorphicMatcher(internal::IsNullMatcher());
+}
+
+// Creates a polymorphic matcher that matches any non-NULL pointer.
+// This is convenient as Not(NULL) doesn't compile (the compiler
+// thinks that that expression is comparing a pointer with an integer).
+inline PolymorphicMatcher<internal::NotNullMatcher > NotNull() {
+  return MakePolymorphicMatcher(internal::NotNullMatcher());
+}
+
+// Creates a polymorphic matcher that matches any argument that
+// references variable x.
+template <typename T>
+inline internal::RefMatcher<T&> Ref(T& x) {  // NOLINT
+  return internal::RefMatcher<T&>(x);
+}
+
+// Creates a polymorphic matcher that matches any NaN floating point.
+inline PolymorphicMatcher<internal::IsNanMatcher> IsNan() {
+  return MakePolymorphicMatcher(internal::IsNanMatcher());
+}
+
+// Creates a matcher that matches any double argument approximately
+// equal to rhs, where two NANs are considered unequal.
+inline internal::FloatingEqMatcher<double> DoubleEq(double rhs) {
+  return internal::FloatingEqMatcher<double>(rhs, false);
+}
+
+// Creates a matcher that matches any double argument approximately
+// equal to rhs, including NaN values when rhs is NaN.
+inline internal::FloatingEqMatcher<double> NanSensitiveDoubleEq(double rhs) {
+  return internal::FloatingEqMatcher<double>(rhs, true);
+}
+
+// Creates a matcher that matches any double argument approximately equal to
+// rhs, up to the specified max absolute error bound, where two NANs are
+// considered unequal.  The max absolute error bound must be non-negative.
+inline internal::FloatingEqMatcher<double> DoubleNear(
+    double rhs, double max_abs_error) {
+  return internal::FloatingEqMatcher<double>(rhs, false, max_abs_error);
+}
+
+// Creates a matcher that matches any double argument approximately equal to
+// rhs, up to the specified max absolute error bound, including NaN values when
+// rhs is NaN.  The max absolute error bound must be non-negative.
+inline internal::FloatingEqMatcher<double> NanSensitiveDoubleNear(
+    double rhs, double max_abs_error) {
+  return internal::FloatingEqMatcher<double>(rhs, true, max_abs_error);
+}
+
+// Creates a matcher that matches any float argument approximately
+// equal to rhs, where two NANs are considered unequal.
+inline internal::FloatingEqMatcher<float> FloatEq(float rhs) {
+  return internal::FloatingEqMatcher<float>(rhs, false);
+}
+
+// Creates a matcher that matches any float argument approximately
+// equal to rhs, including NaN values when rhs is NaN.
+inline internal::FloatingEqMatcher<float> NanSensitiveFloatEq(float rhs) {
+  return internal::FloatingEqMatcher<float>(rhs, true);
+}
+
+// Creates a matcher that matches any float argument approximately equal to
+// rhs, up to the specified max absolute error bound, where two NANs are
+// considered unequal.  The max absolute error bound must be non-negative.
+inline internal::FloatingEqMatcher<float> FloatNear(
+    float rhs, float max_abs_error) {
+  return internal::FloatingEqMatcher<float>(rhs, false, max_abs_error);
+}
+
+// Creates a matcher that matches any float argument approximately equal to
+// rhs, up to the specified max absolute error bound, including NaN values when
+// rhs is NaN.  The max absolute error bound must be non-negative.
+inline internal::FloatingEqMatcher<float> NanSensitiveFloatNear(
+    float rhs, float max_abs_error) {
+  return internal::FloatingEqMatcher<float>(rhs, true, max_abs_error);
+}
+
+// Creates a matcher that matches a pointer (raw or smart) that points
+// to a value that matches inner_matcher.
+template <typename InnerMatcher>
+inline internal::PointeeMatcher<InnerMatcher> Pointee(
+    const InnerMatcher& inner_matcher) {
+  return internal::PointeeMatcher<InnerMatcher>(inner_matcher);
+}
+
+#if GTEST_HAS_RTTI
+// Creates a matcher that matches a pointer or reference that matches
+// inner_matcher when dynamic_cast<To> is applied.
+// The result of dynamic_cast<To> is forwarded to the inner matcher.
+// If To is a pointer and the cast fails, the inner matcher will receive NULL.
+// If To is a reference and the cast fails, this matcher returns false
+// immediately.
+template <typename To>
+inline PolymorphicMatcher<internal::WhenDynamicCastToMatcher<To> >
+WhenDynamicCastTo(const Matcher<To>& inner_matcher) {
+  return MakePolymorphicMatcher(
+      internal::WhenDynamicCastToMatcher<To>(inner_matcher));
+}
+#endif  // GTEST_HAS_RTTI
+
+// Creates a matcher that matches an object whose given field matches
+// 'matcher'.  For example,
+//   Field(&Foo::number, Ge(5))
+// matches a Foo object x if and only if x.number >= 5.
+template <typename Class, typename FieldType, typename FieldMatcher>
+inline PolymorphicMatcher<
+  internal::FieldMatcher<Class, FieldType> > Field(
+    FieldType Class::*field, const FieldMatcher& matcher) {
+  return MakePolymorphicMatcher(
+      internal::FieldMatcher<Class, FieldType>(
+          field, MatcherCast<const FieldType&>(matcher)));
+  // The call to MatcherCast() is required for supporting inner
+  // matchers of compatible types.  For example, it allows
+  //   Field(&Foo::bar, m)
+  // to compile where bar is an int32 and m is a matcher for int64.
+}
+
+// Same as Field() but also takes the name of the field to provide better error
+// messages.
+template <typename Class, typename FieldType, typename FieldMatcher>
+inline PolymorphicMatcher<internal::FieldMatcher<Class, FieldType> > Field(
+    const std::string& field_name, FieldType Class::*field,
+    const FieldMatcher& matcher) {
+  return MakePolymorphicMatcher(internal::FieldMatcher<Class, FieldType>(
+      field_name, field, MatcherCast<const FieldType&>(matcher)));
+}
+
+// Creates a matcher that matches an object whose given property
+// matches 'matcher'.  For example,
+//   Property(&Foo::str, StartsWith("hi"))
+// matches a Foo object x if and only if x.str() starts with "hi".
+template <typename Class, typename PropertyType, typename PropertyMatcher>
+inline PolymorphicMatcher<internal::PropertyMatcher<
+    Class, PropertyType, PropertyType (Class::*)() const> >
+Property(PropertyType (Class::*property)() const,
+         const PropertyMatcher& matcher) {
+  return MakePolymorphicMatcher(
+      internal::PropertyMatcher<Class, PropertyType,
+                                PropertyType (Class::*)() const>(
+          property, MatcherCast<const PropertyType&>(matcher)));
+  // The call to MatcherCast() is required for supporting inner
+  // matchers of compatible types.  For example, it allows
+  //   Property(&Foo::bar, m)
+  // to compile where bar() returns an int32 and m is a matcher for int64.
+}
+
+// Same as Property() above, but also takes the name of the property to provide
+// better error messages.
+template <typename Class, typename PropertyType, typename PropertyMatcher>
+inline PolymorphicMatcher<internal::PropertyMatcher<
+    Class, PropertyType, PropertyType (Class::*)() const> >
+Property(const std::string& property_name,
+         PropertyType (Class::*property)() const,
+         const PropertyMatcher& matcher) {
+  return MakePolymorphicMatcher(
+      internal::PropertyMatcher<Class, PropertyType,
+                                PropertyType (Class::*)() const>(
+          property_name, property, MatcherCast<const PropertyType&>(matcher)));
+}
+
+// The same as above but for reference-qualified member functions.
+template <typename Class, typename PropertyType, typename PropertyMatcher>
+inline PolymorphicMatcher<internal::PropertyMatcher<
+    Class, PropertyType, PropertyType (Class::*)() const &> >
+Property(PropertyType (Class::*property)() const &,
+         const PropertyMatcher& matcher) {
+  return MakePolymorphicMatcher(
+      internal::PropertyMatcher<Class, PropertyType,
+                                PropertyType (Class::*)() const&>(
+          property, MatcherCast<const PropertyType&>(matcher)));
+}
+
+// Three-argument form for reference-qualified member functions.
+template <typename Class, typename PropertyType, typename PropertyMatcher>
+inline PolymorphicMatcher<internal::PropertyMatcher<
+    Class, PropertyType, PropertyType (Class::*)() const &> >
+Property(const std::string& property_name,
+         PropertyType (Class::*property)() const &,
+         const PropertyMatcher& matcher) {
+  return MakePolymorphicMatcher(
+      internal::PropertyMatcher<Class, PropertyType,
+                                PropertyType (Class::*)() const&>(
+          property_name, property, MatcherCast<const PropertyType&>(matcher)));
+}
+
+// Creates a matcher that matches an object if and only if the result of
+// applying a callable to x matches 'matcher'. For example,
+//   ResultOf(f, StartsWith("hi"))
+// matches a Foo object x if and only if f(x) starts with "hi".
+// `callable` parameter can be a function, function pointer, or a functor. It is
+// required to keep no state affecting the results of the calls on it and make
+// no assumptions about how many calls will be made. Any state it keeps must be
+// protected from the concurrent access.
+template <typename Callable, typename InnerMatcher>
+internal::ResultOfMatcher<Callable, InnerMatcher> ResultOf(
+    Callable callable, InnerMatcher matcher) {
+  return internal::ResultOfMatcher<Callable, InnerMatcher>(
+      std::move(callable), std::move(matcher));
+}
+
+// String matchers.
+
+// Matches a string equal to str.
+template <typename T = std::string>
+PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrEq(
+    const internal::StringLike<T>& str) {
+  return MakePolymorphicMatcher(
+      internal::StrEqualityMatcher<std::string>(std::string(str), true, true));
+}
+
+// Matches a string not equal to str.
+template <typename T = std::string>
+PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrNe(
+    const internal::StringLike<T>& str) {
+  return MakePolymorphicMatcher(
+      internal::StrEqualityMatcher<std::string>(std::string(str), false, true));
+}
+
+// Matches a string equal to str, ignoring case.
+template <typename T = std::string>
+PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrCaseEq(
+    const internal::StringLike<T>& str) {
+  return MakePolymorphicMatcher(
+      internal::StrEqualityMatcher<std::string>(std::string(str), true, false));
+}
+
+// Matches a string not equal to str, ignoring case.
+template <typename T = std::string>
+PolymorphicMatcher<internal::StrEqualityMatcher<std::string> > StrCaseNe(
+    const internal::StringLike<T>& str) {
+  return MakePolymorphicMatcher(internal::StrEqualityMatcher<std::string>(
+      std::string(str), false, false));
+}
+
+// Creates a matcher that matches any string, std::string, or C string
+// that contains the given substring.
+template <typename T = std::string>
+PolymorphicMatcher<internal::HasSubstrMatcher<std::string> > HasSubstr(
+    const internal::StringLike<T>& substring) {
+  return MakePolymorphicMatcher(
+      internal::HasSubstrMatcher<std::string>(std::string(substring)));
+}
+
+// Matches a string that starts with 'prefix' (case-sensitive).
+template <typename T = std::string>
+PolymorphicMatcher<internal::StartsWithMatcher<std::string> > StartsWith(
+    const internal::StringLike<T>& prefix) {
+  return MakePolymorphicMatcher(
+      internal::StartsWithMatcher<std::string>(std::string(prefix)));
+}
+
+// Matches a string that ends with 'suffix' (case-sensitive).
+template <typename T = std::string>
+PolymorphicMatcher<internal::EndsWithMatcher<std::string> > EndsWith(
+    const internal::StringLike<T>& suffix) {
+  return MakePolymorphicMatcher(
+      internal::EndsWithMatcher<std::string>(std::string(suffix)));
+}
+
+#if GTEST_HAS_STD_WSTRING
+// Wide string matchers.
+
+// Matches a string equal to str.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring> > StrEq(
+    const std::wstring& str) {
+  return MakePolymorphicMatcher(
+      internal::StrEqualityMatcher<std::wstring>(str, true, true));
+}
+
+// Matches a string not equal to str.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring> > StrNe(
+    const std::wstring& str) {
+  return MakePolymorphicMatcher(
+      internal::StrEqualityMatcher<std::wstring>(str, false, true));
+}
+
+// Matches a string equal to str, ignoring case.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring> >
+StrCaseEq(const std::wstring& str) {
+  return MakePolymorphicMatcher(
+      internal::StrEqualityMatcher<std::wstring>(str, true, false));
+}
+
+// Matches a string not equal to str, ignoring case.
+inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring> >
+StrCaseNe(const std::wstring& str) {
+  return MakePolymorphicMatcher(
+      internal::StrEqualityMatcher<std::wstring>(str, false, false));
+}
+
+// Creates a matcher that matches any ::wstring, std::wstring, or C wide string
+// that contains the given substring.
+inline PolymorphicMatcher<internal::HasSubstrMatcher<std::wstring> > HasSubstr(
+    const std::wstring& substring) {
+  return MakePolymorphicMatcher(
+      internal::HasSubstrMatcher<std::wstring>(substring));
+}
+
+// Matches a string that starts with 'prefix' (case-sensitive).
+inline PolymorphicMatcher<internal::StartsWithMatcher<std::wstring> >
+StartsWith(const std::wstring& prefix) {
+  return MakePolymorphicMatcher(
+      internal::StartsWithMatcher<std::wstring>(prefix));
+}
+
+// Matches a string that ends with 'suffix' (case-sensitive).
+inline PolymorphicMatcher<internal::EndsWithMatcher<std::wstring> > EndsWith(
+    const std::wstring& suffix) {
+  return MakePolymorphicMatcher(
+      internal::EndsWithMatcher<std::wstring>(suffix));
+}
+
+#endif  // GTEST_HAS_STD_WSTRING
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field == the second field.
+inline internal::Eq2Matcher Eq() { return internal::Eq2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field >= the second field.
+inline internal::Ge2Matcher Ge() { return internal::Ge2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field > the second field.
+inline internal::Gt2Matcher Gt() { return internal::Gt2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field <= the second field.
+inline internal::Le2Matcher Le() { return internal::Le2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field < the second field.
+inline internal::Lt2Matcher Lt() { return internal::Lt2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where the
+// first field != the second field.
+inline internal::Ne2Matcher Ne() { return internal::Ne2Matcher(); }
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// FloatEq(first field) matches the second field.
+inline internal::FloatingEq2Matcher<float> FloatEq() {
+  return internal::FloatingEq2Matcher<float>();
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// DoubleEq(first field) matches the second field.
+inline internal::FloatingEq2Matcher<double> DoubleEq() {
+  return internal::FloatingEq2Matcher<double>();
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// FloatEq(first field) matches the second field with NaN equality.
+inline internal::FloatingEq2Matcher<float> NanSensitiveFloatEq() {
+  return internal::FloatingEq2Matcher<float>(true);
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// DoubleEq(first field) matches the second field with NaN equality.
+inline internal::FloatingEq2Matcher<double> NanSensitiveDoubleEq() {
+  return internal::FloatingEq2Matcher<double>(true);
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// FloatNear(first field, max_abs_error) matches the second field.
+inline internal::FloatingEq2Matcher<float> FloatNear(float max_abs_error) {
+  return internal::FloatingEq2Matcher<float>(max_abs_error);
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// DoubleNear(first field, max_abs_error) matches the second field.
+inline internal::FloatingEq2Matcher<double> DoubleNear(double max_abs_error) {
+  return internal::FloatingEq2Matcher<double>(max_abs_error);
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// FloatNear(first field, max_abs_error) matches the second field with NaN
+// equality.
+inline internal::FloatingEq2Matcher<float> NanSensitiveFloatNear(
+    float max_abs_error) {
+  return internal::FloatingEq2Matcher<float>(max_abs_error, true);
+}
+
+// Creates a polymorphic matcher that matches a 2-tuple where
+// DoubleNear(first field, max_abs_error) matches the second field with NaN
+// equality.
+inline internal::FloatingEq2Matcher<double> NanSensitiveDoubleNear(
+    double max_abs_error) {
+  return internal::FloatingEq2Matcher<double>(max_abs_error, true);
+}
+
+// Creates a matcher that matches any value of type T that m doesn't
+// match.
+template <typename InnerMatcher>
+inline internal::NotMatcher<InnerMatcher> Not(InnerMatcher m) {
+  return internal::NotMatcher<InnerMatcher>(m);
+}
+
+// Returns a matcher that matches anything that satisfies the given
+// predicate.  The predicate can be any unary function or functor
+// whose return type can be implicitly converted to bool.
+template <typename Predicate>
+inline PolymorphicMatcher<internal::TrulyMatcher<Predicate> >
+Truly(Predicate pred) {
+  return MakePolymorphicMatcher(internal::TrulyMatcher<Predicate>(pred));
+}
+
+// Returns a matcher that matches the container size. The container must
+// support both size() and size_type which all STL-like containers provide.
+// Note that the parameter 'size' can be a value of type size_type as well as
+// matcher. For instance:
+//   EXPECT_THAT(container, SizeIs(2));     // Checks container has 2 elements.
+//   EXPECT_THAT(container, SizeIs(Le(2));  // Checks container has at most 2.
+template <typename SizeMatcher>
+inline internal::SizeIsMatcher<SizeMatcher>
+SizeIs(const SizeMatcher& size_matcher) {
+  return internal::SizeIsMatcher<SizeMatcher>(size_matcher);
+}
+
+// Returns a matcher that matches the distance between the container's begin()
+// iterator and its end() iterator, i.e. the size of the container. This matcher
+// can be used instead of SizeIs with containers such as std::forward_list which
+// do not implement size(). The container must provide const_iterator (with
+// valid iterator_traits), begin() and end().
+template <typename DistanceMatcher>
+inline internal::BeginEndDistanceIsMatcher<DistanceMatcher>
+BeginEndDistanceIs(const DistanceMatcher& distance_matcher) {
+  return internal::BeginEndDistanceIsMatcher<DistanceMatcher>(distance_matcher);
+}
+
+// Returns a matcher that matches an equal container.
+// This matcher behaves like Eq(), but in the event of mismatch lists the
+// values that are included in one container but not the other. (Duplicate
+// values and order differences are not explained.)
+template <typename Container>
+inline PolymorphicMatcher<internal::ContainerEqMatcher<
+    typename std::remove_const<Container>::type>>
+ContainerEq(const Container& rhs) {
+  return MakePolymorphicMatcher(internal::ContainerEqMatcher<Container>(rhs));
+}
+
+// Returns a matcher that matches a container that, when sorted using
+// the given comparator, matches container_matcher.
+template <typename Comparator, typename ContainerMatcher>
+inline internal::WhenSortedByMatcher<Comparator, ContainerMatcher>
+WhenSortedBy(const Comparator& comparator,
+             const ContainerMatcher& container_matcher) {
+  return internal::WhenSortedByMatcher<Comparator, ContainerMatcher>(
+      comparator, container_matcher);
+}
+
+// Returns a matcher that matches a container that, when sorted using
+// the < operator, matches container_matcher.
+template <typename ContainerMatcher>
+inline internal::WhenSortedByMatcher<internal::LessComparator, ContainerMatcher>
+WhenSorted(const ContainerMatcher& container_matcher) {
+  return
+      internal::WhenSortedByMatcher<internal::LessComparator, ContainerMatcher>(
+          internal::LessComparator(), container_matcher);
+}
+
+// Matches an STL-style container or a native array that contains the
+// same number of elements as in rhs, where its i-th element and rhs's
+// i-th element (as a pair) satisfy the given pair matcher, for all i.
+// TupleMatcher must be able to be safely cast to Matcher<std::tuple<const
+// T1&, const T2&> >, where T1 and T2 are the types of elements in the
+// LHS container and the RHS container respectively.
+template <typename TupleMatcher, typename Container>
+inline internal::PointwiseMatcher<TupleMatcher,
+                                  typename std::remove_const<Container>::type>
+Pointwise(const TupleMatcher& tuple_matcher, const Container& rhs) {
+  return internal::PointwiseMatcher<TupleMatcher, Container>(tuple_matcher,
+                                                             rhs);
+}
+
+
+// Supports the Pointwise(m, {a, b, c}) syntax.
+template <typename TupleMatcher, typename T>
+inline internal::PointwiseMatcher<TupleMatcher, std::vector<T> > Pointwise(
+    const TupleMatcher& tuple_matcher, std::initializer_list<T> rhs) {
+  return Pointwise(tuple_matcher, std::vector<T>(rhs));
+}
+
+
+// UnorderedPointwise(pair_matcher, rhs) matches an STL-style
+// container or a native array that contains the same number of
+// elements as in rhs, where in some permutation of the container, its
+// i-th element and rhs's i-th element (as a pair) satisfy the given
+// pair matcher, for all i.  Tuple2Matcher must be able to be safely
+// cast to Matcher<std::tuple<const T1&, const T2&> >, where T1 and T2 are
+// the types of elements in the LHS container and the RHS container
+// respectively.
+//
+// This is like Pointwise(pair_matcher, rhs), except that the element
+// order doesn't matter.
+template <typename Tuple2Matcher, typename RhsContainer>
+inline internal::UnorderedElementsAreArrayMatcher<
+    typename internal::BoundSecondMatcher<
+        Tuple2Matcher,
+        typename internal::StlContainerView<
+            typename std::remove_const<RhsContainer>::type>::type::value_type>>
+UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
+                   const RhsContainer& rhs_container) {
+  // RhsView allows the same code to handle RhsContainer being a
+  // STL-style container and it being a native C-style array.
+  typedef typename internal::StlContainerView<RhsContainer> RhsView;
+  typedef typename RhsView::type RhsStlContainer;
+  typedef typename RhsStlContainer::value_type Second;
+  const RhsStlContainer& rhs_stl_container =
+      RhsView::ConstReference(rhs_container);
+
+  // Create a matcher for each element in rhs_container.
+  ::std::vector<internal::BoundSecondMatcher<Tuple2Matcher, Second> > matchers;
+  for (typename RhsStlContainer::const_iterator it = rhs_stl_container.begin();
+       it != rhs_stl_container.end(); ++it) {
+    matchers.push_back(
+        internal::MatcherBindSecond(tuple2_matcher, *it));
+  }
+
+  // Delegate the work to UnorderedElementsAreArray().
+  return UnorderedElementsAreArray(matchers);
+}
+
+
+// Supports the UnorderedPointwise(m, {a, b, c}) syntax.
+template <typename Tuple2Matcher, typename T>
+inline internal::UnorderedElementsAreArrayMatcher<
+    typename internal::BoundSecondMatcher<Tuple2Matcher, T> >
+UnorderedPointwise(const Tuple2Matcher& tuple2_matcher,
+                   std::initializer_list<T> rhs) {
+  return UnorderedPointwise(tuple2_matcher, std::vector<T>(rhs));
+}
+
+// Matches an STL-style container or a native array that contains at
+// least one element matching the given value or matcher.
+//
+// Examples:
+//   ::std::set<int> page_ids;
+//   page_ids.insert(3);
+//   page_ids.insert(1);
+//   EXPECT_THAT(page_ids, Contains(1));
+//   EXPECT_THAT(page_ids, Contains(Gt(2)));
+//   EXPECT_THAT(page_ids, Not(Contains(4)));  // See below for Times(0)
+//
+//   ::std::map<int, size_t> page_lengths;
+//   page_lengths[1] = 100;
+//   EXPECT_THAT(page_lengths,
+//               Contains(::std::pair<const int, size_t>(1, 100)));
+//
+//   const char* user_ids[] = { "joe", "mike", "tom" };
+//   EXPECT_THAT(user_ids, Contains(Eq(::std::string("tom"))));
+//
+// The matcher supports a modifier `Times` that allows to check for arbitrary
+// occurrences including testing for absence with Times(0).
+//
+// Examples:
+//   ::std::vector<int> ids;
+//   ids.insert(1);
+//   ids.insert(1);
+//   ids.insert(3);
+//   EXPECT_THAT(ids, Contains(1).Times(2));      // 1 occurs 2 times
+//   EXPECT_THAT(ids, Contains(2).Times(0));      // 2 is not present
+//   EXPECT_THAT(ids, Contains(3).Times(Ge(1)));  // 3 occurs at least once
+
+template <typename M>
+inline internal::ContainsMatcher<M> Contains(M matcher) {
+  return internal::ContainsMatcher<M>(matcher);
+}
+
+// IsSupersetOf(iterator_first, iterator_last)
+// IsSupersetOf(pointer, count)
+// IsSupersetOf(array)
+// IsSupersetOf(container)
+// IsSupersetOf({e1, e2, ..., en})
+//
+// IsSupersetOf() verifies that a surjective partial mapping onto a collection
+// of matchers exists. In other words, a container matches
+// IsSupersetOf({e1, ..., en}) if and only if there is a permutation
+// {y1, ..., yn} of some of the container's elements where y1 matches e1,
+// ..., and yn matches en. Obviously, the size of the container must be >= n
+// in order to have a match. Examples:
+//
+// - {1, 2, 3} matches IsSupersetOf({Ge(3), Ne(0)}), as 3 matches Ge(3) and
+//   1 matches Ne(0).
+// - {1, 2} doesn't match IsSupersetOf({Eq(1), Lt(2)}), even though 1 matches
+//   both Eq(1) and Lt(2). The reason is that different matchers must be used
+//   for elements in different slots of the container.
+// - {1, 1, 2} matches IsSupersetOf({Eq(1), Lt(2)}), as (the first) 1 matches
+//   Eq(1) and (the second) 1 matches Lt(2).
+// - {1, 2, 3} matches IsSupersetOf(Gt(1), Gt(1)), as 2 matches (the first)
+//   Gt(1) and 3 matches (the second) Gt(1).
+//
+// The matchers can be specified as an array, a pointer and count, a container,
+// an initializer list, or an STL iterator range. In each of these cases, the
+// underlying matchers can be either values or matchers.
+
+template <typename Iter>
+inline internal::UnorderedElementsAreArrayMatcher<
+    typename ::std::iterator_traits<Iter>::value_type>
+IsSupersetOf(Iter first, Iter last) {
+  typedef typename ::std::iterator_traits<Iter>::value_type T;
+  return internal::UnorderedElementsAreArrayMatcher<T>(
+      internal::UnorderedMatcherRequire::Superset, first, last);
+}
+
+template <typename T>
+inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf(
+    const T* pointer, size_t count) {
+  return IsSupersetOf(pointer, pointer + count);
+}
+
+template <typename T, size_t N>
+inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf(
+    const T (&array)[N]) {
+  return IsSupersetOf(array, N);
+}
+
+template <typename Container>
+inline internal::UnorderedElementsAreArrayMatcher<
+    typename Container::value_type>
+IsSupersetOf(const Container& container) {
+  return IsSupersetOf(container.begin(), container.end());
+}
+
+template <typename T>
+inline internal::UnorderedElementsAreArrayMatcher<T> IsSupersetOf(
+    ::std::initializer_list<T> xs) {
+  return IsSupersetOf(xs.begin(), xs.end());
+}
+
+// IsSubsetOf(iterator_first, iterator_last)
+// IsSubsetOf(pointer, count)
+// IsSubsetOf(array)
+// IsSubsetOf(container)
+// IsSubsetOf({e1, e2, ..., en})
+//
+// IsSubsetOf() verifies that an injective mapping onto a collection of matchers
+// exists.  In other words, a container matches IsSubsetOf({e1, ..., en}) if and
+// only if there is a subset of matchers {m1, ..., mk} which would match the
+// container using UnorderedElementsAre.  Obviously, the size of the container
+// must be <= n in order to have a match. Examples:
+//
+// - {1} matches IsSubsetOf({Gt(0), Lt(0)}), as 1 matches Gt(0).
+// - {1, -1} matches IsSubsetOf({Lt(0), Gt(0)}), as 1 matches Gt(0) and -1
+//   matches Lt(0).
+// - {1, 2} doesn't matches IsSubsetOf({Gt(0), Lt(0)}), even though 1 and 2 both
+//   match Gt(0). The reason is that different matchers must be used for
+//   elements in different slots of the container.
+//
+// The matchers can be specified as an array, a pointer and count, a container,
+// an initializer list, or an STL iterator range. In each of these cases, the
+// underlying matchers can be either values or matchers.
+
+template <typename Iter>
+inline internal::UnorderedElementsAreArrayMatcher<
+    typename ::std::iterator_traits<Iter>::value_type>
+IsSubsetOf(Iter first, Iter last) {
+  typedef typename ::std::iterator_traits<Iter>::value_type T;
+  return internal::UnorderedElementsAreArrayMatcher<T>(
+      internal::UnorderedMatcherRequire::Subset, first, last);
+}
+
+template <typename T>
+inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf(
+    const T* pointer, size_t count) {
+  return IsSubsetOf(pointer, pointer + count);
+}
+
+template <typename T, size_t N>
+inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf(
+    const T (&array)[N]) {
+  return IsSubsetOf(array, N);
+}
+
+template <typename Container>
+inline internal::UnorderedElementsAreArrayMatcher<
+    typename Container::value_type>
+IsSubsetOf(const Container& container) {
+  return IsSubsetOf(container.begin(), container.end());
+}
+
+template <typename T>
+inline internal::UnorderedElementsAreArrayMatcher<T> IsSubsetOf(
+    ::std::initializer_list<T> xs) {
+  return IsSubsetOf(xs.begin(), xs.end());
+}
+
+// Matches an STL-style container or a native array that contains only
+// elements matching the given value or matcher.
+//
+// Each(m) is semantically equivalent to `Not(Contains(Not(m)))`. Only
+// the messages are different.
+//
+// Examples:
+//   ::std::set<int> page_ids;
+//   // Each(m) matches an empty container, regardless of what m is.
+//   EXPECT_THAT(page_ids, Each(Eq(1)));
+//   EXPECT_THAT(page_ids, Each(Eq(77)));
+//
+//   page_ids.insert(3);
+//   EXPECT_THAT(page_ids, Each(Gt(0)));
+//   EXPECT_THAT(page_ids, Not(Each(Gt(4))));
+//   page_ids.insert(1);
+//   EXPECT_THAT(page_ids, Not(Each(Lt(2))));
+//
+//   ::std::map<int, size_t> page_lengths;
+//   page_lengths[1] = 100;
+//   page_lengths[2] = 200;
+//   page_lengths[3] = 300;
+//   EXPECT_THAT(page_lengths, Not(Each(Pair(1, 100))));
+//   EXPECT_THAT(page_lengths, Each(Key(Le(3))));
+//
+//   const char* user_ids[] = { "joe", "mike", "tom" };
+//   EXPECT_THAT(user_ids, Not(Each(Eq(::std::string("tom")))));
+template <typename M>
+inline internal::EachMatcher<M> Each(M matcher) {
+  return internal::EachMatcher<M>(matcher);
+}
+
+// Key(inner_matcher) matches an std::pair whose 'first' field matches
+// inner_matcher.  For example, Contains(Key(Ge(5))) can be used to match an
+// std::map that contains at least one element whose key is >= 5.
+template <typename M>
+inline internal::KeyMatcher<M> Key(M inner_matcher) {
+  return internal::KeyMatcher<M>(inner_matcher);
+}
+
+// Pair(first_matcher, second_matcher) matches a std::pair whose 'first' field
+// matches first_matcher and whose 'second' field matches second_matcher.  For
+// example, EXPECT_THAT(map_type, ElementsAre(Pair(Ge(5), "foo"))) can be used
+// to match a std::map<int, string> that contains exactly one element whose key
+// is >= 5 and whose value equals "foo".
+template <typename FirstMatcher, typename SecondMatcher>
+inline internal::PairMatcher<FirstMatcher, SecondMatcher>
+Pair(FirstMatcher first_matcher, SecondMatcher second_matcher) {
+  return internal::PairMatcher<FirstMatcher, SecondMatcher>(
+      first_matcher, second_matcher);
+}
+
+namespace no_adl {
+// Conditional() creates a matcher that conditionally uses either the first or
+// second matcher provided. For example, we could create an `equal if, and only
+// if' matcher using the Conditonal wrapper as follows:
+//
+//   EXPECT_THAT(result, Conditional(condition, Eq(expected), Ne(expected)));
+template <typename MatcherTrue, typename MatcherFalse>
+internal::ConditionalMatcher<MatcherTrue, MatcherFalse> Conditional(
+    bool condition, MatcherTrue matcher_true, MatcherFalse matcher_false) {
+  return internal::ConditionalMatcher<MatcherTrue, MatcherFalse>(
+      condition, std::move(matcher_true), std::move(matcher_false));
+}
+
+// FieldsAre(matchers...) matches piecewise the fields of compatible structs.
+// These include those that support `get<I>(obj)`, and when structured bindings
+// are enabled any class that supports them.
+// In particular, `std::tuple`, `std::pair`, `std::array` and aggregate types.
+template <typename... M>
+internal::FieldsAreMatcher<typename std::decay<M>::type...> FieldsAre(
+    M&&... matchers) {
+  return internal::FieldsAreMatcher<typename std::decay<M>::type...>(
+      std::forward<M>(matchers)...);
+}
+
+// Creates a matcher that matches a pointer (raw or smart) that matches
+// inner_matcher.
+template <typename InnerMatcher>
+inline internal::PointerMatcher<InnerMatcher> Pointer(
+    const InnerMatcher& inner_matcher) {
+  return internal::PointerMatcher<InnerMatcher>(inner_matcher);
+}
+
+// Creates a matcher that matches an object that has an address that matches
+// inner_matcher.
+template <typename InnerMatcher>
+inline internal::AddressMatcher<InnerMatcher> Address(
+    const InnerMatcher& inner_matcher) {
+  return internal::AddressMatcher<InnerMatcher>(inner_matcher);
+}
+}  // namespace no_adl
+
+// Returns a predicate that is satisfied by anything that matches the
+// given matcher.
+template <typename M>
+inline internal::MatcherAsPredicate<M> Matches(M matcher) {
+  return internal::MatcherAsPredicate<M>(matcher);
+}
+
+// Returns true if and only if the value matches the matcher.
+template <typename T, typename M>
+inline bool Value(const T& value, M matcher) {
+  return testing::Matches(matcher)(value);
+}
+
+// Matches the value against the given matcher and explains the match
+// result to listener.
+template <typename T, typename M>
+inline bool ExplainMatchResult(
+    M matcher, const T& value, MatchResultListener* listener) {
+  return SafeMatcherCast<const T&>(matcher).MatchAndExplain(value, listener);
+}
+
+// Returns a string representation of the given matcher.  Useful for description
+// strings of matchers defined using MATCHER_P* macros that accept matchers as
+// their arguments.  For example:
+//
+// MATCHER_P(XAndYThat, matcher,
+//           "X that " + DescribeMatcher<int>(matcher, negation) +
+//               " and Y that " + DescribeMatcher<double>(matcher, negation)) {
+//   return ExplainMatchResult(matcher, arg.x(), result_listener) &&
+//          ExplainMatchResult(matcher, arg.y(), result_listener);
+// }
+template <typename T, typename M>
+std::string DescribeMatcher(const M& matcher, bool negation = false) {
+  ::std::stringstream ss;
+  Matcher<T> monomorphic_matcher = SafeMatcherCast<T>(matcher);
+  if (negation) {
+    monomorphic_matcher.DescribeNegationTo(&ss);
+  } else {
+    monomorphic_matcher.DescribeTo(&ss);
+  }
+  return ss.str();
+}
+
+template <typename... Args>
+internal::ElementsAreMatcher<
+    std::tuple<typename std::decay<const Args&>::type...>>
+ElementsAre(const Args&... matchers) {
+  return internal::ElementsAreMatcher<
+      std::tuple<typename std::decay<const Args&>::type...>>(
+      std::make_tuple(matchers...));
+}
+
+template <typename... Args>
+internal::UnorderedElementsAreMatcher<
+    std::tuple<typename std::decay<const Args&>::type...>>
+UnorderedElementsAre(const Args&... matchers) {
+  return internal::UnorderedElementsAreMatcher<
+      std::tuple<typename std::decay<const Args&>::type...>>(
+      std::make_tuple(matchers...));
+}
+
+// Define variadic matcher versions.
+template <typename... Args>
+internal::AllOfMatcher<typename std::decay<const Args&>::type...> AllOf(
+    const Args&... matchers) {
+  return internal::AllOfMatcher<typename std::decay<const Args&>::type...>(
+      matchers...);
+}
+
+template <typename... Args>
+internal::AnyOfMatcher<typename std::decay<const Args&>::type...> AnyOf(
+    const Args&... matchers) {
+  return internal::AnyOfMatcher<typename std::decay<const Args&>::type...>(
+      matchers...);
+}
+
+// AnyOfArray(array)
+// AnyOfArray(pointer, count)
+// AnyOfArray(container)
+// AnyOfArray({ e1, e2, ..., en })
+// AnyOfArray(iterator_first, iterator_last)
+//
+// AnyOfArray() verifies whether a given value matches any member of a
+// collection of matchers.
+//
+// AllOfArray(array)
+// AllOfArray(pointer, count)
+// AllOfArray(container)
+// AllOfArray({ e1, e2, ..., en })
+// AllOfArray(iterator_first, iterator_last)
+//
+// AllOfArray() verifies whether a given value matches all members of a
+// collection of matchers.
+//
+// The matchers can be specified as an array, a pointer and count, a container,
+// an initializer list, or an STL iterator range. In each of these cases, the
+// underlying matchers can be either values or matchers.
+
+template <typename Iter>
+inline internal::AnyOfArrayMatcher<
+    typename ::std::iterator_traits<Iter>::value_type>
+AnyOfArray(Iter first, Iter last) {
+  return internal::AnyOfArrayMatcher<
+      typename ::std::iterator_traits<Iter>::value_type>(first, last);
+}
+
+template <typename Iter>
+inline internal::AllOfArrayMatcher<
+    typename ::std::iterator_traits<Iter>::value_type>
+AllOfArray(Iter first, Iter last) {
+  return internal::AllOfArrayMatcher<
+      typename ::std::iterator_traits<Iter>::value_type>(first, last);
+}
+
+template <typename T>
+inline internal::AnyOfArrayMatcher<T> AnyOfArray(const T* ptr, size_t count) {
+  return AnyOfArray(ptr, ptr + count);
+}
+
+template <typename T>
+inline internal::AllOfArrayMatcher<T> AllOfArray(const T* ptr, size_t count) {
+  return AllOfArray(ptr, ptr + count);
+}
+
+template <typename T, size_t N>
+inline internal::AnyOfArrayMatcher<T> AnyOfArray(const T (&array)[N]) {
+  return AnyOfArray(array, N);
+}
+
+template <typename T, size_t N>
+inline internal::AllOfArrayMatcher<T> AllOfArray(const T (&array)[N]) {
+  return AllOfArray(array, N);
+}
+
+template <typename Container>
+inline internal::AnyOfArrayMatcher<typename Container::value_type> AnyOfArray(
+    const Container& container) {
+  return AnyOfArray(container.begin(), container.end());
+}
+
+template <typename Container>
+inline internal::AllOfArrayMatcher<typename Container::value_type> AllOfArray(
+    const Container& container) {
+  return AllOfArray(container.begin(), container.end());
+}
+
+template <typename T>
+inline internal::AnyOfArrayMatcher<T> AnyOfArray(
+    ::std::initializer_list<T> xs) {
+  return AnyOfArray(xs.begin(), xs.end());
+}
+
+template <typename T>
+inline internal::AllOfArrayMatcher<T> AllOfArray(
+    ::std::initializer_list<T> xs) {
+  return AllOfArray(xs.begin(), xs.end());
+}
+
+// Args<N1, N2, ..., Nk>(a_matcher) matches a tuple if the selected
+// fields of it matches a_matcher.  C++ doesn't support default
+// arguments for function templates, so we have to overload it.
+template <size_t... k, typename InnerMatcher>
+internal::ArgsMatcher<typename std::decay<InnerMatcher>::type, k...> Args(
+    InnerMatcher&& matcher) {
+  return internal::ArgsMatcher<typename std::decay<InnerMatcher>::type, k...>(
+      std::forward<InnerMatcher>(matcher));
+}
+
+// AllArgs(m) is a synonym of m.  This is useful in
+//
+//   EXPECT_CALL(foo, Bar(_, _)).With(AllArgs(Eq()));
+//
+// which is easier to read than
+//
+//   EXPECT_CALL(foo, Bar(_, _)).With(Eq());
+template <typename InnerMatcher>
+inline InnerMatcher AllArgs(const InnerMatcher& matcher) { return matcher; }
+
+// Returns a matcher that matches the value of an optional<> type variable.
+// The matcher implementation only uses '!arg' and requires that the optional<>
+// type has a 'value_type' member type and that '*arg' is of type 'value_type'
+// and is printable using 'PrintToString'. It is compatible with
+// std::optional/std::experimental::optional.
+// Note that to compare an optional type variable against nullopt you should
+// use Eq(nullopt) and not Eq(Optional(nullopt)). The latter implies that the
+// optional value contains an optional itself.
+template <typename ValueMatcher>
+inline internal::OptionalMatcher<ValueMatcher> Optional(
+    const ValueMatcher& value_matcher) {
+  return internal::OptionalMatcher<ValueMatcher>(value_matcher);
+}
+
+// Returns a matcher that matches the value of a absl::any type variable.
+template <typename T>
+PolymorphicMatcher<internal::any_cast_matcher::AnyCastMatcher<T> > AnyWith(
+    const Matcher<const T&>& matcher) {
+  return MakePolymorphicMatcher(
+      internal::any_cast_matcher::AnyCastMatcher<T>(matcher));
+}
+
+// Returns a matcher that matches the value of a variant<> type variable.
+// The matcher implementation uses ADL to find the holds_alternative and get
+// functions.
+// It is compatible with std::variant.
+template <typename T>
+PolymorphicMatcher<internal::variant_matcher::VariantMatcher<T> > VariantWith(
+    const Matcher<const T&>& matcher) {
+  return MakePolymorphicMatcher(
+      internal::variant_matcher::VariantMatcher<T>(matcher));
+}
+
+#if GTEST_HAS_EXCEPTIONS
+
+// Anything inside the `internal` namespace is internal to the implementation
+// and must not be used in user code!
+namespace internal {
+
+class WithWhatMatcherImpl {
+ public:
+  WithWhatMatcherImpl(Matcher<std::string> matcher)
+      : matcher_(std::move(matcher)) {}
+
+  void DescribeTo(std::ostream* os) const {
+    *os << "contains .what() that ";
+    matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(std::ostream* os) const {
+    *os << "contains .what() that does not ";
+    matcher_.DescribeTo(os);
+  }
+
+  template <typename Err>
+  bool MatchAndExplain(const Err& err, MatchResultListener* listener) const {
+    *listener << "which contains .what() that ";
+    return matcher_.MatchAndExplain(err.what(), listener);
+  }
+
+ private:
+  const Matcher<std::string> matcher_;
+};
+
+inline PolymorphicMatcher<WithWhatMatcherImpl> WithWhat(
+    Matcher<std::string> m) {
+  return MakePolymorphicMatcher(WithWhatMatcherImpl(std::move(m)));
+}
+
+template <typename Err>
+class ExceptionMatcherImpl {
+  class NeverThrown {
+   public:
+    const char* what() const noexcept {
+      return "this exception should never be thrown";
+    }
+  };
+
+  // If the matchee raises an exception of a wrong type, we'd like to
+  // catch it and print its message and type. To do that, we add an additional
+  // catch clause:
+  //
+  //     try { ... }
+  //     catch (const Err&) { /* an expected exception */ }
+  //     catch (const std::exception&) { /* exception of a wrong type */ }
+  //
+  // However, if the `Err` itself is `std::exception`, we'd end up with two
+  // identical `catch` clauses:
+  //
+  //     try { ... }
+  //     catch (const std::exception&) { /* an expected exception */ }
+  //     catch (const std::exception&) { /* exception of a wrong type */ }
+  //
+  // This can cause a warning or an error in some compilers. To resolve
+  // the issue, we use a fake error type whenever `Err` is `std::exception`:
+  //
+  //     try { ... }
+  //     catch (const std::exception&) { /* an expected exception */ }
+  //     catch (const NeverThrown&) { /* exception of a wrong type */ }
+  using DefaultExceptionType = typename std::conditional<
+      std::is_same<typename std::remove_cv<
+                       typename std::remove_reference<Err>::type>::type,
+                   std::exception>::value,
+      const NeverThrown&, const std::exception&>::type;
+
+ public:
+  ExceptionMatcherImpl(Matcher<const Err&> matcher)
+      : matcher_(std::move(matcher)) {}
+
+  void DescribeTo(std::ostream* os) const {
+    *os << "throws an exception which is a " << GetTypeName<Err>();
+    *os << " which ";
+    matcher_.DescribeTo(os);
+  }
+
+  void DescribeNegationTo(std::ostream* os) const {
+    *os << "throws an exception which is not a " << GetTypeName<Err>();
+    *os << " which ";
+    matcher_.DescribeNegationTo(os);
+  }
+
+  template <typename T>
+  bool MatchAndExplain(T&& x, MatchResultListener* listener) const {
+    try {
+      (void)(std::forward<T>(x)());
+    } catch (const Err& err) {
+      *listener << "throws an exception which is a " << GetTypeName<Err>();
+      *listener << " ";
+      return matcher_.MatchAndExplain(err, listener);
+    } catch (DefaultExceptionType err) {
+#if GTEST_HAS_RTTI
+      *listener << "throws an exception of type " << GetTypeName(typeid(err));
+      *listener << " ";
+#else
+      *listener << "throws an std::exception-derived type ";
+#endif
+      *listener << "with description \"" << err.what() << "\"";
+      return false;
+    } catch (...) {
+      *listener << "throws an exception of an unknown type";
+      return false;
+    }
+
+    *listener << "does not throw any exception";
+    return false;
+  }
+
+ private:
+  const Matcher<const Err&> matcher_;
+};
+
+}  // namespace internal
+
+// Throws()
+// Throws(exceptionMatcher)
+// ThrowsMessage(messageMatcher)
+//
+// This matcher accepts a callable and verifies that when invoked, it throws
+// an exception with the given type and properties.
+//
+// Examples:
+//
+//   EXPECT_THAT(
+//       []() { throw std::runtime_error("message"); },
+//       Throws<std::runtime_error>());
+//
+//   EXPECT_THAT(
+//       []() { throw std::runtime_error("message"); },
+//       ThrowsMessage<std::runtime_error>(HasSubstr("message")));
+//
+//   EXPECT_THAT(
+//       []() { throw std::runtime_error("message"); },
+//       Throws<std::runtime_error>(
+//           Property(&std::runtime_error::what, HasSubstr("message"))));
+
+template <typename Err>
+PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> Throws() {
+  return MakePolymorphicMatcher(
+      internal::ExceptionMatcherImpl<Err>(A<const Err&>()));
+}
+
+template <typename Err, typename ExceptionMatcher>
+PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> Throws(
+    const ExceptionMatcher& exception_matcher) {
+  // Using matcher cast allows users to pass a matcher of a more broad type.
+  // For example user may want to pass Matcher<std::exception>
+  // to Throws<std::runtime_error>, or Matcher<int64> to Throws<int32>.
+  return MakePolymorphicMatcher(internal::ExceptionMatcherImpl<Err>(
+      SafeMatcherCast<const Err&>(exception_matcher)));
+}
+
+template <typename Err, typename MessageMatcher>
+PolymorphicMatcher<internal::ExceptionMatcherImpl<Err>> ThrowsMessage(
+    MessageMatcher&& message_matcher) {
+  static_assert(std::is_base_of<std::exception, Err>::value,
+                "expected an std::exception-derived type");
+  return Throws<Err>(internal::WithWhat(
+      MatcherCast<std::string>(std::forward<MessageMatcher>(message_matcher))));
+}
+
+#endif  // GTEST_HAS_EXCEPTIONS
+
+// These macros allow using matchers to check values in Google Test
+// tests.  ASSERT_THAT(value, matcher) and EXPECT_THAT(value, matcher)
+// succeed if and only if the value matches the matcher.  If the assertion
+// fails, the value and the description of the matcher will be printed.
+#define ASSERT_THAT(value, matcher) ASSERT_PRED_FORMAT1(\
+    ::testing::internal::MakePredicateFormatterFromMatcher(matcher), value)
+#define EXPECT_THAT(value, matcher) EXPECT_PRED_FORMAT1(\
+    ::testing::internal::MakePredicateFormatterFromMatcher(matcher), value)
+
+// MATCHER* macroses itself are listed below.
+#define MATCHER(name, description)                                             \
+  class name##Matcher                                                          \
+      : public ::testing::internal::MatcherBaseImpl<name##Matcher> {           \
+   public:                                                                     \
+    template <typename arg_type>                                               \
+    class gmock_Impl : public ::testing::MatcherInterface<const arg_type&> {   \
+     public:                                                                   \
+      gmock_Impl() {}                                                          \
+      bool MatchAndExplain(                                                    \
+          const arg_type& arg,                                                 \
+          ::testing::MatchResultListener* result_listener) const override;     \
+      void DescribeTo(::std::ostream* gmock_os) const override {               \
+        *gmock_os << FormatDescription(false);                                 \
+      }                                                                        \
+      void DescribeNegationTo(::std::ostream* gmock_os) const override {       \
+        *gmock_os << FormatDescription(true);                                  \
+      }                                                                        \
+                                                                               \
+     private:                                                                  \
+      ::std::string FormatDescription(bool negation) const {                   \
+        /* NOLINTNEXTLINE readability-redundant-string-init */                 \
+        ::std::string gmock_description = (description);                       \
+        if (!gmock_description.empty()) {                                      \
+          return gmock_description;                                            \
+        }                                                                      \
+        return ::testing::internal::FormatMatcherDescription(negation, #name,  \
+                                                             {});              \
+      }                                                                        \
+    };                                                                         \
+  };                                                                           \
+  GTEST_ATTRIBUTE_UNUSED_ inline name##Matcher name() { return {}; }           \
+  template <typename arg_type>                                                 \
+  bool name##Matcher::gmock_Impl<arg_type>::MatchAndExplain(                   \
+      const arg_type& arg,                                                     \
+      ::testing::MatchResultListener* result_listener GTEST_ATTRIBUTE_UNUSED_) \
+      const
+
+#define MATCHER_P(name, p0, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP, description, (p0))
+#define MATCHER_P2(name, p0, p1, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP2, description, (p0, p1))
+#define MATCHER_P3(name, p0, p1, p2, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP3, description, (p0, p1, p2))
+#define MATCHER_P4(name, p0, p1, p2, p3, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP4, description, (p0, p1, p2, p3))
+#define MATCHER_P5(name, p0, p1, p2, p3, p4, description)    \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP5, description, \
+                         (p0, p1, p2, p3, p4))
+#define MATCHER_P6(name, p0, p1, p2, p3, p4, p5, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP6, description,  \
+                         (p0, p1, p2, p3, p4, p5))
+#define MATCHER_P7(name, p0, p1, p2, p3, p4, p5, p6, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP7, description,      \
+                         (p0, p1, p2, p3, p4, p5, p6))
+#define MATCHER_P8(name, p0, p1, p2, p3, p4, p5, p6, p7, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP8, description,          \
+                         (p0, p1, p2, p3, p4, p5, p6, p7))
+#define MATCHER_P9(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP9, description,              \
+                         (p0, p1, p2, p3, p4, p5, p6, p7, p8))
+#define MATCHER_P10(name, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, description) \
+  GMOCK_INTERNAL_MATCHER(name, name##MatcherP10, description,                  \
+                         (p0, p1, p2, p3, p4, p5, p6, p7, p8, p9))
+
+#define GMOCK_INTERNAL_MATCHER(name, full_name, description, args)             \
+  template <GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args)>                      \
+  class full_name : public ::testing::internal::MatcherBaseImpl<               \
+                        full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>> { \
+   public:                                                                     \
+    using full_name::MatcherBaseImpl::MatcherBaseImpl;                         \
+    template <typename arg_type>                                               \
+    class gmock_Impl : public ::testing::MatcherInterface<const arg_type&> {   \
+     public:                                                                   \
+      explicit gmock_Impl(GMOCK_INTERNAL_MATCHER_FUNCTION_ARGS(args))          \
+          : GMOCK_INTERNAL_MATCHER_FORWARD_ARGS(args) {}                       \
+      bool MatchAndExplain(                                                    \
+          const arg_type& arg,                                                 \
+          ::testing::MatchResultListener* result_listener) const override;     \
+      void DescribeTo(::std::ostream* gmock_os) const override {               \
+        *gmock_os << FormatDescription(false);                                 \
+      }                                                                        \
+      void DescribeNegationTo(::std::ostream* gmock_os) const override {       \
+        *gmock_os << FormatDescription(true);                                  \
+      }                                                                        \
+      GMOCK_INTERNAL_MATCHER_MEMBERS(args)                                     \
+                                                                               \
+     private:                                                                  \
+      ::std::string FormatDescription(bool negation) const {                   \
+        ::std::string gmock_description = (description);                       \
+        if (!gmock_description.empty()) {                                      \
+          return gmock_description;                                            \
+        }                                                                      \
+        return ::testing::internal::FormatMatcherDescription(                  \
+            negation, #name,                                                   \
+            ::testing::internal::UniversalTersePrintTupleFieldsToStrings(      \
+                ::std::tuple<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>(        \
+                    GMOCK_INTERNAL_MATCHER_MEMBERS_USAGE(args))));             \
+      }                                                                        \
+    };                                                                         \
+  };                                                                           \
+  template <GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args)>                      \
+  inline full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)> name(             \
+      GMOCK_INTERNAL_MATCHER_FUNCTION_ARGS(args)) {                            \
+    return full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>(                \
+        GMOCK_INTERNAL_MATCHER_ARGS_USAGE(args));                              \
+  }                                                                            \
+  template <GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args)>                      \
+  template <typename arg_type>                                                 \
+  bool full_name<GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args)>::gmock_Impl<        \
+      arg_type>::MatchAndExplain(const arg_type& arg,                          \
+                                 ::testing::MatchResultListener*               \
+                                     result_listener GTEST_ATTRIBUTE_UNUSED_)  \
+      const
+
+#define GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAMS(args) \
+  GMOCK_PP_TAIL(                                     \
+      GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAM, , args))
+#define GMOCK_INTERNAL_MATCHER_TEMPLATE_PARAM(i_unused, data_unused, arg) \
+  , typename arg##_type
+
+#define GMOCK_INTERNAL_MATCHER_TYPE_PARAMS(args) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_TYPE_PARAM, , args))
+#define GMOCK_INTERNAL_MATCHER_TYPE_PARAM(i_unused, data_unused, arg) \
+  , arg##_type
+
+#define GMOCK_INTERNAL_MATCHER_FUNCTION_ARGS(args) \
+  GMOCK_PP_TAIL(dummy_first GMOCK_PP_FOR_EACH(     \
+      GMOCK_INTERNAL_MATCHER_FUNCTION_ARG, , args))
+#define GMOCK_INTERNAL_MATCHER_FUNCTION_ARG(i, data_unused, arg) \
+  , arg##_type gmock_p##i
+
+#define GMOCK_INTERNAL_MATCHER_FORWARD_ARGS(args) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_FORWARD_ARG, , args))
+#define GMOCK_INTERNAL_MATCHER_FORWARD_ARG(i, data_unused, arg) \
+  , arg(::std::forward<arg##_type>(gmock_p##i))
+
+#define GMOCK_INTERNAL_MATCHER_MEMBERS(args) \
+  GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_MEMBER, , args)
+#define GMOCK_INTERNAL_MATCHER_MEMBER(i_unused, data_unused, arg) \
+  const arg##_type arg;
+
+#define GMOCK_INTERNAL_MATCHER_MEMBERS_USAGE(args) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_MEMBER_USAGE, , args))
+#define GMOCK_INTERNAL_MATCHER_MEMBER_USAGE(i_unused, data_unused, arg) , arg
+
+#define GMOCK_INTERNAL_MATCHER_ARGS_USAGE(args) \
+  GMOCK_PP_TAIL(GMOCK_PP_FOR_EACH(GMOCK_INTERNAL_MATCHER_ARG_USAGE, , args))
+#define GMOCK_INTERNAL_MATCHER_ARG_USAGE(i, data_unused, arg_unused) \
+  , gmock_p##i
+
+// To prevent ADL on certain functions we put them on a separate namespace.
+using namespace no_adl;  // NOLINT
+
+}  // namespace testing
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251 5046
+
+// Include any custom callback matchers added by the local installation.
+// We must include this header at the end to make sure it can use the
+// declarations from this file.
+#include "gmock/internal/custom/gmock-matchers.h"
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MATCHERS_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-more-actions.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-more-actions.h
new file mode 100644 (file)
index 0000000..ffbe43c
--- /dev/null
@@ -0,0 +1,571 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some commonly used variadic actions.
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
+
+#include <memory>
+#include <utility>
+
+#include "gmock/gmock-actions.h"
+#include "gmock/internal/gmock-port.h"
+
+// Include any custom callback actions added by the local installation.
+#include "gmock/internal/custom/gmock-generated-actions.h"
+
+// Sometimes you want to give an action explicit template parameters
+// that cannot be inferred from its value parameters.  ACTION() and
+// ACTION_P*() don't support that.  ACTION_TEMPLATE() remedies that
+// and can be viewed as an extension to ACTION() and ACTION_P*().
+//
+// The syntax:
+//
+//   ACTION_TEMPLATE(ActionName,
+//                   HAS_m_TEMPLATE_PARAMS(kind1, name1, ..., kind_m, name_m),
+//                   AND_n_VALUE_PARAMS(p1, ..., p_n)) { statements; }
+//
+// defines an action template that takes m explicit template
+// parameters and n value parameters.  name_i is the name of the i-th
+// template parameter, and kind_i specifies whether it's a typename,
+// an integral constant, or a template.  p_i is the name of the i-th
+// value parameter.
+//
+// Example:
+//
+//   // DuplicateArg<k, T>(output) converts the k-th argument of the mock
+//   // function to type T and copies it to *output.
+//   ACTION_TEMPLATE(DuplicateArg,
+//                   HAS_2_TEMPLATE_PARAMS(int, k, typename, T),
+//                   AND_1_VALUE_PARAMS(output)) {
+//     *output = T(::std::get<k>(args));
+//   }
+//   ...
+//     int n;
+//     EXPECT_CALL(mock, Foo(_, _))
+//         .WillOnce(DuplicateArg<1, unsigned char>(&n));
+//
+// To create an instance of an action template, write:
+//
+//   ActionName<t1, ..., t_m>(v1, ..., v_n)
+//
+// where the ts are the template arguments and the vs are the value
+// arguments.  The value argument types are inferred by the compiler.
+// If you want to explicitly specify the value argument types, you can
+// provide additional template arguments:
+//
+//   ActionName<t1, ..., t_m, u1, ..., u_k>(v1, ..., v_n)
+//
+// where u_i is the desired type of v_i.
+//
+// ACTION_TEMPLATE and ACTION/ACTION_P* can be overloaded on the
+// number of value parameters, but not on the number of template
+// parameters.  Without the restriction, the meaning of the following
+// is unclear:
+//
+//   OverloadedAction<int, bool>(x);
+//
+// Are we using a single-template-parameter action where 'bool' refers
+// to the type of x, or are we using a two-template-parameter action
+// where the compiler is asked to infer the type of x?
+//
+// Implementation notes:
+//
+// GMOCK_INTERNAL_*_HAS_m_TEMPLATE_PARAMS and
+// GMOCK_INTERNAL_*_AND_n_VALUE_PARAMS are internal macros for
+// implementing ACTION_TEMPLATE.  The main trick we use is to create
+// new macro invocations when expanding a macro.  For example, we have
+//
+//   #define ACTION_TEMPLATE(name, template_params, value_params)
+//       ... GMOCK_INTERNAL_DECL_##template_params ...
+//
+// which causes ACTION_TEMPLATE(..., HAS_1_TEMPLATE_PARAMS(typename, T), ...)
+// to expand to
+//
+//       ... GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(typename, T) ...
+//
+// Since GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS is a macro, the
+// preprocessor will continue to expand it to
+//
+//       ... typename T ...
+//
+// This technique conforms to the C++ standard and is portable.  It
+// allows us to implement action templates using O(N) code, where N is
+// the maximum number of template/value parameters supported.  Without
+// using it, we'd have to devote O(N^2) amount of code to implement all
+// combinations of m and n.
+
+// Declares the template parameters.
+#define GMOCK_INTERNAL_DECL_HAS_1_TEMPLATE_PARAMS(kind0, name0) kind0 name0
+#define GMOCK_INTERNAL_DECL_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \
+    name1) kind0 name0, kind1 name1
+#define GMOCK_INTERNAL_DECL_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2) kind0 name0, kind1 name1, kind2 name2
+#define GMOCK_INTERNAL_DECL_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3) kind0 name0, kind1 name1, kind2 name2, \
+    kind3 name3
+#define GMOCK_INTERNAL_DECL_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4) kind0 name0, kind1 name1, \
+    kind2 name2, kind3 name3, kind4 name4
+#define GMOCK_INTERNAL_DECL_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5) kind0 name0, \
+    kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5
+#define GMOCK_INTERNAL_DECL_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+    name6) kind0 name0, kind1 name1, kind2 name2, kind3 name3, kind4 name4, \
+    kind5 name5, kind6 name6
+#define GMOCK_INTERNAL_DECL_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+    kind7, name7) kind0 name0, kind1 name1, kind2 name2, kind3 name3, \
+    kind4 name4, kind5 name5, kind6 name6, kind7 name7
+#define GMOCK_INTERNAL_DECL_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+    kind7, name7, kind8, name8) kind0 name0, kind1 name1, kind2 name2, \
+    kind3 name3, kind4 name4, kind5 name5, kind6 name6, kind7 name7, \
+    kind8 name8
+#define GMOCK_INTERNAL_DECL_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \
+    name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+    name6, kind7, name7, kind8, name8, kind9, name9) kind0 name0, \
+    kind1 name1, kind2 name2, kind3 name3, kind4 name4, kind5 name5, \
+    kind6 name6, kind7 name7, kind8 name8, kind9 name9
+
+// Lists the template parameters.
+#define GMOCK_INTERNAL_LIST_HAS_1_TEMPLATE_PARAMS(kind0, name0) name0
+#define GMOCK_INTERNAL_LIST_HAS_2_TEMPLATE_PARAMS(kind0, name0, kind1, \
+    name1) name0, name1
+#define GMOCK_INTERNAL_LIST_HAS_3_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2) name0, name1, name2
+#define GMOCK_INTERNAL_LIST_HAS_4_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3) name0, name1, name2, name3
+#define GMOCK_INTERNAL_LIST_HAS_5_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4) name0, name1, name2, name3, \
+    name4
+#define GMOCK_INTERNAL_LIST_HAS_6_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5) name0, name1, \
+    name2, name3, name4, name5
+#define GMOCK_INTERNAL_LIST_HAS_7_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+    name6) name0, name1, name2, name3, name4, name5, name6
+#define GMOCK_INTERNAL_LIST_HAS_8_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+    kind7, name7) name0, name1, name2, name3, name4, name5, name6, name7
+#define GMOCK_INTERNAL_LIST_HAS_9_TEMPLATE_PARAMS(kind0, name0, kind1, name1, \
+    kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, name6, \
+    kind7, name7, kind8, name8) name0, name1, name2, name3, name4, name5, \
+    name6, name7, name8
+#define GMOCK_INTERNAL_LIST_HAS_10_TEMPLATE_PARAMS(kind0, name0, kind1, \
+    name1, kind2, name2, kind3, name3, kind4, name4, kind5, name5, kind6, \
+    name6, kind7, name7, kind8, name8, kind9, name9) name0, name1, name2, \
+    name3, name4, name5, name6, name7, name8, name9
+
+// Declares the types of value parameters.
+#define GMOCK_INTERNAL_DECL_TYPE_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_DECL_TYPE_AND_1_VALUE_PARAMS(p0) , typename p0##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_2_VALUE_PARAMS(p0, p1) , \
+    typename p0##_type, typename p1##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , \
+    typename p0##_type, typename p1##_type, typename p2##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \
+    typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \
+    typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \
+    typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type, typename p5##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) , typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type, typename p5##_type, \
+    typename p6##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7) , typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type, typename p5##_type, \
+    typename p6##_type, typename p7##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7, p8) , typename p0##_type, typename p1##_type, typename p2##_type, \
+    typename p3##_type, typename p4##_type, typename p5##_type, \
+    typename p6##_type, typename p7##_type, typename p8##_type
+#define GMOCK_INTERNAL_DECL_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7, p8, p9) , typename p0##_type, typename p1##_type, \
+    typename p2##_type, typename p3##_type, typename p4##_type, \
+    typename p5##_type, typename p6##_type, typename p7##_type, \
+    typename p8##_type, typename p9##_type
+
+// Initializes the value parameters.
+#define GMOCK_INTERNAL_INIT_AND_0_VALUE_PARAMS()\
+    ()
+#define GMOCK_INTERNAL_INIT_AND_1_VALUE_PARAMS(p0)\
+    (p0##_type gmock_p0) : p0(::std::move(gmock_p0))
+#define GMOCK_INTERNAL_INIT_AND_2_VALUE_PARAMS(p0, p1)\
+    (p0##_type gmock_p0, p1##_type gmock_p1) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1))
+#define GMOCK_INTERNAL_INIT_AND_3_VALUE_PARAMS(p0, p1, p2)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, \
+        p2##_type gmock_p2) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2))
+#define GMOCK_INTERNAL_INIT_AND_4_VALUE_PARAMS(p0, p1, p2, p3)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3))
+#define GMOCK_INTERNAL_INIT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4))
+#define GMOCK_INTERNAL_INIT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, \
+        p5##_type gmock_p5) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+        p5(::std::move(gmock_p5))
+#define GMOCK_INTERNAL_INIT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+        p6##_type gmock_p6) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6))
+#define GMOCK_INTERNAL_INIT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, p7)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+        p6##_type gmock_p6, p7##_type gmock_p7) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
+        p7(::std::move(gmock_p7))
+#define GMOCK_INTERNAL_INIT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+        p6##_type gmock_p6, p7##_type gmock_p7, \
+        p8##_type gmock_p8) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
+        p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8))
+#define GMOCK_INTERNAL_INIT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9)\
+    (p0##_type gmock_p0, p1##_type gmock_p1, p2##_type gmock_p2, \
+        p3##_type gmock_p3, p4##_type gmock_p4, p5##_type gmock_p5, \
+        p6##_type gmock_p6, p7##_type gmock_p7, p8##_type gmock_p8, \
+        p9##_type gmock_p9) : p0(::std::move(gmock_p0)), \
+        p1(::std::move(gmock_p1)), p2(::std::move(gmock_p2)), \
+        p3(::std::move(gmock_p3)), p4(::std::move(gmock_p4)), \
+        p5(::std::move(gmock_p5)), p6(::std::move(gmock_p6)), \
+        p7(::std::move(gmock_p7)), p8(::std::move(gmock_p8)), \
+        p9(::std::move(gmock_p9))
+
+// Defines the copy constructor
+#define GMOCK_INTERNAL_DEFN_COPY_AND_0_VALUE_PARAMS() \
+    {}  // Avoid https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82134
+#define GMOCK_INTERNAL_DEFN_COPY_AND_1_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_2_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_3_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_4_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_5_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_6_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_7_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_8_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_9_VALUE_PARAMS(...) = default;
+#define GMOCK_INTERNAL_DEFN_COPY_AND_10_VALUE_PARAMS(...) = default;
+
+// Declares the fields for storing the value parameters.
+#define GMOCK_INTERNAL_DEFN_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_DEFN_AND_1_VALUE_PARAMS(p0) p0##_type p0;
+#define GMOCK_INTERNAL_DEFN_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0; \
+    p1##_type p1;
+#define GMOCK_INTERNAL_DEFN_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0; \
+    p1##_type p1; p2##_type p2;
+#define GMOCK_INTERNAL_DEFN_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0; \
+    p1##_type p1; p2##_type p2; p3##_type p3;
+#define GMOCK_INTERNAL_DEFN_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \
+    p4) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4;
+#define GMOCK_INTERNAL_DEFN_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \
+    p5) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
+    p5##_type p5;
+#define GMOCK_INTERNAL_DEFN_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
+    p5##_type p5; p6##_type p6;
+#define GMOCK_INTERNAL_DEFN_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; p4##_type p4; \
+    p5##_type p5; p6##_type p6; p7##_type p7;
+#define GMOCK_INTERNAL_DEFN_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \
+    p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8;
+#define GMOCK_INTERNAL_DEFN_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9) p0##_type p0; p1##_type p1; p2##_type p2; p3##_type p3; \
+    p4##_type p4; p5##_type p5; p6##_type p6; p7##_type p7; p8##_type p8; \
+    p9##_type p9;
+
+// Lists the value parameters.
+#define GMOCK_INTERNAL_LIST_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_LIST_AND_1_VALUE_PARAMS(p0) p0
+#define GMOCK_INTERNAL_LIST_AND_2_VALUE_PARAMS(p0, p1) p0, p1
+#define GMOCK_INTERNAL_LIST_AND_3_VALUE_PARAMS(p0, p1, p2) p0, p1, p2
+#define GMOCK_INTERNAL_LIST_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0, p1, p2, p3
+#define GMOCK_INTERNAL_LIST_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) p0, p1, \
+    p2, p3, p4
+#define GMOCK_INTERNAL_LIST_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) p0, \
+    p1, p2, p3, p4, p5
+#define GMOCK_INTERNAL_LIST_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) p0, p1, p2, p3, p4, p5, p6
+#define GMOCK_INTERNAL_LIST_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7) p0, p1, p2, p3, p4, p5, p6, p7
+#define GMOCK_INTERNAL_LIST_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8) p0, p1, p2, p3, p4, p5, p6, p7, p8
+#define GMOCK_INTERNAL_LIST_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9) p0, p1, p2, p3, p4, p5, p6, p7, p8, p9
+
+// Lists the value parameter types.
+#define GMOCK_INTERNAL_LIST_TYPE_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_LIST_TYPE_AND_1_VALUE_PARAMS(p0) , p0##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_2_VALUE_PARAMS(p0, p1) , p0##_type, \
+    p1##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_3_VALUE_PARAMS(p0, p1, p2) , p0##_type, \
+    p1##_type, p2##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_4_VALUE_PARAMS(p0, p1, p2, p3) , \
+    p0##_type, p1##_type, p2##_type, p3##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) , \
+    p0##_type, p1##_type, p2##_type, p3##_type, p4##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) , \
+    p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, p5##_type, \
+    p6##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+    p5##_type, p6##_type, p7##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7, p8) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+    p5##_type, p6##_type, p7##_type, p8##_type
+#define GMOCK_INTERNAL_LIST_TYPE_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6, p7, p8, p9) , p0##_type, p1##_type, p2##_type, p3##_type, p4##_type, \
+    p5##_type, p6##_type, p7##_type, p8##_type, p9##_type
+
+// Declares the value parameters.
+#define GMOCK_INTERNAL_DECL_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_DECL_AND_1_VALUE_PARAMS(p0) p0##_type p0
+#define GMOCK_INTERNAL_DECL_AND_2_VALUE_PARAMS(p0, p1) p0##_type p0, \
+    p1##_type p1
+#define GMOCK_INTERNAL_DECL_AND_3_VALUE_PARAMS(p0, p1, p2) p0##_type p0, \
+    p1##_type p1, p2##_type p2
+#define GMOCK_INTERNAL_DECL_AND_4_VALUE_PARAMS(p0, p1, p2, p3) p0##_type p0, \
+    p1##_type p1, p2##_type p2, p3##_type p3
+#define GMOCK_INTERNAL_DECL_AND_5_VALUE_PARAMS(p0, p1, p2, p3, \
+    p4) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4
+#define GMOCK_INTERNAL_DECL_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, \
+    p5) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
+    p5##_type p5
+#define GMOCK_INTERNAL_DECL_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, \
+    p6) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
+    p5##_type p5, p6##_type p6
+#define GMOCK_INTERNAL_DECL_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, p4##_type p4, \
+    p5##_type p5, p6##_type p6, p7##_type p7
+#define GMOCK_INTERNAL_DECL_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+    p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8
+#define GMOCK_INTERNAL_DECL_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9) p0##_type p0, p1##_type p1, p2##_type p2, p3##_type p3, \
+    p4##_type p4, p5##_type p5, p6##_type p6, p7##_type p7, p8##_type p8, \
+    p9##_type p9
+
+// The suffix of the class template implementing the action template.
+#define GMOCK_INTERNAL_COUNT_AND_0_VALUE_PARAMS()
+#define GMOCK_INTERNAL_COUNT_AND_1_VALUE_PARAMS(p0) P
+#define GMOCK_INTERNAL_COUNT_AND_2_VALUE_PARAMS(p0, p1) P2
+#define GMOCK_INTERNAL_COUNT_AND_3_VALUE_PARAMS(p0, p1, p2) P3
+#define GMOCK_INTERNAL_COUNT_AND_4_VALUE_PARAMS(p0, p1, p2, p3) P4
+#define GMOCK_INTERNAL_COUNT_AND_5_VALUE_PARAMS(p0, p1, p2, p3, p4) P5
+#define GMOCK_INTERNAL_COUNT_AND_6_VALUE_PARAMS(p0, p1, p2, p3, p4, p5) P6
+#define GMOCK_INTERNAL_COUNT_AND_7_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6) P7
+#define GMOCK_INTERNAL_COUNT_AND_8_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7) P8
+#define GMOCK_INTERNAL_COUNT_AND_9_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8) P9
+#define GMOCK_INTERNAL_COUNT_AND_10_VALUE_PARAMS(p0, p1, p2, p3, p4, p5, p6, \
+    p7, p8, p9) P10
+
+// The name of the class template implementing the action template.
+#define GMOCK_ACTION_CLASS_(name, value_params)\
+    GTEST_CONCAT_TOKEN_(name##Action, GMOCK_INTERNAL_COUNT_##value_params)
+
+#define ACTION_TEMPLATE(name, template_params, value_params)                   \
+  template <GMOCK_INTERNAL_DECL_##template_params                              \
+            GMOCK_INTERNAL_DECL_TYPE_##value_params>                           \
+  class GMOCK_ACTION_CLASS_(name, value_params) {                              \
+   public:                                                                     \
+    explicit GMOCK_ACTION_CLASS_(name, value_params)(                          \
+        GMOCK_INTERNAL_DECL_##value_params)                                    \
+        GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params),    \
+                    = default; ,                                               \
+                    : impl_(std::make_shared<gmock_Impl>(                      \
+                                GMOCK_INTERNAL_LIST_##value_params)) { })      \
+    GMOCK_ACTION_CLASS_(name, value_params)(                                   \
+        const GMOCK_ACTION_CLASS_(name, value_params)&) noexcept               \
+        GMOCK_INTERNAL_DEFN_COPY_##value_params                                \
+    GMOCK_ACTION_CLASS_(name, value_params)(                                   \
+        GMOCK_ACTION_CLASS_(name, value_params)&&) noexcept                    \
+        GMOCK_INTERNAL_DEFN_COPY_##value_params                                \
+    template <typename F>                                                      \
+    operator ::testing::Action<F>() const {                                    \
+      return GMOCK_PP_IF(                                                      \
+          GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params),              \
+                      (::testing::internal::MakeAction<F, gmock_Impl>()),      \
+                      (::testing::internal::MakeAction<F>(impl_)));            \
+    }                                                                          \
+   private:                                                                    \
+    class gmock_Impl {                                                         \
+     public:                                                                   \
+      explicit gmock_Impl GMOCK_INTERNAL_INIT_##value_params {}                \
+      template <typename function_type, typename return_type,                  \
+                typename args_type, GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>         \
+      return_type gmock_PerformImpl(GMOCK_ACTION_ARG_TYPES_AND_NAMES_) const;  \
+      GMOCK_INTERNAL_DEFN_##value_params                                       \
+    };                                                                         \
+    GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(GMOCK_INTERNAL_COUNT_##value_params),        \
+                , std::shared_ptr<const gmock_Impl> impl_;)                    \
+  };                                                                           \
+  template <GMOCK_INTERNAL_DECL_##template_params                              \
+            GMOCK_INTERNAL_DECL_TYPE_##value_params>                           \
+  GMOCK_ACTION_CLASS_(name, value_params)<                                     \
+      GMOCK_INTERNAL_LIST_##template_params                                    \
+      GMOCK_INTERNAL_LIST_TYPE_##value_params> name(                           \
+          GMOCK_INTERNAL_DECL_##value_params) GTEST_MUST_USE_RESULT_;          \
+  template <GMOCK_INTERNAL_DECL_##template_params                              \
+            GMOCK_INTERNAL_DECL_TYPE_##value_params>                           \
+  inline GMOCK_ACTION_CLASS_(name, value_params)<                              \
+      GMOCK_INTERNAL_LIST_##template_params                                    \
+      GMOCK_INTERNAL_LIST_TYPE_##value_params> name(                           \
+          GMOCK_INTERNAL_DECL_##value_params) {                                \
+    return GMOCK_ACTION_CLASS_(name, value_params)<                            \
+        GMOCK_INTERNAL_LIST_##template_params                                  \
+        GMOCK_INTERNAL_LIST_TYPE_##value_params>(                              \
+            GMOCK_INTERNAL_LIST_##value_params);                               \
+  }                                                                            \
+  template <GMOCK_INTERNAL_DECL_##template_params                              \
+            GMOCK_INTERNAL_DECL_TYPE_##value_params>                           \
+  template <typename function_type, typename return_type, typename args_type,  \
+            GMOCK_ACTION_TEMPLATE_ARGS_NAMES_>                                 \
+  return_type GMOCK_ACTION_CLASS_(name, value_params)<                         \
+      GMOCK_INTERNAL_LIST_##template_params                                    \
+      GMOCK_INTERNAL_LIST_TYPE_##value_params>::gmock_Impl::gmock_PerformImpl( \
+          GMOCK_ACTION_ARG_TYPES_AND_NAMES_UNUSED_) const
+
+namespace testing {
+
+// The ACTION*() macros trigger warning C4100 (unreferenced formal
+// parameter) in MSVC with -W4.  Unfortunately they cannot be fixed in
+// the macro definition, as the warnings are generated when the macro
+// is expanded and macro expansion cannot contain #pragma.  Therefore
+// we suppress them here.
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4100)
+#endif
+
+namespace internal {
+
+// internal::InvokeArgument - a helper for InvokeArgument action.
+// The basic overloads are provided here for generic functors.
+// Overloads for other custom-callables are provided in the
+// internal/custom/gmock-generated-actions.h header.
+template <typename F, typename... Args>
+auto InvokeArgument(F f, Args... args) -> decltype(f(args...)) {
+  return f(args...);
+}
+
+template <std::size_t index, typename... Params>
+struct InvokeArgumentAction {
+  template <typename... Args>
+  auto operator()(Args&&... args) const -> decltype(internal::InvokeArgument(
+      std::get<index>(std::forward_as_tuple(std::forward<Args>(args)...)),
+      std::declval<const Params&>()...)) {
+    internal::FlatTuple<Args&&...> args_tuple(FlatTupleConstructTag{},
+                                              std::forward<Args>(args)...);
+    return params.Apply([&](const Params&... unpacked_params) {
+      auto&& callable = args_tuple.template Get<index>();
+      return internal::InvokeArgument(
+          std::forward<decltype(callable)>(callable), unpacked_params...);
+    });
+  }
+
+  internal::FlatTuple<Params...> params;
+};
+
+}  // namespace internal
+
+// The InvokeArgument<N>(a1, a2, ..., a_k) action invokes the N-th
+// (0-based) argument, which must be a k-ary callable, of the mock
+// function, with arguments a1, a2, ..., a_k.
+//
+// Notes:
+//
+//   1. The arguments are passed by value by default.  If you need to
+//   pass an argument by reference, wrap it inside std::ref().  For
+//   example,
+//
+//     InvokeArgument<1>(5, string("Hello"), std::ref(foo))
+//
+//   passes 5 and string("Hello") by value, and passes foo by
+//   reference.
+//
+//   2. If the callable takes an argument by reference but std::ref() is
+//   not used, it will receive the reference to a copy of the value,
+//   instead of the original value.  For example, when the 0-th
+//   argument of the mock function takes a const string&, the action
+//
+//     InvokeArgument<0>(string("Hello"))
+//
+//   makes a copy of the temporary string("Hello") object and passes a
+//   reference of the copy, instead of the original temporary object,
+//   to the callable.  This makes it easy for a user to define an
+//   InvokeArgument action from temporary values and have it performed
+//   later.
+template <std::size_t index, typename... Params>
+internal::InvokeArgumentAction<index, typename std::decay<Params>::type...>
+InvokeArgument(Params&&... params) {
+  return {internal::FlatTuple<typename std::decay<Params>::type...>(
+      internal::FlatTupleConstructTag{}, std::forward<Params>(params)...)};
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+}  // namespace testing
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_ACTIONS_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-more-matchers.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-more-matchers.h
new file mode 100644 (file)
index 0000000..5affa4f
--- /dev/null
@@ -0,0 +1,90 @@
+// Copyright 2013, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements some matchers that depend on gmock-matchers.h.
+//
+// Note that tests are implemented in gmock-matchers_test.cc rather than
+// gmock-more-matchers-test.cc.
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_MATCHERS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_MATCHERS_H_
+
+#include "gmock/gmock-matchers.h"
+
+namespace testing {
+
+// Silence C4100 (unreferenced formal
+// parameter) for MSVC
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4100)
+#if (_MSC_VER == 1900)
+// and silence C4800 (C4800: 'int *const ': forcing value
+// to bool 'true' or 'false') for MSVC 14
+# pragma warning(disable:4800)
+  #endif
+#endif
+
+// Defines a matcher that matches an empty container. The container must
+// support both size() and empty(), which all STL-like containers provide.
+MATCHER(IsEmpty, negation ? "isn't empty" : "is empty") {
+  if (arg.empty()) {
+    return true;
+  }
+  *result_listener << "whose size is " << arg.size();
+  return false;
+}
+
+// Define a matcher that matches a value that evaluates in boolean
+// context to true.  Useful for types that define "explicit operator
+// bool" operators and so can't be compared for equality with true
+// and false.
+MATCHER(IsTrue, negation ? "is false" : "is true") {
+  return static_cast<bool>(arg);
+}
+
+// Define a matcher that matches a value that evaluates in boolean
+// context to false.  Useful for types that define "explicit operator
+// bool" operators and so can't be compared for equality with true
+// and false.
+MATCHER(IsFalse, negation ? "is true" : "is false") {
+  return !static_cast<bool>(arg);
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+
+}  // namespace testing
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_MORE_MATCHERS_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-nice-strict.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-nice-strict.h
new file mode 100644 (file)
index 0000000..101e0d7
--- /dev/null
@@ -0,0 +1,259 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Implements class templates NiceMock, NaggyMock, and StrictMock.
+//
+// Given a mock class MockFoo that is created using Google Mock,
+// NiceMock<MockFoo> is a subclass of MockFoo that allows
+// uninteresting calls (i.e. calls to mock methods that have no
+// EXPECT_CALL specs), NaggyMock<MockFoo> is a subclass of MockFoo
+// that prints a warning when an uninteresting call occurs, and
+// StrictMock<MockFoo> is a subclass of MockFoo that treats all
+// uninteresting calls as errors.
+//
+// Currently a mock is naggy by default, so MockFoo and
+// NaggyMock<MockFoo> behave like the same.  However, we will soon
+// switch the default behavior of mocks to be nice, as that in general
+// leads to more maintainable tests.  When that happens, MockFoo will
+// stop behaving like NaggyMock<MockFoo> and start behaving like
+// NiceMock<MockFoo>.
+//
+// NiceMock, NaggyMock, and StrictMock "inherit" the constructors of
+// their respective base class.  Therefore you can write
+// NiceMock<MockFoo>(5, "a") to construct a nice mock where MockFoo
+// has a constructor that accepts (int, const char*), for example.
+//
+// A known limitation is that NiceMock<MockFoo>, NaggyMock<MockFoo>,
+// and StrictMock<MockFoo> only works for mock methods defined using
+// the MOCK_METHOD* family of macros DIRECTLY in the MockFoo class.
+// If a mock method is defined in a base class of MockFoo, the "nice"
+// or "strict" modifier may not affect it, depending on the compiler.
+// In particular, nesting NiceMock, NaggyMock, and StrictMock is NOT
+// supported.
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
+
+#include <type_traits>
+
+#include "gmock/gmock-spec-builders.h"
+#include "gmock/internal/gmock-port.h"
+
+namespace testing {
+template <class MockClass>
+class NiceMock;
+template <class MockClass>
+class NaggyMock;
+template <class MockClass>
+class StrictMock;
+
+namespace internal {
+template <typename T>
+std::true_type StrictnessModifierProbe(const NiceMock<T>&);
+template <typename T>
+std::true_type StrictnessModifierProbe(const NaggyMock<T>&);
+template <typename T>
+std::true_type StrictnessModifierProbe(const StrictMock<T>&);
+std::false_type StrictnessModifierProbe(...);
+
+template <typename T>
+constexpr bool HasStrictnessModifier() {
+  return decltype(StrictnessModifierProbe(std::declval<const T&>()))::value;
+}
+
+// Base classes that register and deregister with testing::Mock to alter the
+// default behavior around uninteresting calls. Inheriting from one of these
+// classes first and then MockClass ensures the MockClass constructor is run
+// after registration, and that the MockClass destructor runs before
+// deregistration. This guarantees that MockClass's constructor and destructor
+// run with the same level of strictness as its instance methods.
+
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW && \
+    (defined(_MSC_VER) || defined(__clang__))
+// We need to mark these classes with this declspec to ensure that
+// the empty base class optimization is performed.
+#define GTEST_INTERNAL_EMPTY_BASE_CLASS __declspec(empty_bases)
+#else
+#define GTEST_INTERNAL_EMPTY_BASE_CLASS
+#endif
+
+template <typename Base>
+class NiceMockImpl {
+ public:
+  NiceMockImpl() { ::testing::Mock::AllowUninterestingCalls(this); }
+
+  ~NiceMockImpl() { ::testing::Mock::UnregisterCallReaction(this); }
+};
+
+template <typename Base>
+class NaggyMockImpl {
+ public:
+  NaggyMockImpl() { ::testing::Mock::WarnUninterestingCalls(this); }
+
+  ~NaggyMockImpl() { ::testing::Mock::UnregisterCallReaction(this); }
+};
+
+template <typename Base>
+class StrictMockImpl {
+ public:
+  StrictMockImpl() { ::testing::Mock::FailUninterestingCalls(this); }
+
+  ~StrictMockImpl() { ::testing::Mock::UnregisterCallReaction(this); }
+};
+
+}  // namespace internal
+
+template <class MockClass>
+class GTEST_INTERNAL_EMPTY_BASE_CLASS NiceMock
+    : private internal::NiceMockImpl<MockClass>,
+      public MockClass {
+ public:
+  static_assert(!internal::HasStrictnessModifier<MockClass>(),
+                "Can't apply NiceMock to a class hierarchy that already has a "
+                "strictness modifier. See "
+                "https://google.github.io/googletest/"
+                "gmock_cook_book.html#NiceStrictNaggy");
+  NiceMock() : MockClass() {
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
+  }
+
+  // Ideally, we would inherit base class's constructors through a using
+  // declaration, which would preserve their visibility. However, many existing
+  // tests rely on the fact that current implementation reexports protected
+  // constructors as public. These tests would need to be cleaned up first.
+
+  // Single argument constructor is special-cased so that it can be
+  // made explicit.
+  template <typename A>
+  explicit NiceMock(A&& arg) : MockClass(std::forward<A>(arg)) {
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
+  }
+
+  template <typename TArg1, typename TArg2, typename... An>
+  NiceMock(TArg1&& arg1, TArg2&& arg2, An&&... args)
+      : MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2),
+                  std::forward<An>(args)...) {
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
+  }
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(NiceMock);
+};
+
+template <class MockClass>
+class GTEST_INTERNAL_EMPTY_BASE_CLASS NaggyMock
+    : private internal::NaggyMockImpl<MockClass>,
+      public MockClass {
+  static_assert(!internal::HasStrictnessModifier<MockClass>(),
+                "Can't apply NaggyMock to a class hierarchy that already has a "
+                "strictness modifier. See "
+                "https://google.github.io/googletest/"
+                "gmock_cook_book.html#NiceStrictNaggy");
+
+ public:
+  NaggyMock() : MockClass() {
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
+  }
+
+  // Ideally, we would inherit base class's constructors through a using
+  // declaration, which would preserve their visibility. However, many existing
+  // tests rely on the fact that current implementation reexports protected
+  // constructors as public. These tests would need to be cleaned up first.
+
+  // Single argument constructor is special-cased so that it can be
+  // made explicit.
+  template <typename A>
+  explicit NaggyMock(A&& arg) : MockClass(std::forward<A>(arg)) {
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
+  }
+
+  template <typename TArg1, typename TArg2, typename... An>
+  NaggyMock(TArg1&& arg1, TArg2&& arg2, An&&... args)
+      : MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2),
+                  std::forward<An>(args)...) {
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
+  }
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(NaggyMock);
+};
+
+template <class MockClass>
+class GTEST_INTERNAL_EMPTY_BASE_CLASS StrictMock
+    : private internal::StrictMockImpl<MockClass>,
+      public MockClass {
+ public:
+  static_assert(
+      !internal::HasStrictnessModifier<MockClass>(),
+      "Can't apply StrictMock to a class hierarchy that already has a "
+      "strictness modifier. See "
+      "https://google.github.io/googletest/"
+      "gmock_cook_book.html#NiceStrictNaggy");
+  StrictMock() : MockClass() {
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
+  }
+
+  // Ideally, we would inherit base class's constructors through a using
+  // declaration, which would preserve their visibility. However, many existing
+  // tests rely on the fact that current implementation reexports protected
+  // constructors as public. These tests would need to be cleaned up first.
+
+  // Single argument constructor is special-cased so that it can be
+  // made explicit.
+  template <typename A>
+  explicit StrictMock(A&& arg) : MockClass(std::forward<A>(arg)) {
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
+  }
+
+  template <typename TArg1, typename TArg2, typename... An>
+  StrictMock(TArg1&& arg1, TArg2&& arg2, An&&... args)
+      : MockClass(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2),
+                  std::forward<An>(args)...) {
+    static_assert(sizeof(*this) == sizeof(MockClass),
+                  "The impl subclass shouldn't introduce any padding");
+  }
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(StrictMock);
+};
+
+#undef GTEST_INTERNAL_EMPTY_BASE_CLASS
+
+}  // namespace testing
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_NICE_STRICT_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-spec-builders.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock-spec-builders.h
new file mode 100644 (file)
index 0000000..b46cebe
--- /dev/null
@@ -0,0 +1,2036 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file implements the ON_CALL() and EXPECT_CALL() macros.
+//
+// A user can use the ON_CALL() macro to specify the default action of
+// a mock method.  The syntax is:
+//
+//   ON_CALL(mock_object, Method(argument-matchers))
+//       .With(multi-argument-matcher)
+//       .WillByDefault(action);
+//
+//  where the .With() clause is optional.
+//
+// A user can use the EXPECT_CALL() macro to specify an expectation on
+// a mock method.  The syntax is:
+//
+//   EXPECT_CALL(mock_object, Method(argument-matchers))
+//       .With(multi-argument-matchers)
+//       .Times(cardinality)
+//       .InSequence(sequences)
+//       .After(expectations)
+//       .WillOnce(action)
+//       .WillRepeatedly(action)
+//       .RetiresOnSaturation();
+//
+// where all clauses are optional, and .InSequence()/.After()/
+// .WillOnce() can appear any number of times.
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+#include "gmock/gmock-actions.h"
+#include "gmock/gmock-cardinalities.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/internal/gmock-internal-utils.h"
+#include "gmock/internal/gmock-port.h"
+#include "gtest/gtest.h"
+
+#if GTEST_HAS_EXCEPTIONS
+# include <stdexcept>  // NOLINT
+#endif
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
+namespace testing {
+
+// An abstract handle of an expectation.
+class Expectation;
+
+// A set of expectation handles.
+class ExpectationSet;
+
+// Anything inside the 'internal' namespace IS INTERNAL IMPLEMENTATION
+// and MUST NOT BE USED IN USER CODE!!!
+namespace internal {
+
+// Implements a mock function.
+template <typename F> class FunctionMocker;
+
+// Base class for expectations.
+class ExpectationBase;
+
+// Implements an expectation.
+template <typename F> class TypedExpectation;
+
+// Helper class for testing the Expectation class template.
+class ExpectationTester;
+
+// Helper classes for implementing NiceMock, StrictMock, and NaggyMock.
+template <typename MockClass>
+class NiceMockImpl;
+template <typename MockClass>
+class StrictMockImpl;
+template <typename MockClass>
+class NaggyMockImpl;
+
+// Protects the mock object registry (in class Mock), all function
+// mockers, and all expectations.
+//
+// The reason we don't use more fine-grained protection is: when a
+// mock function Foo() is called, it needs to consult its expectations
+// to see which one should be picked.  If another thread is allowed to
+// call a mock function (either Foo() or a different one) at the same
+// time, it could affect the "retired" attributes of Foo()'s
+// expectations when InSequence() is used, and thus affect which
+// expectation gets picked.  Therefore, we sequence all mock function
+// calls to ensure the integrity of the mock objects' states.
+GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_gmock_mutex);
+
+// Untyped base class for ActionResultHolder<R>.
+class UntypedActionResultHolderBase;
+
+// Abstract base class of FunctionMocker.  This is the
+// type-agnostic part of the function mocker interface.  Its pure
+// virtual methods are implemented by FunctionMocker.
+class GTEST_API_ UntypedFunctionMockerBase {
+ public:
+  UntypedFunctionMockerBase();
+  virtual ~UntypedFunctionMockerBase();
+
+  // Verifies that all expectations on this mock function have been
+  // satisfied.  Reports one or more Google Test non-fatal failures
+  // and returns false if not.
+  bool VerifyAndClearExpectationsLocked()
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex);
+
+  // Clears the ON_CALL()s set on this mock function.
+  virtual void ClearDefaultActionsLocked()
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) = 0;
+
+  // In all of the following Untyped* functions, it's the caller's
+  // responsibility to guarantee the correctness of the arguments'
+  // types.
+
+  // Performs the default action with the given arguments and returns
+  // the action's result.  The call description string will be used in
+  // the error message to describe the call in the case the default
+  // action fails.
+  // L = *
+  virtual UntypedActionResultHolderBase* UntypedPerformDefaultAction(
+      void* untyped_args, const std::string& call_description) const = 0;
+
+  // Performs the given action with the given arguments and returns
+  // the action's result.
+  // L = *
+  virtual UntypedActionResultHolderBase* UntypedPerformAction(
+      const void* untyped_action, void* untyped_args) const = 0;
+
+  // Writes a message that the call is uninteresting (i.e. neither
+  // explicitly expected nor explicitly unexpected) to the given
+  // ostream.
+  virtual void UntypedDescribeUninterestingCall(
+      const void* untyped_args,
+      ::std::ostream* os) const
+          GTEST_LOCK_EXCLUDED_(g_gmock_mutex) = 0;
+
+  // Returns the expectation that matches the given function arguments
+  // (or NULL is there's no match); when a match is found,
+  // untyped_action is set to point to the action that should be
+  // performed (or NULL if the action is "do default"), and
+  // is_excessive is modified to indicate whether the call exceeds the
+  // expected number.
+  virtual const ExpectationBase* UntypedFindMatchingExpectation(
+      const void* untyped_args,
+      const void** untyped_action, bool* is_excessive,
+      ::std::ostream* what, ::std::ostream* why)
+          GTEST_LOCK_EXCLUDED_(g_gmock_mutex) = 0;
+
+  // Prints the given function arguments to the ostream.
+  virtual void UntypedPrintArgs(const void* untyped_args,
+                                ::std::ostream* os) const = 0;
+
+  // Sets the mock object this mock method belongs to, and registers
+  // this information in the global mock registry.  Will be called
+  // whenever an EXPECT_CALL() or ON_CALL() is executed on this mock
+  // method.
+  void RegisterOwner(const void* mock_obj)
+      GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
+
+  // Sets the mock object this mock method belongs to, and sets the
+  // name of the mock function.  Will be called upon each invocation
+  // of this mock function.
+  void SetOwnerAndName(const void* mock_obj, const char* name)
+      GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
+
+  // Returns the mock object this mock method belongs to.  Must be
+  // called after RegisterOwner() or SetOwnerAndName() has been
+  // called.
+  const void* MockObject() const
+      GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
+
+  // Returns the name of this mock method.  Must be called after
+  // SetOwnerAndName() has been called.
+  const char* Name() const
+      GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
+
+  // Returns the result of invoking this mock function with the given
+  // arguments.  This function can be safely called from multiple
+  // threads concurrently.  The caller is responsible for deleting the
+  // result.
+  UntypedActionResultHolderBase* UntypedInvokeWith(void* untyped_args)
+      GTEST_LOCK_EXCLUDED_(g_gmock_mutex);
+
+ protected:
+  typedef std::vector<const void*> UntypedOnCallSpecs;
+
+  using UntypedExpectations = std::vector<std::shared_ptr<ExpectationBase>>;
+
+  // Returns an Expectation object that references and co-owns exp,
+  // which must be an expectation on this mock function.
+  Expectation GetHandleOf(ExpectationBase* exp);
+
+  // Address of the mock object this mock method belongs to.  Only
+  // valid after this mock method has been called or
+  // ON_CALL/EXPECT_CALL has been invoked on it.
+  const void* mock_obj_;  // Protected by g_gmock_mutex.
+
+  // Name of the function being mocked.  Only valid after this mock
+  // method has been called.
+  const char* name_;  // Protected by g_gmock_mutex.
+
+  // All default action specs for this function mocker.
+  UntypedOnCallSpecs untyped_on_call_specs_;
+
+  // All expectations for this function mocker.
+  //
+  // It's undefined behavior to interleave expectations (EXPECT_CALLs
+  // or ON_CALLs) and mock function calls.  Also, the order of
+  // expectations is important.  Therefore it's a logic race condition
+  // to read/write untyped_expectations_ concurrently.  In order for
+  // tools like tsan to catch concurrent read/write accesses to
+  // untyped_expectations, we deliberately leave accesses to it
+  // unprotected.
+  UntypedExpectations untyped_expectations_;
+};  // class UntypedFunctionMockerBase
+
+// Untyped base class for OnCallSpec<F>.
+class UntypedOnCallSpecBase {
+ public:
+  // The arguments are the location of the ON_CALL() statement.
+  UntypedOnCallSpecBase(const char* a_file, int a_line)
+      : file_(a_file), line_(a_line), last_clause_(kNone) {}
+
+  // Where in the source file was the default action spec defined?
+  const char* file() const { return file_; }
+  int line() const { return line_; }
+
+ protected:
+  // Gives each clause in the ON_CALL() statement a name.
+  enum Clause {
+    // Do not change the order of the enum members!  The run-time
+    // syntax checking relies on it.
+    kNone,
+    kWith,
+    kWillByDefault
+  };
+
+  // Asserts that the ON_CALL() statement has a certain property.
+  void AssertSpecProperty(bool property,
+                          const std::string& failure_message) const {
+    Assert(property, file_, line_, failure_message);
+  }
+
+  // Expects that the ON_CALL() statement has a certain property.
+  void ExpectSpecProperty(bool property,
+                          const std::string& failure_message) const {
+    Expect(property, file_, line_, failure_message);
+  }
+
+  const char* file_;
+  int line_;
+
+  // The last clause in the ON_CALL() statement as seen so far.
+  // Initially kNone and changes as the statement is parsed.
+  Clause last_clause_;
+};  // class UntypedOnCallSpecBase
+
+// This template class implements an ON_CALL spec.
+template <typename F>
+class OnCallSpec : public UntypedOnCallSpecBase {
+ public:
+  typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+  typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple;
+
+  // Constructs an OnCallSpec object from the information inside
+  // the parenthesis of an ON_CALL() statement.
+  OnCallSpec(const char* a_file, int a_line,
+             const ArgumentMatcherTuple& matchers)
+      : UntypedOnCallSpecBase(a_file, a_line),
+        matchers_(matchers),
+        // By default, extra_matcher_ should match anything.  However,
+        // we cannot initialize it with _ as that causes ambiguity between
+        // Matcher's copy and move constructor for some argument types.
+        extra_matcher_(A<const ArgumentTuple&>()) {}
+
+  // Implements the .With() clause.
+  OnCallSpec& With(const Matcher<const ArgumentTuple&>& m) {
+    // Makes sure this is called at most once.
+    ExpectSpecProperty(last_clause_ < kWith,
+                       ".With() cannot appear "
+                       "more than once in an ON_CALL().");
+    last_clause_ = kWith;
+
+    extra_matcher_ = m;
+    return *this;
+  }
+
+  // Implements the .WillByDefault() clause.
+  OnCallSpec& WillByDefault(const Action<F>& action) {
+    ExpectSpecProperty(last_clause_ < kWillByDefault,
+                       ".WillByDefault() must appear "
+                       "exactly once in an ON_CALL().");
+    last_clause_ = kWillByDefault;
+
+    ExpectSpecProperty(!action.IsDoDefault(),
+                       "DoDefault() cannot be used in ON_CALL().");
+    action_ = action;
+    return *this;
+  }
+
+  // Returns true if and only if the given arguments match the matchers.
+  bool Matches(const ArgumentTuple& args) const {
+    return TupleMatches(matchers_, args) && extra_matcher_.Matches(args);
+  }
+
+  // Returns the action specified by the user.
+  const Action<F>& GetAction() const {
+    AssertSpecProperty(last_clause_ == kWillByDefault,
+                       ".WillByDefault() must appear exactly "
+                       "once in an ON_CALL().");
+    return action_;
+  }
+
+ private:
+  // The information in statement
+  //
+  //   ON_CALL(mock_object, Method(matchers))
+  //       .With(multi-argument-matcher)
+  //       .WillByDefault(action);
+  //
+  // is recorded in the data members like this:
+  //
+  //   source file that contains the statement => file_
+  //   line number of the statement            => line_
+  //   matchers                                => matchers_
+  //   multi-argument-matcher                  => extra_matcher_
+  //   action                                  => action_
+  ArgumentMatcherTuple matchers_;
+  Matcher<const ArgumentTuple&> extra_matcher_;
+  Action<F> action_;
+};  // class OnCallSpec
+
+// Possible reactions on uninteresting calls.
+enum CallReaction {
+  kAllow,
+  kWarn,
+  kFail,
+};
+
+}  // namespace internal
+
+// Utilities for manipulating mock objects.
+class GTEST_API_ Mock {
+ public:
+  // The following public methods can be called concurrently.
+
+  // Tells Google Mock to ignore mock_obj when checking for leaked
+  // mock objects.
+  static void AllowLeak(const void* mock_obj)
+      GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+  // Verifies and clears all expectations on the given mock object.
+  // If the expectations aren't satisfied, generates one or more
+  // Google Test non-fatal failures and returns false.
+  static bool VerifyAndClearExpectations(void* mock_obj)
+      GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+  // Verifies all expectations on the given mock object and clears its
+  // default actions and expectations.  Returns true if and only if the
+  // verification was successful.
+  static bool VerifyAndClear(void* mock_obj)
+      GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+  // Returns whether the mock was created as a naggy mock (default)
+  static bool IsNaggy(void* mock_obj)
+      GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+  // Returns whether the mock was created as a nice mock
+  static bool IsNice(void* mock_obj)
+      GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+  // Returns whether the mock was created as a strict mock
+  static bool IsStrict(void* mock_obj)
+      GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+ private:
+  friend class internal::UntypedFunctionMockerBase;
+
+  // Needed for a function mocker to register itself (so that we know
+  // how to clear a mock object).
+  template <typename F>
+  friend class internal::FunctionMocker;
+
+  template <typename MockClass>
+  friend class internal::NiceMockImpl;
+  template <typename MockClass>
+  friend class internal::NaggyMockImpl;
+  template <typename MockClass>
+  friend class internal::StrictMockImpl;
+
+  // Tells Google Mock to allow uninteresting calls on the given mock
+  // object.
+  static void AllowUninterestingCalls(const void* mock_obj)
+      GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+  // Tells Google Mock to warn the user about uninteresting calls on
+  // the given mock object.
+  static void WarnUninterestingCalls(const void* mock_obj)
+      GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+  // Tells Google Mock to fail uninteresting calls on the given mock
+  // object.
+  static void FailUninterestingCalls(const void* mock_obj)
+      GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+  // Tells Google Mock the given mock object is being destroyed and
+  // its entry in the call-reaction table should be removed.
+  static void UnregisterCallReaction(const void* mock_obj)
+      GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+  // Returns the reaction Google Mock will have on uninteresting calls
+  // made on the given mock object.
+  static internal::CallReaction GetReactionOnUninterestingCalls(
+      const void* mock_obj)
+          GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+  // Verifies that all expectations on the given mock object have been
+  // satisfied.  Reports one or more Google Test non-fatal failures
+  // and returns false if not.
+  static bool VerifyAndClearExpectationsLocked(void* mock_obj)
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex);
+
+  // Clears all ON_CALL()s set on the given mock object.
+  static void ClearDefaultActionsLocked(void* mock_obj)
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex);
+
+  // Registers a mock object and a mock method it owns.
+  static void Register(
+      const void* mock_obj,
+      internal::UntypedFunctionMockerBase* mocker)
+          GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+  // Tells Google Mock where in the source code mock_obj is used in an
+  // ON_CALL or EXPECT_CALL.  In case mock_obj is leaked, this
+  // information helps the user identify which object it is.
+  static void RegisterUseByOnCallOrExpectCall(
+      const void* mock_obj, const char* file, int line)
+          GTEST_LOCK_EXCLUDED_(internal::g_gmock_mutex);
+
+  // Unregisters a mock method; removes the owning mock object from
+  // the registry when the last mock method associated with it has
+  // been unregistered.  This is called only in the destructor of
+  // FunctionMocker.
+  static void UnregisterLocked(internal::UntypedFunctionMockerBase* mocker)
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(internal::g_gmock_mutex);
+};  // class Mock
+
+// An abstract handle of an expectation.  Useful in the .After()
+// clause of EXPECT_CALL() for setting the (partial) order of
+// expectations.  The syntax:
+//
+//   Expectation e1 = EXPECT_CALL(...)...;
+//   EXPECT_CALL(...).After(e1)...;
+//
+// sets two expectations where the latter can only be matched after
+// the former has been satisfied.
+//
+// Notes:
+//   - This class is copyable and has value semantics.
+//   - Constness is shallow: a const Expectation object itself cannot
+//     be modified, but the mutable methods of the ExpectationBase
+//     object it references can be called via expectation_base().
+
+class GTEST_API_ Expectation {
+ public:
+  // Constructs a null object that doesn't reference any expectation.
+  Expectation();
+  Expectation(Expectation&&) = default;
+  Expectation(const Expectation&) = default;
+  Expectation& operator=(Expectation&&) = default;
+  Expectation& operator=(const Expectation&) = default;
+  ~Expectation();
+
+  // This single-argument ctor must not be explicit, in order to support the
+  //   Expectation e = EXPECT_CALL(...);
+  // syntax.
+  //
+  // A TypedExpectation object stores its pre-requisites as
+  // Expectation objects, and needs to call the non-const Retire()
+  // method on the ExpectationBase objects they reference.  Therefore
+  // Expectation must receive a *non-const* reference to the
+  // ExpectationBase object.
+  Expectation(internal::ExpectationBase& exp);  // NOLINT
+
+  // The compiler-generated copy ctor and operator= work exactly as
+  // intended, so we don't need to define our own.
+
+  // Returns true if and only if rhs references the same expectation as this
+  // object does.
+  bool operator==(const Expectation& rhs) const {
+    return expectation_base_ == rhs.expectation_base_;
+  }
+
+  bool operator!=(const Expectation& rhs) const { return !(*this == rhs); }
+
+ private:
+  friend class ExpectationSet;
+  friend class Sequence;
+  friend class ::testing::internal::ExpectationBase;
+  friend class ::testing::internal::UntypedFunctionMockerBase;
+
+  template <typename F>
+  friend class ::testing::internal::FunctionMocker;
+
+  template <typename F>
+  friend class ::testing::internal::TypedExpectation;
+
+  // This comparator is needed for putting Expectation objects into a set.
+  class Less {
+   public:
+    bool operator()(const Expectation& lhs, const Expectation& rhs) const {
+      return lhs.expectation_base_.get() < rhs.expectation_base_.get();
+    }
+  };
+
+  typedef ::std::set<Expectation, Less> Set;
+
+  Expectation(
+      const std::shared_ptr<internal::ExpectationBase>& expectation_base);
+
+  // Returns the expectation this object references.
+  const std::shared_ptr<internal::ExpectationBase>& expectation_base() const {
+    return expectation_base_;
+  }
+
+  // A shared_ptr that co-owns the expectation this handle references.
+  std::shared_ptr<internal::ExpectationBase> expectation_base_;
+};
+
+// A set of expectation handles.  Useful in the .After() clause of
+// EXPECT_CALL() for setting the (partial) order of expectations.  The
+// syntax:
+//
+//   ExpectationSet es;
+//   es += EXPECT_CALL(...)...;
+//   es += EXPECT_CALL(...)...;
+//   EXPECT_CALL(...).After(es)...;
+//
+// sets three expectations where the last one can only be matched
+// after the first two have both been satisfied.
+//
+// This class is copyable and has value semantics.
+class ExpectationSet {
+ public:
+  // A bidirectional iterator that can read a const element in the set.
+  typedef Expectation::Set::const_iterator const_iterator;
+
+  // An object stored in the set.  This is an alias of Expectation.
+  typedef Expectation::Set::value_type value_type;
+
+  // Constructs an empty set.
+  ExpectationSet() {}
+
+  // This single-argument ctor must not be explicit, in order to support the
+  //   ExpectationSet es = EXPECT_CALL(...);
+  // syntax.
+  ExpectationSet(internal::ExpectationBase& exp) {  // NOLINT
+    *this += Expectation(exp);
+  }
+
+  // This single-argument ctor implements implicit conversion from
+  // Expectation and thus must not be explicit.  This allows either an
+  // Expectation or an ExpectationSet to be used in .After().
+  ExpectationSet(const Expectation& e) {  // NOLINT
+    *this += e;
+  }
+
+  // The compiler-generator ctor and operator= works exactly as
+  // intended, so we don't need to define our own.
+
+  // Returns true if and only if rhs contains the same set of Expectation
+  // objects as this does.
+  bool operator==(const ExpectationSet& rhs) const {
+    return expectations_ == rhs.expectations_;
+  }
+
+  bool operator!=(const ExpectationSet& rhs) const { return !(*this == rhs); }
+
+  // Implements the syntax
+  //   expectation_set += EXPECT_CALL(...);
+  ExpectationSet& operator+=(const Expectation& e) {
+    expectations_.insert(e);
+    return *this;
+  }
+
+  int size() const { return static_cast<int>(expectations_.size()); }
+
+  const_iterator begin() const { return expectations_.begin(); }
+  const_iterator end() const { return expectations_.end(); }
+
+ private:
+  Expectation::Set expectations_;
+};
+
+
+// Sequence objects are used by a user to specify the relative order
+// in which the expectations should match.  They are copyable (we rely
+// on the compiler-defined copy constructor and assignment operator).
+class GTEST_API_ Sequence {
+ public:
+  // Constructs an empty sequence.
+  Sequence() : last_expectation_(new Expectation) {}
+
+  // Adds an expectation to this sequence.  The caller must ensure
+  // that no other thread is accessing this Sequence object.
+  void AddExpectation(const Expectation& expectation) const;
+
+ private:
+  // The last expectation in this sequence.
+  std::shared_ptr<Expectation> last_expectation_;
+};  // class Sequence
+
+// An object of this type causes all EXPECT_CALL() statements
+// encountered in its scope to be put in an anonymous sequence.  The
+// work is done in the constructor and destructor.  You should only
+// create an InSequence object on the stack.
+//
+// The sole purpose for this class is to support easy definition of
+// sequential expectations, e.g.
+//
+//   {
+//     InSequence dummy;  // The name of the object doesn't matter.
+//
+//     // The following expectations must match in the order they appear.
+//     EXPECT_CALL(a, Bar())...;
+//     EXPECT_CALL(a, Baz())...;
+//     ...
+//     EXPECT_CALL(b, Xyz())...;
+//   }
+//
+// You can create InSequence objects in multiple threads, as long as
+// they are used to affect different mock objects.  The idea is that
+// each thread can create and set up its own mocks as if it's the only
+// thread.  However, for clarity of your tests we recommend you to set
+// up mocks in the main thread unless you have a good reason not to do
+// so.
+class GTEST_API_ InSequence {
+ public:
+  InSequence();
+  ~InSequence();
+ private:
+  bool sequence_created_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(InSequence);  // NOLINT
+} GTEST_ATTRIBUTE_UNUSED_;
+
+namespace internal {
+
+// Points to the implicit sequence introduced by a living InSequence
+// object (if any) in the current thread or NULL.
+GTEST_API_ extern ThreadLocal<Sequence*> g_gmock_implicit_sequence;
+
+// Base class for implementing expectations.
+//
+// There are two reasons for having a type-agnostic base class for
+// Expectation:
+//
+//   1. We need to store collections of expectations of different
+//   types (e.g. all pre-requisites of a particular expectation, all
+//   expectations in a sequence).  Therefore these expectation objects
+//   must share a common base class.
+//
+//   2. We can avoid binary code bloat by moving methods not depending
+//   on the template argument of Expectation to the base class.
+//
+// This class is internal and mustn't be used by user code directly.
+class GTEST_API_ ExpectationBase {
+ public:
+  // source_text is the EXPECT_CALL(...) source that created this Expectation.
+  ExpectationBase(const char* file, int line, const std::string& source_text);
+
+  virtual ~ExpectationBase();
+
+  // Where in the source file was the expectation spec defined?
+  const char* file() const { return file_; }
+  int line() const { return line_; }
+  const char* source_text() const { return source_text_.c_str(); }
+  // Returns the cardinality specified in the expectation spec.
+  const Cardinality& cardinality() const { return cardinality_; }
+
+  // Describes the source file location of this expectation.
+  void DescribeLocationTo(::std::ostream* os) const {
+    *os << FormatFileLocation(file(), line()) << " ";
+  }
+
+  // Describes how many times a function call matching this
+  // expectation has occurred.
+  void DescribeCallCountTo(::std::ostream* os) const
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex);
+
+  // If this mock method has an extra matcher (i.e. .With(matcher)),
+  // describes it to the ostream.
+  virtual void MaybeDescribeExtraMatcherTo(::std::ostream* os) = 0;
+
+ protected:
+  friend class ::testing::Expectation;
+  friend class UntypedFunctionMockerBase;
+
+  enum Clause {
+    // Don't change the order of the enum members!
+    kNone,
+    kWith,
+    kTimes,
+    kInSequence,
+    kAfter,
+    kWillOnce,
+    kWillRepeatedly,
+    kRetiresOnSaturation
+  };
+
+  typedef std::vector<const void*> UntypedActions;
+
+  // Returns an Expectation object that references and co-owns this
+  // expectation.
+  virtual Expectation GetHandle() = 0;
+
+  // Asserts that the EXPECT_CALL() statement has the given property.
+  void AssertSpecProperty(bool property,
+                          const std::string& failure_message) const {
+    Assert(property, file_, line_, failure_message);
+  }
+
+  // Expects that the EXPECT_CALL() statement has the given property.
+  void ExpectSpecProperty(bool property,
+                          const std::string& failure_message) const {
+    Expect(property, file_, line_, failure_message);
+  }
+
+  // Explicitly specifies the cardinality of this expectation.  Used
+  // by the subclasses to implement the .Times() clause.
+  void SpecifyCardinality(const Cardinality& cardinality);
+
+  // Returns true if and only if the user specified the cardinality
+  // explicitly using a .Times().
+  bool cardinality_specified() const { return cardinality_specified_; }
+
+  // Sets the cardinality of this expectation spec.
+  void set_cardinality(const Cardinality& a_cardinality) {
+    cardinality_ = a_cardinality;
+  }
+
+  // The following group of methods should only be called after the
+  // EXPECT_CALL() statement, and only when g_gmock_mutex is held by
+  // the current thread.
+
+  // Retires all pre-requisites of this expectation.
+  void RetireAllPreRequisites()
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex);
+
+  // Returns true if and only if this expectation is retired.
+  bool is_retired() const
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    return retired_;
+  }
+
+  // Retires this expectation.
+  void Retire()
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    retired_ = true;
+  }
+
+  // Returns true if and only if this expectation is satisfied.
+  bool IsSatisfied() const
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    return cardinality().IsSatisfiedByCallCount(call_count_);
+  }
+
+  // Returns true if and only if this expectation is saturated.
+  bool IsSaturated() const
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    return cardinality().IsSaturatedByCallCount(call_count_);
+  }
+
+  // Returns true if and only if this expectation is over-saturated.
+  bool IsOverSaturated() const
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    return cardinality().IsOverSaturatedByCallCount(call_count_);
+  }
+
+  // Returns true if and only if all pre-requisites of this expectation are
+  // satisfied.
+  bool AllPrerequisitesAreSatisfied() const
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex);
+
+  // Adds unsatisfied pre-requisites of this expectation to 'result'.
+  void FindUnsatisfiedPrerequisites(ExpectationSet* result) const
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex);
+
+  // Returns the number this expectation has been invoked.
+  int call_count() const
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    return call_count_;
+  }
+
+  // Increments the number this expectation has been invoked.
+  void IncrementCallCount()
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    call_count_++;
+  }
+
+  // Checks the action count (i.e. the number of WillOnce() and
+  // WillRepeatedly() clauses) against the cardinality if this hasn't
+  // been done before.  Prints a warning if there are too many or too
+  // few actions.
+  void CheckActionCountIfNotDone() const
+      GTEST_LOCK_EXCLUDED_(mutex_);
+
+  friend class ::testing::Sequence;
+  friend class ::testing::internal::ExpectationTester;
+
+  template <typename Function>
+  friend class TypedExpectation;
+
+  // Implements the .Times() clause.
+  void UntypedTimes(const Cardinality& a_cardinality);
+
+  // This group of fields are part of the spec and won't change after
+  // an EXPECT_CALL() statement finishes.
+  const char* file_;          // The file that contains the expectation.
+  int line_;                  // The line number of the expectation.
+  const std::string source_text_;  // The EXPECT_CALL(...) source text.
+  // True if and only if the cardinality is specified explicitly.
+  bool cardinality_specified_;
+  Cardinality cardinality_;            // The cardinality of the expectation.
+  // The immediate pre-requisites (i.e. expectations that must be
+  // satisfied before this expectation can be matched) of this
+  // expectation.  We use std::shared_ptr in the set because we want an
+  // Expectation object to be co-owned by its FunctionMocker and its
+  // successors.  This allows multiple mock objects to be deleted at
+  // different times.
+  ExpectationSet immediate_prerequisites_;
+
+  // This group of fields are the current state of the expectation,
+  // and can change as the mock function is called.
+  int call_count_;  // How many times this expectation has been invoked.
+  bool retired_;    // True if and only if this expectation has retired.
+  UntypedActions untyped_actions_;
+  bool extra_matcher_specified_;
+  bool repeated_action_specified_;  // True if a WillRepeatedly() was specified.
+  bool retires_on_saturation_;
+  Clause last_clause_;
+  mutable bool action_count_checked_;  // Under mutex_.
+  mutable Mutex mutex_;  // Protects action_count_checked_.
+};  // class ExpectationBase
+
+// Impements an expectation for the given function type.
+template <typename F>
+class TypedExpectation : public ExpectationBase {
+ public:
+  typedef typename Function<F>::ArgumentTuple ArgumentTuple;
+  typedef typename Function<F>::ArgumentMatcherTuple ArgumentMatcherTuple;
+  typedef typename Function<F>::Result Result;
+
+  TypedExpectation(FunctionMocker<F>* owner, const char* a_file, int a_line,
+                   const std::string& a_source_text,
+                   const ArgumentMatcherTuple& m)
+      : ExpectationBase(a_file, a_line, a_source_text),
+        owner_(owner),
+        matchers_(m),
+        // By default, extra_matcher_ should match anything.  However,
+        // we cannot initialize it with _ as that causes ambiguity between
+        // Matcher's copy and move constructor for some argument types.
+        extra_matcher_(A<const ArgumentTuple&>()),
+        repeated_action_(DoDefault()) {}
+
+  ~TypedExpectation() override {
+    // Check the validity of the action count if it hasn't been done
+    // yet (for example, if the expectation was never used).
+    CheckActionCountIfNotDone();
+    for (UntypedActions::const_iterator it = untyped_actions_.begin();
+         it != untyped_actions_.end(); ++it) {
+      delete static_cast<const Action<F>*>(*it);
+    }
+  }
+
+  // Implements the .With() clause.
+  TypedExpectation& With(const Matcher<const ArgumentTuple&>& m) {
+    if (last_clause_ == kWith) {
+      ExpectSpecProperty(false,
+                         ".With() cannot appear "
+                         "more than once in an EXPECT_CALL().");
+    } else {
+      ExpectSpecProperty(last_clause_ < kWith,
+                         ".With() must be the first "
+                         "clause in an EXPECT_CALL().");
+    }
+    last_clause_ = kWith;
+
+    extra_matcher_ = m;
+    extra_matcher_specified_ = true;
+    return *this;
+  }
+
+  // Implements the .Times() clause.
+  TypedExpectation& Times(const Cardinality& a_cardinality) {
+    ExpectationBase::UntypedTimes(a_cardinality);
+    return *this;
+  }
+
+  // Implements the .Times() clause.
+  TypedExpectation& Times(int n) {
+    return Times(Exactly(n));
+  }
+
+  // Implements the .InSequence() clause.
+  TypedExpectation& InSequence(const Sequence& s) {
+    ExpectSpecProperty(last_clause_ <= kInSequence,
+                       ".InSequence() cannot appear after .After(),"
+                       " .WillOnce(), .WillRepeatedly(), or "
+                       ".RetiresOnSaturation().");
+    last_clause_ = kInSequence;
+
+    s.AddExpectation(GetHandle());
+    return *this;
+  }
+  TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2) {
+    return InSequence(s1).InSequence(s2);
+  }
+  TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2,
+                               const Sequence& s3) {
+    return InSequence(s1, s2).InSequence(s3);
+  }
+  TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2,
+                               const Sequence& s3, const Sequence& s4) {
+    return InSequence(s1, s2, s3).InSequence(s4);
+  }
+  TypedExpectation& InSequence(const Sequence& s1, const Sequence& s2,
+                               const Sequence& s3, const Sequence& s4,
+                               const Sequence& s5) {
+    return InSequence(s1, s2, s3, s4).InSequence(s5);
+  }
+
+  // Implements that .After() clause.
+  TypedExpectation& After(const ExpectationSet& s) {
+    ExpectSpecProperty(last_clause_ <= kAfter,
+                       ".After() cannot appear after .WillOnce(),"
+                       " .WillRepeatedly(), or "
+                       ".RetiresOnSaturation().");
+    last_clause_ = kAfter;
+
+    for (ExpectationSet::const_iterator it = s.begin(); it != s.end(); ++it) {
+      immediate_prerequisites_ += *it;
+    }
+    return *this;
+  }
+  TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2) {
+    return After(s1).After(s2);
+  }
+  TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2,
+                          const ExpectationSet& s3) {
+    return After(s1, s2).After(s3);
+  }
+  TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2,
+                          const ExpectationSet& s3, const ExpectationSet& s4) {
+    return After(s1, s2, s3).After(s4);
+  }
+  TypedExpectation& After(const ExpectationSet& s1, const ExpectationSet& s2,
+                          const ExpectationSet& s3, const ExpectationSet& s4,
+                          const ExpectationSet& s5) {
+    return After(s1, s2, s3, s4).After(s5);
+  }
+
+  // Implements the .WillOnce() clause.
+  TypedExpectation& WillOnce(const Action<F>& action) {
+    ExpectSpecProperty(last_clause_ <= kWillOnce,
+                       ".WillOnce() cannot appear after "
+                       ".WillRepeatedly() or .RetiresOnSaturation().");
+    last_clause_ = kWillOnce;
+
+    untyped_actions_.push_back(new Action<F>(action));
+    if (!cardinality_specified()) {
+      set_cardinality(Exactly(static_cast<int>(untyped_actions_.size())));
+    }
+    return *this;
+  }
+
+  // Implements the .WillRepeatedly() clause.
+  TypedExpectation& WillRepeatedly(const Action<F>& action) {
+    if (last_clause_ == kWillRepeatedly) {
+      ExpectSpecProperty(false,
+                         ".WillRepeatedly() cannot appear "
+                         "more than once in an EXPECT_CALL().");
+    } else {
+      ExpectSpecProperty(last_clause_ < kWillRepeatedly,
+                         ".WillRepeatedly() cannot appear "
+                         "after .RetiresOnSaturation().");
+    }
+    last_clause_ = kWillRepeatedly;
+    repeated_action_specified_ = true;
+
+    repeated_action_ = action;
+    if (!cardinality_specified()) {
+      set_cardinality(AtLeast(static_cast<int>(untyped_actions_.size())));
+    }
+
+    // Now that no more action clauses can be specified, we check
+    // whether their count makes sense.
+    CheckActionCountIfNotDone();
+    return *this;
+  }
+
+  // Implements the .RetiresOnSaturation() clause.
+  TypedExpectation& RetiresOnSaturation() {
+    ExpectSpecProperty(last_clause_ < kRetiresOnSaturation,
+                       ".RetiresOnSaturation() cannot appear "
+                       "more than once.");
+    last_clause_ = kRetiresOnSaturation;
+    retires_on_saturation_ = true;
+
+    // Now that no more action clauses can be specified, we check
+    // whether their count makes sense.
+    CheckActionCountIfNotDone();
+    return *this;
+  }
+
+  // Returns the matchers for the arguments as specified inside the
+  // EXPECT_CALL() macro.
+  const ArgumentMatcherTuple& matchers() const {
+    return matchers_;
+  }
+
+  // Returns the matcher specified by the .With() clause.
+  const Matcher<const ArgumentTuple&>& extra_matcher() const {
+    return extra_matcher_;
+  }
+
+  // Returns the action specified by the .WillRepeatedly() clause.
+  const Action<F>& repeated_action() const { return repeated_action_; }
+
+  // If this mock method has an extra matcher (i.e. .With(matcher)),
+  // describes it to the ostream.
+  void MaybeDescribeExtraMatcherTo(::std::ostream* os) override {
+    if (extra_matcher_specified_) {
+      *os << "    Expected args: ";
+      extra_matcher_.DescribeTo(os);
+      *os << "\n";
+    }
+  }
+
+ private:
+  template <typename Function>
+  friend class FunctionMocker;
+
+  // Returns an Expectation object that references and co-owns this
+  // expectation.
+  Expectation GetHandle() override { return owner_->GetHandleOf(this); }
+
+  // The following methods will be called only after the EXPECT_CALL()
+  // statement finishes and when the current thread holds
+  // g_gmock_mutex.
+
+  // Returns true if and only if this expectation matches the given arguments.
+  bool Matches(const ArgumentTuple& args) const
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    return TupleMatches(matchers_, args) && extra_matcher_.Matches(args);
+  }
+
+  // Returns true if and only if this expectation should handle the given
+  // arguments.
+  bool ShouldHandleArguments(const ArgumentTuple& args) const
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+
+    // In case the action count wasn't checked when the expectation
+    // was defined (e.g. if this expectation has no WillRepeatedly()
+    // or RetiresOnSaturation() clause), we check it when the
+    // expectation is used for the first time.
+    CheckActionCountIfNotDone();
+    return !is_retired() && AllPrerequisitesAreSatisfied() && Matches(args);
+  }
+
+  // Describes the result of matching the arguments against this
+  // expectation to the given ostream.
+  void ExplainMatchResultTo(
+      const ArgumentTuple& args,
+      ::std::ostream* os) const
+          GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+
+    if (is_retired()) {
+      *os << "         Expected: the expectation is active\n"
+          << "           Actual: it is retired\n";
+    } else if (!Matches(args)) {
+      if (!TupleMatches(matchers_, args)) {
+        ExplainMatchFailureTupleTo(matchers_, args, os);
+      }
+      StringMatchResultListener listener;
+      if (!extra_matcher_.MatchAndExplain(args, &listener)) {
+        *os << "    Expected args: ";
+        extra_matcher_.DescribeTo(os);
+        *os << "\n           Actual: don't match";
+
+        internal::PrintIfNotEmpty(listener.str(), os);
+        *os << "\n";
+      }
+    } else if (!AllPrerequisitesAreSatisfied()) {
+      *os << "         Expected: all pre-requisites are satisfied\n"
+          << "           Actual: the following immediate pre-requisites "
+          << "are not satisfied:\n";
+      ExpectationSet unsatisfied_prereqs;
+      FindUnsatisfiedPrerequisites(&unsatisfied_prereqs);
+      int i = 0;
+      for (ExpectationSet::const_iterator it = unsatisfied_prereqs.begin();
+           it != unsatisfied_prereqs.end(); ++it) {
+        it->expectation_base()->DescribeLocationTo(os);
+        *os << "pre-requisite #" << i++ << "\n";
+      }
+      *os << "                   (end of pre-requisites)\n";
+    } else {
+      // This line is here just for completeness' sake.  It will never
+      // be executed as currently the ExplainMatchResultTo() function
+      // is called only when the mock function call does NOT match the
+      // expectation.
+      *os << "The call matches the expectation.\n";
+    }
+  }
+
+  // Returns the action that should be taken for the current invocation.
+  const Action<F>& GetCurrentAction(const FunctionMocker<F>* mocker,
+                                    const ArgumentTuple& args) const
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    const int count = call_count();
+    Assert(count >= 1, __FILE__, __LINE__,
+           "call_count() is <= 0 when GetCurrentAction() is "
+           "called - this should never happen.");
+
+    const int action_count = static_cast<int>(untyped_actions_.size());
+    if (action_count > 0 && !repeated_action_specified_ &&
+        count > action_count) {
+      // If there is at least one WillOnce() and no WillRepeatedly(),
+      // we warn the user when the WillOnce() clauses ran out.
+      ::std::stringstream ss;
+      DescribeLocationTo(&ss);
+      ss << "Actions ran out in " << source_text() << "...\n"
+         << "Called " << count << " times, but only "
+         << action_count << " WillOnce()"
+         << (action_count == 1 ? " is" : "s are") << " specified - ";
+      mocker->DescribeDefaultActionTo(args, &ss);
+      Log(kWarning, ss.str(), 1);
+    }
+
+    return count <= action_count
+               ? *static_cast<const Action<F>*>(
+                     untyped_actions_[static_cast<size_t>(count - 1)])
+               : repeated_action();
+  }
+
+  // Given the arguments of a mock function call, if the call will
+  // over-saturate this expectation, returns the default action;
+  // otherwise, returns the next action in this expectation.  Also
+  // describes *what* happened to 'what', and explains *why* Google
+  // Mock does it to 'why'.  This method is not const as it calls
+  // IncrementCallCount().  A return value of NULL means the default
+  // action.
+  const Action<F>* GetActionForArguments(const FunctionMocker<F>* mocker,
+                                         const ArgumentTuple& args,
+                                         ::std::ostream* what,
+                                         ::std::ostream* why)
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    if (IsSaturated()) {
+      // We have an excessive call.
+      IncrementCallCount();
+      *what << "Mock function called more times than expected - ";
+      mocker->DescribeDefaultActionTo(args, what);
+      DescribeCallCountTo(why);
+
+      return nullptr;
+    }
+
+    IncrementCallCount();
+    RetireAllPreRequisites();
+
+    if (retires_on_saturation_ && IsSaturated()) {
+      Retire();
+    }
+
+    // Must be done after IncrementCount()!
+    *what << "Mock function call matches " << source_text() <<"...\n";
+    return &(GetCurrentAction(mocker, args));
+  }
+
+  // All the fields below won't change once the EXPECT_CALL()
+  // statement finishes.
+  FunctionMocker<F>* const owner_;
+  ArgumentMatcherTuple matchers_;
+  Matcher<const ArgumentTuple&> extra_matcher_;
+  Action<F> repeated_action_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TypedExpectation);
+};  // class TypedExpectation
+
+// A MockSpec object is used by ON_CALL() or EXPECT_CALL() for
+// specifying the default behavior of, or expectation on, a mock
+// function.
+
+// Note: class MockSpec really belongs to the ::testing namespace.
+// However if we define it in ::testing, MSVC will complain when
+// classes in ::testing::internal declare it as a friend class
+// template.  To workaround this compiler bug, we define MockSpec in
+// ::testing::internal and import it into ::testing.
+
+// Logs a message including file and line number information.
+GTEST_API_ void LogWithLocation(testing::internal::LogSeverity severity,
+                                const char* file, int line,
+                                const std::string& message);
+
+template <typename F>
+class MockSpec {
+ public:
+  typedef typename internal::Function<F>::ArgumentTuple ArgumentTuple;
+  typedef typename internal::Function<F>::ArgumentMatcherTuple
+      ArgumentMatcherTuple;
+
+  // Constructs a MockSpec object, given the function mocker object
+  // that the spec is associated with.
+  MockSpec(internal::FunctionMocker<F>* function_mocker,
+           const ArgumentMatcherTuple& matchers)
+      : function_mocker_(function_mocker), matchers_(matchers) {}
+
+  // Adds a new default action spec to the function mocker and returns
+  // the newly created spec.
+  internal::OnCallSpec<F>& InternalDefaultActionSetAt(
+      const char* file, int line, const char* obj, const char* call) {
+    LogWithLocation(internal::kInfo, file, line,
+                    std::string("ON_CALL(") + obj + ", " + call + ") invoked");
+    return function_mocker_->AddNewOnCallSpec(file, line, matchers_);
+  }
+
+  // Adds a new expectation spec to the function mocker and returns
+  // the newly created spec.
+  internal::TypedExpectation<F>& InternalExpectedAt(
+      const char* file, int line, const char* obj, const char* call) {
+    const std::string source_text(std::string("EXPECT_CALL(") + obj + ", " +
+                                  call + ")");
+    LogWithLocation(internal::kInfo, file, line, source_text + " invoked");
+    return function_mocker_->AddNewExpectation(
+        file, line, source_text, matchers_);
+  }
+
+  // This operator overload is used to swallow the superfluous parameter list
+  // introduced by the ON/EXPECT_CALL macros. See the macro comments for more
+  // explanation.
+  MockSpec<F>& operator()(const internal::WithoutMatchers&, void* const) {
+    return *this;
+  }
+
+ private:
+  template <typename Function>
+  friend class internal::FunctionMocker;
+
+  // The function mocker that owns this spec.
+  internal::FunctionMocker<F>* const function_mocker_;
+  // The argument matchers specified in the spec.
+  ArgumentMatcherTuple matchers_;
+};  // class MockSpec
+
+// Wrapper type for generically holding an ordinary value or lvalue reference.
+// If T is not a reference type, it must be copyable or movable.
+// ReferenceOrValueWrapper<T> is movable, and will also be copyable unless
+// T is a move-only value type (which means that it will always be copyable
+// if the current platform does not support move semantics).
+//
+// The primary template defines handling for values, but function header
+// comments describe the contract for the whole template (including
+// specializations).
+template <typename T>
+class ReferenceOrValueWrapper {
+ public:
+  // Constructs a wrapper from the given value/reference.
+  explicit ReferenceOrValueWrapper(T value)
+      : value_(std::move(value)) {
+  }
+
+  // Unwraps and returns the underlying value/reference, exactly as
+  // originally passed. The behavior of calling this more than once on
+  // the same object is unspecified.
+  T Unwrap() { return std::move(value_); }
+
+  // Provides nondestructive access to the underlying value/reference.
+  // Always returns a const reference (more precisely,
+  // const std::add_lvalue_reference<T>::type). The behavior of calling this
+  // after calling Unwrap on the same object is unspecified.
+  const T& Peek() const {
+    return value_;
+  }
+
+ private:
+  T value_;
+};
+
+// Specialization for lvalue reference types. See primary template
+// for documentation.
+template <typename T>
+class ReferenceOrValueWrapper<T&> {
+ public:
+  // Workaround for debatable pass-by-reference lint warning (c-library-team
+  // policy precludes NOLINT in this context)
+  typedef T& reference;
+  explicit ReferenceOrValueWrapper(reference ref)
+      : value_ptr_(&ref) {}
+  T& Unwrap() { return *value_ptr_; }
+  const T& Peek() const { return *value_ptr_; }
+
+ private:
+  T* value_ptr_;
+};
+
+// C++ treats the void type specially.  For example, you cannot define
+// a void-typed variable or pass a void value to a function.
+// ActionResultHolder<T> holds a value of type T, where T must be a
+// copyable type or void (T doesn't need to be default-constructable).
+// It hides the syntactic difference between void and other types, and
+// is used to unify the code for invoking both void-returning and
+// non-void-returning mock functions.
+
+// Untyped base class for ActionResultHolder<T>.
+class UntypedActionResultHolderBase {
+ public:
+  virtual ~UntypedActionResultHolderBase() {}
+
+  // Prints the held value as an action's result to os.
+  virtual void PrintAsActionResult(::std::ostream* os) const = 0;
+};
+
+// This generic definition is used when T is not void.
+template <typename T>
+class ActionResultHolder : public UntypedActionResultHolderBase {
+ public:
+  // Returns the held value. Must not be called more than once.
+  T Unwrap() {
+    return result_.Unwrap();
+  }
+
+  // Prints the held value as an action's result to os.
+  void PrintAsActionResult(::std::ostream* os) const override {
+    *os << "\n          Returns: ";
+    // T may be a reference type, so we don't use UniversalPrint().
+    UniversalPrinter<T>::Print(result_.Peek(), os);
+  }
+
+  // Performs the given mock function's default action and returns the
+  // result in a new-ed ActionResultHolder.
+  template <typename F>
+  static ActionResultHolder* PerformDefaultAction(
+      const FunctionMocker<F>* func_mocker,
+      typename Function<F>::ArgumentTuple&& args,
+      const std::string& call_description) {
+    return new ActionResultHolder(Wrapper(func_mocker->PerformDefaultAction(
+        std::move(args), call_description)));
+  }
+
+  // Performs the given action and returns the result in a new-ed
+  // ActionResultHolder.
+  template <typename F>
+  static ActionResultHolder* PerformAction(
+      const Action<F>& action, typename Function<F>::ArgumentTuple&& args) {
+    return new ActionResultHolder(
+        Wrapper(action.Perform(std::move(args))));
+  }
+
+ private:
+  typedef ReferenceOrValueWrapper<T> Wrapper;
+
+  explicit ActionResultHolder(Wrapper result)
+      : result_(std::move(result)) {
+  }
+
+  Wrapper result_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionResultHolder);
+};
+
+// Specialization for T = void.
+template <>
+class ActionResultHolder<void> : public UntypedActionResultHolderBase {
+ public:
+  void Unwrap() { }
+
+  void PrintAsActionResult(::std::ostream* /* os */) const override {}
+
+  // Performs the given mock function's default action and returns ownership
+  // of an empty ActionResultHolder*.
+  template <typename F>
+  static ActionResultHolder* PerformDefaultAction(
+      const FunctionMocker<F>* func_mocker,
+      typename Function<F>::ArgumentTuple&& args,
+      const std::string& call_description) {
+    func_mocker->PerformDefaultAction(std::move(args), call_description);
+    return new ActionResultHolder;
+  }
+
+  // Performs the given action and returns ownership of an empty
+  // ActionResultHolder*.
+  template <typename F>
+  static ActionResultHolder* PerformAction(
+      const Action<F>& action, typename Function<F>::ArgumentTuple&& args) {
+    action.Perform(std::move(args));
+    return new ActionResultHolder;
+  }
+
+ private:
+  ActionResultHolder() {}
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ActionResultHolder);
+};
+
+template <typename F>
+class FunctionMocker;
+
+template <typename R, typename... Args>
+class FunctionMocker<R(Args...)> final : public UntypedFunctionMockerBase {
+  using F = R(Args...);
+
+ public:
+  using Result = R;
+  using ArgumentTuple = std::tuple<Args...>;
+  using ArgumentMatcherTuple = std::tuple<Matcher<Args>...>;
+
+  FunctionMocker() {}
+
+  // There is no generally useful and implementable semantics of
+  // copying a mock object, so copying a mock is usually a user error.
+  // Thus we disallow copying function mockers.  If the user really
+  // wants to copy a mock object, they should implement their own copy
+  // operation, for example:
+  //
+  //   class MockFoo : public Foo {
+  //    public:
+  //     // Defines a copy constructor explicitly.
+  //     MockFoo(const MockFoo& src) {}
+  //     ...
+  //   };
+  FunctionMocker(const FunctionMocker&) = delete;
+  FunctionMocker& operator=(const FunctionMocker&) = delete;
+
+  // The destructor verifies that all expectations on this mock
+  // function have been satisfied.  If not, it will report Google Test
+  // non-fatal failures for the violations.
+  ~FunctionMocker() override GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+    MutexLock l(&g_gmock_mutex);
+    VerifyAndClearExpectationsLocked();
+    Mock::UnregisterLocked(this);
+    ClearDefaultActionsLocked();
+  }
+
+  // Returns the ON_CALL spec that matches this mock function with the
+  // given arguments; returns NULL if no matching ON_CALL is found.
+  // L = *
+  const OnCallSpec<F>* FindOnCallSpec(
+      const ArgumentTuple& args) const {
+    for (UntypedOnCallSpecs::const_reverse_iterator it
+             = untyped_on_call_specs_.rbegin();
+         it != untyped_on_call_specs_.rend(); ++it) {
+      const OnCallSpec<F>* spec = static_cast<const OnCallSpec<F>*>(*it);
+      if (spec->Matches(args))
+        return spec;
+    }
+
+    return nullptr;
+  }
+
+  // Performs the default action of this mock function on the given
+  // arguments and returns the result. Asserts (or throws if
+  // exceptions are enabled) with a helpful call descrption if there
+  // is no valid return value. This method doesn't depend on the
+  // mutable state of this object, and thus can be called concurrently
+  // without locking.
+  // L = *
+  Result PerformDefaultAction(ArgumentTuple&& args,
+                              const std::string& call_description) const {
+    const OnCallSpec<F>* const spec =
+        this->FindOnCallSpec(args);
+    if (spec != nullptr) {
+      return spec->GetAction().Perform(std::move(args));
+    }
+    const std::string message =
+        call_description +
+        "\n    The mock function has no default action "
+        "set, and its return type has no default value set.";
+#if GTEST_HAS_EXCEPTIONS
+    if (!DefaultValue<Result>::Exists()) {
+      throw std::runtime_error(message);
+    }
+#else
+    Assert(DefaultValue<Result>::Exists(), "", -1, message);
+#endif
+    return DefaultValue<Result>::Get();
+  }
+
+  // Performs the default action with the given arguments and returns
+  // the action's result.  The call description string will be used in
+  // the error message to describe the call in the case the default
+  // action fails.  The caller is responsible for deleting the result.
+  // L = *
+  UntypedActionResultHolderBase* UntypedPerformDefaultAction(
+      void* untyped_args,  // must point to an ArgumentTuple
+      const std::string& call_description) const override {
+    ArgumentTuple* args = static_cast<ArgumentTuple*>(untyped_args);
+    return ResultHolder::PerformDefaultAction(this, std::move(*args),
+                                              call_description);
+  }
+
+  // Performs the given action with the given arguments and returns
+  // the action's result.  The caller is responsible for deleting the
+  // result.
+  // L = *
+  UntypedActionResultHolderBase* UntypedPerformAction(
+      const void* untyped_action, void* untyped_args) const override {
+    // Make a copy of the action before performing it, in case the
+    // action deletes the mock object (and thus deletes itself).
+    const Action<F> action = *static_cast<const Action<F>*>(untyped_action);
+    ArgumentTuple* args = static_cast<ArgumentTuple*>(untyped_args);
+    return ResultHolder::PerformAction(action, std::move(*args));
+  }
+
+  // Implements UntypedFunctionMockerBase::ClearDefaultActionsLocked():
+  // clears the ON_CALL()s set on this mock function.
+  void ClearDefaultActionsLocked() override
+      GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+
+    // Deleting our default actions may trigger other mock objects to be
+    // deleted, for example if an action contains a reference counted smart
+    // pointer to that mock object, and that is the last reference. So if we
+    // delete our actions within the context of the global mutex we may deadlock
+    // when this method is called again. Instead, make a copy of the set of
+    // actions to delete, clear our set within the mutex, and then delete the
+    // actions outside of the mutex.
+    UntypedOnCallSpecs specs_to_delete;
+    untyped_on_call_specs_.swap(specs_to_delete);
+
+    g_gmock_mutex.Unlock();
+    for (UntypedOnCallSpecs::const_iterator it =
+             specs_to_delete.begin();
+         it != specs_to_delete.end(); ++it) {
+      delete static_cast<const OnCallSpec<F>*>(*it);
+    }
+
+    // Lock the mutex again, since the caller expects it to be locked when we
+    // return.
+    g_gmock_mutex.Lock();
+  }
+
+  // Returns the result of invoking this mock function with the given
+  // arguments.  This function can be safely called from multiple
+  // threads concurrently.
+  Result Invoke(Args... args) GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+    ArgumentTuple tuple(std::forward<Args>(args)...);
+    std::unique_ptr<ResultHolder> holder(DownCast_<ResultHolder*>(
+        this->UntypedInvokeWith(static_cast<void*>(&tuple))));
+    return holder->Unwrap();
+  }
+
+  MockSpec<F> With(Matcher<Args>... m) {
+    return MockSpec<F>(this, ::std::make_tuple(std::move(m)...));
+  }
+
+ protected:
+  template <typename Function>
+  friend class MockSpec;
+
+  typedef ActionResultHolder<Result> ResultHolder;
+
+  // Adds and returns a default action spec for this mock function.
+  OnCallSpec<F>& AddNewOnCallSpec(
+      const char* file, int line,
+      const ArgumentMatcherTuple& m)
+          GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+    Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line);
+    OnCallSpec<F>* const on_call_spec = new OnCallSpec<F>(file, line, m);
+    untyped_on_call_specs_.push_back(on_call_spec);
+    return *on_call_spec;
+  }
+
+  // Adds and returns an expectation spec for this mock function.
+  TypedExpectation<F>& AddNewExpectation(const char* file, int line,
+                                         const std::string& source_text,
+                                         const ArgumentMatcherTuple& m)
+      GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+    Mock::RegisterUseByOnCallOrExpectCall(MockObject(), file, line);
+    TypedExpectation<F>* const expectation =
+        new TypedExpectation<F>(this, file, line, source_text, m);
+    const std::shared_ptr<ExpectationBase> untyped_expectation(expectation);
+    // See the definition of untyped_expectations_ for why access to
+    // it is unprotected here.
+    untyped_expectations_.push_back(untyped_expectation);
+
+    // Adds this expectation into the implicit sequence if there is one.
+    Sequence* const implicit_sequence = g_gmock_implicit_sequence.get();
+    if (implicit_sequence != nullptr) {
+      implicit_sequence->AddExpectation(Expectation(untyped_expectation));
+    }
+
+    return *expectation;
+  }
+
+ private:
+  template <typename Func> friend class TypedExpectation;
+
+  // Some utilities needed for implementing UntypedInvokeWith().
+
+  // Describes what default action will be performed for the given
+  // arguments.
+  // L = *
+  void DescribeDefaultActionTo(const ArgumentTuple& args,
+                               ::std::ostream* os) const {
+    const OnCallSpec<F>* const spec = FindOnCallSpec(args);
+
+    if (spec == nullptr) {
+      *os << (std::is_void<Result>::value ? "returning directly.\n"
+                                          : "returning default value.\n");
+    } else {
+      *os << "taking default action specified at:\n"
+          << FormatFileLocation(spec->file(), spec->line()) << "\n";
+    }
+  }
+
+  // Writes a message that the call is uninteresting (i.e. neither
+  // explicitly expected nor explicitly unexpected) to the given
+  // ostream.
+  void UntypedDescribeUninterestingCall(const void* untyped_args,
+                                        ::std::ostream* os) const override
+      GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+    const ArgumentTuple& args =
+        *static_cast<const ArgumentTuple*>(untyped_args);
+    *os << "Uninteresting mock function call - ";
+    DescribeDefaultActionTo(args, os);
+    *os << "    Function call: " << Name();
+    UniversalPrint(args, os);
+  }
+
+  // Returns the expectation that matches the given function arguments
+  // (or NULL is there's no match); when a match is found,
+  // untyped_action is set to point to the action that should be
+  // performed (or NULL if the action is "do default"), and
+  // is_excessive is modified to indicate whether the call exceeds the
+  // expected number.
+  //
+  // Critical section: We must find the matching expectation and the
+  // corresponding action that needs to be taken in an ATOMIC
+  // transaction.  Otherwise another thread may call this mock
+  // method in the middle and mess up the state.
+  //
+  // However, performing the action has to be left out of the critical
+  // section.  The reason is that we have no control on what the
+  // action does (it can invoke an arbitrary user function or even a
+  // mock function) and excessive locking could cause a dead lock.
+  const ExpectationBase* UntypedFindMatchingExpectation(
+      const void* untyped_args, const void** untyped_action, bool* is_excessive,
+      ::std::ostream* what, ::std::ostream* why) override
+      GTEST_LOCK_EXCLUDED_(g_gmock_mutex) {
+    const ArgumentTuple& args =
+        *static_cast<const ArgumentTuple*>(untyped_args);
+    MutexLock l(&g_gmock_mutex);
+    TypedExpectation<F>* exp = this->FindMatchingExpectationLocked(args);
+    if (exp == nullptr) {  // A match wasn't found.
+      this->FormatUnexpectedCallMessageLocked(args, what, why);
+      return nullptr;
+    }
+
+    // This line must be done before calling GetActionForArguments(),
+    // which will increment the call count for *exp and thus affect
+    // its saturation status.
+    *is_excessive = exp->IsSaturated();
+    const Action<F>* action = exp->GetActionForArguments(this, args, what, why);
+    if (action != nullptr && action->IsDoDefault())
+      action = nullptr;  // Normalize "do default" to NULL.
+    *untyped_action = action;
+    return exp;
+  }
+
+  // Prints the given function arguments to the ostream.
+  void UntypedPrintArgs(const void* untyped_args,
+                        ::std::ostream* os) const override {
+    const ArgumentTuple& args =
+        *static_cast<const ArgumentTuple*>(untyped_args);
+    UniversalPrint(args, os);
+  }
+
+  // Returns the expectation that matches the arguments, or NULL if no
+  // expectation matches them.
+  TypedExpectation<F>* FindMatchingExpectationLocked(
+      const ArgumentTuple& args) const
+          GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    // See the definition of untyped_expectations_ for why access to
+    // it is unprotected here.
+    for (typename UntypedExpectations::const_reverse_iterator it =
+             untyped_expectations_.rbegin();
+         it != untyped_expectations_.rend(); ++it) {
+      TypedExpectation<F>* const exp =
+          static_cast<TypedExpectation<F>*>(it->get());
+      if (exp->ShouldHandleArguments(args)) {
+        return exp;
+      }
+    }
+    return nullptr;
+  }
+
+  // Returns a message that the arguments don't match any expectation.
+  void FormatUnexpectedCallMessageLocked(
+      const ArgumentTuple& args,
+      ::std::ostream* os,
+      ::std::ostream* why) const
+          GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    *os << "\nUnexpected mock function call - ";
+    DescribeDefaultActionTo(args, os);
+    PrintTriedExpectationsLocked(args, why);
+  }
+
+  // Prints a list of expectations that have been tried against the
+  // current mock function call.
+  void PrintTriedExpectationsLocked(
+      const ArgumentTuple& args,
+      ::std::ostream* why) const
+          GTEST_EXCLUSIVE_LOCK_REQUIRED_(g_gmock_mutex) {
+    g_gmock_mutex.AssertHeld();
+    const size_t count = untyped_expectations_.size();
+    *why << "Google Mock tried the following " << count << " "
+         << (count == 1 ? "expectation, but it didn't match" :
+             "expectations, but none matched")
+         << ":\n";
+    for (size_t i = 0; i < count; i++) {
+      TypedExpectation<F>* const expectation =
+          static_cast<TypedExpectation<F>*>(untyped_expectations_[i].get());
+      *why << "\n";
+      expectation->DescribeLocationTo(why);
+      if (count > 1) {
+        *why << "tried expectation #" << i << ": ";
+      }
+      *why << expectation->source_text() << "...\n";
+      expectation->ExplainMatchResultTo(args, why);
+      expectation->DescribeCallCountTo(why);
+    }
+  }
+};  // class FunctionMocker
+
+// Reports an uninteresting call (whose description is in msg) in the
+// manner specified by 'reaction'.
+void ReportUninterestingCall(CallReaction reaction, const std::string& msg);
+
+}  // namespace internal
+
+namespace internal {
+
+template <typename F>
+class MockFunction;
+
+template <typename R, typename... Args>
+class MockFunction<R(Args...)> {
+ public:
+  MockFunction(const MockFunction&) = delete;
+  MockFunction& operator=(const MockFunction&) = delete;
+
+  std::function<R(Args...)> AsStdFunction() {
+    return [this](Args... args) -> R {
+      return this->Call(std::forward<Args>(args)...);
+    };
+  }
+
+  // Implementation detail: the expansion of the MOCK_METHOD macro.
+  R Call(Args... args) {
+    mock_.SetOwnerAndName(this, "Call");
+    return mock_.Invoke(std::forward<Args>(args)...);
+  }
+
+  MockSpec<R(Args...)> gmock_Call(Matcher<Args>... m) {
+    mock_.RegisterOwner(this);
+    return mock_.With(std::move(m)...);
+  }
+
+  MockSpec<R(Args...)> gmock_Call(const WithoutMatchers&, R (*)(Args...)) {
+    return this->gmock_Call(::testing::A<Args>()...);
+  }
+
+ protected:
+  MockFunction() = default;
+  ~MockFunction() = default;
+
+ private:
+  FunctionMocker<R(Args...)> mock_;
+};
+
+/*
+The SignatureOf<F> struct is a meta-function returning function signature
+corresponding to the provided F argument.
+
+It makes use of MockFunction easier by allowing it to accept more F arguments
+than just function signatures.
+
+Specializations provided here cover a signature type itself and any template
+that can be parameterized with a signature, including std::function and
+boost::function.
+*/
+
+template <typename F, typename = void>
+struct SignatureOf;
+
+template <typename R, typename... Args>
+struct SignatureOf<R(Args...)> {
+  using type = R(Args...);
+};
+
+template <template <typename> class C, typename F>
+struct SignatureOf<C<F>,
+                   typename std::enable_if<std::is_function<F>::value>::type>
+    : SignatureOf<F> {};
+
+template <typename F>
+using SignatureOfT = typename SignatureOf<F>::type;
+
+}  // namespace internal
+
+// A MockFunction<F> type has one mock method whose type is
+// internal::SignatureOfT<F>.  It is useful when you just want your
+// test code to emit some messages and have Google Mock verify the
+// right messages are sent (and perhaps at the right times).  For
+// example, if you are exercising code:
+//
+//   Foo(1);
+//   Foo(2);
+//   Foo(3);
+//
+// and want to verify that Foo(1) and Foo(3) both invoke
+// mock.Bar("a"), but Foo(2) doesn't invoke anything, you can write:
+//
+// TEST(FooTest, InvokesBarCorrectly) {
+//   MyMock mock;
+//   MockFunction<void(string check_point_name)> check;
+//   {
+//     InSequence s;
+//
+//     EXPECT_CALL(mock, Bar("a"));
+//     EXPECT_CALL(check, Call("1"));
+//     EXPECT_CALL(check, Call("2"));
+//     EXPECT_CALL(mock, Bar("a"));
+//   }
+//   Foo(1);
+//   check.Call("1");
+//   Foo(2);
+//   check.Call("2");
+//   Foo(3);
+// }
+//
+// The expectation spec says that the first Bar("a") must happen
+// before check point "1", the second Bar("a") must happen after check
+// point "2", and nothing should happen between the two check
+// points. The explicit check points make it easy to tell which
+// Bar("a") is called by which call to Foo().
+//
+// MockFunction<F> can also be used to exercise code that accepts
+// std::function<internal::SignatureOfT<F>> callbacks. To do so, use
+// AsStdFunction() method to create std::function proxy forwarding to
+// original object's Call. Example:
+//
+// TEST(FooTest, RunsCallbackWithBarArgument) {
+//   MockFunction<int(string)> callback;
+//   EXPECT_CALL(callback, Call("bar")).WillOnce(Return(1));
+//   Foo(callback.AsStdFunction());
+// }
+//
+// The internal::SignatureOfT<F> indirection allows to use other types
+// than just function signature type. This is typically useful when
+// providing a mock for a predefined std::function type. Example:
+//
+// using FilterPredicate = std::function<bool(string)>;
+// void MyFilterAlgorithm(FilterPredicate predicate);
+//
+// TEST(FooTest, FilterPredicateAlwaysAccepts) {
+//   MockFunction<FilterPredicate> predicateMock;
+//   EXPECT_CALL(predicateMock, Call(_)).WillRepeatedly(Return(true));
+//   MyFilterAlgorithm(predicateMock.AsStdFunction());
+// }
+template <typename F>
+class MockFunction : public internal::MockFunction<internal::SignatureOfT<F>> {
+  using Base = internal::MockFunction<internal::SignatureOfT<F>>;
+
+ public:
+  using Base::Base;
+};
+
+// The style guide prohibits "using" statements in a namespace scope
+// inside a header file.  However, the MockSpec class template is
+// meant to be defined in the ::testing namespace.  The following line
+// is just a trick for working around a bug in MSVC 8.0, which cannot
+// handle it if we define MockSpec in ::testing.
+using internal::MockSpec;
+
+// Const(x) is a convenient function for obtaining a const reference
+// to x.  This is useful for setting expectations on an overloaded
+// const mock method, e.g.
+//
+//   class MockFoo : public FooInterface {
+//    public:
+//     MOCK_METHOD0(Bar, int());
+//     MOCK_CONST_METHOD0(Bar, int&());
+//   };
+//
+//   MockFoo foo;
+//   // Expects a call to non-const MockFoo::Bar().
+//   EXPECT_CALL(foo, Bar());
+//   // Expects a call to const MockFoo::Bar().
+//   EXPECT_CALL(Const(foo), Bar());
+template <typename T>
+inline const T& Const(const T& x) { return x; }
+
+// Constructs an Expectation object that references and co-owns exp.
+inline Expectation::Expectation(internal::ExpectationBase& exp)  // NOLINT
+    : expectation_base_(exp.GetHandle().expectation_base()) {}
+
+}  // namespace testing
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+// Implementation for ON_CALL and EXPECT_CALL macros. A separate macro is
+// required to avoid compile errors when the name of the method used in call is
+// a result of macro expansion. See CompilesWithMethodNameExpandedFromMacro
+// tests in internal/gmock-spec-builders_test.cc for more details.
+//
+// This macro supports statements both with and without parameter matchers. If
+// the parameter list is omitted, gMock will accept any parameters, which allows
+// tests to be written that don't need to encode the number of method
+// parameter. This technique may only be used for non-overloaded methods.
+//
+//   // These are the same:
+//   ON_CALL(mock, NoArgsMethod()).WillByDefault(...);
+//   ON_CALL(mock, NoArgsMethod).WillByDefault(...);
+//
+//   // As are these:
+//   ON_CALL(mock, TwoArgsMethod(_, _)).WillByDefault(...);
+//   ON_CALL(mock, TwoArgsMethod).WillByDefault(...);
+//
+//   // Can also specify args if you want, of course:
+//   ON_CALL(mock, TwoArgsMethod(_, 45)).WillByDefault(...);
+//
+//   // Overloads work as long as you specify parameters:
+//   ON_CALL(mock, OverloadedMethod(_)).WillByDefault(...);
+//   ON_CALL(mock, OverloadedMethod(_, _)).WillByDefault(...);
+//
+//   // Oops! Which overload did you want?
+//   ON_CALL(mock, OverloadedMethod).WillByDefault(...);
+//     => ERROR: call to member function 'gmock_OverloadedMethod' is ambiguous
+//
+// How this works: The mock class uses two overloads of the gmock_Method
+// expectation setter method plus an operator() overload on the MockSpec object.
+// In the matcher list form, the macro expands to:
+//
+//   // This statement:
+//   ON_CALL(mock, TwoArgsMethod(_, 45))...
+//
+//   // ...expands to:
+//   mock.gmock_TwoArgsMethod(_, 45)(WithoutMatchers(), nullptr)...
+//   |-------------v---------------||------------v-------------|
+//       invokes first overload        swallowed by operator()
+//
+//   // ...which is essentially:
+//   mock.gmock_TwoArgsMethod(_, 45)...
+//
+// Whereas the form without a matcher list:
+//
+//   // This statement:
+//   ON_CALL(mock, TwoArgsMethod)...
+//
+//   // ...expands to:
+//   mock.gmock_TwoArgsMethod(WithoutMatchers(), nullptr)...
+//   |-----------------------v--------------------------|
+//                 invokes second overload
+//
+//   // ...which is essentially:
+//   mock.gmock_TwoArgsMethod(_, _)...
+//
+// The WithoutMatchers() argument is used to disambiguate overloads and to
+// block the caller from accidentally invoking the second overload directly. The
+// second argument is an internal type derived from the method signature. The
+// failure to disambiguate two overloads of this method in the ON_CALL statement
+// is how we block callers from setting expectations on overloaded methods.
+#define GMOCK_ON_CALL_IMPL_(mock_expr, Setter, call)                    \
+  ((mock_expr).gmock_##call)(::testing::internal::GetWithoutMatchers(), \
+                             nullptr)                                   \
+      .Setter(__FILE__, __LINE__, #mock_expr, #call)
+
+#define ON_CALL(obj, call) \
+  GMOCK_ON_CALL_IMPL_(obj, InternalDefaultActionSetAt, call)
+
+#define EXPECT_CALL(obj, call) \
+  GMOCK_ON_CALL_IMPL_(obj, InternalExpectedAt, call)
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_SPEC_BUILDERS_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/gmock.h
new file mode 100644 (file)
index 0000000..13e435f
--- /dev/null
@@ -0,0 +1,96 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This is the main header file a user should include.
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_H_
+
+// This file implements the following syntax:
+//
+//   ON_CALL(mock_object, Method(...))
+//     .With(...) ?
+//     .WillByDefault(...);
+//
+// where With() is optional and WillByDefault() must appear exactly
+// once.
+//
+//   EXPECT_CALL(mock_object, Method(...))
+//     .With(...) ?
+//     .Times(...) ?
+//     .InSequence(...) *
+//     .WillOnce(...) *
+//     .WillRepeatedly(...) ?
+//     .RetiresOnSaturation() ? ;
+//
+// where all clauses are optional and WillOnce() can be repeated.
+
+#include "gmock/gmock-actions.h"
+#include "gmock/gmock-cardinalities.h"
+#include "gmock/gmock-function-mocker.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock-more-actions.h"
+#include "gmock/gmock-more-matchers.h"
+#include "gmock/gmock-nice-strict.h"
+#include "gmock/internal/gmock-internal-utils.h"
+
+namespace testing {
+
+// Declares Google Mock flags that we want a user to use programmatically.
+GMOCK_DECLARE_bool_(catch_leaked_mocks);
+GMOCK_DECLARE_string_(verbose);
+GMOCK_DECLARE_int32_(default_mock_behavior);
+
+// Initializes Google Mock.  This must be called before running the
+// tests.  In particular, it parses the command line for the flags
+// that Google Mock recognizes.  Whenever a Google Mock flag is seen,
+// it is removed from argv, and *argc is decremented.
+//
+// No value is returned.  Instead, the Google Mock flag variables are
+// updated.
+//
+// Since Google Test is needed for Google Mock to work, this function
+// also initializes Google Test and parses its flags, if that hasn't
+// been done.
+GTEST_API_ void InitGoogleMock(int* argc, char** argv);
+
+// This overloaded version can be used in Windows programs compiled in
+// UNICODE mode.
+GTEST_API_ void InitGoogleMock(int* argc, wchar_t** argv);
+
+// This overloaded version can be used on Arduino/embedded platforms where
+// there is no argc/argv.
+GTEST_API_ void InitGoogleMock();
+
+}  // namespace testing
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_GMOCK_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/internal/custom/gmock-generated-actions.h
new file mode 100644 (file)
index 0000000..39ddeac
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_GENERATED_ACTIONS_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/internal/custom/gmock-matchers.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/internal/custom/gmock-matchers.h
new file mode 100644 (file)
index 0000000..f826117
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Injection point for custom user configurations. See README for details
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_MATCHERS_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/internal/custom/gmock-port.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/internal/custom/gmock-port.h
new file mode 100644 (file)
index 0000000..db03ed2
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Injection point for custom user configurations. See README for details
+//
+// ** Custom implementation starts here **
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_CUSTOM_GMOCK_PORT_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/internal/gmock-internal-utils.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/internal/gmock-internal-utils.h
new file mode 100644 (file)
index 0000000..99a2340
--- /dev/null
@@ -0,0 +1,457 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file defines some utilities useful for implementing Google
+// Mock.  They are subject to change without notice, so please DO NOT
+// USE THEM IN USER CODE.
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
+
+#include <stdio.h>
+#include <ostream>  // NOLINT
+#include <string>
+#include <type_traits>
+#include "gmock/internal/gmock-port.h"
+#include "gtest/gtest.h"
+
+namespace testing {
+
+template <typename>
+class Matcher;
+
+namespace internal {
+
+// Silence MSVC C4100 (unreferenced formal parameter) and
+// C4805('==': unsafe mix of type 'const int' and type 'const bool')
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4100)
+# pragma warning(disable:4805)
+#endif
+
+// Joins a vector of strings as if they are fields of a tuple; returns
+// the joined string.
+GTEST_API_ std::string JoinAsTuple(const Strings& fields);
+
+// Converts an identifier name to a space-separated list of lower-case
+// words.  Each maximum substring of the form [A-Za-z][a-z]*|\d+ is
+// treated as one word.  For example, both "FooBar123" and
+// "foo_bar_123" are converted to "foo bar 123".
+GTEST_API_ std::string ConvertIdentifierNameToWords(const char* id_name);
+
+// GetRawPointer(p) returns the raw pointer underlying p when p is a
+// smart pointer, or returns p itself when p is already a raw pointer.
+// The following default implementation is for the smart pointer case.
+template <typename Pointer>
+inline const typename Pointer::element_type* GetRawPointer(const Pointer& p) {
+  return p.get();
+}
+// This overloaded version is for the raw pointer case.
+template <typename Element>
+inline Element* GetRawPointer(Element* p) { return p; }
+
+// MSVC treats wchar_t as a native type usually, but treats it as the
+// same as unsigned short when the compiler option /Zc:wchar_t- is
+// specified.  It defines _NATIVE_WCHAR_T_DEFINED symbol when wchar_t
+// is a native type.
+#if defined(_MSC_VER) && !defined(_NATIVE_WCHAR_T_DEFINED)
+// wchar_t is a typedef.
+#else
+# define GMOCK_WCHAR_T_IS_NATIVE_ 1
+#endif
+
+// In what follows, we use the term "kind" to indicate whether a type
+// is bool, an integer type (excluding bool), a floating-point type,
+// or none of them.  This categorization is useful for determining
+// when a matcher argument type can be safely converted to another
+// type in the implementation of SafeMatcherCast.
+enum TypeKind {
+  kBool, kInteger, kFloatingPoint, kOther
+};
+
+// KindOf<T>::value is the kind of type T.
+template <typename T> struct KindOf {
+  enum { value = kOther };  // The default kind.
+};
+
+// This macro declares that the kind of 'type' is 'kind'.
+#define GMOCK_DECLARE_KIND_(type, kind) \
+  template <> struct KindOf<type> { enum { value = kind }; }
+
+GMOCK_DECLARE_KIND_(bool, kBool);
+
+// All standard integer types.
+GMOCK_DECLARE_KIND_(char, kInteger);
+GMOCK_DECLARE_KIND_(signed char, kInteger);
+GMOCK_DECLARE_KIND_(unsigned char, kInteger);
+GMOCK_DECLARE_KIND_(short, kInteger);  // NOLINT
+GMOCK_DECLARE_KIND_(unsigned short, kInteger);  // NOLINT
+GMOCK_DECLARE_KIND_(int, kInteger);
+GMOCK_DECLARE_KIND_(unsigned int, kInteger);
+GMOCK_DECLARE_KIND_(long, kInteger);  // NOLINT
+GMOCK_DECLARE_KIND_(unsigned long, kInteger);  // NOLINT
+GMOCK_DECLARE_KIND_(long long, kInteger);  // NOLINT
+GMOCK_DECLARE_KIND_(unsigned long long, kInteger);  // NOLINT
+
+#if GMOCK_WCHAR_T_IS_NATIVE_
+GMOCK_DECLARE_KIND_(wchar_t, kInteger);
+#endif
+
+// All standard floating-point types.
+GMOCK_DECLARE_KIND_(float, kFloatingPoint);
+GMOCK_DECLARE_KIND_(double, kFloatingPoint);
+GMOCK_DECLARE_KIND_(long double, kFloatingPoint);
+
+#undef GMOCK_DECLARE_KIND_
+
+// Evaluates to the kind of 'type'.
+#define GMOCK_KIND_OF_(type) \
+  static_cast< ::testing::internal::TypeKind>( \
+      ::testing::internal::KindOf<type>::value)
+
+// LosslessArithmeticConvertibleImpl<kFromKind, From, kToKind, To>::value
+// is true if and only if arithmetic type From can be losslessly converted to
+// arithmetic type To.
+//
+// It's the user's responsibility to ensure that both From and To are
+// raw (i.e. has no CV modifier, is not a pointer, and is not a
+// reference) built-in arithmetic types, kFromKind is the kind of
+// From, and kToKind is the kind of To; the value is
+// implementation-defined when the above pre-condition is violated.
+template <TypeKind kFromKind, typename From, TypeKind kToKind, typename To>
+using LosslessArithmeticConvertibleImpl = std::integral_constant<
+    bool,
+    // clang-format off
+      // Converting from bool is always lossless
+      (kFromKind == kBool) ? true
+      // Converting between any other type kinds will be lossy if the type
+      // kinds are not the same.
+    : (kFromKind != kToKind) ? false
+    : (kFromKind == kInteger &&
+       // Converting between integers of different widths is allowed so long
+       // as the conversion does not go from signed to unsigned.
+      (((sizeof(From) < sizeof(To)) &&
+        !(std::is_signed<From>::value && !std::is_signed<To>::value)) ||
+       // Converting between integers of the same width only requires the
+       // two types to have the same signedness.
+       ((sizeof(From) == sizeof(To)) &&
+        (std::is_signed<From>::value == std::is_signed<To>::value)))
+       ) ? true
+      // Floating point conversions are lossless if and only if `To` is at least
+      // as wide as `From`.
+    : (kFromKind == kFloatingPoint && (sizeof(From) <= sizeof(To))) ? true
+    : false
+    // clang-format on
+    >;
+
+// LosslessArithmeticConvertible<From, To>::value is true if and only if
+// arithmetic type From can be losslessly converted to arithmetic type To.
+//
+// It's the user's responsibility to ensure that both From and To are
+// raw (i.e. has no CV modifier, is not a pointer, and is not a
+// reference) built-in arithmetic types; the value is
+// implementation-defined when the above pre-condition is violated.
+template <typename From, typename To>
+using LosslessArithmeticConvertible =
+    LosslessArithmeticConvertibleImpl<GMOCK_KIND_OF_(From), From,
+                                      GMOCK_KIND_OF_(To), To>;
+
+// This interface knows how to report a Google Mock failure (either
+// non-fatal or fatal).
+class FailureReporterInterface {
+ public:
+  // The type of a failure (either non-fatal or fatal).
+  enum FailureType {
+    kNonfatal, kFatal
+  };
+
+  virtual ~FailureReporterInterface() {}
+
+  // Reports a failure that occurred at the given source file location.
+  virtual void ReportFailure(FailureType type, const char* file, int line,
+                             const std::string& message) = 0;
+};
+
+// Returns the failure reporter used by Google Mock.
+GTEST_API_ FailureReporterInterface* GetFailureReporter();
+
+// Asserts that condition is true; aborts the process with the given
+// message if condition is false.  We cannot use LOG(FATAL) or CHECK()
+// as Google Mock might be used to mock the log sink itself.  We
+// inline this function to prevent it from showing up in the stack
+// trace.
+inline void Assert(bool condition, const char* file, int line,
+                   const std::string& msg) {
+  if (!condition) {
+    GetFailureReporter()->ReportFailure(FailureReporterInterface::kFatal,
+                                        file, line, msg);
+  }
+}
+inline void Assert(bool condition, const char* file, int line) {
+  Assert(condition, file, line, "Assertion failed.");
+}
+
+// Verifies that condition is true; generates a non-fatal failure if
+// condition is false.
+inline void Expect(bool condition, const char* file, int line,
+                   const std::string& msg) {
+  if (!condition) {
+    GetFailureReporter()->ReportFailure(FailureReporterInterface::kNonfatal,
+                                        file, line, msg);
+  }
+}
+inline void Expect(bool condition, const char* file, int line) {
+  Expect(condition, file, line, "Expectation failed.");
+}
+
+// Severity level of a log.
+enum LogSeverity {
+  kInfo = 0,
+  kWarning = 1
+};
+
+// Valid values for the --gmock_verbose flag.
+
+// All logs (informational and warnings) are printed.
+const char kInfoVerbosity[] = "info";
+// Only warnings are printed.
+const char kWarningVerbosity[] = "warning";
+// No logs are printed.
+const char kErrorVerbosity[] = "error";
+
+// Returns true if and only if a log with the given severity is visible
+// according to the --gmock_verbose flag.
+GTEST_API_ bool LogIsVisible(LogSeverity severity);
+
+// Prints the given message to stdout if and only if 'severity' >= the level
+// specified by the --gmock_verbose flag.  If stack_frames_to_skip >=
+// 0, also prints the stack trace excluding the top
+// stack_frames_to_skip frames.  In opt mode, any positive
+// stack_frames_to_skip is treated as 0, since we don't know which
+// function calls will be inlined by the compiler and need to be
+// conservative.
+GTEST_API_ void Log(LogSeverity severity, const std::string& message,
+                    int stack_frames_to_skip);
+
+// A marker class that is used to resolve parameterless expectations to the
+// correct overload. This must not be instantiable, to prevent client code from
+// accidentally resolving to the overload; for example:
+//
+//    ON_CALL(mock, Method({}, nullptr))...
+//
+class WithoutMatchers {
+ private:
+  WithoutMatchers() {}
+  friend GTEST_API_ WithoutMatchers GetWithoutMatchers();
+};
+
+// Internal use only: access the singleton instance of WithoutMatchers.
+GTEST_API_ WithoutMatchers GetWithoutMatchers();
+
+// Disable MSVC warnings for infinite recursion, since in this case the
+// the recursion is unreachable.
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4717)
+#endif
+
+// Invalid<T>() is usable as an expression of type T, but will terminate
+// the program with an assertion failure if actually run.  This is useful
+// when a value of type T is needed for compilation, but the statement
+// will not really be executed (or we don't care if the statement
+// crashes).
+template <typename T>
+inline T Invalid() {
+  Assert(false, "", -1, "Internal error: attempt to return invalid value");
+  // This statement is unreachable, and would never terminate even if it
+  // could be reached. It is provided only to placate compiler warnings
+  // about missing return statements.
+  return Invalid<T>();
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+// Given a raw type (i.e. having no top-level reference or const
+// modifier) RawContainer that's either an STL-style container or a
+// native array, class StlContainerView<RawContainer> has the
+// following members:
+//
+//   - type is a type that provides an STL-style container view to
+//     (i.e. implements the STL container concept for) RawContainer;
+//   - const_reference is a type that provides a reference to a const
+//     RawContainer;
+//   - ConstReference(raw_container) returns a const reference to an STL-style
+//     container view to raw_container, which is a RawContainer.
+//   - Copy(raw_container) returns an STL-style container view of a
+//     copy of raw_container, which is a RawContainer.
+//
+// This generic version is used when RawContainer itself is already an
+// STL-style container.
+template <class RawContainer>
+class StlContainerView {
+ public:
+  typedef RawContainer type;
+  typedef const type& const_reference;
+
+  static const_reference ConstReference(const RawContainer& container) {
+    static_assert(!std::is_const<RawContainer>::value,
+                  "RawContainer type must not be const");
+    return container;
+  }
+  static type Copy(const RawContainer& container) { return container; }
+};
+
+// This specialization is used when RawContainer is a native array type.
+template <typename Element, size_t N>
+class StlContainerView<Element[N]> {
+ public:
+  typedef typename std::remove_const<Element>::type RawElement;
+  typedef internal::NativeArray<RawElement> type;
+  // NativeArray<T> can represent a native array either by value or by
+  // reference (selected by a constructor argument), so 'const type'
+  // can be used to reference a const native array.  We cannot
+  // 'typedef const type& const_reference' here, as that would mean
+  // ConstReference() has to return a reference to a local variable.
+  typedef const type const_reference;
+
+  static const_reference ConstReference(const Element (&array)[N]) {
+    static_assert(std::is_same<Element, RawElement>::value,
+                  "Element type must not be const");
+    return type(array, N, RelationToSourceReference());
+  }
+  static type Copy(const Element (&array)[N]) {
+    return type(array, N, RelationToSourceCopy());
+  }
+};
+
+// This specialization is used when RawContainer is a native array
+// represented as a (pointer, size) tuple.
+template <typename ElementPointer, typename Size>
+class StlContainerView< ::std::tuple<ElementPointer, Size> > {
+ public:
+  typedef typename std::remove_const<
+      typename std::pointer_traits<ElementPointer>::element_type>::type
+      RawElement;
+  typedef internal::NativeArray<RawElement> type;
+  typedef const type const_reference;
+
+  static const_reference ConstReference(
+      const ::std::tuple<ElementPointer, Size>& array) {
+    return type(std::get<0>(array), std::get<1>(array),
+                RelationToSourceReference());
+  }
+  static type Copy(const ::std::tuple<ElementPointer, Size>& array) {
+    return type(std::get<0>(array), std::get<1>(array), RelationToSourceCopy());
+  }
+};
+
+// The following specialization prevents the user from instantiating
+// StlContainer with a reference type.
+template <typename T> class StlContainerView<T&>;
+
+// A type transform to remove constness from the first part of a pair.
+// Pairs like that are used as the value_type of associative containers,
+// and this transform produces a similar but assignable pair.
+template <typename T>
+struct RemoveConstFromKey {
+  typedef T type;
+};
+
+// Partially specialized to remove constness from std::pair<const K, V>.
+template <typename K, typename V>
+struct RemoveConstFromKey<std::pair<const K, V> > {
+  typedef std::pair<K, V> type;
+};
+
+// Emit an assertion failure due to incorrect DoDefault() usage. Out-of-lined to
+// reduce code size.
+GTEST_API_ void IllegalDoDefault(const char* file, int line);
+
+template <typename F, typename Tuple, size_t... Idx>
+auto ApplyImpl(F&& f, Tuple&& args, IndexSequence<Idx...>) -> decltype(
+    std::forward<F>(f)(std::get<Idx>(std::forward<Tuple>(args))...)) {
+  return std::forward<F>(f)(std::get<Idx>(std::forward<Tuple>(args))...);
+}
+
+// Apply the function to a tuple of arguments.
+template <typename F, typename Tuple>
+auto Apply(F&& f, Tuple&& args) -> decltype(
+    ApplyImpl(std::forward<F>(f), std::forward<Tuple>(args),
+              MakeIndexSequence<std::tuple_size<
+                  typename std::remove_reference<Tuple>::type>::value>())) {
+  return ApplyImpl(std::forward<F>(f), std::forward<Tuple>(args),
+                   MakeIndexSequence<std::tuple_size<
+                       typename std::remove_reference<Tuple>::type>::value>());
+}
+
+// Template struct Function<F>, where F must be a function type, contains
+// the following typedefs:
+//
+//   Result:               the function's return type.
+//   Arg<N>:               the type of the N-th argument, where N starts with 0.
+//   ArgumentTuple:        the tuple type consisting of all parameters of F.
+//   ArgumentMatcherTuple: the tuple type consisting of Matchers for all
+//                         parameters of F.
+//   MakeResultVoid:       the function type obtained by substituting void
+//                         for the return type of F.
+//   MakeResultIgnoredValue:
+//                         the function type obtained by substituting Something
+//                         for the return type of F.
+template <typename T>
+struct Function;
+
+template <typename R, typename... Args>
+struct Function<R(Args...)> {
+  using Result = R;
+  static constexpr size_t ArgumentCount = sizeof...(Args);
+  template <size_t I>
+  using Arg = ElemFromList<I, Args...>;
+  using ArgumentTuple = std::tuple<Args...>;
+  using ArgumentMatcherTuple = std::tuple<Matcher<Args>...>;
+  using MakeResultVoid = void(Args...);
+  using MakeResultIgnoredValue = IgnoredValue(Args...);
+};
+
+template <typename R, typename... Args>
+constexpr size_t Function<R(Args...)>::ArgumentCount;
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_INTERNAL_UTILS_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/internal/gmock-port.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/internal/gmock-port.h
new file mode 100644 (file)
index 0000000..d2d1b74
--- /dev/null
@@ -0,0 +1,85 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Low-level types and utilities for porting Google Mock to various
+// platforms.  All macros ending with _ and symbols defined in an
+// internal namespace are subject to change without notice.  Code
+// outside Google Mock MUST NOT USE THEM DIRECTLY.  Macros that don't
+// end with _ are part of Google Mock's public API and can be used by
+// code outside Google Mock.
+
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
+
+#include <assert.h>
+#include <stdlib.h>
+#include <cstdint>
+#include <iostream>
+
+// Most of the utilities needed for porting Google Mock are also
+// required for Google Test and are defined in gtest-port.h.
+//
+// Note to maintainers: to reduce code duplication, prefer adding
+// portability utilities to Google Test's gtest-port.h instead of
+// here, as Google Mock depends on Google Test.  Only add a utility
+// here if it's truly specific to Google Mock.
+
+#include "gtest/internal/gtest-port.h"
+#include "gmock/internal/custom/gmock-port.h"
+
+// For MS Visual C++, check the compiler version. At least VS 2015 is
+// required to compile Google Mock.
+#if defined(_MSC_VER) && _MSC_VER < 1900
+# error "At least Visual C++ 2015 (14.0) is required to compile Google Mock."
+#endif
+
+// Macro for referencing flags.  This is public as we want the user to
+// use this syntax to reference Google Mock flags.
+#define GMOCK_FLAG(name) FLAGS_gmock_##name
+
+#if !defined(GMOCK_DECLARE_bool_)
+
+// Macros for declaring flags.
+# define GMOCK_DECLARE_bool_(name) extern GTEST_API_ bool GMOCK_FLAG(name)
+# define GMOCK_DECLARE_int32_(name) extern GTEST_API_ int32_t GMOCK_FLAG(name)
+# define GMOCK_DECLARE_string_(name) \
+    extern GTEST_API_ ::std::string GMOCK_FLAG(name)
+
+// Macros for defining flags.
+# define GMOCK_DEFINE_bool_(name, default_val, doc) \
+    GTEST_API_ bool GMOCK_FLAG(name) = (default_val)
+# define GMOCK_DEFINE_int32_(name, default_val, doc) \
+    GTEST_API_ int32_t GMOCK_FLAG(name) = (default_val)
+# define GMOCK_DEFINE_string_(name, default_val, doc) \
+    GTEST_API_ ::std::string GMOCK_FLAG(name) = (default_val)
+
+#endif  // !defined(GMOCK_DECLARE_bool_)
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PORT_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/include/gmock/internal/gmock-pp.h b/src/include/gromacs/external/googletest/googlemock/include/gmock/internal/gmock-pp.h
new file mode 100644 (file)
index 0000000..94d61c0
--- /dev/null
@@ -0,0 +1,279 @@
+#ifndef GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PP_H_
+#define GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PP_H_
+
+// Expands and concatenates the arguments. Constructed macros reevaluate.
+#define GMOCK_PP_CAT(_1, _2) GMOCK_PP_INTERNAL_CAT(_1, _2)
+
+// Expands and stringifies the only argument.
+#define GMOCK_PP_STRINGIZE(...) GMOCK_PP_INTERNAL_STRINGIZE(__VA_ARGS__)
+
+// Returns empty. Given a variadic number of arguments.
+#define GMOCK_PP_EMPTY(...)
+
+// Returns a comma. Given a variadic number of arguments.
+#define GMOCK_PP_COMMA(...) ,
+
+// Returns the only argument.
+#define GMOCK_PP_IDENTITY(_1) _1
+
+// Evaluates to the number of arguments after expansion.
+//
+//   #define PAIR x, y
+//
+//   GMOCK_PP_NARG() => 1
+//   GMOCK_PP_NARG(x) => 1
+//   GMOCK_PP_NARG(x, y) => 2
+//   GMOCK_PP_NARG(PAIR) => 2
+//
+// Requires: the number of arguments after expansion is at most 15.
+#define GMOCK_PP_NARG(...) \
+  GMOCK_PP_INTERNAL_16TH(  \
+      (__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
+
+// Returns 1 if the expansion of arguments has an unprotected comma. Otherwise
+// returns 0. Requires no more than 15 unprotected commas.
+#define GMOCK_PP_HAS_COMMA(...) \
+  GMOCK_PP_INTERNAL_16TH(       \
+      (__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0))
+
+// Returns the first argument.
+#define GMOCK_PP_HEAD(...) GMOCK_PP_INTERNAL_HEAD((__VA_ARGS__, unusedArg))
+
+// Returns the tail. A variadic list of all arguments minus the first. Requires
+// at least one argument.
+#define GMOCK_PP_TAIL(...) GMOCK_PP_INTERNAL_TAIL((__VA_ARGS__))
+
+// Calls CAT(_Macro, NARG(__VA_ARGS__))(__VA_ARGS__)
+#define GMOCK_PP_VARIADIC_CALL(_Macro, ...) \
+  GMOCK_PP_IDENTITY(                        \
+      GMOCK_PP_CAT(_Macro, GMOCK_PP_NARG(__VA_ARGS__))(__VA_ARGS__))
+
+// If the arguments after expansion have no tokens, evaluates to `1`. Otherwise
+// evaluates to `0`.
+//
+// Requires: * the number of arguments after expansion is at most 15.
+//           * If the argument is a macro, it must be able to be called with one
+//             argument.
+//
+// Implementation details:
+//
+// There is one case when it generates a compile error: if the argument is macro
+// that cannot be called with one argument.
+//
+//   #define M(a, b)  // it doesn't matter what it expands to
+//
+//   // Expected: expands to `0`.
+//   // Actual: compile error.
+//   GMOCK_PP_IS_EMPTY(M)
+//
+// There are 4 cases tested:
+//
+// * __VA_ARGS__ possible expansion has no unparen'd commas. Expected 0.
+// * __VA_ARGS__ possible expansion is not enclosed in parenthesis. Expected 0.
+// * __VA_ARGS__ possible expansion is not a macro that ()-evaluates to a comma.
+//   Expected 0
+// * __VA_ARGS__ is empty, or has unparen'd commas, or is enclosed in
+//   parenthesis, or is a macro that ()-evaluates to comma. Expected 1.
+//
+// We trigger detection on '0001', i.e. on empty.
+#define GMOCK_PP_IS_EMPTY(...)                                               \
+  GMOCK_PP_INTERNAL_IS_EMPTY(GMOCK_PP_HAS_COMMA(__VA_ARGS__),                \
+                             GMOCK_PP_HAS_COMMA(GMOCK_PP_COMMA __VA_ARGS__), \
+                             GMOCK_PP_HAS_COMMA(__VA_ARGS__()),              \
+                             GMOCK_PP_HAS_COMMA(GMOCK_PP_COMMA __VA_ARGS__()))
+
+// Evaluates to _Then if _Cond is 1 and _Else if _Cond is 0.
+#define GMOCK_PP_IF(_Cond, _Then, _Else) \
+  GMOCK_PP_CAT(GMOCK_PP_INTERNAL_IF_, _Cond)(_Then, _Else)
+
+// Similar to GMOCK_PP_IF but takes _Then and _Else in parentheses.
+//
+// GMOCK_PP_GENERIC_IF(1, (a, b, c), (d, e, f)) => a, b, c
+// GMOCK_PP_GENERIC_IF(0, (a, b, c), (d, e, f)) => d, e, f
+//
+#define GMOCK_PP_GENERIC_IF(_Cond, _Then, _Else) \
+  GMOCK_PP_REMOVE_PARENS(GMOCK_PP_IF(_Cond, _Then, _Else))
+
+// Evaluates to the number of arguments after expansion. Identifies 'empty' as
+// 0.
+//
+//   #define PAIR x, y
+//
+//   GMOCK_PP_NARG0() => 0
+//   GMOCK_PP_NARG0(x) => 1
+//   GMOCK_PP_NARG0(x, y) => 2
+//   GMOCK_PP_NARG0(PAIR) => 2
+//
+// Requires: * the number of arguments after expansion is at most 15.
+//           * If the argument is a macro, it must be able to be called with one
+//             argument.
+#define GMOCK_PP_NARG0(...) \
+  GMOCK_PP_IF(GMOCK_PP_IS_EMPTY(__VA_ARGS__), 0, GMOCK_PP_NARG(__VA_ARGS__))
+
+// Expands to 1 if the first argument starts with something in parentheses,
+// otherwise to 0.
+#define GMOCK_PP_IS_BEGIN_PARENS(...)                              \
+  GMOCK_PP_HEAD(GMOCK_PP_CAT(GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_, \
+                             GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C __VA_ARGS__))
+
+// Expands to 1 is there is only one argument and it is enclosed in parentheses.
+#define GMOCK_PP_IS_ENCLOSED_PARENS(...)             \
+  GMOCK_PP_IF(GMOCK_PP_IS_BEGIN_PARENS(__VA_ARGS__), \
+              GMOCK_PP_IS_EMPTY(GMOCK_PP_EMPTY __VA_ARGS__), 0)
+
+// Remove the parens, requires GMOCK_PP_IS_ENCLOSED_PARENS(args) => 1.
+#define GMOCK_PP_REMOVE_PARENS(...) GMOCK_PP_INTERNAL_REMOVE_PARENS __VA_ARGS__
+
+// Expands to _Macro(0, _Data, e1) _Macro(1, _Data, e2) ... _Macro(K -1, _Data,
+// eK) as many of GMOCK_INTERNAL_NARG0 _Tuple.
+// Requires: * |_Macro| can be called with 3 arguments.
+//           * |_Tuple| expansion has no more than 15 elements.
+#define GMOCK_PP_FOR_EACH(_Macro, _Data, _Tuple)                        \
+  GMOCK_PP_CAT(GMOCK_PP_INTERNAL_FOR_EACH_IMPL_, GMOCK_PP_NARG0 _Tuple) \
+  (0, _Macro, _Data, _Tuple)
+
+// Expands to _Macro(0, _Data, ) _Macro(1, _Data, ) ... _Macro(K - 1, _Data, )
+// Empty if _K = 0.
+// Requires: * |_Macro| can be called with 3 arguments.
+//           * |_K| literal between 0 and 15
+#define GMOCK_PP_REPEAT(_Macro, _Data, _N)           \
+  GMOCK_PP_CAT(GMOCK_PP_INTERNAL_FOR_EACH_IMPL_, _N) \
+  (0, _Macro, _Data, GMOCK_PP_INTENRAL_EMPTY_TUPLE)
+
+// Increments the argument, requires the argument to be between 0 and 15.
+#define GMOCK_PP_INC(_i) GMOCK_PP_CAT(GMOCK_PP_INTERNAL_INC_, _i)
+
+// Returns comma if _i != 0. Requires _i to be between 0 and 15.
+#define GMOCK_PP_COMMA_IF(_i) GMOCK_PP_CAT(GMOCK_PP_INTERNAL_COMMA_IF_, _i)
+
+// Internal details follow. Do not use any of these symbols outside of this
+// file or we will break your code.
+#define GMOCK_PP_INTENRAL_EMPTY_TUPLE (, , , , , , , , , , , , , , , )
+#define GMOCK_PP_INTERNAL_CAT(_1, _2) _1##_2
+#define GMOCK_PP_INTERNAL_STRINGIZE(...) #__VA_ARGS__
+#define GMOCK_PP_INTERNAL_CAT_5(_1, _2, _3, _4, _5) _1##_2##_3##_4##_5
+#define GMOCK_PP_INTERNAL_IS_EMPTY(_1, _2, _3, _4)                             \
+  GMOCK_PP_HAS_COMMA(GMOCK_PP_INTERNAL_CAT_5(GMOCK_PP_INTERNAL_IS_EMPTY_CASE_, \
+                                             _1, _2, _3, _4))
+#define GMOCK_PP_INTERNAL_IS_EMPTY_CASE_0001 ,
+#define GMOCK_PP_INTERNAL_IF_1(_Then, _Else) _Then
+#define GMOCK_PP_INTERNAL_IF_0(_Then, _Else) _Else
+
+// Because of MSVC treating a token with a comma in it as a single token when
+// passed to another macro, we need to force it to evaluate it as multiple
+// tokens. We do that by using a "IDENTITY(MACRO PARENTHESIZED_ARGS)" macro. We
+// define one per possible macro that relies on this behavior. Note "_Args" must
+// be parenthesized.
+#define GMOCK_PP_INTERNAL_INTERNAL_16TH(_1, _2, _3, _4, _5, _6, _7, _8, _9, \
+                                        _10, _11, _12, _13, _14, _15, _16,  \
+                                        ...)                                \
+  _16
+#define GMOCK_PP_INTERNAL_16TH(_Args) \
+  GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_16TH _Args)
+#define GMOCK_PP_INTERNAL_INTERNAL_HEAD(_1, ...) _1
+#define GMOCK_PP_INTERNAL_HEAD(_Args) \
+  GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_HEAD _Args)
+#define GMOCK_PP_INTERNAL_INTERNAL_TAIL(_1, ...) __VA_ARGS__
+#define GMOCK_PP_INTERNAL_TAIL(_Args) \
+  GMOCK_PP_IDENTITY(GMOCK_PP_INTERNAL_INTERNAL_TAIL _Args)
+
+#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C(...) 1 _
+#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_1 1,
+#define GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_R_GMOCK_PP_INTERNAL_IBP_IS_VARIADIC_C \
+  0,
+#define GMOCK_PP_INTERNAL_REMOVE_PARENS(...) __VA_ARGS__
+#define GMOCK_PP_INTERNAL_INC_0 1
+#define GMOCK_PP_INTERNAL_INC_1 2
+#define GMOCK_PP_INTERNAL_INC_2 3
+#define GMOCK_PP_INTERNAL_INC_3 4
+#define GMOCK_PP_INTERNAL_INC_4 5
+#define GMOCK_PP_INTERNAL_INC_5 6
+#define GMOCK_PP_INTERNAL_INC_6 7
+#define GMOCK_PP_INTERNAL_INC_7 8
+#define GMOCK_PP_INTERNAL_INC_8 9
+#define GMOCK_PP_INTERNAL_INC_9 10
+#define GMOCK_PP_INTERNAL_INC_10 11
+#define GMOCK_PP_INTERNAL_INC_11 12
+#define GMOCK_PP_INTERNAL_INC_12 13
+#define GMOCK_PP_INTERNAL_INC_13 14
+#define GMOCK_PP_INTERNAL_INC_14 15
+#define GMOCK_PP_INTERNAL_INC_15 16
+#define GMOCK_PP_INTERNAL_COMMA_IF_0
+#define GMOCK_PP_INTERNAL_COMMA_IF_1 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_2 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_3 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_4 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_5 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_6 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_7 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_8 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_9 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_10 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_11 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_12 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_13 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_14 ,
+#define GMOCK_PP_INTERNAL_COMMA_IF_15 ,
+#define GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, _element) \
+  _Macro(_i, _Data, _element)
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_0(_i, _Macro, _Data, _Tuple)
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_1(_i, _Macro, _Data, _Tuple) \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple)
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_2(_i, _Macro, _Data, _Tuple)    \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_1(GMOCK_PP_INC(_i), _Macro, _Data,    \
+                                    (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_3(_i, _Macro, _Data, _Tuple)    \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_2(GMOCK_PP_INC(_i), _Macro, _Data,    \
+                                    (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_4(_i, _Macro, _Data, _Tuple)    \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_3(GMOCK_PP_INC(_i), _Macro, _Data,    \
+                                    (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_5(_i, _Macro, _Data, _Tuple)    \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_4(GMOCK_PP_INC(_i), _Macro, _Data,    \
+                                    (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_6(_i, _Macro, _Data, _Tuple)    \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_5(GMOCK_PP_INC(_i), _Macro, _Data,    \
+                                    (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_7(_i, _Macro, _Data, _Tuple)    \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_6(GMOCK_PP_INC(_i), _Macro, _Data,    \
+                                    (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_8(_i, _Macro, _Data, _Tuple)    \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_7(GMOCK_PP_INC(_i), _Macro, _Data,    \
+                                    (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_9(_i, _Macro, _Data, _Tuple)    \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_8(GMOCK_PP_INC(_i), _Macro, _Data,    \
+                                    (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_10(_i, _Macro, _Data, _Tuple)   \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_9(GMOCK_PP_INC(_i), _Macro, _Data,    \
+                                    (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_11(_i, _Macro, _Data, _Tuple)   \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_10(GMOCK_PP_INC(_i), _Macro, _Data,   \
+                                     (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_12(_i, _Macro, _Data, _Tuple)   \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_11(GMOCK_PP_INC(_i), _Macro, _Data,   \
+                                     (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_13(_i, _Macro, _Data, _Tuple)   \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_12(GMOCK_PP_INC(_i), _Macro, _Data,   \
+                                     (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_14(_i, _Macro, _Data, _Tuple)   \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_13(GMOCK_PP_INC(_i), _Macro, _Data,   \
+                                     (GMOCK_PP_TAIL _Tuple))
+#define GMOCK_PP_INTERNAL_FOR_EACH_IMPL_15(_i, _Macro, _Data, _Tuple)   \
+  GMOCK_PP_INTERNAL_CALL_MACRO(_Macro, _i, _Data, GMOCK_PP_HEAD _Tuple) \
+  GMOCK_PP_INTERNAL_FOR_EACH_IMPL_14(GMOCK_PP_INC(_i), _Macro, _Data,   \
+                                     (GMOCK_PP_TAIL _Tuple))
+
+#endif  // GOOGLEMOCK_INCLUDE_GMOCK_INTERNAL_GMOCK_PP_H_
diff --git a/src/include/gromacs/external/googletest/googlemock/test/gmock_link_test.h b/src/include/gromacs/external/googletest/googlemock/test/gmock_link_test.h
new file mode 100644 (file)
index 0000000..5734b2e
--- /dev/null
@@ -0,0 +1,690 @@
+// Copyright 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Google Mock - a framework for writing C++ mock classes.
+//
+// This file tests that:
+// a. A header file defining a mock class can be included in multiple
+//    translation units without causing a link error.
+// b. Actions and matchers can be instantiated with identical template
+//    arguments in different translation units without causing link
+//    errors.
+//    The following constructs are currently tested:
+//    Actions:
+//      Return()
+//      Return(value)
+//      ReturnNull
+//      ReturnRef
+//      Assign
+//      SetArgPointee
+//      SetArrayArgument
+//      SetErrnoAndReturn
+//      Invoke(function)
+//      Invoke(object, method)
+//      InvokeWithoutArgs(function)
+//      InvokeWithoutArgs(object, method)
+//      InvokeArgument
+//      WithArg
+//      WithArgs
+//      WithoutArgs
+//      DoAll
+//      DoDefault
+//      IgnoreResult
+//      Throw
+//      ACTION()-generated
+//      ACTION_P()-generated
+//      ACTION_P2()-generated
+//    Matchers:
+//      _
+//      A
+//      An
+//      Eq
+//      Gt, Lt, Ge, Le, Ne
+//      NotNull
+//      Ref
+//      TypedEq
+//      DoubleEq
+//      FloatEq
+//      NanSensitiveDoubleEq
+//      NanSensitiveFloatEq
+//      ContainsRegex
+//      MatchesRegex
+//      EndsWith
+//      HasSubstr
+//      StartsWith
+//      StrCaseEq
+//      StrCaseNe
+//      StrEq
+//      StrNe
+//      ElementsAre
+//      ElementsAreArray
+//      ContainerEq
+//      Field
+//      Property
+//      ResultOf(function)
+//      ResultOf(callback)
+//      Pointee
+//      Truly(predicate)
+//      AddressSatisfies
+//      AllOf
+//      AnyOf
+//      Not
+//      MatcherCast<T>
+//
+//  Please note: this test does not verify the functioning of these
+//  constructs, only that the programs using them will link successfully.
+//
+// Implementation note:
+// This test requires identical definitions of Interface and Mock to be
+// included in different translation units.  We achieve this by writing
+// them in this header and #including it in gmock_link_test.cc and
+// gmock_link2_test.cc.  Because the symbols generated by the compiler for
+// those constructs must be identical in both translation units,
+// definitions of Interface and Mock tests MUST be kept in the SAME
+// NON-ANONYMOUS namespace in this file.  The test fixture class LinkTest
+// is defined as LinkTest1 in gmock_link_test.cc and as LinkTest2 in
+// gmock_link2_test.cc to avoid producing linker errors.
+
+#ifndef GOOGLEMOCK_TEST_GMOCK_LINK_TEST_H_
+#define GOOGLEMOCK_TEST_GMOCK_LINK_TEST_H_
+
+#include "gmock/gmock.h"
+
+#if !GTEST_OS_WINDOWS_MOBILE
+# include <errno.h>
+#endif
+
+#include <iostream>
+#include <vector>
+
+#include "gtest/gtest.h"
+#include "gtest/internal/gtest-port.h"
+
+using testing::_;
+using testing::A;
+using testing::Action;
+using testing::AllOf;
+using testing::AnyOf;
+using testing::Assign;
+using testing::ContainerEq;
+using testing::DoAll;
+using testing::DoDefault;
+using testing::DoubleEq;
+using testing::ElementsAre;
+using testing::ElementsAreArray;
+using testing::EndsWith;
+using testing::Eq;
+using testing::Field;
+using testing::FloatEq;
+using testing::Ge;
+using testing::Gt;
+using testing::HasSubstr;
+using testing::IgnoreResult;
+using testing::Invoke;
+using testing::InvokeArgument;
+using testing::InvokeWithoutArgs;
+using testing::IsNull;
+using testing::IsSubsetOf;
+using testing::IsSupersetOf;
+using testing::Le;
+using testing::Lt;
+using testing::Matcher;
+using testing::MatcherCast;
+using testing::NanSensitiveDoubleEq;
+using testing::NanSensitiveFloatEq;
+using testing::Ne;
+using testing::Not;
+using testing::NotNull;
+using testing::Pointee;
+using testing::Property;
+using testing::Ref;
+using testing::ResultOf;
+using testing::Return;
+using testing::ReturnNull;
+using testing::ReturnRef;
+using testing::SetArgPointee;
+using testing::SetArrayArgument;
+using testing::StartsWith;
+using testing::StrCaseEq;
+using testing::StrCaseNe;
+using testing::StrEq;
+using testing::StrNe;
+using testing::Truly;
+using testing::TypedEq;
+using testing::WithArg;
+using testing::WithArgs;
+using testing::WithoutArgs;
+
+#if !GTEST_OS_WINDOWS_MOBILE
+using testing::SetErrnoAndReturn;
+#endif
+
+#if GTEST_HAS_EXCEPTIONS
+using testing::Throw;
+#endif
+
+using testing::ContainsRegex;
+using testing::MatchesRegex;
+
+class Interface {
+ public:
+  virtual ~Interface() {}
+  virtual void VoidFromString(char* str) = 0;
+  virtual char* StringFromString(char* str) = 0;
+  virtual int IntFromString(char* str) = 0;
+  virtual int& IntRefFromString(char* str) = 0;
+  virtual void VoidFromFunc(void(*func)(char* str)) = 0;
+  virtual void VoidFromIntRef(int& n) = 0;  // NOLINT
+  virtual void VoidFromFloat(float n) = 0;
+  virtual void VoidFromDouble(double n) = 0;
+  virtual void VoidFromVector(const std::vector<int>& v) = 0;
+};
+
+class Mock: public Interface {
+ public:
+  Mock() {}
+
+  MOCK_METHOD1(VoidFromString, void(char* str));
+  MOCK_METHOD1(StringFromString, char*(char* str));
+  MOCK_METHOD1(IntFromString, int(char* str));
+  MOCK_METHOD1(IntRefFromString, int&(char* str));
+  MOCK_METHOD1(VoidFromFunc, void(void(*func)(char* str)));
+  MOCK_METHOD1(VoidFromIntRef, void(int& n));  // NOLINT
+  MOCK_METHOD1(VoidFromFloat, void(float n));
+  MOCK_METHOD1(VoidFromDouble, void(double n));
+  MOCK_METHOD1(VoidFromVector, void(const std::vector<int>& v));
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(Mock);
+};
+
+class InvokeHelper {
+ public:
+  static void StaticVoidFromVoid() {}
+  void VoidFromVoid() {}
+  static void StaticVoidFromString(char* /* str */) {}
+  void VoidFromString(char* /* str */) {}
+  static int StaticIntFromString(char* /* str */) { return 1; }
+  static bool StaticBoolFromString(const char* /* str */) { return true; }
+};
+
+class FieldHelper {
+ public:
+  explicit FieldHelper(int a_field) : field_(a_field) {}
+  int field() const { return field_; }
+  int field_;  // NOLINT -- need external access to field_ to test
+               //           the Field matcher.
+};
+
+// Tests the linkage of the ReturnVoid action.
+TEST(LinkTest, TestReturnVoid) {
+  Mock mock;
+
+  EXPECT_CALL(mock, VoidFromString(_)).WillOnce(Return());
+  mock.VoidFromString(nullptr);
+}
+
+// Tests the linkage of the Return action.
+TEST(LinkTest, TestReturn) {
+  Mock mock;
+  char ch = 'x';
+
+  EXPECT_CALL(mock, StringFromString(_)).WillOnce(Return(&ch));
+  mock.StringFromString(nullptr);
+}
+
+// Tests the linkage of the ReturnNull action.
+TEST(LinkTest, TestReturnNull) {
+  Mock mock;
+
+  EXPECT_CALL(mock, VoidFromString(_)).WillOnce(Return());
+  mock.VoidFromString(nullptr);
+}
+
+// Tests the linkage of the ReturnRef action.
+TEST(LinkTest, TestReturnRef) {
+  Mock mock;
+  int n = 42;
+
+  EXPECT_CALL(mock, IntRefFromString(_)).WillOnce(ReturnRef(n));
+  mock.IntRefFromString(nullptr);
+}
+
+// Tests the linkage of the Assign action.
+TEST(LinkTest, TestAssign) {
+  Mock mock;
+  char ch = 'x';
+
+  EXPECT_CALL(mock, VoidFromString(_)).WillOnce(Assign(&ch, 'y'));
+  mock.VoidFromString(nullptr);
+}
+
+// Tests the linkage of the SetArgPointee action.
+TEST(LinkTest, TestSetArgPointee) {
+  Mock mock;
+  char ch = 'x';
+
+  EXPECT_CALL(mock, VoidFromString(_)).WillOnce(SetArgPointee<0>('y'));
+  mock.VoidFromString(&ch);
+}
+
+// Tests the linkage of the SetArrayArgument action.
+TEST(LinkTest, TestSetArrayArgument) {
+  Mock mock;
+  char ch = 'x';
+  char ch2 = 'y';
+
+  EXPECT_CALL(mock, VoidFromString(_)).WillOnce(SetArrayArgument<0>(&ch2,
+                                                                    &ch2 + 1));
+  mock.VoidFromString(&ch);
+}
+
+#if !GTEST_OS_WINDOWS_MOBILE
+
+// Tests the linkage of the SetErrnoAndReturn action.
+TEST(LinkTest, TestSetErrnoAndReturn) {
+  Mock mock;
+
+  int saved_errno = errno;
+  EXPECT_CALL(mock, IntFromString(_)).WillOnce(SetErrnoAndReturn(1, -1));
+  mock.IntFromString(nullptr);
+  errno = saved_errno;
+}
+
+#endif  // !GTEST_OS_WINDOWS_MOBILE
+
+// Tests the linkage of the Invoke(function) and Invoke(object, method) actions.
+TEST(LinkTest, TestInvoke) {
+  Mock mock;
+  InvokeHelper test_invoke_helper;
+
+  EXPECT_CALL(mock, VoidFromString(_))
+      .WillOnce(Invoke(&InvokeHelper::StaticVoidFromString))
+      .WillOnce(Invoke(&test_invoke_helper, &InvokeHelper::VoidFromString));
+  mock.VoidFromString(nullptr);
+  mock.VoidFromString(nullptr);
+}
+
+// Tests the linkage of the InvokeWithoutArgs action.
+TEST(LinkTest, TestInvokeWithoutArgs) {
+  Mock mock;
+  InvokeHelper test_invoke_helper;
+
+  EXPECT_CALL(mock, VoidFromString(_))
+      .WillOnce(InvokeWithoutArgs(&InvokeHelper::StaticVoidFromVoid))
+      .WillOnce(InvokeWithoutArgs(&test_invoke_helper,
+                                  &InvokeHelper::VoidFromVoid));
+  mock.VoidFromString(nullptr);
+  mock.VoidFromString(nullptr);
+}
+
+// Tests the linkage of the InvokeArgument action.
+TEST(LinkTest, TestInvokeArgument) {
+  Mock mock;
+  char ch = 'x';
+
+  EXPECT_CALL(mock, VoidFromFunc(_)).WillOnce(InvokeArgument<0>(&ch));
+  mock.VoidFromFunc(InvokeHelper::StaticVoidFromString);
+}
+
+// Tests the linkage of the WithArg action.
+TEST(LinkTest, TestWithArg) {
+  Mock mock;
+
+  EXPECT_CALL(mock, VoidFromString(_))
+      .WillOnce(WithArg<0>(Invoke(&InvokeHelper::StaticVoidFromString)));
+  mock.VoidFromString(nullptr);
+}
+
+// Tests the linkage of the WithArgs action.
+TEST(LinkTest, TestWithArgs) {
+  Mock mock;
+
+  EXPECT_CALL(mock, VoidFromString(_))
+      .WillOnce(WithArgs<0>(Invoke(&InvokeHelper::StaticVoidFromString)));
+  mock.VoidFromString(nullptr);
+}
+
+// Tests the linkage of the WithoutArgs action.
+TEST(LinkTest, TestWithoutArgs) {
+  Mock mock;
+
+  EXPECT_CALL(mock, VoidFromString(_)).WillOnce(WithoutArgs(Return()));
+  mock.VoidFromString(nullptr);
+}
+
+// Tests the linkage of the DoAll action.
+TEST(LinkTest, TestDoAll) {
+  Mock mock;
+  char ch = 'x';
+
+  EXPECT_CALL(mock, VoidFromString(_))
+      .WillOnce(DoAll(SetArgPointee<0>('y'), Return()));
+  mock.VoidFromString(&ch);
+}
+
+// Tests the linkage of the DoDefault action.
+TEST(LinkTest, TestDoDefault) {
+  Mock mock;
+  char ch = 'x';
+
+  ON_CALL(mock, VoidFromString(_)).WillByDefault(Return());
+  EXPECT_CALL(mock, VoidFromString(_)).WillOnce(DoDefault());
+  mock.VoidFromString(&ch);
+}
+
+// Tests the linkage of the IgnoreResult action.
+TEST(LinkTest, TestIgnoreResult) {
+  Mock mock;
+
+  EXPECT_CALL(mock, VoidFromString(_)).WillOnce(IgnoreResult(Return(42)));
+  mock.VoidFromString(nullptr);
+}
+
+#if GTEST_HAS_EXCEPTIONS
+// Tests the linkage of the Throw action.
+TEST(LinkTest, TestThrow) {
+  Mock mock;
+
+  EXPECT_CALL(mock, VoidFromString(_)).WillOnce(Throw(42));
+  EXPECT_THROW(mock.VoidFromString(nullptr), int);
+}
+#endif  // GTEST_HAS_EXCEPTIONS
+
+// The ACTION*() macros trigger warning C4100 (unreferenced formal
+// parameter) in MSVC with -W4.  Unfortunately they cannot be fixed in
+// the macro definition, as the warnings are generated when the macro
+// is expanded and macro expansion cannot contain #pragma.  Therefore
+// we suppress them here.
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning(disable:4100)
+#endif
+
+// Tests the linkage of actions created using ACTION macro.
+namespace {
+ACTION(Return1) { return 1; }
+}
+
+TEST(LinkTest, TestActionMacro) {
+  Mock mock;
+
+  EXPECT_CALL(mock, IntFromString(_)).WillOnce(Return1());
+  mock.IntFromString(nullptr);
+}
+
+// Tests the linkage of actions created using ACTION_P macro.
+namespace {
+ACTION_P(ReturnArgument, ret_value) { return ret_value; }
+}
+
+TEST(LinkTest, TestActionPMacro) {
+  Mock mock;
+
+  EXPECT_CALL(mock, IntFromString(_)).WillOnce(ReturnArgument(42));
+  mock.IntFromString(nullptr);
+}
+
+// Tests the linkage of actions created using ACTION_P2 macro.
+namespace {
+ACTION_P2(ReturnEqualsEitherOf, first, second) {
+  return arg0 == first || arg0 == second;
+}
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+TEST(LinkTest, TestActionP2Macro) {
+  Mock mock;
+  char ch = 'x';
+
+  EXPECT_CALL(mock, IntFromString(_))
+      .WillOnce(ReturnEqualsEitherOf("one", "two"));
+  mock.IntFromString(&ch);
+}
+
+// Tests the linkage of the "_" matcher.
+TEST(LinkTest, TestMatcherAnything) {
+  Mock mock;
+
+  ON_CALL(mock, VoidFromString(_)).WillByDefault(Return());
+}
+
+// Tests the linkage of the A matcher.
+TEST(LinkTest, TestMatcherA) {
+  Mock mock;
+
+  ON_CALL(mock, VoidFromString(A<char*>())).WillByDefault(Return());
+}
+
+// Tests the linkage of the Eq and the "bare value" matcher.
+TEST(LinkTest, TestMatchersEq) {
+  Mock mock;
+  const char* p = "x";
+
+  ON_CALL(mock, VoidFromString(Eq(p))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromString(const_cast<char*>("y")))
+      .WillByDefault(Return());
+}
+
+// Tests the linkage of the Lt, Gt, Le, Ge, and Ne matchers.
+TEST(LinkTest, TestMatchersRelations) {
+  Mock mock;
+
+  ON_CALL(mock, VoidFromFloat(Lt(1.0f))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromFloat(Gt(1.0f))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromFloat(Le(1.0f))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromFloat(Ge(1.0f))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromFloat(Ne(1.0f))).WillByDefault(Return());
+}
+
+// Tests the linkage of the NotNull matcher.
+TEST(LinkTest, TestMatcherNotNull) {
+  Mock mock;
+
+  ON_CALL(mock, VoidFromString(NotNull())).WillByDefault(Return());
+}
+
+// Tests the linkage of the IsNull matcher.
+TEST(LinkTest, TestMatcherIsNull) {
+  Mock mock;
+
+  ON_CALL(mock, VoidFromString(IsNull())).WillByDefault(Return());
+}
+
+// Tests the linkage of the Ref matcher.
+TEST(LinkTest, TestMatcherRef) {
+  Mock mock;
+  int a = 0;
+
+  ON_CALL(mock, VoidFromIntRef(Ref(a))).WillByDefault(Return());
+}
+
+// Tests the linkage of the TypedEq matcher.
+TEST(LinkTest, TestMatcherTypedEq) {
+  Mock mock;
+  long a = 0;
+
+  ON_CALL(mock, VoidFromIntRef(TypedEq<int&>(a))).WillByDefault(Return());
+}
+
+// Tests the linkage of the FloatEq, DoubleEq, NanSensitiveFloatEq and
+// NanSensitiveDoubleEq matchers.
+TEST(LinkTest, TestMatchersFloatingPoint) {
+  Mock mock;
+  float a = 0;
+
+  ON_CALL(mock, VoidFromFloat(FloatEq(a))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromDouble(DoubleEq(a))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromFloat(NanSensitiveFloatEq(a))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromDouble(NanSensitiveDoubleEq(a)))
+      .WillByDefault(Return());
+}
+
+// Tests the linkage of the ContainsRegex matcher.
+TEST(LinkTest, TestMatcherContainsRegex) {
+  Mock mock;
+
+  ON_CALL(mock, VoidFromString(ContainsRegex(".*"))).WillByDefault(Return());
+}
+
+// Tests the linkage of the MatchesRegex matcher.
+TEST(LinkTest, TestMatcherMatchesRegex) {
+  Mock mock;
+
+  ON_CALL(mock, VoidFromString(MatchesRegex(".*"))).WillByDefault(Return());
+}
+
+// Tests the linkage of the StartsWith, EndsWith, and HasSubstr matchers.
+TEST(LinkTest, TestMatchersSubstrings) {
+  Mock mock;
+
+  ON_CALL(mock, VoidFromString(StartsWith("a"))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromString(EndsWith("c"))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromString(HasSubstr("b"))).WillByDefault(Return());
+}
+
+// Tests the linkage of the StrEq, StrNe, StrCaseEq, and StrCaseNe matchers.
+TEST(LinkTest, TestMatchersStringEquality) {
+  Mock mock;
+  ON_CALL(mock, VoidFromString(StrEq("a"))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromString(StrNe("a"))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromString(StrCaseEq("a"))).WillByDefault(Return());
+  ON_CALL(mock, VoidFromString(StrCaseNe("a"))).WillByDefault(Return());
+}
+
+// Tests the linkage of the ElementsAre matcher.
+TEST(LinkTest, TestMatcherElementsAre) {
+  Mock mock;
+
+  ON_CALL(mock, VoidFromVector(ElementsAre('a', _))).WillByDefault(Return());
+}
+
+// Tests the linkage of the ElementsAreArray matcher.
+TEST(LinkTest, TestMatcherElementsAreArray) {
+  Mock mock;
+  char arr[] = { 'a', 'b' };
+
+  ON_CALL(mock, VoidFromVector(ElementsAreArray(arr))).WillByDefault(Return());
+}
+
+// Tests the linkage of the IsSubsetOf matcher.
+TEST(LinkTest, TestMatcherIsSubsetOf) {
+  Mock mock;
+  char arr[] = {'a', 'b'};
+
+  ON_CALL(mock, VoidFromVector(IsSubsetOf(arr))).WillByDefault(Return());
+}
+
+// Tests the linkage of the IsSupersetOf matcher.
+TEST(LinkTest, TestMatcherIsSupersetOf) {
+  Mock mock;
+  char arr[] = {'a', 'b'};
+
+  ON_CALL(mock, VoidFromVector(IsSupersetOf(arr))).WillByDefault(Return());
+}
+
+// Tests the linkage of the ContainerEq matcher.
+TEST(LinkTest, TestMatcherContainerEq) {
+  Mock mock;
+  std::vector<int> v;
+
+  ON_CALL(mock, VoidFromVector(ContainerEq(v))).WillByDefault(Return());
+}
+
+// Tests the linkage of the Field matcher.
+TEST(LinkTest, TestMatcherField) {
+  FieldHelper helper(0);
+
+  Matcher<const FieldHelper&> m = Field(&FieldHelper::field_, Eq(0));
+  EXPECT_TRUE(m.Matches(helper));
+
+  Matcher<const FieldHelper*> m2 = Field(&FieldHelper::field_, Eq(0));
+  EXPECT_TRUE(m2.Matches(&helper));
+}
+
+// Tests the linkage of the Property matcher.
+TEST(LinkTest, TestMatcherProperty) {
+  FieldHelper helper(0);
+
+  Matcher<const FieldHelper&> m = Property(&FieldHelper::field, Eq(0));
+  EXPECT_TRUE(m.Matches(helper));
+
+  Matcher<const FieldHelper*> m2 = Property(&FieldHelper::field, Eq(0));
+  EXPECT_TRUE(m2.Matches(&helper));
+}
+
+// Tests the linkage of the ResultOf matcher.
+TEST(LinkTest, TestMatcherResultOf) {
+  Matcher<char*> m = ResultOf(&InvokeHelper::StaticIntFromString, Eq(1));
+  EXPECT_TRUE(m.Matches(nullptr));
+}
+
+// Tests the linkage of the ResultOf matcher.
+TEST(LinkTest, TestMatcherPointee) {
+  int n = 1;
+
+  Matcher<int*> m = Pointee(Eq(1));
+  EXPECT_TRUE(m.Matches(&n));
+}
+
+// Tests the linkage of the Truly matcher.
+TEST(LinkTest, TestMatcherTruly) {
+  Matcher<const char*> m = Truly(&InvokeHelper::StaticBoolFromString);
+  EXPECT_TRUE(m.Matches(nullptr));
+}
+
+// Tests the linkage of the AllOf matcher.
+TEST(LinkTest, TestMatcherAllOf) {
+  Matcher<int> m = AllOf(_, Eq(1));
+  EXPECT_TRUE(m.Matches(1));
+}
+
+// Tests the linkage of the AnyOf matcher.
+TEST(LinkTest, TestMatcherAnyOf) {
+  Matcher<int> m = AnyOf(_, Eq(1));
+  EXPECT_TRUE(m.Matches(1));
+}
+
+// Tests the linkage of the Not matcher.
+TEST(LinkTest, TestMatcherNot) {
+  Matcher<int> m = Not(_);
+  EXPECT_FALSE(m.Matches(1));
+}
+
+// Tests the linkage of the MatcherCast<T>() function.
+TEST(LinkTest, TestMatcherCast) {
+  Matcher<const char*> m = MatcherCast<const char*>(_);
+  EXPECT_TRUE(m.Matches(nullptr));
+}
+
+#endif  // GOOGLEMOCK_TEST_GMOCK_LINK_TEST_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-death-test.h b/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-death-test.h
new file mode 100644 (file)
index 0000000..33effe1
--- /dev/null
@@ -0,0 +1,343 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//
+// The Google C++ Testing and Mocking Framework (Google Test)
+//
+// This header file defines the public API for death tests.  It is
+// #included by gtest.h so a user doesn't need to include this
+// directly.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
+
+#include "gtest/internal/gtest-death-test-internal.h"
+
+// This flag controls the style of death tests.  Valid values are "threadsafe",
+// meaning that the death test child process will re-execute the test binary
+// from the start, running only a single death test, or "fast",
+// meaning that the child process will execute the test logic immediately
+// after forking.
+GTEST_DECLARE_string_(death_test_style);
+
+namespace testing {
+
+#if GTEST_HAS_DEATH_TEST
+
+namespace internal {
+
+// Returns a Boolean value indicating whether the caller is currently
+// executing in the context of the death test child process.  Tools such as
+// Valgrind heap checkers may need this to modify their behavior in death
+// tests.  IMPORTANT: This is an internal utility.  Using it may break the
+// implementation of death tests.  User code MUST NOT use it.
+GTEST_API_ bool InDeathTestChild();
+
+}  // namespace internal
+
+// The following macros are useful for writing death tests.
+
+// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is
+// executed:
+//
+//   1. It generates a warning if there is more than one active
+//   thread.  This is because it's safe to fork() or clone() only
+//   when there is a single thread.
+//
+//   2. The parent process clone()s a sub-process and runs the death
+//   test in it; the sub-process exits with code 0 at the end of the
+//   death test, if it hasn't exited already.
+//
+//   3. The parent process waits for the sub-process to terminate.
+//
+//   4. The parent process checks the exit code and error message of
+//   the sub-process.
+//
+// Examples:
+//
+//   ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number");
+//   for (int i = 0; i < 5; i++) {
+//     EXPECT_DEATH(server.ProcessRequest(i),
+//                  "Invalid request .* in ProcessRequest()")
+//                  << "Failed to die on request " << i;
+//   }
+//
+//   ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting");
+//
+//   bool KilledBySIGHUP(int exit_code) {
+//     return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP;
+//   }
+//
+//   ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!");
+//
+// The final parameter to each of these macros is a matcher applied to any data
+// the sub-process wrote to stderr.  For compatibility with existing tests, a
+// bare string is interpreted as a regular expression matcher.
+//
+// On the regular expressions used in death tests:
+//
+//   On POSIX-compliant systems (*nix), we use the <regex.h> library,
+//   which uses the POSIX extended regex syntax.
+//
+//   On other platforms (e.g. Windows or Mac), we only support a simple regex
+//   syntax implemented as part of Google Test.  This limited
+//   implementation should be enough most of the time when writing
+//   death tests; though it lacks many features you can find in PCRE
+//   or POSIX extended regex syntax.  For example, we don't support
+//   union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and
+//   repetition count ("x{5,7}"), among others.
+//
+//   Below is the syntax that we do support.  We chose it to be a
+//   subset of both PCRE and POSIX extended regex, so it's easy to
+//   learn wherever you come from.  In the following: 'A' denotes a
+//   literal character, period (.), or a single \\ escape sequence;
+//   'x' and 'y' denote regular expressions; 'm' and 'n' are for
+//   natural numbers.
+//
+//     c     matches any literal character c
+//     \\d   matches any decimal digit
+//     \\D   matches any character that's not a decimal digit
+//     \\f   matches \f
+//     \\n   matches \n
+//     \\r   matches \r
+//     \\s   matches any ASCII whitespace, including \n
+//     \\S   matches any character that's not a whitespace
+//     \\t   matches \t
+//     \\v   matches \v
+//     \\w   matches any letter, _, or decimal digit
+//     \\W   matches any character that \\w doesn't match
+//     \\c   matches any literal character c, which must be a punctuation
+//     .     matches any single character except \n
+//     A?    matches 0 or 1 occurrences of A
+//     A*    matches 0 or many occurrences of A
+//     A+    matches 1 or many occurrences of A
+//     ^     matches the beginning of a string (not that of each line)
+//     $     matches the end of a string (not that of each line)
+//     xy    matches x followed by y
+//
+//   If you accidentally use PCRE or POSIX extended regex features
+//   not implemented by us, you will get a run-time failure.  In that
+//   case, please try to rewrite your regular expression within the
+//   above syntax.
+//
+//   This implementation is *not* meant to be as highly tuned or robust
+//   as a compiled regex library, but should perform well enough for a
+//   death test, which already incurs significant overhead by launching
+//   a child process.
+//
+// Known caveats:
+//
+//   A "threadsafe" style death test obtains the path to the test
+//   program from argv[0] and re-executes it in the sub-process.  For
+//   simplicity, the current implementation doesn't search the PATH
+//   when launching the sub-process.  This means that the user must
+//   invoke the test program via a path that contains at least one
+//   path separator (e.g. path/to/foo_test and
+//   /absolute/path/to/bar_test are fine, but foo_test is not).  This
+//   is rarely a problem as people usually don't put the test binary
+//   directory in PATH.
+//
+
+// Asserts that a given `statement` causes the program to exit, with an
+// integer exit status that satisfies `predicate`, and emitting error output
+// that matches `matcher`.
+# define ASSERT_EXIT(statement, predicate, matcher) \
+    GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_FATAL_FAILURE_)
+
+// Like `ASSERT_EXIT`, but continues on to successive tests in the
+// test suite, if any:
+# define EXPECT_EXIT(statement, predicate, matcher) \
+    GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_NONFATAL_FAILURE_)
+
+// Asserts that a given `statement` causes the program to exit, either by
+// explicitly exiting with a nonzero exit code or being killed by a
+// signal, and emitting error output that matches `matcher`.
+# define ASSERT_DEATH(statement, matcher) \
+    ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher)
+
+// Like `ASSERT_DEATH`, but continues on to successive tests in the
+// test suite, if any:
+# define EXPECT_DEATH(statement, matcher) \
+    EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher)
+
+// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*:
+
+// Tests that an exit code describes a normal exit with a given exit code.
+class GTEST_API_ ExitedWithCode {
+ public:
+  explicit ExitedWithCode(int exit_code);
+  ExitedWithCode(const ExitedWithCode&) = default;
+  void operator=(const ExitedWithCode& other) = delete;
+  bool operator()(int exit_status) const;
+ private:
+  const int exit_code_;
+};
+
+# if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
+// Tests that an exit code describes an exit due to termination by a
+// given signal.
+class GTEST_API_ KilledBySignal {
+ public:
+  explicit KilledBySignal(int signum);
+  bool operator()(int exit_status) const;
+ private:
+  const int signum_;
+};
+# endif  // !GTEST_OS_WINDOWS
+
+// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode.
+// The death testing framework causes this to have interesting semantics,
+// since the sideeffects of the call are only visible in opt mode, and not
+// in debug mode.
+//
+// In practice, this can be used to test functions that utilize the
+// LOG(DFATAL) macro using the following style:
+//
+// int DieInDebugOr12(int* sideeffect) {
+//   if (sideeffect) {
+//     *sideeffect = 12;
+//   }
+//   LOG(DFATAL) << "death";
+//   return 12;
+// }
+//
+// TEST(TestSuite, TestDieOr12WorksInDgbAndOpt) {
+//   int sideeffect = 0;
+//   // Only asserts in dbg.
+//   EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death");
+//
+// #ifdef NDEBUG
+//   // opt-mode has sideeffect visible.
+//   EXPECT_EQ(12, sideeffect);
+// #else
+//   // dbg-mode no visible sideeffect.
+//   EXPECT_EQ(0, sideeffect);
+// #endif
+// }
+//
+// This will assert that DieInDebugReturn12InOpt() crashes in debug
+// mode, usually due to a DCHECK or LOG(DFATAL), but returns the
+// appropriate fallback value (12 in this case) in opt mode. If you
+// need to test that a function has appropriate side-effects in opt
+// mode, include assertions against the side-effects.  A general
+// pattern for this is:
+//
+// EXPECT_DEBUG_DEATH({
+//   // Side-effects here will have an effect after this statement in
+//   // opt mode, but none in debug mode.
+//   EXPECT_EQ(12, DieInDebugOr12(&sideeffect));
+// }, "death");
+//
+# ifdef NDEBUG
+
+#  define EXPECT_DEBUG_DEATH(statement, regex) \
+  GTEST_EXECUTE_STATEMENT_(statement, regex)
+
+#  define ASSERT_DEBUG_DEATH(statement, regex) \
+  GTEST_EXECUTE_STATEMENT_(statement, regex)
+
+# else
+
+#  define EXPECT_DEBUG_DEATH(statement, regex) \
+  EXPECT_DEATH(statement, regex)
+
+#  define ASSERT_DEBUG_DEATH(statement, regex) \
+  ASSERT_DEATH(statement, regex)
+
+# endif  // NDEBUG for EXPECT_DEBUG_DEATH
+#endif  // GTEST_HAS_DEATH_TEST
+
+// This macro is used for implementing macros such as
+// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
+// death tests are not supported. Those macros must compile on such systems
+// if and only if EXPECT_DEATH and ASSERT_DEATH compile with the same parameters
+// on systems that support death tests. This allows one to write such a macro on
+// a system that does not support death tests and be sure that it will compile
+// on a death-test supporting system. It is exposed publicly so that systems
+// that have death-tests with stricter requirements than GTEST_HAS_DEATH_TEST
+// can write their own equivalent of EXPECT_DEATH_IF_SUPPORTED and
+// ASSERT_DEATH_IF_SUPPORTED.
+//
+// Parameters:
+//   statement -  A statement that a macro such as EXPECT_DEATH would test
+//                for program termination. This macro has to make sure this
+//                statement is compiled but not executed, to ensure that
+//                EXPECT_DEATH_IF_SUPPORTED compiles with a certain
+//                parameter if and only if EXPECT_DEATH compiles with it.
+//   regex     -  A regex that a macro such as EXPECT_DEATH would use to test
+//                the output of statement.  This parameter has to be
+//                compiled but not evaluated by this macro, to ensure that
+//                this macro only accepts expressions that a macro such as
+//                EXPECT_DEATH would accept.
+//   terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
+//                and a return statement for ASSERT_DEATH_IF_SUPPORTED.
+//                This ensures that ASSERT_DEATH_IF_SUPPORTED will not
+//                compile inside functions where ASSERT_DEATH doesn't
+//                compile.
+//
+//  The branch that has an always false condition is used to ensure that
+//  statement and regex are compiled (and thus syntactically correct) but
+//  never executed. The unreachable code macro protects the terminator
+//  statement from generating an 'unreachable code' warning in case
+//  statement unconditionally returns or throws. The Message constructor at
+//  the end allows the syntax of streaming additional messages into the
+//  macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
+# define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \
+    GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+    if (::testing::internal::AlwaysTrue()) { \
+      GTEST_LOG_(WARNING) \
+          << "Death tests are not supported on this platform.\n" \
+          << "Statement '" #statement "' cannot be verified."; \
+    } else if (::testing::internal::AlwaysFalse()) { \
+      ::testing::internal::RE::PartialMatch(".*", (regex)); \
+      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+      terminator; \
+    } else \
+      ::testing::Message()
+
+// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and
+// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if
+// death tests are supported; otherwise they just issue a warning.  This is
+// useful when you are combining death test assertions with normal test
+// assertions in one test.
+#if GTEST_HAS_DEATH_TEST
+# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
+    EXPECT_DEATH(statement, regex)
+# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
+    ASSERT_DEATH(statement, regex)
+#else
+# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
+    GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, )
+# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
+    GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return)
+#endif
+
+}  // namespace testing
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-matchers.h b/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-matchers.h
new file mode 100644 (file)
index 0000000..575d136
--- /dev/null
@@ -0,0 +1,934 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The Google C++ Testing and Mocking Framework (Google Test)
+//
+// This file implements just enough of the matcher interface to allow
+// EXPECT_DEATH and friends to accept a matcher argument.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
+
+#include <atomic>
+#include <memory>
+#include <ostream>
+#include <string>
+#include <type_traits>
+
+#include "gtest/gtest-printers.h"
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-port.h"
+
+// MSVC warning C5046 is new as of VS2017 version 15.8.
+#if defined(_MSC_VER) && _MSC_VER >= 1915
+#define GTEST_MAYBE_5046_ 5046
+#else
+#define GTEST_MAYBE_5046_
+#endif
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(
+    4251 GTEST_MAYBE_5046_ /* class A needs to have dll-interface to be used by
+                              clients of class B */
+    /* Symbol involving type with internal linkage not defined */)
+
+namespace testing {
+
+// To implement a matcher Foo for type T, define:
+//   1. a class FooMatcherMatcher that implements the matcher interface:
+//     using is_gtest_matcher = void;
+//     bool MatchAndExplain(const T&, std::ostream*);
+//       (MatchResultListener* can also be used instead of std::ostream*)
+//     void DescribeTo(std::ostream*);
+//     void DescribeNegationTo(std::ostream*);
+//
+//   2. a factory function that creates a Matcher<T> object from a
+//      FooMatcherMatcher.
+
+class MatchResultListener {
+ public:
+  // Creates a listener object with the given underlying ostream.  The
+  // listener does not own the ostream, and does not dereference it
+  // in the constructor or destructor.
+  explicit MatchResultListener(::std::ostream* os) : stream_(os) {}
+  virtual ~MatchResultListener() = 0;  // Makes this class abstract.
+
+  // Streams x to the underlying ostream; does nothing if the ostream
+  // is NULL.
+  template <typename T>
+  MatchResultListener& operator<<(const T& x) {
+    if (stream_ != nullptr) *stream_ << x;
+    return *this;
+  }
+
+  // Returns the underlying ostream.
+  ::std::ostream* stream() { return stream_; }
+
+  // Returns true if and only if the listener is interested in an explanation
+  // of the match result.  A matcher's MatchAndExplain() method can use
+  // this information to avoid generating the explanation when no one
+  // intends to hear it.
+  bool IsInterested() const { return stream_ != nullptr; }
+
+ private:
+  ::std::ostream* const stream_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(MatchResultListener);
+};
+
+inline MatchResultListener::~MatchResultListener() {
+}
+
+// An instance of a subclass of this knows how to describe itself as a
+// matcher.
+class GTEST_API_ MatcherDescriberInterface {
+ public:
+  virtual ~MatcherDescriberInterface() {}
+
+  // Describes this matcher to an ostream.  The function should print
+  // a verb phrase that describes the property a value matching this
+  // matcher should have.  The subject of the verb phrase is the value
+  // being matched.  For example, the DescribeTo() method of the Gt(7)
+  // matcher prints "is greater than 7".
+  virtual void DescribeTo(::std::ostream* os) const = 0;
+
+  // Describes the negation of this matcher to an ostream.  For
+  // example, if the description of this matcher is "is greater than
+  // 7", the negated description could be "is not greater than 7".
+  // You are not required to override this when implementing
+  // MatcherInterface, but it is highly advised so that your matcher
+  // can produce good error messages.
+  virtual void DescribeNegationTo(::std::ostream* os) const {
+    *os << "not (";
+    DescribeTo(os);
+    *os << ")";
+  }
+};
+
+// The implementation of a matcher.
+template <typename T>
+class MatcherInterface : public MatcherDescriberInterface {
+ public:
+  // Returns true if and only if the matcher matches x; also explains the
+  // match result to 'listener' if necessary (see the next paragraph), in
+  // the form of a non-restrictive relative clause ("which ...",
+  // "whose ...", etc) that describes x.  For example, the
+  // MatchAndExplain() method of the Pointee(...) matcher should
+  // generate an explanation like "which points to ...".
+  //
+  // Implementations of MatchAndExplain() should add an explanation of
+  // the match result *if and only if* they can provide additional
+  // information that's not already present (or not obvious) in the
+  // print-out of x and the matcher's description.  Whether the match
+  // succeeds is not a factor in deciding whether an explanation is
+  // needed, as sometimes the caller needs to print a failure message
+  // when the match succeeds (e.g. when the matcher is used inside
+  // Not()).
+  //
+  // For example, a "has at least 10 elements" matcher should explain
+  // what the actual element count is, regardless of the match result,
+  // as it is useful information to the reader; on the other hand, an
+  // "is empty" matcher probably only needs to explain what the actual
+  // size is when the match fails, as it's redundant to say that the
+  // size is 0 when the value is already known to be empty.
+  //
+  // You should override this method when defining a new matcher.
+  //
+  // It's the responsibility of the caller (Google Test) to guarantee
+  // that 'listener' is not NULL.  This helps to simplify a matcher's
+  // implementation when it doesn't care about the performance, as it
+  // can talk to 'listener' without checking its validity first.
+  // However, in order to implement dummy listeners efficiently,
+  // listener->stream() may be NULL.
+  virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0;
+
+  // Inherits these methods from MatcherDescriberInterface:
+  //   virtual void DescribeTo(::std::ostream* os) const = 0;
+  //   virtual void DescribeNegationTo(::std::ostream* os) const;
+};
+
+namespace internal {
+
+struct AnyEq {
+  template <typename A, typename B>
+  bool operator()(const A& a, const B& b) const { return a == b; }
+};
+struct AnyNe {
+  template <typename A, typename B>
+  bool operator()(const A& a, const B& b) const { return a != b; }
+};
+struct AnyLt {
+  template <typename A, typename B>
+  bool operator()(const A& a, const B& b) const { return a < b; }
+};
+struct AnyGt {
+  template <typename A, typename B>
+  bool operator()(const A& a, const B& b) const { return a > b; }
+};
+struct AnyLe {
+  template <typename A, typename B>
+  bool operator()(const A& a, const B& b) const { return a <= b; }
+};
+struct AnyGe {
+  template <typename A, typename B>
+  bool operator()(const A& a, const B& b) const { return a >= b; }
+};
+
+// A match result listener that ignores the explanation.
+class DummyMatchResultListener : public MatchResultListener {
+ public:
+  DummyMatchResultListener() : MatchResultListener(nullptr) {}
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(DummyMatchResultListener);
+};
+
+// A match result listener that forwards the explanation to a given
+// ostream.  The difference between this and MatchResultListener is
+// that the former is concrete.
+class StreamMatchResultListener : public MatchResultListener {
+ public:
+  explicit StreamMatchResultListener(::std::ostream* os)
+      : MatchResultListener(os) {}
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener);
+};
+
+struct SharedPayloadBase {
+  std::atomic<int> ref{1};
+  void Ref() { ref.fetch_add(1, std::memory_order_relaxed); }
+  bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; }
+};
+
+template <typename T>
+struct SharedPayload : SharedPayloadBase {
+  explicit SharedPayload(const T& v) : value(v) {}
+  explicit SharedPayload(T&& v) : value(std::move(v)) {}
+
+  static void Destroy(SharedPayloadBase* shared) {
+    delete static_cast<SharedPayload*>(shared);
+  }
+
+  T value;
+};
+
+// An internal class for implementing Matcher<T>, which will derive
+// from it.  We put functionalities common to all Matcher<T>
+// specializations here to avoid code duplication.
+template <typename T>
+class MatcherBase : private MatcherDescriberInterface {
+ public:
+  // Returns true if and only if the matcher matches x; also explains the
+  // match result to 'listener'.
+  bool MatchAndExplain(const T& x, MatchResultListener* listener) const {
+    GTEST_CHECK_(vtable_ != nullptr);
+    return vtable_->match_and_explain(*this, x, listener);
+  }
+
+  // Returns true if and only if this matcher matches x.
+  bool Matches(const T& x) const {
+    DummyMatchResultListener dummy;
+    return MatchAndExplain(x, &dummy);
+  }
+
+  // Describes this matcher to an ostream.
+  void DescribeTo(::std::ostream* os) const final {
+    GTEST_CHECK_(vtable_ != nullptr);
+    vtable_->describe(*this, os, false);
+  }
+
+  // Describes the negation of this matcher to an ostream.
+  void DescribeNegationTo(::std::ostream* os) const final {
+    GTEST_CHECK_(vtable_ != nullptr);
+    vtable_->describe(*this, os, true);
+  }
+
+  // Explains why x matches, or doesn't match, the matcher.
+  void ExplainMatchResultTo(const T& x, ::std::ostream* os) const {
+    StreamMatchResultListener listener(os);
+    MatchAndExplain(x, &listener);
+  }
+
+  // Returns the describer for this matcher object; retains ownership
+  // of the describer, which is only guaranteed to be alive when
+  // this matcher object is alive.
+  const MatcherDescriberInterface* GetDescriber() const {
+    if (vtable_ == nullptr) return nullptr;
+    return vtable_->get_describer(*this);
+  }
+
+ protected:
+  MatcherBase() : vtable_(nullptr) {}
+
+  // Constructs a matcher from its implementation.
+  template <typename U>
+  explicit MatcherBase(const MatcherInterface<U>* impl) {
+    Init(impl);
+  }
+
+  template <typename M, typename = typename std::remove_reference<
+                            M>::type::is_gtest_matcher>
+  MatcherBase(M&& m) {  // NOLINT
+    Init(std::forward<M>(m));
+  }
+
+  MatcherBase(const MatcherBase& other)
+      : vtable_(other.vtable_), buffer_(other.buffer_) {
+#ifndef __clang_analyzer__
+    if (IsShared()) buffer_.shared->Ref();
+#endif
+  }
+
+  MatcherBase& operator=(const MatcherBase& other) {
+    if (this == &other) return *this;
+    Destroy();
+    vtable_ = other.vtable_;
+    buffer_ = other.buffer_;
+    if (IsShared()) buffer_.shared->Ref();
+    return *this;
+  }
+
+  MatcherBase(MatcherBase&& other)
+      : vtable_(other.vtable_), buffer_(other.buffer_) {
+    other.vtable_ = nullptr;
+  }
+
+  MatcherBase& operator=(MatcherBase&& other) {
+    if (this == &other) return *this;
+    Destroy();
+    vtable_ = other.vtable_;
+    buffer_ = other.buffer_;
+    other.vtable_ = nullptr;
+    return *this;
+  }
+
+  ~MatcherBase() override { Destroy(); }
+
+ private:
+  struct VTable {
+    bool (*match_and_explain)(const MatcherBase&, const T&,
+                              MatchResultListener*);
+    void (*describe)(const MatcherBase&, std::ostream*, bool negation);
+    // Returns the captured object if it implements the interface, otherwise
+    // returns the MatcherBase itself.
+    const MatcherDescriberInterface* (*get_describer)(const MatcherBase&);
+    // Called on shared instances when the reference count reaches 0.
+    void (*shared_destroy)(SharedPayloadBase*);
+  };
+
+  bool IsShared() const {
+    return vtable_ != nullptr && vtable_->shared_destroy != nullptr;
+  }
+
+  // If the implementation uses a listener, call that.
+  template <typename P>
+  static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
+                                  MatchResultListener* listener)
+      -> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) {
+    return P::Get(m).MatchAndExplain(value, listener->stream());
+  }
+
+  template <typename P>
+  static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
+                                  MatchResultListener* listener)
+      -> decltype(P::Get(m).MatchAndExplain(value, listener)) {
+    return P::Get(m).MatchAndExplain(value, listener);
+  }
+
+  template <typename P>
+  static void DescribeImpl(const MatcherBase& m, std::ostream* os,
+                           bool negation) {
+    if (negation) {
+      P::Get(m).DescribeNegationTo(os);
+    } else {
+      P::Get(m).DescribeTo(os);
+    }
+  }
+
+  template <typename P>
+  static const MatcherDescriberInterface* GetDescriberImpl(
+      const MatcherBase& m) {
+    // If the impl is a MatcherDescriberInterface, then return it.
+    // Otherwise use MatcherBase itself.
+    // This allows us to implement the GetDescriber() function without support
+    // from the impl, but some users really want to get their impl back when
+    // they call GetDescriber().
+    // We use std::get on a tuple as a workaround of not having `if constexpr`.
+    return std::get<(
+        std::is_convertible<decltype(&P::Get(m)),
+                            const MatcherDescriberInterface*>::value
+            ? 1
+            : 0)>(std::make_tuple(&m, &P::Get(m)));
+  }
+
+  template <typename P>
+  const VTable* GetVTable() {
+    static constexpr VTable kVTable = {&MatchAndExplainImpl<P>,
+                                       &DescribeImpl<P>, &GetDescriberImpl<P>,
+                                       P::shared_destroy};
+    return &kVTable;
+  }
+
+  union Buffer {
+    // Add some types to give Buffer some common alignment/size use cases.
+    void* ptr;
+    double d;
+    int64_t i;
+    // And add one for the out-of-line cases.
+    SharedPayloadBase* shared;
+  };
+
+  void Destroy() {
+#ifndef __clang_analyzer__
+      if (IsShared() && buffer_.shared->Unref()) {
+      vtable_->shared_destroy(buffer_.shared);
+    }
+#endif
+  }
+
+  template <typename M>
+  static constexpr bool IsInlined() {
+    return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) &&
+           std::is_trivially_copy_constructible<M>::value &&
+           std::is_trivially_destructible<M>::value;
+  }
+
+  template <typename M, bool = MatcherBase::IsInlined<M>()>
+  struct ValuePolicy {
+    static const M& Get(const MatcherBase& m) {
+      // When inlined along with Init, need to be explicit to avoid violating
+      // strict aliasing rules.
+      const M *ptr = static_cast<const M*>(
+          static_cast<const void*>(&m.buffer_));
+      return *ptr;
+    }
+    static void Init(MatcherBase& m, M impl) {
+      ::new (static_cast<void*>(&m.buffer_)) M(impl);
+    }
+    static constexpr auto shared_destroy = nullptr;
+  };
+
+  template <typename M>
+  struct ValuePolicy<M, false> {
+    using Shared = SharedPayload<M>;
+    static const M& Get(const MatcherBase& m) {
+      return static_cast<Shared*>(m.buffer_.shared)->value;
+    }
+    template <typename Arg>
+    static void Init(MatcherBase& m, Arg&& arg) {
+      m.buffer_.shared = new Shared(std::forward<Arg>(arg));
+    }
+    static constexpr auto shared_destroy = &Shared::Destroy;
+  };
+
+  template <typename U, bool B>
+  struct ValuePolicy<const MatcherInterface<U>*, B> {
+    using M = const MatcherInterface<U>;
+    using Shared = SharedPayload<std::unique_ptr<M>>;
+    static const M& Get(const MatcherBase& m) {
+      return *static_cast<Shared*>(m.buffer_.shared)->value;
+    }
+    static void Init(MatcherBase& m, M* impl) {
+      m.buffer_.shared = new Shared(std::unique_ptr<M>(impl));
+    }
+
+    static constexpr auto shared_destroy = &Shared::Destroy;
+  };
+
+  template <typename M>
+  void Init(M&& m) {
+    using MM = typename std::decay<M>::type;
+    using Policy = ValuePolicy<MM>;
+    vtable_ = GetVTable<Policy>();
+    Policy::Init(*this, std::forward<M>(m));
+  }
+
+  const VTable* vtable_;
+  Buffer buffer_;
+};
+
+}  // namespace internal
+
+// A Matcher<T> is a copyable and IMMUTABLE (except by assignment)
+// object that can check whether a value of type T matches.  The
+// implementation of Matcher<T> is just a std::shared_ptr to const
+// MatcherInterface<T>.  Don't inherit from Matcher!
+template <typename T>
+class Matcher : public internal::MatcherBase<T> {
+ public:
+  // Constructs a null matcher.  Needed for storing Matcher objects in STL
+  // containers.  A default-constructed matcher is not yet initialized.  You
+  // cannot use it until a valid value has been assigned to it.
+  explicit Matcher() {}  // NOLINT
+
+  // Constructs a matcher from its implementation.
+  explicit Matcher(const MatcherInterface<const T&>* impl)
+      : internal::MatcherBase<T>(impl) {}
+
+  template <typename U>
+  explicit Matcher(
+      const MatcherInterface<U>* impl,
+      typename std::enable_if<!std::is_same<U, const U&>::value>::type* =
+          nullptr)
+      : internal::MatcherBase<T>(impl) {}
+
+  template <typename M, typename = typename std::remove_reference<
+                            M>::type::is_gtest_matcher>
+  Matcher(M&& m) : internal::MatcherBase<T>(std::forward<M>(m)) {}  // NOLINT
+
+  // Implicit constructor here allows people to write
+  // EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes
+  Matcher(T value);  // NOLINT
+};
+
+// The following two specializations allow the user to write str
+// instead of Eq(str) and "foo" instead of Eq("foo") when a std::string
+// matcher is expected.
+template <>
+class GTEST_API_ Matcher<const std::string&>
+    : public internal::MatcherBase<const std::string&> {
+ public:
+  Matcher() {}
+
+  explicit Matcher(const MatcherInterface<const std::string&>* impl)
+      : internal::MatcherBase<const std::string&>(impl) {}
+
+  template <typename M, typename = typename std::remove_reference<
+                            M>::type::is_gtest_matcher>
+  Matcher(M&& m)  // NOLINT
+      : internal::MatcherBase<const std::string&>(std::forward<M>(m)) {}
+
+  // Allows the user to write str instead of Eq(str) sometimes, where
+  // str is a std::string object.
+  Matcher(const std::string& s);  // NOLINT
+
+  // Allows the user to write "foo" instead of Eq("foo") sometimes.
+  Matcher(const char* s);  // NOLINT
+};
+
+template <>
+class GTEST_API_ Matcher<std::string>
+    : public internal::MatcherBase<std::string> {
+ public:
+  Matcher() {}
+
+  explicit Matcher(const MatcherInterface<const std::string&>* impl)
+      : internal::MatcherBase<std::string>(impl) {}
+  explicit Matcher(const MatcherInterface<std::string>* impl)
+      : internal::MatcherBase<std::string>(impl) {}
+
+  template <typename M, typename = typename std::remove_reference<
+                            M>::type::is_gtest_matcher>
+  Matcher(M&& m)  // NOLINT
+      : internal::MatcherBase<std::string>(std::forward<M>(m)) {}
+
+  // Allows the user to write str instead of Eq(str) sometimes, where
+  // str is a string object.
+  Matcher(const std::string& s);  // NOLINT
+
+  // Allows the user to write "foo" instead of Eq("foo") sometimes.
+  Matcher(const char* s);  // NOLINT
+};
+
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+// The following two specializations allow the user to write str
+// instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view
+// matcher is expected.
+template <>
+class GTEST_API_ Matcher<const internal::StringView&>
+    : public internal::MatcherBase<const internal::StringView&> {
+ public:
+  Matcher() {}
+
+  explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
+      : internal::MatcherBase<const internal::StringView&>(impl) {}
+
+  template <typename M, typename = typename std::remove_reference<
+                            M>::type::is_gtest_matcher>
+  Matcher(M&& m)  // NOLINT
+      : internal::MatcherBase<const internal::StringView&>(std::forward<M>(m)) {
+  }
+
+  // Allows the user to write str instead of Eq(str) sometimes, where
+  // str is a std::string object.
+  Matcher(const std::string& s);  // NOLINT
+
+  // Allows the user to write "foo" instead of Eq("foo") sometimes.
+  Matcher(const char* s);  // NOLINT
+
+  // Allows the user to pass absl::string_views or std::string_views directly.
+  Matcher(internal::StringView s);  // NOLINT
+};
+
+template <>
+class GTEST_API_ Matcher<internal::StringView>
+    : public internal::MatcherBase<internal::StringView> {
+ public:
+  Matcher() {}
+
+  explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
+      : internal::MatcherBase<internal::StringView>(impl) {}
+  explicit Matcher(const MatcherInterface<internal::StringView>* impl)
+      : internal::MatcherBase<internal::StringView>(impl) {}
+
+  template <typename M, typename = typename std::remove_reference<
+                            M>::type::is_gtest_matcher>
+  Matcher(M&& m)  // NOLINT
+      : internal::MatcherBase<internal::StringView>(std::forward<M>(m)) {}
+
+  // Allows the user to write str instead of Eq(str) sometimes, where
+  // str is a std::string object.
+  Matcher(const std::string& s);  // NOLINT
+
+  // Allows the user to write "foo" instead of Eq("foo") sometimes.
+  Matcher(const char* s);  // NOLINT
+
+  // Allows the user to pass absl::string_views or std::string_views directly.
+  Matcher(internal::StringView s);  // NOLINT
+};
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
+
+// Prints a matcher in a human-readable format.
+template <typename T>
+std::ostream& operator<<(std::ostream& os, const Matcher<T>& matcher) {
+  matcher.DescribeTo(&os);
+  return os;
+}
+
+// The PolymorphicMatcher class template makes it easy to implement a
+// polymorphic matcher (i.e. a matcher that can match values of more
+// than one type, e.g. Eq(n) and NotNull()).
+//
+// To define a polymorphic matcher, a user should provide an Impl
+// class that has a DescribeTo() method and a DescribeNegationTo()
+// method, and define a member function (or member function template)
+//
+//   bool MatchAndExplain(const Value& value,
+//                        MatchResultListener* listener) const;
+//
+// See the definition of NotNull() for a complete example.
+template <class Impl>
+class PolymorphicMatcher {
+ public:
+  explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {}
+
+  // Returns a mutable reference to the underlying matcher
+  // implementation object.
+  Impl& mutable_impl() { return impl_; }
+
+  // Returns an immutable reference to the underlying matcher
+  // implementation object.
+  const Impl& impl() const { return impl_; }
+
+  template <typename T>
+  operator Matcher<T>() const {
+    return Matcher<T>(new MonomorphicImpl<const T&>(impl_));
+  }
+
+ private:
+  template <typename T>
+  class MonomorphicImpl : public MatcherInterface<T> {
+   public:
+    explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
+
+    void DescribeTo(::std::ostream* os) const override { impl_.DescribeTo(os); }
+
+    void DescribeNegationTo(::std::ostream* os) const override {
+      impl_.DescribeNegationTo(os);
+    }
+
+    bool MatchAndExplain(T x, MatchResultListener* listener) const override {
+      return impl_.MatchAndExplain(x, listener);
+    }
+
+   private:
+    const Impl impl_;
+  };
+
+  Impl impl_;
+};
+
+// Creates a matcher from its implementation.
+// DEPRECATED: Especially in the generic code, prefer:
+//   Matcher<T>(new MyMatcherImpl<const T&>(...));
+//
+// MakeMatcher may create a Matcher that accepts its argument by value, which
+// leads to unnecessary copies & lack of support for non-copyable types.
+template <typename T>
+inline Matcher<T> MakeMatcher(const MatcherInterface<T>* impl) {
+  return Matcher<T>(impl);
+}
+
+// Creates a polymorphic matcher from its implementation.  This is
+// easier to use than the PolymorphicMatcher<Impl> constructor as it
+// doesn't require you to explicitly write the template argument, e.g.
+//
+//   MakePolymorphicMatcher(foo);
+// vs
+//   PolymorphicMatcher<TypeOfFoo>(foo);
+template <class Impl>
+inline PolymorphicMatcher<Impl> MakePolymorphicMatcher(const Impl& impl) {
+  return PolymorphicMatcher<Impl>(impl);
+}
+
+namespace internal {
+// Implements a matcher that compares a given value with a
+// pre-supplied value using one of the ==, <=, <, etc, operators.  The
+// two values being compared don't have to have the same type.
+//
+// The matcher defined here is polymorphic (for example, Eq(5) can be
+// used to match an int, a short, a double, etc).  Therefore we use
+// a template type conversion operator in the implementation.
+//
+// The following template definition assumes that the Rhs parameter is
+// a "bare" type (i.e. neither 'const T' nor 'T&').
+template <typename D, typename Rhs, typename Op>
+class ComparisonBase {
+ public:
+  explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {}
+
+  using is_gtest_matcher = void;
+
+  template <typename Lhs>
+  bool MatchAndExplain(const Lhs& lhs, std::ostream*) const {
+    return Op()(lhs, Unwrap(rhs_));
+  }
+  void DescribeTo(std::ostream* os) const {
+    *os << D::Desc() << " ";
+    UniversalPrint(Unwrap(rhs_), os);
+  }
+  void DescribeNegationTo(std::ostream* os) const {
+    *os << D::NegatedDesc() << " ";
+    UniversalPrint(Unwrap(rhs_), os);
+  }
+
+ private:
+  template <typename T>
+  static const T& Unwrap(const T& v) {
+    return v;
+  }
+  template <typename T>
+  static const T& Unwrap(std::reference_wrapper<T> v) {
+    return v;
+  }
+
+  Rhs rhs_;
+};
+
+template <typename Rhs>
+class EqMatcher : public ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq> {
+ public:
+  explicit EqMatcher(const Rhs& rhs)
+      : ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq>(rhs) { }
+  static const char* Desc() { return "is equal to"; }
+  static const char* NegatedDesc() { return "isn't equal to"; }
+};
+template <typename Rhs>
+class NeMatcher : public ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe> {
+ public:
+  explicit NeMatcher(const Rhs& rhs)
+      : ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe>(rhs) { }
+  static const char* Desc() { return "isn't equal to"; }
+  static const char* NegatedDesc() { return "is equal to"; }
+};
+template <typename Rhs>
+class LtMatcher : public ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt> {
+ public:
+  explicit LtMatcher(const Rhs& rhs)
+      : ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt>(rhs) { }
+  static const char* Desc() { return "is <"; }
+  static const char* NegatedDesc() { return "isn't <"; }
+};
+template <typename Rhs>
+class GtMatcher : public ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt> {
+ public:
+  explicit GtMatcher(const Rhs& rhs)
+      : ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt>(rhs) { }
+  static const char* Desc() { return "is >"; }
+  static const char* NegatedDesc() { return "isn't >"; }
+};
+template <typename Rhs>
+class LeMatcher : public ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe> {
+ public:
+  explicit LeMatcher(const Rhs& rhs)
+      : ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe>(rhs) { }
+  static const char* Desc() { return "is <="; }
+  static const char* NegatedDesc() { return "isn't <="; }
+};
+template <typename Rhs>
+class GeMatcher : public ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe> {
+ public:
+  explicit GeMatcher(const Rhs& rhs)
+      : ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe>(rhs) { }
+  static const char* Desc() { return "is >="; }
+  static const char* NegatedDesc() { return "isn't >="; }
+};
+
+template <typename T, typename = typename std::enable_if<
+                          std::is_constructible<std::string, T>::value>::type>
+using StringLike = T;
+
+// Implements polymorphic matchers MatchesRegex(regex) and
+// ContainsRegex(regex), which can be used as a Matcher<T> as long as
+// T can be converted to a string.
+class MatchesRegexMatcher {
+ public:
+  MatchesRegexMatcher(const RE* regex, bool full_match)
+      : regex_(regex), full_match_(full_match) {}
+
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  bool MatchAndExplain(const internal::StringView& s,
+                       MatchResultListener* listener) const {
+    return MatchAndExplain(std::string(s), listener);
+  }
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
+
+  // Accepts pointer types, particularly:
+  //   const char*
+  //   char*
+  //   const wchar_t*
+  //   wchar_t*
+  template <typename CharType>
+  bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
+    return s != nullptr && MatchAndExplain(std::string(s), listener);
+  }
+
+  // Matches anything that can convert to std::string.
+  //
+  // This is a template, not just a plain function with const std::string&,
+  // because absl::string_view has some interfering non-explicit constructors.
+  template <class MatcheeStringType>
+  bool MatchAndExplain(const MatcheeStringType& s,
+                       MatchResultListener* /* listener */) const {
+    const std::string& s2(s);
+    return full_match_ ? RE::FullMatch(s2, *regex_)
+                       : RE::PartialMatch(s2, *regex_);
+  }
+
+  void DescribeTo(::std::ostream* os) const {
+    *os << (full_match_ ? "matches" : "contains") << " regular expression ";
+    UniversalPrinter<std::string>::Print(regex_->pattern(), os);
+  }
+
+  void DescribeNegationTo(::std::ostream* os) const {
+    *os << "doesn't " << (full_match_ ? "match" : "contain")
+        << " regular expression ";
+    UniversalPrinter<std::string>::Print(regex_->pattern(), os);
+  }
+
+ private:
+  const std::shared_ptr<const RE> regex_;
+  const bool full_match_;
+};
+}  // namespace internal
+
+// Matches a string that fully matches regular expression 'regex'.
+// The matcher takes ownership of 'regex'.
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
+    const internal::RE* regex) {
+  return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true));
+}
+template <typename T = std::string>
+PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
+    const internal::StringLike<T>& regex) {
+  return MatchesRegex(new internal::RE(std::string(regex)));
+}
+
+// Matches a string that contains regular expression 'regex'.
+// The matcher takes ownership of 'regex'.
+inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
+    const internal::RE* regex) {
+  return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false));
+}
+template <typename T = std::string>
+PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
+    const internal::StringLike<T>& regex) {
+  return ContainsRegex(new internal::RE(std::string(regex)));
+}
+
+// Creates a polymorphic matcher that matches anything equal to x.
+// Note: if the parameter of Eq() were declared as const T&, Eq("foo")
+// wouldn't compile.
+template <typename T>
+inline internal::EqMatcher<T> Eq(T x) { return internal::EqMatcher<T>(x); }
+
+// Constructs a Matcher<T> from a 'value' of type T.  The constructed
+// matcher matches any value that's equal to 'value'.
+template <typename T>
+Matcher<T>::Matcher(T value) { *this = Eq(value); }
+
+// Creates a monomorphic matcher that matches anything with type Lhs
+// and equal to rhs.  A user may need to use this instead of Eq(...)
+// in order to resolve an overloading ambiguity.
+//
+// TypedEq<T>(x) is just a convenient short-hand for Matcher<T>(Eq(x))
+// or Matcher<T>(x), but more readable than the latter.
+//
+// We could define similar monomorphic matchers for other comparison
+// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do
+// it yet as those are used much less than Eq() in practice.  A user
+// can always write Matcher<T>(Lt(5)) to be explicit about the type,
+// for example.
+template <typename Lhs, typename Rhs>
+inline Matcher<Lhs> TypedEq(const Rhs& rhs) { return Eq(rhs); }
+
+// Creates a polymorphic matcher that matches anything >= x.
+template <typename Rhs>
+inline internal::GeMatcher<Rhs> Ge(Rhs x) {
+  return internal::GeMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything > x.
+template <typename Rhs>
+inline internal::GtMatcher<Rhs> Gt(Rhs x) {
+  return internal::GtMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything <= x.
+template <typename Rhs>
+inline internal::LeMatcher<Rhs> Le(Rhs x) {
+  return internal::LeMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything < x.
+template <typename Rhs>
+inline internal::LtMatcher<Rhs> Lt(Rhs x) {
+  return internal::LtMatcher<Rhs>(x);
+}
+
+// Creates a polymorphic matcher that matches anything != x.
+template <typename Rhs>
+inline internal::NeMatcher<Rhs> Ne(Rhs x) {
+  return internal::NeMatcher<Rhs>(x);
+}
+}  // namespace testing
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251 5046
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-message.h b/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-message.h
new file mode 100644 (file)
index 0000000..bad5161
--- /dev/null
@@ -0,0 +1,217 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//
+// The Google C++ Testing and Mocking Framework (Google Test)
+//
+// This header file defines the Message class.
+//
+// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
+// leave some internal implementation details in this header file.
+// They are clearly marked by comments like this:
+//
+//   // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+//
+// Such code is NOT meant to be used by a user directly, and is subject
+// to CHANGE WITHOUT NOTICE.  Therefore DO NOT DEPEND ON IT in a user
+// program!
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
+
+#include <limits>
+#include <memory>
+#include <sstream>
+
+#include "gtest/internal/gtest-port.h"
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
+// Ensures that there is at least one operator<< in the global namespace.
+// See Message& operator<<(...) below for why.
+void operator<<(const testing::internal::Secret&, int);
+
+namespace testing {
+
+// The Message class works like an ostream repeater.
+//
+// Typical usage:
+//
+//   1. You stream a bunch of values to a Message object.
+//      It will remember the text in a stringstream.
+//   2. Then you stream the Message object to an ostream.
+//      This causes the text in the Message to be streamed
+//      to the ostream.
+//
+// For example;
+//
+//   testing::Message foo;
+//   foo << 1 << " != " << 2;
+//   std::cout << foo;
+//
+// will print "1 != 2".
+//
+// Message is not intended to be inherited from.  In particular, its
+// destructor is not virtual.
+//
+// Note that stringstream behaves differently in gcc and in MSVC.  You
+// can stream a NULL char pointer to it in the former, but not in the
+// latter (it causes an access violation if you do).  The Message
+// class hides this difference by treating a NULL char pointer as
+// "(null)".
+class GTEST_API_ Message {
+ private:
+  // The type of basic IO manipulators (endl, ends, and flush) for
+  // narrow streams.
+  typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&);
+
+ public:
+  // Constructs an empty Message.
+  Message();
+
+  // Copy constructor.
+  Message(const Message& msg) : ss_(new ::std::stringstream) {  // NOLINT
+    *ss_ << msg.GetString();
+  }
+
+  // Constructs a Message from a C-string.
+  explicit Message(const char* str) : ss_(new ::std::stringstream) {
+    *ss_ << str;
+  }
+
+  // Streams a non-pointer value to this object.
+  template <typename T>
+  inline Message& operator <<(const T& val) {
+    // Some libraries overload << for STL containers.  These
+    // overloads are defined in the global namespace instead of ::std.
+    //
+    // C++'s symbol lookup rule (i.e. Koenig lookup) says that these
+    // overloads are visible in either the std namespace or the global
+    // namespace, but not other namespaces, including the testing
+    // namespace which Google Test's Message class is in.
+    //
+    // To allow STL containers (and other types that has a << operator
+    // defined in the global namespace) to be used in Google Test
+    // assertions, testing::Message must access the custom << operator
+    // from the global namespace.  With this using declaration,
+    // overloads of << defined in the global namespace and those
+    // visible via Koenig lookup are both exposed in this function.
+    using ::operator <<;
+    *ss_ << val;
+    return *this;
+  }
+
+  // Streams a pointer value to this object.
+  //
+  // This function is an overload of the previous one.  When you
+  // stream a pointer to a Message, this definition will be used as it
+  // is more specialized.  (The C++ Standard, section
+  // [temp.func.order].)  If you stream a non-pointer, then the
+  // previous definition will be used.
+  //
+  // The reason for this overload is that streaming a NULL pointer to
+  // ostream is undefined behavior.  Depending on the compiler, you
+  // may get "0", "(nil)", "(null)", or an access violation.  To
+  // ensure consistent result across compilers, we always treat NULL
+  // as "(null)".
+  template <typename T>
+  inline Message& operator <<(T* const& pointer) {  // NOLINT
+    if (pointer == nullptr) {
+      *ss_ << "(null)";
+    } else {
+      *ss_ << pointer;
+    }
+    return *this;
+  }
+
+  // Since the basic IO manipulators are overloaded for both narrow
+  // and wide streams, we have to provide this specialized definition
+  // of operator <<, even though its body is the same as the
+  // templatized version above.  Without this definition, streaming
+  // endl or other basic IO manipulators to Message will confuse the
+  // compiler.
+  Message& operator <<(BasicNarrowIoManip val) {
+    *ss_ << val;
+    return *this;
+  }
+
+  // Instead of 1/0, we want to see true/false for bool values.
+  Message& operator <<(bool b) {
+    return *this << (b ? "true" : "false");
+  }
+
+  // These two overloads allow streaming a wide C string to a Message
+  // using the UTF-8 encoding.
+  Message& operator <<(const wchar_t* wide_c_str);
+  Message& operator <<(wchar_t* wide_c_str);
+
+#if GTEST_HAS_STD_WSTRING
+  // Converts the given wide string to a narrow string using the UTF-8
+  // encoding, and streams the result to this Message object.
+  Message& operator <<(const ::std::wstring& wstr);
+#endif  // GTEST_HAS_STD_WSTRING
+
+  // Gets the text streamed to this object so far as an std::string.
+  // Each '\0' character in the buffer is replaced with "\\0".
+  //
+  // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+  std::string GetString() const;
+
+ private:
+  // We'll hold the text streamed to this object here.
+  const std::unique_ptr< ::std::stringstream> ss_;
+
+  // We declare (but don't implement) this to prevent the compiler
+  // from implementing the assignment operator.
+  void operator=(const Message&);
+};
+
+// Streams a Message to an ostream.
+inline std::ostream& operator <<(std::ostream& os, const Message& sb) {
+  return os << sb.GetString();
+}
+
+namespace internal {
+
+// Converts a streamable value to an std::string.  A NULL pointer is
+// converted to "(null)".  When the input value is a ::string,
+// ::std::string, ::wstring, or ::std::wstring object, each NUL
+// character in it is replaced with "\\0".
+template <typename T>
+std::string StreamableToString(const T& streamable) {
+  return (Message() << streamable).GetString();
+}
+
+}  // namespace internal
+}  // namespace testing
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-param-test.h b/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-param-test.h
new file mode 100644 (file)
index 0000000..5558117
--- /dev/null
@@ -0,0 +1,505 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Macros and functions for implementing parameterized tests
+// in Google C++ Testing and Mocking Framework (Google Test)
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
+
+// Value-parameterized tests allow you to test your code with different
+// parameters without writing multiple copies of the same test.
+//
+// Here is how you use value-parameterized tests:
+
+#if 0
+
+// To write value-parameterized tests, first you should define a fixture
+// class. It is usually derived from testing::TestWithParam<T> (see below for
+// another inheritance scheme that's sometimes useful in more complicated
+// class hierarchies), where the type of your parameter values.
+// TestWithParam<T> is itself derived from testing::Test. T can be any
+// copyable type. If it's a raw pointer, you are responsible for managing the
+// lifespan of the pointed values.
+
+class FooTest : public ::testing::TestWithParam<const char*> {
+  // You can implement all the usual class fixture members here.
+};
+
+// Then, use the TEST_P macro to define as many parameterized tests
+// for this fixture as you want. The _P suffix is for "parameterized"
+// or "pattern", whichever you prefer to think.
+
+TEST_P(FooTest, DoesBlah) {
+  // Inside a test, access the test parameter with the GetParam() method
+  // of the TestWithParam<T> class:
+  EXPECT_TRUE(foo.Blah(GetParam()));
+  ...
+}
+
+TEST_P(FooTest, HasBlahBlah) {
+  ...
+}
+
+// Finally, you can use INSTANTIATE_TEST_SUITE_P to instantiate the test
+// case with any set of parameters you want. Google Test defines a number
+// of functions for generating test parameters. They return what we call
+// (surprise!) parameter generators. Here is a summary of them, which
+// are all in the testing namespace:
+//
+//
+//  Range(begin, end [, step]) - Yields values {begin, begin+step,
+//                               begin+step+step, ...}. The values do not
+//                               include end. step defaults to 1.
+//  Values(v1, v2, ..., vN)    - Yields values {v1, v2, ..., vN}.
+//  ValuesIn(container)        - Yields values from a C-style array, an STL
+//  ValuesIn(begin,end)          container, or an iterator range [begin, end).
+//  Bool()                     - Yields sequence {false, true}.
+//  Combine(g1, g2, ..., gN)   - Yields all combinations (the Cartesian product
+//                               for the math savvy) of the values generated
+//                               by the N generators.
+//
+// For more details, see comments at the definitions of these functions below
+// in this file.
+//
+// The following statement will instantiate tests from the FooTest test suite
+// each with parameter values "meeny", "miny", and "moe".
+
+INSTANTIATE_TEST_SUITE_P(InstantiationName,
+                         FooTest,
+                         Values("meeny", "miny", "moe"));
+
+// To distinguish different instances of the pattern, (yes, you
+// can instantiate it more than once) the first argument to the
+// INSTANTIATE_TEST_SUITE_P macro is a prefix that will be added to the
+// actual test suite name. Remember to pick unique prefixes for different
+// instantiations. The tests from the instantiation above will have
+// these names:
+//
+//    * InstantiationName/FooTest.DoesBlah/0 for "meeny"
+//    * InstantiationName/FooTest.DoesBlah/1 for "miny"
+//    * InstantiationName/FooTest.DoesBlah/2 for "moe"
+//    * InstantiationName/FooTest.HasBlahBlah/0 for "meeny"
+//    * InstantiationName/FooTest.HasBlahBlah/1 for "miny"
+//    * InstantiationName/FooTest.HasBlahBlah/2 for "moe"
+//
+// You can use these names in --gtest_filter.
+//
+// This statement will instantiate all tests from FooTest again, each
+// with parameter values "cat" and "dog":
+
+const char* pets[] = {"cat", "dog"};
+INSTANTIATE_TEST_SUITE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
+
+// The tests from the instantiation above will have these names:
+//
+//    * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat"
+//    * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog"
+//    * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat"
+//    * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog"
+//
+// Please note that INSTANTIATE_TEST_SUITE_P will instantiate all tests
+// in the given test suite, whether their definitions come before or
+// AFTER the INSTANTIATE_TEST_SUITE_P statement.
+//
+// Please also note that generator expressions (including parameters to the
+// generators) are evaluated in InitGoogleTest(), after main() has started.
+// This allows the user on one hand, to adjust generator parameters in order
+// to dynamically determine a set of tests to run and on the other hand,
+// give the user a chance to inspect the generated tests with Google Test
+// reflection API before RUN_ALL_TESTS() is executed.
+//
+// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc
+// for more examples.
+//
+// In the future, we plan to publish the API for defining new parameter
+// generators. But for now this interface remains part of the internal
+// implementation and is subject to change.
+//
+//
+// A parameterized test fixture must be derived from testing::Test and from
+// testing::WithParamInterface<T>, where T is the type of the parameter
+// values. Inheriting from TestWithParam<T> satisfies that requirement because
+// TestWithParam<T> inherits from both Test and WithParamInterface. In more
+// complicated hierarchies, however, it is occasionally useful to inherit
+// separately from Test and WithParamInterface. For example:
+
+class BaseTest : public ::testing::Test {
+  // You can inherit all the usual members for a non-parameterized test
+  // fixture here.
+};
+
+class DerivedTest : public BaseTest, public ::testing::WithParamInterface<int> {
+  // The usual test fixture members go here too.
+};
+
+TEST_F(BaseTest, HasFoo) {
+  // This is an ordinary non-parameterized test.
+}
+
+TEST_P(DerivedTest, DoesBlah) {
+  // GetParam works just the same here as if you inherit from TestWithParam.
+  EXPECT_TRUE(foo.Blah(GetParam()));
+}
+
+#endif  // 0
+
+#include <iterator>
+#include <utility>
+
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-param-util.h"
+#include "gtest/internal/gtest-port.h"
+
+namespace testing {
+
+// Functions producing parameter generators.
+//
+// Google Test uses these generators to produce parameters for value-
+// parameterized tests. When a parameterized test suite is instantiated
+// with a particular generator, Google Test creates and runs tests
+// for each element in the sequence produced by the generator.
+//
+// In the following sample, tests from test suite FooTest are instantiated
+// each three times with parameter values 3, 5, and 8:
+//
+// class FooTest : public TestWithParam<int> { ... };
+//
+// TEST_P(FooTest, TestThis) {
+// }
+// TEST_P(FooTest, TestThat) {
+// }
+// INSTANTIATE_TEST_SUITE_P(TestSequence, FooTest, Values(3, 5, 8));
+//
+
+// Range() returns generators providing sequences of values in a range.
+//
+// Synopsis:
+// Range(start, end)
+//   - returns a generator producing a sequence of values {start, start+1,
+//     start+2, ..., }.
+// Range(start, end, step)
+//   - returns a generator producing a sequence of values {start, start+step,
+//     start+step+step, ..., }.
+// Notes:
+//   * The generated sequences never include end. For example, Range(1, 5)
+//     returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2)
+//     returns a generator producing {1, 3, 5, 7}.
+//   * start and end must have the same type. That type may be any integral or
+//     floating-point type or a user defined type satisfying these conditions:
+//     * It must be assignable (have operator=() defined).
+//     * It must have operator+() (operator+(int-compatible type) for
+//       two-operand version).
+//     * It must have operator<() defined.
+//     Elements in the resulting sequences will also have that type.
+//   * Condition start < end must be satisfied in order for resulting sequences
+//     to contain any elements.
+//
+template <typename T, typename IncrementT>
+internal::ParamGenerator<T> Range(T start, T end, IncrementT step) {
+  return internal::ParamGenerator<T>(
+      new internal::RangeGenerator<T, IncrementT>(start, end, step));
+}
+
+template <typename T>
+internal::ParamGenerator<T> Range(T start, T end) {
+  return Range(start, end, 1);
+}
+
+// ValuesIn() function allows generation of tests with parameters coming from
+// a container.
+//
+// Synopsis:
+// ValuesIn(const T (&array)[N])
+//   - returns a generator producing sequences with elements from
+//     a C-style array.
+// ValuesIn(const Container& container)
+//   - returns a generator producing sequences with elements from
+//     an STL-style container.
+// ValuesIn(Iterator begin, Iterator end)
+//   - returns a generator producing sequences with elements from
+//     a range [begin, end) defined by a pair of STL-style iterators. These
+//     iterators can also be plain C pointers.
+//
+// Please note that ValuesIn copies the values from the containers
+// passed in and keeps them to generate tests in RUN_ALL_TESTS().
+//
+// Examples:
+//
+// This instantiates tests from test suite StringTest
+// each with C-string values of "foo", "bar", and "baz":
+//
+// const char* strings[] = {"foo", "bar", "baz"};
+// INSTANTIATE_TEST_SUITE_P(StringSequence, StringTest, ValuesIn(strings));
+//
+// This instantiates tests from test suite StlStringTest
+// each with STL strings with values "a" and "b":
+//
+// ::std::vector< ::std::string> GetParameterStrings() {
+//   ::std::vector< ::std::string> v;
+//   v.push_back("a");
+//   v.push_back("b");
+//   return v;
+// }
+//
+// INSTANTIATE_TEST_SUITE_P(CharSequence,
+//                          StlStringTest,
+//                          ValuesIn(GetParameterStrings()));
+//
+//
+// This will also instantiate tests from CharTest
+// each with parameter values 'a' and 'b':
+//
+// ::std::list<char> GetParameterChars() {
+//   ::std::list<char> list;
+//   list.push_back('a');
+//   list.push_back('b');
+//   return list;
+// }
+// ::std::list<char> l = GetParameterChars();
+// INSTANTIATE_TEST_SUITE_P(CharSequence2,
+//                          CharTest,
+//                          ValuesIn(l.begin(), l.end()));
+//
+template <typename ForwardIterator>
+internal::ParamGenerator<
+    typename std::iterator_traits<ForwardIterator>::value_type>
+ValuesIn(ForwardIterator begin, ForwardIterator end) {
+  typedef typename std::iterator_traits<ForwardIterator>::value_type ParamType;
+  return internal::ParamGenerator<ParamType>(
+      new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end));
+}
+
+template <typename T, size_t N>
+internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) {
+  return ValuesIn(array, array + N);
+}
+
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+    const Container& container) {
+  return ValuesIn(container.begin(), container.end());
+}
+
+// Values() allows generating tests from explicitly specified list of
+// parameters.
+//
+// Synopsis:
+// Values(T v1, T v2, ..., T vN)
+//   - returns a generator producing sequences with elements v1, v2, ..., vN.
+//
+// For example, this instantiates tests from test suite BarTest each
+// with values "one", "two", and "three":
+//
+// INSTANTIATE_TEST_SUITE_P(NumSequence,
+//                          BarTest,
+//                          Values("one", "two", "three"));
+//
+// This instantiates tests from test suite BazTest each with values 1, 2, 3.5.
+// The exact type of values will depend on the type of parameter in BazTest.
+//
+// INSTANTIATE_TEST_SUITE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5));
+//
+//
+template <typename... T>
+internal::ValueArray<T...> Values(T... v) {
+  return internal::ValueArray<T...>(std::move(v)...);
+}
+
+// Bool() allows generating tests with parameters in a set of (false, true).
+//
+// Synopsis:
+// Bool()
+//   - returns a generator producing sequences with elements {false, true}.
+//
+// It is useful when testing code that depends on Boolean flags. Combinations
+// of multiple flags can be tested when several Bool()'s are combined using
+// Combine() function.
+//
+// In the following example all tests in the test suite FlagDependentTest
+// will be instantiated twice with parameters false and true.
+//
+// class FlagDependentTest : public testing::TestWithParam<bool> {
+//   virtual void SetUp() {
+//     external_flag = GetParam();
+//   }
+// }
+// INSTANTIATE_TEST_SUITE_P(BoolSequence, FlagDependentTest, Bool());
+//
+inline internal::ParamGenerator<bool> Bool() {
+  return Values(false, true);
+}
+
+// Combine() allows the user to combine two or more sequences to produce
+// values of a Cartesian product of those sequences' elements.
+//
+// Synopsis:
+// Combine(gen1, gen2, ..., genN)
+//   - returns a generator producing sequences with elements coming from
+//     the Cartesian product of elements from the sequences generated by
+//     gen1, gen2, ..., genN. The sequence elements will have a type of
+//     std::tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types
+//     of elements from sequences produces by gen1, gen2, ..., genN.
+//
+// Example:
+//
+// This will instantiate tests in test suite AnimalTest each one with
+// the parameter values tuple("cat", BLACK), tuple("cat", WHITE),
+// tuple("dog", BLACK), and tuple("dog", WHITE):
+//
+// enum Color { BLACK, GRAY, WHITE };
+// class AnimalTest
+//     : public testing::TestWithParam<std::tuple<const char*, Color> > {...};
+//
+// TEST_P(AnimalTest, AnimalLooksNice) {...}
+//
+// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest,
+//                          Combine(Values("cat", "dog"),
+//                                  Values(BLACK, WHITE)));
+//
+// This will instantiate tests in FlagDependentTest with all variations of two
+// Boolean flags:
+//
+// class FlagDependentTest
+//     : public testing::TestWithParam<std::tuple<bool, bool> > {
+//   virtual void SetUp() {
+//     // Assigns external_flag_1 and external_flag_2 values from the tuple.
+//     std::tie(external_flag_1, external_flag_2) = GetParam();
+//   }
+// };
+//
+// TEST_P(FlagDependentTest, TestFeature1) {
+//   // Test your code using external_flag_1 and external_flag_2 here.
+// }
+// INSTANTIATE_TEST_SUITE_P(TwoBoolSequence, FlagDependentTest,
+//                          Combine(Bool(), Bool()));
+//
+template <typename... Generator>
+internal::CartesianProductHolder<Generator...> Combine(const Generator&... g) {
+  return internal::CartesianProductHolder<Generator...>(g...);
+}
+
+#define TEST_P(test_suite_name, test_name)                                     \
+  class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                     \
+      : public test_suite_name {                                               \
+   public:                                                                     \
+    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {}                    \
+    void TestBody() override;                                                  \
+                                                                               \
+   private:                                                                    \
+    static int AddToRegistry() {                                               \
+      ::testing::UnitTest::GetInstance()                                       \
+          ->parameterized_test_registry()                                      \
+          .GetTestSuitePatternHolder<test_suite_name>(                         \
+              GTEST_STRINGIFY_(test_suite_name),                               \
+              ::testing::internal::CodeLocation(__FILE__, __LINE__))           \
+          ->AddTestPattern(                                                    \
+              GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name),  \
+              new ::testing::internal::TestMetaFactory<GTEST_TEST_CLASS_NAME_( \
+                  test_suite_name, test_name)>(),                              \
+              ::testing::internal::CodeLocation(__FILE__, __LINE__));          \
+      return 0;                                                                \
+    }                                                                          \
+    static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_;               \
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name,    \
+                                                           test_name));        \
+  };                                                                           \
+  int GTEST_TEST_CLASS_NAME_(test_suite_name,                                  \
+                             test_name)::gtest_registering_dummy_ =            \
+      GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry();     \
+  void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()
+
+// The last argument to INSTANTIATE_TEST_SUITE_P allows the user to specify
+// generator and an optional function or functor that generates custom test name
+// suffixes based on the test parameters. Such a function or functor should
+// accept one argument of type testing::TestParamInfo<class ParamType>, and
+// return std::string.
+//
+// testing::PrintToStringParamName is a builtin test suffix generator that
+// returns the value of testing::PrintToString(GetParam()).
+//
+// Note: test names must be non-empty, unique, and may only contain ASCII
+// alphanumeric characters or underscore. Because PrintToString adds quotes
+// to std::string and C strings, it won't work for these types.
+
+#define GTEST_EXPAND_(arg) arg
+#define GTEST_GET_FIRST_(first, ...) first
+#define GTEST_GET_SECOND_(first, second, ...) second
+
+#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...)                \
+  static ::testing::internal::ParamGenerator<test_suite_name::ParamType>      \
+      gtest_##prefix##test_suite_name##_EvalGenerator_() {                    \
+    return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_));        \
+  }                                                                           \
+  static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_(   \
+      const ::testing::TestParamInfo<test_suite_name::ParamType>& info) {     \
+    if (::testing::internal::AlwaysFalse()) {                                 \
+      ::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_(      \
+          __VA_ARGS__,                                                        \
+          ::testing::internal::DefaultParamName<test_suite_name::ParamType>,  \
+          DUMMY_PARAM_)));                                                    \
+      auto t = std::make_tuple(__VA_ARGS__);                                  \
+      static_assert(std::tuple_size<decltype(t)>::value <= 2,                 \
+                    "Too Many Args!");                                        \
+    }                                                                         \
+    return ((GTEST_EXPAND_(GTEST_GET_SECOND_(                                 \
+        __VA_ARGS__,                                                          \
+        ::testing::internal::DefaultParamName<test_suite_name::ParamType>,    \
+        DUMMY_PARAM_))))(info);                                               \
+  }                                                                           \
+  static int gtest_##prefix##test_suite_name##_dummy_                         \
+      GTEST_ATTRIBUTE_UNUSED_ =                                               \
+          ::testing::UnitTest::GetInstance()                                  \
+              ->parameterized_test_registry()                                 \
+              .GetTestSuitePatternHolder<test_suite_name>(                    \
+                  GTEST_STRINGIFY_(test_suite_name),                          \
+                  ::testing::internal::CodeLocation(__FILE__, __LINE__))      \
+              ->AddTestSuiteInstantiation(                                    \
+                  GTEST_STRINGIFY_(prefix),                                   \
+                  &gtest_##prefix##test_suite_name##_EvalGenerator_,          \
+                  &gtest_##prefix##test_suite_name##_EvalGenerateName_,       \
+                  __FILE__, __LINE__)
+
+
+// Allow Marking a Parameterized test class as not needing to be instantiated.
+#define GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(T)                   \
+  namespace gtest_do_not_use_outside_namespace_scope {}                   \
+  static const ::testing::internal::MarkAsIgnored gtest_allow_ignore_##T( \
+      GTEST_STRINGIFY_(T))
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+#define INSTANTIATE_TEST_CASE_P                                            \
+  static_assert(::testing::internal::InstantiateTestCase_P_IsDeprecated(), \
+                "");                                                       \
+  INSTANTIATE_TEST_SUITE_P
+#endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+}  // namespace testing
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-printers.h b/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-printers.h
new file mode 100644 (file)
index 0000000..2e33df4
--- /dev/null
@@ -0,0 +1,1027 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Google Test - The Google C++ Testing and Mocking Framework
+//
+// This file implements a universal value printer that can print a
+// value of any type T:
+//
+//   void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
+//
+// A user can teach this function how to print a class type T by
+// defining either operator<<() or PrintTo() in the namespace that
+// defines T.  More specifically, the FIRST defined function in the
+// following list will be used (assuming T is defined in namespace
+// foo):
+//
+//   1. foo::PrintTo(const T&, ostream*)
+//   2. operator<<(ostream&, const T&) defined in either foo or the
+//      global namespace.
+//
+// However if T is an STL-style container then it is printed element-wise
+// unless foo::PrintTo(const T&, ostream*) is defined. Note that
+// operator<<() is ignored for container types.
+//
+// If none of the above is defined, it will print the debug string of
+// the value if it is a protocol buffer, or print the raw bytes in the
+// value otherwise.
+//
+// To aid debugging: when T is a reference type, the address of the
+// value is also printed; when T is a (const) char pointer, both the
+// pointer value and the NUL-terminated string it points to are
+// printed.
+//
+// We also provide some convenient wrappers:
+//
+//   // Prints a value to a string.  For a (const or not) char
+//   // pointer, the NUL-terminated string (but not the pointer) is
+//   // printed.
+//   std::string ::testing::PrintToString(const T& value);
+//
+//   // Prints a value tersely: for a reference type, the referenced
+//   // value (but not the address) is printed; for a (const or not) char
+//   // pointer, the NUL-terminated string (but not the pointer) is
+//   // printed.
+//   void ::testing::internal::UniversalTersePrint(const T& value, ostream*);
+//
+//   // Prints value using the type inferred by the compiler.  The difference
+//   // from UniversalTersePrint() is that this function prints both the
+//   // pointer and the NUL-terminated string for a (const or not) char pointer.
+//   void ::testing::internal::UniversalPrint(const T& value, ostream*);
+//
+//   // Prints the fields of a tuple tersely to a string vector, one
+//   // element for each field. Tuple support must be enabled in
+//   // gtest-port.h.
+//   std::vector<string> UniversalTersePrintTupleFieldsToStrings(
+//       const Tuple& value);
+//
+// Known limitation:
+//
+// The print primitives print the elements of an STL-style container
+// using the compiler-inferred type of *iter where iter is a
+// const_iterator of the container.  When const_iterator is an input
+// iterator but not a forward iterator, this inferred type may not
+// match value_type, and the print output may be incorrect.  In
+// practice, this is rarely a problem as for most containers
+// const_iterator is a forward iterator.  We'll fix this if there's an
+// actual need for it.  Note that this fix cannot rely on value_type
+// being defined as many user-defined container types don't have
+// value_type.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
+
+#include <functional>
+#include <memory>
+#include <ostream>  // NOLINT
+#include <sstream>
+#include <string>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-port.h"
+
+namespace testing {
+
+// Definitions in the internal* namespaces are subject to change without notice.
+// DO NOT USE THEM IN USER CODE!
+namespace internal {
+
+template <typename T>
+void UniversalPrint(const T& value, ::std::ostream* os);
+
+// Used to print an STL-style container when the user doesn't define
+// a PrintTo() for it.
+struct ContainerPrinter {
+  template <typename T,
+            typename = typename std::enable_if<
+                (sizeof(IsContainerTest<T>(0)) == sizeof(IsContainer)) &&
+                !IsRecursiveContainer<T>::value>::type>
+  static void PrintValue(const T& container, std::ostream* os) {
+    const size_t kMaxCount = 32;  // The maximum number of elements to print.
+    *os << '{';
+    size_t count = 0;
+    for (auto&& elem : container) {
+      if (count > 0) {
+        *os << ',';
+        if (count == kMaxCount) {  // Enough has been printed.
+          *os << " ...";
+          break;
+        }
+      }
+      *os << ' ';
+      // We cannot call PrintTo(elem, os) here as PrintTo() doesn't
+      // handle `elem` being a native array.
+      internal::UniversalPrint(elem, os);
+      ++count;
+    }
+
+    if (count > 0) {
+      *os << ' ';
+    }
+    *os << '}';
+  }
+};
+
+// Used to print a pointer that is neither a char pointer nor a member
+// pointer, when the user doesn't define PrintTo() for it.  (A member
+// variable pointer or member function pointer doesn't really point to
+// a location in the address space.  Their representation is
+// implementation-defined.  Therefore they will be printed as raw
+// bytes.)
+struct FunctionPointerPrinter {
+  template <typename T, typename = typename std::enable_if<
+                            std::is_function<T>::value>::type>
+  static void PrintValue(T* p, ::std::ostream* os) {
+    if (p == nullptr) {
+      *os << "NULL";
+    } else {
+      // T is a function type, so '*os << p' doesn't do what we want
+      // (it just prints p as bool).  We want to print p as a const
+      // void*.
+      *os << reinterpret_cast<const void*>(p);
+    }
+  }
+};
+
+struct PointerPrinter {
+  template <typename T>
+  static void PrintValue(T* p, ::std::ostream* os) {
+    if (p == nullptr) {
+      *os << "NULL";
+    } else {
+      // T is not a function type.  We just call << to print p,
+      // relying on ADL to pick up user-defined << for their pointer
+      // types, if any.
+      *os << p;
+    }
+  }
+};
+
+namespace internal_stream_operator_without_lexical_name_lookup {
+
+// The presence of an operator<< here will terminate lexical scope lookup
+// straight away (even though it cannot be a match because of its argument
+// types). Thus, the two operator<< calls in StreamPrinter will find only ADL
+// candidates.
+struct LookupBlocker {};
+void operator<<(LookupBlocker, LookupBlocker);
+
+struct StreamPrinter {
+  template <typename T,
+            // Don't accept member pointers here. We'd print them via implicit
+            // conversion to bool, which isn't useful.
+            typename = typename std::enable_if<
+                !std::is_member_pointer<T>::value>::type,
+            // Only accept types for which we can find a streaming operator via
+            // ADL (possibly involving implicit conversions).
+            typename = decltype(std::declval<std::ostream&>()
+                                << std::declval<const T&>())>
+  static void PrintValue(const T& value, ::std::ostream* os) {
+    // Call streaming operator found by ADL, possibly with implicit conversions
+    // of the arguments.
+    *os << value;
+  }
+};
+
+}  // namespace internal_stream_operator_without_lexical_name_lookup
+
+struct ProtobufPrinter {
+  // We print a protobuf using its ShortDebugString() when the string
+  // doesn't exceed this many characters; otherwise we print it using
+  // DebugString() for better readability.
+  static const size_t kProtobufOneLinerMaxLength = 50;
+
+  template <typename T,
+            typename = typename std::enable_if<
+                internal::HasDebugStringAndShortDebugString<T>::value>::type>
+  static void PrintValue(const T& value, ::std::ostream* os) {
+    std::string pretty_str = value.ShortDebugString();
+    if (pretty_str.length() > kProtobufOneLinerMaxLength) {
+      pretty_str = "\n" + value.DebugString();
+    }
+    *os << ("<" + pretty_str + ">");
+  }
+};
+
+struct ConvertibleToIntegerPrinter {
+  // Since T has no << operator or PrintTo() but can be implicitly
+  // converted to BiggestInt, we print it as a BiggestInt.
+  //
+  // Most likely T is an enum type (either named or unnamed), in which
+  // case printing it as an integer is the desired behavior.  In case
+  // T is not an enum, printing it as an integer is the best we can do
+  // given that it has no user-defined printer.
+  static void PrintValue(internal::BiggestInt value, ::std::ostream* os) {
+    *os << value;
+  }
+};
+
+struct ConvertibleToStringViewPrinter {
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+  static void PrintValue(internal::StringView value, ::std::ostream* os) {
+    internal::UniversalPrint(value, os);
+  }
+#endif
+};
+
+
+// Prints the given number of bytes in the given object to the given
+// ostream.
+GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes,
+                                     size_t count,
+                                     ::std::ostream* os);
+struct RawBytesPrinter {
+  // SFINAE on `sizeof` to make sure we have a complete type.
+  template <typename T, size_t = sizeof(T)>
+  static void PrintValue(const T& value, ::std::ostream* os) {
+    PrintBytesInObjectTo(
+        static_cast<const unsigned char*>(
+            // Load bearing cast to void* to support iOS
+            reinterpret_cast<const void*>(std::addressof(value))),
+        sizeof(value), os);
+  }
+};
+
+struct FallbackPrinter {
+  template <typename T>
+  static void PrintValue(const T&, ::std::ostream* os) {
+    *os << "(incomplete type)";
+  }
+};
+
+// Try every printer in order and return the first one that works.
+template <typename T, typename E, typename Printer, typename... Printers>
+struct FindFirstPrinter : FindFirstPrinter<T, E, Printers...> {};
+
+template <typename T, typename Printer, typename... Printers>
+struct FindFirstPrinter<
+    T, decltype(Printer::PrintValue(std::declval<const T&>(), nullptr)),
+    Printer, Printers...> {
+  using type = Printer;
+};
+
+// Select the best printer in the following order:
+//  - Print containers (they have begin/end/etc).
+//  - Print function pointers.
+//  - Print object pointers.
+//  - Use the stream operator, if available.
+//  - Print protocol buffers.
+//  - Print types convertible to BiggestInt.
+//  - Print types convertible to StringView, if available.
+//  - Fallback to printing the raw bytes of the object.
+template <typename T>
+void PrintWithFallback(const T& value, ::std::ostream* os) {
+  using Printer = typename FindFirstPrinter<
+      T, void, ContainerPrinter, FunctionPointerPrinter, PointerPrinter,
+      internal_stream_operator_without_lexical_name_lookup::StreamPrinter,
+      ProtobufPrinter, ConvertibleToIntegerPrinter,
+      ConvertibleToStringViewPrinter, RawBytesPrinter, FallbackPrinter>::type;
+  Printer::PrintValue(value, os);
+}
+
+// FormatForComparison<ToPrint, OtherOperand>::Format(value) formats a
+// value of type ToPrint that is an operand of a comparison assertion
+// (e.g. ASSERT_EQ).  OtherOperand is the type of the other operand in
+// the comparison, and is used to help determine the best way to
+// format the value.  In particular, when the value is a C string
+// (char pointer) and the other operand is an STL string object, we
+// want to format the C string as a string, since we know it is
+// compared by value with the string object.  If the value is a char
+// pointer but the other operand is not an STL string object, we don't
+// know whether the pointer is supposed to point to a NUL-terminated
+// string, and thus want to print it as a pointer to be safe.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+
+// The default case.
+template <typename ToPrint, typename OtherOperand>
+class FormatForComparison {
+ public:
+  static ::std::string Format(const ToPrint& value) {
+    return ::testing::PrintToString(value);
+  }
+};
+
+// Array.
+template <typename ToPrint, size_t N, typename OtherOperand>
+class FormatForComparison<ToPrint[N], OtherOperand> {
+ public:
+  static ::std::string Format(const ToPrint* value) {
+    return FormatForComparison<const ToPrint*, OtherOperand>::Format(value);
+  }
+};
+
+// By default, print C string as pointers to be safe, as we don't know
+// whether they actually point to a NUL-terminated string.
+
+#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType)                \
+  template <typename OtherOperand>                                      \
+  class FormatForComparison<CharType*, OtherOperand> {                  \
+   public:                                                              \
+    static ::std::string Format(CharType* value) {                      \
+      return ::testing::PrintToString(static_cast<const void*>(value)); \
+    }                                                                   \
+  }
+
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t);
+#ifdef __cpp_lib_char8_t
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char8_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char8_t);
+#endif
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char16_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char16_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char32_t);
+GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char32_t);
+
+#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_
+
+// If a C string is compared with an STL string object, we know it's meant
+// to point to a NUL-terminated string, and thus can print it as a string.
+
+#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \
+  template <>                                                           \
+  class FormatForComparison<CharType*, OtherStringType> {               \
+   public:                                                              \
+    static ::std::string Format(CharType* value) {                      \
+      return ::testing::PrintToString(value);                           \
+    }                                                                   \
+  }
+
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string);
+#ifdef __cpp_char8_t
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char8_t, ::std::u8string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char8_t, ::std::u8string);
+#endif
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char16_t, ::std::u16string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char16_t, ::std::u16string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char32_t, ::std::u32string);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char32_t, ::std::u32string);
+
+#if GTEST_HAS_STD_WSTRING
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring);
+GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring);
+#endif
+
+#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_
+
+// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc)
+// operand to be used in a failure message.  The type (but not value)
+// of the other operand may affect the format.  This allows us to
+// print a char* as a raw pointer when it is compared against another
+// char* or void*, and print it as a C string when it is compared
+// against an std::string object, for example.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+template <typename T1, typename T2>
+std::string FormatForComparisonFailureMessage(
+    const T1& value, const T2& /* other_operand */) {
+  return FormatForComparison<T1, T2>::Format(value);
+}
+
+// UniversalPrinter<T>::Print(value, ostream_ptr) prints the given
+// value to the given ostream.  The caller must ensure that
+// 'ostream_ptr' is not NULL, or the behavior is undefined.
+//
+// We define UniversalPrinter as a class template (as opposed to a
+// function template), as we need to partially specialize it for
+// reference types, which cannot be done with function templates.
+template <typename T>
+class UniversalPrinter;
+
+// Prints the given value using the << operator if it has one;
+// otherwise prints the bytes in it.  This is what
+// UniversalPrinter<T>::Print() does when PrintTo() is not specialized
+// or overloaded for type T.
+//
+// A user can override this behavior for a class type Foo by defining
+// an overload of PrintTo() in the namespace where Foo is defined.  We
+// give the user this option as sometimes defining a << operator for
+// Foo is not desirable (e.g. the coding style may prevent doing it,
+// or there is already a << operator but it doesn't do what the user
+// wants).
+template <typename T>
+void PrintTo(const T& value, ::std::ostream* os) {
+  internal::PrintWithFallback(value, os);
+}
+
+// The following list of PrintTo() overloads tells
+// UniversalPrinter<T>::Print() how to print standard types (built-in
+// types, strings, plain arrays, and pointers).
+
+// Overloads for various char types.
+GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os);
+GTEST_API_ void PrintTo(signed char c, ::std::ostream* os);
+inline void PrintTo(char c, ::std::ostream* os) {
+  // When printing a plain char, we always treat it as unsigned.  This
+  // way, the output won't be affected by whether the compiler thinks
+  // char is signed or not.
+  PrintTo(static_cast<unsigned char>(c), os);
+}
+
+// Overloads for other simple built-in types.
+inline void PrintTo(bool x, ::std::ostream* os) {
+  *os << (x ? "true" : "false");
+}
+
+// Overload for wchar_t type.
+// Prints a wchar_t as a symbol if it is printable or as its internal
+// code otherwise and also as its decimal code (except for L'\0').
+// The L'\0' char is printed as "L'\\0'". The decimal code is printed
+// as signed integer when wchar_t is implemented by the compiler
+// as a signed type and is printed as an unsigned integer when wchar_t
+// is implemented as an unsigned type.
+GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os);
+
+GTEST_API_ void PrintTo(char32_t c, ::std::ostream* os);
+inline void PrintTo(char16_t c, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<char32_t>(c), os);
+}
+#ifdef __cpp_char8_t
+inline void PrintTo(char8_t c, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<char32_t>(c), os);
+}
+#endif
+
+// Overloads for C strings.
+GTEST_API_ void PrintTo(const char* s, ::std::ostream* os);
+inline void PrintTo(char* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const char*>(s), os);
+}
+
+// signed/unsigned char is often used for representing binary data, so
+// we print pointers to it as void* to be safe.
+inline void PrintTo(const signed char* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const void*>(s), os);
+}
+inline void PrintTo(signed char* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const void*>(s), os);
+}
+inline void PrintTo(const unsigned char* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const void*>(s), os);
+}
+inline void PrintTo(unsigned char* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const void*>(s), os);
+}
+#ifdef __cpp_char8_t
+// Overloads for u8 strings.
+GTEST_API_ void PrintTo(const char8_t* s, ::std::ostream* os);
+inline void PrintTo(char8_t* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const char8_t*>(s), os);
+}
+#endif
+// Overloads for u16 strings.
+GTEST_API_ void PrintTo(const char16_t* s, ::std::ostream* os);
+inline void PrintTo(char16_t* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const char16_t*>(s), os);
+}
+// Overloads for u32 strings.
+GTEST_API_ void PrintTo(const char32_t* s, ::std::ostream* os);
+inline void PrintTo(char32_t* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const char32_t*>(s), os);
+}
+
+// MSVC can be configured to define wchar_t as a typedef of unsigned
+// short.  It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native
+// type.  When wchar_t is a typedef, defining an overload for const
+// wchar_t* would cause unsigned short* be printed as a wide string,
+// possibly causing invalid memory accesses.
+#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
+// Overloads for wide C strings
+GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os);
+inline void PrintTo(wchar_t* s, ::std::ostream* os) {
+  PrintTo(ImplicitCast_<const wchar_t*>(s), os);
+}
+#endif
+
+// Overload for C arrays.  Multi-dimensional arrays are printed
+// properly.
+
+// Prints the given number of elements in an array, without printing
+// the curly braces.
+template <typename T>
+void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) {
+  UniversalPrint(a[0], os);
+  for (size_t i = 1; i != count; i++) {
+    *os << ", ";
+    UniversalPrint(a[i], os);
+  }
+}
+
+// Overloads for ::std::string.
+GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os);
+inline void PrintTo(const ::std::string& s, ::std::ostream* os) {
+  PrintStringTo(s, os);
+}
+
+// Overloads for ::std::u8string
+#ifdef __cpp_char8_t
+GTEST_API_ void PrintU8StringTo(const ::std::u8string& s, ::std::ostream* os);
+inline void PrintTo(const ::std::u8string& s, ::std::ostream* os) {
+  PrintU8StringTo(s, os);
+}
+#endif
+
+// Overloads for ::std::u16string
+GTEST_API_ void PrintU16StringTo(const ::std::u16string& s, ::std::ostream* os);
+inline void PrintTo(const ::std::u16string& s, ::std::ostream* os) {
+  PrintU16StringTo(s, os);
+}
+
+// Overloads for ::std::u32string
+GTEST_API_ void PrintU32StringTo(const ::std::u32string& s, ::std::ostream* os);
+inline void PrintTo(const ::std::u32string& s, ::std::ostream* os) {
+  PrintU32StringTo(s, os);
+}
+
+// Overloads for ::std::wstring.
+#if GTEST_HAS_STD_WSTRING
+GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os);
+inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) {
+  PrintWideStringTo(s, os);
+}
+#endif  // GTEST_HAS_STD_WSTRING
+
+#if GTEST_INTERNAL_HAS_STRING_VIEW
+// Overload for internal::StringView.
+inline void PrintTo(internal::StringView sp, ::std::ostream* os) {
+  PrintTo(::std::string(sp), os);
+}
+#endif  // GTEST_INTERNAL_HAS_STRING_VIEW
+
+inline void PrintTo(std::nullptr_t, ::std::ostream* os) { *os << "(nullptr)"; }
+
+template <typename T>
+void PrintTo(std::reference_wrapper<T> ref, ::std::ostream* os) {
+  UniversalPrinter<T&>::Print(ref.get(), os);
+}
+
+inline const void* VoidifyPointer(const void* p) { return p; }
+inline const void* VoidifyPointer(volatile const void* p) {
+  return const_cast<const void*>(p);
+}
+
+template <typename T, typename Ptr>
+void PrintSmartPointer(const Ptr& ptr, std::ostream* os, char) {
+  if (ptr == nullptr) {
+    *os << "(nullptr)";
+  } else {
+    // We can't print the value. Just print the pointer..
+    *os << "(" << (VoidifyPointer)(ptr.get()) << ")";
+  }
+}
+template <typename T, typename Ptr,
+          typename = typename std::enable_if<!std::is_void<T>::value &&
+                                             !std::is_array<T>::value>::type>
+void PrintSmartPointer(const Ptr& ptr, std::ostream* os, int) {
+  if (ptr == nullptr) {
+    *os << "(nullptr)";
+  } else {
+    *os << "(ptr = " << (VoidifyPointer)(ptr.get()) << ", value = ";
+    UniversalPrinter<T>::Print(*ptr, os);
+    *os << ")";
+  }
+}
+
+template <typename T, typename D>
+void PrintTo(const std::unique_ptr<T, D>& ptr, std::ostream* os) {
+  (PrintSmartPointer<T>)(ptr, os, 0);
+}
+
+template <typename T>
+void PrintTo(const std::shared_ptr<T>& ptr, std::ostream* os) {
+  (PrintSmartPointer<T>)(ptr, os, 0);
+}
+
+// Helper function for printing a tuple.  T must be instantiated with
+// a tuple type.
+template <typename T>
+void PrintTupleTo(const T&, std::integral_constant<size_t, 0>,
+                  ::std::ostream*) {}
+
+template <typename T, size_t I>
+void PrintTupleTo(const T& t, std::integral_constant<size_t, I>,
+                  ::std::ostream* os) {
+  PrintTupleTo(t, std::integral_constant<size_t, I - 1>(), os);
+  GTEST_INTENTIONAL_CONST_COND_PUSH_()
+  if (I > 1) {
+    GTEST_INTENTIONAL_CONST_COND_POP_()
+    *os << ", ";
+  }
+  UniversalPrinter<typename std::tuple_element<I - 1, T>::type>::Print(
+      std::get<I - 1>(t), os);
+}
+
+template <typename... Types>
+void PrintTo(const ::std::tuple<Types...>& t, ::std::ostream* os) {
+  *os << "(";
+  PrintTupleTo(t, std::integral_constant<size_t, sizeof...(Types)>(), os);
+  *os << ")";
+}
+
+// Overload for std::pair.
+template <typename T1, typename T2>
+void PrintTo(const ::std::pair<T1, T2>& value, ::std::ostream* os) {
+  *os << '(';
+  // We cannot use UniversalPrint(value.first, os) here, as T1 may be
+  // a reference type.  The same for printing value.second.
+  UniversalPrinter<T1>::Print(value.first, os);
+  *os << ", ";
+  UniversalPrinter<T2>::Print(value.second, os);
+  *os << ')';
+}
+
+// Implements printing a non-reference type T by letting the compiler
+// pick the right overload of PrintTo() for T.
+template <typename T>
+class UniversalPrinter {
+ public:
+  // MSVC warns about adding const to a function type, so we want to
+  // disable the warning.
+  GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180)
+
+  // Note: we deliberately don't call this PrintTo(), as that name
+  // conflicts with ::testing::internal::PrintTo in the body of the
+  // function.
+  static void Print(const T& value, ::std::ostream* os) {
+    // By default, ::testing::internal::PrintTo() is used for printing
+    // the value.
+    //
+    // Thanks to Koenig look-up, if T is a class and has its own
+    // PrintTo() function defined in its namespace, that function will
+    // be visible here.  Since it is more specific than the generic ones
+    // in ::testing::internal, it will be picked by the compiler in the
+    // following statement - exactly what we want.
+    PrintTo(value, os);
+  }
+
+  GTEST_DISABLE_MSC_WARNINGS_POP_()
+};
+
+// Remove any const-qualifiers before passing a type to UniversalPrinter.
+template <typename T>
+class UniversalPrinter<const T> : public UniversalPrinter<T> {};
+
+#if GTEST_INTERNAL_HAS_ANY
+
+// Printer for std::any / absl::any
+
+template <>
+class UniversalPrinter<Any> {
+ public:
+  static void Print(const Any& value, ::std::ostream* os) {
+    if (value.has_value()) {
+      *os << "value of type " << GetTypeName(value);
+    } else {
+      *os << "no value";
+    }
+  }
+
+ private:
+  static std::string GetTypeName(const Any& value) {
+#if GTEST_HAS_RTTI
+    return internal::GetTypeName(value.type());
+#else
+    static_cast<void>(value);  // possibly unused
+    return "<unknown_type>";
+#endif  // GTEST_HAS_RTTI
+  }
+};
+
+#endif  // GTEST_INTERNAL_HAS_ANY
+
+#if GTEST_INTERNAL_HAS_OPTIONAL
+
+// Printer for std::optional / absl::optional
+
+template <typename T>
+class UniversalPrinter<Optional<T>> {
+ public:
+  static void Print(const Optional<T>& value, ::std::ostream* os) {
+    *os << '(';
+    if (!value) {
+      *os << "nullopt";
+    } else {
+      UniversalPrint(*value, os);
+    }
+    *os << ')';
+  }
+};
+
+#endif  // GTEST_INTERNAL_HAS_OPTIONAL
+
+#if GTEST_INTERNAL_HAS_VARIANT
+
+// Printer for std::variant / absl::variant
+
+template <typename... T>
+class UniversalPrinter<Variant<T...>> {
+ public:
+  static void Print(const Variant<T...>& value, ::std::ostream* os) {
+    *os << '(';
+#if GTEST_HAS_ABSL
+    absl::visit(Visitor{os, value.index()}, value);
+#else
+    std::visit(Visitor{os, value.index()}, value);
+#endif  // GTEST_HAS_ABSL
+    *os << ')';
+  }
+
+ private:
+  struct Visitor {
+    template <typename U>
+    void operator()(const U& u) const {
+      *os << "'" << GetTypeName<U>() << "(index = " << index
+          << ")' with value ";
+      UniversalPrint(u, os);
+    }
+    ::std::ostream* os;
+    std::size_t index;
+  };
+};
+
+#endif  // GTEST_INTERNAL_HAS_VARIANT
+
+// UniversalPrintArray(begin, len, os) prints an array of 'len'
+// elements, starting at address 'begin'.
+template <typename T>
+void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) {
+  if (len == 0) {
+    *os << "{}";
+  } else {
+    *os << "{ ";
+    const size_t kThreshold = 18;
+    const size_t kChunkSize = 8;
+    // If the array has more than kThreshold elements, we'll have to
+    // omit some details by printing only the first and the last
+    // kChunkSize elements.
+    if (len <= kThreshold) {
+      PrintRawArrayTo(begin, len, os);
+    } else {
+      PrintRawArrayTo(begin, kChunkSize, os);
+      *os << ", ..., ";
+      PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os);
+    }
+    *os << " }";
+  }
+}
+// This overload prints a (const) char array compactly.
+GTEST_API_ void UniversalPrintArray(
+    const char* begin, size_t len, ::std::ostream* os);
+
+#ifdef __cpp_char8_t
+// This overload prints a (const) char8_t array compactly.
+GTEST_API_ void UniversalPrintArray(const char8_t* begin, size_t len,
+                                    ::std::ostream* os);
+#endif
+
+// This overload prints a (const) char16_t array compactly.
+GTEST_API_ void UniversalPrintArray(const char16_t* begin, size_t len,
+                                    ::std::ostream* os);
+
+// This overload prints a (const) char32_t array compactly.
+GTEST_API_ void UniversalPrintArray(const char32_t* begin, size_t len,
+                                    ::std::ostream* os);
+
+// This overload prints a (const) wchar_t array compactly.
+GTEST_API_ void UniversalPrintArray(
+    const wchar_t* begin, size_t len, ::std::ostream* os);
+
+// Implements printing an array type T[N].
+template <typename T, size_t N>
+class UniversalPrinter<T[N]> {
+ public:
+  // Prints the given array, omitting some elements when there are too
+  // many.
+  static void Print(const T (&a)[N], ::std::ostream* os) {
+    UniversalPrintArray(a, N, os);
+  }
+};
+
+// Implements printing a reference type T&.
+template <typename T>
+class UniversalPrinter<T&> {
+ public:
+  // MSVC warns about adding const to a function type, so we want to
+  // disable the warning.
+  GTEST_DISABLE_MSC_WARNINGS_PUSH_(4180)
+
+  static void Print(const T& value, ::std::ostream* os) {
+    // Prints the address of the value.  We use reinterpret_cast here
+    // as static_cast doesn't compile when T is a function type.
+    *os << "@" << reinterpret_cast<const void*>(&value) << " ";
+
+    // Then prints the value itself.
+    UniversalPrint(value, os);
+  }
+
+  GTEST_DISABLE_MSC_WARNINGS_POP_()
+};
+
+// Prints a value tersely: for a reference type, the referenced value
+// (but not the address) is printed; for a (const) char pointer, the
+// NUL-terminated string (but not the pointer) is printed.
+
+template <typename T>
+class UniversalTersePrinter {
+ public:
+  static void Print(const T& value, ::std::ostream* os) {
+    UniversalPrint(value, os);
+  }
+};
+template <typename T>
+class UniversalTersePrinter<T&> {
+ public:
+  static void Print(const T& value, ::std::ostream* os) {
+    UniversalPrint(value, os);
+  }
+};
+template <typename T, size_t N>
+class UniversalTersePrinter<T[N]> {
+ public:
+  static void Print(const T (&value)[N], ::std::ostream* os) {
+    UniversalPrinter<T[N]>::Print(value, os);
+  }
+};
+template <>
+class UniversalTersePrinter<const char*> {
+ public:
+  static void Print(const char* str, ::std::ostream* os) {
+    if (str == nullptr) {
+      *os << "NULL";
+    } else {
+      UniversalPrint(std::string(str), os);
+    }
+  }
+};
+template <>
+class UniversalTersePrinter<char*> : public UniversalTersePrinter<const char*> {
+};
+
+#ifdef __cpp_char8_t
+template <>
+class UniversalTersePrinter<const char8_t*> {
+ public:
+  static void Print(const char8_t* str, ::std::ostream* os) {
+    if (str == nullptr) {
+      *os << "NULL";
+    } else {
+      UniversalPrint(::std::u8string(str), os);
+    }
+  }
+};
+template <>
+class UniversalTersePrinter<char8_t*>
+    : public UniversalTersePrinter<const char8_t*> {};
+#endif
+
+template <>
+class UniversalTersePrinter<const char16_t*> {
+ public:
+  static void Print(const char16_t* str, ::std::ostream* os) {
+    if (str == nullptr) {
+      *os << "NULL";
+    } else {
+      UniversalPrint(::std::u16string(str), os);
+    }
+  }
+};
+template <>
+class UniversalTersePrinter<char16_t*>
+    : public UniversalTersePrinter<const char16_t*> {};
+
+template <>
+class UniversalTersePrinter<const char32_t*> {
+ public:
+  static void Print(const char32_t* str, ::std::ostream* os) {
+    if (str == nullptr) {
+      *os << "NULL";
+    } else {
+      UniversalPrint(::std::u32string(str), os);
+    }
+  }
+};
+template <>
+class UniversalTersePrinter<char32_t*>
+    : public UniversalTersePrinter<const char32_t*> {};
+
+#if GTEST_HAS_STD_WSTRING
+template <>
+class UniversalTersePrinter<const wchar_t*> {
+ public:
+  static void Print(const wchar_t* str, ::std::ostream* os) {
+    if (str == nullptr) {
+      *os << "NULL";
+    } else {
+      UniversalPrint(::std::wstring(str), os);
+    }
+  }
+};
+#endif
+
+template <>
+class UniversalTersePrinter<wchar_t*> {
+ public:
+  static void Print(wchar_t* str, ::std::ostream* os) {
+    UniversalTersePrinter<const wchar_t*>::Print(str, os);
+  }
+};
+
+template <typename T>
+void UniversalTersePrint(const T& value, ::std::ostream* os) {
+  UniversalTersePrinter<T>::Print(value, os);
+}
+
+// Prints a value using the type inferred by the compiler.  The
+// difference between this and UniversalTersePrint() is that for a
+// (const) char pointer, this prints both the pointer and the
+// NUL-terminated string.
+template <typename T>
+void UniversalPrint(const T& value, ::std::ostream* os) {
+  // A workarond for the bug in VC++ 7.1 that prevents us from instantiating
+  // UniversalPrinter with T directly.
+  typedef T T1;
+  UniversalPrinter<T1>::Print(value, os);
+}
+
+typedef ::std::vector< ::std::string> Strings;
+
+  // Tersely prints the first N fields of a tuple to a string vector,
+  // one element for each field.
+template <typename Tuple>
+void TersePrintPrefixToStrings(const Tuple&, std::integral_constant<size_t, 0>,
+                               Strings*) {}
+template <typename Tuple, size_t I>
+void TersePrintPrefixToStrings(const Tuple& t,
+                               std::integral_constant<size_t, I>,
+                               Strings* strings) {
+  TersePrintPrefixToStrings(t, std::integral_constant<size_t, I - 1>(),
+                            strings);
+  ::std::stringstream ss;
+  UniversalTersePrint(std::get<I - 1>(t), &ss);
+  strings->push_back(ss.str());
+}
+
+// Prints the fields of a tuple tersely to a string vector, one
+// element for each field.  See the comment before
+// UniversalTersePrint() for how we define "tersely".
+template <typename Tuple>
+Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
+  Strings result;
+  TersePrintPrefixToStrings(
+      value, std::integral_constant<size_t, std::tuple_size<Tuple>::value>(),
+      &result);
+  return result;
+}
+
+}  // namespace internal
+
+template <typename T>
+::std::string PrintToString(const T& value) {
+  ::std::stringstream ss;
+  internal::UniversalTersePrinter<T>::Print(value, &ss);
+  return ss.str();
+}
+
+}  // namespace testing
+
+// Include any custom printer added by the local installation.
+// We must include this header at the end to make sure it can use the
+// declarations from this file.
+#include "gtest/internal/custom/gtest-printers.h"
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_PRINTERS_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-spi.h b/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-spi.h
new file mode 100644 (file)
index 0000000..bce8b22
--- /dev/null
@@ -0,0 +1,236 @@
+// Copyright 2007, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Utilities for testing Google Test itself and code that uses Google Test
+// (e.g. frameworks built on top of Google Test).
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
+
+#include "gtest/gtest.h"
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
+namespace testing {
+
+// This helper class can be used to mock out Google Test failure reporting
+// so that we can test Google Test or code that builds on Google Test.
+//
+// An object of this class appends a TestPartResult object to the
+// TestPartResultArray object given in the constructor whenever a Google Test
+// failure is reported. It can either intercept only failures that are
+// generated in the same thread that created this object or it can intercept
+// all generated failures. The scope of this mock object can be controlled with
+// the second argument to the two arguments constructor.
+class GTEST_API_ ScopedFakeTestPartResultReporter
+    : public TestPartResultReporterInterface {
+ public:
+  // The two possible mocking modes of this object.
+  enum InterceptMode {
+    INTERCEPT_ONLY_CURRENT_THREAD,  // Intercepts only thread local failures.
+    INTERCEPT_ALL_THREADS           // Intercepts all failures.
+  };
+
+  // The c'tor sets this object as the test part result reporter used
+  // by Google Test.  The 'result' parameter specifies where to report the
+  // results. This reporter will only catch failures generated in the current
+  // thread. DEPRECATED
+  explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result);
+
+  // Same as above, but you can choose the interception scope of this object.
+  ScopedFakeTestPartResultReporter(InterceptMode intercept_mode,
+                                   TestPartResultArray* result);
+
+  // The d'tor restores the previous test part result reporter.
+  ~ScopedFakeTestPartResultReporter() override;
+
+  // Appends the TestPartResult object to the TestPartResultArray
+  // received in the constructor.
+  //
+  // This method is from the TestPartResultReporterInterface
+  // interface.
+  void ReportTestPartResult(const TestPartResult& result) override;
+
+ private:
+  void Init();
+
+  const InterceptMode intercept_mode_;
+  TestPartResultReporterInterface* old_reporter_;
+  TestPartResultArray* const result_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter);
+};
+
+namespace internal {
+
+// A helper class for implementing EXPECT_FATAL_FAILURE() and
+// EXPECT_NONFATAL_FAILURE().  Its destructor verifies that the given
+// TestPartResultArray contains exactly one failure that has the given
+// type and contains the given substring.  If that's not the case, a
+// non-fatal failure will be generated.
+class GTEST_API_ SingleFailureChecker {
+ public:
+  // The constructor remembers the arguments.
+  SingleFailureChecker(const TestPartResultArray* results,
+                       TestPartResult::Type type, const std::string& substr);
+  ~SingleFailureChecker();
+ private:
+  const TestPartResultArray* const results_;
+  const TestPartResult::Type type_;
+  const std::string substr_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker);
+};
+
+}  // namespace internal
+
+}  // namespace testing
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+// A set of macros for testing Google Test assertions or code that's expected
+// to generate Google Test fatal failures.  It verifies that the given
+// statement will cause exactly one fatal Google Test failure with 'substr'
+// being part of the failure message.
+//
+// There are two different versions of this macro. EXPECT_FATAL_FAILURE only
+// affects and considers failures generated in the current thread and
+// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
+//
+// The verification of the assertion is done correctly even when the statement
+// throws an exception or aborts the current function.
+//
+// Known restrictions:
+//   - 'statement' cannot reference local non-static variables or
+//     non-static members of the current object.
+//   - 'statement' cannot return a value.
+//   - You cannot stream a failure message to this macro.
+//
+// Note that even though the implementations of the following two
+// macros are much alike, we cannot refactor them to use a common
+// helper macro, due to some peculiarity in how the preprocessor
+// works.  The AcceptsMacroThatExpandsToUnprotectedComma test in
+// gtest_unittest.cc will fail to compile if we do that.
+#define EXPECT_FATAL_FAILURE(statement, substr) \
+  do { \
+    class GTestExpectFatalFailureHelper {\
+     public:\
+      static void Execute() { statement; }\
+    };\
+    ::testing::TestPartResultArray gtest_failures;\
+    ::testing::internal::SingleFailureChecker gtest_checker(\
+        &gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
+    {\
+      ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+          ::testing::ScopedFakeTestPartResultReporter:: \
+          INTERCEPT_ONLY_CURRENT_THREAD, &gtest_failures);\
+      GTestExpectFatalFailureHelper::Execute();\
+    }\
+  } while (::testing::internal::AlwaysFalse())
+
+#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
+  do { \
+    class GTestExpectFatalFailureHelper {\
+     public:\
+      static void Execute() { statement; }\
+    };\
+    ::testing::TestPartResultArray gtest_failures;\
+    ::testing::internal::SingleFailureChecker gtest_checker(\
+        &gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr));\
+    {\
+      ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+          ::testing::ScopedFakeTestPartResultReporter:: \
+          INTERCEPT_ALL_THREADS, &gtest_failures);\
+      GTestExpectFatalFailureHelper::Execute();\
+    }\
+  } while (::testing::internal::AlwaysFalse())
+
+// A macro for testing Google Test assertions or code that's expected to
+// generate Google Test non-fatal failures.  It asserts that the given
+// statement will cause exactly one non-fatal Google Test failure with 'substr'
+// being part of the failure message.
+//
+// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only
+// affects and considers failures generated in the current thread and
+// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
+//
+// 'statement' is allowed to reference local variables and members of
+// the current object.
+//
+// The verification of the assertion is done correctly even when the statement
+// throws an exception or aborts the current function.
+//
+// Known restrictions:
+//   - You cannot stream a failure message to this macro.
+//
+// Note that even though the implementations of the following two
+// macros are much alike, we cannot refactor them to use a common
+// helper macro, due to some peculiarity in how the preprocessor
+// works.  If we do that, the code won't compile when the user gives
+// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that
+// expands to code containing an unprotected comma.  The
+// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc
+// catches that.
+//
+// For the same reason, we have to write
+//   if (::testing::internal::AlwaysTrue()) { statement; }
+// instead of
+//   GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
+// to avoid an MSVC warning on unreachable code.
+#define EXPECT_NONFATAL_FAILURE(statement, substr) \
+  do {\
+    ::testing::TestPartResultArray gtest_failures;\
+    ::testing::internal::SingleFailureChecker gtest_checker(\
+        &gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
+        (substr));\
+    {\
+      ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+          ::testing::ScopedFakeTestPartResultReporter:: \
+          INTERCEPT_ONLY_CURRENT_THREAD, &gtest_failures);\
+      if (::testing::internal::AlwaysTrue()) { statement; }\
+    }\
+  } while (::testing::internal::AlwaysFalse())
+
+#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
+  do {\
+    ::testing::TestPartResultArray gtest_failures;\
+    ::testing::internal::SingleFailureChecker gtest_checker(\
+        &gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
+        (substr));\
+    {\
+      ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\
+          ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \
+          &gtest_failures);\
+      if (::testing::internal::AlwaysTrue()) { statement; }\
+    }\
+  } while (::testing::internal::AlwaysFalse())
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-test-part.h b/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-test-part.h
new file mode 100644 (file)
index 0000000..db9f4b9
--- /dev/null
@@ -0,0 +1,182 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
+
+#include <iosfwd>
+#include <vector>
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-string.h"
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
+namespace testing {
+
+// A copyable object representing the result of a test part (i.e. an
+// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()).
+//
+// Don't inherit from TestPartResult as its destructor is not virtual.
+class GTEST_API_ TestPartResult {
+ public:
+  // The possible outcomes of a test part (i.e. an assertion or an
+  // explicit SUCCEED(), FAIL(), or ADD_FAILURE()).
+  enum Type {
+    kSuccess,          // Succeeded.
+    kNonFatalFailure,  // Failed but the test can continue.
+    kFatalFailure,     // Failed and the test should be terminated.
+    kSkip              // Skipped.
+  };
+
+  // C'tor.  TestPartResult does NOT have a default constructor.
+  // Always use this constructor (with parameters) to create a
+  // TestPartResult object.
+  TestPartResult(Type a_type, const char* a_file_name, int a_line_number,
+                 const char* a_message)
+      : type_(a_type),
+        file_name_(a_file_name == nullptr ? "" : a_file_name),
+        line_number_(a_line_number),
+        summary_(ExtractSummary(a_message)),
+        message_(a_message) {}
+
+  // Gets the outcome of the test part.
+  Type type() const { return type_; }
+
+  // Gets the name of the source file where the test part took place, or
+  // NULL if it's unknown.
+  const char* file_name() const {
+    return file_name_.empty() ? nullptr : file_name_.c_str();
+  }
+
+  // Gets the line in the source file where the test part took place,
+  // or -1 if it's unknown.
+  int line_number() const { return line_number_; }
+
+  // Gets the summary of the failure message.
+  const char* summary() const { return summary_.c_str(); }
+
+  // Gets the message associated with the test part.
+  const char* message() const { return message_.c_str(); }
+
+  // Returns true if and only if the test part was skipped.
+  bool skipped() const { return type_ == kSkip; }
+
+  // Returns true if and only if the test part passed.
+  bool passed() const { return type_ == kSuccess; }
+
+  // Returns true if and only if the test part non-fatally failed.
+  bool nonfatally_failed() const { return type_ == kNonFatalFailure; }
+
+  // Returns true if and only if the test part fatally failed.
+  bool fatally_failed() const { return type_ == kFatalFailure; }
+
+  // Returns true if and only if the test part failed.
+  bool failed() const { return fatally_failed() || nonfatally_failed(); }
+
+ private:
+  Type type_;
+
+  // Gets the summary of the failure message by omitting the stack
+  // trace in it.
+  static std::string ExtractSummary(const char* message);
+
+  // The name of the source file where the test part took place, or
+  // "" if the source file is unknown.
+  std::string file_name_;
+  // The line in the source file where the test part took place, or -1
+  // if the line number is unknown.
+  int line_number_;
+  std::string summary_;  // The test failure summary.
+  std::string message_;  // The test failure message.
+};
+
+// Prints a TestPartResult object.
+std::ostream& operator<<(std::ostream& os, const TestPartResult& result);
+
+// An array of TestPartResult objects.
+//
+// Don't inherit from TestPartResultArray as its destructor is not
+// virtual.
+class GTEST_API_ TestPartResultArray {
+ public:
+  TestPartResultArray() {}
+
+  // Appends the given TestPartResult to the array.
+  void Append(const TestPartResult& result);
+
+  // Returns the TestPartResult at the given index (0-based).
+  const TestPartResult& GetTestPartResult(int index) const;
+
+  // Returns the number of TestPartResult objects in the array.
+  int size() const;
+
+ private:
+  std::vector<TestPartResult> array_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray);
+};
+
+// This interface knows how to report a test part result.
+class GTEST_API_ TestPartResultReporterInterface {
+ public:
+  virtual ~TestPartResultReporterInterface() {}
+
+  virtual void ReportTestPartResult(const TestPartResult& result) = 0;
+};
+
+namespace internal {
+
+// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a
+// statement generates new fatal failures. To do so it registers itself as the
+// current test part result reporter. Besides checking if fatal failures were
+// reported, it only delegates the reporting to the former result reporter.
+// The original result reporter is restored in the destructor.
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+class GTEST_API_ HasNewFatalFailureHelper
+    : public TestPartResultReporterInterface {
+ public:
+  HasNewFatalFailureHelper();
+  ~HasNewFatalFailureHelper() override;
+  void ReportTestPartResult(const TestPartResult& result) override;
+  bool has_new_fatal_failure() const { return has_new_fatal_failure_; }
+ private:
+  bool has_new_fatal_failure_;
+  TestPartResultReporterInterface* original_reporter_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper);
+};
+
+}  // namespace internal
+
+}  // namespace testing
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-typed-test.h b/src/include/gromacs/external/googletest/googletest/include/gtest/gtest-typed-test.h
new file mode 100644 (file)
index 0000000..8e078b3
--- /dev/null
@@ -0,0 +1,327 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
+
+// This header implements typed tests and type-parameterized tests.
+
+// Typed (aka type-driven) tests repeat the same test for types in a
+// list.  You must know which types you want to test with when writing
+// typed tests. Here's how you do it:
+
+#if 0
+
+// First, define a fixture class template.  It should be parameterized
+// by a type.  Remember to derive it from testing::Test.
+template <typename T>
+class FooTest : public testing::Test {
+ public:
+  ...
+  typedef std::list<T> List;
+  static T shared_;
+  T value_;
+};
+
+// Next, associate a list of types with the test suite, which will be
+// repeated for each type in the list.  The typedef is necessary for
+// the macro to parse correctly.
+typedef testing::Types<char, int, unsigned int> MyTypes;
+TYPED_TEST_SUITE(FooTest, MyTypes);
+
+// If the type list contains only one type, you can write that type
+// directly without Types<...>:
+//   TYPED_TEST_SUITE(FooTest, int);
+
+// Then, use TYPED_TEST() instead of TEST_F() to define as many typed
+// tests for this test suite as you want.
+TYPED_TEST(FooTest, DoesBlah) {
+  // Inside a test, refer to the special name TypeParam to get the type
+  // parameter.  Since we are inside a derived class template, C++ requires
+  // us to visit the members of FooTest via 'this'.
+  TypeParam n = this->value_;
+
+  // To visit static members of the fixture, add the TestFixture::
+  // prefix.
+  n += TestFixture::shared_;
+
+  // To refer to typedefs in the fixture, add the "typename
+  // TestFixture::" prefix.
+  typename TestFixture::List values;
+  values.push_back(n);
+  ...
+}
+
+TYPED_TEST(FooTest, HasPropertyA) { ... }
+
+// TYPED_TEST_SUITE takes an optional third argument which allows to specify a
+// class that generates custom test name suffixes based on the type. This should
+// be a class which has a static template function GetName(int index) returning
+// a string for each type. The provided integer index equals the index of the
+// type in the provided type list. In many cases the index can be ignored.
+//
+// For example:
+//   class MyTypeNames {
+//    public:
+//     template <typename T>
+//     static std::string GetName(int) {
+//       if (std::is_same<T, char>()) return "char";
+//       if (std::is_same<T, int>()) return "int";
+//       if (std::is_same<T, unsigned int>()) return "unsignedInt";
+//     }
+//   };
+//   TYPED_TEST_SUITE(FooTest, MyTypes, MyTypeNames);
+
+#endif  // 0
+
+// Type-parameterized tests are abstract test patterns parameterized
+// by a type.  Compared with typed tests, type-parameterized tests
+// allow you to define the test pattern without knowing what the type
+// parameters are.  The defined pattern can be instantiated with
+// different types any number of times, in any number of translation
+// units.
+//
+// If you are designing an interface or concept, you can define a
+// suite of type-parameterized tests to verify properties that any
+// valid implementation of the interface/concept should have.  Then,
+// each implementation can easily instantiate the test suite to verify
+// that it conforms to the requirements, without having to write
+// similar tests repeatedly.  Here's an example:
+
+#if 0
+
+// First, define a fixture class template.  It should be parameterized
+// by a type.  Remember to derive it from testing::Test.
+template <typename T>
+class FooTest : public testing::Test {
+  ...
+};
+
+// Next, declare that you will define a type-parameterized test suite
+// (the _P suffix is for "parameterized" or "pattern", whichever you
+// prefer):
+TYPED_TEST_SUITE_P(FooTest);
+
+// Then, use TYPED_TEST_P() to define as many type-parameterized tests
+// for this type-parameterized test suite as you want.
+TYPED_TEST_P(FooTest, DoesBlah) {
+  // Inside a test, refer to TypeParam to get the type parameter.
+  TypeParam n = 0;
+  ...
+}
+
+TYPED_TEST_P(FooTest, HasPropertyA) { ... }
+
+// Now the tricky part: you need to register all test patterns before
+// you can instantiate them.  The first argument of the macro is the
+// test suite name; the rest are the names of the tests in this test
+// case.
+REGISTER_TYPED_TEST_SUITE_P(FooTest,
+                            DoesBlah, HasPropertyA);
+
+// Finally, you are free to instantiate the pattern with the types you
+// want.  If you put the above code in a header file, you can #include
+// it in multiple C++ source files and instantiate it multiple times.
+//
+// To distinguish different instances of the pattern, the first
+// argument to the INSTANTIATE_* macro is a prefix that will be added
+// to the actual test suite name.  Remember to pick unique prefixes for
+// different instances.
+typedef testing::Types<char, int, unsigned int> MyTypes;
+INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
+
+// If the type list contains only one type, you can write that type
+// directly without Types<...>:
+//   INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int);
+//
+// Similar to the optional argument of TYPED_TEST_SUITE above,
+// INSTANTIATE_TEST_SUITE_P takes an optional fourth argument which allows to
+// generate custom names.
+//   INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes, MyTypeNames);
+
+#endif  // 0
+
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-port.h"
+#include "gtest/internal/gtest-type-util.h"
+
+// Implements typed tests.
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Expands to the name of the typedef for the type parameters of the
+// given test suite.
+#define GTEST_TYPE_PARAMS_(TestSuiteName) gtest_type_params_##TestSuiteName##_
+
+// Expands to the name of the typedef for the NameGenerator, responsible for
+// creating the suffixes of the name.
+#define GTEST_NAME_GENERATOR_(TestSuiteName) \
+  gtest_type_params_##TestSuiteName##_NameGenerator
+
+#define TYPED_TEST_SUITE(CaseName, Types, ...)                          \
+  typedef ::testing::internal::GenerateTypeList<Types>::type            \
+      GTEST_TYPE_PARAMS_(CaseName);                                     \
+  typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \
+      GTEST_NAME_GENERATOR_(CaseName)
+
+#define TYPED_TEST(CaseName, TestName)                                        \
+  static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1,                       \
+                "test-name must not be empty");                               \
+  template <typename gtest_TypeParam_>                                        \
+  class GTEST_TEST_CLASS_NAME_(CaseName, TestName)                            \
+      : public CaseName<gtest_TypeParam_> {                                   \
+   private:                                                                   \
+    typedef CaseName<gtest_TypeParam_> TestFixture;                           \
+    typedef gtest_TypeParam_ TypeParam;                                       \
+    void TestBody() override;                                                 \
+  };                                                                          \
+  static bool gtest_##CaseName##_##TestName##_registered_                     \
+      GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest<   \
+          CaseName,                                                           \
+          ::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(CaseName,   \
+                                                                  TestName)>, \
+          GTEST_TYPE_PARAMS_(                                                 \
+              CaseName)>::Register("",                                        \
+                                   ::testing::internal::CodeLocation(         \
+                                       __FILE__, __LINE__),                   \
+                                   GTEST_STRINGIFY_(CaseName),                \
+                                   GTEST_STRINGIFY_(TestName), 0,             \
+                                   ::testing::internal::GenerateNames<        \
+                                       GTEST_NAME_GENERATOR_(CaseName),       \
+                                       GTEST_TYPE_PARAMS_(CaseName)>());      \
+  template <typename gtest_TypeParam_>                                        \
+  void GTEST_TEST_CLASS_NAME_(CaseName,                                       \
+                              TestName)<gtest_TypeParam_>::TestBody()
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+#define TYPED_TEST_CASE                                                \
+  static_assert(::testing::internal::TypedTestCaseIsDeprecated(), ""); \
+  TYPED_TEST_SUITE
+#endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+// Implements type-parameterized tests.
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Expands to the namespace name that the type-parameterized tests for
+// the given type-parameterized test suite are defined in.  The exact
+// name of the namespace is subject to change without notice.
+#define GTEST_SUITE_NAMESPACE_(TestSuiteName) gtest_suite_##TestSuiteName##_
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Expands to the name of the variable used to remember the names of
+// the defined tests in the given test suite.
+#define GTEST_TYPED_TEST_SUITE_P_STATE_(TestSuiteName) \
+  gtest_typed_test_suite_p_state_##TestSuiteName##_
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY.
+//
+// Expands to the name of the variable used to remember the names of
+// the registered tests in the given test suite.
+#define GTEST_REGISTERED_TEST_NAMES_(TestSuiteName) \
+  gtest_registered_test_names_##TestSuiteName##_
+
+// The variables defined in the type-parameterized test macros are
+// static as typically these macros are used in a .h file that can be
+// #included in multiple translation units linked together.
+#define TYPED_TEST_SUITE_P(SuiteName)              \
+  static ::testing::internal::TypedTestSuitePState \
+      GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName)
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+#define TYPED_TEST_CASE_P                                                 \
+  static_assert(::testing::internal::TypedTestCase_P_IsDeprecated(), ""); \
+  TYPED_TEST_SUITE_P
+#endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+#define TYPED_TEST_P(SuiteName, TestName)                             \
+  namespace GTEST_SUITE_NAMESPACE_(SuiteName) {                       \
+    template <typename gtest_TypeParam_>                              \
+    class TestName : public SuiteName<gtest_TypeParam_> {             \
+     private:                                                         \
+      typedef SuiteName<gtest_TypeParam_> TestFixture;                \
+      typedef gtest_TypeParam_ TypeParam;                             \
+      void TestBody() override;                                       \
+    };                                                                \
+    static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
+        GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName(       \
+            __FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName),          \
+            GTEST_STRINGIFY_(TestName));                              \
+  }                                                                   \
+  template <typename gtest_TypeParam_>                                \
+  void GTEST_SUITE_NAMESPACE_(                                        \
+      SuiteName)::TestName<gtest_TypeParam_>::TestBody()
+
+// Note: this won't work correctly if the trailing arguments are macros.
+#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...)                         \
+  namespace GTEST_SUITE_NAMESPACE_(SuiteName) {                             \
+    typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_;    \
+  }                                                                         \
+  static const char* const GTEST_REGISTERED_TEST_NAMES_(                    \
+      SuiteName) GTEST_ATTRIBUTE_UNUSED_ =                                  \
+      GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \
+          GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__)
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+#define REGISTER_TYPED_TEST_CASE_P                                           \
+  static_assert(::testing::internal::RegisterTypedTestCase_P_IsDeprecated(), \
+                "");                                                         \
+  REGISTER_TYPED_TEST_SUITE_P
+#endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...)       \
+  static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1,                       \
+                "test-suit-prefix must not be empty");                      \
+  static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ =        \
+      ::testing::internal::TypeParameterizedTestSuite<                      \
+          SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_,    \
+          ::testing::internal::GenerateTypeList<Types>::type>::             \
+          Register(GTEST_STRINGIFY_(Prefix),                                \
+                   ::testing::internal::CodeLocation(__FILE__, __LINE__),   \
+                   &GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName),             \
+                   GTEST_STRINGIFY_(SuiteName),                             \
+                   GTEST_REGISTERED_TEST_NAMES_(SuiteName),                 \
+                   ::testing::internal::GenerateNames<                      \
+                       ::testing::internal::NameGeneratorSelector<          \
+                           __VA_ARGS__>::type,                              \
+                       ::testing::internal::GenerateTypeList<Types>::type>())
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+#define INSTANTIATE_TYPED_TEST_CASE_P                                      \
+  static_assert(                                                           \
+      ::testing::internal::InstantiateTypedTestCase_P_IsDeprecated(), ""); \
+  INSTANTIATE_TYPED_TEST_SUITE_P
+#endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/gtest.h b/src/include/gromacs/external/googletest/googletest/include/gtest/gtest.h
new file mode 100644 (file)
index 0000000..3d04b89
--- /dev/null
@@ -0,0 +1,2497 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//
+// The Google C++ Testing and Mocking Framework (Google Test)
+//
+// This header file defines the public API for Google Test.  It should be
+// included by any test program that uses Google Test.
+//
+// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
+// leave some internal implementation details in this header file.
+// They are clearly marked by comments like this:
+//
+//   // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+//
+// Such code is NOT meant to be used by a user directly, and is subject
+// to CHANGE WITHOUT NOTICE.  Therefore DO NOT DEPEND ON IT in a user
+// program!
+//
+// Acknowledgment: Google Test borrowed the idea of automatic test
+// registration from Barthelemy Dagenais' (barthelemy@prologique.com)
+// easyUnit framework.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_H_
+
+#include <cstddef>
+#include <limits>
+#include <memory>
+#include <ostream>
+#include <type_traits>
+#include <vector>
+
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-string.h"
+#include "gtest/gtest-death-test.h"
+#include "gtest/gtest-matchers.h"
+#include "gtest/gtest-message.h"
+#include "gtest/gtest-param-test.h"
+#include "gtest/gtest-printers.h"
+#include "gtest/gtest_prod.h"
+#include "gtest/gtest-test-part.h"
+#include "gtest/gtest-typed-test.h"
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
+// Declares the flags.
+
+// This flag temporary enables the disabled tests.
+GTEST_DECLARE_bool_(also_run_disabled_tests);
+
+// This flag brings the debugger on an assertion failure.
+GTEST_DECLARE_bool_(break_on_failure);
+
+// This flag controls whether Google Test catches all test-thrown exceptions
+// and logs them as failures.
+GTEST_DECLARE_bool_(catch_exceptions);
+
+// This flag enables using colors in terminal output. Available values are
+// "yes" to enable colors, "no" (disable colors), or "auto" (the default)
+// to let Google Test decide.
+GTEST_DECLARE_string_(color);
+
+// This flag controls whether the test runner should continue execution past
+// first failure.
+GTEST_DECLARE_bool_(fail_fast);
+
+// This flag sets up the filter to select by name using a glob pattern
+// the tests to run. If the filter is not given all tests are executed.
+GTEST_DECLARE_string_(filter);
+
+// This flag controls whether Google Test installs a signal handler that dumps
+// debugging information when fatal signals are raised.
+GTEST_DECLARE_bool_(install_failure_signal_handler);
+
+// This flag causes the Google Test to list tests. None of the tests listed
+// are actually run if the flag is provided.
+GTEST_DECLARE_bool_(list_tests);
+
+// This flag controls whether Google Test emits a detailed XML report to a file
+// in addition to its normal textual output.
+GTEST_DECLARE_string_(output);
+
+// This flags control whether Google Test prints only test failures.
+GTEST_DECLARE_bool_(brief);
+
+// This flags control whether Google Test prints the elapsed time for each
+// test.
+GTEST_DECLARE_bool_(print_time);
+
+// This flags control whether Google Test prints UTF8 characters as text.
+GTEST_DECLARE_bool_(print_utf8);
+
+// This flag specifies the random number seed.
+GTEST_DECLARE_int32_(random_seed);
+
+// This flag sets how many times the tests are repeated. The default value
+// is 1. If the value is -1 the tests are repeating forever.
+GTEST_DECLARE_int32_(repeat);
+
+// This flag controls whether Google Test Environments are recreated for each
+// repeat of the tests. The default value is true. If set to false the global
+// test Environment objects are only set up once, for the first iteration, and
+// only torn down once, for the last.
+GTEST_DECLARE_bool_(recreate_environments_when_repeating);
+
+// This flag controls whether Google Test includes Google Test internal
+// stack frames in failure stack traces.
+GTEST_DECLARE_bool_(show_internal_stack_frames);
+
+// When this flag is specified, tests' order is randomized on every iteration.
+GTEST_DECLARE_bool_(shuffle);
+
+// This flag specifies the maximum number of stack frames to be
+// printed in a failure message.
+GTEST_DECLARE_int32_(stack_trace_depth);
+
+// When this flag is specified, a failed assertion will throw an
+// exception if exceptions are enabled, or exit the program with a
+// non-zero code otherwise. For use with an external test framework.
+GTEST_DECLARE_bool_(throw_on_failure);
+
+// When this flag is set with a "host:port" string, on supported
+// platforms test results are streamed to the specified port on
+// the specified host machine.
+GTEST_DECLARE_string_(stream_result_to);
+
+#if GTEST_USE_OWN_FLAGFILE_FLAG_
+GTEST_DECLARE_string_(flagfile);
+#endif  // GTEST_USE_OWN_FLAGFILE_FLAG_
+
+namespace testing {
+
+// Silence C4100 (unreferenced formal parameter) and 4805
+// unsafe mix of type 'const int' and type 'const bool'
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4805)
+#pragma warning(disable : 4100)
+#endif
+
+// The upper limit for valid stack trace depths.
+const int kMaxStackTraceDepth = 100;
+
+namespace internal {
+
+class AssertHelper;
+class DefaultGlobalTestPartResultReporter;
+class ExecDeathTest;
+class NoExecDeathTest;
+class FinalSuccessChecker;
+class GTestFlagSaver;
+class StreamingListenerTest;
+class TestResultAccessor;
+class TestEventListenersAccessor;
+class TestEventRepeater;
+class UnitTestRecordPropertyTestHelper;
+class WindowsDeathTest;
+class FuchsiaDeathTest;
+class UnitTestImpl* GetUnitTestImpl();
+void ReportFailureInUnknownLocation(TestPartResult::Type result_type,
+                                    const std::string& message);
+std::set<std::string>* GetIgnoredParameterizedTestSuites();
+
+}  // namespace internal
+
+// The friend relationship of some of these classes is cyclic.
+// If we don't forward declare them the compiler might confuse the classes
+// in friendship clauses with same named classes on the scope.
+class Test;
+class TestSuite;
+
+// Old API is still available but deprecated
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+using TestCase = TestSuite;
+#endif
+class TestInfo;
+class UnitTest;
+
+// A class for indicating whether an assertion was successful.  When
+// the assertion wasn't successful, the AssertionResult object
+// remembers a non-empty message that describes how it failed.
+//
+// To create an instance of this class, use one of the factory functions
+// (AssertionSuccess() and AssertionFailure()).
+//
+// This class is useful for two purposes:
+//   1. Defining predicate functions to be used with Boolean test assertions
+//      EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts
+//   2. Defining predicate-format functions to be
+//      used with predicate assertions (ASSERT_PRED_FORMAT*, etc).
+//
+// For example, if you define IsEven predicate:
+//
+//   testing::AssertionResult IsEven(int n) {
+//     if ((n % 2) == 0)
+//       return testing::AssertionSuccess();
+//     else
+//       return testing::AssertionFailure() << n << " is odd";
+//   }
+//
+// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5)))
+// will print the message
+//
+//   Value of: IsEven(Fib(5))
+//     Actual: false (5 is odd)
+//   Expected: true
+//
+// instead of a more opaque
+//
+//   Value of: IsEven(Fib(5))
+//     Actual: false
+//   Expected: true
+//
+// in case IsEven is a simple Boolean predicate.
+//
+// If you expect your predicate to be reused and want to support informative
+// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up
+// about half as often as positive ones in our tests), supply messages for
+// both success and failure cases:
+//
+//   testing::AssertionResult IsEven(int n) {
+//     if ((n % 2) == 0)
+//       return testing::AssertionSuccess() << n << " is even";
+//     else
+//       return testing::AssertionFailure() << n << " is odd";
+//   }
+//
+// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print
+//
+//   Value of: IsEven(Fib(6))
+//     Actual: true (8 is even)
+//   Expected: false
+//
+// NB: Predicates that support negative Boolean assertions have reduced
+// performance in positive ones so be careful not to use them in tests
+// that have lots (tens of thousands) of positive Boolean assertions.
+//
+// To use this class with EXPECT_PRED_FORMAT assertions such as:
+//
+//   // Verifies that Foo() returns an even number.
+//   EXPECT_PRED_FORMAT1(IsEven, Foo());
+//
+// you need to define:
+//
+//   testing::AssertionResult IsEven(const char* expr, int n) {
+//     if ((n % 2) == 0)
+//       return testing::AssertionSuccess();
+//     else
+//       return testing::AssertionFailure()
+//         << "Expected: " << expr << " is even\n  Actual: it's " << n;
+//   }
+//
+// If Foo() returns 5, you will see the following message:
+//
+//   Expected: Foo() is even
+//     Actual: it's 5
+//
+class GTEST_API_ AssertionResult {
+ public:
+  // Copy constructor.
+  // Used in EXPECT_TRUE/FALSE(assertion_result).
+  AssertionResult(const AssertionResult& other);
+
+// C4800 is a level 3 warning in Visual Studio 2015 and earlier.
+// This warning is not emitted in Visual Studio 2017.
+// This warning is off by default starting in Visual Studio 2019 but can be
+// enabled with command-line options.
+#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920)
+  GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */)
+#endif
+
+  // Used in the EXPECT_TRUE/FALSE(bool_expression).
+  //
+  // T must be contextually convertible to bool.
+  //
+  // The second parameter prevents this overload from being considered if
+  // the argument is implicitly convertible to AssertionResult. In that case
+  // we want AssertionResult's copy constructor to be used.
+  template <typename T>
+  explicit AssertionResult(
+      const T& success,
+      typename std::enable_if<
+          !std::is_convertible<T, AssertionResult>::value>::type*
+      /*enabler*/
+      = nullptr)
+      : success_(success) {}
+
+#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920)
+  GTEST_DISABLE_MSC_WARNINGS_POP_()
+#endif
+
+  // Assignment operator.
+  AssertionResult& operator=(AssertionResult other) {
+    swap(other);
+    return *this;
+  }
+
+  // Returns true if and only if the assertion succeeded.
+  operator bool() const { return success_; }  // NOLINT
+
+  // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
+  AssertionResult operator!() const;
+
+  // Returns the text streamed into this AssertionResult. Test assertions
+  // use it when they fail (i.e., the predicate's outcome doesn't match the
+  // assertion's expectation). When nothing has been streamed into the
+  // object, returns an empty string.
+  const char* message() const {
+    return message_.get() != nullptr ? message_->c_str() : "";
+  }
+  // Deprecated; please use message() instead.
+  const char* failure_message() const { return message(); }
+
+  // Streams a custom failure message into this object.
+  template <typename T> AssertionResult& operator<<(const T& value) {
+    AppendMessage(Message() << value);
+    return *this;
+  }
+
+  // Allows streaming basic output manipulators such as endl or flush into
+  // this object.
+  AssertionResult& operator<<(
+      ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) {
+    AppendMessage(Message() << basic_manipulator);
+    return *this;
+  }
+
+ private:
+  // Appends the contents of message to message_.
+  void AppendMessage(const Message& a_message) {
+    if (message_.get() == nullptr) message_.reset(new ::std::string);
+    message_->append(a_message.GetString().c_str());
+  }
+
+  // Swap the contents of this AssertionResult with other.
+  void swap(AssertionResult& other);
+
+  // Stores result of the assertion predicate.
+  bool success_;
+  // Stores the message describing the condition in case the expectation
+  // construct is not satisfied with the predicate's outcome.
+  // Referenced via a pointer to avoid taking too much stack frame space
+  // with test assertions.
+  std::unique_ptr< ::std::string> message_;
+};
+
+// Makes a successful assertion result.
+GTEST_API_ AssertionResult AssertionSuccess();
+
+// Makes a failed assertion result.
+GTEST_API_ AssertionResult AssertionFailure();
+
+// Makes a failed assertion result with the given failure message.
+// Deprecated; use AssertionFailure() << msg.
+GTEST_API_ AssertionResult AssertionFailure(const Message& msg);
+
+}  // namespace testing
+
+// Includes the auto-generated header that implements a family of generic
+// predicate assertion macros. This include comes late because it relies on
+// APIs declared above.
+#include "gtest/gtest_pred_impl.h"
+
+namespace testing {
+
+// The abstract class that all tests inherit from.
+//
+// In Google Test, a unit test program contains one or many TestSuites, and
+// each TestSuite contains one or many Tests.
+//
+// When you define a test using the TEST macro, you don't need to
+// explicitly derive from Test - the TEST macro automatically does
+// this for you.
+//
+// The only time you derive from Test is when defining a test fixture
+// to be used in a TEST_F.  For example:
+//
+//   class FooTest : public testing::Test {
+//    protected:
+//     void SetUp() override { ... }
+//     void TearDown() override { ... }
+//     ...
+//   };
+//
+//   TEST_F(FooTest, Bar) { ... }
+//   TEST_F(FooTest, Baz) { ... }
+//
+// Test is not copyable.
+class GTEST_API_ Test {
+ public:
+  friend class TestInfo;
+
+  // The d'tor is virtual as we intend to inherit from Test.
+  virtual ~Test();
+
+  // Sets up the stuff shared by all tests in this test suite.
+  //
+  // Google Test will call Foo::SetUpTestSuite() before running the first
+  // test in test suite Foo.  Hence a sub-class can define its own
+  // SetUpTestSuite() method to shadow the one defined in the super
+  // class.
+  static void SetUpTestSuite() {}
+
+  // Tears down the stuff shared by all tests in this test suite.
+  //
+  // Google Test will call Foo::TearDownTestSuite() after running the last
+  // test in test suite Foo.  Hence a sub-class can define its own
+  // TearDownTestSuite() method to shadow the one defined in the super
+  // class.
+  static void TearDownTestSuite() {}
+
+  // Legacy API is deprecated but still available. Use SetUpTestSuite and
+  // TearDownTestSuite instead.
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  static void TearDownTestCase() {}
+  static void SetUpTestCase() {}
+#endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  // Returns true if and only if the current test has a fatal failure.
+  static bool HasFatalFailure();
+
+  // Returns true if and only if the current test has a non-fatal failure.
+  static bool HasNonfatalFailure();
+
+  // Returns true if and only if the current test was skipped.
+  static bool IsSkipped();
+
+  // Returns true if and only if the current test has a (either fatal or
+  // non-fatal) failure.
+  static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); }
+
+  // Logs a property for the current test, test suite, or for the entire
+  // invocation of the test program when used outside of the context of a
+  // test suite.  Only the last value for a given key is remembered.  These
+  // are public static so they can be called from utility functions that are
+  // not members of the test fixture.  Calls to RecordProperty made during
+  // lifespan of the test (from the moment its constructor starts to the
+  // moment its destructor finishes) will be output in XML as attributes of
+  // the <testcase> element.  Properties recorded from fixture's
+  // SetUpTestSuite or TearDownTestSuite are logged as attributes of the
+  // corresponding <testsuite> element.  Calls to RecordProperty made in the
+  // global context (before or after invocation of RUN_ALL_TESTS and from
+  // SetUp/TearDown method of Environment objects registered with Google
+  // Test) will be output as attributes of the <testsuites> element.
+  static void RecordProperty(const std::string& key, const std::string& value);
+  static void RecordProperty(const std::string& key, int value);
+
+ protected:
+  // Creates a Test object.
+  Test();
+
+  // Sets up the test fixture.
+  virtual void SetUp();
+
+  // Tears down the test fixture.
+  virtual void TearDown();
+
+ private:
+  // Returns true if and only if the current test has the same fixture class
+  // as the first test in the current test suite.
+  static bool HasSameFixtureClass();
+
+  // Runs the test after the test fixture has been set up.
+  //
+  // A sub-class must implement this to define the test logic.
+  //
+  // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM.
+  // Instead, use the TEST or TEST_F macro.
+  virtual void TestBody() = 0;
+
+  // Sets up, executes, and tears down the test.
+  void Run();
+
+  // Deletes self.  We deliberately pick an unusual name for this
+  // internal method to avoid clashing with names used in user TESTs.
+  void DeleteSelf_() { delete this; }
+
+  const std::unique_ptr<GTEST_FLAG_SAVER_> gtest_flag_saver_;
+
+  // Often a user misspells SetUp() as Setup() and spends a long time
+  // wondering why it is never called by Google Test.  The declaration of
+  // the following method is solely for catching such an error at
+  // compile time:
+  //
+  //   - The return type is deliberately chosen to be not void, so it
+  //   will be a conflict if void Setup() is declared in the user's
+  //   test fixture.
+  //
+  //   - This method is private, so it will be another compiler error
+  //   if the method is called from the user's test fixture.
+  //
+  // DO NOT OVERRIDE THIS FUNCTION.
+  //
+  // If you see an error about overriding the following function or
+  // about it being private, you have mis-spelled SetUp() as Setup().
+  struct Setup_should_be_spelled_SetUp {};
+  virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; }
+
+  // We disallow copying Tests.
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(Test);
+};
+
+typedef internal::TimeInMillis TimeInMillis;
+
+// A copyable object representing a user specified test property which can be
+// output as a key/value string pair.
+//
+// Don't inherit from TestProperty as its destructor is not virtual.
+class TestProperty {
+ public:
+  // C'tor.  TestProperty does NOT have a default constructor.
+  // Always use this constructor (with parameters) to create a
+  // TestProperty object.
+  TestProperty(const std::string& a_key, const std::string& a_value) :
+    key_(a_key), value_(a_value) {
+  }
+
+  // Gets the user supplied key.
+  const char* key() const {
+    return key_.c_str();
+  }
+
+  // Gets the user supplied value.
+  const char* value() const {
+    return value_.c_str();
+  }
+
+  // Sets a new value, overriding the one supplied in the constructor.
+  void SetValue(const std::string& new_value) {
+    value_ = new_value;
+  }
+
+ private:
+  // The key supplied by the user.
+  std::string key_;
+  // The value supplied by the user.
+  std::string value_;
+};
+
+// The result of a single Test.  This includes a list of
+// TestPartResults, a list of TestProperties, a count of how many
+// death tests there are in the Test, and how much time it took to run
+// the Test.
+//
+// TestResult is not copyable.
+class GTEST_API_ TestResult {
+ public:
+  // Creates an empty TestResult.
+  TestResult();
+
+  // D'tor.  Do not inherit from TestResult.
+  ~TestResult();
+
+  // Gets the number of all test parts.  This is the sum of the number
+  // of successful test parts and the number of failed test parts.
+  int total_part_count() const;
+
+  // Returns the number of the test properties.
+  int test_property_count() const;
+
+  // Returns true if and only if the test passed (i.e. no test part failed).
+  bool Passed() const { return !Skipped() && !Failed(); }
+
+  // Returns true if and only if the test was skipped.
+  bool Skipped() const;
+
+  // Returns true if and only if the test failed.
+  bool Failed() const;
+
+  // Returns true if and only if the test fatally failed.
+  bool HasFatalFailure() const;
+
+  // Returns true if and only if the test has a non-fatal failure.
+  bool HasNonfatalFailure() const;
+
+  // Returns the elapsed time, in milliseconds.
+  TimeInMillis elapsed_time() const { return elapsed_time_; }
+
+  // Gets the time of the test case start, in ms from the start of the
+  // UNIX epoch.
+  TimeInMillis start_timestamp() const { return start_timestamp_; }
+
+  // Returns the i-th test part result among all the results. i can range from 0
+  // to total_part_count() - 1. If i is not in that range, aborts the program.
+  const TestPartResult& GetTestPartResult(int i) const;
+
+  // Returns the i-th test property. i can range from 0 to
+  // test_property_count() - 1. If i is not in that range, aborts the
+  // program.
+  const TestProperty& GetTestProperty(int i) const;
+
+ private:
+  friend class TestInfo;
+  friend class TestSuite;
+  friend class UnitTest;
+  friend class internal::DefaultGlobalTestPartResultReporter;
+  friend class internal::ExecDeathTest;
+  friend class internal::TestResultAccessor;
+  friend class internal::UnitTestImpl;
+  friend class internal::WindowsDeathTest;
+  friend class internal::FuchsiaDeathTest;
+
+  // Gets the vector of TestPartResults.
+  const std::vector<TestPartResult>& test_part_results() const {
+    return test_part_results_;
+  }
+
+  // Gets the vector of TestProperties.
+  const std::vector<TestProperty>& test_properties() const {
+    return test_properties_;
+  }
+
+  // Sets the start time.
+  void set_start_timestamp(TimeInMillis start) { start_timestamp_ = start; }
+
+  // Sets the elapsed time.
+  void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; }
+
+  // Adds a test property to the list. The property is validated and may add
+  // a non-fatal failure if invalid (e.g., if it conflicts with reserved
+  // key names). If a property is already recorded for the same key, the
+  // value will be updated, rather than storing multiple values for the same
+  // key.  xml_element specifies the element for which the property is being
+  // recorded and is used for validation.
+  void RecordProperty(const std::string& xml_element,
+                      const TestProperty& test_property);
+
+  // Adds a failure if the key is a reserved attribute of Google Test
+  // testsuite tags.  Returns true if the property is valid.
+  // FIXME: Validate attribute names are legal and human readable.
+  static bool ValidateTestProperty(const std::string& xml_element,
+                                   const TestProperty& test_property);
+
+  // Adds a test part result to the list.
+  void AddTestPartResult(const TestPartResult& test_part_result);
+
+  // Returns the death test count.
+  int death_test_count() const { return death_test_count_; }
+
+  // Increments the death test count, returning the new count.
+  int increment_death_test_count() { return ++death_test_count_; }
+
+  // Clears the test part results.
+  void ClearTestPartResults();
+
+  // Clears the object.
+  void Clear();
+
+  // Protects mutable state of the property vector and of owned
+  // properties, whose values may be updated.
+  internal::Mutex test_properties_mutex_;
+
+  // The vector of TestPartResults
+  std::vector<TestPartResult> test_part_results_;
+  // The vector of TestProperties
+  std::vector<TestProperty> test_properties_;
+  // Running count of death tests.
+  int death_test_count_;
+  // The start time, in milliseconds since UNIX Epoch.
+  TimeInMillis start_timestamp_;
+  // The elapsed time, in milliseconds.
+  TimeInMillis elapsed_time_;
+
+  // We disallow copying TestResult.
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult);
+};  // class TestResult
+
+// A TestInfo object stores the following information about a test:
+//
+//   Test suite name
+//   Test name
+//   Whether the test should be run
+//   A function pointer that creates the test object when invoked
+//   Test result
+//
+// The constructor of TestInfo registers itself with the UnitTest
+// singleton such that the RUN_ALL_TESTS() macro knows which tests to
+// run.
+class GTEST_API_ TestInfo {
+ public:
+  // Destructs a TestInfo object.  This function is not virtual, so
+  // don't inherit from TestInfo.
+  ~TestInfo();
+
+  // Returns the test suite name.
+  const char* test_suite_name() const { return test_suite_name_.c_str(); }
+
+// Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  const char* test_case_name() const { return test_suite_name(); }
+#endif  // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  // Returns the test name.
+  const char* name() const { return name_.c_str(); }
+
+  // Returns the name of the parameter type, or NULL if this is not a typed
+  // or a type-parameterized test.
+  const char* type_param() const {
+    if (type_param_.get() != nullptr) return type_param_->c_str();
+    return nullptr;
+  }
+
+  // Returns the text representation of the value parameter, or NULL if this
+  // is not a value-parameterized test.
+  const char* value_param() const {
+    if (value_param_.get() != nullptr) return value_param_->c_str();
+    return nullptr;
+  }
+
+  // Returns the file name where this test is defined.
+  const char* file() const { return location_.file.c_str(); }
+
+  // Returns the line where this test is defined.
+  int line() const { return location_.line; }
+
+  // Return true if this test should not be run because it's in another shard.
+  bool is_in_another_shard() const { return is_in_another_shard_; }
+
+  // Returns true if this test should run, that is if the test is not
+  // disabled (or it is disabled but the also_run_disabled_tests flag has
+  // been specified) and its full name matches the user-specified filter.
+  //
+  // Google Test allows the user to filter the tests by their full names.
+  // The full name of a test Bar in test suite Foo is defined as
+  // "Foo.Bar".  Only the tests that match the filter will run.
+  //
+  // A filter is a colon-separated list of glob (not regex) patterns,
+  // optionally followed by a '-' and a colon-separated list of
+  // negative patterns (tests to exclude).  A test is run if it
+  // matches one of the positive patterns and does not match any of
+  // the negative patterns.
+  //
+  // For example, *A*:Foo.* is a filter that matches any string that
+  // contains the character 'A' or starts with "Foo.".
+  bool should_run() const { return should_run_; }
+
+  // Returns true if and only if this test will appear in the XML report.
+  bool is_reportable() const {
+    // The XML report includes tests matching the filter, excluding those
+    // run in other shards.
+    return matches_filter_ && !is_in_another_shard_;
+  }
+
+  // Returns the result of the test.
+  const TestResult* result() const { return &result_; }
+
+ private:
+#if GTEST_HAS_DEATH_TEST
+  friend class internal::DefaultDeathTestFactory;
+#endif  // GTEST_HAS_DEATH_TEST
+  friend class Test;
+  friend class TestSuite;
+  friend class internal::UnitTestImpl;
+  friend class internal::StreamingListenerTest;
+  friend TestInfo* internal::MakeAndRegisterTestInfo(
+      const char* test_suite_name, const char* name, const char* type_param,
+      const char* value_param, internal::CodeLocation code_location,
+      internal::TypeId fixture_class_id, internal::SetUpTestSuiteFunc set_up_tc,
+      internal::TearDownTestSuiteFunc tear_down_tc,
+      internal::TestFactoryBase* factory);
+
+  // Constructs a TestInfo object. The newly constructed instance assumes
+  // ownership of the factory object.
+  TestInfo(const std::string& test_suite_name, const std::string& name,
+           const char* a_type_param,   // NULL if not a type-parameterized test
+           const char* a_value_param,  // NULL if not a value-parameterized test
+           internal::CodeLocation a_code_location,
+           internal::TypeId fixture_class_id,
+           internal::TestFactoryBase* factory);
+
+  // Increments the number of death tests encountered in this test so
+  // far.
+  int increment_death_test_count() {
+    return result_.increment_death_test_count();
+  }
+
+  // Creates the test object, runs it, records its result, and then
+  // deletes it.
+  void Run();
+
+  // Skip and records the test result for this object.
+  void Skip();
+
+  static void ClearTestResult(TestInfo* test_info) {
+    test_info->result_.Clear();
+  }
+
+  // These fields are immutable properties of the test.
+  const std::string test_suite_name_;    // test suite name
+  const std::string name_;               // Test name
+  // Name of the parameter type, or NULL if this is not a typed or a
+  // type-parameterized test.
+  const std::unique_ptr<const ::std::string> type_param_;
+  // Text representation of the value parameter, or NULL if this is not a
+  // value-parameterized test.
+  const std::unique_ptr<const ::std::string> value_param_;
+  internal::CodeLocation location_;
+  const internal::TypeId fixture_class_id_;  // ID of the test fixture class
+  bool should_run_;           // True if and only if this test should run
+  bool is_disabled_;          // True if and only if this test is disabled
+  bool matches_filter_;       // True if this test matches the
+                              // user-specified filter.
+  bool is_in_another_shard_;  // Will be run in another shard.
+  internal::TestFactoryBase* const factory_;  // The factory that creates
+                                              // the test object
+
+  // This field is mutable and needs to be reset before running the
+  // test for the second time.
+  TestResult result_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo);
+};
+
+// A test suite, which consists of a vector of TestInfos.
+//
+// TestSuite is not copyable.
+class GTEST_API_ TestSuite {
+ public:
+  // Creates a TestSuite with the given name.
+  //
+  // TestSuite does NOT have a default constructor.  Always use this
+  // constructor to create a TestSuite object.
+  //
+  // Arguments:
+  //
+  //   name:         name of the test suite
+  //   a_type_param: the name of the test's type parameter, or NULL if
+  //                 this is not a type-parameterized test.
+  //   set_up_tc:    pointer to the function that sets up the test suite
+  //   tear_down_tc: pointer to the function that tears down the test suite
+  TestSuite(const char* name, const char* a_type_param,
+            internal::SetUpTestSuiteFunc set_up_tc,
+            internal::TearDownTestSuiteFunc tear_down_tc);
+
+  // Destructor of TestSuite.
+  virtual ~TestSuite();
+
+  // Gets the name of the TestSuite.
+  const char* name() const { return name_.c_str(); }
+
+  // Returns the name of the parameter type, or NULL if this is not a
+  // type-parameterized test suite.
+  const char* type_param() const {
+    if (type_param_.get() != nullptr) return type_param_->c_str();
+    return nullptr;
+  }
+
+  // Returns true if any test in this test suite should run.
+  bool should_run() const { return should_run_; }
+
+  // Gets the number of successful tests in this test suite.
+  int successful_test_count() const;
+
+  // Gets the number of skipped tests in this test suite.
+  int skipped_test_count() const;
+
+  // Gets the number of failed tests in this test suite.
+  int failed_test_count() const;
+
+  // Gets the number of disabled tests that will be reported in the XML report.
+  int reportable_disabled_test_count() const;
+
+  // Gets the number of disabled tests in this test suite.
+  int disabled_test_count() const;
+
+  // Gets the number of tests to be printed in the XML report.
+  int reportable_test_count() const;
+
+  // Get the number of tests in this test suite that should run.
+  int test_to_run_count() const;
+
+  // Gets the number of all tests in this test suite.
+  int total_test_count() const;
+
+  // Returns true if and only if the test suite passed.
+  bool Passed() const { return !Failed(); }
+
+  // Returns true if and only if the test suite failed.
+  bool Failed() const {
+    return failed_test_count() > 0 || ad_hoc_test_result().Failed();
+  }
+
+  // Returns the elapsed time, in milliseconds.
+  TimeInMillis elapsed_time() const { return elapsed_time_; }
+
+  // Gets the time of the test suite start, in ms from the start of the
+  // UNIX epoch.
+  TimeInMillis start_timestamp() const { return start_timestamp_; }
+
+  // Returns the i-th test among all the tests. i can range from 0 to
+  // total_test_count() - 1. If i is not in that range, returns NULL.
+  const TestInfo* GetTestInfo(int i) const;
+
+  // Returns the TestResult that holds test properties recorded during
+  // execution of SetUpTestSuite and TearDownTestSuite.
+  const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; }
+
+ private:
+  friend class Test;
+  friend class internal::UnitTestImpl;
+
+  // Gets the (mutable) vector of TestInfos in this TestSuite.
+  std::vector<TestInfo*>& test_info_list() { return test_info_list_; }
+
+  // Gets the (immutable) vector of TestInfos in this TestSuite.
+  const std::vector<TestInfo*>& test_info_list() const {
+    return test_info_list_;
+  }
+
+  // Returns the i-th test among all the tests. i can range from 0 to
+  // total_test_count() - 1. If i is not in that range, returns NULL.
+  TestInfo* GetMutableTestInfo(int i);
+
+  // Sets the should_run member.
+  void set_should_run(bool should) { should_run_ = should; }
+
+  // Adds a TestInfo to this test suite.  Will delete the TestInfo upon
+  // destruction of the TestSuite object.
+  void AddTestInfo(TestInfo * test_info);
+
+  // Clears the results of all tests in this test suite.
+  void ClearResult();
+
+  // Clears the results of all tests in the given test suite.
+  static void ClearTestSuiteResult(TestSuite* test_suite) {
+    test_suite->ClearResult();
+  }
+
+  // Runs every test in this TestSuite.
+  void Run();
+
+  // Skips the execution of tests under this TestSuite
+  void Skip();
+
+  // Runs SetUpTestSuite() for this TestSuite.  This wrapper is needed
+  // for catching exceptions thrown from SetUpTestSuite().
+  void RunSetUpTestSuite() {
+    if (set_up_tc_ != nullptr) {
+      (*set_up_tc_)();
+    }
+  }
+
+  // Runs TearDownTestSuite() for this TestSuite.  This wrapper is
+  // needed for catching exceptions thrown from TearDownTestSuite().
+  void RunTearDownTestSuite() {
+    if (tear_down_tc_ != nullptr) {
+      (*tear_down_tc_)();
+    }
+  }
+
+  // Returns true if and only if test passed.
+  static bool TestPassed(const TestInfo* test_info) {
+    return test_info->should_run() && test_info->result()->Passed();
+  }
+
+  // Returns true if and only if test skipped.
+  static bool TestSkipped(const TestInfo* test_info) {
+    return test_info->should_run() && test_info->result()->Skipped();
+  }
+
+  // Returns true if and only if test failed.
+  static bool TestFailed(const TestInfo* test_info) {
+    return test_info->should_run() && test_info->result()->Failed();
+  }
+
+  // Returns true if and only if the test is disabled and will be reported in
+  // the XML report.
+  static bool TestReportableDisabled(const TestInfo* test_info) {
+    return test_info->is_reportable() && test_info->is_disabled_;
+  }
+
+  // Returns true if and only if test is disabled.
+  static bool TestDisabled(const TestInfo* test_info) {
+    return test_info->is_disabled_;
+  }
+
+  // Returns true if and only if this test will appear in the XML report.
+  static bool TestReportable(const TestInfo* test_info) {
+    return test_info->is_reportable();
+  }
+
+  // Returns true if the given test should run.
+  static bool ShouldRunTest(const TestInfo* test_info) {
+    return test_info->should_run();
+  }
+
+  // Shuffles the tests in this test suite.
+  void ShuffleTests(internal::Random* random);
+
+  // Restores the test order to before the first shuffle.
+  void UnshuffleTests();
+
+  // Name of the test suite.
+  std::string name_;
+  // Name of the parameter type, or NULL if this is not a typed or a
+  // type-parameterized test.
+  const std::unique_ptr<const ::std::string> type_param_;
+  // The vector of TestInfos in their original order.  It owns the
+  // elements in the vector.
+  std::vector<TestInfo*> test_info_list_;
+  // Provides a level of indirection for the test list to allow easy
+  // shuffling and restoring the test order.  The i-th element in this
+  // vector is the index of the i-th test in the shuffled test list.
+  std::vector<int> test_indices_;
+  // Pointer to the function that sets up the test suite.
+  internal::SetUpTestSuiteFunc set_up_tc_;
+  // Pointer to the function that tears down the test suite.
+  internal::TearDownTestSuiteFunc tear_down_tc_;
+  // True if and only if any test in this test suite should run.
+  bool should_run_;
+  // The start time, in milliseconds since UNIX Epoch.
+  TimeInMillis start_timestamp_;
+  // Elapsed time, in milliseconds.
+  TimeInMillis elapsed_time_;
+  // Holds test properties recorded during execution of SetUpTestSuite and
+  // TearDownTestSuite.
+  TestResult ad_hoc_test_result_;
+
+  // We disallow copying TestSuites.
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestSuite);
+};
+
+// An Environment object is capable of setting up and tearing down an
+// environment.  You should subclass this to define your own
+// environment(s).
+//
+// An Environment object does the set-up and tear-down in virtual
+// methods SetUp() and TearDown() instead of the constructor and the
+// destructor, as:
+//
+//   1. You cannot safely throw from a destructor.  This is a problem
+//      as in some cases Google Test is used where exceptions are enabled, and
+//      we may want to implement ASSERT_* using exceptions where they are
+//      available.
+//   2. You cannot use ASSERT_* directly in a constructor or
+//      destructor.
+class Environment {
+ public:
+  // The d'tor is virtual as we need to subclass Environment.
+  virtual ~Environment() {}
+
+  // Override this to define how to set up the environment.
+  virtual void SetUp() {}
+
+  // Override this to define how to tear down the environment.
+  virtual void TearDown() {}
+ private:
+  // If you see an error about overriding the following function or
+  // about it being private, you have mis-spelled SetUp() as Setup().
+  struct Setup_should_be_spelled_SetUp {};
+  virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; }
+};
+
+#if GTEST_HAS_EXCEPTIONS
+
+// Exception which can be thrown from TestEventListener::OnTestPartResult.
+class GTEST_API_ AssertionException
+    : public internal::GoogleTestFailureException {
+ public:
+  explicit AssertionException(const TestPartResult& result)
+      : GoogleTestFailureException(result) {}
+};
+
+#endif  // GTEST_HAS_EXCEPTIONS
+
+// The interface for tracing execution of tests. The methods are organized in
+// the order the corresponding events are fired.
+class TestEventListener {
+ public:
+  virtual ~TestEventListener() {}
+
+  // Fired before any test activity starts.
+  virtual void OnTestProgramStart(const UnitTest& unit_test) = 0;
+
+  // Fired before each iteration of tests starts.  There may be more than
+  // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration
+  // index, starting from 0.
+  virtual void OnTestIterationStart(const UnitTest& unit_test,
+                                    int iteration) = 0;
+
+  // Fired before environment set-up for each iteration of tests starts.
+  virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0;
+
+  // Fired after environment set-up for each iteration of tests ends.
+  virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0;
+
+  // Fired before the test suite starts.
+  virtual void OnTestSuiteStart(const TestSuite& /*test_suite*/) {}
+
+  //  Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  virtual void OnTestCaseStart(const TestCase& /*test_case*/) {}
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  // Fired before the test starts.
+  virtual void OnTestStart(const TestInfo& test_info) = 0;
+
+  // Fired after a failed assertion or a SUCCEED() invocation.
+  // If you want to throw an exception from this function to skip to the next
+  // TEST, it must be AssertionException defined above, or inherited from it.
+  virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0;
+
+  // Fired after the test ends.
+  virtual void OnTestEnd(const TestInfo& test_info) = 0;
+
+  // Fired after the test suite ends.
+  virtual void OnTestSuiteEnd(const TestSuite& /*test_suite*/) {}
+
+//  Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {}
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  // Fired before environment tear-down for each iteration of tests starts.
+  virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0;
+
+  // Fired after environment tear-down for each iteration of tests ends.
+  virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0;
+
+  // Fired after each iteration of tests finishes.
+  virtual void OnTestIterationEnd(const UnitTest& unit_test,
+                                  int iteration) = 0;
+
+  // Fired after all test activities have ended.
+  virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0;
+};
+
+// The convenience class for users who need to override just one or two
+// methods and are not concerned that a possible change to a signature of
+// the methods they override will not be caught during the build.  For
+// comments about each method please see the definition of TestEventListener
+// above.
+class EmptyTestEventListener : public TestEventListener {
+ public:
+  void OnTestProgramStart(const UnitTest& /*unit_test*/) override {}
+  void OnTestIterationStart(const UnitTest& /*unit_test*/,
+                            int /*iteration*/) override {}
+  void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) override {}
+  void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) override {}
+  void OnTestSuiteStart(const TestSuite& /*test_suite*/) override {}
+//  Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  void OnTestCaseStart(const TestCase& /*test_case*/) override {}
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  void OnTestStart(const TestInfo& /*test_info*/) override {}
+  void OnTestPartResult(const TestPartResult& /*test_part_result*/) override {}
+  void OnTestEnd(const TestInfo& /*test_info*/) override {}
+  void OnTestSuiteEnd(const TestSuite& /*test_suite*/) override {}
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  void OnTestCaseEnd(const TestCase& /*test_case*/) override {}
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) override {}
+  void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) override {}
+  void OnTestIterationEnd(const UnitTest& /*unit_test*/,
+                          int /*iteration*/) override {}
+  void OnTestProgramEnd(const UnitTest& /*unit_test*/) override {}
+};
+
+// TestEventListeners lets users add listeners to track events in Google Test.
+class GTEST_API_ TestEventListeners {
+ public:
+  TestEventListeners();
+  ~TestEventListeners();
+
+  // Appends an event listener to the end of the list. Google Test assumes
+  // the ownership of the listener (i.e. it will delete the listener when
+  // the test program finishes).
+  void Append(TestEventListener* listener);
+
+  // Removes the given event listener from the list and returns it.  It then
+  // becomes the caller's responsibility to delete the listener. Returns
+  // NULL if the listener is not found in the list.
+  TestEventListener* Release(TestEventListener* listener);
+
+  // Returns the standard listener responsible for the default console
+  // output.  Can be removed from the listeners list to shut down default
+  // console output.  Note that removing this object from the listener list
+  // with Release transfers its ownership to the caller and makes this
+  // function return NULL the next time.
+  TestEventListener* default_result_printer() const {
+    return default_result_printer_;
+  }
+
+  // Returns the standard listener responsible for the default XML output
+  // controlled by the --gtest_output=xml flag.  Can be removed from the
+  // listeners list by users who want to shut down the default XML output
+  // controlled by this flag and substitute it with custom one.  Note that
+  // removing this object from the listener list with Release transfers its
+  // ownership to the caller and makes this function return NULL the next
+  // time.
+  TestEventListener* default_xml_generator() const {
+    return default_xml_generator_;
+  }
+
+ private:
+  friend class TestSuite;
+  friend class TestInfo;
+  friend class internal::DefaultGlobalTestPartResultReporter;
+  friend class internal::NoExecDeathTest;
+  friend class internal::TestEventListenersAccessor;
+  friend class internal::UnitTestImpl;
+
+  // Returns repeater that broadcasts the TestEventListener events to all
+  // subscribers.
+  TestEventListener* repeater();
+
+  // Sets the default_result_printer attribute to the provided listener.
+  // The listener is also added to the listener list and previous
+  // default_result_printer is removed from it and deleted. The listener can
+  // also be NULL in which case it will not be added to the list. Does
+  // nothing if the previous and the current listener objects are the same.
+  void SetDefaultResultPrinter(TestEventListener* listener);
+
+  // Sets the default_xml_generator attribute to the provided listener.  The
+  // listener is also added to the listener list and previous
+  // default_xml_generator is removed from it and deleted. The listener can
+  // also be NULL in which case it will not be added to the list. Does
+  // nothing if the previous and the current listener objects are the same.
+  void SetDefaultXmlGenerator(TestEventListener* listener);
+
+  // Controls whether events will be forwarded by the repeater to the
+  // listeners in the list.
+  bool EventForwardingEnabled() const;
+  void SuppressEventForwarding();
+
+  // The actual list of listeners.
+  internal::TestEventRepeater* repeater_;
+  // Listener responsible for the standard result output.
+  TestEventListener* default_result_printer_;
+  // Listener responsible for the creation of the XML output file.
+  TestEventListener* default_xml_generator_;
+
+  // We disallow copying TestEventListeners.
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners);
+};
+
+// A UnitTest consists of a vector of TestSuites.
+//
+// This is a singleton class.  The only instance of UnitTest is
+// created when UnitTest::GetInstance() is first called.  This
+// instance is never deleted.
+//
+// UnitTest is not copyable.
+//
+// This class is thread-safe as long as the methods are called
+// according to their specification.
+class GTEST_API_ UnitTest {
+ public:
+  // Gets the singleton UnitTest object.  The first time this method
+  // is called, a UnitTest object is constructed and returned.
+  // Consecutive calls will return the same object.
+  static UnitTest* GetInstance();
+
+  // Runs all tests in this UnitTest object and prints the result.
+  // Returns 0 if successful, or 1 otherwise.
+  //
+  // This method can only be called from the main thread.
+  //
+  // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+  int Run() GTEST_MUST_USE_RESULT_;
+
+  // Returns the working directory when the first TEST() or TEST_F()
+  // was executed.  The UnitTest object owns the string.
+  const char* original_working_dir() const;
+
+  // Returns the TestSuite object for the test that's currently running,
+  // or NULL if no test is running.
+  const TestSuite* current_test_suite() const GTEST_LOCK_EXCLUDED_(mutex_);
+
+// Legacy API is still available but deprecated
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  const TestCase* current_test_case() const GTEST_LOCK_EXCLUDED_(mutex_);
+#endif
+
+  // Returns the TestInfo object for the test that's currently running,
+  // or NULL if no test is running.
+  const TestInfo* current_test_info() const
+      GTEST_LOCK_EXCLUDED_(mutex_);
+
+  // Returns the random seed used at the start of the current test run.
+  int random_seed() const;
+
+  // Returns the ParameterizedTestSuiteRegistry object used to keep track of
+  // value-parameterized tests and instantiate and register them.
+  //
+  // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+  internal::ParameterizedTestSuiteRegistry& parameterized_test_registry()
+      GTEST_LOCK_EXCLUDED_(mutex_);
+
+  // Gets the number of successful test suites.
+  int successful_test_suite_count() const;
+
+  // Gets the number of failed test suites.
+  int failed_test_suite_count() const;
+
+  // Gets the number of all test suites.
+  int total_test_suite_count() const;
+
+  // Gets the number of all test suites that contain at least one test
+  // that should run.
+  int test_suite_to_run_count() const;
+
+  //  Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  int successful_test_case_count() const;
+  int failed_test_case_count() const;
+  int total_test_case_count() const;
+  int test_case_to_run_count() const;
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  // Gets the number of successful tests.
+  int successful_test_count() const;
+
+  // Gets the number of skipped tests.
+  int skipped_test_count() const;
+
+  // Gets the number of failed tests.
+  int failed_test_count() const;
+
+  // Gets the number of disabled tests that will be reported in the XML report.
+  int reportable_disabled_test_count() const;
+
+  // Gets the number of disabled tests.
+  int disabled_test_count() const;
+
+  // Gets the number of tests to be printed in the XML report.
+  int reportable_test_count() const;
+
+  // Gets the number of all tests.
+  int total_test_count() const;
+
+  // Gets the number of tests that should run.
+  int test_to_run_count() const;
+
+  // Gets the time of the test program start, in ms from the start of the
+  // UNIX epoch.
+  TimeInMillis start_timestamp() const;
+
+  // Gets the elapsed time, in milliseconds.
+  TimeInMillis elapsed_time() const;
+
+  // Returns true if and only if the unit test passed (i.e. all test suites
+  // passed).
+  bool Passed() const;
+
+  // Returns true if and only if the unit test failed (i.e. some test suite
+  // failed or something outside of all tests failed).
+  bool Failed() const;
+
+  // Gets the i-th test suite among all the test suites. i can range from 0 to
+  // total_test_suite_count() - 1. If i is not in that range, returns NULL.
+  const TestSuite* GetTestSuite(int i) const;
+
+//  Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  const TestCase* GetTestCase(int i) const;
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  // Returns the TestResult containing information on test failures and
+  // properties logged outside of individual test suites.
+  const TestResult& ad_hoc_test_result() const;
+
+  // Returns the list of event listeners that can be used to track events
+  // inside Google Test.
+  TestEventListeners& listeners();
+
+ private:
+  // Registers and returns a global test environment.  When a test
+  // program is run, all global test environments will be set-up in
+  // the order they were registered.  After all tests in the program
+  // have finished, all global test environments will be torn-down in
+  // the *reverse* order they were registered.
+  //
+  // The UnitTest object takes ownership of the given environment.
+  //
+  // This method can only be called from the main thread.
+  Environment* AddEnvironment(Environment* env);
+
+  // Adds a TestPartResult to the current TestResult object.  All
+  // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc)
+  // eventually call this to report their results.  The user code
+  // should use the assertion macros instead of calling this directly.
+  void AddTestPartResult(TestPartResult::Type result_type,
+                         const char* file_name,
+                         int line_number,
+                         const std::string& message,
+                         const std::string& os_stack_trace)
+      GTEST_LOCK_EXCLUDED_(mutex_);
+
+  // Adds a TestProperty to the current TestResult object when invoked from
+  // inside a test, to current TestSuite's ad_hoc_test_result_ when invoked
+  // from SetUpTestSuite or TearDownTestSuite, or to the global property set
+  // when invoked elsewhere.  If the result already contains a property with
+  // the same key, the value will be updated.
+  void RecordProperty(const std::string& key, const std::string& value);
+
+  // Gets the i-th test suite among all the test suites. i can range from 0 to
+  // total_test_suite_count() - 1. If i is not in that range, returns NULL.
+  TestSuite* GetMutableTestSuite(int i);
+
+  // Accessors for the implementation object.
+  internal::UnitTestImpl* impl() { return impl_; }
+  const internal::UnitTestImpl* impl() const { return impl_; }
+
+  // These classes and functions are friends as they need to access private
+  // members of UnitTest.
+  friend class ScopedTrace;
+  friend class Test;
+  friend class internal::AssertHelper;
+  friend class internal::StreamingListenerTest;
+  friend class internal::UnitTestRecordPropertyTestHelper;
+  friend Environment* AddGlobalTestEnvironment(Environment* env);
+  friend std::set<std::string>* internal::GetIgnoredParameterizedTestSuites();
+  friend internal::UnitTestImpl* internal::GetUnitTestImpl();
+  friend void internal::ReportFailureInUnknownLocation(
+      TestPartResult::Type result_type,
+      const std::string& message);
+
+  // Creates an empty UnitTest.
+  UnitTest();
+
+  // D'tor
+  virtual ~UnitTest();
+
+  // Pushes a trace defined by SCOPED_TRACE() on to the per-thread
+  // Google Test trace stack.
+  void PushGTestTrace(const internal::TraceInfo& trace)
+      GTEST_LOCK_EXCLUDED_(mutex_);
+
+  // Pops a trace from the per-thread Google Test trace stack.
+  void PopGTestTrace()
+      GTEST_LOCK_EXCLUDED_(mutex_);
+
+  // Protects mutable state in *impl_.  This is mutable as some const
+  // methods need to lock it too.
+  mutable internal::Mutex mutex_;
+
+  // Opaque implementation object.  This field is never changed once
+  // the object is constructed.  We don't mark it as const here, as
+  // doing so will cause a warning in the constructor of UnitTest.
+  // Mutable state in *impl_ is protected by mutex_.
+  internal::UnitTestImpl* impl_;
+
+  // We disallow copying UnitTest.
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest);
+};
+
+// A convenient wrapper for adding an environment for the test
+// program.
+//
+// You should call this before RUN_ALL_TESTS() is called, probably in
+// main().  If you use gtest_main, you need to call this before main()
+// starts for it to take effect.  For example, you can define a global
+// variable like this:
+//
+//   testing::Environment* const foo_env =
+//       testing::AddGlobalTestEnvironment(new FooEnvironment);
+//
+// However, we strongly recommend you to write your own main() and
+// call AddGlobalTestEnvironment() there, as relying on initialization
+// of global variables makes the code harder to read and may cause
+// problems when you register multiple environments from different
+// translation units and the environments have dependencies among them
+// (remember that the compiler doesn't guarantee the order in which
+// global variables from different translation units are initialized).
+inline Environment* AddGlobalTestEnvironment(Environment* env) {
+  return UnitTest::GetInstance()->AddEnvironment(env);
+}
+
+// Initializes Google Test.  This must be called before calling
+// RUN_ALL_TESTS().  In particular, it parses a command line for the
+// flags that Google Test recognizes.  Whenever a Google Test flag is
+// seen, it is removed from argv, and *argc is decremented.
+//
+// No value is returned.  Instead, the Google Test flag variables are
+// updated.
+//
+// Calling the function for the second time has no user-visible effect.
+GTEST_API_ void InitGoogleTest(int* argc, char** argv);
+
+// This overloaded version can be used in Windows programs compiled in
+// UNICODE mode.
+GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv);
+
+// This overloaded version can be used on Arduino/embedded platforms where
+// there is no argc/argv.
+GTEST_API_ void InitGoogleTest();
+
+namespace internal {
+
+// Separate the error generating code from the code path to reduce the stack
+// frame size of CmpHelperEQ. This helps reduce the overhead of some sanitizers
+// when calling EXPECT_* in a tight loop.
+template <typename T1, typename T2>
+AssertionResult CmpHelperEQFailure(const char* lhs_expression,
+                                   const char* rhs_expression,
+                                   const T1& lhs, const T2& rhs) {
+  return EqFailure(lhs_expression,
+                   rhs_expression,
+                   FormatForComparisonFailureMessage(lhs, rhs),
+                   FormatForComparisonFailureMessage(rhs, lhs),
+                   false);
+}
+
+// This block of code defines operator==/!=
+// to block lexical scope lookup.
+// It prevents using invalid operator==/!= defined at namespace scope.
+struct faketype {};
+inline bool operator==(faketype, faketype) { return true; }
+inline bool operator!=(faketype, faketype) { return false; }
+
+// The helper function for {ASSERT|EXPECT}_EQ.
+template <typename T1, typename T2>
+AssertionResult CmpHelperEQ(const char* lhs_expression,
+                            const char* rhs_expression,
+                            const T1& lhs,
+                            const T2& rhs) {
+  if (lhs == rhs) {
+    return AssertionSuccess();
+  }
+
+  return CmpHelperEQFailure(lhs_expression, rhs_expression, lhs, rhs);
+}
+
+class EqHelper {
+ public:
+  // This templatized version is for the general case.
+  template <
+      typename T1, typename T2,
+      // Disable this overload for cases where one argument is a pointer
+      // and the other is the null pointer constant.
+      typename std::enable_if<!std::is_integral<T1>::value ||
+                              !std::is_pointer<T2>::value>::type* = nullptr>
+  static AssertionResult Compare(const char* lhs_expression,
+                                 const char* rhs_expression, const T1& lhs,
+                                 const T2& rhs) {
+    return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs);
+  }
+
+  // With this overloaded version, we allow anonymous enums to be used
+  // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous
+  // enums can be implicitly cast to BiggestInt.
+  //
+  // Even though its body looks the same as the above version, we
+  // cannot merge the two, as it will make anonymous enums unhappy.
+  static AssertionResult Compare(const char* lhs_expression,
+                                 const char* rhs_expression,
+                                 BiggestInt lhs,
+                                 BiggestInt rhs) {
+    return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs);
+  }
+
+  template <typename T>
+  static AssertionResult Compare(
+      const char* lhs_expression, const char* rhs_expression,
+      // Handle cases where '0' is used as a null pointer literal.
+      std::nullptr_t /* lhs */, T* rhs) {
+    // We already know that 'lhs' is a null pointer.
+    return CmpHelperEQ(lhs_expression, rhs_expression, static_cast<T*>(nullptr),
+                       rhs);
+  }
+};
+
+// Separate the error generating code from the code path to reduce the stack
+// frame size of CmpHelperOP. This helps reduce the overhead of some sanitizers
+// when calling EXPECT_OP in a tight loop.
+template <typename T1, typename T2>
+AssertionResult CmpHelperOpFailure(const char* expr1, const char* expr2,
+                                   const T1& val1, const T2& val2,
+                                   const char* op) {
+  return AssertionFailure()
+         << "Expected: (" << expr1 << ") " << op << " (" << expr2
+         << "), actual: " << FormatForComparisonFailureMessage(val1, val2)
+         << " vs " << FormatForComparisonFailureMessage(val2, val1);
+}
+
+// A macro for implementing the helper functions needed to implement
+// ASSERT_?? and EXPECT_??.  It is here just to avoid copy-and-paste
+// of similar code.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+
+#define GTEST_IMPL_CMP_HELPER_(op_name, op)\
+template <typename T1, typename T2>\
+AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \
+                                   const T1& val1, const T2& val2) {\
+  if (val1 op val2) {\
+    return AssertionSuccess();\
+  } else {\
+    return CmpHelperOpFailure(expr1, expr2, val1, val2, #op);\
+  }\
+}
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+
+// Implements the helper function for {ASSERT|EXPECT}_NE
+GTEST_IMPL_CMP_HELPER_(NE, !=)
+// Implements the helper function for {ASSERT|EXPECT}_LE
+GTEST_IMPL_CMP_HELPER_(LE, <=)
+// Implements the helper function for {ASSERT|EXPECT}_LT
+GTEST_IMPL_CMP_HELPER_(LT, <)
+// Implements the helper function for {ASSERT|EXPECT}_GE
+GTEST_IMPL_CMP_HELPER_(GE, >=)
+// Implements the helper function for {ASSERT|EXPECT}_GT
+GTEST_IMPL_CMP_HELPER_(GT, >)
+
+#undef GTEST_IMPL_CMP_HELPER_
+
+// The helper function for {ASSERT|EXPECT}_STREQ.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression,
+                                          const char* s2_expression,
+                                          const char* s1,
+                                          const char* s2);
+
+// The helper function for {ASSERT|EXPECT}_STRCASEEQ.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* s1_expression,
+                                              const char* s2_expression,
+                                              const char* s1,
+                                              const char* s2);
+
+// The helper function for {ASSERT|EXPECT}_STRNE.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression,
+                                          const char* s2_expression,
+                                          const char* s1,
+                                          const char* s2);
+
+// The helper function for {ASSERT|EXPECT}_STRCASENE.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression,
+                                              const char* s2_expression,
+                                              const char* s1,
+                                              const char* s2);
+
+
+// Helper function for *_STREQ on wide strings.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTREQ(const char* s1_expression,
+                                          const char* s2_expression,
+                                          const wchar_t* s1,
+                                          const wchar_t* s2);
+
+// Helper function for *_STRNE on wide strings.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression,
+                                          const char* s2_expression,
+                                          const wchar_t* s1,
+                                          const wchar_t* s2);
+
+}  // namespace internal
+
+// IsSubstring() and IsNotSubstring() are intended to be used as the
+// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by
+// themselves.  They check whether needle is a substring of haystack
+// (NULL is considered a substring of itself only), and return an
+// appropriate error message when they fail.
+//
+// The {needle,haystack}_expr arguments are the stringified
+// expressions that generated the two real arguments.
+GTEST_API_ AssertionResult IsSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const char* needle, const char* haystack);
+GTEST_API_ AssertionResult IsSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const wchar_t* needle, const wchar_t* haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const char* needle, const char* haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const wchar_t* needle, const wchar_t* haystack);
+GTEST_API_ AssertionResult IsSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const ::std::string& needle, const ::std::string& haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const ::std::string& needle, const ::std::string& haystack);
+
+#if GTEST_HAS_STD_WSTRING
+GTEST_API_ AssertionResult IsSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const ::std::wstring& needle, const ::std::wstring& haystack);
+GTEST_API_ AssertionResult IsNotSubstring(
+    const char* needle_expr, const char* haystack_expr,
+    const ::std::wstring& needle, const ::std::wstring& haystack);
+#endif  // GTEST_HAS_STD_WSTRING
+
+namespace internal {
+
+// Helper template function for comparing floating-points.
+//
+// Template parameter:
+//
+//   RawType: the raw floating-point type (either float or double)
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+template <typename RawType>
+AssertionResult CmpHelperFloatingPointEQ(const char* lhs_expression,
+                                         const char* rhs_expression,
+                                         RawType lhs_value,
+                                         RawType rhs_value) {
+  const FloatingPoint<RawType> lhs(lhs_value), rhs(rhs_value);
+
+  if (lhs.AlmostEquals(rhs)) {
+    return AssertionSuccess();
+  }
+
+  ::std::stringstream lhs_ss;
+  lhs_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+         << lhs_value;
+
+  ::std::stringstream rhs_ss;
+  rhs_ss << std::setprecision(std::numeric_limits<RawType>::digits10 + 2)
+         << rhs_value;
+
+  return EqFailure(lhs_expression,
+                   rhs_expression,
+                   StringStreamToString(&lhs_ss),
+                   StringStreamToString(&rhs_ss),
+                   false);
+}
+
+// Helper function for implementing ASSERT_NEAR.
+//
+// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
+GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1,
+                                                const char* expr2,
+                                                const char* abs_error_expr,
+                                                double val1,
+                                                double val2,
+                                                double abs_error);
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+// A class that enables one to stream messages to assertion macros
+class GTEST_API_ AssertHelper {
+ public:
+  // Constructor.
+  AssertHelper(TestPartResult::Type type,
+               const char* file,
+               int line,
+               const char* message);
+  ~AssertHelper();
+
+  // Message assignment is a semantic trick to enable assertion
+  // streaming; see the GTEST_MESSAGE_ macro below.
+  void operator=(const Message& message) const;
+
+ private:
+  // We put our data in a struct so that the size of the AssertHelper class can
+  // be as small as possible.  This is important because gcc is incapable of
+  // re-using stack space even for temporary variables, so every EXPECT_EQ
+  // reserves stack space for another AssertHelper.
+  struct AssertHelperData {
+    AssertHelperData(TestPartResult::Type t,
+                     const char* srcfile,
+                     int line_num,
+                     const char* msg)
+        : type(t), file(srcfile), line(line_num), message(msg) { }
+
+    TestPartResult::Type const type;
+    const char* const file;
+    int const line;
+    std::string const message;
+
+   private:
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData);
+  };
+
+  AssertHelperData* const data_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper);
+};
+
+}  // namespace internal
+
+// The pure interface class that all value-parameterized tests inherit from.
+// A value-parameterized class must inherit from both ::testing::Test and
+// ::testing::WithParamInterface. In most cases that just means inheriting
+// from ::testing::TestWithParam, but more complicated test hierarchies
+// may need to inherit from Test and WithParamInterface at different levels.
+//
+// This interface has support for accessing the test parameter value via
+// the GetParam() method.
+//
+// Use it with one of the parameter generator defining functions, like Range(),
+// Values(), ValuesIn(), Bool(), and Combine().
+//
+// class FooTest : public ::testing::TestWithParam<int> {
+//  protected:
+//   FooTest() {
+//     // Can use GetParam() here.
+//   }
+//   ~FooTest() override {
+//     // Can use GetParam() here.
+//   }
+//   void SetUp() override {
+//     // Can use GetParam() here.
+//   }
+//   void TearDown override {
+//     // Can use GetParam() here.
+//   }
+// };
+// TEST_P(FooTest, DoesBar) {
+//   // Can use GetParam() method here.
+//   Foo foo;
+//   ASSERT_TRUE(foo.DoesBar(GetParam()));
+// }
+// INSTANTIATE_TEST_SUITE_P(OneToTenRange, FooTest, ::testing::Range(1, 10));
+
+template <typename T>
+class WithParamInterface {
+ public:
+  typedef T ParamType;
+  virtual ~WithParamInterface() {}
+
+  // The current parameter value. Is also available in the test fixture's
+  // constructor.
+  static const ParamType& GetParam() {
+    GTEST_CHECK_(parameter_ != nullptr)
+        << "GetParam() can only be called inside a value-parameterized test "
+        << "-- did you intend to write TEST_P instead of TEST_F?";
+    return *parameter_;
+  }
+
+ private:
+  // Sets parameter value. The caller is responsible for making sure the value
+  // remains alive and unchanged throughout the current test.
+  static void SetParam(const ParamType* parameter) {
+    parameter_ = parameter;
+  }
+
+  // Static value used for accessing parameter during a test lifetime.
+  static const ParamType* parameter_;
+
+  // TestClass must be a subclass of WithParamInterface<T> and Test.
+  template <class TestClass> friend class internal::ParameterizedTestFactory;
+};
+
+template <typename T>
+const T* WithParamInterface<T>::parameter_ = nullptr;
+
+// Most value-parameterized classes can ignore the existence of
+// WithParamInterface, and can just inherit from ::testing::TestWithParam.
+
+template <typename T>
+class TestWithParam : public Test, public WithParamInterface<T> {
+};
+
+// Macros for indicating success/failure in test code.
+
+// Skips test in runtime.
+// Skipping test aborts current function.
+// Skipped tests are neither successful nor failed.
+#define GTEST_SKIP() GTEST_SKIP_("")
+
+// ADD_FAILURE unconditionally adds a failure to the current test.
+// SUCCEED generates a success - it doesn't automatically make the
+// current test successful, as a test is only successful when it has
+// no failure.
+//
+// EXPECT_* verifies that a certain condition is satisfied.  If not,
+// it behaves like ADD_FAILURE.  In particular:
+//
+//   EXPECT_TRUE  verifies that a Boolean condition is true.
+//   EXPECT_FALSE verifies that a Boolean condition is false.
+//
+// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except
+// that they will also abort the current function on failure.  People
+// usually want the fail-fast behavior of FAIL and ASSERT_*, but those
+// writing data-driven tests often find themselves using ADD_FAILURE
+// and EXPECT_* more.
+
+// Generates a nonfatal failure with a generic message.
+#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed")
+
+// Generates a nonfatal failure at the given source file location with
+// a generic message.
+#define ADD_FAILURE_AT(file, line) \
+  GTEST_MESSAGE_AT_(file, line, "Failed", \
+                    ::testing::TestPartResult::kNonFatalFailure)
+
+// Generates a fatal failure with a generic message.
+#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed")
+
+// Like GTEST_FAIL(), but at the given source file location.
+#define GTEST_FAIL_AT(file, line)         \
+  GTEST_MESSAGE_AT_(file, line, "Failed", \
+                    ::testing::TestPartResult::kFatalFailure)
+
+// Define this macro to 1 to omit the definition of FAIL(), which is a
+// generic name and clashes with some other libraries.
+#if !GTEST_DONT_DEFINE_FAIL
+# define FAIL() GTEST_FAIL()
+#endif
+
+// Generates a success with a generic message.
+#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded")
+
+// Define this macro to 1 to omit the definition of SUCCEED(), which
+// is a generic name and clashes with some other libraries.
+#if !GTEST_DONT_DEFINE_SUCCEED
+# define SUCCEED() GTEST_SUCCEED()
+#endif
+
+// Macros for testing exceptions.
+//
+//    * {ASSERT|EXPECT}_THROW(statement, expected_exception):
+//         Tests that the statement throws the expected exception.
+//    * {ASSERT|EXPECT}_NO_THROW(statement):
+//         Tests that the statement doesn't throw any exception.
+//    * {ASSERT|EXPECT}_ANY_THROW(statement):
+//         Tests that the statement throws an exception.
+
+#define EXPECT_THROW(statement, expected_exception) \
+  GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_NO_THROW(statement) \
+  GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_ANY_THROW(statement) \
+  GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_THROW(statement, expected_exception) \
+  GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_)
+#define ASSERT_NO_THROW(statement) \
+  GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_)
+#define ASSERT_ANY_THROW(statement) \
+  GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_)
+
+// Boolean assertions. Condition can be either a Boolean expression or an
+// AssertionResult. For more information on how to use AssertionResult with
+// these macros see comments on that class.
+#define GTEST_EXPECT_TRUE(condition) \
+  GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
+                      GTEST_NONFATAL_FAILURE_)
+#define GTEST_EXPECT_FALSE(condition) \
+  GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
+                      GTEST_NONFATAL_FAILURE_)
+#define GTEST_ASSERT_TRUE(condition) \
+  GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \
+                      GTEST_FATAL_FAILURE_)
+#define GTEST_ASSERT_FALSE(condition) \
+  GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \
+                      GTEST_FATAL_FAILURE_)
+
+// Define these macros to 1 to omit the definition of the corresponding
+// EXPECT or ASSERT, which clashes with some users' own code.
+
+#if !GTEST_DONT_DEFINE_EXPECT_TRUE
+#define EXPECT_TRUE(condition) GTEST_EXPECT_TRUE(condition)
+#endif
+
+#if !GTEST_DONT_DEFINE_EXPECT_FALSE
+#define EXPECT_FALSE(condition) GTEST_EXPECT_FALSE(condition)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_TRUE
+#define ASSERT_TRUE(condition) GTEST_ASSERT_TRUE(condition)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_FALSE
+#define ASSERT_FALSE(condition) GTEST_ASSERT_FALSE(condition)
+#endif
+
+// Macros for testing equalities and inequalities.
+//
+//    * {ASSERT|EXPECT}_EQ(v1, v2): Tests that v1 == v2
+//    * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2
+//    * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2
+//    * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2
+//    * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2
+//    * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2
+//
+// When they are not, Google Test prints both the tested expressions and
+// their actual values.  The values must be compatible built-in types,
+// or you will get a compiler error.  By "compatible" we mean that the
+// values can be compared by the respective operator.
+//
+// Note:
+//
+//   1. It is possible to make a user-defined type work with
+//   {ASSERT|EXPECT}_??(), but that requires overloading the
+//   comparison operators and is thus discouraged by the Google C++
+//   Usage Guide.  Therefore, you are advised to use the
+//   {ASSERT|EXPECT}_TRUE() macro to assert that two objects are
+//   equal.
+//
+//   2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on
+//   pointers (in particular, C strings).  Therefore, if you use it
+//   with two C strings, you are testing how their locations in memory
+//   are related, not how their content is related.  To compare two C
+//   strings by content, use {ASSERT|EXPECT}_STR*().
+//
+//   3. {ASSERT|EXPECT}_EQ(v1, v2) is preferred to
+//   {ASSERT|EXPECT}_TRUE(v1 == v2), as the former tells you
+//   what the actual value is when it fails, and similarly for the
+//   other comparisons.
+//
+//   4. Do not depend on the order in which {ASSERT|EXPECT}_??()
+//   evaluate their arguments, which is undefined.
+//
+//   5. These macros evaluate their arguments exactly once.
+//
+// Examples:
+//
+//   EXPECT_NE(Foo(), 5);
+//   EXPECT_EQ(a_pointer, NULL);
+//   ASSERT_LT(i, array_size);
+//   ASSERT_GT(records.size(), 0) << "There is no record left.";
+
+#define EXPECT_EQ(val1, val2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)
+#define EXPECT_NE(val1, val2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
+#define EXPECT_LE(val1, val2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
+#define EXPECT_LT(val1, val2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2)
+#define EXPECT_GE(val1, val2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2)
+#define EXPECT_GT(val1, val2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)
+
+#define GTEST_ASSERT_EQ(val1, val2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)
+#define GTEST_ASSERT_NE(val1, val2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
+#define GTEST_ASSERT_LE(val1, val2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
+#define GTEST_ASSERT_LT(val1, val2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2)
+#define GTEST_ASSERT_GE(val1, val2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2)
+#define GTEST_ASSERT_GT(val1, val2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)
+
+// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of
+// ASSERT_XY(), which clashes with some users' own code.
+
+#if !GTEST_DONT_DEFINE_ASSERT_EQ
+# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_NE
+# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_LE
+# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_LT
+# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_GE
+# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2)
+#endif
+
+#if !GTEST_DONT_DEFINE_ASSERT_GT
+# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2)
+#endif
+
+// C-string Comparisons.  All tests treat NULL and any non-NULL string
+// as different.  Two NULLs are equal.
+//
+//    * {ASSERT|EXPECT}_STREQ(s1, s2):     Tests that s1 == s2
+//    * {ASSERT|EXPECT}_STRNE(s1, s2):     Tests that s1 != s2
+//    * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case
+//    * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case
+//
+// For wide or narrow string objects, you can use the
+// {ASSERT|EXPECT}_??() macros.
+//
+// Don't depend on the order in which the arguments are evaluated,
+// which is undefined.
+//
+// These macros evaluate their arguments exactly once.
+
+#define EXPECT_STREQ(s1, s2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2)
+#define EXPECT_STRNE(s1, s2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2)
+#define EXPECT_STRCASEEQ(s1, s2) \
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2)
+#define EXPECT_STRCASENE(s1, s2)\
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2)
+
+#define ASSERT_STREQ(s1, s2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, s1, s2)
+#define ASSERT_STRNE(s1, s2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2)
+#define ASSERT_STRCASEEQ(s1, s2) \
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, s1, s2)
+#define ASSERT_STRCASENE(s1, s2)\
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2)
+
+// Macros for comparing floating-point numbers.
+//
+//    * {ASSERT|EXPECT}_FLOAT_EQ(val1, val2):
+//         Tests that two float values are almost equal.
+//    * {ASSERT|EXPECT}_DOUBLE_EQ(val1, val2):
+//         Tests that two double values are almost equal.
+//    * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error):
+//         Tests that v1 and v2 are within the given distance to each other.
+//
+// Google Test uses ULP-based comparison to automatically pick a default
+// error bound that is appropriate for the operands.  See the
+// FloatingPoint template class in gtest-internal.h if you are
+// interested in the implementation details.
+
+#define EXPECT_FLOAT_EQ(val1, val2)\
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \
+                      val1, val2)
+
+#define EXPECT_DOUBLE_EQ(val1, val2)\
+  EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \
+                      val1, val2)
+
+#define ASSERT_FLOAT_EQ(val1, val2)\
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<float>, \
+                      val1, val2)
+
+#define ASSERT_DOUBLE_EQ(val1, val2)\
+  ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ<double>, \
+                      val1, val2)
+
+#define EXPECT_NEAR(val1, val2, abs_error)\
+  EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \
+                      val1, val2, abs_error)
+
+#define ASSERT_NEAR(val1, val2, abs_error)\
+  ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \
+                      val1, val2, abs_error)
+
+// These predicate format functions work on floating-point values, and
+// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g.
+//
+//   EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0);
+
+// Asserts that val1 is less than, or almost equal to, val2.  Fails
+// otherwise.  In particular, it fails if either val1 or val2 is NaN.
+GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2,
+                                   float val1, float val2);
+GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2,
+                                    double val1, double val2);
+
+
+#if GTEST_OS_WINDOWS
+
+// Macros that test for HRESULT failure and success, these are only useful
+// on Windows, and rely on Windows SDK macros and APIs to compile.
+//
+//    * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr)
+//
+// When expr unexpectedly fails or succeeds, Google Test prints the
+// expected result and the actual result with both a human-readable
+// string representation of the error, if available, as well as the
+// hex result code.
+# define EXPECT_HRESULT_SUCCEEDED(expr) \
+    EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr))
+
+# define ASSERT_HRESULT_SUCCEEDED(expr) \
+    ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr))
+
+# define EXPECT_HRESULT_FAILED(expr) \
+    EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr))
+
+# define ASSERT_HRESULT_FAILED(expr) \
+    ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr))
+
+#endif  // GTEST_OS_WINDOWS
+
+// Macros that execute statement and check that it doesn't generate new fatal
+// failures in the current thread.
+//
+//   * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement);
+//
+// Examples:
+//
+//   EXPECT_NO_FATAL_FAILURE(Process());
+//   ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed";
+//
+#define ASSERT_NO_FATAL_FAILURE(statement) \
+    GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_)
+#define EXPECT_NO_FATAL_FAILURE(statement) \
+    GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_)
+
+// Causes a trace (including the given source file path and line number,
+// and the given message) to be included in every test failure message generated
+// by code in the scope of the lifetime of an instance of this class. The effect
+// is undone with the destruction of the instance.
+//
+// The message argument can be anything streamable to std::ostream.
+//
+// Example:
+//   testing::ScopedTrace trace("file.cc", 123, "message");
+//
+class GTEST_API_ ScopedTrace {
+ public:
+  // The c'tor pushes the given source file location and message onto
+  // a trace stack maintained by Google Test.
+
+  // Template version. Uses Message() to convert the values into strings.
+  // Slow, but flexible.
+  template <typename T>
+  ScopedTrace(const char* file, int line, const T& message) {
+    PushTrace(file, line, (Message() << message).GetString());
+  }
+
+  // Optimize for some known types.
+  ScopedTrace(const char* file, int line, const char* message) {
+    PushTrace(file, line, message ? message : "(null)");
+  }
+
+  ScopedTrace(const char* file, int line, const std::string& message) {
+    PushTrace(file, line, message);
+  }
+
+  // The d'tor pops the info pushed by the c'tor.
+  //
+  // Note that the d'tor is not virtual in order to be efficient.
+  // Don't inherit from ScopedTrace!
+  ~ScopedTrace();
+
+ private:
+  void PushTrace(const char* file, int line, std::string message);
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace);
+} GTEST_ATTRIBUTE_UNUSED_;  // A ScopedTrace object does its job in its
+                            // c'tor and d'tor.  Therefore it doesn't
+                            // need to be used otherwise.
+
+// Causes a trace (including the source file path, the current line
+// number, and the given message) to be included in every test failure
+// message generated by code in the current scope.  The effect is
+// undone when the control leaves the current scope.
+//
+// The message argument can be anything streamable to std::ostream.
+//
+// In the implementation, we include the current line number as part
+// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s
+// to appear in the same block - as long as they are on different
+// lines.
+//
+// Assuming that each thread maintains its own stack of traces.
+// Therefore, a SCOPED_TRACE() would (correctly) only affect the
+// assertions in its own thread.
+#define SCOPED_TRACE(message) \
+  ::testing::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\
+    __FILE__, __LINE__, (message))
+
+// Compile-time assertion for type equality.
+// StaticAssertTypeEq<type1, type2>() compiles if and only if type1 and type2
+// are the same type.  The value it returns is not interesting.
+//
+// Instead of making StaticAssertTypeEq a class template, we make it a
+// function template that invokes a helper class template.  This
+// prevents a user from misusing StaticAssertTypeEq<T1, T2> by
+// defining objects of that type.
+//
+// CAVEAT:
+//
+// When used inside a method of a class template,
+// StaticAssertTypeEq<T1, T2>() is effective ONLY IF the method is
+// instantiated.  For example, given:
+//
+//   template <typename T> class Foo {
+//    public:
+//     void Bar() { testing::StaticAssertTypeEq<int, T>(); }
+//   };
+//
+// the code:
+//
+//   void Test1() { Foo<bool> foo; }
+//
+// will NOT generate a compiler error, as Foo<bool>::Bar() is never
+// actually instantiated.  Instead, you need:
+//
+//   void Test2() { Foo<bool> foo; foo.Bar(); }
+//
+// to cause a compiler error.
+template <typename T1, typename T2>
+constexpr bool StaticAssertTypeEq() noexcept {
+  static_assert(std::is_same<T1, T2>::value, "T1 and T2 are not the same type");
+  return true;
+}
+
+// Defines a test.
+//
+// The first parameter is the name of the test suite, and the second
+// parameter is the name of the test within the test suite.
+//
+// The convention is to end the test suite name with "Test".  For
+// example, a test suite for the Foo class can be named FooTest.
+//
+// Test code should appear between braces after an invocation of
+// this macro.  Example:
+//
+//   TEST(FooTest, InitializesCorrectly) {
+//     Foo foo;
+//     EXPECT_TRUE(foo.StatusIsOK());
+//   }
+
+// Note that we call GetTestTypeId() instead of GetTypeId<
+// ::testing::Test>() here to get the type ID of testing::Test.  This
+// is to work around a suspected linker bug when using Google Test as
+// a framework on Mac OS X.  The bug causes GetTypeId<
+// ::testing::Test>() to return different values depending on whether
+// the call is from the Google Test framework itself or from user test
+// code.  GetTestTypeId() is guaranteed to always return the same
+// value, as it always calls GetTypeId<>() from the Google Test
+// framework.
+#define GTEST_TEST(test_suite_name, test_name)             \
+  GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \
+              ::testing::internal::GetTestTypeId())
+
+// Define this macro to 1 to omit the definition of TEST(), which
+// is a generic name and clashes with some other libraries.
+#if !GTEST_DONT_DEFINE_TEST
+#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name)
+#endif
+
+// Defines a test that uses a test fixture.
+//
+// The first parameter is the name of the test fixture class, which
+// also doubles as the test suite name.  The second parameter is the
+// name of the test within the test suite.
+//
+// A test fixture class must be declared earlier.  The user should put
+// the test code between braces after using this macro.  Example:
+//
+//   class FooTest : public testing::Test {
+//    protected:
+//     void SetUp() override { b_.AddElement(3); }
+//
+//     Foo a_;
+//     Foo b_;
+//   };
+//
+//   TEST_F(FooTest, InitializesCorrectly) {
+//     EXPECT_TRUE(a_.StatusIsOK());
+//   }
+//
+//   TEST_F(FooTest, ReturnsElementCountCorrectly) {
+//     EXPECT_EQ(a_.size(), 0);
+//     EXPECT_EQ(b_.size(), 1);
+//   }
+#define GTEST_TEST_F(test_fixture, test_name)\
+  GTEST_TEST_(test_fixture, test_name, test_fixture, \
+              ::testing::internal::GetTypeId<test_fixture>())
+#if !GTEST_DONT_DEFINE_TEST_F
+#define TEST_F(test_fixture, test_name) GTEST_TEST_F(test_fixture, test_name)
+#endif
+
+// Returns a path to temporary directory.
+// Tries to determine an appropriate directory for the platform.
+GTEST_API_ std::string TempDir();
+
+#ifdef _MSC_VER
+#  pragma warning(pop)
+#endif
+
+// Dynamically registers a test with the framework.
+//
+// This is an advanced API only to be used when the `TEST` macros are
+// insufficient. The macros should be preferred when possible, as they avoid
+// most of the complexity of calling this function.
+//
+// The `factory` argument is a factory callable (move-constructible) object or
+// function pointer that creates a new instance of the Test object. It
+// handles ownership to the caller. The signature of the callable is
+// `Fixture*()`, where `Fixture` is the test fixture class for the test. All
+// tests registered with the same `test_suite_name` must return the same
+// fixture type. This is checked at runtime.
+//
+// The framework will infer the fixture class from the factory and will call
+// the `SetUpTestSuite` and `TearDownTestSuite` for it.
+//
+// Must be called before `RUN_ALL_TESTS()` is invoked, otherwise behavior is
+// undefined.
+//
+// Use case example:
+//
+// class MyFixture : public ::testing::Test {
+//  public:
+//   // All of these optional, just like in regular macro usage.
+//   static void SetUpTestSuite() { ... }
+//   static void TearDownTestSuite() { ... }
+//   void SetUp() override { ... }
+//   void TearDown() override { ... }
+// };
+//
+// class MyTest : public MyFixture {
+//  public:
+//   explicit MyTest(int data) : data_(data) {}
+//   void TestBody() override { ... }
+//
+//  private:
+//   int data_;
+// };
+//
+// void RegisterMyTests(const std::vector<int>& values) {
+//   for (int v : values) {
+//     ::testing::RegisterTest(
+//         "MyFixture", ("Test" + std::to_string(v)).c_str(), nullptr,
+//         std::to_string(v).c_str(),
+//         __FILE__, __LINE__,
+//         // Important to use the fixture type as the return type here.
+//         [=]() -> MyFixture* { return new MyTest(v); });
+//   }
+// }
+// ...
+// int main(int argc, char** argv) {
+//   std::vector<int> values_to_test = LoadValuesFromConfig();
+//   RegisterMyTests(values_to_test);
+//   ...
+//   return RUN_ALL_TESTS();
+// }
+//
+template <int&... ExplicitParameterBarrier, typename Factory>
+TestInfo* RegisterTest(const char* test_suite_name, const char* test_name,
+                       const char* type_param, const char* value_param,
+                       const char* file, int line, Factory factory) {
+  using TestT = typename std::remove_pointer<decltype(factory())>::type;
+
+  class FactoryImpl : public internal::TestFactoryBase {
+   public:
+    explicit FactoryImpl(Factory f) : factory_(std::move(f)) {}
+    Test* CreateTest() override { return factory_(); }
+
+   private:
+    Factory factory_;
+  };
+
+  return internal::MakeAndRegisterTestInfo(
+      test_suite_name, test_name, type_param, value_param,
+      internal::CodeLocation(file, line), internal::GetTypeId<TestT>(),
+      internal::SuiteApiResolver<TestT>::GetSetUpCaseOrSuite(file, line),
+      internal::SuiteApiResolver<TestT>::GetTearDownCaseOrSuite(file, line),
+      new FactoryImpl{std::move(factory)});
+}
+
+}  // namespace testing
+
+// Use this function in main() to run all tests.  It returns 0 if all
+// tests are successful, or 1 otherwise.
+//
+// RUN_ALL_TESTS() should be invoked after the command line has been
+// parsed by InitGoogleTest().
+//
+// This function was formerly a macro; thus, it is in the global
+// namespace and has an all-caps name.
+int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_;
+
+inline int RUN_ALL_TESTS() {
+  return ::testing::UnitTest::GetInstance()->Run();
+}
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/gtest_pred_impl.h b/src/include/gromacs/external/googletest/googletest/include/gtest/gtest_pred_impl.h
new file mode 100644 (file)
index 0000000..67a96c9
--- /dev/null
@@ -0,0 +1,358 @@
+// Copyright 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file is AUTOMATICALLY GENERATED on 07/21/2021 by command
+// 'gen_gtest_pred_impl.py 5'.  DO NOT EDIT BY HAND!
+//
+// Implements a family of generic predicate assertion macros.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
+
+#include "gtest/gtest.h"
+
+namespace testing {
+
+// This header implements a family of generic predicate assertion
+// macros:
+//
+//   ASSERT_PRED_FORMAT1(pred_format, v1)
+//   ASSERT_PRED_FORMAT2(pred_format, v1, v2)
+//   ...
+//
+// where pred_format is a function or functor that takes n (in the
+// case of ASSERT_PRED_FORMATn) values and their source expression
+// text, and returns a testing::AssertionResult.  See the definition
+// of ASSERT_EQ in gtest.h for an example.
+//
+// If you don't care about formatting, you can use the more
+// restrictive version:
+//
+//   ASSERT_PRED1(pred, v1)
+//   ASSERT_PRED2(pred, v1, v2)
+//   ...
+//
+// where pred is an n-ary function or functor that returns bool,
+// and the values v1, v2, ..., must support the << operator for
+// streaming to std::ostream.
+//
+// We also define the EXPECT_* variations.
+//
+// For now we only support predicates whose arity is at most 5.
+// Please email googletestframework@googlegroups.com if you need
+// support for higher arities.
+
+// GTEST_ASSERT_ is the basic statement to which all of the assertions
+// in this file reduce.  Don't use this in your code.
+
+#define GTEST_ASSERT_(expression, on_failure) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (const ::testing::AssertionResult gtest_ar = (expression)) \
+    ; \
+  else \
+    on_failure(gtest_ar.failure_message())
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED1.  Don't use
+// this in your code.
+template <typename Pred,
+          typename T1>
+AssertionResult AssertPred1Helper(const char* pred_text,
+                                  const char* e1,
+                                  Pred pred,
+                                  const T1& v1) {
+  if (pred(v1)) return AssertionSuccess();
+
+  return AssertionFailure()
+         << pred_text << "(" << e1 << ") evaluates to false, where"
+         << "\n"
+         << e1 << " evaluates to " << ::testing::PrintToString(v1);
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\
+  GTEST_ASSERT_(pred_format(#v1, v1), \
+                on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED1.  Don't use
+// this in your code.
+#define GTEST_PRED1_(pred, v1, on_failure)\
+  GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \
+                                             #v1, \
+                                             pred, \
+                                             v1), on_failure)
+
+// Unary predicate assertion macros.
+#define EXPECT_PRED_FORMAT1(pred_format, v1) \
+  GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED1(pred, v1) \
+  GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT1(pred_format, v1) \
+  GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED1(pred, v1) \
+  GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED2.  Don't use
+// this in your code.
+template <typename Pred,
+          typename T1,
+          typename T2>
+AssertionResult AssertPred2Helper(const char* pred_text,
+                                  const char* e1,
+                                  const char* e2,
+                                  Pred pred,
+                                  const T1& v1,
+                                  const T2& v2) {
+  if (pred(v1, v2)) return AssertionSuccess();
+
+  return AssertionFailure()
+         << pred_text << "(" << e1 << ", " << e2
+         << ") evaluates to false, where"
+         << "\n"
+         << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
+         << e2 << " evaluates to " << ::testing::PrintToString(v2);
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\
+  GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \
+                on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED2.  Don't use
+// this in your code.
+#define GTEST_PRED2_(pred, v1, v2, on_failure)\
+  GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \
+                                             #v1, \
+                                             #v2, \
+                                             pred, \
+                                             v1, \
+                                             v2), on_failure)
+
+// Binary predicate assertion macros.
+#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
+  GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED2(pred, v1, v2) \
+  GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \
+  GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED2(pred, v1, v2) \
+  GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED3.  Don't use
+// this in your code.
+template <typename Pred,
+          typename T1,
+          typename T2,
+          typename T3>
+AssertionResult AssertPred3Helper(const char* pred_text,
+                                  const char* e1,
+                                  const char* e2,
+                                  const char* e3,
+                                  Pred pred,
+                                  const T1& v1,
+                                  const T2& v2,
+                                  const T3& v3) {
+  if (pred(v1, v2, v3)) return AssertionSuccess();
+
+  return AssertionFailure()
+         << pred_text << "(" << e1 << ", " << e2 << ", " << e3
+         << ") evaluates to false, where"
+         << "\n"
+         << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
+         << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
+         << e3 << " evaluates to " << ::testing::PrintToString(v3);
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\
+  GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \
+                on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED3.  Don't use
+// this in your code.
+#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\
+  GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \
+                                             #v1, \
+                                             #v2, \
+                                             #v3, \
+                                             pred, \
+                                             v1, \
+                                             v2, \
+                                             v3), on_failure)
+
+// Ternary predicate assertion macros.
+#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \
+  GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED3(pred, v1, v2, v3) \
+  GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \
+  GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED3(pred, v1, v2, v3) \
+  GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED4.  Don't use
+// this in your code.
+template <typename Pred,
+          typename T1,
+          typename T2,
+          typename T3,
+          typename T4>
+AssertionResult AssertPred4Helper(const char* pred_text,
+                                  const char* e1,
+                                  const char* e2,
+                                  const char* e3,
+                                  const char* e4,
+                                  Pred pred,
+                                  const T1& v1,
+                                  const T2& v2,
+                                  const T3& v3,
+                                  const T4& v4) {
+  if (pred(v1, v2, v3, v4)) return AssertionSuccess();
+
+  return AssertionFailure()
+         << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4
+         << ") evaluates to false, where"
+         << "\n"
+         << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
+         << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
+         << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n"
+         << e4 << " evaluates to " << ::testing::PrintToString(v4);
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\
+  GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \
+                on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED4.  Don't use
+// this in your code.
+#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\
+  GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \
+                                             #v1, \
+                                             #v2, \
+                                             #v3, \
+                                             #v4, \
+                                             pred, \
+                                             v1, \
+                                             v2, \
+                                             v3, \
+                                             v4), on_failure)
+
+// 4-ary predicate assertion macros.
+#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
+  GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED4(pred, v1, v2, v3, v4) \
+  GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
+  GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED4(pred, v1, v2, v3, v4) \
+  GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
+
+
+
+// Helper function for implementing {EXPECT|ASSERT}_PRED5.  Don't use
+// this in your code.
+template <typename Pred,
+          typename T1,
+          typename T2,
+          typename T3,
+          typename T4,
+          typename T5>
+AssertionResult AssertPred5Helper(const char* pred_text,
+                                  const char* e1,
+                                  const char* e2,
+                                  const char* e3,
+                                  const char* e4,
+                                  const char* e5,
+                                  Pred pred,
+                                  const T1& v1,
+                                  const T2& v2,
+                                  const T3& v3,
+                                  const T4& v4,
+                                  const T5& v5) {
+  if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess();
+
+  return AssertionFailure()
+         << pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4
+         << ", " << e5 << ") evaluates to false, where"
+         << "\n"
+         << e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
+         << e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
+         << e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n"
+         << e4 << " evaluates to " << ::testing::PrintToString(v4) << "\n"
+         << e5 << " evaluates to " << ::testing::PrintToString(v5);
+}
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5.
+// Don't use this in your code.
+#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\
+  GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \
+                on_failure)
+
+// Internal macro for implementing {EXPECT|ASSERT}_PRED5.  Don't use
+// this in your code.
+#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\
+  GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \
+                                             #v1, \
+                                             #v2, \
+                                             #v3, \
+                                             #v4, \
+                                             #v5, \
+                                             pred, \
+                                             v1, \
+                                             v2, \
+                                             v3, \
+                                             v4, \
+                                             v5), on_failure)
+
+// 5-ary predicate assertion macros.
+#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
+  GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
+#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \
+  GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
+#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
+  GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
+#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \
+  GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
+
+
+
+}  // namespace testing
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/gtest_prod.h b/src/include/gromacs/external/googletest/googletest/include/gtest/gtest_prod.h
new file mode 100644 (file)
index 0000000..25766ee
--- /dev/null
@@ -0,0 +1,60 @@
+// Copyright 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//
+// Google C++ Testing and Mocking Framework definitions useful in production code.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
+#define GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
+
+// When you need to test the private or protected members of a class,
+// use the FRIEND_TEST macro to declare your tests as friends of the
+// class.  For example:
+//
+// class MyClass {
+//  private:
+//   void PrivateMethod();
+//   FRIEND_TEST(MyClassTest, PrivateMethodWorks);
+// };
+//
+// class MyClassTest : public testing::Test {
+//   // ...
+// };
+//
+// TEST_F(MyClassTest, PrivateMethodWorks) {
+//   // Can call MyClass::PrivateMethod() here.
+// }
+//
+// Note: The test class must be in the same namespace as the class being tested.
+// For example, putting MyClassTest in an anonymous namespace will not work.
+
+#define FRIEND_TEST(test_case_name, test_name)\
+friend class test_case_name##_##test_name##_Test
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/internal/custom/gtest-port.h b/src/include/gromacs/external/googletest/googletest/include/gtest/internal/custom/gtest-port.h
new file mode 100644 (file)
index 0000000..db02881
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Injection point for custom user configurations. See README for details
+//
+// ** Custom implementation starts here **
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/internal/custom/gtest-printers.h b/src/include/gromacs/external/googletest/googletest/include/gtest/internal/custom/gtest-printers.h
new file mode 100644 (file)
index 0000000..b9495d8
--- /dev/null
@@ -0,0 +1,42 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file provides an injection point for custom printers in a local
+// installation of gTest.
+// It will be included from gtest-printers.h and the overrides in this file
+// will be visible to everyone.
+//
+// Injection point for custom user configurations. See README for details
+//
+// ** Custom implementation starts here **
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/internal/custom/gtest.h b/src/include/gromacs/external/googletest/googletest/include/gtest/internal/custom/gtest.h
new file mode 100644 (file)
index 0000000..afaaf17
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Injection point for custom user configurations. See README for details
+//
+// ** Custom implementation starts here **
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-death-test-internal.h b/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-death-test-internal.h
new file mode 100644 (file)
index 0000000..8e0023a
--- /dev/null
@@ -0,0 +1,301 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// The Google C++ Testing and Mocking Framework (Google Test)
+//
+// This header file defines internal utilities needed for implementing
+// death tests.  They are subject to change without notice.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
+
+#include "gtest/gtest-matchers.h"
+#include "gtest/internal/gtest-internal.h"
+
+#include <stdio.h>
+#include <memory>
+
+GTEST_DECLARE_string_(internal_run_death_test);
+
+namespace testing {
+namespace internal {
+
+// Names of the flags (needed for parsing Google Test flags).
+const char kDeathTestStyleFlag[] = "death_test_style";
+const char kDeathTestUseFork[] = "death_test_use_fork";
+const char kInternalRunDeathTestFlag[] = "internal_run_death_test";
+
+#if GTEST_HAS_DEATH_TEST
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
+// DeathTest is a class that hides much of the complexity of the
+// GTEST_DEATH_TEST_ macro.  It is abstract; its static Create method
+// returns a concrete class that depends on the prevailing death test
+// style, as defined by the --gtest_death_test_style and/or
+// --gtest_internal_run_death_test flags.
+
+// In describing the results of death tests, these terms are used with
+// the corresponding definitions:
+//
+// exit status:  The integer exit information in the format specified
+//               by wait(2)
+// exit code:    The integer code passed to exit(3), _exit(2), or
+//               returned from main()
+class GTEST_API_ DeathTest {
+ public:
+  // Create returns false if there was an error determining the
+  // appropriate action to take for the current death test; for example,
+  // if the gtest_death_test_style flag is set to an invalid value.
+  // The LastMessage method will return a more detailed message in that
+  // case.  Otherwise, the DeathTest pointer pointed to by the "test"
+  // argument is set.  If the death test should be skipped, the pointer
+  // is set to NULL; otherwise, it is set to the address of a new concrete
+  // DeathTest object that controls the execution of the current test.
+  static bool Create(const char* statement, Matcher<const std::string&> matcher,
+                     const char* file, int line, DeathTest** test);
+  DeathTest();
+  virtual ~DeathTest() { }
+
+  // A helper class that aborts a death test when it's deleted.
+  class ReturnSentinel {
+   public:
+    explicit ReturnSentinel(DeathTest* test) : test_(test) { }
+    ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); }
+   private:
+    DeathTest* const test_;
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel);
+  } GTEST_ATTRIBUTE_UNUSED_;
+
+  // An enumeration of possible roles that may be taken when a death
+  // test is encountered.  EXECUTE means that the death test logic should
+  // be executed immediately.  OVERSEE means that the program should prepare
+  // the appropriate environment for a child process to execute the death
+  // test, then wait for it to complete.
+  enum TestRole { OVERSEE_TEST, EXECUTE_TEST };
+
+  // An enumeration of the three reasons that a test might be aborted.
+  enum AbortReason {
+    TEST_ENCOUNTERED_RETURN_STATEMENT,
+    TEST_THREW_EXCEPTION,
+    TEST_DID_NOT_DIE
+  };
+
+  // Assumes one of the above roles.
+  virtual TestRole AssumeRole() = 0;
+
+  // Waits for the death test to finish and returns its status.
+  virtual int Wait() = 0;
+
+  // Returns true if the death test passed; that is, the test process
+  // exited during the test, its exit status matches a user-supplied
+  // predicate, and its stderr output matches a user-supplied regular
+  // expression.
+  // The user-supplied predicate may be a macro expression rather
+  // than a function pointer or functor, or else Wait and Passed could
+  // be combined.
+  virtual bool Passed(bool exit_status_ok) = 0;
+
+  // Signals that the death test did not die as expected.
+  virtual void Abort(AbortReason reason) = 0;
+
+  // Returns a human-readable outcome message regarding the outcome of
+  // the last death test.
+  static const char* LastMessage();
+
+  static void set_last_death_test_message(const std::string& message);
+
+ private:
+  // A string containing a description of the outcome of the last death test.
+  static std::string last_death_test_message_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest);
+};
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+// Factory interface for death tests.  May be mocked out for testing.
+class DeathTestFactory {
+ public:
+  virtual ~DeathTestFactory() { }
+  virtual bool Create(const char* statement,
+                      Matcher<const std::string&> matcher, const char* file,
+                      int line, DeathTest** test) = 0;
+};
+
+// A concrete DeathTestFactory implementation for normal use.
+class DefaultDeathTestFactory : public DeathTestFactory {
+ public:
+  bool Create(const char* statement, Matcher<const std::string&> matcher,
+              const char* file, int line, DeathTest** test) override;
+};
+
+// Returns true if exit_status describes a process that was terminated
+// by a signal, or exited normally with a nonzero exit code.
+GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
+
+// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads
+// and interpreted as a regex (rather than an Eq matcher) for legacy
+// compatibility.
+inline Matcher<const ::std::string&> MakeDeathTestMatcher(
+    ::testing::internal::RE regex) {
+  return ContainsRegex(regex.pattern());
+}
+inline Matcher<const ::std::string&> MakeDeathTestMatcher(const char* regex) {
+  return ContainsRegex(regex);
+}
+inline Matcher<const ::std::string&> MakeDeathTestMatcher(
+    const ::std::string& regex) {
+  return ContainsRegex(regex);
+}
+
+// If a Matcher<const ::std::string&> is passed to EXPECT_DEATH (etc.), it's
+// used directly.
+inline Matcher<const ::std::string&> MakeDeathTestMatcher(
+    Matcher<const ::std::string&> matcher) {
+  return matcher;
+}
+
+// Traps C++ exceptions escaping statement and reports them as test
+// failures. Note that trapping SEH exceptions is not implemented here.
+# if GTEST_HAS_EXCEPTIONS
+#  define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
+  try { \
+    GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+  } catch (const ::std::exception& gtest_exception) { \
+    fprintf(\
+        stderr, \
+        "\n%s: Caught std::exception-derived exception escaping the " \
+        "death test statement. Exception message: %s\n", \
+        ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \
+        gtest_exception.what()); \
+    fflush(stderr); \
+    death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
+  } catch (...) { \
+    death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
+  }
+
+# else
+#  define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
+  GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
+
+# endif
+
+// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*,
+// ASSERT_EXIT*, and EXPECT_EXIT*.
+#define GTEST_DEATH_TEST_(statement, predicate, regex_or_matcher, fail)        \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_                                                \
+  if (::testing::internal::AlwaysTrue()) {                                     \
+    ::testing::internal::DeathTest* gtest_dt;                                  \
+    if (!::testing::internal::DeathTest::Create(                               \
+            #statement,                                                        \
+            ::testing::internal::MakeDeathTestMatcher(regex_or_matcher),       \
+            __FILE__, __LINE__, &gtest_dt)) {                                  \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__);                        \
+    }                                                                          \
+    if (gtest_dt != nullptr) {                                                 \
+      std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \
+      switch (gtest_dt->AssumeRole()) {                                        \
+        case ::testing::internal::DeathTest::OVERSEE_TEST:                     \
+          if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) {                \
+            goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__);                  \
+          }                                                                    \
+          break;                                                               \
+        case ::testing::internal::DeathTest::EXECUTE_TEST: {                   \
+          ::testing::internal::DeathTest::ReturnSentinel gtest_sentinel(       \
+              gtest_dt);                                                       \
+          GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt);            \
+          gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE);   \
+          break;                                                               \
+        }                                                                      \
+      }                                                                        \
+    }                                                                          \
+  } else                                                                       \
+    GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__)                                \
+        : fail(::testing::internal::DeathTest::LastMessage())
+// The symbol "fail" here expands to something into which a message
+// can be streamed.
+
+// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in
+// NDEBUG mode. In this case we need the statements to be executed and the macro
+// must accept a streamed message even though the message is never printed.
+// The regex object is not evaluated, but it is used to prevent "unused"
+// warnings and to avoid an expression that doesn't compile in debug mode.
+#define GTEST_EXECUTE_STATEMENT_(statement, regex_or_matcher)    \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_                                  \
+  if (::testing::internal::AlwaysTrue()) {                       \
+    GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement);   \
+  } else if (!::testing::internal::AlwaysTrue()) {               \
+    ::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \
+  } else                                                         \
+    ::testing::Message()
+
+// A class representing the parsed contents of the
+// --gtest_internal_run_death_test flag, as it existed when
+// RUN_ALL_TESTS was called.
+class InternalRunDeathTestFlag {
+ public:
+  InternalRunDeathTestFlag(const std::string& a_file,
+                           int a_line,
+                           int an_index,
+                           int a_write_fd)
+      : file_(a_file), line_(a_line), index_(an_index),
+        write_fd_(a_write_fd) {}
+
+  ~InternalRunDeathTestFlag() {
+    if (write_fd_ >= 0)
+      posix::Close(write_fd_);
+  }
+
+  const std::string& file() const { return file_; }
+  int line() const { return line_; }
+  int index() const { return index_; }
+  int write_fd() const { return write_fd_; }
+
+ private:
+  std::string file_;
+  int line_;
+  int index_;
+  int write_fd_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag);
+};
+
+// Returns a newly created InternalRunDeathTestFlag object with fields
+// initialized from the GTEST_FLAG(internal_run_death_test) flag if
+// the flag is specified; otherwise returns NULL.
+InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag();
+
+#endif  // GTEST_HAS_DEATH_TEST
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-filepath.h b/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-filepath.h
new file mode 100644 (file)
index 0000000..43f47dc
--- /dev/null
@@ -0,0 +1,209 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Google Test filepath utilities
+//
+// This header file declares classes and functions used internally by
+// Google Test.  They are subject to change without notice.
+//
+// This file is #included in gtest/internal/gtest-internal.h.
+// Do not include this header file separately!
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
+
+#include "gtest/internal/gtest-string.h"
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
+namespace testing {
+namespace internal {
+
+// FilePath - a class for file and directory pathname manipulation which
+// handles platform-specific conventions (like the pathname separator).
+// Used for helper functions for naming files in a directory for xml output.
+// Except for Set methods, all methods are const or static, which provides an
+// "immutable value object" -- useful for peace of mind.
+// A FilePath with a value ending in a path separator ("like/this/") represents
+// a directory, otherwise it is assumed to represent a file. In either case,
+// it may or may not represent an actual file or directory in the file system.
+// Names are NOT checked for syntax correctness -- no checking for illegal
+// characters, malformed paths, etc.
+
+class GTEST_API_ FilePath {
+ public:
+  FilePath() : pathname_("") { }
+  FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { }
+
+  explicit FilePath(const std::string& pathname) : pathname_(pathname) {
+    Normalize();
+  }
+
+  FilePath& operator=(const FilePath& rhs) {
+    Set(rhs);
+    return *this;
+  }
+
+  void Set(const FilePath& rhs) {
+    pathname_ = rhs.pathname_;
+  }
+
+  const std::string& string() const { return pathname_; }
+  const char* c_str() const { return pathname_.c_str(); }
+
+  // Returns the current working directory, or "" if unsuccessful.
+  static FilePath GetCurrentDir();
+
+  // Given directory = "dir", base_name = "test", number = 0,
+  // extension = "xml", returns "dir/test.xml". If number is greater
+  // than zero (e.g., 12), returns "dir/test_12.xml".
+  // On Windows platform, uses \ as the separator rather than /.
+  static FilePath MakeFileName(const FilePath& directory,
+                               const FilePath& base_name,
+                               int number,
+                               const char* extension);
+
+  // Given directory = "dir", relative_path = "test.xml",
+  // returns "dir/test.xml".
+  // On Windows, uses \ as the separator rather than /.
+  static FilePath ConcatPaths(const FilePath& directory,
+                              const FilePath& relative_path);
+
+  // Returns a pathname for a file that does not currently exist. The pathname
+  // will be directory/base_name.extension or
+  // directory/base_name_<number>.extension if directory/base_name.extension
+  // already exists. The number will be incremented until a pathname is found
+  // that does not already exist.
+  // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
+  // There could be a race condition if two or more processes are calling this
+  // function at the same time -- they could both pick the same filename.
+  static FilePath GenerateUniqueFileName(const FilePath& directory,
+                                         const FilePath& base_name,
+                                         const char* extension);
+
+  // Returns true if and only if the path is "".
+  bool IsEmpty() const { return pathname_.empty(); }
+
+  // If input name has a trailing separator character, removes it and returns
+  // the name, otherwise return the name string unmodified.
+  // On Windows platform, uses \ as the separator, other platforms use /.
+  FilePath RemoveTrailingPathSeparator() const;
+
+  // Returns a copy of the FilePath with the directory part removed.
+  // Example: FilePath("path/to/file").RemoveDirectoryName() returns
+  // FilePath("file"). If there is no directory part ("just_a_file"), it returns
+  // the FilePath unmodified. If there is no file part ("just_a_dir/") it
+  // returns an empty FilePath ("").
+  // On Windows platform, '\' is the path separator, otherwise it is '/'.
+  FilePath RemoveDirectoryName() const;
+
+  // RemoveFileName returns the directory path with the filename removed.
+  // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
+  // If the FilePath is "a_file" or "/a_file", RemoveFileName returns
+  // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
+  // not have a file, like "just/a/dir/", it returns the FilePath unmodified.
+  // On Windows platform, '\' is the path separator, otherwise it is '/'.
+  FilePath RemoveFileName() const;
+
+  // Returns a copy of the FilePath with the case-insensitive extension removed.
+  // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
+  // FilePath("dir/file"). If a case-insensitive extension is not
+  // found, returns a copy of the original FilePath.
+  FilePath RemoveExtension(const char* extension) const;
+
+  // Creates directories so that path exists. Returns true if successful or if
+  // the directories already exist; returns false if unable to create
+  // directories for any reason. Will also return false if the FilePath does
+  // not represent a directory (that is, it doesn't end with a path separator).
+  bool CreateDirectoriesRecursively() const;
+
+  // Create the directory so that path exists. Returns true if successful or
+  // if the directory already exists; returns false if unable to create the
+  // directory for any reason, including if the parent directory does not
+  // exist. Not named "CreateDirectory" because that's a macro on Windows.
+  bool CreateFolder() const;
+
+  // Returns true if FilePath describes something in the file-system,
+  // either a file, directory, or whatever, and that something exists.
+  bool FileOrDirectoryExists() const;
+
+  // Returns true if pathname describes a directory in the file-system
+  // that exists.
+  bool DirectoryExists() const;
+
+  // Returns true if FilePath ends with a path separator, which indicates that
+  // it is intended to represent a directory. Returns false otherwise.
+  // This does NOT check that a directory (or file) actually exists.
+  bool IsDirectory() const;
+
+  // Returns true if pathname describes a root directory. (Windows has one
+  // root directory per disk drive.)
+  bool IsRootDirectory() const;
+
+  // Returns true if pathname describes an absolute path.
+  bool IsAbsolutePath() const;
+
+ private:
+  // Replaces multiple consecutive separators with a single separator.
+  // For example, "bar///foo" becomes "bar/foo". Does not eliminate other
+  // redundancies that might be in a pathname involving "." or "..".
+  //
+  // A pathname with multiple consecutive separators may occur either through
+  // user error or as a result of some scripts or APIs that generate a pathname
+  // with a trailing separator. On other platforms the same API or script
+  // may NOT generate a pathname with a trailing "/". Then elsewhere that
+  // pathname may have another "/" and pathname components added to it,
+  // without checking for the separator already being there.
+  // The script language and operating system may allow paths like "foo//bar"
+  // but some of the functions in FilePath will not handle that correctly. In
+  // particular, RemoveTrailingPathSeparator() only removes one separator, and
+  // it is called in CreateDirectoriesRecursively() assuming that it will change
+  // a pathname from directory syntax (trailing separator) to filename syntax.
+  //
+  // On Windows this method also replaces the alternate path separator '/' with
+  // the primary path separator '\\', so that for example "bar\\/\\foo" becomes
+  // "bar\\foo".
+
+  void Normalize();
+
+  // Returns a pointer to the last occurrence of a valid path separator in
+  // the FilePath. On Windows, for example, both '/' and '\' are valid path
+  // separators. Returns NULL if no path separator was found.
+  const char* FindLastPathSeparator() const;
+
+  std::string pathname_;
+};  // class FilePath
+
+}  // namespace internal
+}  // namespace testing
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-internal.h b/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-internal.h
new file mode 100644 (file)
index 0000000..d38204f
--- /dev/null
@@ -0,0 +1,1558 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// The Google C++ Testing and Mocking Framework (Google Test)
+//
+// This header file declares functions and macros used internally by
+// Google Test.  They are subject to change without notice.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
+
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_OS_LINUX
+# include <stdlib.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+# include <unistd.h>
+#endif  // GTEST_OS_LINUX
+
+#if GTEST_HAS_EXCEPTIONS
+# include <stdexcept>
+#endif
+
+#include <ctype.h>
+#include <float.h>
+#include <string.h>
+#include <cstdint>
+#include <iomanip>
+#include <limits>
+#include <map>
+#include <set>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "gtest/gtest-message.h"
+#include "gtest/internal/gtest-filepath.h"
+#include "gtest/internal/gtest-string.h"
+#include "gtest/internal/gtest-type-util.h"
+
+// Due to C++ preprocessor weirdness, we need double indirection to
+// concatenate two tokens when one of them is __LINE__.  Writing
+//
+//   foo ## __LINE__
+//
+// will result in the token foo__LINE__, instead of foo followed by
+// the current line number.  For more details, see
+// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6
+#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar)
+#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar
+
+// Stringifies its argument.
+// Work around a bug in visual studio which doesn't accept code like this:
+//
+//   #define GTEST_STRINGIFY_(name) #name
+//   #define MACRO(a, b, c) ... GTEST_STRINGIFY_(a) ...
+//   MACRO(, x, y)
+//
+// Complaining about the argument to GTEST_STRINGIFY_ being empty.
+// This is allowed by the spec.
+#define GTEST_STRINGIFY_HELPER_(name, ...) #name
+#define GTEST_STRINGIFY_(...) GTEST_STRINGIFY_HELPER_(__VA_ARGS__, )
+
+namespace proto2 {
+class MessageLite;
+}
+
+namespace testing {
+
+// Forward declarations.
+
+class AssertionResult;                 // Result of an assertion.
+class Message;                         // Represents a failure message.
+class Test;                            // Represents a test.
+class TestInfo;                        // Information about a test.
+class TestPartResult;                  // Result of a test part.
+class UnitTest;                        // A collection of test suites.
+
+template <typename T>
+::std::string PrintToString(const T& value);
+
+namespace internal {
+
+struct TraceInfo;                      // Information about a trace point.
+class TestInfoImpl;                    // Opaque implementation of TestInfo
+class UnitTestImpl;                    // Opaque implementation of UnitTest
+
+// The text used in failure messages to indicate the start of the
+// stack trace.
+GTEST_API_ extern const char kStackTraceMarker[];
+
+// An IgnoredValue object can be implicitly constructed from ANY value.
+class IgnoredValue {
+  struct Sink {};
+ public:
+  // This constructor template allows any value to be implicitly
+  // converted to IgnoredValue.  The object has no data member and
+  // doesn't try to remember anything about the argument.  We
+  // deliberately omit the 'explicit' keyword in order to allow the
+  // conversion to be implicit.
+  // Disable the conversion if T already has a magical conversion operator.
+  // Otherwise we get ambiguity.
+  template <typename T,
+            typename std::enable_if<!std::is_convertible<T, Sink>::value,
+                                    int>::type = 0>
+  IgnoredValue(const T& /* ignored */) {}  // NOLINT(runtime/explicit)
+};
+
+// Appends the user-supplied message to the Google-Test-generated message.
+GTEST_API_ std::string AppendUserMessage(
+    const std::string& gtest_msg, const Message& user_msg);
+
+#if GTEST_HAS_EXCEPTIONS
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4275 \
+/* an exported class was derived from a class that was not exported */)
+
+// This exception is thrown by (and only by) a failed Google Test
+// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions
+// are enabled).  We derive it from std::runtime_error, which is for
+// errors presumably detectable only at run time.  Since
+// std::runtime_error inherits from std::exception, many testing
+// frameworks know how to extract and print the message inside it.
+class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error {
+ public:
+  explicit GoogleTestFailureException(const TestPartResult& failure);
+};
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4275
+
+#endif  // GTEST_HAS_EXCEPTIONS
+
+namespace edit_distance {
+// Returns the optimal edits to go from 'left' to 'right'.
+// All edits cost the same, with replace having lower priority than
+// add/remove.
+// Simple implementation of the Wagner-Fischer algorithm.
+// See http://en.wikipedia.org/wiki/Wagner-Fischer_algorithm
+enum EditType { kMatch, kAdd, kRemove, kReplace };
+GTEST_API_ std::vector<EditType> CalculateOptimalEdits(
+    const std::vector<size_t>& left, const std::vector<size_t>& right);
+
+// Same as above, but the input is represented as strings.
+GTEST_API_ std::vector<EditType> CalculateOptimalEdits(
+    const std::vector<std::string>& left,
+    const std::vector<std::string>& right);
+
+// Create a diff of the input strings in Unified diff format.
+GTEST_API_ std::string CreateUnifiedDiff(const std::vector<std::string>& left,
+                                         const std::vector<std::string>& right,
+                                         size_t context = 2);
+
+}  // namespace edit_distance
+
+// Calculate the diff between 'left' and 'right' and return it in unified diff
+// format.
+// If not null, stores in 'total_line_count' the total number of lines found
+// in left + right.
+GTEST_API_ std::string DiffStrings(const std::string& left,
+                                   const std::string& right,
+                                   size_t* total_line_count);
+
+// Constructs and returns the message for an equality assertion
+// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure.
+//
+// The first four parameters are the expressions used in the assertion
+// and their values, as strings.  For example, for ASSERT_EQ(foo, bar)
+// where foo is 5 and bar is 6, we have:
+//
+//   expected_expression: "foo"
+//   actual_expression:   "bar"
+//   expected_value:      "5"
+//   actual_value:        "6"
+//
+// The ignoring_case parameter is true if and only if the assertion is a
+// *_STRCASEEQ*.  When it's true, the string " (ignoring case)" will
+// be inserted into the message.
+GTEST_API_ AssertionResult EqFailure(const char* expected_expression,
+                                     const char* actual_expression,
+                                     const std::string& expected_value,
+                                     const std::string& actual_value,
+                                     bool ignoring_case);
+
+// Constructs a failure message for Boolean assertions such as EXPECT_TRUE.
+GTEST_API_ std::string GetBoolAssertionFailureMessage(
+    const AssertionResult& assertion_result,
+    const char* expression_text,
+    const char* actual_predicate_value,
+    const char* expected_predicate_value);
+
+// This template class represents an IEEE floating-point number
+// (either single-precision or double-precision, depending on the
+// template parameters).
+//
+// The purpose of this class is to do more sophisticated number
+// comparison.  (Due to round-off error, etc, it's very unlikely that
+// two floating-points will be equal exactly.  Hence a naive
+// comparison by the == operation often doesn't work.)
+//
+// Format of IEEE floating-point:
+//
+//   The most-significant bit being the leftmost, an IEEE
+//   floating-point looks like
+//
+//     sign_bit exponent_bits fraction_bits
+//
+//   Here, sign_bit is a single bit that designates the sign of the
+//   number.
+//
+//   For float, there are 8 exponent bits and 23 fraction bits.
+//
+//   For double, there are 11 exponent bits and 52 fraction bits.
+//
+//   More details can be found at
+//   http://en.wikipedia.org/wiki/IEEE_floating-point_standard.
+//
+// Template parameter:
+//
+//   RawType: the raw floating-point type (either float or double)
+template <typename RawType>
+class FloatingPoint {
+ public:
+  // Defines the unsigned integer type that has the same size as the
+  // floating point number.
+  typedef typename TypeWithSize<sizeof(RawType)>::UInt Bits;
+
+  // Constants.
+
+  // # of bits in a number.
+  static const size_t kBitCount = 8*sizeof(RawType);
+
+  // # of fraction bits in a number.
+  static const size_t kFractionBitCount =
+    std::numeric_limits<RawType>::digits - 1;
+
+  // # of exponent bits in a number.
+  static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount;
+
+  // The mask for the sign bit.
+  static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1);
+
+  // The mask for the fraction bits.
+  static const Bits kFractionBitMask =
+    ~static_cast<Bits>(0) >> (kExponentBitCount + 1);
+
+  // The mask for the exponent bits.
+  static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask);
+
+  // How many ULP's (Units in the Last Place) we want to tolerate when
+  // comparing two numbers.  The larger the value, the more error we
+  // allow.  A 0 value means that two numbers must be exactly the same
+  // to be considered equal.
+  //
+  // The maximum error of a single floating-point operation is 0.5
+  // units in the last place.  On Intel CPU's, all floating-point
+  // calculations are done with 80-bit precision, while double has 64
+  // bits.  Therefore, 4 should be enough for ordinary use.
+  //
+  // See the following article for more details on ULP:
+  // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
+  static const uint32_t kMaxUlps = 4;
+
+  // Constructs a FloatingPoint from a raw floating-point number.
+  //
+  // On an Intel CPU, passing a non-normalized NAN (Not a Number)
+  // around may change its bits, although the new value is guaranteed
+  // to be also a NAN.  Therefore, don't expect this constructor to
+  // preserve the bits in x when x is a NAN.
+  explicit FloatingPoint(const RawType& x) { u_.value_ = x; }
+
+  // Static methods
+
+  // Reinterprets a bit pattern as a floating-point number.
+  //
+  // This function is needed to test the AlmostEquals() method.
+  static RawType ReinterpretBits(const Bits bits) {
+    FloatingPoint fp(0);
+    fp.u_.bits_ = bits;
+    return fp.u_.value_;
+  }
+
+  // Returns the floating-point number that represent positive infinity.
+  static RawType Infinity() {
+    return ReinterpretBits(kExponentBitMask);
+  }
+
+  // Returns the maximum representable finite floating-point number.
+  static RawType Max();
+
+  // Non-static methods
+
+  // Returns the bits that represents this number.
+  const Bits &bits() const { return u_.bits_; }
+
+  // Returns the exponent bits of this number.
+  Bits exponent_bits() const { return kExponentBitMask & u_.bits_; }
+
+  // Returns the fraction bits of this number.
+  Bits fraction_bits() const { return kFractionBitMask & u_.bits_; }
+
+  // Returns the sign bit of this number.
+  Bits sign_bit() const { return kSignBitMask & u_.bits_; }
+
+  // Returns true if and only if this is NAN (not a number).
+  bool is_nan() const {
+    // It's a NAN if the exponent bits are all ones and the fraction
+    // bits are not entirely zeros.
+    return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
+  }
+
+  // Returns true if and only if this number is at most kMaxUlps ULP's away
+  // from rhs.  In particular, this function:
+  //
+  //   - returns false if either number is (or both are) NAN.
+  //   - treats really large numbers as almost equal to infinity.
+  //   - thinks +0.0 and -0.0 are 0 DLP's apart.
+  bool AlmostEquals(const FloatingPoint& rhs) const {
+    // The IEEE standard says that any comparison operation involving
+    // a NAN must return false.
+    if (is_nan() || rhs.is_nan()) return false;
+
+    return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_)
+        <= kMaxUlps;
+  }
+
+ private:
+  // The data type used to store the actual floating-point number.
+  union FloatingPointUnion {
+    RawType value_;  // The raw floating-point number.
+    Bits bits_;      // The bits that represent the number.
+  };
+
+  // Converts an integer from the sign-and-magnitude representation to
+  // the biased representation.  More precisely, let N be 2 to the
+  // power of (kBitCount - 1), an integer x is represented by the
+  // unsigned number x + N.
+  //
+  // For instance,
+  //
+  //   -N + 1 (the most negative number representable using
+  //          sign-and-magnitude) is represented by 1;
+  //   0      is represented by N; and
+  //   N - 1  (the biggest number representable using
+  //          sign-and-magnitude) is represented by 2N - 1.
+  //
+  // Read http://en.wikipedia.org/wiki/Signed_number_representations
+  // for more details on signed number representations.
+  static Bits SignAndMagnitudeToBiased(const Bits &sam) {
+    if (kSignBitMask & sam) {
+      // sam represents a negative number.
+      return ~sam + 1;
+    } else {
+      // sam represents a positive number.
+      return kSignBitMask | sam;
+    }
+  }
+
+  // Given two numbers in the sign-and-magnitude representation,
+  // returns the distance between them as an unsigned number.
+  static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1,
+                                                     const Bits &sam2) {
+    const Bits biased1 = SignAndMagnitudeToBiased(sam1);
+    const Bits biased2 = SignAndMagnitudeToBiased(sam2);
+    return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
+  }
+
+  FloatingPointUnion u_;
+};
+
+// We cannot use std::numeric_limits<T>::max() as it clashes with the max()
+// macro defined by <windows.h>.
+template <>
+inline float FloatingPoint<float>::Max() { return FLT_MAX; }
+template <>
+inline double FloatingPoint<double>::Max() { return DBL_MAX; }
+
+// Typedefs the instances of the FloatingPoint template class that we
+// care to use.
+typedef FloatingPoint<float> Float;
+typedef FloatingPoint<double> Double;
+
+// In order to catch the mistake of putting tests that use different
+// test fixture classes in the same test suite, we need to assign
+// unique IDs to fixture classes and compare them.  The TypeId type is
+// used to hold such IDs.  The user should treat TypeId as an opaque
+// type: the only operation allowed on TypeId values is to compare
+// them for equality using the == operator.
+typedef const void* TypeId;
+
+template <typename T>
+class TypeIdHelper {
+ public:
+  // dummy_ must not have a const type.  Otherwise an overly eager
+  // compiler (e.g. MSVC 7.1 & 8.0) may try to merge
+  // TypeIdHelper<T>::dummy_ for different Ts as an "optimization".
+  static bool dummy_;
+};
+
+template <typename T>
+bool TypeIdHelper<T>::dummy_ = false;
+
+// GetTypeId<T>() returns the ID of type T.  Different values will be
+// returned for different types.  Calling the function twice with the
+// same type argument is guaranteed to return the same ID.
+template <typename T>
+TypeId GetTypeId() {
+  // The compiler is required to allocate a different
+  // TypeIdHelper<T>::dummy_ variable for each T used to instantiate
+  // the template.  Therefore, the address of dummy_ is guaranteed to
+  // be unique.
+  return &(TypeIdHelper<T>::dummy_);
+}
+
+// Returns the type ID of ::testing::Test.  Always call this instead
+// of GetTypeId< ::testing::Test>() to get the type ID of
+// ::testing::Test, as the latter may give the wrong result due to a
+// suspected linker bug when compiling Google Test as a Mac OS X
+// framework.
+GTEST_API_ TypeId GetTestTypeId();
+
+// Defines the abstract factory interface that creates instances
+// of a Test object.
+class TestFactoryBase {
+ public:
+  virtual ~TestFactoryBase() {}
+
+  // Creates a test instance to run. The instance is both created and destroyed
+  // within TestInfoImpl::Run()
+  virtual Test* CreateTest() = 0;
+
+ protected:
+  TestFactoryBase() {}
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase);
+};
+
+// This class provides implementation of TeastFactoryBase interface.
+// It is used in TEST and TEST_F macros.
+template <class TestClass>
+class TestFactoryImpl : public TestFactoryBase {
+ public:
+  Test* CreateTest() override { return new TestClass; }
+};
+
+#if GTEST_OS_WINDOWS
+
+// Predicate-formatters for implementing the HRESULT checking macros
+// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}
+// We pass a long instead of HRESULT to avoid causing an
+// include dependency for the HRESULT type.
+GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr,
+                                            long hr);  // NOLINT
+GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr,
+                                            long hr);  // NOLINT
+
+#endif  // GTEST_OS_WINDOWS
+
+// Types of SetUpTestSuite() and TearDownTestSuite() functions.
+using SetUpTestSuiteFunc = void (*)();
+using TearDownTestSuiteFunc = void (*)();
+
+struct CodeLocation {
+  CodeLocation(const std::string& a_file, int a_line)
+      : file(a_file), line(a_line) {}
+
+  std::string file;
+  int line;
+};
+
+//  Helper to identify which setup function for TestCase / TestSuite to call.
+//  Only one function is allowed, either TestCase or TestSute but not both.
+
+// Utility functions to help SuiteApiResolver
+using SetUpTearDownSuiteFuncType = void (*)();
+
+inline SetUpTearDownSuiteFuncType GetNotDefaultOrNull(
+    SetUpTearDownSuiteFuncType a, SetUpTearDownSuiteFuncType def) {
+  return a == def ? nullptr : a;
+}
+
+template <typename T>
+//  Note that SuiteApiResolver inherits from T because
+//  SetUpTestSuite()/TearDownTestSuite() could be protected. Ths way
+//  SuiteApiResolver can access them.
+struct SuiteApiResolver : T {
+  // testing::Test is only forward declared at this point. So we make it a
+  // dependend class for the compiler to be OK with it.
+  using Test =
+      typename std::conditional<sizeof(T) != 0, ::testing::Test, void>::type;
+
+  static SetUpTearDownSuiteFuncType GetSetUpCaseOrSuite(const char* filename,
+                                                        int line_num) {
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+    SetUpTearDownSuiteFuncType test_case_fp =
+        GetNotDefaultOrNull(&T::SetUpTestCase, &Test::SetUpTestCase);
+    SetUpTearDownSuiteFuncType test_suite_fp =
+        GetNotDefaultOrNull(&T::SetUpTestSuite, &Test::SetUpTestSuite);
+
+    GTEST_CHECK_(!test_case_fp || !test_suite_fp)
+        << "Test can not provide both SetUpTestSuite and SetUpTestCase, please "
+           "make sure there is only one present at "
+        << filename << ":" << line_num;
+
+    return test_case_fp != nullptr ? test_case_fp : test_suite_fp;
+#else
+    (void)(filename);
+    (void)(line_num);
+    return &T::SetUpTestSuite;
+#endif
+  }
+
+  static SetUpTearDownSuiteFuncType GetTearDownCaseOrSuite(const char* filename,
+                                                           int line_num) {
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+    SetUpTearDownSuiteFuncType test_case_fp =
+        GetNotDefaultOrNull(&T::TearDownTestCase, &Test::TearDownTestCase);
+    SetUpTearDownSuiteFuncType test_suite_fp =
+        GetNotDefaultOrNull(&T::TearDownTestSuite, &Test::TearDownTestSuite);
+
+    GTEST_CHECK_(!test_case_fp || !test_suite_fp)
+        << "Test can not provide both TearDownTestSuite and TearDownTestCase,"
+           " please make sure there is only one present at"
+        << filename << ":" << line_num;
+
+    return test_case_fp != nullptr ? test_case_fp : test_suite_fp;
+#else
+    (void)(filename);
+    (void)(line_num);
+    return &T::TearDownTestSuite;
+#endif
+  }
+};
+
+// Creates a new TestInfo object and registers it with Google Test;
+// returns the created object.
+//
+// Arguments:
+//
+//   test_suite_name:  name of the test suite
+//   name:             name of the test
+//   type_param:       the name of the test's type parameter, or NULL if
+//                     this is not a typed or a type-parameterized test.
+//   value_param:      text representation of the test's value parameter,
+//                     or NULL if this is not a type-parameterized test.
+//   code_location:    code location where the test is defined
+//   fixture_class_id: ID of the test fixture class
+//   set_up_tc:        pointer to the function that sets up the test suite
+//   tear_down_tc:     pointer to the function that tears down the test suite
+//   factory:          pointer to the factory that creates a test object.
+//                     The newly created TestInfo instance will assume
+//                     ownership of the factory object.
+GTEST_API_ TestInfo* MakeAndRegisterTestInfo(
+    const char* test_suite_name, const char* name, const char* type_param,
+    const char* value_param, CodeLocation code_location,
+    TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc,
+    TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory);
+
+// If *pstr starts with the given prefix, modifies *pstr to be right
+// past the prefix and returns true; otherwise leaves *pstr unchanged
+// and returns false.  None of pstr, *pstr, and prefix can be NULL.
+GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr);
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
+// State of the definition of a type-parameterized test suite.
+class GTEST_API_ TypedTestSuitePState {
+ public:
+  TypedTestSuitePState() : registered_(false) {}
+
+  // Adds the given test name to defined_test_names_ and return true
+  // if the test suite hasn't been registered; otherwise aborts the
+  // program.
+  bool AddTestName(const char* file, int line, const char* case_name,
+                   const char* test_name) {
+    if (registered_) {
+      fprintf(stderr,
+              "%s Test %s must be defined before "
+              "REGISTER_TYPED_TEST_SUITE_P(%s, ...).\n",
+              FormatFileLocation(file, line).c_str(), test_name, case_name);
+      fflush(stderr);
+      posix::Abort();
+    }
+    registered_tests_.insert(
+        ::std::make_pair(test_name, CodeLocation(file, line)));
+    return true;
+  }
+
+  bool TestExists(const std::string& test_name) const {
+    return registered_tests_.count(test_name) > 0;
+  }
+
+  const CodeLocation& GetCodeLocation(const std::string& test_name) const {
+    RegisteredTestsMap::const_iterator it = registered_tests_.find(test_name);
+    GTEST_CHECK_(it != registered_tests_.end());
+    return it->second;
+  }
+
+  // Verifies that registered_tests match the test names in
+  // defined_test_names_; returns registered_tests if successful, or
+  // aborts the program otherwise.
+  const char* VerifyRegisteredTestNames(const char* test_suite_name,
+                                        const char* file, int line,
+                                        const char* registered_tests);
+
+ private:
+  typedef ::std::map<std::string, CodeLocation> RegisteredTestsMap;
+
+  bool registered_;
+  RegisteredTestsMap registered_tests_;
+};
+
+//  Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+using TypedTestCasePState = TypedTestSuitePState;
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+// Skips to the first non-space char after the first comma in 'str';
+// returns NULL if no comma is found in 'str'.
+inline const char* SkipComma(const char* str) {
+  const char* comma = strchr(str, ',');
+  if (comma == nullptr) {
+    return nullptr;
+  }
+  while (IsSpace(*(++comma))) {}
+  return comma;
+}
+
+// Returns the prefix of 'str' before the first comma in it; returns
+// the entire string if it contains no comma.
+inline std::string GetPrefixUntilComma(const char* str) {
+  const char* comma = strchr(str, ',');
+  return comma == nullptr ? str : std::string(str, comma);
+}
+
+// Splits a given string on a given delimiter, populating a given
+// vector with the fields.
+void SplitString(const ::std::string& str, char delimiter,
+                 ::std::vector< ::std::string>* dest);
+
+// The default argument to the template below for the case when the user does
+// not provide a name generator.
+struct DefaultNameGenerator {
+  template <typename T>
+  static std::string GetName(int i) {
+    return StreamableToString(i);
+  }
+};
+
+template <typename Provided = DefaultNameGenerator>
+struct NameGeneratorSelector {
+  typedef Provided type;
+};
+
+template <typename NameGenerator>
+void GenerateNamesRecursively(internal::None, std::vector<std::string>*, int) {}
+
+template <typename NameGenerator, typename Types>
+void GenerateNamesRecursively(Types, std::vector<std::string>* result, int i) {
+  result->push_back(NameGenerator::template GetName<typename Types::Head>(i));
+  GenerateNamesRecursively<NameGenerator>(typename Types::Tail(), result,
+                                          i + 1);
+}
+
+template <typename NameGenerator, typename Types>
+std::vector<std::string> GenerateNames() {
+  std::vector<std::string> result;
+  GenerateNamesRecursively<NameGenerator>(Types(), &result, 0);
+  return result;
+}
+
+// TypeParameterizedTest<Fixture, TestSel, Types>::Register()
+// registers a list of type-parameterized tests with Google Test.  The
+// return value is insignificant - we just need to return something
+// such that we can call this function in a namespace scope.
+//
+// Implementation note: The GTEST_TEMPLATE_ macro declares a template
+// template parameter.  It's defined in gtest-type-util.h.
+template <GTEST_TEMPLATE_ Fixture, class TestSel, typename Types>
+class TypeParameterizedTest {
+ public:
+  // 'index' is the index of the test in the type list 'Types'
+  // specified in INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, TestSuite,
+  // Types).  Valid values for 'index' are [0, N - 1] where N is the
+  // length of Types.
+  static bool Register(const char* prefix, const CodeLocation& code_location,
+                       const char* case_name, const char* test_names, int index,
+                       const std::vector<std::string>& type_names =
+                           GenerateNames<DefaultNameGenerator, Types>()) {
+    typedef typename Types::Head Type;
+    typedef Fixture<Type> FixtureClass;
+    typedef typename GTEST_BIND_(TestSel, Type) TestClass;
+
+    // First, registers the first type-parameterized test in the type
+    // list.
+    MakeAndRegisterTestInfo(
+        (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name +
+         "/" + type_names[static_cast<size_t>(index)])
+            .c_str(),
+        StripTrailingSpaces(GetPrefixUntilComma(test_names)).c_str(),
+        GetTypeName<Type>().c_str(),
+        nullptr,  // No value parameter.
+        code_location, GetTypeId<FixtureClass>(),
+        SuiteApiResolver<TestClass>::GetSetUpCaseOrSuite(
+            code_location.file.c_str(), code_location.line),
+        SuiteApiResolver<TestClass>::GetTearDownCaseOrSuite(
+            code_location.file.c_str(), code_location.line),
+        new TestFactoryImpl<TestClass>);
+
+    // Next, recurses (at compile time) with the tail of the type list.
+    return TypeParameterizedTest<Fixture, TestSel,
+                                 typename Types::Tail>::Register(prefix,
+                                                                 code_location,
+                                                                 case_name,
+                                                                 test_names,
+                                                                 index + 1,
+                                                                 type_names);
+  }
+};
+
+// The base case for the compile time recursion.
+template <GTEST_TEMPLATE_ Fixture, class TestSel>
+class TypeParameterizedTest<Fixture, TestSel, internal::None> {
+ public:
+  static bool Register(const char* /*prefix*/, const CodeLocation&,
+                       const char* /*case_name*/, const char* /*test_names*/,
+                       int /*index*/,
+                       const std::vector<std::string>& =
+                           std::vector<std::string>() /*type_names*/) {
+    return true;
+  }
+};
+
+GTEST_API_ void RegisterTypeParameterizedTestSuite(const char* test_suite_name,
+                                                   CodeLocation code_location);
+GTEST_API_ void RegisterTypeParameterizedTestSuiteInstantiation(
+    const char* case_name);
+
+// TypeParameterizedTestSuite<Fixture, Tests, Types>::Register()
+// registers *all combinations* of 'Tests' and 'Types' with Google
+// Test.  The return value is insignificant - we just need to return
+// something such that we can call this function in a namespace scope.
+template <GTEST_TEMPLATE_ Fixture, typename Tests, typename Types>
+class TypeParameterizedTestSuite {
+ public:
+  static bool Register(const char* prefix, CodeLocation code_location,
+                       const TypedTestSuitePState* state, const char* case_name,
+                       const char* test_names,
+                       const std::vector<std::string>& type_names =
+                           GenerateNames<DefaultNameGenerator, Types>()) {
+    RegisterTypeParameterizedTestSuiteInstantiation(case_name);
+    std::string test_name = StripTrailingSpaces(
+        GetPrefixUntilComma(test_names));
+    if (!state->TestExists(test_name)) {
+      fprintf(stderr, "Failed to get code location for test %s.%s at %s.",
+              case_name, test_name.c_str(),
+              FormatFileLocation(code_location.file.c_str(),
+                                 code_location.line).c_str());
+      fflush(stderr);
+      posix::Abort();
+    }
+    const CodeLocation& test_location = state->GetCodeLocation(test_name);
+
+    typedef typename Tests::Head Head;
+
+    // First, register the first test in 'Test' for each type in 'Types'.
+    TypeParameterizedTest<Fixture, Head, Types>::Register(
+        prefix, test_location, case_name, test_names, 0, type_names);
+
+    // Next, recurses (at compile time) with the tail of the test list.
+    return TypeParameterizedTestSuite<Fixture, typename Tests::Tail,
+                                      Types>::Register(prefix, code_location,
+                                                       state, case_name,
+                                                       SkipComma(test_names),
+                                                       type_names);
+  }
+};
+
+// The base case for the compile time recursion.
+template <GTEST_TEMPLATE_ Fixture, typename Types>
+class TypeParameterizedTestSuite<Fixture, internal::None, Types> {
+ public:
+  static bool Register(const char* /*prefix*/, const CodeLocation&,
+                       const TypedTestSuitePState* /*state*/,
+                       const char* /*case_name*/, const char* /*test_names*/,
+                       const std::vector<std::string>& =
+                           std::vector<std::string>() /*type_names*/) {
+    return true;
+  }
+};
+
+// Returns the current OS stack trace as an std::string.
+//
+// The maximum number of stack frames to be included is specified by
+// the gtest_stack_trace_depth flag.  The skip_count parameter
+// specifies the number of top frames to be skipped, which doesn't
+// count against the number of frames to be included.
+//
+// For example, if Foo() calls Bar(), which in turn calls
+// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in
+// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't.
+GTEST_API_ std::string GetCurrentOsStackTraceExceptTop(
+    UnitTest* unit_test, int skip_count);
+
+// Helpers for suppressing warnings on unreachable code or constant
+// condition.
+
+// Always returns true.
+GTEST_API_ bool AlwaysTrue();
+
+// Always returns false.
+inline bool AlwaysFalse() { return !AlwaysTrue(); }
+
+// Helper for suppressing false warning from Clang on a const char*
+// variable declared in a conditional expression always being NULL in
+// the else branch.
+struct GTEST_API_ ConstCharPtr {
+  ConstCharPtr(const char* str) : value(str) {}
+  operator bool() const { return true; }
+  const char* value;
+};
+
+// Helper for declaring std::string within 'if' statement
+// in pre C++17 build environment.
+struct TrueWithString {
+  TrueWithString() = default;
+  explicit TrueWithString(const char* str) : value(str) {}
+  explicit TrueWithString(const std::string& str) : value(str) {}
+  explicit operator bool() const { return true; }
+  std::string value;
+};
+
+// A simple Linear Congruential Generator for generating random
+// numbers with a uniform distribution.  Unlike rand() and srand(), it
+// doesn't use global state (and therefore can't interfere with user
+// code).  Unlike rand_r(), it's portable.  An LCG isn't very random,
+// but it's good enough for our purposes.
+class GTEST_API_ Random {
+ public:
+  static const uint32_t kMaxRange = 1u << 31;
+
+  explicit Random(uint32_t seed) : state_(seed) {}
+
+  void Reseed(uint32_t seed) { state_ = seed; }
+
+  // Generates a random number from [0, range).  Crashes if 'range' is
+  // 0 or greater than kMaxRange.
+  uint32_t Generate(uint32_t range);
+
+ private:
+  uint32_t state_;
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(Random);
+};
+
+// Turns const U&, U&, const U, and U all into U.
+#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \
+  typename std::remove_const<typename std::remove_reference<T>::type>::type
+
+// HasDebugStringAndShortDebugString<T>::value is a compile-time bool constant
+// that's true if and only if T has methods DebugString() and ShortDebugString()
+// that return std::string.
+template <typename T>
+class HasDebugStringAndShortDebugString {
+ private:
+  template <typename C>
+  static auto CheckDebugString(C*) -> typename std::is_same<
+      std::string, decltype(std::declval<const C>().DebugString())>::type;
+  template <typename>
+  static std::false_type CheckDebugString(...);
+
+  template <typename C>
+  static auto CheckShortDebugString(C*) -> typename std::is_same<
+      std::string, decltype(std::declval<const C>().ShortDebugString())>::type;
+  template <typename>
+  static std::false_type CheckShortDebugString(...);
+
+  using HasDebugStringType = decltype(CheckDebugString<T>(nullptr));
+  using HasShortDebugStringType = decltype(CheckShortDebugString<T>(nullptr));
+
+ public:
+  static constexpr bool value =
+      HasDebugStringType::value && HasShortDebugStringType::value;
+};
+
+template <typename T>
+constexpr bool HasDebugStringAndShortDebugString<T>::value;
+
+// When the compiler sees expression IsContainerTest<C>(0), if C is an
+// STL-style container class, the first overload of IsContainerTest
+// will be viable (since both C::iterator* and C::const_iterator* are
+// valid types and NULL can be implicitly converted to them).  It will
+// be picked over the second overload as 'int' is a perfect match for
+// the type of argument 0.  If C::iterator or C::const_iterator is not
+// a valid type, the first overload is not viable, and the second
+// overload will be picked.  Therefore, we can determine whether C is
+// a container class by checking the type of IsContainerTest<C>(0).
+// The value of the expression is insignificant.
+//
+// In C++11 mode we check the existence of a const_iterator and that an
+// iterator is properly implemented for the container.
+//
+// For pre-C++11 that we look for both C::iterator and C::const_iterator.
+// The reason is that C++ injects the name of a class as a member of the
+// class itself (e.g. you can refer to class iterator as either
+// 'iterator' or 'iterator::iterator').  If we look for C::iterator
+// only, for example, we would mistakenly think that a class named
+// iterator is an STL container.
+//
+// Also note that the simpler approach of overloading
+// IsContainerTest(typename C::const_iterator*) and
+// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++.
+typedef int IsContainer;
+template <class C,
+          class Iterator = decltype(::std::declval<const C&>().begin()),
+          class = decltype(::std::declval<const C&>().end()),
+          class = decltype(++::std::declval<Iterator&>()),
+          class = decltype(*::std::declval<Iterator>()),
+          class = typename C::const_iterator>
+IsContainer IsContainerTest(int /* dummy */) {
+  return 0;
+}
+
+typedef char IsNotContainer;
+template <class C>
+IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; }
+
+// Trait to detect whether a type T is a hash table.
+// The heuristic used is that the type contains an inner type `hasher` and does
+// not contain an inner type `reverse_iterator`.
+// If the container is iterable in reverse, then order might actually matter.
+template <typename T>
+struct IsHashTable {
+ private:
+  template <typename U>
+  static char test(typename U::hasher*, typename U::reverse_iterator*);
+  template <typename U>
+  static int test(typename U::hasher*, ...);
+  template <typename U>
+  static char test(...);
+
+ public:
+  static const bool value = sizeof(test<T>(nullptr, nullptr)) == sizeof(int);
+};
+
+template <typename T>
+const bool IsHashTable<T>::value;
+
+template <typename C,
+          bool = sizeof(IsContainerTest<C>(0)) == sizeof(IsContainer)>
+struct IsRecursiveContainerImpl;
+
+template <typename C>
+struct IsRecursiveContainerImpl<C, false> : public std::false_type {};
+
+// Since the IsRecursiveContainerImpl depends on the IsContainerTest we need to
+// obey the same inconsistencies as the IsContainerTest, namely check if
+// something is a container is relying on only const_iterator in C++11 and
+// is relying on both const_iterator and iterator otherwise
+template <typename C>
+struct IsRecursiveContainerImpl<C, true> {
+  using value_type = decltype(*std::declval<typename C::const_iterator>());
+  using type =
+      std::is_same<typename std::remove_const<
+                       typename std::remove_reference<value_type>::type>::type,
+                   C>;
+};
+
+// IsRecursiveContainer<Type> is a unary compile-time predicate that
+// evaluates whether C is a recursive container type. A recursive container
+// type is a container type whose value_type is equal to the container type
+// itself. An example for a recursive container type is
+// boost::filesystem::path, whose iterator has a value_type that is equal to
+// boost::filesystem::path.
+template <typename C>
+struct IsRecursiveContainer : public IsRecursiveContainerImpl<C>::type {};
+
+// Utilities for native arrays.
+
+// ArrayEq() compares two k-dimensional native arrays using the
+// elements' operator==, where k can be any integer >= 0.  When k is
+// 0, ArrayEq() degenerates into comparing a single pair of values.
+
+template <typename T, typename U>
+bool ArrayEq(const T* lhs, size_t size, const U* rhs);
+
+// This generic version is used when k is 0.
+template <typename T, typename U>
+inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; }
+
+// This overload is used when k >= 1.
+template <typename T, typename U, size_t N>
+inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) {
+  return internal::ArrayEq(lhs, N, rhs);
+}
+
+// This helper reduces code bloat.  If we instead put its logic inside
+// the previous ArrayEq() function, arrays with different sizes would
+// lead to different copies of the template code.
+template <typename T, typename U>
+bool ArrayEq(const T* lhs, size_t size, const U* rhs) {
+  for (size_t i = 0; i != size; i++) {
+    if (!internal::ArrayEq(lhs[i], rhs[i]))
+      return false;
+  }
+  return true;
+}
+
+// Finds the first element in the iterator range [begin, end) that
+// equals elem.  Element may be a native array type itself.
+template <typename Iter, typename Element>
+Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) {
+  for (Iter it = begin; it != end; ++it) {
+    if (internal::ArrayEq(*it, elem))
+      return it;
+  }
+  return end;
+}
+
+// CopyArray() copies a k-dimensional native array using the elements'
+// operator=, where k can be any integer >= 0.  When k is 0,
+// CopyArray() degenerates into copying a single value.
+
+template <typename T, typename U>
+void CopyArray(const T* from, size_t size, U* to);
+
+// This generic version is used when k is 0.
+template <typename T, typename U>
+inline void CopyArray(const T& from, U* to) { *to = from; }
+
+// This overload is used when k >= 1.
+template <typename T, typename U, size_t N>
+inline void CopyArray(const T(&from)[N], U(*to)[N]) {
+  internal::CopyArray(from, N, *to);
+}
+
+// This helper reduces code bloat.  If we instead put its logic inside
+// the previous CopyArray() function, arrays with different sizes
+// would lead to different copies of the template code.
+template <typename T, typename U>
+void CopyArray(const T* from, size_t size, U* to) {
+  for (size_t i = 0; i != size; i++) {
+    internal::CopyArray(from[i], to + i);
+  }
+}
+
+// The relation between an NativeArray object (see below) and the
+// native array it represents.
+// We use 2 different structs to allow non-copyable types to be used, as long
+// as RelationToSourceReference() is passed.
+struct RelationToSourceReference {};
+struct RelationToSourceCopy {};
+
+// Adapts a native array to a read-only STL-style container.  Instead
+// of the complete STL container concept, this adaptor only implements
+// members useful for Google Mock's container matchers.  New members
+// should be added as needed.  To simplify the implementation, we only
+// support Element being a raw type (i.e. having no top-level const or
+// reference modifier).  It's the client's responsibility to satisfy
+// this requirement.  Element can be an array type itself (hence
+// multi-dimensional arrays are supported).
+template <typename Element>
+class NativeArray {
+ public:
+  // STL-style container typedefs.
+  typedef Element value_type;
+  typedef Element* iterator;
+  typedef const Element* const_iterator;
+
+  // Constructs from a native array. References the source.
+  NativeArray(const Element* array, size_t count, RelationToSourceReference) {
+    InitRef(array, count);
+  }
+
+  // Constructs from a native array. Copies the source.
+  NativeArray(const Element* array, size_t count, RelationToSourceCopy) {
+    InitCopy(array, count);
+  }
+
+  // Copy constructor.
+  NativeArray(const NativeArray& rhs) {
+    (this->*rhs.clone_)(rhs.array_, rhs.size_);
+  }
+
+  ~NativeArray() {
+    if (clone_ != &NativeArray::InitRef)
+      delete[] array_;
+  }
+
+  // STL-style container methods.
+  size_t size() const { return size_; }
+  const_iterator begin() const { return array_; }
+  const_iterator end() const { return array_ + size_; }
+  bool operator==(const NativeArray& rhs) const {
+    return size() == rhs.size() &&
+        ArrayEq(begin(), size(), rhs.begin());
+  }
+
+ private:
+  static_assert(!std::is_const<Element>::value, "Type must not be const");
+  static_assert(!std::is_reference<Element>::value,
+                "Type must not be a reference");
+
+  // Initializes this object with a copy of the input.
+  void InitCopy(const Element* array, size_t a_size) {
+    Element* const copy = new Element[a_size];
+    CopyArray(array, a_size, copy);
+    array_ = copy;
+    size_ = a_size;
+    clone_ = &NativeArray::InitCopy;
+  }
+
+  // Initializes this object with a reference of the input.
+  void InitRef(const Element* array, size_t a_size) {
+    array_ = array;
+    size_ = a_size;
+    clone_ = &NativeArray::InitRef;
+  }
+
+  const Element* array_;
+  size_t size_;
+  void (NativeArray::*clone_)(const Element*, size_t);
+};
+
+// Backport of std::index_sequence.
+template <size_t... Is>
+struct IndexSequence {
+  using type = IndexSequence;
+};
+
+// Double the IndexSequence, and one if plus_one is true.
+template <bool plus_one, typename T, size_t sizeofT>
+struct DoubleSequence;
+template <size_t... I, size_t sizeofT>
+struct DoubleSequence<true, IndexSequence<I...>, sizeofT> {
+  using type = IndexSequence<I..., (sizeofT + I)..., 2 * sizeofT>;
+};
+template <size_t... I, size_t sizeofT>
+struct DoubleSequence<false, IndexSequence<I...>, sizeofT> {
+  using type = IndexSequence<I..., (sizeofT + I)...>;
+};
+
+// Backport of std::make_index_sequence.
+// It uses O(ln(N)) instantiation depth.
+template <size_t N>
+struct MakeIndexSequenceImpl
+    : DoubleSequence<N % 2 == 1, typename MakeIndexSequenceImpl<N / 2>::type,
+                     N / 2>::type {};
+
+template <>
+struct MakeIndexSequenceImpl<0> : IndexSequence<> {};
+
+template <size_t N>
+using MakeIndexSequence = typename MakeIndexSequenceImpl<N>::type;
+
+template <typename... T>
+using IndexSequenceFor = typename MakeIndexSequence<sizeof...(T)>::type;
+
+template <size_t>
+struct Ignore {
+  Ignore(...);  // NOLINT
+};
+
+template <typename>
+struct ElemFromListImpl;
+template <size_t... I>
+struct ElemFromListImpl<IndexSequence<I...>> {
+  // We make Ignore a template to solve a problem with MSVC.
+  // A non-template Ignore would work fine with `decltype(Ignore(I))...`, but
+  // MSVC doesn't understand how to deal with that pack expansion.
+  // Use `0 * I` to have a single instantiation of Ignore.
+  template <typename R>
+  static R Apply(Ignore<0 * I>..., R (*)(), ...);
+};
+
+template <size_t N, typename... T>
+struct ElemFromList {
+  using type =
+      decltype(ElemFromListImpl<typename MakeIndexSequence<N>::type>::Apply(
+          static_cast<T (*)()>(nullptr)...));
+};
+
+struct FlatTupleConstructTag {};
+
+template <typename... T>
+class FlatTuple;
+
+template <typename Derived, size_t I>
+struct FlatTupleElemBase;
+
+template <typename... T, size_t I>
+struct FlatTupleElemBase<FlatTuple<T...>, I> {
+  using value_type = typename ElemFromList<I, T...>::type;
+  FlatTupleElemBase() = default;
+  template <typename Arg>
+  explicit FlatTupleElemBase(FlatTupleConstructTag, Arg&& t)
+      : value(std::forward<Arg>(t)) {}
+  value_type value;
+};
+
+template <typename Derived, typename Idx>
+struct FlatTupleBase;
+
+template <size_t... Idx, typename... T>
+struct FlatTupleBase<FlatTuple<T...>, IndexSequence<Idx...>>
+    : FlatTupleElemBase<FlatTuple<T...>, Idx>... {
+  using Indices = IndexSequence<Idx...>;
+  FlatTupleBase() = default;
+  template <typename... Args>
+  explicit FlatTupleBase(FlatTupleConstructTag, Args&&... args)
+      : FlatTupleElemBase<FlatTuple<T...>, Idx>(FlatTupleConstructTag{},
+                                                std::forward<Args>(args))... {}
+
+  template <size_t I>
+  const typename ElemFromList<I, T...>::type& Get() const {
+    return FlatTupleElemBase<FlatTuple<T...>, I>::value;
+  }
+
+  template <size_t I>
+  typename ElemFromList<I, T...>::type& Get() {
+    return FlatTupleElemBase<FlatTuple<T...>, I>::value;
+  }
+
+  template <typename F>
+  auto Apply(F&& f) -> decltype(std::forward<F>(f)(this->Get<Idx>()...)) {
+    return std::forward<F>(f)(Get<Idx>()...);
+  }
+
+  template <typename F>
+  auto Apply(F&& f) const -> decltype(std::forward<F>(f)(this->Get<Idx>()...)) {
+    return std::forward<F>(f)(Get<Idx>()...);
+  }
+};
+
+// Analog to std::tuple but with different tradeoffs.
+// This class minimizes the template instantiation depth, thus allowing more
+// elements than std::tuple would. std::tuple has been seen to require an
+// instantiation depth of more than 10x the number of elements in some
+// implementations.
+// FlatTuple and ElemFromList are not recursive and have a fixed depth
+// regardless of T...
+// MakeIndexSequence, on the other hand, it is recursive but with an
+// instantiation depth of O(ln(N)).
+template <typename... T>
+class FlatTuple
+    : private FlatTupleBase<FlatTuple<T...>,
+                            typename MakeIndexSequence<sizeof...(T)>::type> {
+  using Indices = typename FlatTupleBase<
+      FlatTuple<T...>, typename MakeIndexSequence<sizeof...(T)>::type>::Indices;
+
+ public:
+  FlatTuple() = default;
+  template <typename... Args>
+  explicit FlatTuple(FlatTupleConstructTag tag, Args&&... args)
+      : FlatTuple::FlatTupleBase(tag, std::forward<Args>(args)...) {}
+
+  using FlatTuple::FlatTupleBase::Apply;
+  using FlatTuple::FlatTupleBase::Get;
+};
+
+// Utility functions to be called with static_assert to induce deprecation
+// warnings.
+GTEST_INTERNAL_DEPRECATED(
+    "INSTANTIATE_TEST_CASE_P is deprecated, please use "
+    "INSTANTIATE_TEST_SUITE_P")
+constexpr bool InstantiateTestCase_P_IsDeprecated() { return true; }
+
+GTEST_INTERNAL_DEPRECATED(
+    "TYPED_TEST_CASE_P is deprecated, please use "
+    "TYPED_TEST_SUITE_P")
+constexpr bool TypedTestCase_P_IsDeprecated() { return true; }
+
+GTEST_INTERNAL_DEPRECATED(
+    "TYPED_TEST_CASE is deprecated, please use "
+    "TYPED_TEST_SUITE")
+constexpr bool TypedTestCaseIsDeprecated() { return true; }
+
+GTEST_INTERNAL_DEPRECATED(
+    "REGISTER_TYPED_TEST_CASE_P is deprecated, please use "
+    "REGISTER_TYPED_TEST_SUITE_P")
+constexpr bool RegisterTypedTestCase_P_IsDeprecated() { return true; }
+
+GTEST_INTERNAL_DEPRECATED(
+    "INSTANTIATE_TYPED_TEST_CASE_P is deprecated, please use "
+    "INSTANTIATE_TYPED_TEST_SUITE_P")
+constexpr bool InstantiateTypedTestCase_P_IsDeprecated() { return true; }
+
+}  // namespace internal
+}  // namespace testing
+
+namespace std {
+// Some standard library implementations use `struct tuple_size` and some use
+// `class tuple_size`. Clang warns about the mismatch.
+// https://reviews.llvm.org/D55466
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmismatched-tags"
+#endif
+template <typename... Ts>
+struct tuple_size<testing::internal::FlatTuple<Ts...>>
+    : std::integral_constant<size_t, sizeof...(Ts)> {};
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+}  // namespace std
+
+#define GTEST_MESSAGE_AT_(file, line, message, result_type) \
+  ::testing::internal::AssertHelper(result_type, file, line, message) \
+    = ::testing::Message()
+
+#define GTEST_MESSAGE_(message, result_type) \
+  GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type)
+
+#define GTEST_FATAL_FAILURE_(message) \
+  return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure)
+
+#define GTEST_NONFATAL_FAILURE_(message) \
+  GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure)
+
+#define GTEST_SUCCESS_(message) \
+  GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess)
+
+#define GTEST_SKIP_(message) \
+  return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSkip)
+
+// Suppress MSVC warning 4072 (unreachable code) for the code following
+// statement if it returns or throws (or doesn't return or throw in some
+// situations).
+// NOTE: The "else" is important to keep this expansion to prevent a top-level
+// "else" from attaching to our "if".
+#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \
+  if (::testing::internal::AlwaysTrue()) {                        \
+    statement;                                                    \
+  } else                     /* NOLINT */                         \
+    static_assert(true, "")  // User must have a semicolon after expansion.
+
+#if GTEST_HAS_EXCEPTIONS
+
+namespace testing {
+namespace internal {
+
+class NeverThrown {
+ public:
+  const char* what() const noexcept {
+    return "this exception should never be thrown";
+  }
+};
+
+}  // namespace internal
+}  // namespace testing
+
+#if GTEST_HAS_RTTI
+
+#define GTEST_EXCEPTION_TYPE_(e) ::testing::internal::GetTypeName(typeid(e))
+
+#else  // GTEST_HAS_RTTI
+
+#define GTEST_EXCEPTION_TYPE_(e) \
+  std::string { "an std::exception-derived error" }
+
+#endif  // GTEST_HAS_RTTI
+
+#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception)   \
+  catch (typename std::conditional<                                            \
+         std::is_same<typename std::remove_cv<typename std::remove_reference<  \
+                          expected_exception>::type>::type,                    \
+                      std::exception>::value,                                  \
+         const ::testing::internal::NeverThrown&, const std::exception&>::type \
+             e) {                                                              \
+    gtest_msg.value = "Expected: " #statement                                  \
+                      " throws an exception of type " #expected_exception      \
+                      ".\n  Actual: it throws ";                               \
+    gtest_msg.value += GTEST_EXCEPTION_TYPE_(e);                               \
+    gtest_msg.value += " with description \"";                                 \
+    gtest_msg.value += e.what();                                               \
+    gtest_msg.value += "\".";                                                  \
+    goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__);                \
+  }
+
+#else  // GTEST_HAS_EXCEPTIONS
+
+#define GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception)
+
+#endif  // GTEST_HAS_EXCEPTIONS
+
+#define GTEST_TEST_THROW_(statement, expected_exception, fail)              \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_                                             \
+  if (::testing::internal::TrueWithString gtest_msg{}) {                    \
+    bool gtest_caught_expected = false;                                     \
+    try {                                                                   \
+      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement);            \
+    } catch (expected_exception const&) {                                   \
+      gtest_caught_expected = true;                                         \
+    }                                                                       \
+    GTEST_TEST_THROW_CATCH_STD_EXCEPTION_(statement, expected_exception)    \
+    catch (...) {                                                           \
+      gtest_msg.value = "Expected: " #statement                             \
+                        " throws an exception of type " #expected_exception \
+                        ".\n  Actual: it throws a different type.";         \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__);           \
+    }                                                                       \
+    if (!gtest_caught_expected) {                                           \
+      gtest_msg.value = "Expected: " #statement                             \
+                        " throws an exception of type " #expected_exception \
+                        ".\n  Actual: it throws nothing.";                  \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__);           \
+    }                                                                       \
+  } else /*NOLINT*/                                                         \
+    GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__)                   \
+        : fail(gtest_msg.value.c_str())
+
+#if GTEST_HAS_EXCEPTIONS
+
+#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_()                \
+  catch (std::exception const& e) {                               \
+    gtest_msg.value = "it throws ";                               \
+    gtest_msg.value += GTEST_EXCEPTION_TYPE_(e);                  \
+    gtest_msg.value += " with description \"";                    \
+    gtest_msg.value += e.what();                                  \
+    gtest_msg.value += "\".";                                     \
+    goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \
+  }
+
+#else  // GTEST_HAS_EXCEPTIONS
+
+#define GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_()
+
+#endif  // GTEST_HAS_EXCEPTIONS
+
+#define GTEST_TEST_NO_THROW_(statement, fail) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (::testing::internal::TrueWithString gtest_msg{}) { \
+    try { \
+      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+    } \
+    GTEST_TEST_NO_THROW_CATCH_STD_EXCEPTION_() \
+    catch (...) { \
+      gtest_msg.value = "it throws."; \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \
+    } \
+  } else \
+    GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \
+      fail(("Expected: " #statement " doesn't throw an exception.\n" \
+            "  Actual: " + gtest_msg.value).c_str())
+
+#define GTEST_TEST_ANY_THROW_(statement, fail) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (::testing::internal::AlwaysTrue()) { \
+    bool gtest_caught_any = false; \
+    try { \
+      GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+    } \
+    catch (...) { \
+      gtest_caught_any = true; \
+    } \
+    if (!gtest_caught_any) { \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \
+    } \
+  } else \
+    GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \
+      fail("Expected: " #statement " throws an exception.\n" \
+           "  Actual: it doesn't.")
+
+
+// Implements Boolean test assertions such as EXPECT_TRUE. expression can be
+// either a boolean expression or an AssertionResult. text is a textual
+// representation of expression as it was passed into the EXPECT_TRUE.
+#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (const ::testing::AssertionResult gtest_ar_ = \
+      ::testing::AssertionResult(expression)) \
+    ; \
+  else \
+    fail(::testing::internal::GetBoolAssertionFailureMessage(\
+        gtest_ar_, text, #actual, #expected).c_str())
+
+#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \
+  GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+  if (::testing::internal::AlwaysTrue()) { \
+    ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \
+    GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
+    if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \
+      goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \
+    } \
+  } else \
+    GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \
+      fail("Expected: " #statement " doesn't generate new fatal " \
+           "failures in the current thread.\n" \
+           "  Actual: it does.")
+
+// Expands to the name of the class that implements the given test.
+#define GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
+  test_suite_name##_##test_name##_Test
+
+// Helper macro for defining tests.
+#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id)      \
+  static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1,                \
+                "test_suite_name must not be empty");                         \
+  static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1,                      \
+                "test_name must not be empty");                               \
+  class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                    \
+      : public parent_class {                                                 \
+   public:                                                                    \
+    GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default;           \
+    ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name,   \
+                                                           test_name));       \
+    GTEST_DISALLOW_MOVE_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name,   \
+                                                           test_name));       \
+                                                                              \
+   private:                                                                   \
+    void TestBody() override;                                                 \
+    static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;     \
+  };                                                                          \
+                                                                              \
+  ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name,          \
+                                                    test_name)::test_info_ =  \
+      ::testing::internal::MakeAndRegisterTestInfo(                           \
+          #test_suite_name, #test_name, nullptr, nullptr,                     \
+          ::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \
+          ::testing::internal::SuiteApiResolver<                              \
+              parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__),         \
+          ::testing::internal::SuiteApiResolver<                              \
+              parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__),      \
+          new ::testing::internal::TestFactoryImpl</* NOLINT(cppcoreguidelines-owning-memory) */ GTEST_TEST_CLASS_NAME_(    \
+              test_suite_name, test_name)>);                                  \
+  void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-param-util.h b/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-param-util.h
new file mode 100644 (file)
index 0000000..d2312c0
--- /dev/null
@@ -0,0 +1,945 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Type and function utilities for implementing parameterized tests.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
+
+#include <ctype.h>
+
+#include <cassert>
+#include <iterator>
+#include <memory>
+#include <set>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "gtest/internal/gtest-internal.h"
+#include "gtest/internal/gtest-port.h"
+#include "gtest/gtest-printers.h"
+#include "gtest/gtest-test-part.h"
+
+namespace testing {
+// Input to a parameterized test name generator, describing a test parameter.
+// Consists of the parameter value and the integer parameter index.
+template <class ParamType>
+struct TestParamInfo {
+  TestParamInfo(const ParamType& a_param, size_t an_index) :
+    param(a_param),
+    index(an_index) {}
+  ParamType param;
+  size_t index;
+};
+
+// A builtin parameterized test name generator which returns the result of
+// testing::PrintToString.
+struct PrintToStringParamName {
+  template <class ParamType>
+  std::string operator()(const TestParamInfo<ParamType>& info) const {
+    return PrintToString(info.param);
+  }
+};
+
+namespace internal {
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+// Utility Functions
+
+// Outputs a message explaining invalid registration of different
+// fixture class for the same test suite. This may happen when
+// TEST_P macro is used to define two tests with the same name
+// but in different namespaces.
+GTEST_API_ void ReportInvalidTestSuiteType(const char* test_suite_name,
+                                           CodeLocation code_location);
+
+template <typename> class ParamGeneratorInterface;
+template <typename> class ParamGenerator;
+
+// Interface for iterating over elements provided by an implementation
+// of ParamGeneratorInterface<T>.
+template <typename T>
+class ParamIteratorInterface {
+ public:
+  virtual ~ParamIteratorInterface() {}
+  // A pointer to the base generator instance.
+  // Used only for the purposes of iterator comparison
+  // to make sure that two iterators belong to the same generator.
+  virtual const ParamGeneratorInterface<T>* BaseGenerator() const = 0;
+  // Advances iterator to point to the next element
+  // provided by the generator. The caller is responsible
+  // for not calling Advance() on an iterator equal to
+  // BaseGenerator()->End().
+  virtual void Advance() = 0;
+  // Clones the iterator object. Used for implementing copy semantics
+  // of ParamIterator<T>.
+  virtual ParamIteratorInterface* Clone() const = 0;
+  // Dereferences the current iterator and provides (read-only) access
+  // to the pointed value. It is the caller's responsibility not to call
+  // Current() on an iterator equal to BaseGenerator()->End().
+  // Used for implementing ParamGenerator<T>::operator*().
+  virtual const T* Current() const = 0;
+  // Determines whether the given iterator and other point to the same
+  // element in the sequence generated by the generator.
+  // Used for implementing ParamGenerator<T>::operator==().
+  virtual bool Equals(const ParamIteratorInterface& other) const = 0;
+};
+
+// Class iterating over elements provided by an implementation of
+// ParamGeneratorInterface<T>. It wraps ParamIteratorInterface<T>
+// and implements the const forward iterator concept.
+template <typename T>
+class ParamIterator {
+ public:
+  typedef T value_type;
+  typedef const T& reference;
+  typedef ptrdiff_t difference_type;
+
+  // ParamIterator assumes ownership of the impl_ pointer.
+  ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {}
+  ParamIterator& operator=(const ParamIterator& other) {
+    if (this != &other)
+      impl_.reset(other.impl_->Clone());
+    return *this;
+  }
+
+  const T& operator*() const { return *impl_->Current(); }
+  const T* operator->() const { return impl_->Current(); }
+  // Prefix version of operator++.
+  ParamIterator& operator++() {
+    impl_->Advance();
+    return *this;
+  }
+  // Postfix version of operator++.
+  ParamIterator operator++(int /*unused*/) {
+    ParamIteratorInterface<T>* clone = impl_->Clone();
+    impl_->Advance();
+    return ParamIterator(clone);
+  }
+  bool operator==(const ParamIterator& other) const {
+    return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_);
+  }
+  bool operator!=(const ParamIterator& other) const {
+    return !(*this == other);
+  }
+
+ private:
+  friend class ParamGenerator<T>;
+  explicit ParamIterator(ParamIteratorInterface<T>* impl) : impl_(impl) {}
+  std::unique_ptr<ParamIteratorInterface<T> > impl_;
+};
+
+// ParamGeneratorInterface<T> is the binary interface to access generators
+// defined in other translation units.
+template <typename T>
+class ParamGeneratorInterface {
+ public:
+  typedef T ParamType;
+
+  virtual ~ParamGeneratorInterface() {}
+
+  // Generator interface definition
+  virtual ParamIteratorInterface<T>* Begin() const = 0;
+  virtual ParamIteratorInterface<T>* End() const = 0;
+};
+
+// Wraps ParamGeneratorInterface<T> and provides general generator syntax
+// compatible with the STL Container concept.
+// This class implements copy initialization semantics and the contained
+// ParamGeneratorInterface<T> instance is shared among all copies
+// of the original object. This is possible because that instance is immutable.
+template<typename T>
+class ParamGenerator {
+ public:
+  typedef ParamIterator<T> iterator;
+
+  explicit ParamGenerator(ParamGeneratorInterface<T>* impl) : impl_(impl) {}
+  ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {}
+
+  ParamGenerator& operator=(const ParamGenerator& other) {
+    impl_ = other.impl_;
+    return *this;
+  }
+
+  iterator begin() const { return iterator(impl_->Begin()); }
+  iterator end() const { return iterator(impl_->End()); }
+
+ private:
+  std::shared_ptr<const ParamGeneratorInterface<T> > impl_;
+};
+
+// Generates values from a range of two comparable values. Can be used to
+// generate sequences of user-defined types that implement operator+() and
+// operator<().
+// This class is used in the Range() function.
+template <typename T, typename IncrementT>
+class RangeGenerator : public ParamGeneratorInterface<T> {
+ public:
+  RangeGenerator(T begin, T end, IncrementT step)
+      : begin_(begin), end_(end),
+        step_(step), end_index_(CalculateEndIndex(begin, end, step)) {}
+  ~RangeGenerator() override {}
+
+  ParamIteratorInterface<T>* Begin() const override {
+    return new Iterator(this, begin_, 0, step_);
+  }
+  ParamIteratorInterface<T>* End() const override {
+    return new Iterator(this, end_, end_index_, step_);
+  }
+
+ private:
+  class Iterator : public ParamIteratorInterface<T> {
+   public:
+    Iterator(const ParamGeneratorInterface<T>* base, T value, int index,
+             IncrementT step)
+        : base_(base), value_(value), index_(index), step_(step) {}
+    ~Iterator() override {}
+
+    const ParamGeneratorInterface<T>* BaseGenerator() const override {
+      return base_;
+    }
+    void Advance() override {
+      value_ = static_cast<T>(value_ + step_);
+      index_++;
+    }
+    ParamIteratorInterface<T>* Clone() const override {
+      return new Iterator(*this);
+    }
+    const T* Current() const override { return &value_; }
+    bool Equals(const ParamIteratorInterface<T>& other) const override {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const int other_index =
+          CheckedDowncastToActualType<const Iterator>(&other)->index_;
+      return index_ == other_index;
+    }
+
+   private:
+    Iterator(const Iterator& other)
+        : ParamIteratorInterface<T>(),
+          base_(other.base_), value_(other.value_), index_(other.index_),
+          step_(other.step_) {}
+
+    // No implementation - assignment is unsupported.
+    void operator=(const Iterator& other);
+
+    const ParamGeneratorInterface<T>* const base_;
+    T value_;
+    int index_;
+    const IncrementT step_;
+  };  // class RangeGenerator::Iterator
+
+  static int CalculateEndIndex(const T& begin,
+                               const T& end,
+                               const IncrementT& step) {
+    int end_index = 0;
+    for (T i = begin; i < end; i = static_cast<T>(i + step))
+      end_index++;
+    return end_index;
+  }
+
+  // No implementation - assignment is unsupported.
+  void operator=(const RangeGenerator& other);
+
+  const T begin_;
+  const T end_;
+  const IncrementT step_;
+  // The index for the end() iterator. All the elements in the generated
+  // sequence are indexed (0-based) to aid iterator comparison.
+  const int end_index_;
+};  // class RangeGenerator
+
+
+// Generates values from a pair of STL-style iterators. Used in the
+// ValuesIn() function. The elements are copied from the source range
+// since the source can be located on the stack, and the generator
+// is likely to persist beyond that stack frame.
+template <typename T>
+class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface<T> {
+ public:
+  template <typename ForwardIterator>
+  ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end)
+      : container_(begin, end) {}
+  ~ValuesInIteratorRangeGenerator() override {}
+
+  ParamIteratorInterface<T>* Begin() const override {
+    return new Iterator(this, container_.begin());
+  }
+  ParamIteratorInterface<T>* End() const override {
+    return new Iterator(this, container_.end());
+  }
+
+ private:
+  typedef typename ::std::vector<T> ContainerType;
+
+  class Iterator : public ParamIteratorInterface<T> {
+   public:
+    Iterator(const ParamGeneratorInterface<T>* base,
+             typename ContainerType::const_iterator iterator)
+        : base_(base), iterator_(iterator) {}
+    ~Iterator() override {}
+
+    const ParamGeneratorInterface<T>* BaseGenerator() const override {
+      return base_;
+    }
+    void Advance() override {
+      ++iterator_;
+      value_.reset();
+    }
+    ParamIteratorInterface<T>* Clone() const override {
+      return new Iterator(*this);
+    }
+    // We need to use cached value referenced by iterator_ because *iterator_
+    // can return a temporary object (and of type other then T), so just
+    // having "return &*iterator_;" doesn't work.
+    // value_ is updated here and not in Advance() because Advance()
+    // can advance iterator_ beyond the end of the range, and we cannot
+    // detect that fact. The client code, on the other hand, is
+    // responsible for not calling Current() on an out-of-range iterator.
+    const T* Current() const override {
+      if (value_.get() == nullptr) value_.reset(new T(*iterator_));
+      return value_.get();
+    }
+    bool Equals(const ParamIteratorInterface<T>& other) const override {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      return iterator_ ==
+          CheckedDowncastToActualType<const Iterator>(&other)->iterator_;
+    }
+
+   private:
+    Iterator(const Iterator& other)
+          // The explicit constructor call suppresses a false warning
+          // emitted by gcc when supplied with the -Wextra option.
+        : ParamIteratorInterface<T>(),
+          base_(other.base_),
+          iterator_(other.iterator_) {}
+
+    const ParamGeneratorInterface<T>* const base_;
+    typename ContainerType::const_iterator iterator_;
+    // A cached value of *iterator_. We keep it here to allow access by
+    // pointer in the wrapping iterator's operator->().
+    // value_ needs to be mutable to be accessed in Current().
+    // Use of std::unique_ptr helps manage cached value's lifetime,
+    // which is bound by the lifespan of the iterator itself.
+    mutable std::unique_ptr<const T> value_;
+  };  // class ValuesInIteratorRangeGenerator::Iterator
+
+  // No implementation - assignment is unsupported.
+  void operator=(const ValuesInIteratorRangeGenerator& other);
+
+  const ContainerType container_;
+};  // class ValuesInIteratorRangeGenerator
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Default parameterized test name generator, returns a string containing the
+// integer test parameter index.
+template <class ParamType>
+std::string DefaultParamName(const TestParamInfo<ParamType>& info) {
+  Message name_stream;
+  name_stream << info.index;
+  return name_stream.GetString();
+}
+
+template <typename T = int>
+void TestNotEmpty() {
+  static_assert(sizeof(T) == 0, "Empty arguments are not allowed.");
+}
+template <typename T = int>
+void TestNotEmpty(const T&) {}
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Stores a parameter value and later creates tests parameterized with that
+// value.
+template <class TestClass>
+class ParameterizedTestFactory : public TestFactoryBase {
+ public:
+  typedef typename TestClass::ParamType ParamType;
+  explicit ParameterizedTestFactory(ParamType parameter) :
+      parameter_(parameter) {}
+  Test* CreateTest() override {
+    TestClass::SetParam(&parameter_);
+    return new TestClass();
+  }
+
+ private:
+  const ParamType parameter_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory);
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// TestMetaFactoryBase is a base class for meta-factories that create
+// test factories for passing into MakeAndRegisterTestInfo function.
+template <class ParamType>
+class TestMetaFactoryBase {
+ public:
+  virtual ~TestMetaFactoryBase() {}
+
+  virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0;
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// TestMetaFactory creates test factories for passing into
+// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives
+// ownership of test factory pointer, same factory object cannot be passed
+// into that method twice. But ParameterizedTestSuiteInfo is going to call
+// it for each Test/Parameter value combination. Thus it needs meta factory
+// creator class.
+template <class TestSuite>
+class TestMetaFactory
+    : public TestMetaFactoryBase<typename TestSuite::ParamType> {
+ public:
+  using ParamType = typename TestSuite::ParamType;
+
+  TestMetaFactory() {}
+
+  TestFactoryBase* CreateTestFactory(ParamType parameter) override {
+    return new ParameterizedTestFactory<TestSuite>(parameter);
+  }
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory);
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// ParameterizedTestSuiteInfoBase is a generic interface
+// to ParameterizedTestSuiteInfo classes. ParameterizedTestSuiteInfoBase
+// accumulates test information provided by TEST_P macro invocations
+// and generators provided by INSTANTIATE_TEST_SUITE_P macro invocations
+// and uses that information to register all resulting test instances
+// in RegisterTests method. The ParameterizeTestSuiteRegistry class holds
+// a collection of pointers to the ParameterizedTestSuiteInfo objects
+// and calls RegisterTests() on each of them when asked.
+class ParameterizedTestSuiteInfoBase {
+ public:
+  virtual ~ParameterizedTestSuiteInfoBase() {}
+
+  // Base part of test suite name for display purposes.
+  virtual const std::string& GetTestSuiteName() const = 0;
+  // Test suite id to verify identity.
+  virtual TypeId GetTestSuiteTypeId() const = 0;
+  // UnitTest class invokes this method to register tests in this
+  // test suite right before running them in RUN_ALL_TESTS macro.
+  // This method should not be called more than once on any single
+  // instance of a ParameterizedTestSuiteInfoBase derived class.
+  virtual void RegisterTests() = 0;
+
+ protected:
+  ParameterizedTestSuiteInfoBase() {}
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfoBase);
+};
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Report a the name of a test_suit as safe to ignore
+// as the side effect of construction of this type.
+struct GTEST_API_ MarkAsIgnored {
+  explicit MarkAsIgnored(const char* test_suite);
+};
+
+GTEST_API_ void InsertSyntheticTestCase(const std::string& name,
+                                        CodeLocation location, bool has_test_p);
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// ParameterizedTestSuiteInfo accumulates tests obtained from TEST_P
+// macro invocations for a particular test suite and generators
+// obtained from INSTANTIATE_TEST_SUITE_P macro invocations for that
+// test suite. It registers tests with all values generated by all
+// generators when asked.
+template <class TestSuite>
+class ParameterizedTestSuiteInfo : public ParameterizedTestSuiteInfoBase {
+ public:
+  // ParamType and GeneratorCreationFunc are private types but are required
+  // for declarations of public methods AddTestPattern() and
+  // AddTestSuiteInstantiation().
+  using ParamType = typename TestSuite::ParamType;
+  // A function that returns an instance of appropriate generator type.
+  typedef ParamGenerator<ParamType>(GeneratorCreationFunc)();
+  using ParamNameGeneratorFunc = std::string(const TestParamInfo<ParamType>&);
+
+  explicit ParameterizedTestSuiteInfo(const char* name,
+                                      CodeLocation code_location)
+      : test_suite_name_(name), code_location_(code_location) {}
+
+  // Test suite base name for display purposes.
+  const std::string& GetTestSuiteName() const override {
+    return test_suite_name_;
+  }
+  // Test suite id to verify identity.
+  TypeId GetTestSuiteTypeId() const override { return GetTypeId<TestSuite>(); }
+  // TEST_P macro uses AddTestPattern() to record information
+  // about a single test in a LocalTestInfo structure.
+  // test_suite_name is the base name of the test suite (without invocation
+  // prefix). test_base_name is the name of an individual test without
+  // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is
+  // test suite base name and DoBar is test base name.
+  void AddTestPattern(const char* test_suite_name, const char* test_base_name,
+                      TestMetaFactoryBase<ParamType>* meta_factory,
+                      CodeLocation code_location) {
+    tests_.push_back(std::shared_ptr<TestInfo>(new TestInfo(
+        test_suite_name, test_base_name, meta_factory, code_location)));
+  }
+  // INSTANTIATE_TEST_SUITE_P macro uses AddGenerator() to record information
+  // about a generator.
+  int AddTestSuiteInstantiation(const std::string& instantiation_name,
+                                GeneratorCreationFunc* func,
+                                ParamNameGeneratorFunc* name_func,
+                                const char* file, int line) {
+    instantiations_.push_back(
+        InstantiationInfo(instantiation_name, func, name_func, file, line));
+    return 0;  // Return value used only to run this method in namespace scope.
+  }
+  // UnitTest class invokes this method to register tests in this test suite
+  // right before running tests in RUN_ALL_TESTS macro.
+  // This method should not be called more than once on any single
+  // instance of a ParameterizedTestSuiteInfoBase derived class.
+  // UnitTest has a guard to prevent from calling this method more than once.
+  void RegisterTests() override {
+    bool generated_instantiations = false;
+
+    for (typename TestInfoContainer::iterator test_it = tests_.begin();
+         test_it != tests_.end(); ++test_it) {
+      std::shared_ptr<TestInfo> test_info = *test_it;
+      for (typename InstantiationContainer::iterator gen_it =
+               instantiations_.begin(); gen_it != instantiations_.end();
+               ++gen_it) {
+        const std::string& instantiation_name = gen_it->name;
+        ParamGenerator<ParamType> generator((*gen_it->generator)());
+        ParamNameGeneratorFunc* name_func = gen_it->name_func;
+        const char* file = gen_it->file;
+        int line = gen_it->line;
+
+        std::string test_suite_name;
+        if ( !instantiation_name.empty() )
+          test_suite_name = instantiation_name + "/";
+        test_suite_name += test_info->test_suite_base_name;
+
+        size_t i = 0;
+        std::set<std::string> test_param_names;
+        for (typename ParamGenerator<ParamType>::iterator param_it =
+                 generator.begin();
+             param_it != generator.end(); ++param_it, ++i) {
+          generated_instantiations = true;
+
+          Message test_name_stream;
+
+          std::string param_name = name_func(
+              TestParamInfo<ParamType>(*param_it, i));
+
+          GTEST_CHECK_(IsValidParamName(param_name))
+              << "Parameterized test name '" << param_name
+              << "' is invalid, in " << file
+              << " line " << line << std::endl;
+
+          GTEST_CHECK_(test_param_names.count(param_name) == 0)
+              << "Duplicate parameterized test name '" << param_name
+              << "', in " << file << " line " << line << std::endl;
+
+          test_param_names.insert(param_name);
+
+          if (!test_info->test_base_name.empty()) {
+            test_name_stream << test_info->test_base_name << "/";
+          }
+          test_name_stream << param_name;
+          MakeAndRegisterTestInfo(
+              test_suite_name.c_str(), test_name_stream.GetString().c_str(),
+              nullptr,  // No type parameter.
+              PrintToString(*param_it).c_str(), test_info->code_location,
+              GetTestSuiteTypeId(),
+              SuiteApiResolver<TestSuite>::GetSetUpCaseOrSuite(file, line),
+              SuiteApiResolver<TestSuite>::GetTearDownCaseOrSuite(file, line),
+              test_info->test_meta_factory->CreateTestFactory(*param_it));
+        }  // for param_it
+      }  // for gen_it
+    }  // for test_it
+
+    if (!generated_instantiations) {
+      // There are no generaotrs, or they all generate nothing ...
+      InsertSyntheticTestCase(GetTestSuiteName(), code_location_,
+                              !tests_.empty());
+    }
+  }    // RegisterTests
+
+ private:
+  // LocalTestInfo structure keeps information about a single test registered
+  // with TEST_P macro.
+  struct TestInfo {
+    TestInfo(const char* a_test_suite_base_name, const char* a_test_base_name,
+             TestMetaFactoryBase<ParamType>* a_test_meta_factory,
+             CodeLocation a_code_location)
+        : test_suite_base_name(a_test_suite_base_name),
+          test_base_name(a_test_base_name),
+          test_meta_factory(a_test_meta_factory),
+          code_location(a_code_location) {}
+
+    const std::string test_suite_base_name;
+    const std::string test_base_name;
+    const std::unique_ptr<TestMetaFactoryBase<ParamType> > test_meta_factory;
+    const CodeLocation code_location;
+  };
+  using TestInfoContainer = ::std::vector<std::shared_ptr<TestInfo> >;
+  // Records data received from INSTANTIATE_TEST_SUITE_P macros:
+  //  <Instantiation name, Sequence generator creation function,
+  //     Name generator function, Source file, Source line>
+  struct InstantiationInfo {
+      InstantiationInfo(const std::string &name_in,
+                        GeneratorCreationFunc* generator_in,
+                        ParamNameGeneratorFunc* name_func_in,
+                        const char* file_in,
+                        int line_in)
+          : name(name_in),
+            generator(generator_in),
+            name_func(name_func_in),
+            file(file_in),
+            line(line_in) {}
+
+      std::string name;
+      GeneratorCreationFunc* generator;
+      ParamNameGeneratorFunc* name_func;
+      const char* file;
+      int line;
+  };
+  typedef ::std::vector<InstantiationInfo> InstantiationContainer;
+
+  static bool IsValidParamName(const std::string& name) {
+    // Check for empty string
+    if (name.empty())
+      return false;
+
+    // Check for invalid characters
+    for (std::string::size_type index = 0; index < name.size(); ++index) {
+      if (!IsAlNum(name[index]) && name[index] != '_')
+        return false;
+    }
+
+    return true;
+  }
+
+  const std::string test_suite_name_;
+  CodeLocation code_location_;
+  TestInfoContainer tests_;
+  InstantiationContainer instantiations_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteInfo);
+};  // class ParameterizedTestSuiteInfo
+
+//  Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+template <class TestCase>
+using ParameterizedTestCaseInfo = ParameterizedTestSuiteInfo<TestCase>;
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// ParameterizedTestSuiteRegistry contains a map of
+// ParameterizedTestSuiteInfoBase classes accessed by test suite names. TEST_P
+// and INSTANTIATE_TEST_SUITE_P macros use it to locate their corresponding
+// ParameterizedTestSuiteInfo descriptors.
+class ParameterizedTestSuiteRegistry {
+ public:
+  ParameterizedTestSuiteRegistry() {}
+  ~ParameterizedTestSuiteRegistry() {
+    for (auto& test_suite_info : test_suite_infos_) {
+      delete test_suite_info;
+    }
+  }
+
+  // Looks up or creates and returns a structure containing information about
+  // tests and instantiations of a particular test suite.
+  template <class TestSuite>
+  ParameterizedTestSuiteInfo<TestSuite>* GetTestSuitePatternHolder(
+      const char* test_suite_name, CodeLocation code_location) {
+    ParameterizedTestSuiteInfo<TestSuite>* typed_test_info = nullptr;
+    for (auto& test_suite_info : test_suite_infos_) {
+      if (test_suite_info->GetTestSuiteName() == test_suite_name) {
+        if (test_suite_info->GetTestSuiteTypeId() != GetTypeId<TestSuite>()) {
+          // Complain about incorrect usage of Google Test facilities
+          // and terminate the program since we cannot guaranty correct
+          // test suite setup and tear-down in this case.
+          ReportInvalidTestSuiteType(test_suite_name, code_location);
+          posix::Abort();
+        } else {
+          // At this point we are sure that the object we found is of the same
+          // type we are looking for, so we downcast it to that type
+          // without further checks.
+          typed_test_info = CheckedDowncastToActualType<
+              ParameterizedTestSuiteInfo<TestSuite> >(test_suite_info);
+        }
+        break;
+      }
+    }
+    if (typed_test_info == nullptr) {
+      typed_test_info = new ParameterizedTestSuiteInfo<TestSuite>(
+          test_suite_name, code_location);
+      test_suite_infos_.push_back(typed_test_info);
+    }
+    return typed_test_info;
+  }
+  void RegisterTests() {
+    for (auto& test_suite_info : test_suite_infos_) {
+      test_suite_info->RegisterTests();
+    }
+  }
+//  Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  template <class TestCase>
+  ParameterizedTestCaseInfo<TestCase>* GetTestCasePatternHolder(
+      const char* test_case_name, CodeLocation code_location) {
+    return GetTestSuitePatternHolder<TestCase>(test_case_name, code_location);
+  }
+
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+ private:
+  using TestSuiteInfoContainer = ::std::vector<ParameterizedTestSuiteInfoBase*>;
+
+  TestSuiteInfoContainer test_suite_infos_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestSuiteRegistry);
+};
+
+// Keep track of what type-parameterized test suite are defined and
+// where as well as which are intatiated. This allows susequently
+// identifying suits that are defined but never used.
+class TypeParameterizedTestSuiteRegistry {
+ public:
+  // Add a suite definition
+  void RegisterTestSuite(const char* test_suite_name,
+                         CodeLocation code_location);
+
+  // Add an instantiation of a suit.
+  void RegisterInstantiation(const char* test_suite_name);
+
+  // For each suit repored as defined but not reported as instantiation,
+  // emit a test that reports that fact (configurably, as an error).
+  void CheckForInstantiations();
+
+ private:
+  struct TypeParameterizedTestSuiteInfo {
+    explicit TypeParameterizedTestSuiteInfo(CodeLocation c)
+        : code_location(c), instantiated(false) {}
+
+    CodeLocation code_location;
+    bool instantiated;
+  };
+
+  std::map<std::string, TypeParameterizedTestSuiteInfo> suites_;
+};
+
+}  // namespace internal
+
+// Forward declarations of ValuesIn(), which is implemented in
+// include/gtest/gtest-param-test.h.
+template <class Container>
+internal::ParamGenerator<typename Container::value_type> ValuesIn(
+    const Container& container);
+
+namespace internal {
+// Used in the Values() function to provide polymorphic capabilities.
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4100)
+#endif
+
+template <typename... Ts>
+class ValueArray {
+ public:
+  explicit ValueArray(Ts... v) : v_(FlatTupleConstructTag{}, std::move(v)...) {}
+
+  template <typename T>
+  operator ParamGenerator<T>() const {  // NOLINT
+    return ValuesIn(MakeVector<T>(MakeIndexSequence<sizeof...(Ts)>()));
+  }
+
+ private:
+  template <typename T, size_t... I>
+  std::vector<T> MakeVector(IndexSequence<I...>) const {
+    return std::vector<T>{static_cast<T>(v_.template Get<I>())...};
+  }
+
+  FlatTuple<Ts...> v_;
+};
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+template <typename... T>
+class CartesianProductGenerator
+    : public ParamGeneratorInterface<::std::tuple<T...>> {
+ public:
+  typedef ::std::tuple<T...> ParamType;
+
+  CartesianProductGenerator(const std::tuple<ParamGenerator<T>...>& g)
+      : generators_(g) {}
+  ~CartesianProductGenerator() override {}
+
+  ParamIteratorInterface<ParamType>* Begin() const override {
+    return new Iterator(this, generators_, false);
+  }
+  ParamIteratorInterface<ParamType>* End() const override {
+    return new Iterator(this, generators_, true);
+  }
+
+ private:
+  template <class I>
+  class IteratorImpl;
+  template <size_t... I>
+  class IteratorImpl<IndexSequence<I...>>
+      : public ParamIteratorInterface<ParamType> {
+   public:
+    IteratorImpl(const ParamGeneratorInterface<ParamType>* base,
+             const std::tuple<ParamGenerator<T>...>& generators, bool is_end)
+        : base_(base),
+          begin_(std::get<I>(generators).begin()...),
+          end_(std::get<I>(generators).end()...),
+          current_(is_end ? end_ : begin_) {
+      ComputeCurrentValue();
+    }
+    ~IteratorImpl() override {}
+
+    const ParamGeneratorInterface<ParamType>* BaseGenerator() const override {
+      return base_;
+    }
+    // Advance should not be called on beyond-of-range iterators
+    // so no component iterators must be beyond end of range, either.
+    void Advance() override {
+      assert(!AtEnd());
+      // Advance the last iterator.
+      ++std::get<sizeof...(T) - 1>(current_);
+      // if that reaches end, propagate that up.
+      AdvanceIfEnd<sizeof...(T) - 1>();
+      ComputeCurrentValue();
+    }
+    ParamIteratorInterface<ParamType>* Clone() const override {
+      return new IteratorImpl(*this);
+    }
+
+    const ParamType* Current() const override { return current_value_.get(); }
+
+    bool Equals(const ParamIteratorInterface<ParamType>& other) const override {
+      // Having the same base generator guarantees that the other
+      // iterator is of the same type and we can downcast.
+      GTEST_CHECK_(BaseGenerator() == other.BaseGenerator())
+          << "The program attempted to compare iterators "
+          << "from different generators." << std::endl;
+      const IteratorImpl* typed_other =
+          CheckedDowncastToActualType<const IteratorImpl>(&other);
+
+      // We must report iterators equal if they both point beyond their
+      // respective ranges. That can happen in a variety of fashions,
+      // so we have to consult AtEnd().
+      if (AtEnd() && typed_other->AtEnd()) return true;
+
+      bool same = true;
+      bool dummy[] = {
+          (same = same && std::get<I>(current_) ==
+                              std::get<I>(typed_other->current_))...};
+      (void)dummy;
+      return same;
+    }
+
+   private:
+    template <size_t ThisI>
+    void AdvanceIfEnd() {
+      if (std::get<ThisI>(current_) != std::get<ThisI>(end_)) return;
+
+      bool last = ThisI == 0;
+      if (last) {
+        // We are done. Nothing else to propagate.
+        return;
+      }
+
+      constexpr size_t NextI = ThisI - (ThisI != 0);
+      std::get<ThisI>(current_) = std::get<ThisI>(begin_);
+      ++std::get<NextI>(current_);
+      AdvanceIfEnd<NextI>();
+    }
+
+    void ComputeCurrentValue() {
+      if (!AtEnd())
+        current_value_ = std::make_shared<ParamType>(*std::get<I>(current_)...);
+    }
+    bool AtEnd() const {
+      bool at_end = false;
+      bool dummy[] = {
+          (at_end = at_end || std::get<I>(current_) == std::get<I>(end_))...};
+      (void)dummy;
+      return at_end;
+    }
+
+    const ParamGeneratorInterface<ParamType>* const base_;
+    std::tuple<typename ParamGenerator<T>::iterator...> begin_;
+    std::tuple<typename ParamGenerator<T>::iterator...> end_;
+    std::tuple<typename ParamGenerator<T>::iterator...> current_;
+    std::shared_ptr<ParamType> current_value_;
+  };
+
+  using Iterator = IteratorImpl<typename MakeIndexSequence<sizeof...(T)>::type>;
+
+  std::tuple<ParamGenerator<T>...> generators_;
+};
+
+template <class... Gen>
+class CartesianProductHolder {
+ public:
+  CartesianProductHolder(const Gen&... g) : generators_(g...) {}
+  template <typename... T>
+  operator ParamGenerator<::std::tuple<T...>>() const {
+    return ParamGenerator<::std::tuple<T...>>(
+        new CartesianProductGenerator<T...>(generators_));
+  }
+
+ private:
+  std::tuple<Gen...> generators_;
+};
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-port-arch.h b/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-port-arch.h
new file mode 100644 (file)
index 0000000..4dcdc89
--- /dev/null
@@ -0,0 +1,116 @@
+// Copyright 2015, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// The Google C++ Testing and Mocking Framework (Google Test)
+//
+// This header file defines the GTEST_OS_* macro.
+// It is separate from gtest-port.h so that custom/gtest-port.h can include it.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
+
+// Determines the platform on which Google Test is compiled.
+#ifdef __CYGWIN__
+# define GTEST_OS_CYGWIN 1
+# elif defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__)
+#  define GTEST_OS_WINDOWS_MINGW 1
+#  define GTEST_OS_WINDOWS 1
+#elif defined _WIN32
+# define GTEST_OS_WINDOWS 1
+# ifdef _WIN32_WCE
+#  define GTEST_OS_WINDOWS_MOBILE 1
+# elif defined(WINAPI_FAMILY)
+#  include <winapifamily.h>
+#  if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+#   define GTEST_OS_WINDOWS_DESKTOP 1
+#  elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
+#   define GTEST_OS_WINDOWS_PHONE 1
+#  elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
+#   define GTEST_OS_WINDOWS_RT 1
+#  elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE)
+#   define GTEST_OS_WINDOWS_PHONE 1
+#   define GTEST_OS_WINDOWS_TV_TITLE 1
+#  else
+    // WINAPI_FAMILY defined but no known partition matched.
+    // Default to desktop.
+#   define GTEST_OS_WINDOWS_DESKTOP 1
+#  endif
+# else
+#  define GTEST_OS_WINDOWS_DESKTOP 1
+# endif  // _WIN32_WCE
+#elif defined __OS2__
+# define GTEST_OS_OS2 1
+#elif defined __APPLE__
+# define GTEST_OS_MAC 1
+# include <TargetConditionals.h>
+# if TARGET_OS_IPHONE
+#  define GTEST_OS_IOS 1
+# endif
+#elif defined __DragonFly__
+# define GTEST_OS_DRAGONFLY 1
+#elif defined __FreeBSD__
+# define GTEST_OS_FREEBSD 1
+#elif defined __Fuchsia__
+# define GTEST_OS_FUCHSIA 1
+#elif defined(__GNU__)
+# define GTEST_OS_GNU_HURD 1
+#elif defined(__GLIBC__) && defined(__FreeBSD_kernel__)
+# define GTEST_OS_GNU_KFREEBSD 1
+#elif defined __linux__
+# define GTEST_OS_LINUX 1
+# if defined __ANDROID__
+#  define GTEST_OS_LINUX_ANDROID 1
+# endif
+#elif defined __MVS__
+# define GTEST_OS_ZOS 1
+#elif defined(__sun) && defined(__SVR4)
+# define GTEST_OS_SOLARIS 1
+#elif defined(_AIX)
+# define GTEST_OS_AIX 1
+#elif defined(__hpux)
+# define GTEST_OS_HPUX 1
+#elif defined __native_client__
+# define GTEST_OS_NACL 1
+#elif defined __NetBSD__
+# define GTEST_OS_NETBSD 1
+#elif defined __OpenBSD__
+# define GTEST_OS_OPENBSD 1
+#elif defined __QNX__
+# define GTEST_OS_QNX 1
+#elif defined(__HAIKU__)
+#define GTEST_OS_HAIKU 1
+#elif defined ESP8266
+#define GTEST_OS_ESP8266 1
+#elif defined ESP32
+#define GTEST_OS_ESP32 1
+#elif defined(__XTENSA__)
+#define GTEST_OS_XTENSA 1
+#endif  // __CYGWIN__
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-port.h b/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-port.h
new file mode 100644 (file)
index 0000000..bfa2a4b
--- /dev/null
@@ -0,0 +1,2406 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Low-level types and utilities for porting Google Test to various
+// platforms.  All macros ending with _ and symbols defined in an
+// internal namespace are subject to change without notice.  Code
+// outside Google Test MUST NOT USE THEM DIRECTLY.  Macros that don't
+// end with _ are part of Google Test's public API and can be used by
+// code outside Google Test.
+//
+// This file is fundamental to Google Test.  All other Google Test source
+// files are expected to #include this.  Therefore, it cannot #include
+// any other Google Test header.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
+
+// Environment-describing macros
+// -----------------------------
+//
+// Google Test can be used in many different environments.  Macros in
+// this section tell Google Test what kind of environment it is being
+// used in, such that Google Test can provide environment-specific
+// features and implementations.
+//
+// Google Test tries to automatically detect the properties of its
+// environment, so users usually don't need to worry about these
+// macros.  However, the automatic detection is not perfect.
+// Sometimes it's necessary for a user to define some of the following
+// macros in the build script to override Google Test's decisions.
+//
+// If the user doesn't define a macro in the list, Google Test will
+// provide a default definition.  After this header is #included, all
+// macros in this list will be defined to either 1 or 0.
+//
+// Notes to maintainers:
+//   - Each macro here is a user-tweakable knob; do not grow the list
+//     lightly.
+//   - Use #if to key off these macros.  Don't use #ifdef or "#if
+//     defined(...)", which will not work as these macros are ALWAYS
+//     defined.
+//
+//   GTEST_HAS_CLONE          - Define it to 1/0 to indicate that clone(2)
+//                              is/isn't available.
+//   GTEST_HAS_EXCEPTIONS     - Define it to 1/0 to indicate that exceptions
+//                              are enabled.
+//   GTEST_HAS_POSIX_RE       - Define it to 1/0 to indicate that POSIX regular
+//                              expressions are/aren't available.
+//   GTEST_HAS_PTHREAD        - Define it to 1/0 to indicate that <pthread.h>
+//                              is/isn't available.
+//   GTEST_HAS_RTTI           - Define it to 1/0 to indicate that RTTI is/isn't
+//                              enabled.
+//   GTEST_HAS_STD_WSTRING    - Define it to 1/0 to indicate that
+//                              std::wstring does/doesn't work (Google Test can
+//                              be used where std::wstring is unavailable).
+//   GTEST_HAS_SEH            - Define it to 1/0 to indicate whether the
+//                              compiler supports Microsoft's "Structured
+//                              Exception Handling".
+//   GTEST_HAS_STREAM_REDIRECTION
+//                            - Define it to 1/0 to indicate whether the
+//                              platform supports I/O stream redirection using
+//                              dup() and dup2().
+//   GTEST_LINKED_AS_SHARED_LIBRARY
+//                            - Define to 1 when compiling tests that use
+//                              Google Test as a shared library (known as
+//                              DLL on Windows).
+//   GTEST_CREATE_SHARED_LIBRARY
+//                            - Define to 1 when compiling Google Test itself
+//                              as a shared library.
+//   GTEST_DEFAULT_DEATH_TEST_STYLE
+//                            - The default value of --gtest_death_test_style.
+//                              The legacy default has been "fast" in the open
+//                              source version since 2008. The recommended value
+//                              is "threadsafe", and can be set in
+//                              custom/gtest-port.h.
+
+// Platform-indicating macros
+// --------------------------
+//
+// Macros indicating the platform on which Google Test is being used
+// (a macro is defined to 1 if compiled on the given platform;
+// otherwise UNDEFINED -- it's never defined to 0.).  Google Test
+// defines these macros automatically.  Code outside Google Test MUST
+// NOT define them.
+//
+//   GTEST_OS_AIX      - IBM AIX
+//   GTEST_OS_CYGWIN   - Cygwin
+//   GTEST_OS_DRAGONFLY - DragonFlyBSD
+//   GTEST_OS_FREEBSD  - FreeBSD
+//   GTEST_OS_FUCHSIA  - Fuchsia
+//   GTEST_OS_GNU_HURD - GNU/Hurd
+//   GTEST_OS_GNU_KFREEBSD - GNU/kFreeBSD
+//   GTEST_OS_HAIKU    - Haiku
+//   GTEST_OS_HPUX     - HP-UX
+//   GTEST_OS_LINUX    - Linux
+//     GTEST_OS_LINUX_ANDROID - Google Android
+//   GTEST_OS_MAC      - Mac OS X
+//     GTEST_OS_IOS    - iOS
+//   GTEST_OS_NACL     - Google Native Client (NaCl)
+//   GTEST_OS_NETBSD   - NetBSD
+//   GTEST_OS_OPENBSD  - OpenBSD
+//   GTEST_OS_OS2      - OS/2
+//   GTEST_OS_QNX      - QNX
+//   GTEST_OS_SOLARIS  - Sun Solaris
+//   GTEST_OS_WINDOWS  - Windows (Desktop, MinGW, or Mobile)
+//     GTEST_OS_WINDOWS_DESKTOP  - Windows Desktop
+//     GTEST_OS_WINDOWS_MINGW    - MinGW
+//     GTEST_OS_WINDOWS_MOBILE   - Windows Mobile
+//     GTEST_OS_WINDOWS_PHONE    - Windows Phone
+//     GTEST_OS_WINDOWS_RT       - Windows Store App/WinRT
+//   GTEST_OS_ZOS      - z/OS
+//
+// Among the platforms, Cygwin, Linux, Mac OS X, and Windows have the
+// most stable support.  Since core members of the Google Test project
+// don't have access to other platforms, support for them may be less
+// stable.  If you notice any problems on your platform, please notify
+// googletestframework@googlegroups.com (patches for fixing them are
+// even more welcome!).
+//
+// It is possible that none of the GTEST_OS_* macros are defined.
+
+// Feature-indicating macros
+// -------------------------
+//
+// Macros indicating which Google Test features are available (a macro
+// is defined to 1 if the corresponding feature is supported;
+// otherwise UNDEFINED -- it's never defined to 0.).  Google Test
+// defines these macros automatically.  Code outside Google Test MUST
+// NOT define them.
+//
+// These macros are public so that portable tests can be written.
+// Such tests typically surround code using a feature with an #if
+// which controls that code.  For example:
+//
+// #if GTEST_HAS_DEATH_TEST
+//   EXPECT_DEATH(DoSomethingDeadly());
+// #endif
+//
+//   GTEST_HAS_DEATH_TEST   - death tests
+//   GTEST_HAS_TYPED_TEST   - typed tests
+//   GTEST_HAS_TYPED_TEST_P - type-parameterized tests
+//   GTEST_IS_THREADSAFE    - Google Test is thread-safe.
+//   GTEST_USES_POSIX_RE    - enhanced POSIX regex is used. Do not confuse with
+//                            GTEST_HAS_POSIX_RE (see above) which users can
+//                            define themselves.
+//   GTEST_USES_SIMPLE_RE   - our own simple regex is used;
+//                            the above RE\b(s) are mutually exclusive.
+
+// Misc public macros
+// ------------------
+//
+//   GTEST_FLAG(flag_name)  - references the variable corresponding to
+//                            the given Google Test flag.
+
+// Internal utilities
+// ------------------
+//
+// The following macros and utilities are for Google Test's INTERNAL
+// use only.  Code outside Google Test MUST NOT USE THEM DIRECTLY.
+//
+// Macros for basic C++ coding:
+//   GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning.
+//   GTEST_ATTRIBUTE_UNUSED_  - declares that a class' instances or a
+//                              variable don't have to be used.
+//   GTEST_DISALLOW_ASSIGN_   - disables copy operator=.
+//   GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=.
+//   GTEST_DISALLOW_MOVE_ASSIGN_   - disables move operator=.
+//   GTEST_DISALLOW_MOVE_AND_ASSIGN_ - disables move ctor and operator=.
+//   GTEST_MUST_USE_RESULT_   - declares that a function's result must be used.
+//   GTEST_INTENTIONAL_CONST_COND_PUSH_ - start code section where MSVC C4127 is
+//                                        suppressed (constant conditional).
+//   GTEST_INTENTIONAL_CONST_COND_POP_  - finish code section where MSVC C4127
+//                                        is suppressed.
+//   GTEST_INTERNAL_HAS_ANY - for enabling UniversalPrinter<std::any> or
+//                            UniversalPrinter<absl::any> specializations.
+//   GTEST_INTERNAL_HAS_OPTIONAL - for enabling UniversalPrinter<std::optional>
+//   or
+//                                 UniversalPrinter<absl::optional>
+//                                 specializations.
+//   GTEST_INTERNAL_HAS_STRING_VIEW - for enabling Matcher<std::string_view> or
+//                                    Matcher<absl::string_view>
+//                                    specializations.
+//   GTEST_INTERNAL_HAS_VARIANT - for enabling UniversalPrinter<std::variant> or
+//                                UniversalPrinter<absl::variant>
+//                                specializations.
+//
+// Synchronization:
+//   Mutex, MutexLock, ThreadLocal, GetThreadCount()
+//                            - synchronization primitives.
+//
+// Regular expressions:
+//   RE             - a simple regular expression class using the POSIX
+//                    Extended Regular Expression syntax on UNIX-like platforms
+//                    or a reduced regular exception syntax on other
+//                    platforms, including Windows.
+// Logging:
+//   GTEST_LOG_()   - logs messages at the specified severity level.
+//   LogToStderr()  - directs all log messages to stderr.
+//   FlushInfoLog() - flushes informational log messages.
+//
+// Stdout and stderr capturing:
+//   CaptureStdout()     - starts capturing stdout.
+//   GetCapturedStdout() - stops capturing stdout and returns the captured
+//                         string.
+//   CaptureStderr()     - starts capturing stderr.
+//   GetCapturedStderr() - stops capturing stderr and returns the captured
+//                         string.
+//
+// Integer types:
+//   TypeWithSize   - maps an integer to a int type.
+//   TimeInMillis   - integers of known sizes.
+//   BiggestInt     - the biggest signed integer type.
+//
+// Command-line utilities:
+//   GTEST_DECLARE_*()  - declares a flag.
+//   GTEST_DEFINE_*()   - defines a flag.
+//   GetInjectableArgvs() - returns the command line as a vector of strings.
+//
+// Environment variable utilities:
+//   GetEnv()             - gets the value of an environment variable.
+//   BoolFromGTestEnv()   - parses a bool environment variable.
+//   Int32FromGTestEnv()  - parses an int32_t environment variable.
+//   StringFromGTestEnv() - parses a string environment variable.
+//
+// Deprecation warnings:
+//   GTEST_INTERNAL_DEPRECATED(message) - attribute marking a function as
+//                                        deprecated; calling a marked function
+//                                        should generate a compiler warning
+
+#include <ctype.h>   // for isspace, etc
+#include <stddef.h>  // for ptrdiff_t
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cerrno>
+#include <cstdint>
+#include <limits>
+#include <type_traits>
+
+#ifndef _WIN32_WCE
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif  // !_WIN32_WCE
+
+#if defined __APPLE__
+# include <AvailabilityMacros.h>
+# include <TargetConditionals.h>
+#endif
+
+#include <iostream>  // NOLINT
+#include <locale>
+#include <memory>
+#include <string>  // NOLINT
+#include <tuple>
+#include <vector>  // NOLINT
+
+#include "gtest/internal/custom/gtest-port.h"
+#include "gtest/internal/gtest-port-arch.h"
+
+#if !defined(GTEST_DEV_EMAIL_)
+# define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com"
+# define GTEST_FLAG_PREFIX_ "gtest_"
+# define GTEST_FLAG_PREFIX_DASH_ "gtest-"
+# define GTEST_FLAG_PREFIX_UPPER_ "GTEST_"
+# define GTEST_NAME_ "Google Test"
+# define GTEST_PROJECT_URL_ "https://github.com/google/googletest/"
+#endif  // !defined(GTEST_DEV_EMAIL_)
+
+#if !defined(GTEST_INIT_GOOGLE_TEST_NAME_)
+# define GTEST_INIT_GOOGLE_TEST_NAME_ "testing::InitGoogleTest"
+#endif  // !defined(GTEST_INIT_GOOGLE_TEST_NAME_)
+
+// Determines the version of gcc that is used to compile this.
+#ifdef __GNUC__
+// 40302 means version 4.3.2.
+# define GTEST_GCC_VER_ \
+    (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__)
+#endif  // __GNUC__
+
+// Macros for disabling Microsoft Visual C++ warnings.
+//
+//   GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 4385)
+//   /* code that triggers warnings C4800 and C4385 */
+//   GTEST_DISABLE_MSC_WARNINGS_POP_()
+#if defined(_MSC_VER)
+# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings) \
+    __pragma(warning(push))                        \
+    __pragma(warning(disable: warnings))
+# define GTEST_DISABLE_MSC_WARNINGS_POP_()          \
+    __pragma(warning(pop))
+#else
+// Not all compilers are MSVC
+# define GTEST_DISABLE_MSC_WARNINGS_PUSH_(warnings)
+# define GTEST_DISABLE_MSC_WARNINGS_POP_()
+#endif
+
+// Clang on Windows does not understand MSVC's pragma warning.
+// We need clang-specific way to disable function deprecation warning.
+#ifdef __clang__
+# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_()                         \
+    _Pragma("clang diagnostic push")                                  \
+    _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") \
+    _Pragma("clang diagnostic ignored \"-Wdeprecated-implementations\"")
+#define GTEST_DISABLE_MSC_DEPRECATED_POP_() \
+    _Pragma("clang diagnostic pop")
+#else
+# define GTEST_DISABLE_MSC_DEPRECATED_PUSH_() \
+    GTEST_DISABLE_MSC_WARNINGS_PUSH_(4996)
+# define GTEST_DISABLE_MSC_DEPRECATED_POP_() \
+    GTEST_DISABLE_MSC_WARNINGS_POP_()
+#endif
+
+// Brings in definitions for functions used in the testing::internal::posix
+// namespace (read, write, close, chdir, isatty, stat). We do not currently
+// use them on Windows Mobile.
+#if GTEST_OS_WINDOWS
+# if !GTEST_OS_WINDOWS_MOBILE
+#  include <direct.h>
+#  include <io.h>
+# endif
+// In order to avoid having to include <windows.h>, use forward declaration
+#if GTEST_OS_WINDOWS_MINGW && !defined(__MINGW64_VERSION_MAJOR)
+// MinGW defined _CRITICAL_SECTION and _RTL_CRITICAL_SECTION as two
+// separate (equivalent) structs, instead of using typedef
+typedef struct _CRITICAL_SECTION GTEST_CRITICAL_SECTION;
+#else
+// Assume CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION.
+// This assumption is verified by
+// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION.
+typedef struct _RTL_CRITICAL_SECTION GTEST_CRITICAL_SECTION;
+#endif
+#elif GTEST_OS_XTENSA
+#include <unistd.h>
+// Xtensa toolchains define strcasecmp in the string.h header instead of
+// strings.h. string.h is already included.
+#else
+// This assumes that non-Windows OSes provide unistd.h. For OSes where this
+// is not the case, we need to include headers that provide the functions
+// mentioned above.
+# include <unistd.h>
+# include <strings.h>
+#endif  // GTEST_OS_WINDOWS
+
+#if GTEST_OS_LINUX_ANDROID
+// Used to define __ANDROID_API__ matching the target NDK API level.
+#  include <android/api-level.h>  // NOLINT
+#endif
+
+// Defines this to true if and only if Google Test can use POSIX regular
+// expressions.
+#ifndef GTEST_HAS_POSIX_RE
+# if GTEST_OS_LINUX_ANDROID
+// On Android, <regex.h> is only available starting with Gingerbread.
+#  define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9)
+# else
+#define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS && !GTEST_OS_XTENSA)
+# endif
+#endif
+
+#if GTEST_USES_PCRE
+// The appropriate headers have already been included.
+
+#elif GTEST_HAS_POSIX_RE
+
+// On some platforms, <regex.h> needs someone to define size_t, and
+// won't compile otherwise.  We can #include it here as we already
+// included <stdlib.h>, which is guaranteed to define size_t through
+// <stddef.h>.
+# include <regex.h>  // NOLINT
+
+# define GTEST_USES_POSIX_RE 1
+
+#elif GTEST_OS_WINDOWS
+
+// <regex.h> is not available on Windows.  Use our own simple regex
+// implementation instead.
+# define GTEST_USES_SIMPLE_RE 1
+
+#else
+
+// <regex.h> may not be available on this platform.  Use our own
+// simple regex implementation instead.
+# define GTEST_USES_SIMPLE_RE 1
+
+#endif  // GTEST_USES_PCRE
+
+#ifndef GTEST_HAS_EXCEPTIONS
+// The user didn't tell us whether exceptions are enabled, so we need
+// to figure it out.
+# if defined(_MSC_VER) && defined(_CPPUNWIND)
+// MSVC defines _CPPUNWIND to 1 if and only if exceptions are enabled.
+#  define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__BORLANDC__)
+// C++Builder's implementation of the STL uses the _HAS_EXCEPTIONS
+// macro to enable exceptions, so we'll do the same.
+// Assumes that exceptions are enabled by default.
+#  ifndef _HAS_EXCEPTIONS
+#   define _HAS_EXCEPTIONS 1
+#  endif  // _HAS_EXCEPTIONS
+#  define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS
+# elif defined(__clang__)
+// clang defines __EXCEPTIONS if and only if exceptions are enabled before clang
+// 220714, but if and only if cleanups are enabled after that. In Obj-C++ files,
+// there can be cleanups for ObjC exceptions which also need cleanups, even if
+// C++ exceptions are disabled. clang has __has_feature(cxx_exceptions) which
+// checks for C++ exceptions starting at clang r206352, but which checked for
+// cleanups prior to that. To reliably check for C++ exception availability with
+// clang, check for
+// __EXCEPTIONS && __has_feature(cxx_exceptions).
+#  define GTEST_HAS_EXCEPTIONS (__EXCEPTIONS && __has_feature(cxx_exceptions))
+# elif defined(__GNUC__) && __EXCEPTIONS
+// gcc defines __EXCEPTIONS to 1 if and only if exceptions are enabled.
+#  define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__SUNPRO_CC)
+// Sun Pro CC supports exceptions.  However, there is no compile-time way of
+// detecting whether they are enabled or not.  Therefore, we assume that
+// they are enabled unless the user tells us otherwise.
+#  define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__IBMCPP__) && __EXCEPTIONS
+// xlC defines __EXCEPTIONS to 1 if and only if exceptions are enabled.
+#  define GTEST_HAS_EXCEPTIONS 1
+# elif defined(__HP_aCC)
+// Exception handling is in effect by default in HP aCC compiler. It has to
+// be turned of by +noeh compiler option if desired.
+#  define GTEST_HAS_EXCEPTIONS 1
+# else
+// For other compilers, we assume exceptions are disabled to be
+// conservative.
+#  define GTEST_HAS_EXCEPTIONS 0
+# endif  // defined(_MSC_VER) || defined(__BORLANDC__)
+#endif  // GTEST_HAS_EXCEPTIONS
+
+#ifndef GTEST_HAS_STD_WSTRING
+// The user didn't tell us whether ::std::wstring is available, so we need
+// to figure it out.
+// Cygwin 1.7 and below doesn't support ::std::wstring.
+// Solaris' libc++ doesn't support it either.  Android has
+// no support for it at least as recent as Froyo (2.2).
+#define GTEST_HAS_STD_WSTRING                                         \
+  (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \
+     GTEST_OS_HAIKU || GTEST_OS_ESP32 || GTEST_OS_ESP8266 || GTEST_OS_XTENSA))
+
+#endif  // GTEST_HAS_STD_WSTRING
+
+// Determines whether RTTI is available.
+#ifndef GTEST_HAS_RTTI
+// The user didn't tell us whether RTTI is enabled, so we need to
+// figure it out.
+
+# ifdef _MSC_VER
+
+#ifdef _CPPRTTI  // MSVC defines this macro if and only if RTTI is enabled.
+#   define GTEST_HAS_RTTI 1
+#  else
+#   define GTEST_HAS_RTTI 0
+#  endif
+
+// Starting with version 4.3.2, gcc defines __GXX_RTTI if and only if RTTI is
+// enabled.
+# elif defined(__GNUC__)
+
+#  ifdef __GXX_RTTI
+// When building against STLport with the Android NDK and with
+// -frtti -fno-exceptions, the build fails at link time with undefined
+// references to __cxa_bad_typeid. Note sure if STL or toolchain bug,
+// so disable RTTI when detected.
+#   if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \
+       !defined(__EXCEPTIONS)
+#    define GTEST_HAS_RTTI 0
+#   else
+#    define GTEST_HAS_RTTI 1
+#   endif  // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS
+#  else
+#   define GTEST_HAS_RTTI 0
+#  endif  // __GXX_RTTI
+
+// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends
+// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the
+// first version with C++ support.
+# elif defined(__clang__)
+
+#  define GTEST_HAS_RTTI __has_feature(cxx_rtti)
+
+// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if
+// both the typeid and dynamic_cast features are present.
+# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900)
+
+#  ifdef __RTTI_ALL__
+#   define GTEST_HAS_RTTI 1
+#  else
+#   define GTEST_HAS_RTTI 0
+#  endif
+
+# else
+
+// For all other compilers, we assume RTTI is enabled.
+#  define GTEST_HAS_RTTI 1
+
+# endif  // _MSC_VER
+
+#endif  // GTEST_HAS_RTTI
+
+// It's this header's responsibility to #include <typeinfo> when RTTI
+// is enabled.
+#if GTEST_HAS_RTTI
+# include <typeinfo>
+#endif
+
+// Determines whether Google Test can use the pthreads library.
+#ifndef GTEST_HAS_PTHREAD
+// The user didn't tell us explicitly, so we make reasonable assumptions about
+// which platforms have pthreads support.
+//
+// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0
+// to your compiler flags.
+#define GTEST_HAS_PTHREAD                                                      \
+  (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX || GTEST_OS_QNX ||          \
+   GTEST_OS_FREEBSD || GTEST_OS_NACL || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA || \
+   GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_OPENBSD ||          \
+   GTEST_OS_HAIKU || GTEST_OS_GNU_HURD)
+#endif  // GTEST_HAS_PTHREAD
+
+#if GTEST_HAS_PTHREAD
+// gtest-port.h guarantees to #include <pthread.h> when GTEST_HAS_PTHREAD is
+// true.
+# include <pthread.h>  // NOLINT
+
+// For timespec and nanosleep, used below.
+# include <time.h>  // NOLINT
+#endif
+
+// Determines whether clone(2) is supported.
+// Usually it will only be available on Linux, excluding
+// Linux on the Itanium architecture.
+// Also see http://linux.die.net/man/2/clone.
+#ifndef GTEST_HAS_CLONE
+// The user didn't tell us, so we need to figure it out.
+
+# if GTEST_OS_LINUX && !defined(__ia64__)
+#  if GTEST_OS_LINUX_ANDROID
+// On Android, clone() became available at different API levels for each 32-bit
+// architecture.
+#    if defined(__LP64__) || \
+        (defined(__arm__) && __ANDROID_API__ >= 9) || \
+        (defined(__mips__) && __ANDROID_API__ >= 12) || \
+        (defined(__i386__) && __ANDROID_API__ >= 17)
+#     define GTEST_HAS_CLONE 1
+#    else
+#     define GTEST_HAS_CLONE 0
+#    endif
+#  else
+#   define GTEST_HAS_CLONE 1
+#  endif
+# else
+#  define GTEST_HAS_CLONE 0
+# endif  // GTEST_OS_LINUX && !defined(__ia64__)
+
+#endif  // GTEST_HAS_CLONE
+
+// Determines whether to support stream redirection. This is used to test
+// output correctness and to implement death tests.
+#ifndef GTEST_HAS_STREAM_REDIRECTION
+// By default, we assume that stream redirection is supported on all
+// platforms except known mobile ones.
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \
+    GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA
+#  define GTEST_HAS_STREAM_REDIRECTION 0
+# else
+#  define GTEST_HAS_STREAM_REDIRECTION 1
+# endif  // !GTEST_OS_WINDOWS_MOBILE
+#endif  // GTEST_HAS_STREAM_REDIRECTION
+
+// Determines whether to support death tests.
+// pops up a dialog window that cannot be suppressed programmatically.
+#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS ||             \
+     (GTEST_OS_MAC && !GTEST_OS_IOS) ||                                   \
+     (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER) || GTEST_OS_WINDOWS_MINGW ||  \
+     GTEST_OS_AIX || GTEST_OS_HPUX || GTEST_OS_OPENBSD || GTEST_OS_QNX || \
+     GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_FUCHSIA ||           \
+     GTEST_OS_DRAGONFLY || GTEST_OS_GNU_KFREEBSD || GTEST_OS_HAIKU ||     \
+     GTEST_OS_GNU_HURD)
+# define GTEST_HAS_DEATH_TEST 1
+#endif
+
+// Determines whether to support type-driven tests.
+
+// Typed tests need <typeinfo> and variadic macros, which GCC, VC++ 8.0,
+// Sun Pro CC, IBM Visual Age, and HP aCC support.
+#if defined(__GNUC__) || defined(_MSC_VER) || defined(__SUNPRO_CC) || \
+    defined(__IBMCPP__) || defined(__HP_aCC)
+# define GTEST_HAS_TYPED_TEST 1
+# define GTEST_HAS_TYPED_TEST_P 1
+#endif
+
+// Determines whether the system compiler uses UTF-16 for encoding wide strings.
+#define GTEST_WIDE_STRING_USES_UTF16_ \
+  (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_AIX || GTEST_OS_OS2)
+
+// Determines whether test results can be streamed to a socket.
+#if GTEST_OS_LINUX || GTEST_OS_GNU_KFREEBSD || GTEST_OS_DRAGONFLY || \
+    GTEST_OS_FREEBSD || GTEST_OS_NETBSD || GTEST_OS_OPENBSD ||       \
+    GTEST_OS_GNU_HURD
+# define GTEST_CAN_STREAM_RESULTS_ 1
+#endif
+
+// Defines some utility macros.
+
+// The GNU compiler emits a warning if nested "if" statements are followed by
+// an "else" statement and braces are not used to explicitly disambiguate the
+// "else" binding.  This leads to problems with code like:
+//
+//   if (gate)
+//     ASSERT_*(condition) << "Some message";
+//
+// The "switch (0) case 0:" idiom is used to suppress this.
+#ifdef __INTEL_COMPILER
+# define GTEST_AMBIGUOUS_ELSE_BLOCKER_
+#else
+# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default:  // NOLINT
+#endif
+
+// Use this annotation at the end of a struct/class definition to
+// prevent the compiler from optimizing away instances that are never
+// used.  This is useful when all interesting logic happens inside the
+// c'tor and / or d'tor.  Example:
+//
+//   struct Foo {
+//     Foo() { ... }
+//   } GTEST_ATTRIBUTE_UNUSED_;
+//
+// Also use it after a variable or parameter declaration to tell the
+// compiler the variable/parameter does not have to be used.
+#if defined(__GNUC__) && !defined(COMPILER_ICC)
+# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused))
+#elif defined(__clang__)
+# if __has_attribute(unused)
+#  define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused))
+# endif
+#endif
+#ifndef GTEST_ATTRIBUTE_UNUSED_
+# define GTEST_ATTRIBUTE_UNUSED_
+#endif
+
+// Use this annotation before a function that takes a printf format string.
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(COMPILER_ICC)
+# if defined(__MINGW_PRINTF_FORMAT)
+// MinGW has two different printf implementations. Ensure the format macro
+// matches the selected implementation. See
+// https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/.
+#  define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \
+       __attribute__((__format__(__MINGW_PRINTF_FORMAT, string_index, \
+                                 first_to_check)))
+# else
+#  define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check) \
+       __attribute__((__format__(__printf__, string_index, first_to_check)))
+# endif
+#else
+# define GTEST_ATTRIBUTE_PRINTF_(string_index, first_to_check)
+#endif
+
+
+// A macro to disallow copy operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_ASSIGN_(type) \
+  type& operator=(type const &) = delete
+
+// A macro to disallow copy constructor and operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type) \
+  type(type const&) = delete;                 \
+  type& operator=(type const&) = delete
+
+// A macro to disallow move operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_MOVE_ASSIGN_(type) \
+  type& operator=(type &&) noexcept = delete
+
+// A macro to disallow move constructor and operator=
+// This should be used in the private: declarations for a class.
+#define GTEST_DISALLOW_MOVE_AND_ASSIGN_(type) \
+  type(type&&) noexcept = delete;             \
+  type& operator=(type&&) noexcept = delete
+
+// Tell the compiler to warn about unused return values for functions declared
+// with this macro.  The macro should be used on function declarations
+// following the argument list:
+//
+//   Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_;
+#if defined(__GNUC__) && !defined(COMPILER_ICC)
+# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result))
+#else
+# define GTEST_MUST_USE_RESULT_
+#endif  // __GNUC__ && !COMPILER_ICC
+
+// MS C++ compiler emits warning when a conditional expression is compile time
+// constant. In some contexts this warning is false positive and needs to be
+// suppressed. Use the following two macros in such cases:
+//
+// GTEST_INTENTIONAL_CONST_COND_PUSH_()
+// while (true) {
+// GTEST_INTENTIONAL_CONST_COND_POP_()
+// }
+# define GTEST_INTENTIONAL_CONST_COND_PUSH_() \
+    GTEST_DISABLE_MSC_WARNINGS_PUSH_(4127)
+# define GTEST_INTENTIONAL_CONST_COND_POP_() \
+    GTEST_DISABLE_MSC_WARNINGS_POP_()
+
+// Determine whether the compiler supports Microsoft's Structured Exception
+// Handling.  This is supported by several Windows compilers but generally
+// does not exist on any other system.
+#ifndef GTEST_HAS_SEH
+// The user didn't tell us, so we need to figure it out.
+
+# if defined(_MSC_VER) || defined(__BORLANDC__)
+// These two compilers are known to support SEH.
+#  define GTEST_HAS_SEH 1
+# else
+// Assume no SEH.
+#  define GTEST_HAS_SEH 0
+# endif
+
+#endif  // GTEST_HAS_SEH
+
+#ifndef GTEST_IS_THREADSAFE
+
+#define GTEST_IS_THREADSAFE                                                 \
+  (GTEST_HAS_MUTEX_AND_THREAD_LOCAL_ ||                                     \
+   (GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT) || \
+   GTEST_HAS_PTHREAD)
+
+#endif  // GTEST_IS_THREADSAFE
+
+// GTEST_API_ qualifies all symbols that must be exported. The definitions below
+// are guarded by #ifndef to give embedders a chance to define GTEST_API_ in
+// gtest/internal/custom/gtest-port.h
+#ifndef GTEST_API_
+
+#ifdef _MSC_VER
+# if GTEST_LINKED_AS_SHARED_LIBRARY
+#  define GTEST_API_ __declspec(dllimport)
+# elif GTEST_CREATE_SHARED_LIBRARY
+#  define GTEST_API_ __declspec(dllexport)
+# endif
+#elif __GNUC__ >= 4 || defined(__clang__)
+# define GTEST_API_ __attribute__((visibility ("default")))
+#endif  // _MSC_VER
+
+#endif  // GTEST_API_
+
+#ifndef GTEST_API_
+# define GTEST_API_
+#endif  // GTEST_API_
+
+#ifndef GTEST_DEFAULT_DEATH_TEST_STYLE
+# define GTEST_DEFAULT_DEATH_TEST_STYLE  "fast"
+#endif  // GTEST_DEFAULT_DEATH_TEST_STYLE
+
+#ifdef __GNUC__
+// Ask the compiler to never inline a given function.
+# define GTEST_NO_INLINE_ __attribute__((noinline))
+#else
+# define GTEST_NO_INLINE_
+#endif
+
+// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project.
+#if !defined(GTEST_HAS_CXXABI_H_)
+# if defined(__GLIBCXX__) || (defined(_LIBCPP_VERSION) && !defined(_MSC_VER))
+#  define GTEST_HAS_CXXABI_H_ 1
+# else
+#  define GTEST_HAS_CXXABI_H_ 0
+# endif
+#endif
+
+// A function level attribute to disable checking for use of uninitialized
+// memory when built with MemorySanitizer.
+#if defined(__clang__)
+# if __has_feature(memory_sanitizer)
+#  define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ \
+       __attribute__((no_sanitize_memory))
+# else
+#  define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
+# endif  // __has_feature(memory_sanitizer)
+#else
+# define GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
+#endif  // __clang__
+
+// A function level attribute to disable AddressSanitizer instrumentation.
+#if defined(__clang__)
+# if __has_feature(address_sanitizer)
+#  define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_ \
+       __attribute__((no_sanitize_address))
+# else
+#  define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
+# endif  // __has_feature(address_sanitizer)
+#else
+# define GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
+#endif  // __clang__
+
+// A function level attribute to disable HWAddressSanitizer instrumentation.
+#if defined(__clang__)
+# if __has_feature(hwaddress_sanitizer)
+#  define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_ \
+       __attribute__((no_sanitize("hwaddress")))
+# else
+#  define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
+# endif  // __has_feature(hwaddress_sanitizer)
+#else
+# define GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
+#endif  // __clang__
+
+// A function level attribute to disable ThreadSanitizer instrumentation.
+#if defined(__clang__)
+# if __has_feature(thread_sanitizer)
+#  define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ \
+       __attribute__((no_sanitize_thread))
+# else
+#  define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
+# endif  // __has_feature(thread_sanitizer)
+#else
+# define GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
+#endif  // __clang__
+
+namespace testing {
+
+class Message;
+
+// Legacy imports for backwards compatibility.
+// New code should use std:: names directly.
+using std::get;
+using std::make_tuple;
+using std::tuple;
+using std::tuple_element;
+using std::tuple_size;
+
+namespace internal {
+
+// A secret type that Google Test users don't know about.  It has no
+// definition on purpose.  Therefore it's impossible to create a
+// Secret object, which is what we want.
+class Secret;
+
+// The GTEST_COMPILE_ASSERT_ is a legacy macro used to verify that a compile
+// time expression is true (in new code, use static_assert instead). For
+// example, you could use it to verify the size of a static array:
+//
+//   GTEST_COMPILE_ASSERT_(GTEST_ARRAY_SIZE_(names) == NUM_NAMES,
+//                         names_incorrect_size);
+//
+// The second argument to the macro must be a valid C++ identifier. If the
+// expression is false, compiler will issue an error containing this identifier.
+#define GTEST_COMPILE_ASSERT_(expr, msg) static_assert(expr, #msg)
+
+// A helper for suppressing warnings on constant condition.  It just
+// returns 'condition'.
+GTEST_API_ bool IsTrue(bool condition);
+
+// Defines RE.
+
+#if GTEST_USES_PCRE
+// if used, PCRE is injected by custom/gtest-port.h
+#elif GTEST_USES_POSIX_RE || GTEST_USES_SIMPLE_RE
+
+// A simple C++ wrapper for <regex.h>.  It uses the POSIX Extended
+// Regular Expression syntax.
+class GTEST_API_ RE {
+ public:
+  // A copy constructor is required by the Standard to initialize object
+  // references from r-values.
+  RE(const RE& other) { Init(other.pattern()); }
+
+  // Constructs an RE from a string.
+  RE(const ::std::string& regex) { Init(regex.c_str()); }  // NOLINT
+
+  RE(const char* regex) { Init(regex); }  // NOLINT
+  ~RE();
+
+  // Returns the string representation of the regex.
+  const char* pattern() const { return pattern_; }
+
+  // FullMatch(str, re) returns true if and only if regular expression re
+  // matches the entire str.
+  // PartialMatch(str, re) returns true if and only if regular expression re
+  // matches a substring of str (including str itself).
+  static bool FullMatch(const ::std::string& str, const RE& re) {
+    return FullMatch(str.c_str(), re);
+  }
+  static bool PartialMatch(const ::std::string& str, const RE& re) {
+    return PartialMatch(str.c_str(), re);
+  }
+
+  static bool FullMatch(const char* str, const RE& re);
+  static bool PartialMatch(const char* str, const RE& re);
+
+ private:
+  void Init(const char* regex);
+  const char* pattern_;
+  bool is_valid_;
+
+# if GTEST_USES_POSIX_RE
+
+  regex_t full_regex_;     // For FullMatch().
+  regex_t partial_regex_;  // For PartialMatch().
+
+# else  // GTEST_USES_SIMPLE_RE
+
+  const char* full_pattern_;  // For FullMatch();
+
+# endif
+};
+
+#endif  // GTEST_USES_PCRE
+
+// Formats a source file path and a line number as they would appear
+// in an error message from the compiler used to compile this code.
+GTEST_API_ ::std::string FormatFileLocation(const char* file, int line);
+
+// Formats a file location for compiler-independent XML output.
+// Although this function is not platform dependent, we put it next to
+// FormatFileLocation in order to contrast the two functions.
+GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file,
+                                                               int line);
+
+// Defines logging utilities:
+//   GTEST_LOG_(severity) - logs messages at the specified severity level. The
+//                          message itself is streamed into the macro.
+//   LogToStderr()  - directs all log messages to stderr.
+//   FlushInfoLog() - flushes informational log messages.
+
+enum GTestLogSeverity {
+  GTEST_INFO,
+  GTEST_WARNING,
+  GTEST_ERROR,
+  GTEST_FATAL
+};
+
+// Formats log entry severity, provides a stream object for streaming the
+// log message, and terminates the message with a newline when going out of
+// scope.
+class GTEST_API_ GTestLog {
+ public:
+  GTestLog(GTestLogSeverity severity, const char* file, int line);
+
+  // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program.
+  ~GTestLog();
+
+  ::std::ostream& GetStream() { return ::std::cerr; }
+
+ private:
+  const GTestLogSeverity severity_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog);
+};
+
+#if !defined(GTEST_LOG_)
+
+# define GTEST_LOG_(severity) \
+    ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \
+                                  __FILE__, __LINE__).GetStream()
+
+inline void LogToStderr() {}
+inline void FlushInfoLog() { fflush(nullptr); }
+
+#endif  // !defined(GTEST_LOG_)
+
+#if !defined(GTEST_CHECK_)
+// INTERNAL IMPLEMENTATION - DO NOT USE.
+//
+// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition
+// is not satisfied.
+//  Synopsys:
+//    GTEST_CHECK_(boolean_condition);
+//     or
+//    GTEST_CHECK_(boolean_condition) << "Additional message";
+//
+//    This checks the condition and if the condition is not satisfied
+//    it prints message about the condition violation, including the
+//    condition itself, plus additional message streamed into it, if any,
+//    and then it aborts the program. It aborts the program irrespective of
+//    whether it is built in the debug mode or not.
+# define GTEST_CHECK_(condition) \
+    GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
+    if (::testing::internal::IsTrue(condition)) \
+      ; \
+    else \
+      GTEST_LOG_(FATAL) << "Condition " #condition " failed. "
+#endif  // !defined(GTEST_CHECK_)
+
+// An all-mode assert to verify that the given POSIX-style function
+// call returns 0 (indicating success).  Known limitation: this
+// doesn't expand to a balanced 'if' statement, so enclose the macro
+// in {} if you need to use it as the only statement in an 'if'
+// branch.
+#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \
+  if (const int gtest_error = (posix_call)) \
+    GTEST_LOG_(FATAL) << #posix_call << "failed with error " \
+                      << gtest_error
+
+// Transforms "T" into "const T&" according to standard reference collapsing
+// rules (this is only needed as a backport for C++98 compilers that do not
+// support reference collapsing). Specifically, it transforms:
+//
+//   char         ==> const char&
+//   const char   ==> const char&
+//   char&        ==> char&
+//   const char&  ==> const char&
+//
+// Note that the non-const reference will not have "const" added. This is
+// standard, and necessary so that "T" can always bind to "const T&".
+template <typename T>
+struct ConstRef { typedef const T& type; };
+template <typename T>
+struct ConstRef<T&> { typedef T& type; };
+
+// The argument T must depend on some template parameters.
+#define GTEST_REFERENCE_TO_CONST_(T) \
+  typename ::testing::internal::ConstRef<T>::type
+
+// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
+//
+// Use ImplicitCast_ as a safe version of static_cast for upcasting in
+// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a
+// const Foo*).  When you use ImplicitCast_, the compiler checks that
+// the cast is safe.  Such explicit ImplicitCast_s are necessary in
+// surprisingly many situations where C++ demands an exact type match
+// instead of an argument type convertable to a target type.
+//
+// The syntax for using ImplicitCast_ is the same as for static_cast:
+//
+//   ImplicitCast_<ToType>(expr)
+//
+// ImplicitCast_ would have been part of the C++ standard library,
+// but the proposal was submitted too late.  It will probably make
+// its way into the language in the future.
+//
+// This relatively ugly name is intentional. It prevents clashes with
+// similar functions users may have (e.g., implicit_cast). The internal
+// namespace alone is not enough because the function can be found by ADL.
+template<typename To>
+inline To ImplicitCast_(To x) { return x; }
+
+// When you upcast (that is, cast a pointer from type Foo to type
+// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts
+// always succeed.  When you downcast (that is, cast a pointer from
+// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
+// how do you know the pointer is really of type SubclassOfFoo?  It
+// could be a bare Foo, or of type DifferentSubclassOfFoo.  Thus,
+// when you downcast, you should use this macro.  In debug mode, we
+// use dynamic_cast<> to double-check the downcast is legal (we die
+// if it's not).  In normal mode, we do the efficient static_cast<>
+// instead.  Thus, it's important to test in debug mode to make sure
+// the cast is legal!
+//    This is the only place in the code we should use dynamic_cast<>.
+// In particular, you SHOULDN'T be using dynamic_cast<> in order to
+// do RTTI (eg code like this:
+//    if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
+//    if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
+// You should design the code some other way not to need this.
+//
+// This relatively ugly name is intentional. It prevents clashes with
+// similar functions users may have (e.g., down_cast). The internal
+// namespace alone is not enough because the function can be found by ADL.
+template<typename To, typename From>  // use like this: DownCast_<T*>(foo);
+inline To DownCast_(From* f) {  // so we only accept pointers
+  // Ensures that To is a sub-type of From *.  This test is here only
+  // for compile-time type checking, and has no overhead in an
+  // optimized build at run-time, as it will be optimized away
+  // completely.
+  GTEST_INTENTIONAL_CONST_COND_PUSH_()
+  if (false) {
+  GTEST_INTENTIONAL_CONST_COND_POP_()
+  const To to = nullptr;
+  ::testing::internal::ImplicitCast_<From*>(to);
+  }
+
+#if GTEST_HAS_RTTI
+  // RTTI: debug mode only!
+  GTEST_CHECK_(f == nullptr || dynamic_cast<To>(f) != nullptr);
+#endif
+  return static_cast<To>(f);
+}
+
+// Downcasts the pointer of type Base to Derived.
+// Derived must be a subclass of Base. The parameter MUST
+// point to a class of type Derived, not any subclass of it.
+// When RTTI is available, the function performs a runtime
+// check to enforce this.
+template <class Derived, class Base>
+Derived* CheckedDowncastToActualType(Base* base) {
+#if GTEST_HAS_RTTI
+  GTEST_CHECK_(typeid(*base) == typeid(Derived));
+#endif
+
+#if GTEST_HAS_DOWNCAST_
+  return ::down_cast<Derived*>(base);
+#elif GTEST_HAS_RTTI
+  return dynamic_cast<Derived*>(base);  // NOLINT
+#else
+  return static_cast<Derived*>(base);  // Poor man's downcast.
+#endif
+}
+
+#if GTEST_HAS_STREAM_REDIRECTION
+
+// Defines the stderr capturer:
+//   CaptureStdout     - starts capturing stdout.
+//   GetCapturedStdout - stops capturing stdout and returns the captured string.
+//   CaptureStderr     - starts capturing stderr.
+//   GetCapturedStderr - stops capturing stderr and returns the captured string.
+//
+GTEST_API_ void CaptureStdout();
+GTEST_API_ std::string GetCapturedStdout();
+GTEST_API_ void CaptureStderr();
+GTEST_API_ std::string GetCapturedStderr();
+
+#endif  // GTEST_HAS_STREAM_REDIRECTION
+// Returns the size (in bytes) of a file.
+GTEST_API_ size_t GetFileSize(FILE* file);
+
+// Reads the entire content of a file as a string.
+GTEST_API_ std::string ReadEntireFile(FILE* file);
+
+// All command line arguments.
+GTEST_API_ std::vector<std::string> GetArgvs();
+
+#if GTEST_HAS_DEATH_TEST
+
+std::vector<std::string> GetInjectableArgvs();
+// Deprecated: pass the args vector by value instead.
+void SetInjectableArgvs(const std::vector<std::string>* new_argvs);
+void SetInjectableArgvs(const std::vector<std::string>& new_argvs);
+void ClearInjectableArgvs();
+
+#endif  // GTEST_HAS_DEATH_TEST
+
+// Defines synchronization primitives.
+#if GTEST_IS_THREADSAFE
+# if GTEST_HAS_PTHREAD
+// Sleeps for (roughly) n milliseconds.  This function is only for testing
+// Google Test's own constructs.  Don't use it in user tests, either
+// directly or indirectly.
+inline void SleepMilliseconds(int n) {
+  const timespec time = {
+    0,                  // 0 seconds.
+    n * 1000L * 1000L,  // And n ms.
+  };
+  nanosleep(&time, nullptr);
+}
+# endif  // GTEST_HAS_PTHREAD
+
+# if GTEST_HAS_NOTIFICATION_
+// Notification has already been imported into the namespace.
+// Nothing to do here.
+
+# elif GTEST_HAS_PTHREAD
+// Allows a controller thread to pause execution of newly created
+// threads until notified.  Instances of this class must be created
+// and destroyed in the controller thread.
+//
+// This class is only for testing Google Test's own constructs. Do not
+// use it in user tests, either directly or indirectly.
+class Notification {
+ public:
+  Notification() : notified_(false) {
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr));
+  }
+  ~Notification() {
+    pthread_mutex_destroy(&mutex_);
+  }
+
+  // Notifies all threads created with this notification to start. Must
+  // be called from the controller thread.
+  void Notify() {
+    pthread_mutex_lock(&mutex_);
+    notified_ = true;
+    pthread_mutex_unlock(&mutex_);
+  }
+
+  // Blocks until the controller thread notifies. Must be called from a test
+  // thread.
+  void WaitForNotification() {
+    for (;;) {
+      pthread_mutex_lock(&mutex_);
+      const bool notified = notified_;
+      pthread_mutex_unlock(&mutex_);
+      if (notified)
+        break;
+      SleepMilliseconds(10);
+    }
+  }
+
+ private:
+  pthread_mutex_t mutex_;
+  bool notified_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification);
+};
+
+# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
+
+GTEST_API_ void SleepMilliseconds(int n);
+
+// Provides leak-safe Windows kernel handle ownership.
+// Used in death tests and in threading support.
+class GTEST_API_ AutoHandle {
+ public:
+  // Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to
+  // avoid including <windows.h> in this header file. Including <windows.h> is
+  // undesirable because it defines a lot of symbols and macros that tend to
+  // conflict with client code. This assumption is verified by
+  // WindowsTypesTest.HANDLEIsVoidStar.
+  typedef void* Handle;
+  AutoHandle();
+  explicit AutoHandle(Handle handle);
+
+  ~AutoHandle();
+
+  Handle Get() const;
+  void Reset();
+  void Reset(Handle handle);
+
+ private:
+  // Returns true if and only if the handle is a valid handle object that can be
+  // closed.
+  bool IsCloseable() const;
+
+  Handle handle_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle);
+};
+
+// Allows a controller thread to pause execution of newly created
+// threads until notified.  Instances of this class must be created
+// and destroyed in the controller thread.
+//
+// This class is only for testing Google Test's own constructs. Do not
+// use it in user tests, either directly or indirectly.
+class GTEST_API_ Notification {
+ public:
+  Notification();
+  void Notify();
+  void WaitForNotification();
+
+ private:
+  AutoHandle event_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification);
+};
+# endif  // GTEST_HAS_NOTIFICATION_
+
+// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD
+// defined, but we don't want to use MinGW's pthreads implementation, which
+// has conformance problems with some versions of the POSIX standard.
+# if GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW
+
+// As a C-function, ThreadFuncWithCLinkage cannot be templated itself.
+// Consequently, it cannot select a correct instantiation of ThreadWithParam
+// in order to call its Run(). Introducing ThreadWithParamBase as a
+// non-templated base class for ThreadWithParam allows us to bypass this
+// problem.
+class ThreadWithParamBase {
+ public:
+  virtual ~ThreadWithParamBase() {}
+  virtual void Run() = 0;
+};
+
+// pthread_create() accepts a pointer to a function type with the C linkage.
+// According to the Standard (7.5/1), function types with different linkages
+// are different even if they are otherwise identical.  Some compilers (for
+// example, SunStudio) treat them as different types.  Since class methods
+// cannot be defined with C-linkage we need to define a free C-function to
+// pass into pthread_create().
+extern "C" inline void* ThreadFuncWithCLinkage(void* thread) {
+  static_cast<ThreadWithParamBase*>(thread)->Run();
+  return nullptr;
+}
+
+// Helper class for testing Google Test's multi-threading constructs.
+// To use it, write:
+//
+//   void ThreadFunc(int param) { /* Do things with param */ }
+//   Notification thread_can_start;
+//   ...
+//   // The thread_can_start parameter is optional; you can supply NULL.
+//   ThreadWithParam<int> thread(&ThreadFunc, 5, &thread_can_start);
+//   thread_can_start.Notify();
+//
+// These classes are only for testing Google Test's own constructs. Do
+// not use them in user tests, either directly or indirectly.
+template <typename T>
+class ThreadWithParam : public ThreadWithParamBase {
+ public:
+  typedef void UserThreadFunc(T);
+
+  ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start)
+      : func_(func),
+        param_(param),
+        thread_can_start_(thread_can_start),
+        finished_(false) {
+    ThreadWithParamBase* const base = this;
+    // The thread can be created only after all fields except thread_
+    // have been initialized.
+    GTEST_CHECK_POSIX_SUCCESS_(
+        pthread_create(&thread_, nullptr, &ThreadFuncWithCLinkage, base));
+  }
+  ~ThreadWithParam() override { Join(); }
+
+  void Join() {
+    if (!finished_) {
+      GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, nullptr));
+      finished_ = true;
+    }
+  }
+
+  void Run() override {
+    if (thread_can_start_ != nullptr) thread_can_start_->WaitForNotification();
+    func_(param_);
+  }
+
+ private:
+  UserThreadFunc* const func_;  // User-supplied thread function.
+  const T param_;  // User-supplied parameter to the thread function.
+  // When non-NULL, used to block execution until the controller thread
+  // notifies.
+  Notification* const thread_can_start_;
+  bool finished_;  // true if and only if we know that the thread function has
+                   // finished.
+  pthread_t thread_;  // The native thread object.
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam);
+};
+# endif  // !GTEST_OS_WINDOWS && GTEST_HAS_PTHREAD ||
+         // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_
+
+# if GTEST_HAS_MUTEX_AND_THREAD_LOCAL_
+// Mutex and ThreadLocal have already been imported into the namespace.
+// Nothing to do here.
+
+# elif GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
+
+// Mutex implements mutex on Windows platforms.  It is used in conjunction
+// with class MutexLock:
+//
+//   Mutex mutex;
+//   ...
+//   MutexLock lock(&mutex);  // Acquires the mutex and releases it at the
+//                            // end of the current scope.
+//
+// A static Mutex *must* be defined or declared using one of the following
+// macros:
+//   GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex);
+//   GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex);
+//
+// (A non-static Mutex is defined/declared in the usual way).
+class GTEST_API_ Mutex {
+ public:
+  enum MutexType { kStatic = 0, kDynamic = 1 };
+  // We rely on kStaticMutex being 0 as it is to what the linker initializes
+  // type_ in static mutexes.  critical_section_ will be initialized lazily
+  // in ThreadSafeLazyInit().
+  enum StaticConstructorSelector { kStaticMutex = 0 };
+
+  // This constructor intentionally does nothing.  It relies on type_ being
+  // statically initialized to 0 (effectively setting it to kStatic) and on
+  // ThreadSafeLazyInit() to lazily initialize the rest of the members.
+  explicit Mutex(StaticConstructorSelector /*dummy*/) {}
+
+  Mutex();
+  ~Mutex();
+
+  void Lock();
+
+  void Unlock();
+
+  // Does nothing if the current thread holds the mutex. Otherwise, crashes
+  // with high probability.
+  void AssertHeld();
+
+ private:
+  // Initializes owner_thread_id_ and critical_section_ in static mutexes.
+  void ThreadSafeLazyInit();
+
+  // Per https://blogs.msdn.microsoft.com/oldnewthing/20040223-00/?p=40503,
+  // we assume that 0 is an invalid value for thread IDs.
+  unsigned int owner_thread_id_;
+
+  // For static mutexes, we rely on these members being initialized to zeros
+  // by the linker.
+  MutexType type_;
+  long critical_section_init_phase_;  // NOLINT
+  GTEST_CRITICAL_SECTION* critical_section_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
+};
+
+# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
+    extern ::testing::internal::Mutex mutex
+
+# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
+    ::testing::internal::Mutex mutex(::testing::internal::Mutex::kStaticMutex)
+
+// We cannot name this class MutexLock because the ctor declaration would
+// conflict with a macro named MutexLock, which is defined on some
+// platforms. That macro is used as a defensive measure to prevent against
+// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than
+// "MutexLock l(&mu)".  Hence the typedef trick below.
+class GTestMutexLock {
+ public:
+  explicit GTestMutexLock(Mutex* mutex)
+      : mutex_(mutex) { mutex_->Lock(); }
+
+  ~GTestMutexLock() { mutex_->Unlock(); }
+
+ private:
+  Mutex* const mutex_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock);
+};
+
+typedef GTestMutexLock MutexLock;
+
+// Base class for ValueHolder<T>.  Allows a caller to hold and delete a value
+// without knowing its type.
+class ThreadLocalValueHolderBase {
+ public:
+  virtual ~ThreadLocalValueHolderBase() {}
+};
+
+// Provides a way for a thread to send notifications to a ThreadLocal
+// regardless of its parameter type.
+class ThreadLocalBase {
+ public:
+  // Creates a new ValueHolder<T> object holding a default value passed to
+  // this ThreadLocal<T>'s constructor and returns it.  It is the caller's
+  // responsibility not to call this when the ThreadLocal<T> instance already
+  // has a value on the current thread.
+  virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const = 0;
+
+ protected:
+  ThreadLocalBase() {}
+  virtual ~ThreadLocalBase() {}
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocalBase);
+};
+
+// Maps a thread to a set of ThreadLocals that have values instantiated on that
+// thread and notifies them when the thread exits.  A ThreadLocal instance is
+// expected to persist until all threads it has values on have terminated.
+class GTEST_API_ ThreadLocalRegistry {
+ public:
+  // Registers thread_local_instance as having value on the current thread.
+  // Returns a value that can be used to identify the thread from other threads.
+  static ThreadLocalValueHolderBase* GetValueOnCurrentThread(
+      const ThreadLocalBase* thread_local_instance);
+
+  // Invoked when a ThreadLocal instance is destroyed.
+  static void OnThreadLocalDestroyed(
+      const ThreadLocalBase* thread_local_instance);
+};
+
+class GTEST_API_ ThreadWithParamBase {
+ public:
+  void Join();
+
+ protected:
+  class Runnable {
+   public:
+    virtual ~Runnable() {}
+    virtual void Run() = 0;
+  };
+
+  ThreadWithParamBase(Runnable *runnable, Notification* thread_can_start);
+  virtual ~ThreadWithParamBase();
+
+ private:
+  AutoHandle thread_;
+};
+
+// Helper class for testing Google Test's multi-threading constructs.
+template <typename T>
+class ThreadWithParam : public ThreadWithParamBase {
+ public:
+  typedef void UserThreadFunc(T);
+
+  ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start)
+      : ThreadWithParamBase(new RunnableImpl(func, param), thread_can_start) {
+  }
+  virtual ~ThreadWithParam() {}
+
+ private:
+  class RunnableImpl : public Runnable {
+   public:
+    RunnableImpl(UserThreadFunc* func, T param)
+        : func_(func),
+          param_(param) {
+    }
+    virtual ~RunnableImpl() {}
+    virtual void Run() {
+      func_(param_);
+    }
+
+   private:
+    UserThreadFunc* const func_;
+    const T param_;
+
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(RunnableImpl);
+  };
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam);
+};
+
+// Implements thread-local storage on Windows systems.
+//
+//   // Thread 1
+//   ThreadLocal<int> tl(100);  // 100 is the default value for each thread.
+//
+//   // Thread 2
+//   tl.set(150);  // Changes the value for thread 2 only.
+//   EXPECT_EQ(150, tl.get());
+//
+//   // Thread 1
+//   EXPECT_EQ(100, tl.get());  // In thread 1, tl has the original value.
+//   tl.set(200);
+//   EXPECT_EQ(200, tl.get());
+//
+// The template type argument T must have a public copy constructor.
+// In addition, the default ThreadLocal constructor requires T to have
+// a public default constructor.
+//
+// The users of a TheadLocal instance have to make sure that all but one
+// threads (including the main one) using that instance have exited before
+// destroying it. Otherwise, the per-thread objects managed for them by the
+// ThreadLocal instance are not guaranteed to be destroyed on all platforms.
+//
+// Google Test only uses global ThreadLocal objects.  That means they
+// will die after main() has returned.  Therefore, no per-thread
+// object managed by Google Test will be leaked as long as all threads
+// using Google Test have exited when main() returns.
+template <typename T>
+class ThreadLocal : public ThreadLocalBase {
+ public:
+  ThreadLocal() : default_factory_(new DefaultValueHolderFactory()) {}
+  explicit ThreadLocal(const T& value)
+      : default_factory_(new InstanceValueHolderFactory(value)) {}
+
+  ~ThreadLocal() { ThreadLocalRegistry::OnThreadLocalDestroyed(this); }
+
+  T* pointer() { return GetOrCreateValue(); }
+  const T* pointer() const { return GetOrCreateValue(); }
+  const T& get() const { return *pointer(); }
+  void set(const T& value) { *pointer() = value; }
+
+ private:
+  // Holds a value of T.  Can be deleted via its base class without the caller
+  // knowing the type of T.
+  class ValueHolder : public ThreadLocalValueHolderBase {
+   public:
+    ValueHolder() : value_() {}
+    explicit ValueHolder(const T& value) : value_(value) {}
+
+    T* pointer() { return &value_; }
+
+   private:
+    T value_;
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder);
+  };
+
+
+  T* GetOrCreateValue() const {
+    return static_cast<ValueHolder*>(
+        ThreadLocalRegistry::GetValueOnCurrentThread(this))->pointer();
+  }
+
+  virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const {
+    return default_factory_->MakeNewHolder();
+  }
+
+  class ValueHolderFactory {
+   public:
+    ValueHolderFactory() {}
+    virtual ~ValueHolderFactory() {}
+    virtual ValueHolder* MakeNewHolder() const = 0;
+
+   private:
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory);
+  };
+
+  class DefaultValueHolderFactory : public ValueHolderFactory {
+   public:
+    DefaultValueHolderFactory() {}
+    ValueHolder* MakeNewHolder() const override { return new ValueHolder(); }
+
+   private:
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory);
+  };
+
+  class InstanceValueHolderFactory : public ValueHolderFactory {
+   public:
+    explicit InstanceValueHolderFactory(const T& value) : value_(value) {}
+    ValueHolder* MakeNewHolder() const override {
+      return new ValueHolder(value_);
+    }
+
+   private:
+    const T value_;  // The value for each thread.
+
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory);
+  };
+
+  std::unique_ptr<ValueHolderFactory> default_factory_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
+};
+
+# elif GTEST_HAS_PTHREAD
+
+// MutexBase and Mutex implement mutex on pthreads-based platforms.
+class MutexBase {
+ public:
+  // Acquires this mutex.
+  void Lock() {
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_));
+    owner_ = pthread_self();
+    has_owner_ = true;
+  }
+
+  // Releases this mutex.
+  void Unlock() {
+    // Since the lock is being released the owner_ field should no longer be
+    // considered valid. We don't protect writing to has_owner_ here, as it's
+    // the caller's responsibility to ensure that the current thread holds the
+    // mutex when this is called.
+    has_owner_ = false;
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_));
+  }
+
+  // Does nothing if the current thread holds the mutex. Otherwise, crashes
+  // with high probability.
+  void AssertHeld() const {
+    GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self()))
+        << "The current thread is not holding the mutex @" << this;
+  }
+
+  // A static mutex may be used before main() is entered.  It may even
+  // be used before the dynamic initialization stage.  Therefore we
+  // must be able to initialize a static mutex object at link time.
+  // This means MutexBase has to be a POD and its member variables
+  // have to be public.
+ public:
+  pthread_mutex_t mutex_;  // The underlying pthread mutex.
+  // has_owner_ indicates whether the owner_ field below contains a valid thread
+  // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All
+  // accesses to the owner_ field should be protected by a check of this field.
+  // An alternative might be to memset() owner_ to all zeros, but there's no
+  // guarantee that a zero'd pthread_t is necessarily invalid or even different
+  // from pthread_self().
+  bool has_owner_;
+  pthread_t owner_;  // The thread holding the mutex.
+};
+
+// Forward-declares a static mutex.
+#  define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
+     extern ::testing::internal::MutexBase mutex
+
+// Defines and statically (i.e. at link time) initializes a static mutex.
+// The initialization list here does not explicitly initialize each field,
+// instead relying on default initialization for the unspecified fields. In
+// particular, the owner_ field (a pthread_t) is not explicitly initialized.
+// This allows initialization to work whether pthread_t is a scalar or struct.
+// The flag -Wmissing-field-initializers must not be specified for this to work.
+#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
+  ::testing::internal::MutexBase mutex = {PTHREAD_MUTEX_INITIALIZER, false, 0}
+
+// The Mutex class can only be used for mutexes created at runtime. It
+// shares its API with MutexBase otherwise.
+class Mutex : public MutexBase {
+ public:
+  Mutex() {
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, nullptr));
+    has_owner_ = false;
+  }
+  ~Mutex() {
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_));
+  }
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
+};
+
+// We cannot name this class MutexLock because the ctor declaration would
+// conflict with a macro named MutexLock, which is defined on some
+// platforms. That macro is used as a defensive measure to prevent against
+// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than
+// "MutexLock l(&mu)".  Hence the typedef trick below.
+class GTestMutexLock {
+ public:
+  explicit GTestMutexLock(MutexBase* mutex)
+      : mutex_(mutex) { mutex_->Lock(); }
+
+  ~GTestMutexLock() { mutex_->Unlock(); }
+
+ private:
+  MutexBase* const mutex_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock);
+};
+
+typedef GTestMutexLock MutexLock;
+
+// Helpers for ThreadLocal.
+
+// pthread_key_create() requires DeleteThreadLocalValue() to have
+// C-linkage.  Therefore it cannot be templatized to access
+// ThreadLocal<T>.  Hence the need for class
+// ThreadLocalValueHolderBase.
+class ThreadLocalValueHolderBase {
+ public:
+  virtual ~ThreadLocalValueHolderBase() {}
+};
+
+// Called by pthread to delete thread-local data stored by
+// pthread_setspecific().
+extern "C" inline void DeleteThreadLocalValue(void* value_holder) {
+  delete static_cast<ThreadLocalValueHolderBase*>(value_holder);
+}
+
+// Implements thread-local storage on pthreads-based systems.
+template <typename T>
+class GTEST_API_ ThreadLocal {
+ public:
+  ThreadLocal()
+      : key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {}
+  explicit ThreadLocal(const T& value)
+      : key_(CreateKey()),
+        default_factory_(new InstanceValueHolderFactory(value)) {}
+
+  ~ThreadLocal() {
+    // Destroys the managed object for the current thread, if any.
+    DeleteThreadLocalValue(pthread_getspecific(key_));
+
+    // Releases resources associated with the key.  This will *not*
+    // delete managed objects for other threads.
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_));
+  }
+
+  T* pointer() { return GetOrCreateValue(); }
+  const T* pointer() const { return GetOrCreateValue(); }
+  const T& get() const { return *pointer(); }
+  void set(const T& value) { *pointer() = value; }
+
+ private:
+  // Holds a value of type T.
+  class ValueHolder : public ThreadLocalValueHolderBase {
+   public:
+    ValueHolder() : value_() {}
+    explicit ValueHolder(const T& value) : value_(value) {}
+
+    T* pointer() { return &value_; }
+
+   private:
+    T value_;
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder);
+  };
+
+  static pthread_key_t CreateKey() {
+    pthread_key_t key;
+    // When a thread exits, DeleteThreadLocalValue() will be called on
+    // the object managed for that thread.
+    GTEST_CHECK_POSIX_SUCCESS_(
+        pthread_key_create(&key, &DeleteThreadLocalValue));
+    return key;
+  }
+
+  T* GetOrCreateValue() const {
+    ThreadLocalValueHolderBase* const holder =
+        static_cast<ThreadLocalValueHolderBase*>(pthread_getspecific(key_));
+    if (holder != nullptr) {
+      return CheckedDowncastToActualType<ValueHolder>(holder)->pointer();
+    }
+
+    ValueHolder* const new_holder = default_factory_->MakeNewHolder();
+    ThreadLocalValueHolderBase* const holder_base = new_holder;
+    GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base));
+    return new_holder->pointer();
+  }
+
+  class ValueHolderFactory {
+   public:
+    ValueHolderFactory() {}
+    virtual ~ValueHolderFactory() {}
+    virtual ValueHolder* MakeNewHolder() const = 0;
+
+   private:
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory);
+  };
+
+  class DefaultValueHolderFactory : public ValueHolderFactory {
+   public:
+    DefaultValueHolderFactory() {}
+    ValueHolder* MakeNewHolder() const override { return new ValueHolder(); }
+
+   private:
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory);
+  };
+
+  class InstanceValueHolderFactory : public ValueHolderFactory {
+   public:
+    explicit InstanceValueHolderFactory(const T& value) : value_(value) {}
+    ValueHolder* MakeNewHolder() const override {
+      return new ValueHolder(value_);
+    }
+
+   private:
+    const T value_;  // The value for each thread.
+
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory);
+  };
+
+  // A key pthreads uses for looking up per-thread values.
+  const pthread_key_t key_;
+  std::unique_ptr<ValueHolderFactory> default_factory_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
+};
+
+# endif  // GTEST_HAS_MUTEX_AND_THREAD_LOCAL_
+
+#else  // GTEST_IS_THREADSAFE
+
+// A dummy implementation of synchronization primitives (mutex, lock,
+// and thread-local variable).  Necessary for compiling Google Test where
+// mutex is not supported - using Google Test in multiple threads is not
+// supported on such platforms.
+
+class Mutex {
+ public:
+  Mutex() {}
+  void Lock() {}
+  void Unlock() {}
+  void AssertHeld() const {}
+};
+
+# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
+  extern ::testing::internal::Mutex mutex
+
+# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex
+
+// We cannot name this class MutexLock because the ctor declaration would
+// conflict with a macro named MutexLock, which is defined on some
+// platforms. That macro is used as a defensive measure to prevent against
+// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than
+// "MutexLock l(&mu)".  Hence the typedef trick below.
+class GTestMutexLock {
+ public:
+  explicit GTestMutexLock(Mutex*) {}  // NOLINT
+};
+
+typedef GTestMutexLock MutexLock;
+
+template <typename T>
+class GTEST_API_ ThreadLocal {
+ public:
+  ThreadLocal() : value_() {}
+  explicit ThreadLocal(const T& value) : value_(value) {}
+  T* pointer() { return &value_; }
+  const T* pointer() const { return &value_; }
+  const T& get() const { return value_; }
+  void set(const T& value) { value_ = value; }
+ private:
+  T value_;
+};
+
+#endif  // GTEST_IS_THREADSAFE
+
+// Returns the number of threads running in the process, or 0 to indicate that
+// we cannot detect it.
+GTEST_API_ size_t GetThreadCount();
+
+#if GTEST_OS_WINDOWS
+# define GTEST_PATH_SEP_ "\\"
+# define GTEST_HAS_ALT_PATH_SEP_ 1
+#else
+# define GTEST_PATH_SEP_ "/"
+# define GTEST_HAS_ALT_PATH_SEP_ 0
+#endif  // GTEST_OS_WINDOWS
+
+// Utilities for char.
+
+// isspace(int ch) and friends accept an unsigned char or EOF.  char
+// may be signed, depending on the compiler (or compiler flags).
+// Therefore we need to cast a char to unsigned char before calling
+// isspace(), etc.
+
+inline bool IsAlpha(char ch) {
+  return isalpha(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsAlNum(char ch) {
+  return isalnum(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsDigit(char ch) {
+  return isdigit(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsLower(char ch) {
+  return islower(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsSpace(char ch) {
+  return isspace(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsUpper(char ch) {
+  return isupper(static_cast<unsigned char>(ch)) != 0;
+}
+inline bool IsXDigit(char ch) {
+  return isxdigit(static_cast<unsigned char>(ch)) != 0;
+}
+#ifdef __cpp_char8_t
+inline bool IsXDigit(char8_t ch) {
+  return isxdigit(static_cast<unsigned char>(ch)) != 0;
+}
+#endif
+inline bool IsXDigit(char16_t ch) {
+  const unsigned char low_byte = static_cast<unsigned char>(ch);
+  return ch == low_byte && isxdigit(low_byte) != 0;
+}
+inline bool IsXDigit(char32_t ch) {
+  const unsigned char low_byte = static_cast<unsigned char>(ch);
+  return ch == low_byte && isxdigit(low_byte) != 0;
+}
+inline bool IsXDigit(wchar_t ch) {
+  const unsigned char low_byte = static_cast<unsigned char>(ch);
+  return ch == low_byte && isxdigit(low_byte) != 0;
+}
+
+inline char ToLower(char ch) {
+  return static_cast<char>(tolower(static_cast<unsigned char>(ch)));
+}
+inline char ToUpper(char ch) {
+  return static_cast<char>(toupper(static_cast<unsigned char>(ch)));
+}
+
+inline std::string StripTrailingSpaces(std::string str) {
+  std::string::iterator it = str.end();
+  while (it != str.begin() && IsSpace(*--it))
+    it = str.erase(it);
+  return str;
+}
+
+// The testing::internal::posix namespace holds wrappers for common
+// POSIX functions.  These wrappers hide the differences between
+// Windows/MSVC and POSIX systems.  Since some compilers define these
+// standard functions as macros, the wrapper cannot have the same name
+// as the wrapped function.
+
+namespace posix {
+
+// Functions with a different name on Windows.
+
+#if GTEST_OS_WINDOWS
+
+typedef struct _stat StatStruct;
+
+# ifdef __BORLANDC__
+inline int DoIsATTY(int fd) { return isatty(fd); }
+inline int StrCaseCmp(const char* s1, const char* s2) {
+  return stricmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return strdup(src); }
+# else  // !__BORLANDC__
+#  if GTEST_OS_WINDOWS_MOBILE
+inline int DoIsATTY(int /* fd */) { return 0; }
+#  else
+inline int DoIsATTY(int fd) { return _isatty(fd); }
+#  endif  // GTEST_OS_WINDOWS_MOBILE
+inline int StrCaseCmp(const char* s1, const char* s2) {
+  return _stricmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return _strdup(src); }
+# endif  // __BORLANDC__
+
+# if GTEST_OS_WINDOWS_MOBILE
+inline int FileNo(FILE* file) { return reinterpret_cast<int>(_fileno(file)); }
+// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this
+// time and thus not defined there.
+# else
+inline int FileNo(FILE* file) { return _fileno(file); }
+inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); }
+inline int RmDir(const char* dir) { return _rmdir(dir); }
+inline bool IsDir(const StatStruct& st) {
+  return (_S_IFDIR & st.st_mode) != 0;
+}
+# endif  // GTEST_OS_WINDOWS_MOBILE
+
+#elif GTEST_OS_ESP8266
+typedef struct stat StatStruct;
+
+inline int FileNo(FILE* file) { return fileno(file); }
+inline int DoIsATTY(int fd) { return isatty(fd); }
+inline int Stat(const char* path, StatStruct* buf) {
+  // stat function not implemented on ESP8266
+  return 0;
+}
+inline int StrCaseCmp(const char* s1, const char* s2) {
+  return strcasecmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return strdup(src); }
+inline int RmDir(const char* dir) { return rmdir(dir); }
+inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
+
+#else
+
+typedef struct stat StatStruct;
+
+inline int FileNo(FILE* file) { return fileno(file); }
+inline int DoIsATTY(int fd) { return isatty(fd); }
+inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); }
+inline int StrCaseCmp(const char* s1, const char* s2) {
+  return strcasecmp(s1, s2);
+}
+inline char* StrDup(const char* src) { return strdup(src); }
+inline int RmDir(const char* dir) { return rmdir(dir); }
+inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); }
+
+#endif  // GTEST_OS_WINDOWS
+
+inline int IsATTY(int fd) {
+  // DoIsATTY might change errno (for example ENOTTY in case you redirect stdout
+  // to a file on Linux), which is unexpected, so save the previous value, and
+  // restore it after the call.
+  int savedErrno = errno;
+  int isAttyValue = DoIsATTY(fd);
+  errno = savedErrno;
+
+  return isAttyValue;
+}
+
+// Functions deprecated by MSVC 8.0.
+
+GTEST_DISABLE_MSC_DEPRECATED_PUSH_()
+
+// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and
+// StrError() aren't needed on Windows CE at this time and thus not
+// defined there.
+
+#if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && \
+    !GTEST_OS_WINDOWS_RT && !GTEST_OS_ESP8266 && !GTEST_OS_XTENSA
+inline int ChDir(const char* dir) { return chdir(dir); }
+#endif
+inline FILE* FOpen(const char* path, const char* mode) {
+#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
+  struct wchar_codecvt : public std::codecvt<wchar_t, char, std::mbstate_t> {};
+  std::wstring_convert<wchar_codecvt> converter;
+  std::wstring wide_path = converter.from_bytes(path);
+  std::wstring wide_mode = converter.from_bytes(mode);
+  return _wfopen(wide_path.c_str(), wide_mode.c_str());
+#else  // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
+  return fopen(path, mode);
+#endif  // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MINGW
+}
+#if !GTEST_OS_WINDOWS_MOBILE
+inline FILE *FReopen(const char* path, const char* mode, FILE* stream) {
+  return freopen(path, mode, stream);
+}
+inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); }
+#endif
+inline int FClose(FILE* fp) { return fclose(fp); }
+#if !GTEST_OS_WINDOWS_MOBILE
+inline int Read(int fd, void* buf, unsigned int count) {
+  return static_cast<int>(read(fd, buf, count));
+}
+inline int Write(int fd, const void* buf, unsigned int count) {
+  return static_cast<int>(write(fd, buf, count));
+}
+inline int Close(int fd) { return close(fd); }
+inline const char* StrError(int errnum) { return strerror(errnum); }
+#endif
+inline const char* GetEnv(const char* name) {
+#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \
+    GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_XTENSA
+  // We are on an embedded platform, which has no environment variables.
+  static_cast<void>(name);  // To prevent 'unused argument' warning.
+  return nullptr;
+#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9)
+  // Environment variables which we programmatically clear will be set to the
+  // empty string rather than unset (NULL).  Handle that case.
+  const char* const env = getenv(name);
+  return (env != nullptr && env[0] != '\0') ? env : nullptr;
+#else
+  return getenv(name);
+#endif
+}
+
+GTEST_DISABLE_MSC_DEPRECATED_POP_()
+
+#if GTEST_OS_WINDOWS_MOBILE
+// Windows CE has no C library. The abort() function is used in
+// several places in Google Test. This implementation provides a reasonable
+// imitation of standard behaviour.
+[[noreturn]] void Abort();
+#else
+[[noreturn]] inline void Abort() { abort(); }
+#endif  // GTEST_OS_WINDOWS_MOBILE
+
+}  // namespace posix
+
+// MSVC "deprecates" snprintf and issues warnings wherever it is used.  In
+// order to avoid these warnings, we need to use _snprintf or _snprintf_s on
+// MSVC-based platforms.  We map the GTEST_SNPRINTF_ macro to the appropriate
+// function in order to achieve that.  We use macro definition here because
+// snprintf is a variadic function.
+#if _MSC_VER && !GTEST_OS_WINDOWS_MOBILE
+// MSVC 2005 and above support variadic macros.
+# define GTEST_SNPRINTF_(buffer, size, format, ...) \
+     _snprintf_s(buffer, size, size, format, __VA_ARGS__)
+#elif defined(_MSC_VER)
+// Windows CE does not define _snprintf_s
+# define GTEST_SNPRINTF_ _snprintf
+#else
+# define GTEST_SNPRINTF_ snprintf
+#endif
+
+// The biggest signed integer type the compiler supports.
+//
+// long long is guaranteed to be at least 64-bits in C++11.
+using BiggestInt = long long;  // NOLINT
+
+// The maximum number a BiggestInt can represent.
+constexpr BiggestInt kMaxBiggestInt = (std::numeric_limits<BiggestInt>::max)();
+
+// This template class serves as a compile-time function from size to
+// type.  It maps a size in bytes to a primitive type with that
+// size. e.g.
+//
+//   TypeWithSize<4>::UInt
+//
+// is typedef-ed to be unsigned int (unsigned integer made up of 4
+// bytes).
+//
+// Such functionality should belong to STL, but I cannot find it
+// there.
+//
+// Google Test uses this class in the implementation of floating-point
+// comparison.
+//
+// For now it only handles UInt (unsigned int) as that's all Google Test
+// needs.  Other types can be easily added in the future if need
+// arises.
+template <size_t size>
+class TypeWithSize {
+ public:
+  // This prevents the user from using TypeWithSize<N> with incorrect
+  // values of N.
+  using UInt = void;
+};
+
+// The specialization for size 4.
+template <>
+class TypeWithSize<4> {
+ public:
+  using Int = std::int32_t;
+  using UInt = std::uint32_t;
+};
+
+// The specialization for size 8.
+template <>
+class TypeWithSize<8> {
+ public:
+  using Int = std::int64_t;
+  using UInt = std::uint64_t;
+};
+
+// Integer types of known sizes.
+using TimeInMillis = int64_t;  // Represents time in milliseconds.
+
+// Utilities for command line flags and environment variables.
+
+// Macro for referencing flags.
+#if !defined(GTEST_FLAG)
+# define GTEST_FLAG(name) FLAGS_gtest_##name
+#endif  // !defined(GTEST_FLAG)
+
+#if !defined(GTEST_USE_OWN_FLAGFILE_FLAG_)
+# define GTEST_USE_OWN_FLAGFILE_FLAG_ 1
+#endif  // !defined(GTEST_USE_OWN_FLAGFILE_FLAG_)
+
+#if !defined(GTEST_DECLARE_bool_)
+# define GTEST_FLAG_SAVER_ ::testing::internal::GTestFlagSaver
+
+// Macros for declaring flags.
+#define GTEST_DECLARE_bool_(name)          \
+  namespace testing {                      \
+  GTEST_API_ extern bool GTEST_FLAG(name); \
+  }
+#define GTEST_DECLARE_int32_(name)                 \
+  namespace testing {                              \
+  GTEST_API_ extern std::int32_t GTEST_FLAG(name); \
+  }
+#define GTEST_DECLARE_string_(name)                 \
+  namespace testing {                               \
+  GTEST_API_ extern ::std::string GTEST_FLAG(name); \
+  }
+
+// Macros for defining flags.
+#define GTEST_DEFINE_bool_(name, default_val, doc)  \
+  namespace testing {                               \
+  GTEST_API_ bool GTEST_FLAG(name) = (default_val); \
+  }
+#define GTEST_DEFINE_int32_(name, default_val, doc)         \
+  namespace testing {                                       \
+  GTEST_API_ std::int32_t GTEST_FLAG(name) = (default_val); \
+  }
+#define GTEST_DEFINE_string_(name, default_val, doc)         \
+  namespace testing {                                        \
+  GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val); \
+  }
+
+#endif  // !defined(GTEST_DECLARE_bool_)
+
+#if !defined(GTEST_FLAG_GET)
+#define GTEST_FLAG_GET(name) ::testing::GTEST_FLAG(name)
+#define GTEST_FLAG_SET(name, value) (void)(::testing::GTEST_FLAG(name) = value)
+#endif  // !defined(GTEST_FLAG_GET)
+
+// Thread annotations
+#if !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_)
+# define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)
+# define GTEST_LOCK_EXCLUDED_(locks)
+#endif  // !defined(GTEST_EXCLUSIVE_LOCK_REQUIRED_)
+
+// Parses 'str' for a 32-bit signed integer.  If successful, writes the result
+// to *value and returns true; otherwise leaves *value unchanged and returns
+// false.
+GTEST_API_ bool ParseInt32(const Message& src_text, const char* str,
+                           int32_t* value);
+
+// Parses a bool/int32_t/string from the environment variable
+// corresponding to the given Google Test flag.
+bool BoolFromGTestEnv(const char* flag, bool default_val);
+GTEST_API_ int32_t Int32FromGTestEnv(const char* flag, int32_t default_val);
+std::string OutputFlagAlsoCheckEnvVar();
+const char* StringFromGTestEnv(const char* flag, const char* default_val);
+
+}  // namespace internal
+}  // namespace testing
+
+#if !defined(GTEST_INTERNAL_DEPRECATED)
+
+// Internal Macro to mark an API deprecated, for googletest usage only
+// Usage: class GTEST_INTERNAL_DEPRECATED(message) MyClass or
+// GTEST_INTERNAL_DEPRECATED(message) <return_type> myFunction(); Every usage of
+// a deprecated entity will trigger a warning when compiled with
+// `-Wdeprecated-declarations` option (clang, gcc, any __GNUC__ compiler).
+// For msvc /W3 option will need to be used
+// Note that for 'other' compilers this macro evaluates to nothing to prevent
+// compilations errors.
+#if defined(_MSC_VER)
+#define GTEST_INTERNAL_DEPRECATED(message) __declspec(deprecated(message))
+#elif defined(__GNUC__)
+#define GTEST_INTERNAL_DEPRECATED(message) __attribute__((deprecated(message)))
+#else
+#define GTEST_INTERNAL_DEPRECATED(message)
+#endif
+
+#endif  // !defined(GTEST_INTERNAL_DEPRECATED)
+
+#if GTEST_HAS_ABSL
+// Always use absl::any for UniversalPrinter<> specializations if googletest
+// is built with absl support.
+#define GTEST_INTERNAL_HAS_ANY 1
+#include "absl/types/any.h"
+namespace testing {
+namespace internal {
+using Any = ::absl::any;
+}  // namespace internal
+}  // namespace testing
+#else
+#ifdef __has_include
+#if __has_include(<any>) && __cplusplus >= 201703L
+// Otherwise for C++17 and higher use std::any for UniversalPrinter<>
+// specializations.
+#define GTEST_INTERNAL_HAS_ANY 1
+#include <any>
+namespace testing {
+namespace internal {
+using Any = ::std::any;
+}  // namespace internal
+}  // namespace testing
+// The case where absl is configured NOT to alias std::any is not
+// supported.
+#endif  // __has_include(<any>) && __cplusplus >= 201703L
+#endif  // __has_include
+#endif  // GTEST_HAS_ABSL
+
+#if GTEST_HAS_ABSL
+// Always use absl::optional for UniversalPrinter<> specializations if
+// googletest is built with absl support.
+#define GTEST_INTERNAL_HAS_OPTIONAL 1
+#include "absl/types/optional.h"
+namespace testing {
+namespace internal {
+template <typename T>
+using Optional = ::absl::optional<T>;
+}  // namespace internal
+}  // namespace testing
+#else
+#ifdef __has_include
+#if __has_include(<optional>) && __cplusplus >= 201703L
+// Otherwise for C++17 and higher use std::optional for UniversalPrinter<>
+// specializations.
+#define GTEST_INTERNAL_HAS_OPTIONAL 1
+#include <optional>
+namespace testing {
+namespace internal {
+template <typename T>
+using Optional = ::std::optional<T>;
+}  // namespace internal
+}  // namespace testing
+// The case where absl is configured NOT to alias std::optional is not
+// supported.
+#endif  // __has_include(<optional>) && __cplusplus >= 201703L
+#endif  // __has_include
+#endif  // GTEST_HAS_ABSL
+
+#if GTEST_HAS_ABSL
+// Always use absl::string_view for Matcher<> specializations if googletest
+// is built with absl support.
+# define GTEST_INTERNAL_HAS_STRING_VIEW 1
+#include "absl/strings/string_view.h"
+namespace testing {
+namespace internal {
+using StringView = ::absl::string_view;
+}  // namespace internal
+}  // namespace testing
+#else
+# ifdef __has_include
+#   if __has_include(<string_view>) && __cplusplus >= 201703L
+// Otherwise for C++17 and higher use std::string_view for Matcher<>
+// specializations.
+#   define GTEST_INTERNAL_HAS_STRING_VIEW 1
+#include <string_view>
+namespace testing {
+namespace internal {
+using StringView = ::std::string_view;
+}  // namespace internal
+}  // namespace testing
+// The case where absl is configured NOT to alias std::string_view is not
+// supported.
+#  endif  // __has_include(<string_view>) && __cplusplus >= 201703L
+# endif  // __has_include
+#endif  // GTEST_HAS_ABSL
+
+#if GTEST_HAS_ABSL
+// Always use absl::variant for UniversalPrinter<> specializations if googletest
+// is built with absl support.
+#define GTEST_INTERNAL_HAS_VARIANT 1
+#include "absl/types/variant.h"
+namespace testing {
+namespace internal {
+template <typename... T>
+using Variant = ::absl::variant<T...>;
+}  // namespace internal
+}  // namespace testing
+#else
+#ifdef __has_include
+#if __has_include(<variant>) && __cplusplus >= 201703L
+// Otherwise for C++17 and higher use std::variant for UniversalPrinter<>
+// specializations.
+#define GTEST_INTERNAL_HAS_VARIANT 1
+#include <variant>
+namespace testing {
+namespace internal {
+template <typename... T>
+using Variant = ::std::variant<T...>;
+}  // namespace internal
+}  // namespace testing
+// The case where absl is configured NOT to alias std::variant is not supported.
+#endif  // __has_include(<variant>) && __cplusplus >= 201703L
+#endif  // __has_include
+#endif  // GTEST_HAS_ABSL
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-string.h b/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-string.h
new file mode 100644 (file)
index 0000000..0f2bcbe
--- /dev/null
@@ -0,0 +1,173 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// The Google C++ Testing and Mocking Framework (Google Test)
+//
+// This header file declares the String class and functions used internally by
+// Google Test.  They are subject to change without notice. They should not used
+// by code external to Google Test.
+//
+// This header file is #included by gtest-internal.h.
+// It should not be #included by other files.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
+
+#ifdef __BORLANDC__
+// string.h is not guaranteed to provide strcpy on C++ Builder.
+# include <mem.h>
+#endif
+
+#include <string.h>
+#include <cstdint>
+#include <string>
+
+#include "gtest/internal/gtest-port.h"
+
+namespace testing {
+namespace internal {
+
+// String - an abstract class holding static string utilities.
+class GTEST_API_ String {
+ public:
+  // Static utility methods
+
+  // Clones a 0-terminated C string, allocating memory using new.  The
+  // caller is responsible for deleting the return value using
+  // delete[].  Returns the cloned string, or NULL if the input is
+  // NULL.
+  //
+  // This is different from strdup() in string.h, which allocates
+  // memory using malloc().
+  static const char* CloneCString(const char* c_str);
+
+#if GTEST_OS_WINDOWS_MOBILE
+  // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be
+  // able to pass strings to Win32 APIs on CE we need to convert them
+  // to 'Unicode', UTF-16.
+
+  // Creates a UTF-16 wide string from the given ANSI string, allocating
+  // memory using new. The caller is responsible for deleting the return
+  // value using delete[]. Returns the wide string, or NULL if the
+  // input is NULL.
+  //
+  // The wide string is created using the ANSI codepage (CP_ACP) to
+  // match the behaviour of the ANSI versions of Win32 calls and the
+  // C runtime.
+  static LPCWSTR AnsiToUtf16(const char* c_str);
+
+  // Creates an ANSI string from the given wide string, allocating
+  // memory using new. The caller is responsible for deleting the return
+  // value using delete[]. Returns the ANSI string, or NULL if the
+  // input is NULL.
+  //
+  // The returned string is created using the ANSI codepage (CP_ACP) to
+  // match the behaviour of the ANSI versions of Win32 calls and the
+  // C runtime.
+  static const char* Utf16ToAnsi(LPCWSTR utf16_str);
+#endif
+
+  // Compares two C strings.  Returns true if and only if they have the same
+  // content.
+  //
+  // Unlike strcmp(), this function can handle NULL argument(s).  A
+  // NULL C string is considered different to any non-NULL C string,
+  // including the empty string.
+  static bool CStringEquals(const char* lhs, const char* rhs);
+
+  // Converts a wide C string to a String using the UTF-8 encoding.
+  // NULL will be converted to "(null)".  If an error occurred during
+  // the conversion, "(failed to convert from wide string)" is
+  // returned.
+  static std::string ShowWideCString(const wchar_t* wide_c_str);
+
+  // Compares two wide C strings.  Returns true if and only if they have the
+  // same content.
+  //
+  // Unlike wcscmp(), this function can handle NULL argument(s).  A
+  // NULL C string is considered different to any non-NULL C string,
+  // including the empty string.
+  static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs);
+
+  // Compares two C strings, ignoring case.  Returns true if and only if
+  // they have the same content.
+  //
+  // Unlike strcasecmp(), this function can handle NULL argument(s).
+  // A NULL C string is considered different to any non-NULL C string,
+  // including the empty string.
+  static bool CaseInsensitiveCStringEquals(const char* lhs,
+                                           const char* rhs);
+
+  // Compares two wide C strings, ignoring case.  Returns true if and only if
+  // they have the same content.
+  //
+  // Unlike wcscasecmp(), this function can handle NULL argument(s).
+  // A NULL C string is considered different to any non-NULL wide C string,
+  // including the empty string.
+  // NB: The implementations on different platforms slightly differ.
+  // On windows, this method uses _wcsicmp which compares according to LC_CTYPE
+  // environment variable. On GNU platform this method uses wcscasecmp
+  // which compares according to LC_CTYPE category of the current locale.
+  // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
+  // current locale.
+  static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
+                                               const wchar_t* rhs);
+
+  // Returns true if and only if the given string ends with the given suffix,
+  // ignoring case. Any string is considered to end with an empty suffix.
+  static bool EndsWithCaseInsensitive(
+      const std::string& str, const std::string& suffix);
+
+  // Formats an int value as "%02d".
+  static std::string FormatIntWidth2(int value);  // "%02d" for width == 2
+
+  // Formats an int value to given width with leading zeros.
+  static std::string FormatIntWidthN(int value, int width);
+
+  // Formats an int value as "%X".
+  static std::string FormatHexInt(int value);
+
+  // Formats an int value as "%X".
+  static std::string FormatHexUInt32(uint32_t value);
+
+  // Formats a byte as "%02X".
+  static std::string FormatByte(unsigned char value);
+
+ private:
+  String();  // Not meant to be instantiated.
+};  // class String
+
+// Gets the content of the stringstream's buffer as an std::string.  Each '\0'
+// character in the buffer is replaced with "\\0".
+GTEST_API_ std::string StringStreamToString(::std::stringstream* stream);
+
+}  // namespace internal
+}  // namespace testing
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
diff --git a/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-type-util.h b/src/include/gromacs/external/googletest/googletest/include/gtest/internal/gtest-type-util.h
new file mode 100644 (file)
index 0000000..90910e9
--- /dev/null
@@ -0,0 +1,181 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Type utilities needed for implementing typed and type-parameterized
+// tests.
+
+#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
+
+#include "gtest/internal/gtest-port.h"
+
+// #ifdef __GNUC__ is too general here.  It is possible to use gcc without using
+// libstdc++ (which is where cxxabi.h comes from).
+# if GTEST_HAS_CXXABI_H_
+#  include <cxxabi.h>
+# elif defined(__HP_aCC)
+#  include <acxx_demangle.h>
+# endif  // GTEST_HASH_CXXABI_H_
+
+namespace testing {
+namespace internal {
+
+// Canonicalizes a given name with respect to the Standard C++ Library.
+// This handles removing the inline namespace within `std` that is
+// used by various standard libraries (e.g., `std::__1`).  Names outside
+// of namespace std are returned unmodified.
+inline std::string CanonicalizeForStdLibVersioning(std::string s) {
+  static const char prefix[] = "std::__";
+  if (s.compare(0, strlen(prefix), prefix) == 0) {
+    std::string::size_type end = s.find("::", strlen(prefix));
+    if (end != s.npos) {
+      // Erase everything between the initial `std` and the second `::`.
+      s.erase(strlen("std"), end - strlen("std"));
+    }
+  }
+  return s;
+}
+
+#if GTEST_HAS_RTTI
+// GetTypeName(const std::type_info&) returns a human-readable name of type T.
+inline std::string GetTypeName(const std::type_info& type) {
+  const char* const name = type.name();
+#if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC)
+  int status = 0;
+  // gcc's implementation of typeid(T).name() mangles the type name,
+  // so we have to demangle it.
+#if GTEST_HAS_CXXABI_H_
+  using abi::__cxa_demangle;
+#endif  // GTEST_HAS_CXXABI_H_
+  char* const readable_name = __cxa_demangle(name, nullptr, nullptr, &status);
+  const std::string name_str(status == 0 ? readable_name : name);
+  free(readable_name);
+  return CanonicalizeForStdLibVersioning(name_str);
+#else
+  return name;
+#endif  // GTEST_HAS_CXXABI_H_ || __HP_aCC
+}
+#endif  // GTEST_HAS_RTTI
+
+// GetTypeName<T>() returns a human-readable name of type T if and only if
+// RTTI is enabled, otherwise it returns a dummy type name.
+// NB: This function is also used in Google Mock, so don't move it inside of
+// the typed-test-only section below.
+template <typename T>
+std::string GetTypeName() {
+#if GTEST_HAS_RTTI
+  return GetTypeName(typeid(T));
+#else
+  return "<type>";
+#endif  // GTEST_HAS_RTTI
+}
+
+// A unique type indicating an empty node
+struct None {};
+
+# define GTEST_TEMPLATE_ template <typename T> class
+
+// The template "selector" struct TemplateSel<Tmpl> is used to
+// represent Tmpl, which must be a class template with one type
+// parameter, as a type.  TemplateSel<Tmpl>::Bind<T>::type is defined
+// as the type Tmpl<T>.  This allows us to actually instantiate the
+// template "selected" by TemplateSel<Tmpl>.
+//
+// This trick is necessary for simulating typedef for class templates,
+// which C++ doesn't support directly.
+template <GTEST_TEMPLATE_ Tmpl>
+struct TemplateSel {
+  template <typename T>
+  struct Bind {
+    typedef Tmpl<T> type;
+  };
+};
+
+# define GTEST_BIND_(TmplSel, T) \
+  TmplSel::template Bind<T>::type
+
+template <GTEST_TEMPLATE_ Head_, GTEST_TEMPLATE_... Tail_>
+struct Templates {
+  using Head = TemplateSel<Head_>;
+  using Tail = Templates<Tail_...>;
+};
+
+template <GTEST_TEMPLATE_ Head_>
+struct Templates<Head_> {
+  using Head = TemplateSel<Head_>;
+  using Tail = None;
+};
+
+// Tuple-like type lists
+template <typename Head_, typename... Tail_>
+struct Types {
+  using Head = Head_;
+  using Tail = Types<Tail_...>;
+};
+
+template <typename Head_>
+struct Types<Head_> {
+  using Head = Head_;
+  using Tail = None;
+};
+
+// Helper metafunctions to tell apart a single type from types
+// generated by ::testing::Types
+template <typename... Ts>
+struct ProxyTypeList {
+  using type = Types<Ts...>;
+};
+
+template <typename>
+struct is_proxy_type_list : std::false_type {};
+
+template <typename... Ts>
+struct is_proxy_type_list<ProxyTypeList<Ts...>> : std::true_type {};
+
+// Generator which conditionally creates type lists.
+// It recognizes if a requested type list should be created
+// and prevents creating a new type list nested within another one.
+template <typename T>
+struct GenerateTypeList {
+ private:
+  using proxy = typename std::conditional<is_proxy_type_list<T>::value, T,
+                                          ProxyTypeList<T>>::type;
+
+ public:
+  using type = typename proxy::type;
+};
+
+}  // namespace internal
+
+template <typename... Ts>
+using Types = internal::ProxyTypeList<Ts...>;
+
+}  // namespace testing
+
+#endif  // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
diff --git a/src/include/gromacs/external/googletest/googletest/samples/prime_tables.h b/src/include/gromacs/external/googletest/googletest/samples/prime_tables.h
new file mode 100644 (file)
index 0000000..3a10352
--- /dev/null
@@ -0,0 +1,126 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+// This provides interface PrimeTable that determines whether a number is a
+// prime and determines a next prime number. This interface is used
+// in Google Test samples demonstrating use of parameterized tests.
+
+#ifndef GOOGLETEST_SAMPLES_PRIME_TABLES_H_
+#define GOOGLETEST_SAMPLES_PRIME_TABLES_H_
+
+#include <algorithm>
+
+// The prime table interface.
+class PrimeTable {
+ public:
+  virtual ~PrimeTable() {}
+
+  // Returns true if and only if n is a prime number.
+  virtual bool IsPrime(int n) const = 0;
+
+  // Returns the smallest prime number greater than p; or returns -1
+  // if the next prime is beyond the capacity of the table.
+  virtual int GetNextPrime(int p) const = 0;
+};
+
+// Implementation #1 calculates the primes on-the-fly.
+class OnTheFlyPrimeTable : public PrimeTable {
+ public:
+  bool IsPrime(int n) const override {
+    if (n <= 1) return false;
+
+    for (int i = 2; i*i <= n; i++) {
+      // n is divisible by an integer other than 1 and itself.
+      if ((n % i) == 0) return false;
+    }
+
+    return true;
+  }
+
+  int GetNextPrime(int p) const override {
+    if (p < 0) return -1;
+
+    for (int n = p + 1;; n++) {
+      if (IsPrime(n)) return n;
+    }
+  }
+};
+
+// Implementation #2 pre-calculates the primes and stores the result
+// in an array.
+class PreCalculatedPrimeTable : public PrimeTable {
+ public:
+  // 'max' specifies the maximum number the prime table holds.
+  explicit PreCalculatedPrimeTable(int max)
+      : is_prime_size_(max + 1), is_prime_(new bool[max + 1]) {
+    CalculatePrimesUpTo(max);
+  }
+  ~PreCalculatedPrimeTable() override { delete[] is_prime_; }
+
+  bool IsPrime(int n) const override {
+    return 0 <= n && n < is_prime_size_ && is_prime_[n];
+  }
+
+  int GetNextPrime(int p) const override {
+    for (int n = p + 1; n < is_prime_size_; n++) {
+      if (is_prime_[n]) return n;
+    }
+
+    return -1;
+  }
+
+ private:
+  void CalculatePrimesUpTo(int max) {
+    ::std::fill(is_prime_, is_prime_ + is_prime_size_, true);
+    is_prime_[0] = is_prime_[1] = false;
+
+    // Checks every candidate for prime number (we know that 2 is the only even
+    // prime).
+    for (int i = 2; i*i <= max; i += i%2+1) {
+      if (!is_prime_[i]) continue;
+
+      // Marks all multiples of i (except i itself) as non-prime.
+      // We are starting here from i-th multiplier, because all smaller
+      // complex numbers were already marked.
+      for (int j = i*i; j <= max; j += i) {
+        is_prime_[j] = false;
+      }
+    }
+  }
+
+  const int is_prime_size_;
+  bool* const is_prime_;
+
+  // Disables compiler warning "assignment operator could not be generated."
+  void operator=(const PreCalculatedPrimeTable& rhs);
+};
+
+#endif  // GOOGLETEST_SAMPLES_PRIME_TABLES_H_
diff --git a/src/include/gromacs/external/googletest/googletest/samples/sample1.h b/src/include/gromacs/external/googletest/googletest/samples/sample1.h
new file mode 100644 (file)
index 0000000..ba392cf
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// A sample program demonstrating using Google C++ testing framework.
+
+#ifndef GOOGLETEST_SAMPLES_SAMPLE1_H_
+#define GOOGLETEST_SAMPLES_SAMPLE1_H_
+
+// Returns n! (the factorial of n).  For negative n, n! is defined to be 1.
+int Factorial(int n);
+
+// Returns true if and only if n is a prime number.
+bool IsPrime(int n);
+
+#endif  // GOOGLETEST_SAMPLES_SAMPLE1_H_
diff --git a/src/include/gromacs/external/googletest/googletest/samples/sample2.h b/src/include/gromacs/external/googletest/googletest/samples/sample2.h
new file mode 100644 (file)
index 0000000..0f98689
--- /dev/null
@@ -0,0 +1,80 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// A sample program demonstrating using Google C++ testing framework.
+
+#ifndef GOOGLETEST_SAMPLES_SAMPLE2_H_
+#define GOOGLETEST_SAMPLES_SAMPLE2_H_
+
+#include <string.h>
+
+
+// A simple string class.
+class MyString {
+ private:
+  const char* c_string_;
+  const MyString& operator=(const MyString& rhs);
+
+ public:
+  // Clones a 0-terminated C string, allocating memory using new.
+  static const char* CloneCString(const char* a_c_string);
+
+  ////////////////////////////////////////////////////////////
+  //
+  // C'tors
+
+  // The default c'tor constructs a NULL string.
+  MyString() : c_string_(nullptr) {}
+
+  // Constructs a MyString by cloning a 0-terminated C string.
+  explicit MyString(const char* a_c_string) : c_string_(nullptr) {
+    Set(a_c_string);
+  }
+
+  // Copy c'tor
+  MyString(const MyString& string) : c_string_(nullptr) {
+    Set(string.c_string_);
+  }
+
+  ////////////////////////////////////////////////////////////
+  //
+  // D'tor.  MyString is intended to be a final class, so the d'tor
+  // doesn't need to be virtual.
+  ~MyString() { delete[] c_string_; }
+
+  // Gets the 0-terminated C string this MyString object represents.
+  const char* c_string() const { return c_string_; }
+
+  size_t Length() const { return c_string_ == nullptr ? 0 : strlen(c_string_); }
+
+  // Sets the 0-terminated C string this MyString object represents.
+  void Set(const char* c_string);
+};
+
+#endif  // GOOGLETEST_SAMPLES_SAMPLE2_H_
diff --git a/src/include/gromacs/external/googletest/googletest/samples/sample3-inl.h b/src/include/gromacs/external/googletest/googletest/samples/sample3-inl.h
new file mode 100644 (file)
index 0000000..659e0f0
--- /dev/null
@@ -0,0 +1,172 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// A sample program demonstrating using Google C++ testing framework.
+
+#ifndef GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
+#define GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
+
+#include <stddef.h>
+
+
+// Queue is a simple queue implemented as a singled-linked list.
+//
+// The element type must support copy constructor.
+template <typename E>  // E is the element type
+class Queue;
+
+// QueueNode is a node in a Queue, which consists of an element of
+// type E and a pointer to the next node.
+template <typename E>  // E is the element type
+class QueueNode {
+  friend class Queue<E>;
+
+ public:
+  // Gets the element in this node.
+  const E& element() const { return element_; }
+
+  // Gets the next node in the queue.
+  QueueNode* next() { return next_; }
+  const QueueNode* next() const { return next_; }
+
+ private:
+  // Creates a node with a given element value.  The next pointer is
+  // set to NULL.
+  explicit QueueNode(const E& an_element)
+      : element_(an_element), next_(nullptr) {}
+
+  // We disable the default assignment operator and copy c'tor.
+  const QueueNode& operator = (const QueueNode&);
+  QueueNode(const QueueNode&);
+
+  E element_;
+  QueueNode* next_;
+};
+
+template <typename E>  // E is the element type.
+class Queue {
+ public:
+  // Creates an empty queue.
+  Queue() : head_(nullptr), last_(nullptr), size_(0) {}
+
+  // D'tor.  Clears the queue.
+  ~Queue() { Clear(); }
+
+  // Clears the queue.
+  void Clear() {
+    if (size_ > 0) {
+      // 1. Deletes every node.
+      QueueNode<E>* node = head_;
+      QueueNode<E>* next = node->next();
+      for (; ;) {
+        delete node;
+        node = next;
+        if (node == nullptr) break;
+        next = node->next();
+      }
+
+      // 2. Resets the member variables.
+      head_ = last_ = nullptr;
+      size_ = 0;
+    }
+  }
+
+  // Gets the number of elements.
+  size_t Size() const { return size_; }
+
+  // Gets the first element of the queue, or NULL if the queue is empty.
+  QueueNode<E>* Head() { return head_; }
+  const QueueNode<E>* Head() const { return head_; }
+
+  // Gets the last element of the queue, or NULL if the queue is empty.
+  QueueNode<E>* Last() { return last_; }
+  const QueueNode<E>* Last() const { return last_; }
+
+  // Adds an element to the end of the queue.  A copy of the element is
+  // created using the copy constructor, and then stored in the queue.
+  // Changes made to the element in the queue doesn't affect the source
+  // object, and vice versa.
+  void Enqueue(const E& element) {
+    QueueNode<E>* new_node = new QueueNode<E>(element);
+
+    if (size_ == 0) {
+      head_ = last_ = new_node;
+      size_ = 1;
+    } else {
+      last_->next_ = new_node;
+      last_ = new_node;
+      size_++;
+    }
+  }
+
+  // Removes the head of the queue and returns it.  Returns NULL if
+  // the queue is empty.
+  E* Dequeue() {
+    if (size_ == 0) {
+      return nullptr;
+    }
+
+    const QueueNode<E>* const old_head = head_;
+    head_ = head_->next_;
+    size_--;
+    if (size_ == 0) {
+      last_ = nullptr;
+    }
+
+    E* element = new E(old_head->element());
+    delete old_head;
+
+    return element;
+  }
+
+  // Applies a function/functor on each element of the queue, and
+  // returns the result in a new queue.  The original queue is not
+  // affected.
+  template <typename F>
+  Queue* Map(F function) const {
+    Queue* new_queue = new Queue();
+    for (const QueueNode<E>* node = head_; node != nullptr;
+         node = node->next_) {
+      new_queue->Enqueue(function(node->element()));
+    }
+
+    return new_queue;
+  }
+
+ private:
+  QueueNode<E>* head_;  // The first node of the queue.
+  QueueNode<E>* last_;  // The last node of the queue.
+  size_t size_;  // The number of elements in the queue.
+
+  // We disallow copying a queue.
+  Queue(const Queue&);
+  const Queue& operator = (const Queue&);
+};
+
+#endif  // GOOGLETEST_SAMPLES_SAMPLE3_INL_H_
diff --git a/src/include/gromacs/external/googletest/googletest/samples/sample4.h b/src/include/gromacs/external/googletest/googletest/samples/sample4.h
new file mode 100644 (file)
index 0000000..0c4ed92
--- /dev/null
@@ -0,0 +1,53 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// A sample program demonstrating using Google C++ testing framework.
+#ifndef GOOGLETEST_SAMPLES_SAMPLE4_H_
+#define GOOGLETEST_SAMPLES_SAMPLE4_H_
+
+// A simple monotonic counter.
+class Counter {
+ private:
+  int counter_;
+
+ public:
+  // Creates a counter that starts at 0.
+  Counter() : counter_(0) {}
+
+  // Returns the current counter value, and increments it.
+  int Increment();
+
+  // Returns the current counter value, and decrements it.
+  int Decrement();
+
+  // Prints the current counter value to STDOUT.
+  void Print() const;
+};
+
+#endif  // GOOGLETEST_SAMPLES_SAMPLE4_H_
diff --git a/src/include/gromacs/external/googletest/googletest/src/gtest-internal-inl.h b/src/include/gromacs/external/googletest/googletest/src/gtest-internal-inl.h
new file mode 100644 (file)
index 0000000..075b84c
--- /dev/null
@@ -0,0 +1,1204 @@
+// Copyright 2005, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Utility functions and classes used by the Google C++ testing framework.//
+// This file contains purely Google Test's internal implementation.  Please
+// DO NOT #INCLUDE IT IN A USER PROGRAM.
+
+#ifndef GOOGLETEST_SRC_GTEST_INTERNAL_INL_H_
+#define GOOGLETEST_SRC_GTEST_INTERNAL_INL_H_
+
+#ifndef _WIN32_WCE
+# include <errno.h>
+#endif  // !_WIN32_WCE
+#include <stddef.h>
+#include <stdlib.h>  // For strtoll/_strtoul64/malloc/free.
+#include <string.h>  // For memmove.
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gtest/internal/gtest-port.h"
+
+#if GTEST_CAN_STREAM_RESULTS_
+# include <arpa/inet.h>  // NOLINT
+# include <netdb.h>  // NOLINT
+#endif
+
+#if GTEST_OS_WINDOWS
+# include <windows.h>  // NOLINT
+#endif  // GTEST_OS_WINDOWS
+
+#include "gtest/gtest.h"
+#include "gtest/gtest-spi.h"
+
+GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
+/* class A needs to have dll-interface to be used by clients of class B */)
+
+// Declares the flags.
+//
+// We don't want the users to modify this flag in the code, but want
+// Google Test's own unit tests to be able to access it. Therefore we
+// declare it here as opposed to in gtest.h.
+GTEST_DECLARE_bool_(death_test_use_fork);
+
+namespace testing {
+namespace internal {
+
+// The value of GetTestTypeId() as seen from within the Google Test
+// library.  This is solely for testing GetTestTypeId().
+GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest;
+
+// A valid random seed must be in [1, kMaxRandomSeed].
+const int kMaxRandomSeed = 99999;
+
+// g_help_flag is true if and only if the --help flag or an equivalent form
+// is specified on the command line.
+GTEST_API_ extern bool g_help_flag;
+
+// Returns the current time in milliseconds.
+GTEST_API_ TimeInMillis GetTimeInMillis();
+
+// Returns true if and only if Google Test should use colors in the output.
+GTEST_API_ bool ShouldUseColor(bool stdout_is_tty);
+
+// Formats the given time in milliseconds as seconds.
+GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms);
+
+// Converts the given time in milliseconds to a date string in the ISO 8601
+// format, without the timezone information.  N.B.: due to the use the
+// non-reentrant localtime() function, this function is not thread safe.  Do
+// not use it in any code that can be called from multiple threads.
+GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms);
+
+// Parses a string for an Int32 flag, in the form of "--flag=value".
+//
+// On success, stores the value of the flag in *value, and returns
+// true.  On failure, returns false without changing *value.
+GTEST_API_ bool ParseFlag(const char* str, const char* flag, int32_t* value);
+
+// Returns a random seed in range [1, kMaxRandomSeed] based on the
+// given --gtest_random_seed flag value.
+inline int GetRandomSeedFromFlag(int32_t random_seed_flag) {
+  const unsigned int raw_seed = (random_seed_flag == 0) ?
+      static_cast<unsigned int>(GetTimeInMillis()) :
+      static_cast<unsigned int>(random_seed_flag);
+
+  // Normalizes the actual seed to range [1, kMaxRandomSeed] such that
+  // it's easy to type.
+  const int normalized_seed =
+      static_cast<int>((raw_seed - 1U) %
+                       static_cast<unsigned int>(kMaxRandomSeed)) + 1;
+  return normalized_seed;
+}
+
+// Returns the first valid random seed after 'seed'.  The behavior is
+// undefined if 'seed' is invalid.  The seed after kMaxRandomSeed is
+// considered to be 1.
+inline int GetNextRandomSeed(int seed) {
+  GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed)
+      << "Invalid random seed " << seed << " - must be in [1, "
+      << kMaxRandomSeed << "].";
+  const int next_seed = seed + 1;
+  return (next_seed > kMaxRandomSeed) ? 1 : next_seed;
+}
+
+// This class saves the values of all Google Test flags in its c'tor, and
+// restores them in its d'tor.
+class GTestFlagSaver {
+ public:
+  // The c'tor.
+  GTestFlagSaver() {
+    also_run_disabled_tests_ = GTEST_FLAG_GET(also_run_disabled_tests);
+    break_on_failure_ = GTEST_FLAG_GET(break_on_failure);
+    catch_exceptions_ = GTEST_FLAG_GET(catch_exceptions);
+    color_ = GTEST_FLAG_GET(color);
+    death_test_style_ = GTEST_FLAG_GET(death_test_style);
+    death_test_use_fork_ = GTEST_FLAG_GET(death_test_use_fork);
+    fail_fast_ = GTEST_FLAG_GET(fail_fast);
+    filter_ = GTEST_FLAG_GET(filter);
+    internal_run_death_test_ = GTEST_FLAG_GET(internal_run_death_test);
+    list_tests_ = GTEST_FLAG_GET(list_tests);
+    output_ = GTEST_FLAG_GET(output);
+    brief_ = GTEST_FLAG_GET(brief);
+    print_time_ = GTEST_FLAG_GET(print_time);
+    print_utf8_ = GTEST_FLAG_GET(print_utf8);
+    random_seed_ = GTEST_FLAG_GET(random_seed);
+    repeat_ = GTEST_FLAG_GET(repeat);
+    recreate_environments_when_repeating_ =
+        GTEST_FLAG_GET(recreate_environments_when_repeating);
+    shuffle_ = GTEST_FLAG_GET(shuffle);
+    stack_trace_depth_ = GTEST_FLAG_GET(stack_trace_depth);
+    stream_result_to_ = GTEST_FLAG_GET(stream_result_to);
+    throw_on_failure_ = GTEST_FLAG_GET(throw_on_failure);
+  }
+
+  // The d'tor is not virtual.  DO NOT INHERIT FROM THIS CLASS.
+  ~GTestFlagSaver() {
+    GTEST_FLAG_SET(also_run_disabled_tests, also_run_disabled_tests_);
+    GTEST_FLAG_SET(break_on_failure, break_on_failure_);
+    GTEST_FLAG_SET(catch_exceptions, catch_exceptions_);
+    GTEST_FLAG_SET(color, color_);
+    GTEST_FLAG_SET(death_test_style, death_test_style_);
+    GTEST_FLAG_SET(death_test_use_fork, death_test_use_fork_);
+    GTEST_FLAG_SET(filter, filter_);
+    GTEST_FLAG_SET(fail_fast, fail_fast_);
+    GTEST_FLAG_SET(internal_run_death_test, internal_run_death_test_);
+    GTEST_FLAG_SET(list_tests, list_tests_);
+    GTEST_FLAG_SET(output, output_);
+    GTEST_FLAG_SET(brief, brief_);
+    GTEST_FLAG_SET(print_time, print_time_);
+    GTEST_FLAG_SET(print_utf8, print_utf8_);
+    GTEST_FLAG_SET(random_seed, random_seed_);
+    GTEST_FLAG_SET(repeat, repeat_);
+    GTEST_FLAG_SET(recreate_environments_when_repeating,
+                   recreate_environments_when_repeating_);
+    GTEST_FLAG_SET(shuffle, shuffle_);
+    GTEST_FLAG_SET(stack_trace_depth, stack_trace_depth_);
+    GTEST_FLAG_SET(stream_result_to, stream_result_to_);
+    GTEST_FLAG_SET(throw_on_failure, throw_on_failure_);
+  }
+
+ private:
+  // Fields for saving the original values of flags.
+  bool also_run_disabled_tests_;
+  bool break_on_failure_;
+  bool catch_exceptions_;
+  std::string color_;
+  std::string death_test_style_;
+  bool death_test_use_fork_;
+  bool fail_fast_;
+  std::string filter_;
+  std::string internal_run_death_test_;
+  bool list_tests_;
+  std::string output_;
+  bool brief_;
+  bool print_time_;
+  bool print_utf8_;
+  int32_t random_seed_;
+  int32_t repeat_;
+  bool recreate_environments_when_repeating_;
+  bool shuffle_;
+  int32_t stack_trace_depth_;
+  std::string stream_result_to_;
+  bool throw_on_failure_;
+} GTEST_ATTRIBUTE_UNUSED_;
+
+// Converts a Unicode code point to a narrow string in UTF-8 encoding.
+// code_point parameter is of type UInt32 because wchar_t may not be
+// wide enough to contain a code point.
+// If the code_point is not a valid Unicode code point
+// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted
+// to "(Invalid Unicode 0xXXXXXXXX)".
+GTEST_API_ std::string CodePointToUtf8(uint32_t code_point);
+
+// Converts a wide string to a narrow string in UTF-8 encoding.
+// The wide string is assumed to have the following encoding:
+//   UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin)
+//   UTF-32 if sizeof(wchar_t) == 4 (on Linux)
+// Parameter str points to a null-terminated wide string.
+// Parameter num_chars may additionally limit the number
+// of wchar_t characters processed. -1 is used when the entire string
+// should be processed.
+// If the string contains code points that are not valid Unicode code points
+// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output
+// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding
+// and contains invalid UTF-16 surrogate pairs, values in those pairs
+// will be encoded as individual Unicode characters from Basic Normal Plane.
+GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars);
+
+// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file
+// if the variable is present. If a file already exists at this location, this
+// function will write over it. If the variable is present, but the file cannot
+// be created, prints an error and exits.
+void WriteToShardStatusFileIfNeeded();
+
+// Checks whether sharding is enabled by examining the relevant
+// environment variable values. If the variables are present,
+// but inconsistent (e.g., shard_index >= total_shards), prints
+// an error and exits. If in_subprocess_for_death_test, sharding is
+// disabled because it must only be applied to the original test
+// process. Otherwise, we could filter out death tests we intended to execute.
+GTEST_API_ bool ShouldShard(const char* total_shards_str,
+                            const char* shard_index_str,
+                            bool in_subprocess_for_death_test);
+
+// Parses the environment variable var as a 32-bit integer. If it is unset,
+// returns default_val. If it is not a 32-bit integer, prints an error and
+// and aborts.
+GTEST_API_ int32_t Int32FromEnvOrDie(const char* env_var, int32_t default_val);
+
+// Given the total number of shards, the shard index, and the test id,
+// returns true if and only if the test should be run on this shard. The test id
+// is some arbitrary but unique non-negative integer assigned to each test
+// method. Assumes that 0 <= shard_index < total_shards.
+GTEST_API_ bool ShouldRunTestOnShard(
+    int total_shards, int shard_index, int test_id);
+
+// STL container utilities.
+
+// Returns the number of elements in the given container that satisfy
+// the given predicate.
+template <class Container, typename Predicate>
+inline int CountIf(const Container& c, Predicate predicate) {
+  // Implemented as an explicit loop since std::count_if() in libCstd on
+  // Solaris has a non-standard signature.
+  int count = 0;
+  for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) {
+    if (predicate(*it))
+      ++count;
+  }
+  return count;
+}
+
+// Applies a function/functor to each element in the container.
+template <class Container, typename Functor>
+void ForEach(const Container& c, Functor functor) {
+  std::for_each(c.begin(), c.end(), functor);
+}
+
+// Returns the i-th element of the vector, or default_value if i is not
+// in range [0, v.size()).
+template <typename E>
+inline E GetElementOr(const std::vector<E>& v, int i, E default_value) {
+  return (i < 0 || i >= static_cast<int>(v.size())) ? default_value
+                                                    : v[static_cast<size_t>(i)];
+}
+
+// Performs an in-place shuffle of a range of the vector's elements.
+// 'begin' and 'end' are element indices as an STL-style range;
+// i.e. [begin, end) are shuffled, where 'end' == size() means to
+// shuffle to the end of the vector.
+template <typename E>
+void ShuffleRange(internal::Random* random, int begin, int end,
+                  std::vector<E>* v) {
+  const int size = static_cast<int>(v->size());
+  GTEST_CHECK_(0 <= begin && begin <= size)
+      << "Invalid shuffle range start " << begin << ": must be in range [0, "
+      << size << "].";
+  GTEST_CHECK_(begin <= end && end <= size)
+      << "Invalid shuffle range finish " << end << ": must be in range ["
+      << begin << ", " << size << "].";
+
+  // Fisher-Yates shuffle, from
+  // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
+  for (int range_width = end - begin; range_width >= 2; range_width--) {
+    const int last_in_range = begin + range_width - 1;
+    const int selected =
+        begin +
+        static_cast<int>(random->Generate(static_cast<uint32_t>(range_width)));
+    std::swap((*v)[static_cast<size_t>(selected)],
+              (*v)[static_cast<size_t>(last_in_range)]);
+  }
+}
+
+// Performs an in-place shuffle of the vector's elements.
+template <typename E>
+inline void Shuffle(internal::Random* random, std::vector<E>* v) {
+  ShuffleRange(random, 0, static_cast<int>(v->size()), v);
+}
+
+// A function for deleting an object.  Handy for being used as a
+// functor.
+template <typename T>
+static void Delete(T* x) {
+  delete x;
+}
+
+// A predicate that checks the key of a TestProperty against a known key.
+//
+// TestPropertyKeyIs is copyable.
+class TestPropertyKeyIs {
+ public:
+  // Constructor.
+  //
+  // TestPropertyKeyIs has NO default constructor.
+  explicit TestPropertyKeyIs(const std::string& key) : key_(key) {}
+
+  // Returns true if and only if the test name of test property matches on key_.
+  bool operator()(const TestProperty& test_property) const {
+    return test_property.key() == key_;
+  }
+
+ private:
+  std::string key_;
+};
+
+// Class UnitTestOptions.
+//
+// This class contains functions for processing options the user
+// specifies when running the tests.  It has only static members.
+//
+// In most cases, the user can specify an option using either an
+// environment variable or a command line flag.  E.g. you can set the
+// test filter using either GTEST_FILTER or --gtest_filter.  If both
+// the variable and the flag are present, the latter overrides the
+// former.
+class GTEST_API_ UnitTestOptions {
+ public:
+  // Functions for processing the gtest_output flag.
+
+  // Returns the output format, or "" for normal printed output.
+  static std::string GetOutputFormat();
+
+  // Returns the absolute path of the requested output file, or the
+  // default (test_detail.xml in the original working directory) if
+  // none was explicitly specified.
+  static std::string GetAbsolutePathToOutputFile();
+
+  // Functions for processing the gtest_filter flag.
+
+  // Returns true if and only if the user-specified filter matches the test
+  // suite name and the test name.
+  static bool FilterMatchesTest(const std::string& test_suite_name,
+                                const std::string& test_name);
+
+#if GTEST_OS_WINDOWS
+  // Function for supporting the gtest_catch_exception flag.
+
+  // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the
+  // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise.
+  // This function is useful as an __except condition.
+  static int GTestShouldProcessSEH(DWORD exception_code);
+#endif  // GTEST_OS_WINDOWS
+
+  // Returns true if "name" matches the ':' separated list of glob-style
+  // filters in "filter".
+  static bool MatchesFilter(const std::string& name, const char* filter);
+};
+
+// Returns the current application's name, removing directory path if that
+// is present.  Used by UnitTestOptions::GetOutputFile.
+GTEST_API_ FilePath GetCurrentExecutableName();
+
+// The role interface for getting the OS stack trace as a string.
+class OsStackTraceGetterInterface {
+ public:
+  OsStackTraceGetterInterface() {}
+  virtual ~OsStackTraceGetterInterface() {}
+
+  // Returns the current OS stack trace as an std::string.  Parameters:
+  //
+  //   max_depth  - the maximum number of stack frames to be included
+  //                in the trace.
+  //   skip_count - the number of top frames to be skipped; doesn't count
+  //                against max_depth.
+  virtual std::string CurrentStackTrace(int max_depth, int skip_count) = 0;
+
+  // UponLeavingGTest() should be called immediately before Google Test calls
+  // user code. It saves some information about the current stack that
+  // CurrentStackTrace() will use to find and hide Google Test stack frames.
+  virtual void UponLeavingGTest() = 0;
+
+  // This string is inserted in place of stack frames that are part of
+  // Google Test's implementation.
+  static const char* const kElidedFramesMarker;
+
+ private:
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface);
+};
+
+// A working implementation of the OsStackTraceGetterInterface interface.
+class OsStackTraceGetter : public OsStackTraceGetterInterface {
+ public:
+  OsStackTraceGetter() {}
+
+  std::string CurrentStackTrace(int max_depth, int skip_count) override;
+  void UponLeavingGTest() override;
+
+ private:
+#if GTEST_HAS_ABSL
+  Mutex mutex_;  // Protects all internal state.
+
+  // We save the stack frame below the frame that calls user code.
+  // We do this because the address of the frame immediately below
+  // the user code changes between the call to UponLeavingGTest()
+  // and any calls to the stack trace code from within the user code.
+  void* caller_frame_ = nullptr;
+#endif  // GTEST_HAS_ABSL
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter);
+};
+
+// Information about a Google Test trace point.
+struct TraceInfo {
+  const char* file;
+  int line;
+  std::string message;
+};
+
+// This is the default global test part result reporter used in UnitTestImpl.
+// This class should only be used by UnitTestImpl.
+class DefaultGlobalTestPartResultReporter
+  : public TestPartResultReporterInterface {
+ public:
+  explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test);
+  // Implements the TestPartResultReporterInterface. Reports the test part
+  // result in the current test.
+  void ReportTestPartResult(const TestPartResult& result) override;
+
+ private:
+  UnitTestImpl* const unit_test_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter);
+};
+
+// This is the default per thread test part result reporter used in
+// UnitTestImpl. This class should only be used by UnitTestImpl.
+class DefaultPerThreadTestPartResultReporter
+    : public TestPartResultReporterInterface {
+ public:
+  explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test);
+  // Implements the TestPartResultReporterInterface. The implementation just
+  // delegates to the current global test part result reporter of *unit_test_.
+  void ReportTestPartResult(const TestPartResult& result) override;
+
+ private:
+  UnitTestImpl* const unit_test_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter);
+};
+
+// The private implementation of the UnitTest class.  We don't protect
+// the methods under a mutex, as this class is not accessible by a
+// user and the UnitTest class that delegates work to this class does
+// proper locking.
+class GTEST_API_ UnitTestImpl {
+ public:
+  explicit UnitTestImpl(UnitTest* parent);
+  virtual ~UnitTestImpl();
+
+  // There are two different ways to register your own TestPartResultReporter.
+  // You can register your own repoter to listen either only for test results
+  // from the current thread or for results from all threads.
+  // By default, each per-thread test result repoter just passes a new
+  // TestPartResult to the global test result reporter, which registers the
+  // test part result for the currently running test.
+
+  // Returns the global test part result reporter.
+  TestPartResultReporterInterface* GetGlobalTestPartResultReporter();
+
+  // Sets the global test part result reporter.
+  void SetGlobalTestPartResultReporter(
+      TestPartResultReporterInterface* reporter);
+
+  // Returns the test part result reporter for the current thread.
+  TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread();
+
+  // Sets the test part result reporter for the current thread.
+  void SetTestPartResultReporterForCurrentThread(
+      TestPartResultReporterInterface* reporter);
+
+  // Gets the number of successful test suites.
+  int successful_test_suite_count() const;
+
+  // Gets the number of failed test suites.
+  int failed_test_suite_count() const;
+
+  // Gets the number of all test suites.
+  int total_test_suite_count() const;
+
+  // Gets the number of all test suites that contain at least one test
+  // that should run.
+  int test_suite_to_run_count() const;
+
+  // Gets the number of successful tests.
+  int successful_test_count() const;
+
+  // Gets the number of skipped tests.
+  int skipped_test_count() const;
+
+  // Gets the number of failed tests.
+  int failed_test_count() const;
+
+  // Gets the number of disabled tests that will be reported in the XML report.
+  int reportable_disabled_test_count() const;
+
+  // Gets the number of disabled tests.
+  int disabled_test_count() const;
+
+  // Gets the number of tests to be printed in the XML report.
+  int reportable_test_count() const;
+
+  // Gets the number of all tests.
+  int total_test_count() const;
+
+  // Gets the number of tests that should run.
+  int test_to_run_count() const;
+
+  // Gets the time of the test program start, in ms from the start of the
+  // UNIX epoch.
+  TimeInMillis start_timestamp() const { return start_timestamp_; }
+
+  // Gets the elapsed time, in milliseconds.
+  TimeInMillis elapsed_time() const { return elapsed_time_; }
+
+  // Returns true if and only if the unit test passed (i.e. all test suites
+  // passed).
+  bool Passed() const { return !Failed(); }
+
+  // Returns true if and only if the unit test failed (i.e. some test suite
+  // failed or something outside of all tests failed).
+  bool Failed() const {
+    return failed_test_suite_count() > 0 || ad_hoc_test_result()->Failed();
+  }
+
+  // Gets the i-th test suite among all the test suites. i can range from 0 to
+  // total_test_suite_count() - 1. If i is not in that range, returns NULL.
+  const TestSuite* GetTestSuite(int i) const {
+    const int index = GetElementOr(test_suite_indices_, i, -1);
+    return index < 0 ? nullptr : test_suites_[static_cast<size_t>(i)];
+  }
+
+  //  Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  const TestCase* GetTestCase(int i) const { return GetTestSuite(i); }
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  // Gets the i-th test suite among all the test suites. i can range from 0 to
+  // total_test_suite_count() - 1. If i is not in that range, returns NULL.
+  TestSuite* GetMutableSuiteCase(int i) {
+    const int index = GetElementOr(test_suite_indices_, i, -1);
+    return index < 0 ? nullptr : test_suites_[static_cast<size_t>(index)];
+  }
+
+  // Provides access to the event listener list.
+  TestEventListeners* listeners() { return &listeners_; }
+
+  // Returns the TestResult for the test that's currently running, or
+  // the TestResult for the ad hoc test if no test is running.
+  TestResult* current_test_result();
+
+  // Returns the TestResult for the ad hoc test.
+  const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; }
+
+  // Sets the OS stack trace getter.
+  //
+  // Does nothing if the input and the current OS stack trace getter
+  // are the same; otherwise, deletes the old getter and makes the
+  // input the current getter.
+  void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter);
+
+  // Returns the current OS stack trace getter if it is not NULL;
+  // otherwise, creates an OsStackTraceGetter, makes it the current
+  // getter, and returns it.
+  OsStackTraceGetterInterface* os_stack_trace_getter();
+
+  // Returns the current OS stack trace as an std::string.
+  //
+  // The maximum number of stack frames to be included is specified by
+  // the gtest_stack_trace_depth flag.  The skip_count parameter
+  // specifies the number of top frames to be skipped, which doesn't
+  // count against the number of frames to be included.
+  //
+  // For example, if Foo() calls Bar(), which in turn calls
+  // CurrentOsStackTraceExceptTop(1), Foo() will be included in the
+  // trace but Bar() and CurrentOsStackTraceExceptTop() won't.
+  std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_;
+
+  // Finds and returns a TestSuite with the given name.  If one doesn't
+  // exist, creates one and returns it.
+  //
+  // Arguments:
+  //
+  //   test_suite_name: name of the test suite
+  //   type_param:      the name of the test's type parameter, or NULL if
+  //                    this is not a typed or a type-parameterized test.
+  //   set_up_tc:       pointer to the function that sets up the test suite
+  //   tear_down_tc:    pointer to the function that tears down the test suite
+  TestSuite* GetTestSuite(const char* test_suite_name, const char* type_param,
+                          internal::SetUpTestSuiteFunc set_up_tc,
+                          internal::TearDownTestSuiteFunc tear_down_tc);
+
+//  Legacy API is deprecated but still available
+#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+  TestCase* GetTestCase(const char* test_case_name, const char* type_param,
+                        internal::SetUpTestSuiteFunc set_up_tc,
+                        internal::TearDownTestSuiteFunc tear_down_tc) {
+    return GetTestSuite(test_case_name, type_param, set_up_tc, tear_down_tc);
+  }
+#endif  //  GTEST_REMOVE_LEGACY_TEST_CASEAPI_
+
+  // Adds a TestInfo to the unit test.
+  //
+  // Arguments:
+  //
+  //   set_up_tc:    pointer to the function that sets up the test suite
+  //   tear_down_tc: pointer to the function that tears down the test suite
+  //   test_info:    the TestInfo object
+  void AddTestInfo(internal::SetUpTestSuiteFunc set_up_tc,
+                   internal::TearDownTestSuiteFunc tear_down_tc,
+                   TestInfo* test_info) {
+#if GTEST_HAS_DEATH_TEST
+    // In order to support thread-safe death tests, we need to
+    // remember the original working directory when the test program
+    // was first invoked.  We cannot do this in RUN_ALL_TESTS(), as
+    // the user may have changed the current directory before calling
+    // RUN_ALL_TESTS().  Therefore we capture the current directory in
+    // AddTestInfo(), which is called to register a TEST or TEST_F
+    // before main() is reached.
+    if (original_working_dir_.IsEmpty()) {
+      original_working_dir_.Set(FilePath::GetCurrentDir());
+      GTEST_CHECK_(!original_working_dir_.IsEmpty())
+          << "Failed to get the current working directory.";
+    }
+#endif  // GTEST_HAS_DEATH_TEST
+
+    GetTestSuite(test_info->test_suite_name(), test_info->type_param(),
+                 set_up_tc, tear_down_tc)
+        ->AddTestInfo(test_info);
+  }
+
+  // Returns ParameterizedTestSuiteRegistry object used to keep track of
+  // value-parameterized tests and instantiate and register them.
+  internal::ParameterizedTestSuiteRegistry& parameterized_test_registry() {
+    return parameterized_test_registry_;
+  }
+
+  std::set<std::string>* ignored_parameterized_test_suites() {
+    return &ignored_parameterized_test_suites_;
+  }
+
+  // Returns TypeParameterizedTestSuiteRegistry object used to keep track of
+  // type-parameterized tests and instantiations of them.
+  internal::TypeParameterizedTestSuiteRegistry&
+  type_parameterized_test_registry() {
+    return type_parameterized_test_registry_;
+  }
+
+  // Sets the TestSuite object for the test that's currently running.
+  void set_current_test_suite(TestSuite* a_current_test_suite) {
+    current_test_suite_ = a_current_test_suite;
+  }
+
+  // Sets the TestInfo object for the test that's currently running.  If
+  // current_test_info is NULL, the assertion results will be stored in
+  // ad_hoc_test_result_.
+  void set_current_test_info(TestInfo* a_current_test_info) {
+    current_test_info_ = a_current_test_info;
+  }
+
+  // Registers all parameterized tests defined using TEST_P and
+  // INSTANTIATE_TEST_SUITE_P, creating regular tests for each test/parameter
+  // combination. This method can be called more then once; it has guards
+  // protecting from registering the tests more then once.  If
+  // value-parameterized tests are disabled, RegisterParameterizedTests is
+  // present but does nothing.
+  void RegisterParameterizedTests();
+
+  // Runs all tests in this UnitTest object, prints the result, and
+  // returns true if all tests are successful.  If any exception is
+  // thrown during a test, this test is considered to be failed, but
+  // the rest of the tests will still be run.
+  bool RunAllTests();
+
+  // Clears the results of all tests, except the ad hoc tests.
+  void ClearNonAdHocTestResult() {
+    ForEach(test_suites_, TestSuite::ClearTestSuiteResult);
+  }
+
+  // Clears the results of ad-hoc test assertions.
+  void ClearAdHocTestResult() {
+    ad_hoc_test_result_.Clear();
+  }
+
+  // Adds a TestProperty to the current TestResult object when invoked in a
+  // context of a test or a test suite, or to the global property set. If the
+  // result already contains a property with the same key, the value will be
+  // updated.
+  void RecordProperty(const TestProperty& test_property);
+
+  enum ReactionToSharding {
+    HONOR_SHARDING_PROTOCOL,
+    IGNORE_SHARDING_PROTOCOL
+  };
+
+  // Matches the full name of each test against the user-specified
+  // filter to decide whether the test should run, then records the
+  // result in each TestSuite and TestInfo object.
+  // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests
+  // based on sharding variables in the environment.
+  // Returns the number of tests that should run.
+  int FilterTests(ReactionToSharding shard_tests);
+
+  // Prints the names of the tests matching the user-specified filter flag.
+  void ListTestsMatchingFilter();
+
+  const TestSuite* current_test_suite() const { return current_test_suite_; }
+  TestInfo* current_test_info() { return current_test_info_; }
+  const TestInfo* current_test_info() const { return current_test_info_; }
+
+  // Returns the vector of environments that need to be set-up/torn-down
+  // before/after the tests are run.
+  std::vector<Environment*>& environments() { return environments_; }
+
+  // Getters for the per-thread Google Test trace stack.
+  std::vector<TraceInfo>& gtest_trace_stack() {
+    return *(gtest_trace_stack_.pointer());
+  }
+  const std::vector<TraceInfo>& gtest_trace_stack() const {
+    return gtest_trace_stack_.get();
+  }
+
+#if GTEST_HAS_DEATH_TEST
+  void InitDeathTestSubprocessControlInfo() {
+    internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag());
+  }
+  // Returns a pointer to the parsed --gtest_internal_run_death_test
+  // flag, or NULL if that flag was not specified.
+  // This information is useful only in a death test child process.
+  // Must not be called before a call to InitGoogleTest.
+  const InternalRunDeathTestFlag* internal_run_death_test_flag() const {
+    return internal_run_death_test_flag_.get();
+  }
+
+  // Returns a pointer to the current death test factory.
+  internal::DeathTestFactory* death_test_factory() {
+    return death_test_factory_.get();
+  }
+
+  void SuppressTestEventsIfInSubprocess();
+
+  friend class ReplaceDeathTestFactory;
+#endif  // GTEST_HAS_DEATH_TEST
+
+  // Initializes the event listener performing XML output as specified by
+  // UnitTestOptions. Must not be called before InitGoogleTest.
+  void ConfigureXmlOutput();
+
+#if GTEST_CAN_STREAM_RESULTS_
+  // Initializes the event listener for streaming test results to a socket.
+  // Must not be called before InitGoogleTest.
+  void ConfigureStreamingOutput();
+#endif
+
+  // Performs initialization dependent upon flag values obtained in
+  // ParseGoogleTestFlagsOnly.  Is called from InitGoogleTest after the call to
+  // ParseGoogleTestFlagsOnly.  In case a user neglects to call InitGoogleTest
+  // this function is also called from RunAllTests.  Since this function can be
+  // called more than once, it has to be idempotent.
+  void PostFlagParsingInit();
+
+  // Gets the random seed used at the start of the current test iteration.
+  int random_seed() const { return random_seed_; }
+
+  // Gets the random number generator.
+  internal::Random* random() { return &random_; }
+
+  // Shuffles all test suites, and the tests within each test suite,
+  // making sure that death tests are still run first.
+  void ShuffleTests();
+
+  // Restores the test suites and tests to their order before the first shuffle.
+  void UnshuffleTests();
+
+  // Returns the value of GTEST_FLAG(catch_exceptions) at the moment
+  // UnitTest::Run() starts.
+  bool catch_exceptions() const { return catch_exceptions_; }
+
+ private:
+  friend class ::testing::UnitTest;
+
+  // Used by UnitTest::Run() to capture the state of
+  // GTEST_FLAG(catch_exceptions) at the moment it starts.
+  void set_catch_exceptions(bool value) { catch_exceptions_ = value; }
+
+  // The UnitTest object that owns this implementation object.
+  UnitTest* const parent_;
+
+  // The working directory when the first TEST() or TEST_F() was
+  // executed.
+  internal::FilePath original_working_dir_;
+
+  // The default test part result reporters.
+  DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_;
+  DefaultPerThreadTestPartResultReporter
+      default_per_thread_test_part_result_reporter_;
+
+  // Points to (but doesn't own) the global test part result reporter.
+  TestPartResultReporterInterface* global_test_part_result_repoter_;
+
+  // Protects read and write access to global_test_part_result_reporter_.
+  internal::Mutex global_test_part_result_reporter_mutex_;
+
+  // Points to (but doesn't own) the per-thread test part result reporter.
+  internal::ThreadLocal<TestPartResultReporterInterface*>
+      per_thread_test_part_result_reporter_;
+
+  // The vector of environments that need to be set-up/torn-down
+  // before/after the tests are run.
+  std::vector<Environment*> environments_;
+
+  // The vector of TestSuites in their original order.  It owns the
+  // elements in the vector.
+  std::vector<TestSuite*> test_suites_;
+
+  // Provides a level of indirection for the test suite list to allow
+  // easy shuffling and restoring the test suite order.  The i-th
+  // element of this vector is the index of the i-th test suite in the
+  // shuffled order.
+  std::vector<int> test_suite_indices_;
+
+  // ParameterizedTestRegistry object used to register value-parameterized
+  // tests.
+  internal::ParameterizedTestSuiteRegistry parameterized_test_registry_;
+  internal::TypeParameterizedTestSuiteRegistry
+      type_parameterized_test_registry_;
+
+  // The set holding the name of parameterized
+  // test suites that may go uninstantiated.
+  std::set<std::string> ignored_parameterized_test_suites_;
+
+  // Indicates whether RegisterParameterizedTests() has been called already.
+  bool parameterized_tests_registered_;
+
+  // Index of the last death test suite registered.  Initially -1.
+  int last_death_test_suite_;
+
+  // This points to the TestSuite for the currently running test.  It
+  // changes as Google Test goes through one test suite after another.
+  // When no test is running, this is set to NULL and Google Test
+  // stores assertion results in ad_hoc_test_result_.  Initially NULL.
+  TestSuite* current_test_suite_;
+
+  // This points to the TestInfo for the currently running test.  It
+  // changes as Google Test goes through one test after another.  When
+  // no test is running, this is set to NULL and Google Test stores
+  // assertion results in ad_hoc_test_result_.  Initially NULL.
+  TestInfo* current_test_info_;
+
+  // Normally, a user only writes assertions inside a TEST or TEST_F,
+  // or inside a function called by a TEST or TEST_F.  Since Google
+  // Test keeps track of which test is current running, it can
+  // associate such an assertion with the test it belongs to.
+  //
+  // If an assertion is encountered when no TEST or TEST_F is running,
+  // Google Test attributes the assertion result to an imaginary "ad hoc"
+  // test, and records the result in ad_hoc_test_result_.
+  TestResult ad_hoc_test_result_;
+
+  // The list of event listeners that can be used to track events inside
+  // Google Test.
+  TestEventListeners listeners_;
+
+  // The OS stack trace getter.  Will be deleted when the UnitTest
+  // object is destructed.  By default, an OsStackTraceGetter is used,
+  // but the user can set this field to use a custom getter if that is
+  // desired.
+  OsStackTraceGetterInterface* os_stack_trace_getter_;
+
+  // True if and only if PostFlagParsingInit() has been called.
+  bool post_flag_parse_init_performed_;
+
+  // The random number seed used at the beginning of the test run.
+  int random_seed_;
+
+  // Our random number generator.
+  internal::Random random_;
+
+  // The time of the test program start, in ms from the start of the
+  // UNIX epoch.
+  TimeInMillis start_timestamp_;
+
+  // How long the test took to run, in milliseconds.
+  TimeInMillis elapsed_time_;
+
+#if GTEST_HAS_DEATH_TEST
+  // The decomposed components of the gtest_internal_run_death_test flag,
+  // parsed when RUN_ALL_TESTS is called.
+  std::unique_ptr<InternalRunDeathTestFlag> internal_run_death_test_flag_;
+  std::unique_ptr<internal::DeathTestFactory> death_test_factory_;
+#endif  // GTEST_HAS_DEATH_TEST
+
+  // A per-thread stack of traces created by the SCOPED_TRACE() macro.
+  internal::ThreadLocal<std::vector<TraceInfo> > gtest_trace_stack_;
+
+  // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests()
+  // starts.
+  bool catch_exceptions_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl);
+};  // class UnitTestImpl
+
+// Convenience function for accessing the global UnitTest
+// implementation object.
+inline UnitTestImpl* GetUnitTestImpl() {
+  return UnitTest::GetInstance()->impl();
+}
+
+#if GTEST_USES_SIMPLE_RE
+
+// Internal helper functions for implementing the simple regular
+// expression matcher.
+GTEST_API_ bool IsInSet(char ch, const char* str);
+GTEST_API_ bool IsAsciiDigit(char ch);
+GTEST_API_ bool IsAsciiPunct(char ch);
+GTEST_API_ bool IsRepeat(char ch);
+GTEST_API_ bool IsAsciiWhiteSpace(char ch);
+GTEST_API_ bool IsAsciiWordChar(char ch);
+GTEST_API_ bool IsValidEscape(char ch);
+GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch);
+GTEST_API_ bool ValidateRegex(const char* regex);
+GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str);
+GTEST_API_ bool MatchRepetitionAndRegexAtHead(
+    bool escaped, char ch, char repeat, const char* regex, const char* str);
+GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str);
+
+#endif  // GTEST_USES_SIMPLE_RE
+
+// Parses the command line for Google Test flags, without initializing
+// other parts of Google Test.
+GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv);
+GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv);
+
+#if GTEST_HAS_DEATH_TEST
+
+// Returns the message describing the last system error, regardless of the
+// platform.
+GTEST_API_ std::string GetLastErrnoDescription();
+
+// Attempts to parse a string into a positive integer pointed to by the
+// number parameter.  Returns true if that is possible.
+// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use
+// it here.
+template <typename Integer>
+bool ParseNaturalNumber(const ::std::string& str, Integer* number) {
+  // Fail fast if the given string does not begin with a digit;
+  // this bypasses strtoXXX's "optional leading whitespace and plus
+  // or minus sign" semantics, which are undesirable here.
+  if (str.empty() || !IsDigit(str[0])) {
+    return false;
+  }
+  errno = 0;
+
+  char* end;
+  // BiggestConvertible is the largest integer type that system-provided
+  // string-to-number conversion routines can return.
+  using BiggestConvertible = unsigned long long;  // NOLINT
+
+  const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10);  // NOLINT
+  const bool parse_success = *end == '\0' && errno == 0;
+
+  GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed));
+
+  const Integer result = static_cast<Integer>(parsed);
+  if (parse_success && static_cast<BiggestConvertible>(result) == parsed) {
+    *number = result;
+    return true;
+  }
+  return false;
+}
+#endif  // GTEST_HAS_DEATH_TEST
+
+// TestResult contains some private methods that should be hidden from
+// Google Test user but are required for testing. This class allow our tests
+// to access them.
+//
+// This class is supplied only for the purpose of testing Google Test's own
+// constructs. Do not use it in user tests, either directly or indirectly.
+class TestResultAccessor {
+ public:
+  static void RecordProperty(TestResult* test_result,
+                             const std::string& xml_element,
+                             const TestProperty& property) {
+    test_result->RecordProperty(xml_element, property);
+  }
+
+  static void ClearTestPartResults(TestResult* test_result) {
+    test_result->ClearTestPartResults();
+  }
+
+  static const std::vector<testing::TestPartResult>& test_part_results(
+      const TestResult& test_result) {
+    return test_result.test_part_results();
+  }
+};
+
+#if GTEST_CAN_STREAM_RESULTS_
+
+// Streams test results to the given port on the given host machine.
+class StreamingListener : public EmptyTestEventListener {
+ public:
+  // Abstract base class for writing strings to a socket.
+  class AbstractSocketWriter {
+   public:
+    virtual ~AbstractSocketWriter() {}
+
+    // Sends a string to the socket.
+    virtual void Send(const std::string& message) = 0;
+
+    // Closes the socket.
+    virtual void CloseConnection() {}
+
+    // Sends a string and a newline to the socket.
+    void SendLn(const std::string& message) { Send(message + "\n"); }
+  };
+
+  // Concrete class for actually writing strings to a socket.
+  class SocketWriter : public AbstractSocketWriter {
+   public:
+    SocketWriter(const std::string& host, const std::string& port)
+        : sockfd_(-1), host_name_(host), port_num_(port) {
+      MakeConnection();
+    }
+
+    ~SocketWriter() override {
+      if (sockfd_ != -1)
+        CloseConnection();
+    }
+
+    // Sends a string to the socket.
+    void Send(const std::string& message) override {
+      GTEST_CHECK_(sockfd_ != -1)
+          << "Send() can be called only when there is a connection.";
+
+      const auto len = static_cast<size_t>(message.length());
+      if (write(sockfd_, message.c_str(), len) != static_cast<ssize_t>(len)) {
+        GTEST_LOG_(WARNING)
+            << "stream_result_to: failed to stream to "
+            << host_name_ << ":" << port_num_;
+      }
+    }
+
+   private:
+    // Creates a client socket and connects to the server.
+    void MakeConnection();
+
+    // Closes the socket.
+    void CloseConnection() override {
+      GTEST_CHECK_(sockfd_ != -1)
+          << "CloseConnection() can be called only when there is a connection.";
+
+      close(sockfd_);
+      sockfd_ = -1;
+    }
+
+    int sockfd_;  // socket file descriptor
+    const std::string host_name_;
+    const std::string port_num_;
+
+    GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter);
+  };  // class SocketWriter
+
+  // Escapes '=', '&', '%', and '\n' characters in str as "%xx".
+  static std::string UrlEncode(const char* str);
+
+  StreamingListener(const std::string& host, const std::string& port)
+      : socket_writer_(new SocketWriter(host, port)) {
+    Start();
+  }
+
+  explicit StreamingListener(AbstractSocketWriter* socket_writer)
+      : socket_writer_(socket_writer) { Start(); }
+
+  void OnTestProgramStart(const UnitTest& /* unit_test */) override {
+    SendLn("event=TestProgramStart");
+  }
+
+  void OnTestProgramEnd(const UnitTest& unit_test) override {
+    // Note that Google Test current only report elapsed time for each
+    // test iteration, not for the entire test program.
+    SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed()));
+
+    // Notify the streaming server to stop.
+    socket_writer_->CloseConnection();
+  }
+
+  void OnTestIterationStart(const UnitTest& /* unit_test */,
+                            int iteration) override {
+    SendLn("event=TestIterationStart&iteration=" +
+           StreamableToString(iteration));
+  }
+
+  void OnTestIterationEnd(const UnitTest& unit_test,
+                          int /* iteration */) override {
+    SendLn("event=TestIterationEnd&passed=" +
+           FormatBool(unit_test.Passed()) + "&elapsed_time=" +
+           StreamableToString(unit_test.elapsed_time()) + "ms");
+  }
+
+  // Note that "event=TestCaseStart" is a wire format and has to remain
+  // "case" for compatibility
+  void OnTestCaseStart(const TestCase& test_case) override {
+    SendLn(std::string("event=TestCaseStart&name=") + test_case.name());
+  }
+
+  // Note that "event=TestCaseEnd" is a wire format and has to remain
+  // "case" for compatibility
+  void OnTestCaseEnd(const TestCase& test_case) override {
+    SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) +
+           "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) +
+           "ms");
+  }
+
+  void OnTestStart(const TestInfo& test_info) override {
+    SendLn(std::string("event=TestStart&name=") + test_info.name());
+  }
+
+  void OnTestEnd(const TestInfo& test_info) override {
+    SendLn("event=TestEnd&passed=" +
+           FormatBool((test_info.result())->Passed()) +
+           "&elapsed_time=" +
+           StreamableToString((test_info.result())->elapsed_time()) + "ms");
+  }
+
+  void OnTestPartResult(const TestPartResult& test_part_result) override {
+    const char* file_name = test_part_result.file_name();
+    if (file_name == nullptr) file_name = "";
+    SendLn("event=TestPartResult&file=" + UrlEncode(file_name) +
+           "&line=" + StreamableToString(test_part_result.line_number()) +
+           "&message=" + UrlEncode(test_part_result.message()));
+  }
+
+ private:
+  // Sends the given message and a newline to the socket.
+  void SendLn(const std::string& message) { socket_writer_->SendLn(message); }
+
+  // Called at the start of streaming to notify the receiver what
+  // protocol we are using.
+  void Start() { SendLn("gtest_streaming_protocol_version=1.0"); }
+
+  std::string FormatBool(bool value) { return value ? "1" : "0"; }
+
+  const std::unique_ptr<AbstractSocketWriter> socket_writer_;
+
+  GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener);
+};  // class StreamingListener
+
+#endif  // GTEST_CAN_STREAM_RESULTS_
+
+}  // namespace internal
+}  // namespace testing
+
+GTEST_DISABLE_MSC_WARNINGS_POP_()  //  4251
+
+#endif  // GOOGLETEST_SRC_GTEST_INTERNAL_INL_H_
diff --git a/src/include/gromacs/external/googletest/googletest/test/googletest-param-test-test.h b/src/include/gromacs/external/googletest/googletest/test/googletest-param-test-test.h
new file mode 100644 (file)
index 0000000..8919375
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// The Google C++ Testing and Mocking Framework (Google Test)
+//
+// This header file provides classes and functions used internally
+// for testing Google Test itself.
+
+#ifndef GOOGLETEST_TEST_GOOGLETEST_PARAM_TEST_TEST_H_
+#define GOOGLETEST_TEST_GOOGLETEST_PARAM_TEST_TEST_H_
+
+#include "gtest/gtest.h"
+
+// Test fixture for testing definition and instantiation of a test
+// in separate translation units.
+class ExternalInstantiationTest : public ::testing::TestWithParam<int> {
+};
+
+// Test fixture for testing instantiation of a test in multiple
+// translation units.
+class InstantiationInMultipleTranslationUnitsTest
+    : public ::testing::TestWithParam<int> {
+};
+
+#endif  // GOOGLETEST_TEST_GOOGLETEST_PARAM_TEST_TEST_H_
diff --git a/src/include/gromacs/external/googletest/googletest/test/gtest-typed-test_test.h b/src/include/gromacs/external/googletest/googletest/test/gtest-typed-test_test.h
new file mode 100644 (file)
index 0000000..8ce559c
--- /dev/null
@@ -0,0 +1,60 @@
+// Copyright 2008 Google Inc.
+// All Rights Reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GOOGLETEST_TEST_GTEST_TYPED_TEST_TEST_H_
+#define GOOGLETEST_TEST_GTEST_TYPED_TEST_TEST_H_
+
+#include "gtest/gtest.h"
+
+using testing::Test;
+
+// For testing that the same type-parameterized test case can be
+// instantiated in different translation units linked together.
+// ContainerTest will be instantiated in both gtest-typed-test_test.cc
+// and gtest-typed-test2_test.cc.
+
+template <typename T>
+class ContainerTest : public Test {
+};
+
+TYPED_TEST_SUITE_P(ContainerTest);
+
+TYPED_TEST_P(ContainerTest, CanBeDefaultConstructed) {
+  TypeParam container;
+}
+
+TYPED_TEST_P(ContainerTest, InitialSizeIsZero) {
+  TypeParam container;
+  EXPECT_EQ(0U, container.size());
+}
+
+REGISTER_TYPED_TEST_SUITE_P(ContainerTest,
+                            CanBeDefaultConstructed, InitialSizeIsZero);
+
+#endif  // GOOGLETEST_TEST_GTEST_TYPED_TEST_TEST_H_
diff --git a/src/include/gromacs/external/googletest/googletest/test/production.h b/src/include/gromacs/external/googletest/googletest/test/production.h
new file mode 100644 (file)
index 0000000..41a5472
--- /dev/null
@@ -0,0 +1,54 @@
+// Copyright 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//
+// This is part of the unit test for gtest_prod.h.
+
+#ifndef GOOGLETEST_TEST_PRODUCTION_H_
+#define GOOGLETEST_TEST_PRODUCTION_H_
+
+#include "gtest/gtest_prod.h"
+
+class PrivateCode {
+ public:
+  // Declares a friend test that does not use a fixture.
+  FRIEND_TEST(PrivateCodeTest, CanAccessPrivateMembers);
+
+  // Declares a friend test that uses a fixture.
+  FRIEND_TEST(PrivateCodeFixtureTest, CanAccessPrivateMembers);
+
+  PrivateCode();
+
+  int x() const { return x_; }
+ private:
+  void set_x(int an_x) { x_ = an_x; }
+  int x_;
+};
+
+#endif  // GOOGLETEST_TEST_PRODUCTION_H_
diff --git a/src/include/gromacs/external/lmfit/lmmin.h b/src/include/gromacs/external/lmfit/lmmin.h
new file mode 100644 (file)
index 0000000..d2ccfae
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Library:   lmfit (Levenberg-Marquardt least squares fitting)
+ *
+ * File:      lmmin.h
+ *
+ * Contents:  Declarations for Levenberg-Marquardt minimization.
+ *
+ * Copyright: Joachim Wuttke, Forschungszentrum Juelich GmbH (2004-2013)
+ *
+ * License:   see ../COPYING (FreeBSD)
+ *
+ * Homepage:  apps.jcns.fz-juelich.de/lmfit
+ */
+
+#ifndef LMMIN_H
+#define LMMIN_H
+
+#include "lmstruct.h"
+
+/* Levenberg-Marquardt minimization. */
+void lmmin(
+    const int n_par, double* par, const int m_dat, const double* y,
+    const void* data,
+    void (*evaluate)(
+        const double* par, const int m_dat, const void* data,
+        double* fvec, int* userbreak),
+    const lm_control_struct* control, lm_status_struct* status);
+/*
+ *   This routine contains the core algorithm of our library.
+ *
+ *   It minimizes the sum of the squares of m nonlinear functions
+ *   in n variables by a modified Levenberg-Marquardt algorithm.
+ *   The function evaluation is done by the user-provided routine 'evaluate'.
+ *   The Jacobian is then calculated by a forward-difference approximation.
+ *
+ *   Parameters:
+ *
+ *      n_par is the number of variables (INPUT, positive integer).
+ *
+ *      par is the solution vector (INPUT/OUTPUT, array of length n).
+ *        On input it must be set to an estimated solution.
+ *        On output it yields the final estimate of the solution.
+ *
+ *      m_dat is the number of functions to be minimized (INPUT, positive integer).
+ *        It must fulfill m>=n.
+ *
+ *      y contains data to be fitted. Use a null pointer if there are no data.
+ *
+ *      data is a pointer that is ignored by lmmin; it is however forwarded
+ *        to the user-supplied functions evaluate and printout.
+ *        In a typical application, it contains experimental data to be fitted.
+ *
+ *      evaluate is a user-supplied function that calculates the m functions.
+ *        Parameters:
+ *          n, x, m, data as above.
+ *          fvec is an array of length m; on OUTPUT, it must contain the
+ *            m function values for the parameter vector x.
+ *          userbreak is an integer pointer. When *userbreak is set to a
+ *            nonzero value, lmmin will terminate.
+ *
+ *      control contains INPUT variables that control the fit algorithm,
+ *        as declared and explained in lmstruct.h
+ *
+ *      status contains OUTPUT variables that inform about the fit result,
+ *        as declared and explained in lmstruct.h
+ */
+
+/* Refined calculation of Eucledian norm. */
+double lm_enorm(const int, const double*);
+double lm_fnorm(const int, const double*, const double*);
+
+#endif /* LMMIN_H */
diff --git a/src/include/gromacs/external/lmfit/lmstruct.h b/src/include/gromacs/external/lmfit/lmstruct.h
new file mode 100644 (file)
index 0000000..1f513b3
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Library:   lmfit (Levenberg-Marquardt least squares fitting)
+ *
+ * File:      lmstruct.h
+ *
+ * Contents:  Declarations of parameter records, used in lmmin.h and lmcurve.h
+ *
+ * Copyright: Joachim Wuttke, Forschungszentrum Juelich GmbH (2004-2013)
+ *
+ * License:   see ../COPYING (FreeBSD)
+ *
+ * Homepage:  apps.jcns.fz-juelich.de/lmfit
+ */
+
+#ifndef LMSTRUCT_H
+#define LMSTRUCT_H
+
+#include <stdio.h>
+
+/* Collection of input parameters for fit control. */
+typedef struct {
+    double ftol;      /* Relative error desired in the sum of squares.
+                         Termination occurs when both the actual and
+                         predicted relative reductions in the sum of squares
+                         are at most ftol. */
+    double xtol;      /* Relative error between last two approximations.
+                         Termination occurs when the relative error between
+                         two consecutive iterates is at most xtol. */
+    double gtol;      /* Orthogonality desired between fvec and its derivs.
+                         Termination occurs when the cosine of the angle
+                         between fvec and any column of the Jacobian is at
+                         most gtol in absolute value. */
+    double epsilon;   /* Step used to calculate the Jacobian, should be
+                         slightly larger than the relative error in the
+                         user-supplied functions. */
+    double stepbound; /* Used in determining the initial step bound. This
+                         bound is set to the product of stepbound and the
+                         Euclidean norm of diag*x if nonzero, or else to
+                         stepbound itself. In most cases stepbound should lie
+                         in the interval (0.1,100.0). Generally, the value
+                         100.0 is recommended. */
+    int patience;     /* Used to set the maximum number of function evaluations
+                         to patience*(number_of_parameters+1). */
+    int scale_diag;   /* If 1, the variables will be rescaled internally.
+                         Recommended value is 1. */
+    FILE* msgfile;    /* Progress messages will be written to this file. */
+    int verbosity;    /* OR'ed: 1: print some messages; 2: print Jacobian. */
+    int n_maxpri;     /* -1, or max number of parameters to print. */
+    int m_maxpri;     /* -1, or max number of residuals to print. */
+} lm_control_struct;
+
+/* Collection of output parameters for status info. */
+typedef struct {
+    double fnorm;  /* norm of the residue vector fvec. */
+    int nfev;      /* actual number of iterations. */
+    int outcome;   /* Status indicator. Nonnegative values are used as index
+                      for the message text lm_infmsg, set in lmmin.c. */
+    int userbreak; /* Set when function evaluation requests termination. */
+} lm_status_struct;
+
+/* Preset (and recommended) control parameter settings. */
+extern const lm_control_struct lm_control_double;
+extern const lm_control_struct lm_control_float;
+
+/* Preset message texts. */
+
+extern const char* lm_infmsg[];
+extern const char* lm_shortmsg[];
+
+#endif /* LMSTRUCT_H */
diff --git a/src/include/gromacs/external/muparser/include/muParser.h b/src/include/gromacs/external/muparser/include/muParser.h
new file mode 100644 (file)
index 0000000..321fba9
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_H
+#define MU_PARSER_H
+
+//--- Standard includes ------------------------------------------------------------------------
+#include <vector>
+
+//--- Parser includes --------------------------------------------------------------------------
+#include "muParserBase.h"
+#include "muParserTemplateMagic.h"
+
+/** \file
+       \brief Definition of the standard floating point parser.
+*/
+
+namespace mu
+{
+       /** \brief Mathematical expressions parser.
+
+         Standard implementation of the mathematical expressions parser.
+         Can be used as a reference implementation for subclassing the parser.
+       */
+       class API_EXPORT_CXX Parser : public ParserBase
+       {
+       public:
+
+               Parser();
+
+               virtual void InitCharSets();
+               virtual void InitFun();
+               virtual void InitConst();
+               virtual void InitOprt();
+               virtual void OnDetectVar(string_type* pExpr, int& nStart, int& nEnd);
+
+               value_type Diff(value_type* a_Var, value_type a_fPos, value_type a_fEpsilon = 0) const;
+
+       protected:
+
+               static int IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal);
+       };
+} // namespace mu
+
+#endif
+
diff --git a/src/include/gromacs/external/muparser/include/muParserBase.h b/src/include/gromacs/external/muparser/include/muParserBase.h
new file mode 100644 (file)
index 0000000..7793818
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_BASE_H
+#define MU_PARSER_BASE_H
+
+//--- Standard includes ------------------------------------------------------------------------
+#include <cmath>
+#include <string>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <locale>
+#include <limits.h>
+
+//--- Parser includes --------------------------------------------------------------------------
+#include "muParserDef.h"
+#include "muParserTokenReader.h"
+#include "muParserBytecode.h"
+#include "muParserError.h"
+
+
+namespace mu
+{
+       /** \file
+               \brief This file contains the class definition of the muparser engine.
+       */
+
+       /** \brief Mathematical expressions parser (base parser engine).
+
+               This is the implementation of a bytecode based mathematical expressions parser.
+               The formula will be parsed from string and converted into a bytecode.
+               Future calculations will be done with the bytecode instead the formula string
+               resulting in a significant performance increase.
+               Complementary to a set of internally implemented functions the parser is able to handle
+               user defined functions and variables.
+       */
+       class API_EXPORT_CXX ParserBase
+       {
+               friend class ParserTokenReader;
+
+       private:
+
+               /** \brief Typedef for the parse functions.
+
+                 The parse function do the actual work. The parser exchanges
+                 the function pointer to the parser function depending on
+                 which state it is in. (i.e. bytecode parser vs. string parser)
+               */
+               typedef value_type(ParserBase::* ParseFunction)() const;
+
+               /** \brief Type used for storing an array of values. */
+               typedef std::vector<value_type> valbuf_type;
+
+               /** \brief Type for a vector of strings. */
+               typedef std::vector<string_type> stringbuf_type;
+
+               /** \brief Typedef for the token reader. */
+               typedef ParserTokenReader token_reader_type;
+
+               /** \brief Type used for parser tokens. */
+               typedef ParserToken<value_type, string_type> token_type;
+
+               /** \brief Maximum number of threads spawned by OpenMP when using the bulk mode. */
+               static const int s_MaxNumOpenMPThreads;
+
+       public:
+
+               /** \brief Type of the error class.
+
+                 Included for backwards compatibility.
+               */
+               typedef ParserError exception_type;
+
+               static void EnableDebugDump(bool bDumpCmd, bool bDumpStack);
+
+               ParserBase();
+               ParserBase(const ParserBase& a_Parser);
+               ParserBase& operator=(const ParserBase& a_Parser);
+
+               virtual ~ParserBase();
+
+               value_type Eval() const;
+               value_type* Eval(int& nStackSize) const;
+               void Eval(value_type* results, int nBulkSize);
+
+               int GetNumResults() const;
+
+               void SetExpr(const string_type& a_sExpr);
+               void SetVarFactory(facfun_type a_pFactory, void* pUserData = nullptr);
+
+               void SetDecSep(char_type cDecSep);
+               void SetThousandsSep(char_type cThousandsSep = 0);
+               void ResetLocale();
+
+               void EnableOptimizer(bool a_bIsOn = true);
+               void EnableBuiltInOprt(bool a_bIsOn = true);
+
+               bool HasBuiltInOprt() const;
+               void AddValIdent(identfun_type a_pCallback);
+
+               /** \fn void mu::ParserBase::DefineFun(const string_type &a_strName, fun_type0 a_pFun, bool a_bAllowOpt = true)
+                       \brief Define a parser function without arguments.
+                       \param a_strName Name of the function
+                       \param a_pFun Pointer to the callback function
+                       \param a_bAllowOpt A flag indicating this function may be optimized
+               */
+               template<typename T>
+               void DefineFun(const string_type& a_strName, T a_pFun, bool a_bAllowOpt = true)
+               {
+                       AddCallback(a_strName, ParserCallback(a_pFun, a_bAllowOpt), m_FunDef, ValidNameChars());
+               }
+
+               void DefineOprt(const string_type& a_strName, fun_type2 a_pFun, unsigned a_iPri = 0, EOprtAssociativity a_eAssociativity = oaLEFT, bool a_bAllowOpt = false);
+               void DefineConst(const string_type& a_sName, value_type a_fVal);
+               void DefineStrConst(const string_type& a_sName, const string_type& a_strVal);
+               void DefineVar(const string_type& a_sName, value_type* a_fVar);
+               void DefinePostfixOprt(const string_type& a_strFun, fun_type1 a_pOprt, bool a_bAllowOpt = true);
+               void DefineInfixOprt(const string_type& a_strName, fun_type1 a_pOprt, int a_iPrec = prINFIX, bool a_bAllowOpt = true);
+
+               // Clear user defined variables, constants or functions
+               void ClearVar();
+               void ClearFun();
+               void ClearConst();
+               void ClearInfixOprt();
+               void ClearPostfixOprt();
+               void ClearOprt();
+
+               void RemoveVar(const string_type& a_strVarName);
+               const varmap_type& GetUsedVar() const;
+               const varmap_type& GetVar() const;
+               const valmap_type& GetConst() const;
+               const string_type& GetExpr() const;
+               const funmap_type& GetFunDef() const;
+               string_type GetVersion(EParserVersionInfo eInfo = pviFULL) const;
+
+               const char_type** GetOprtDef() const;
+               void DefineNameChars(const char_type* a_szCharset);
+               void DefineOprtChars(const char_type* a_szCharset);
+               void DefineInfixOprtChars(const char_type* a_szCharset);
+
+               const char_type* ValidNameChars() const;
+               const char_type* ValidOprtChars() const;
+               const char_type* ValidInfixOprtChars() const;
+
+               void SetArgSep(char_type cArgSep);
+               char_type GetArgSep() const;
+
+       protected:
+
+               void Init();
+               void Error(EErrorCodes a_iErrc, int a_iPos = (int)mu::string_type::npos, const string_type& a_strTok = string_type()) const;
+
+               virtual void InitCharSets() = 0;
+               virtual void InitFun() = 0;
+               virtual void InitConst() = 0;
+               virtual void InitOprt() = 0;
+
+               virtual void OnDetectVar(string_type* pExpr, int& nStart, int& nEnd);
+
+               static const char_type* c_DefaultOprt[];
+               static std::locale s_locale;  ///< The locale used by the parser
+               static bool g_DbgDumpCmdCode;
+               static bool g_DbgDumpStack;
+
+               /** \brief A facet class used to change decimal and thousands separator. */
+               template<class TChar>
+               class change_dec_sep : public std::numpunct<TChar>
+               {
+               public:
+
+                       explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3)
+                               :std::numpunct<TChar>()
+                               , m_nGroup(nGroup)
+                               , m_cDecPoint(cDecSep)
+                               , m_cThousandsSep(cThousandsSep)
+                       {}
+
+               protected:
+
+                       virtual char_type do_decimal_point() const
+                       {
+                               return m_cDecPoint;
+                       }
+
+                       virtual char_type do_thousands_sep() const
+                       {
+                               return m_cThousandsSep;
+                       }
+
+                       virtual std::string do_grouping() const
+                       {
+                               // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4
+                               // courtesy of Jens Bartsch
+                               // original code:
+                               //        return std::string(1, (char)m_nGroup); 
+                               // new code:
+                               return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX));
+                       }
+
+               private:
+
+                       int m_nGroup;
+                       char_type m_cDecPoint;
+                       char_type m_cThousandsSep;
+               };
+
+       private:
+
+               void Assign(const ParserBase& a_Parser);
+               void InitTokenReader();
+               void ReInit() const;
+
+               void AddCallback(const string_type& a_strName, const ParserCallback& a_Callback, funmap_type& a_Storage, const char_type* a_szCharSet);
+               void ApplyRemainingOprt(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal) const;
+               void ApplyBinOprt(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal) const;
+               void ApplyIfElse(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal) const;
+               void ApplyFunc(std::stack<token_type>& a_stOpt, std::stack<token_type>& a_stVal, int iArgCount) const;
+
+               token_type ApplyStrFunc(const token_type& a_FunTok, const std::vector<token_type>& a_vArg) const;
+
+               int GetOprtPrecedence(const token_type& a_Tok) const;
+               EOprtAssociativity GetOprtAssociativity(const token_type& a_Tok) const;
+
+               void CreateRPN() const;
+
+               value_type ParseString() const;
+               value_type ParseCmdCode() const;
+               value_type ParseCmdCodeShort() const;
+               value_type ParseCmdCodeBulk(int nOffset, int nThreadID) const;
+
+               void  CheckName(const string_type& a_strName, const string_type& a_CharSet) const;
+               void  CheckOprt(const string_type& a_sName, const ParserCallback& a_Callback, const string_type& a_szCharSet) const;
+
+               void StackDump(const std::stack<token_type >& a_stVal, const std::stack<token_type >& a_stOprt) const;
+
+               /** \brief Pointer to the parser function.
+
+                 Eval() calls the function whose address is stored there.
+               */
+               mutable ParseFunction  m_pParseFormula;
+               mutable ParserByteCode m_vRPN;        ///< The Bytecode class.
+               mutable stringbuf_type  m_vStringBuf; ///< String buffer, used for storing string function arguments
+               stringbuf_type  m_vStringVarBuf;
+
+               std::unique_ptr<token_reader_type> m_pTokenReader; ///< Managed pointer to the token reader object.
+
+               funmap_type  m_FunDef;         ///< Map of function names and pointers.
+               funmap_type  m_PostOprtDef;    ///< Postfix operator callbacks
+               funmap_type  m_InfixOprtDef;   ///< unary infix operator.
+               funmap_type  m_OprtDef;        ///< Binary operator callbacks
+               valmap_type  m_ConstDef;       ///< user constants.
+               strmap_type  m_StrVarDef;      ///< user defined string constants
+               varmap_type  m_VarDef;         ///< user defind variables.
+
+               bool m_bBuiltInOp;             ///< Flag that can be used for switching built in operators on and off
+
+               string_type m_sNameChars;      ///< Charset for names
+               string_type m_sOprtChars;      ///< Charset for postfix/ binary operator tokens
+               string_type m_sInfixOprtChars; ///< Charset for infix operator tokens
+
+               // items merely used for caching state information
+               mutable valbuf_type m_vStackBuffer; ///< This is merely a buffer used for the stack in the cmd parsing routine
+               mutable int m_nFinalResultIdx;
+       };
+
+} // namespace mu
+
+#endif
diff --git a/src/include/gromacs/external/muparser/include/muParserBytecode.h b/src/include/gromacs/external/muparser/include/muParserBytecode.h
new file mode 100644 (file)
index 0000000..5074e23
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_BYTECODE_H
+#define MU_PARSER_BYTECODE_H
+
+#include <string>
+#include <stack>
+#include <vector>
+
+#include "muParserDef.h"
+#include "muParserError.h"
+#include "muParserToken.h"
+
+/** \file
+       \brief Definition of the parser bytecode class.
+*/
+
+
+namespace mu
+{
+       struct SToken
+       {
+               ECmdCode Cmd;
+
+               union
+               {
+                       struct //SValData
+                       {
+                               value_type* ptr;
+                               value_type  data;
+                               value_type  data2;
+                       } Val;
+
+                       struct //SFunData
+                       {
+                               // Note: generic_fun_type is merely a placeholder. The real type could be 
+                               //       anything between gun_type1 and fun_type9. I can't use a void
+                               //       pointer due to constraints in the ANSI standard which allows
+                               //       data pointers and function pointers to differ in size.
+                               generic_fun_type ptr;
+                               int   argc;
+                               int   idx;
+                       } Fun;
+
+                       struct //SOprtData
+                       {
+                               value_type* ptr;
+                               int offset;
+                       } Oprt;
+               };
+       };
+
+
+       /** \brief Bytecode implementation of the Math Parser.
+
+               The bytecode contains the formula converted to revers polish notation stored in a continious
+               memory area. Associated with this data are operator codes, variable pointers, constant
+               values and function pointers. Those are necessary in order to calculate the result.
+               All those data items will be casted to the underlying datatype of the bytecode.
+       */
+       class ParserByteCode final
+       {
+       private:
+
+               /** \brief Token type for internal use only. */
+               typedef ParserToken<value_type, string_type> token_type;
+
+               /** \brief Token vector for storing the RPN. */
+               typedef std::vector<SToken> rpn_type;
+
+               /** \brief Position in the Calculation array. */
+               unsigned m_iStackPos;
+
+               /** \brief Maximum size needed for the stack. */
+               std::size_t m_iMaxStackSize;
+
+               /** \brief The actual rpn storage. */
+               rpn_type  m_vRPN;
+
+               bool m_bEnableOptimizer;
+
+               void ConstantFolding(ECmdCode a_Oprt);
+
+       public:
+
+               ParserByteCode();
+               ParserByteCode(const ParserByteCode& a_ByteCode);
+               ParserByteCode& operator=(const ParserByteCode& a_ByteCode);
+               void Assign(const ParserByteCode& a_ByteCode);
+
+               void AddVar(value_type* a_pVar);
+               void AddVal(value_type a_fVal);
+               void AddOp(ECmdCode a_Oprt);
+               void AddIfElse(ECmdCode a_Oprt);
+               void AddAssignOp(value_type* a_pVar);
+               void AddFun(generic_fun_type a_pFun, int a_iArgc);
+               void AddBulkFun(generic_fun_type a_pFun, int a_iArgc);
+               void AddStrFun(generic_fun_type a_pFun, int a_iArgc, int a_iIdx);
+
+               void EnableOptimizer(bool bStat);
+
+               void Finalize();
+               void clear();
+               std::size_t GetMaxStackSize() const;
+
+               std::size_t GetSize() const
+               {
+                       return m_vRPN.size();
+               }
+
+               inline const SToken* GetBase() const
+               {
+                       if (m_vRPN.size() == 0)
+                               throw ParserError(ecINTERNAL_ERROR);
+                       else
+                               return &m_vRPN[0];
+               }
+
+               void AsciiDump();
+       };
+
+} // namespace mu
+
+#endif
+
+
diff --git a/src/include/gromacs/external/muparser/include/muParserCallback.h b/src/include/gromacs/external/muparser/include/muParserCallback.h
new file mode 100644 (file)
index 0000000..1a8d060
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#ifndef MU_PARSER_CALLBACK_H
+#define MU_PARSER_CALLBACK_H
+
+#include "muParserDef.h"
+
+/** \file
+       \brief Definition of the parser callback class.
+*/
+
+namespace mu
+{
+
+       /** \brief Encapsulation of prototypes for a numerical parser function.
+
+               Encapsulates the prototyp for numerical parser functions. The class
+               stores the number of arguments for parser functions as well
+               as additional flags indication the function is non optimizeable.
+               The pointer to the callback function pointer is stored as void*
+               and needs to be casted according to the argument count.
+               Negative argument counts indicate a parser function with a variable number
+               of arguments.
+       */
+       class API_EXPORT_CXX ParserCallback final
+       {
+       public:
+               ParserCallback(fun_type0  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type1  a_pFun, bool a_bAllowOpti, int a_iPrec = -1, ECmdCode a_iCode = cmFUNC);
+               ParserCallback(fun_type2  a_pFun, bool a_bAllowOpti, int a_iPrec, EOprtAssociativity a_eAssociativity);
+               ParserCallback(fun_type2  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type3  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type4  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type5  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type6  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type7  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type8  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type9  a_pFun, bool a_bAllowOpti);
+               ParserCallback(fun_type10 a_pFun, bool a_bAllowOpti);
+
+               ParserCallback(bulkfun_type0  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type1  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type2  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type3  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type4  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type5  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type6  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type7  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type8  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type9  a_pFun, bool a_bAllowOpti);
+               ParserCallback(bulkfun_type10 a_pFun, bool a_bAllowOpti);
+
+               ParserCallback(multfun_type a_pFun, bool a_bAllowOpti);
+               ParserCallback(strfun_type1 a_pFun, bool a_bAllowOpti);
+               ParserCallback(strfun_type2 a_pFun, bool a_bAllowOpti);
+               ParserCallback(strfun_type3 a_pFun, bool a_bAllowOpti);
+               ParserCallback(strfun_type4 a_pFun, bool a_bAllowOpti);
+               ParserCallback(strfun_type5 a_pFun, bool a_bAllowOpti);
+               ParserCallback();
+               ParserCallback(const ParserCallback& a_Fun);
+    ParserCallback & operator=(const ParserCallback& a_Fun);
+
+               ParserCallback* Clone() const;
+
+               bool  IsOptimizable() const;
+               void* GetAddr() const;
+               ECmdCode  GetCode() const;
+               ETypeCode GetType() const;
+               int GetPri()  const;
+               EOprtAssociativity GetAssociativity() const;
+               int GetArgc() const;
+
+       private:
+               void* m_pFun;                   ///< Pointer to the callback function, casted to void
+
+               /** \brief Number of numeric function arguments
+
+                       This number is negative for functions with variable number of arguments. in this cases
+                       they represent the actual number of arguments found.
+               */
+               int   m_iArgc;
+               int   m_iPri;                   ///< Valid only for binary and infix operators; Operator precedence.
+               EOprtAssociativity m_eOprtAsct; ///< Operator associativity; Valid only for binary operators 
+               ECmdCode  m_iCode;
+               ETypeCode m_iType;
+               bool  m_bAllowOpti;             ///< Flag indication optimizeability 
+       };
+
+
+       /** \brief Container for Callback objects. */
+       typedef std::map<string_type, ParserCallback> funmap_type;
+
+} // namespace mu
+
+#endif
+
diff --git a/src/include/gromacs/external/muparser/include/muParserDLL.h b/src/include/gromacs/external/muparser/include/muParserDLL.h
new file mode 100644 (file)
index 0000000..18051c4
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_DLL_H
+#define MU_PARSER_DLL_H
+
+#include "muParserFixes.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+       /** \file
+               \brief This file contains the DLL interface of muparser.
+       */
+
+       // Basic types
+       typedef void* muParserHandle_t;    // parser handle
+
+#ifndef _UNICODE
+       typedef char   muChar_t;            // character type
+#else
+       typedef wchar_t   muChar_t;            // character type
+#endif
+
+       typedef int    muBool_t;            // boolean type
+       typedef int    muInt_t;             // integer type 
+       typedef double muFloat_t;           // floating point type
+
+       // function types for calculation
+       typedef muFloat_t(*muFun0_t)(void);
+       typedef muFloat_t(*muFun1_t)(muFloat_t);
+       typedef muFloat_t(*muFun2_t)(muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun3_t)(muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun4_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun5_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun6_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun7_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun8_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun9_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muFun10_t)(muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+
+       // Function prototypes for bulkmode functions
+       typedef muFloat_t(*muBulkFun0_t)(int, int);
+       typedef muFloat_t(*muBulkFun1_t)(int, int, muFloat_t);
+       typedef muFloat_t(*muBulkFun2_t)(int, int, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun3_t)(int, int, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun4_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun5_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun6_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun7_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun8_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun9_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+       typedef muFloat_t(*muBulkFun10_t)(int, int, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t, muFloat_t);
+
+       typedef muFloat_t(*muMultFun_t)(const muFloat_t*, muInt_t);
+       typedef muFloat_t(*muStrFun1_t)(const muChar_t*);
+       typedef muFloat_t(*muStrFun2_t)(const muChar_t*, muFloat_t);
+       typedef muFloat_t(*muStrFun3_t)(const muChar_t*, muFloat_t, muFloat_t);
+
+       // Functions for parser management
+       typedef void (*muErrorHandler_t)(muParserHandle_t a_hParser);           // [optional] callback to an error handler
+       typedef muFloat_t* (*muFacFun_t)(const muChar_t*, void*);               // [optional] callback for creating new variables
+       typedef muInt_t(*muIdentFun_t)(const muChar_t*, muInt_t*, muFloat_t*); // [optional] value identification callbacks
+
+       //-----------------------------------------------------------------------------------------------------
+       // Constants
+       static const int muOPRT_ASCT_LEFT = 0;
+       static const int muOPRT_ASCT_RIGHT = 1;
+
+       static const int muBASETYPE_FLOAT = 0;
+       static const int muBASETYPE_INT = 1;
+
+       //-----------------------------------------------------------------------------------------------------
+       //
+       //
+       // muParser C compatible bindings
+       //
+       //
+       //-----------------------------------------------------------------------------------------------------
+
+
+       // Basic operations / initialization  
+       API_EXPORT(muParserHandle_t) mupCreate(int nBaseType);
+       API_EXPORT(void) mupRelease(muParserHandle_t a_hParser);
+       API_EXPORT(const muChar_t*) mupGetExpr(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupSetExpr(muParserHandle_t a_hParser, const muChar_t* a_szExpr);
+       API_EXPORT(void) mupSetVarFactory(muParserHandle_t a_hParser, muFacFun_t a_pFactory, void* pUserData);
+       API_EXPORT(const muChar_t*) mupGetVersion(muParserHandle_t a_hParser);
+       API_EXPORT(muFloat_t) mupEval(muParserHandle_t a_hParser);
+       API_EXPORT(muFloat_t*) mupEvalMulti(muParserHandle_t a_hParser, int* nNum);
+       API_EXPORT(void) mupEvalBulk(muParserHandle_t a_hParser, muFloat_t* a_fResult, int nSize);
+
+       // Defining callbacks / variables / constants
+       API_EXPORT(void) mupDefineFun0(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun0_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun1_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun2_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun3_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun4(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun4_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun5(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun5_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun6(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun6_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun7(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun7_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun8(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun8_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun9(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun9_t a_pFun, muBool_t a_bOptimize);
+       API_EXPORT(void) mupDefineFun10(muParserHandle_t a_hParser, const muChar_t* a_szName, muFun10_t a_pFun, muBool_t a_bOptimize);
+
+       // Defining bulkmode functions
+       API_EXPORT(void) mupDefineBulkFun0(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun0_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun1_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun2_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun3_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun4(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun4_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun5(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun5_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun6(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun6_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun7(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun7_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun8(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun8_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun9(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun9_t a_pFun);
+       API_EXPORT(void) mupDefineBulkFun10(muParserHandle_t a_hParser, const muChar_t* a_szName, muBulkFun10_t a_pFun);
+
+       // string functions
+       API_EXPORT(void) mupDefineStrFun1(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun1_t a_pFun);
+       API_EXPORT(void) mupDefineStrFun2(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun2_t a_pFun);
+       API_EXPORT(void) mupDefineStrFun3(muParserHandle_t a_hParser, const muChar_t* a_szName, muStrFun3_t a_pFun);
+
+       API_EXPORT(void) mupDefineMultFun(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muMultFun_t a_pFun,
+               muBool_t a_bOptimize);
+
+       API_EXPORT(void) mupDefineOprt(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muFun2_t a_pFun,
+               muInt_t a_nPrec,
+               muInt_t a_nOprtAsct,
+               muBool_t a_bOptimize);
+
+       API_EXPORT(void) mupDefineConst(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muFloat_t a_fVal);
+
+       API_EXPORT(void) mupDefineStrConst(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               const muChar_t* a_sVal);
+
+       API_EXPORT(void) mupDefineVar(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muFloat_t* a_fVar);
+
+       API_EXPORT(void) mupDefineBulkVar(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muFloat_t* a_fVar);
+
+       API_EXPORT(void) mupDefinePostfixOprt(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muFun1_t a_pOprt,
+               muBool_t a_bOptimize);
+
+
+       API_EXPORT(void) mupDefineInfixOprt(muParserHandle_t a_hParser,
+               const muChar_t* a_szName,
+               muFun1_t a_pOprt,
+               muBool_t a_bOptimize);
+
+       // Define character sets for identifiers
+       API_EXPORT(void) mupDefineNameChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset);
+       API_EXPORT(void) mupDefineOprtChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset);
+       API_EXPORT(void) mupDefineInfixOprtChars(muParserHandle_t a_hParser, const muChar_t* a_szCharset);
+
+       // Remove all / single variables
+       API_EXPORT(void) mupRemoveVar(muParserHandle_t a_hParser, const muChar_t* a_szName);
+       API_EXPORT(void) mupClearVar(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupClearConst(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupClearOprt(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupClearFun(muParserHandle_t a_hParser);
+
+       // Querying variables / expression variables / constants
+       API_EXPORT(int) mupGetExprVarNum(muParserHandle_t a_hParser);
+       API_EXPORT(int) mupGetVarNum(muParserHandle_t a_hParser);
+       API_EXPORT(int) mupGetConstNum(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupGetExprVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t** a_pVar);
+       API_EXPORT(void) mupGetVar(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t** a_pVar);
+       API_EXPORT(void) mupGetConst(muParserHandle_t a_hParser, unsigned a_iVar, const muChar_t** a_pszName, muFloat_t* a_pVar);
+       API_EXPORT(void) mupSetArgSep(muParserHandle_t a_hParser, const muChar_t cArgSep);
+       API_EXPORT(void) mupSetDecSep(muParserHandle_t a_hParser, const muChar_t cArgSep);
+       API_EXPORT(void) mupSetThousandsSep(muParserHandle_t a_hParser, const muChar_t cArgSep);
+       API_EXPORT(void) mupResetLocale(muParserHandle_t a_hParser);
+
+       // Add value recognition callbacks
+       API_EXPORT(void) mupAddValIdent(muParserHandle_t a_hParser, muIdentFun_t);
+
+       // Error handling
+       API_EXPORT(muBool_t) mupError(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupErrorReset(muParserHandle_t a_hParser);
+       API_EXPORT(void) mupSetErrorHandler(muParserHandle_t a_hParser, muErrorHandler_t a_pErrHandler);
+       API_EXPORT(const muChar_t*) mupGetErrorMsg(muParserHandle_t a_hParser);
+       API_EXPORT(muInt_t) mupGetErrorCode(muParserHandle_t a_hParser);
+       API_EXPORT(muInt_t) mupGetErrorPos(muParserHandle_t a_hParser);
+       API_EXPORT(const muChar_t*) mupGetErrorToken(muParserHandle_t a_hParser);
+       //API_EXPORT(const muChar_t*) mupGetErrorExpr(muParserHandle_t a_hParser);
+
+       // This is used for .NET only. It creates a new variable allowing the dll to
+       // manage the variable rather than the .NET garbage collector.
+       API_EXPORT(muFloat_t*) mupCreateVar(void);
+       API_EXPORT(void) mupReleaseVar(muFloat_t*);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // include guard
diff --git a/src/include/gromacs/external/muparser/include/muParserDef.h b/src/include/gromacs/external/muparser/include/muParserDef.h
new file mode 100644 (file)
index 0000000..7ce24f2
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MUP_DEF_H
+#define MUP_DEF_H
+
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <map>
+
+#include "muParserFixes.h"
+
+/** \file
+       \brief This file contains standard definitions used by the parser.
+*/
+
+/** \brief Define the base datatype for values.
+
+  This datatype must be a built in value type. You can not use custom classes.
+  It should be working with all types except "int"!
+*/
+#define MUP_BASETYPE double
+
+/** \brief Activate this option in order to compile with OpenMP support.
+
+  OpenMP is used only in the bulk mode it may increase the performance a bit.
+
+  !!! DO NOT ACTIVATE THIS MACRO HERE IF YOU USE CMAKE FOR BUILDING !!!
+
+  use the cmake option instead!
+*/
+//#define MUP_USE_OPENMP
+
+#if defined(_UNICODE)
+       /** \brief Definition of the basic parser string type. */
+       #define MUP_STRING_TYPE std::wstring
+
+       #if !defined(_T)
+               #define _T(x) L##x
+       #endif // not defined _T
+#else
+       #ifndef _T
+               #define _T(x) x
+       #endif
+
+       /** \brief Definition of the basic parser string type. */
+       #define MUP_STRING_TYPE std::string
+#endif
+
+/** \brief An assertion that does not kill the program. */
+#define MUP_ASSERT(COND)                                                                                       \
+            if (!(COND))                                                                                       \
+            {                                                                                                          \
+              stringstream_type ss;                                                                    \
+              ss << _T("Assertion \"") _T(#COND) _T("\" failed: ")     \
+                 << __FILE__ << _T(" line ")                                           \
+                 << __LINE__ << _T(".");                                                       \
+              throw ParserError( ecINTERNAL_ERROR, -1, ss.str());   \
+            }
+
+#if defined(_MSC_VER)
+       #pragma warning(push)
+       #pragma warning(disable : 26812) 
+#endif
+
+
+namespace mu
+{
+#if defined(_UNICODE)
+
+       /** \brief Encapsulate wcout. */
+       inline std::wostream& console()
+       {
+               return std::wcout;
+       }
+
+       /** \brief Encapsulate cin. */
+       inline std::wistream& console_in()
+       {
+               return std::wcin;
+       }
+
+#else
+
+       /** \brief Encapsulate cout.
+
+         Used for supporting UNICODE more easily.
+       */
+       inline std::ostream& console()
+       {
+               return std::cout;
+       }
+
+       /** \brief Encapsulate cin.
+
+         Used for supporting UNICODE more easily.
+       */
+       inline std::istream& console_in()
+       {
+               return std::cin;
+       }
+
+#endif
+
+       /** \brief Bytecode values.
+
+               \attention The order of the operator entries must match the order in ParserBase::c_DefaultOprt!
+       */
+       enum ECmdCode
+       {
+               // The following are codes for built in binary operators
+               // apart from built in operators the user has the opportunity to
+               // add user defined operators.
+               cmLE = 0,                       ///< Operator item:  less or equal
+               cmGE = 1,                       ///< Operator item:  greater or equal
+               cmNEQ = 2,                      ///< Operator item:  not equal
+               cmEQ = 3,                       ///< Operator item:  equals
+               cmLT = 4,                       ///< Operator item:  less than
+               cmGT = 5,                       ///< Operator item:  greater than
+               cmADD = 6,                      ///< Operator item:  add
+               cmSUB = 7,                      ///< Operator item:  subtract
+               cmMUL = 8,                      ///< Operator item:  multiply
+               cmDIV = 9,                      ///< Operator item:  division
+               cmPOW = 10,                     ///< Operator item:  y to the power of ...
+               cmLAND = 11,
+               cmLOR = 12,
+               cmASSIGN = 13,          ///< Operator item:  Assignment operator
+               cmBO = 14,                      ///< Operator item:  opening bracket
+               cmBC = 15,                      ///< Operator item:  closing bracket
+               cmIF = 16,                      ///< For use in the ternary if-then-else operator
+               cmELSE = 17,            ///< For use in the ternary if-then-else operator
+               cmENDIF = 18,           ///< For use in the ternary if-then-else operator
+               cmARG_SEP = 19,         ///< function argument separator
+               cmVAR = 20,                     ///< variable item
+               cmVAL = 21,                     ///< value item
+
+               // For optimization purposes
+               cmVARPOW2 = 22,
+               cmVARPOW3 = 23,
+               cmVARPOW4 = 24,
+               cmVARMUL = 25,
+
+               // operators and functions
+               cmFUNC = 26,            ///< Code for a generic function item
+               cmFUNC_STR,                     ///< Code for a function with a string parameter
+               cmFUNC_BULK,            ///< Special callbacks for Bulk mode with an additional parameter for the bulk index 
+               cmSTRING,                       ///< Code for a string token
+               cmOPRT_BIN,                     ///< user defined binary operator
+               cmOPRT_POSTFIX,         ///< code for postfix operators
+               cmOPRT_INFIX,           ///< code for infix operators
+               cmEND,                          ///< end of formula
+               cmUNKNOWN                       ///< uninitialized item
+       };
+
+       /** \brief Types internally used by the parser.
+       */
+       enum ETypeCode
+       {
+               tpSTR = 0,     ///< String type (Function arguments and constants only, no string variables)
+               tpDBL = 1,     ///< Floating point variables
+               tpVOID = 2      ///< Undefined type.
+       };
+
+
+       enum EParserVersionInfo
+       {
+               pviBRIEF,
+               pviFULL
+       };
+
+
+       /** \brief Parser operator precedence values. */
+       enum EOprtAssociativity
+       {
+               oaLEFT = 0,
+               oaRIGHT = 1,
+               oaNONE = 2
+       };
+
+
+       /** \brief Parser operator precedence values. */
+       enum EOprtPrecedence
+       {
+               // binary operators
+               prLOR = 1,
+               prLAND = 2,
+               prLOGIC = 3,    ///< logic operators
+               prCMP = 4,              ///< comparsion operators
+               prADD_SUB = 5,  ///< addition
+               prMUL_DIV = 6,  ///< multiplication/division
+               prPOW = 7,              ///< power operator priority (highest)
+
+               // infix operators
+               prINFIX = 6,    ///< Signs have a higher priority than ADD_SUB, but lower than power operator
+               prPOSTFIX = 6   ///< Postfix operator priority (currently unused)
+       };
+
+
+       /** \brief Error codes. */
+       enum EErrorCodes
+       {
+               // Formula syntax errors
+               ecUNEXPECTED_OPERATOR = 0,      ///< Unexpected binary operator found
+               ecUNASSIGNABLE_TOKEN = 1,       ///< Token can't be identified.
+               ecUNEXPECTED_EOF = 2,           ///< Unexpected end of formula. (Example: "2+sin(")
+               ecUNEXPECTED_ARG_SEP = 3,       ///< An unexpected comma has been found. (Example: "1,23")
+               ecUNEXPECTED_ARG = 4,           ///< An unexpected argument has been found
+               ecUNEXPECTED_VAL = 5,           ///< An unexpected value token has been found
+               ecUNEXPECTED_VAR = 6,           ///< An unexpected variable token has been found
+               ecUNEXPECTED_PARENS = 7,        ///< Unexpected Parenthesis, opening or closing
+               ecUNEXPECTED_STR = 8,           ///< A string has been found at an inapropriate position
+               ecSTRING_EXPECTED = 9,          ///< A string function has been called with a different type of argument
+               ecVAL_EXPECTED = 10,            ///< A numerical function has been called with a non value type of argument
+               ecMISSING_PARENS = 11,          ///< Missing parens. (Example: "3*sin(3")
+               ecUNEXPECTED_FUN = 12,          ///< Unexpected function found. (Example: "sin(8)cos(9)")
+               ecUNTERMINATED_STRING = 13,     ///< unterminated string constant. (Example: "3*valueof("hello)")
+               ecTOO_MANY_PARAMS = 14,         ///< Too many function parameters
+               ecTOO_FEW_PARAMS = 15,          ///< Too few function parameters. (Example: "ite(1<2,2)")
+               ecOPRT_TYPE_CONFLICT = 16,      ///< binary operators may only be applied to value items of the same type
+               ecSTR_RESULT = 17,                      ///< result is a string
+
+               // Invalid Parser input Parameters
+               ecINVALID_NAME = 18,                    ///< Invalid function, variable or constant name.
+               ecINVALID_BINOP_IDENT = 19,             ///< Invalid binary operator identifier
+               ecINVALID_INFIX_IDENT = 20,             ///< Invalid function, variable or constant name.
+               ecINVALID_POSTFIX_IDENT = 21,   ///< Invalid function, variable or constant name.
+
+               ecBUILTIN_OVERLOAD = 22, ///< Trying to overload builtin operator
+               ecINVALID_FUN_PTR = 23, ///< Invalid callback function pointer 
+               ecINVALID_VAR_PTR = 24, ///< Invalid variable pointer 
+               ecEMPTY_EXPRESSION = 25, ///< The Expression is empty
+               ecNAME_CONFLICT = 26, ///< Name conflict
+               ecOPT_PRI = 27, ///< Invalid operator priority
+               // 
+               ecDOMAIN_ERROR = 28, ///< catch division by zero, sqrt(-1), log(0) (currently unused)
+               ecDIV_BY_ZERO = 29, ///< Division by zero (currently unused)
+               ecGENERIC = 30, ///< Generic error
+               ecLOCALE = 31, ///< Conflict with current locale
+
+               ecUNEXPECTED_CONDITIONAL = 32,
+               ecMISSING_ELSE_CLAUSE = 33,
+               ecMISPLACED_COLON = 34,
+
+               ecUNREASONABLE_NUMBER_OF_COMPUTATIONS = 35,
+
+               ecIDENTIFIER_TOO_LONG = 36, ///< Thrown when an identifier with more then 255 characters is used.
+
+               ecEXPRESSION_TOO_LONG = 37, ///< Throw an exception if the expression has more than 10000 characters. (an arbitrary limit)
+
+               ecINVALID_CHARACTERS_FOUND = 38,///< The expression or identifier contains invalid non printable characters
+
+               // internal errors
+               ecINTERNAL_ERROR = 39,    ///< Internal error of any kind.
+
+               // The last two are special entries 
+               ecCOUNT,                      ///< This is no error code, It just stores just the total number of error codes
+               ecUNDEFINED = -1  ///< Undefined message, placeholder to detect unassigned error messages
+       };
+
+       //------------------------------------------------------------------------------
+       // Basic Types
+       //------------------------------------------------------------------------------
+
+       /** \brief The numeric datatype used by the parser.
+
+         Normally this is a floating point type either single or double precision.
+       */
+       typedef MUP_BASETYPE value_type;
+
+       /** \brief The stringtype used by the parser.
+
+         Depends on whether UNICODE is used or not.
+       */
+       typedef MUP_STRING_TYPE string_type;
+
+       /** \brief The character type used by the parser.
+
+         Depends on whether UNICODE is used or not.
+       */
+       typedef string_type::value_type char_type;
+
+       /** \brief Typedef for easily using stringstream that respect the parser stringtype. */
+       typedef std::basic_stringstream<char_type, std::char_traits<char_type>, std::allocator<char_type> > stringstream_type;
+
+       // Data container types
+
+       /** \brief Type used for storing variables. */
+       typedef std::map<string_type, value_type*> varmap_type;
+
+       /** \brief Type used for storing constants. */
+       typedef std::map<string_type, value_type> valmap_type;
+
+       /** \brief Type for assigning a string name to an index in the internal string table. */
+       typedef std::map<string_type, std::size_t> strmap_type;
+
+       // Parser callbacks
+
+       /** \brief Callback type used for functions without arguments. */
+       typedef value_type(*generic_fun_type)();
+
+       /** \brief Callback type used for functions without arguments. */
+       typedef value_type(*fun_type0)();
+
+       /** \brief Callback type used for functions with a single arguments. */
+       typedef value_type(*fun_type1)(value_type);
+
+       /** \brief Callback type used for functions with two arguments. */
+       typedef value_type(*fun_type2)(value_type, value_type);
+
+       /** \brief Callback type used for functions with three arguments. */
+       typedef value_type(*fun_type3)(value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with four arguments. */
+       typedef value_type(*fun_type4)(value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with five arguments. */
+       typedef value_type(*fun_type5)(value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with six arguments. */
+       typedef value_type(*fun_type6)(value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with seven arguments. */
+       typedef value_type(*fun_type7)(value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with eight arguments. */
+       typedef value_type(*fun_type8)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with nine arguments. */
+       typedef value_type(*fun_type9)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with ten arguments. */
+       typedef value_type(*fun_type10)(value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions without arguments. */
+       typedef value_type(*bulkfun_type0)(int, int);
+
+       /** \brief Callback type used for functions with a single arguments. */
+       typedef value_type(*bulkfun_type1)(int, int, value_type);
+
+       /** \brief Callback type used for functions with two arguments. */
+       typedef value_type(*bulkfun_type2)(int, int, value_type, value_type);
+
+       /** \brief Callback type used for functions with three arguments. */
+       typedef value_type(*bulkfun_type3)(int, int, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with four arguments. */
+       typedef value_type(*bulkfun_type4)(int, int, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with five arguments. */
+       typedef value_type(*bulkfun_type5)(int, int, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with six arguments. */
+       typedef value_type(*bulkfun_type6)(int, int, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with seven arguments. */
+       typedef value_type(*bulkfun_type7)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with eight arguments. */
+       typedef value_type(*bulkfun_type8)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with nine arguments. */
+       typedef value_type(*bulkfun_type9)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with ten arguments. */
+       typedef value_type(*bulkfun_type10)(int, int, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions with a variable argument list. */
+       typedef value_type(*multfun_type)(const value_type*, int);
+
+       /** \brief Callback type used for functions taking a string as an argument. */
+       typedef value_type(*strfun_type1)(const char_type*);
+
+       /** \brief Callback type used for functions taking a string and a value as arguments. */
+       typedef value_type(*strfun_type2)(const char_type*, value_type);
+
+       /** \brief Callback type used for functions taking a string and two values as arguments. */
+       typedef value_type(*strfun_type3)(const char_type*, value_type, value_type);
+
+       /** \brief Callback type used for functions taking a string and a value as arguments. */
+       typedef value_type(*strfun_type4)(const char_type*, value_type, value_type, value_type);
+
+       /** \brief Callback type used for functions taking a string and two values as arguments. */
+       typedef value_type(*strfun_type5)(const char_type*, value_type, value_type, value_type, value_type);
+
+       /** \brief Callback used for functions that identify values in a string. */
+       typedef int (*identfun_type)(const char_type* sExpr, int* nPos, value_type* fVal);
+
+       /** \brief Callback used for variable creation factory functions. */
+       typedef value_type* (*facfun_type)(const char_type*, void*);
+
+       static const int MaxLenExpression = 5000;
+       static const int MaxLenIdentifier = 100;
+       static const string_type ParserVersion = string_type(_T("2.3.2"));
+       static const string_type ParserVersionDate = string_type(_T("20200617"));
+} // end of namespace
+
+#if defined(_MSC_VER)
+       #pragma warning(pop)
+#endif
+
+#endif
+
diff --git a/src/include/gromacs/external/muparser/include/muParserError.h b/src/include/gromacs/external/muparser/include/muParserError.h
new file mode 100644 (file)
index 0000000..438b8b6
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_ERROR_H
+#define MU_PARSER_ERROR_H
+
+#include <stdexcept>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <memory>
+
+#include "muParserDef.h"
+
+/** \file
+       \brief This file defines the error class used by the parser.
+*/
+
+namespace mu
+{
+       /** \brief A class that handles the error messages.     */
+       class ParserErrorMsg final
+       {
+       public:
+               static const ParserErrorMsg& Instance();
+               string_type operator[](unsigned a_iIdx) const;
+
+       private:
+               ParserErrorMsg& operator=(const ParserErrorMsg&) = delete;
+               ParserErrorMsg(const ParserErrorMsg&) = delete;
+               ParserErrorMsg();
+
+               ~ParserErrorMsg() = default;
+
+               std::vector<string_type>  m_vErrMsg;  ///< A vector with the predefined error messages
+       };
+
+
+       /** \brief Error class of the parser.
+
+         Part of the math parser package.
+       */
+       class API_EXPORT_CXX ParserError
+       {
+       private:
+
+               /** \brief Replace all ocuurences of a substring with another string. */
+               void ReplaceSubString(string_type& strSource, const string_type& strFind, const string_type& strReplaceWith);
+               void Reset();
+
+       public:
+
+               ParserError();
+               explicit ParserError(EErrorCodes a_iErrc);
+               explicit ParserError(const string_type& sMsg);
+               ParserError(EErrorCodes a_iErrc, const string_type& sTok, const string_type& sFormula = string_type(), int a_iPos = -1);
+               ParserError(EErrorCodes a_iErrc, int a_iPos, const string_type& sTok);
+               ParserError(const char_type* a_szMsg, int a_iPos = -1, const string_type& sTok = string_type());
+               ParserError(const ParserError& a_Obj);
+
+               ParserError& operator=(const ParserError& a_Obj);
+               ~ParserError();
+
+               void SetFormula(const string_type& a_strFormula);
+               const string_type& GetExpr() const;
+               const string_type& GetMsg() const;
+               int GetPos() const;
+               const string_type& GetToken() const;
+               EErrorCodes GetCode() const;
+
+       private:
+               string_type m_strMsg;     ///< The message string
+               string_type m_strFormula; ///< Formula string
+               string_type m_strTok;     ///< Token related with the error
+               int m_iPos;               ///< Formula position related to the error 
+               EErrorCodes m_iErrc;      ///< Error code
+               const ParserErrorMsg& m_ErrMsg;
+       };
+
+} // namespace mu
+
+#endif
+
diff --git a/src/include/gromacs/external/muparser/include/muParserFixes.h b/src/include/gromacs/external/muparser/include/muParserFixes.h
new file mode 100644 (file)
index 0000000..59aecad
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_FIXES_H
+#define MU_PARSER_FIXES_H
+
+/** \file
+       \brief This file contains compatibility fixes for some platforms.
+*/
+
+//
+// Compatibility fixes
+//
+
+/* From http://gcc.gnu.org/wiki/Visibility */
+/* Generic helper definitions for shared library support */
+#if defined _WIN32 || defined __CYGWIN__
+       #define MUPARSER_HELPER_DLL_IMPORT __declspec(dllimport)
+       #define MUPARSER_HELPER_DLL_EXPORT __declspec(dllexport)
+       #define MUPARSER_HELPER_DLL_LOCAL
+#else
+       #if __GNUC__ >= 4
+               #define MUPARSER_HELPER_DLL_IMPORT __attribute__ ((visibility ("default")))
+               #define MUPARSER_HELPER_DLL_EXPORT __attribute__ ((visibility ("default")))
+               #define MUPARSER_HELPER_DLL_LOCAL  __attribute__ ((visibility ("hidden")))
+       #else
+               #define MUPARSER_HELPER_DLL_IMPORT
+               #define MUPARSER_HELPER_DLL_EXPORT
+               #define MUPARSER_HELPER_DLL_LOCAL
+       #endif
+#endif
+
+/* 
+       Now we use the generic helper definitions above to define API_EXPORT_CXX and MUPARSER_LOCAL.
+       API_EXPORT_CXX is used for the public API symbols. It either DLL imports or DLL exports (or does nothing for static build)
+       MUPARSER_LOCAL is used for non-api symbols.
+*/
+
+#ifndef MUPARSER_STATIC /* defined if muParser is compiled as a DLL */
+
+       #ifdef MUPARSERLIB_EXPORTS /* defined if we are building the muParser DLL (instead of using it) */
+               #define API_EXPORT_CXX MUPARSER_HELPER_DLL_EXPORT
+       #else
+               #define API_EXPORT_CXX MUPARSER_HELPER_DLL_IMPORT
+       #endif /* MUPARSER_DLL_EXPORTS */
+       #define MUPARSER_LOCAL MUPARSER_HELPER_DLL_LOCAL
+
+#else /* MUPARSER_STATIC is defined: this means muParser is a static lib. */
+
+       #define API_EXPORT_CXX
+       #define MUPARSER_LOCAL
+
+#endif /* !MUPARSER_STATIC */
+
+
+#ifdef _WIN32
+       #define API_EXPORT(TYPE) API_EXPORT_CXX TYPE __cdecl
+#else
+       #define API_EXPORT(TYPE) TYPE
+#endif
+
+
+#endif // include guard
+
+
diff --git a/src/include/gromacs/external/muparser/include/muParserInt.h b/src/include/gromacs/external/muparser/include/muParserInt.h
new file mode 100644 (file)
index 0000000..dd772cf
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_INT_H
+#define MU_PARSER_INT_H
+
+#include "muParserBase.h"
+#include <vector>
+
+
+/** \file
+       \brief Definition of a parser using integer value.
+*/
+
+
+namespace mu
+{
+
+       /** \brief Mathematical expressions parser.
+
+         This version of the parser handles only integer numbers. It disables the built in operators thus it is
+         slower than muParser. Integer values are stored in the double value_type and converted if needed.
+       */
+       class ParserInt : public ParserBase
+       {
+       private:
+               static int  Round(value_type v) { return (int)(v + ((v >= 0) ? 0.5 : -0.5)); };
+
+               static value_type  Abs(value_type);
+               static value_type  Sign(value_type);
+               static value_type  Ite(value_type, value_type, value_type);
+               // !! The unary Minus is a MUST, otherwise you can't use negative signs !!
+               static value_type  UnaryMinus(value_type);
+               // Functions with variable number of arguments
+               static value_type  Sum(const value_type* a_afArg, int a_iArgc);  // sum
+               static value_type  Min(const value_type* a_afArg, int a_iArgc);  // minimum
+               static value_type  Max(const value_type* a_afArg, int a_iArgc);  // maximum
+               // binary operator callbacks
+               static value_type  Add(value_type v1, value_type v2);
+               static value_type  Sub(value_type v1, value_type v2);
+               static value_type  Mul(value_type v1, value_type v2);
+               static value_type  Div(value_type v1, value_type v2);
+               static value_type  Mod(value_type v1, value_type v2);
+               static value_type  Pow(value_type v1, value_type v2);
+               static value_type  Shr(value_type v1, value_type v2);
+               static value_type  Shl(value_type v1, value_type v2);
+               static value_type  LogAnd(value_type v1, value_type v2);
+               static value_type  LogOr(value_type v1, value_type v2);
+               static value_type  And(value_type v1, value_type v2);
+               static value_type  Or(value_type v1, value_type v2);
+               static value_type  Xor(value_type v1, value_type v2);
+               static value_type  Less(value_type v1, value_type v2);
+               static value_type  Greater(value_type v1, value_type v2);
+               static value_type  LessEq(value_type v1, value_type v2);
+               static value_type  GreaterEq(value_type v1, value_type v2);
+               static value_type  Equal(value_type v1, value_type v2);
+               static value_type  NotEqual(value_type v1, value_type v2);
+               static value_type  Not(value_type v1);
+
+               static int IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_iVal);
+               static int IsBinVal(const char_type* a_szExpr, int* a_iPos, value_type* a_iVal);
+               static int IsVal(const char_type* a_szExpr, int* a_iPos, value_type* a_iVal);
+
+               /** \brief A facet class used to change decimal and thousands separator. */
+               template<class TChar>
+               class change_dec_sep : public std::numpunct<TChar>
+               {
+               public:
+
+                       explicit change_dec_sep(char_type cDecSep, char_type cThousandsSep = 0, int nGroup = 3)
+                               :std::numpunct<TChar>()
+                               , m_cDecPoint(cDecSep)
+                               , m_cThousandsSep(cThousandsSep)
+                               , m_nGroup(nGroup)
+                       {}
+
+               protected:
+
+                       virtual char_type do_decimal_point() const
+                       {
+                               return m_cDecPoint;
+                       }
+
+                       virtual char_type do_thousands_sep() const
+                       {
+                               return m_cThousandsSep;
+                       }
+
+                       virtual std::string do_grouping() const
+                       {
+                               // fix for issue 4: https://code.google.com/p/muparser/issues/detail?id=4
+                               // courtesy of Jens Bartsch
+                               // original code:
+                               //        return std::string(1, (char)m_nGroup); 
+                               // new code:
+                               return std::string(1, (char)(m_cThousandsSep > 0 ? m_nGroup : CHAR_MAX));
+                       }
+
+               private:
+
+                       int m_nGroup;
+                       char_type m_cDecPoint;
+                       char_type m_cThousandsSep;
+               };
+
+       public:
+               ParserInt();
+
+               virtual void InitFun();
+               virtual void InitOprt();
+               virtual void InitConst();
+               virtual void InitCharSets();
+       };
+
+} // namespace mu
+
+#endif
+
diff --git a/src/include/gromacs/external/muparser/include/muParserTemplateMagic.h b/src/include/gromacs/external/muparser/include/muParserTemplateMagic.h
new file mode 100644 (file)
index 0000000..1b26d00
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_TEMPLATE_MAGIC_H
+#define MU_PARSER_TEMPLATE_MAGIC_H
+
+#include <cmath>
+#include "muParserError.h"
+
+
+namespace mu
+{
+       //-----------------------------------------------------------------------------------------------
+       //
+       // Compile time type detection
+       //
+       //-----------------------------------------------------------------------------------------------
+
+       /** \brief A class singling out integer types at compile time using
+                          template meta programming.
+       */
+       template<typename T>
+       struct TypeInfo
+       {
+               static bool IsInteger() { return false; }
+       };
+
+       template<>
+       struct TypeInfo<char>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<short>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<int>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<long>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<unsigned char>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<unsigned short>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<unsigned int>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+       template<>
+       struct TypeInfo<unsigned long>
+       {
+               static bool IsInteger() { return true; }
+       };
+
+
+       //-----------------------------------------------------------------------------------------------
+       //
+       // Standard math functions with dummy overload for integer types
+       //
+       //-----------------------------------------------------------------------------------------------
+
+       /** \brief A template class for providing wrappers for essential math functions.
+
+         This template is spezialized for several types in order to provide a unified interface
+         for parser internal math function calls regardless of the data type.
+       */
+       template<typename T>
+       struct MathImpl
+       {
+               static T Sin(T v) { return sin(v); }
+               static T Cos(T v) { return cos(v); }
+               static T Tan(T v) { return tan(v); }
+               static T ASin(T v) { return asin(v); }
+               static T ACos(T v) { return acos(v); }
+               static T ATan(T v) { return atan(v); }
+               static T ATan2(T v1, T v2) { return atan2(v1, v2); }
+               static T Sinh(T v) { return sinh(v); }
+               static T Cosh(T v) { return cosh(v); }
+               static T Tanh(T v) { return tanh(v); }
+               static T ASinh(T v) { return log(v + sqrt(v * v + 1)); }
+               static T ACosh(T v) { return log(v + sqrt(v * v - 1)); }
+               static T ATanh(T v) { return ((T)0.5 * log((1 + v) / (1 - v))); }
+               static T Log(T v) { return log(v); }
+               static T Log2(T v) { return log(v) / log((T)2); } // Logarithm base 2
+               static T Log10(T v) { return log10(v); }         // Logarithm base 10
+               static T Exp(T v) { return exp(v); }
+               static T Abs(T v) { return (v >= 0) ? v : -v; }
+               static T Sqrt(T v) { return sqrt(v); }
+               static T Rint(T v) { return floor(v + (T)0.5); }
+               static T Sign(T v) { return (T)((v < 0) ? -1 : (v > 0) ? 1 : 0); }
+               static T Pow(T v1, T v2) { return std::pow(v1, v2); }
+
+               static T UnaryMinus(T v) { return -v; }
+               static T UnaryPlus(T v) { return v; }
+
+               static T Sum(const T *a_afArg, int a_iArgc)
+               {
+                       if (!a_iArgc)
+                               throw ParserError(_T("too few arguments for function sum."));
+
+                       T fRes = 0;
+                       for (int i = 0; i < a_iArgc; ++i) fRes += a_afArg[i];
+                       return fRes;
+               }
+
+               static T Avg(const T *a_afArg, int a_iArgc)
+               {
+                       if (!a_iArgc)
+                               throw ParserError(_T("too few arguments for function sum."));
+
+                       T fRes = 0;
+                       for (int i = 0; i < a_iArgc; ++i) fRes += a_afArg[i];
+                       return fRes / (T)a_iArgc;
+               }
+
+               static T Min(const T *a_afArg, int a_iArgc)
+               {
+                       if (!a_iArgc)
+                               throw ParserError(_T("too few arguments for function min."));
+
+                       T fRes = a_afArg[0];
+                       for (int i = 0; i < a_iArgc; ++i)
+                               fRes = std::min(fRes, a_afArg[i]);
+
+                       return fRes;
+               }
+
+               static T Max(const T *a_afArg, int a_iArgc)
+               {
+                       if (!a_iArgc)
+                               throw ParserError(_T("too few arguments for function min."));
+
+                       T fRes = a_afArg[0];
+                       for (int i = 0; i < a_iArgc; ++i) fRes = std::max(fRes, a_afArg[i]);
+
+                       return fRes;
+               }
+
+
+#if defined (__GNUG__)
+               // Bei zu genauer definition von pi kann die Berechnung von
+               // sin(pi*a) mit a=1 10 x langsamer sein! 
+               static constexpr T CONST_PI = (T)3.141592653589;
+#else
+               static constexpr T CONST_PI = (T)3.141592653589793238462643;
+#endif
+
+               static constexpr T CONST_E = (T)2.718281828459045235360287;
+       };
+}
+
+#endif
diff --git a/src/include/gromacs/external/muparser/include/muParserTest.h b/src/include/gromacs/external/muparser/include/muParserTest.h
new file mode 100644 (file)
index 0000000..9f4be55
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_TEST_H
+#define MU_PARSER_TEST_H
+
+#include <string>
+#include <cstdlib>
+#include <numeric> // for accumulate
+#include "muParser.h"
+#include "muParserInt.h"
+
+/** \file
+       \brief This file contains the parser test class.
+*/
+
+namespace mu
+{
+       /** \brief Namespace for test cases. */
+       namespace Test
+       {
+               /** \brief Test cases for unit testing. */
+               class API_EXPORT_CXX ParserTester final
+               {
+               private:
+                       static int c_iCount;
+
+                       static value_type f0() { return 42; };
+
+                       // Multiarg callbacks
+                       static value_type f1of1(value_type v) { return v; };
+
+                       static value_type f1of2(value_type v, value_type) { return v; };
+                       static value_type f2of2(value_type, value_type v) { return v; };
+
+                       static value_type f1of3(value_type v, value_type, value_type) { return v; };
+                       static value_type f2of3(value_type, value_type v, value_type) { return v; };
+                       static value_type f3of3(value_type, value_type, value_type v) { return v; };
+
+                       static value_type f1of4(value_type v, value_type, value_type, value_type) { return v; }
+                       static value_type f2of4(value_type, value_type v, value_type, value_type) { return v; }
+                       static value_type f3of4(value_type, value_type, value_type v, value_type) { return v; }
+                       static value_type f4of4(value_type, value_type, value_type, value_type v) { return v; }
+
+                       static value_type f1of5(value_type v, value_type, value_type, value_type, value_type) { return v; }
+                       static value_type f2of5(value_type, value_type v, value_type, value_type, value_type) { return v; }
+                       static value_type f3of5(value_type, value_type, value_type v, value_type, value_type) { return v; }
+                       static value_type f4of5(value_type, value_type, value_type, value_type v, value_type) { return v; }
+                       static value_type f5of5(value_type, value_type, value_type, value_type, value_type v) { return v; }
+
+                       static value_type Min(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1 < a_fVal2) ? a_fVal1 : a_fVal2; }
+                       static value_type Max(value_type a_fVal1, value_type a_fVal2) { return (a_fVal1 > a_fVal2) ? a_fVal1 : a_fVal2; }
+
+                       static value_type plus2(value_type v1) { return v1 + 2; }
+                       static value_type times3(value_type v1) { return v1 * 3; }
+                       static value_type sqr(value_type v1) { return v1 * v1; }
+                       static value_type sign(value_type v) { return -v; }
+                       static value_type add(value_type v1, value_type v2) { return v1 + v2; }
+                       static value_type land(value_type v1, value_type v2) { return (int)v1 & (int)v2; }
+
+
+                       static value_type FirstArg(const value_type* a_afArg, int a_iArgc)
+                       {
+                               if (!a_iArgc)
+                                       throw mu::Parser::exception_type(_T("too few arguments for function FirstArg."));
+
+                               return  a_afArg[0];
+                       }
+
+                       static value_type LastArg(const value_type* a_afArg, int a_iArgc)
+                       {
+                               if (!a_iArgc)
+                                       throw mu::Parser::exception_type(_T("too few arguments for function LastArg."));
+
+                               return  a_afArg[a_iArgc - 1];
+                       }
+
+                       static value_type Sum(const value_type* a_afArg, int a_iArgc)
+                       {
+                               if (!a_iArgc)
+                                       throw mu::Parser::exception_type(_T("too few arguments for function sum."));
+
+                               value_type fRes = 0;
+                               for (int i = 0; i < a_iArgc; ++i) fRes += a_afArg[i];
+                               return fRes;
+                       }
+
+                       static value_type Rnd(value_type v)
+                       {
+                               return (value_type)(1 + (v * std::rand() / (RAND_MAX + 1.0)));
+                       }
+
+                       static value_type RndWithString(const char_type*)
+                       {
+                               return (value_type)(1.0 + (1000.0 * std::rand() / (RAND_MAX + 1.0)));
+                       }
+
+                       static value_type Ping()
+                       {
+                               return 10;
+                       }
+
+                       static value_type ValueOf(const char_type*)
+                       {
+                               return 123;
+                       }
+
+                       static value_type StrFun1(const char_type* v1)
+                       {
+                               int val(0);
+                               stringstream_type(v1) >> val;
+                               return (value_type)val;
+                       }
+
+                       static value_type StrFun2(const char_type* v1, value_type v2)
+                       {
+                               int val(0);
+                               stringstream_type(v1) >> val;
+                               return (value_type)(val + v2);
+                       }
+
+                       static value_type StrFun3(const char_type* v1, value_type v2, value_type v3)
+                       {
+                               int val(0);
+                               stringstream_type(v1) >> val;
+                               return val + v2 + v3;
+                       }
+
+                       static value_type StrFun4(const char_type* v1, value_type v2, value_type v3, value_type v4)
+                       {
+                               int val(0);
+                               stringstream_type(v1) >> val;
+                               return val + v2 + v3 + v4;
+                       }
+
+                       static value_type StrFun5(const char_type* v1, value_type v2, value_type v3, value_type v4, value_type v5)
+                       {
+                               int val(0);
+                               stringstream_type(v1) >> val;
+                               return val + v2 + v3 + v4 + v5;
+                       }
+
+                       static value_type StrToFloat(const char_type* a_szMsg)
+                       {
+                               value_type val(0);
+                               stringstream_type(a_szMsg) >> val;
+                               return val;
+                       }
+
+                       // postfix operator callback
+                       static value_type Mega(value_type a_fVal) { return a_fVal * (value_type)1e6; }
+                       static value_type Micro(value_type a_fVal) { return a_fVal * (value_type)1e-6; }
+                       static value_type Milli(value_type a_fVal) { return a_fVal / (value_type)1e3; }
+
+                       // Custom value recognition
+                       static int IsHexVal(const char_type* a_szExpr, int* a_iPos, value_type* a_fVal);
+
+                       int TestNames();
+                       int TestSyntax();
+                       int TestMultiArg();
+                       int TestPostFix();
+                       int TestExpression();
+                       int TestInfixOprt();
+                       int TestBinOprt();
+                       int TestVarConst();
+                       int TestInterface();
+                       int TestException();
+                       int TestStrArg();
+                       int TestIfThenElse();
+                       int TestBulkMode();
+                       int TestOssFuzzTestCases();
+
+                       void Abort() const;
+
+               public:
+                       typedef int (ParserTester::* testfun_type)();
+
+                       ParserTester();
+                       int Run();
+
+               private:
+                       std::vector<testfun_type> m_vTestFun;
+                       void AddTest(testfun_type a_pFun);
+
+                       // Test Double Parser
+                       int EqnTest(const string_type& a_str, double a_fRes, bool a_fPass);
+                       int EqnTestWithVarChange(const string_type& a_str, double a_fRes1, double a_fVar1,      double a_fRes2, double a_fVar2);
+                       int ThrowTest(const string_type& a_str, int a_iErrc, bool a_bFail = true);
+
+                       // Test Int Parser
+                       int EqnTestInt(const string_type& a_str, double a_fRes, bool a_fPass);
+
+                       // Test Bulkmode
+                       int EqnTestBulk(const string_type& a_str, double a_fRes[4], bool a_fPass);
+
+               };
+       } // namespace Test
+} // namespace mu
+
+#endif
+
diff --git a/src/include/gromacs/external/muparser/include/muParserToken.h b/src/include/gromacs/external/muparser/include/muParserToken.h
new file mode 100644 (file)
index 0000000..f12a4e8
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_TOKEN_H
+#define MU_PARSER_TOKEN_H
+
+#include <string>
+#include <stack>
+#include <vector>
+#include <memory>
+
+#if defined(_MSC_VER)
+       #pragma warning(push)
+       #pragma warning(disable : 26812) 
+#endif
+
+#include "muParserError.h"
+#include "muParserCallback.h"
+
+/** \file
+       \brief This file contains the parser token definition.
+*/
+
+namespace mu
+{
+       /** \brief Encapsulation of the data for a single formula token.
+
+               Formula token implementation. Part of the Math Parser Package.
+               Formula tokens can be either one of the following:
+               <ul>
+                       <li>value</li>
+                       <li>variable</li>
+                       <li>function with numerical arguments</li>
+                       <li>functions with a string as argument</li>
+                       <li>prefix operators</li>
+                       <li>infix operators</li>
+                       <li>binary operator</li>
+               </ul>
+       */
+       template<typename TBase, typename TString>
+       class ParserToken final
+       {
+       private:
+
+               ECmdCode  m_iCode;  ///< Type of the token; The token type is a constant of type #ECmdCode.
+               ETypeCode m_iType;
+               void* m_pTok;           ///< Stores Token pointer; not applicable for all tokens
+               int  m_iIdx;            ///< An otional index to an external buffer storing the token data
+               TString m_strTok;   ///< Token string
+               TString m_strVal;   ///< Value for string variables
+               value_type m_fVal;  ///< the value 
+               std::unique_ptr<ParserCallback> m_pCallback;
+
+       public:
+
+               /** \brief Constructor (default).
+
+                       Sets token to an neutral state of type cmUNKNOWN.
+                       \throw nothrow
+                       \sa ECmdCode
+               */
+               ParserToken()
+                       :m_iCode(cmUNKNOWN)
+                       , m_iType(tpVOID)
+                       , m_pTok(0)
+                       , m_iIdx(-1)
+                       , m_strTok()
+                       , m_strVal()
+                       , m_fVal(0)
+                       , m_pCallback()
+               {}
+
+               //------------------------------------------------------------------------------
+               /** \brief Create token from another one.
+
+                       Implemented by calling Assign(...)
+                       \throw nothrow
+                       \post m_iType==cmUNKNOWN
+                       \sa #Assign
+               */
+               ParserToken(const ParserToken& a_Tok)
+               {
+                       Assign(a_Tok);
+               }
+
+               
+               /** \brief Assignment operator.
+
+                       Copy token state from another token and return this.
+                       Implemented by calling Assign(...).
+                       \throw nothrow
+               */
+               ParserToken& operator=(const ParserToken& a_Tok)
+               {
+                       Assign(a_Tok);
+                       return *this;
+               }
+
+
+               /** \brief Copy token information from argument.
+
+                       \throw nothrow
+               */
+               void Assign(const ParserToken& a_Tok)
+               {
+                       m_iCode = a_Tok.m_iCode;
+                       m_pTok = a_Tok.m_pTok;
+                       m_strTok = a_Tok.m_strTok;
+                       m_iIdx = a_Tok.m_iIdx;
+                       m_strVal = a_Tok.m_strVal;
+                       m_iType = a_Tok.m_iType;
+                       m_fVal = a_Tok.m_fVal;
+                       // create new callback object if a_Tok has one 
+                       m_pCallback.reset(a_Tok.m_pCallback.get() ? a_Tok.m_pCallback->Clone() : 0);
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Assign a token type.
+
+                 Token may not be of type value, variable or function. Those have separate set functions.
+
+                 \pre [assert] a_iType!=cmVAR
+                 \pre [assert] a_iType!=cmVAL
+                 \pre [assert] a_iType!=cmFUNC
+                 \post m_fVal = 0
+                 \post m_pTok = 0
+               */
+               ParserToken& Set(ECmdCode a_iType, const TString& a_strTok = TString())
+               {
+                       // The following types can't be set this way, they have special Set functions
+                       MUP_ASSERT(a_iType != cmVAR);
+                       MUP_ASSERT(a_iType != cmVAL);
+                       MUP_ASSERT(a_iType != cmFUNC);
+
+                       m_iCode = a_iType;
+                       m_iType = tpVOID;
+                       m_pTok = 0;
+                       m_strTok = a_strTok;
+                       m_iIdx = -1;
+
+                       return *this;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Set Callback type. */
+               ParserToken& Set(const ParserCallback& a_pCallback, const TString& a_sTok)
+               {
+                       MUP_ASSERT(a_pCallback.GetAddr());
+
+                       m_iCode = a_pCallback.GetCode();
+                       m_iType = tpVOID;
+                       m_strTok = a_sTok;
+                       m_pCallback.reset(new ParserCallback(a_pCallback));
+
+                       m_pTok = 0;
+                       m_iIdx = -1;
+
+                       return *this;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Make this token a value token.
+
+                       Member variables not necessary for value tokens will be invalidated.
+                       \throw nothrow
+               */
+               ParserToken& SetVal(TBase a_fVal, const TString& a_strTok = TString())
+               {
+                       m_iCode = cmVAL;
+                       m_iType = tpDBL;
+                       m_fVal = a_fVal;
+                       m_strTok = a_strTok;
+                       m_iIdx = -1;
+
+                       m_pTok = 0;
+                       m_pCallback.reset(0);
+
+                       return *this;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief make this token a variable token.
+
+                       Member variables not necessary for variable tokens will be invalidated.
+                       \throw nothrow
+               */
+               ParserToken& SetVar(TBase* a_pVar, const TString& a_strTok)
+               {
+                       m_iCode = cmVAR;
+                       m_iType = tpDBL;
+                       m_strTok = a_strTok;
+                       m_iIdx = -1;
+                       m_pTok = (void*)a_pVar;
+                       m_pCallback.reset(0);
+                       return *this;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Make this token a variable token.
+
+                       Member variables not necessary for variable tokens will be invalidated.
+                       \throw nothrow
+               */
+               ParserToken& SetString(const TString& a_strTok, std::size_t a_iSize)
+               {
+                       m_iCode = cmSTRING;
+                       m_iType = tpSTR;
+                       m_strTok = a_strTok;
+                       m_iIdx = static_cast<int>(a_iSize);
+
+                       m_pTok = 0;
+                       m_pCallback.reset(0);
+                       return *this;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Set an index associated with the token related data.
+
+                       In cmSTRFUNC - This is the index to a string table in the main parser.
+                       \param a_iIdx The index the string function result will take in the bytecode parser.
+                       \throw exception_type if #a_iIdx<0 or #m_iType!=cmSTRING
+               */
+               void SetIdx(int a_iIdx)
+               {
+                       if (m_iCode != cmSTRING || a_iIdx < 0)
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       m_iIdx = a_iIdx;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Return Index associated with the token related data.
+
+                       In cmSTRFUNC - This is the index to a string table in the main parser.
+
+                       \throw exception_type if #m_iIdx<0 or #m_iType!=cmSTRING
+                       \return The index the result will take in the Bytecode calculatin array (#m_iIdx).
+               */
+               int GetIdx() const
+               {
+                       if (m_iIdx < 0 || m_iCode != cmSTRING)
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       return m_iIdx;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Return the token type.
+
+                       \return #m_iType
+                       \throw nothrow
+               */
+               ECmdCode GetCode() const
+               {
+                       if (m_pCallback.get())
+                       {
+                               return m_pCallback->GetCode();
+                       }
+                       else
+                       {
+                               return m_iCode;
+                       }
+               }
+
+               //------------------------------------------------------------------------------
+               ETypeCode GetType() const
+               {
+                       if (m_pCallback.get())
+                       {
+                               return m_pCallback->GetType();
+                       }
+                       else
+                       {
+                               return m_iType;
+                       }
+               }
+
+               //------------------------------------------------------------------------------
+               int GetPri() const
+               {
+                       if (!m_pCallback.get())
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       if (m_pCallback->GetCode() != cmOPRT_BIN && m_pCallback->GetCode() != cmOPRT_INFIX)
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       return m_pCallback->GetPri();
+               }
+
+               //------------------------------------------------------------------------------
+               EOprtAssociativity GetAssociativity() const
+               {
+                       if (m_pCallback.get() == nullptr || m_pCallback->GetCode() != cmOPRT_BIN)
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       return m_pCallback->GetAssociativity();
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Return the address of the callback function assoziated with
+                                  function and operator tokens.
+
+                       \return The pointer stored in #m_pTok.
+                       \throw exception_type if token type is non of:
+                                  <ul>
+                                        <li>cmFUNC</li>
+                                        <li>cmSTRFUNC</li>
+                                        <li>cmPOSTOP</li>
+                                        <li>cmINFIXOP</li>
+                                        <li>cmOPRT_BIN</li>
+                                  </ul>
+                       \sa ECmdCode
+               */
+               generic_fun_type GetFuncAddr() const
+               {
+                       return (m_pCallback.get()) ? (generic_fun_type)m_pCallback->GetAddr() : 0;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \biref Get value of the token.
+
+                       Only applicable to variable and value tokens.
+                       \throw exception_type if token is no value/variable token.
+               */
+               TBase GetVal() const
+               {
+                       switch (m_iCode)
+                       {
+                       case cmVAL:  return m_fVal;
+                       case cmVAR:  return *((TBase*)m_pTok);
+                       default:     throw ParserError(ecVAL_EXPECTED);
+                       }
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Get address of a variable token.
+
+                 Valid only if m_iType==CmdVar.
+                 \throw exception_type if token is no variable token.
+               */
+               TBase* GetVar() const
+               {
+                       if (m_iCode != cmVAR)
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       return (TBase*)m_pTok;
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Return the number of function arguments.
+
+                 Valid only if m_iType==CmdFUNC.
+               */
+               int GetArgCount() const
+               {
+                       MUP_ASSERT(m_pCallback.get());
+
+                       if (!m_pCallback->GetAddr())
+                               throw ParserError(ecINTERNAL_ERROR);
+
+                       return m_pCallback->GetArgc();
+               }
+
+               //------------------------------------------------------------------------------
+               /** \brief Return the token identifier.
+
+                       If #m_iType is cmSTRING the token identifier is the value of the string argument
+                       for a string function.
+                       \return #m_strTok
+                       \throw nothrow
+                       \sa m_strTok
+               */
+               const TString& GetAsString() const
+               {
+                       return m_strTok;
+               }
+       };
+} // namespace mu
+
+#if defined(_MSC_VER)
+       #pragma warning(pop)
+#endif
+
+#endif
diff --git a/src/include/gromacs/external/muparser/include/muParserTokenReader.h b/src/include/gromacs/external/muparser/include/muParserTokenReader.h
new file mode 100644 (file)
index 0000000..caeb920
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+
+        _____  __ _____________ _______  ______ ___________
+       /     \|  |  \____ \__  \\_  __ \/  ___// __ \_  __ \
+   |  Y Y  \  |  /  |_> > __ \|  | \/\___ \\  ___/|  | \/
+   |__|_|  /____/|   __(____  /__|  /____  >\___  >__|
+                \/      |__|       \/           \/     \/
+   Copyright (C) 2004 - 2020 Ingo Berg
+
+       Redistribution and use in source and binary forms, with or without modification, are permitted
+       provided that the following conditions are met:
+
+         * Redistributions of source code must retain the above copyright notice, this list of
+               conditions and the following disclaimer.
+         * Redistributions in binary form must reproduce the above copyright notice, this list of
+               conditions and the following disclaimer in the documentation and/or other materials provided
+               with the distribution.
+
+       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+       IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+       FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+       CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+       DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+       IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+       OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef MU_PARSER_TOKEN_READER_H
+#define MU_PARSER_TOKEN_READER_H
+
+#include <cstdio>
+#include <cstring>
+#include <list>
+#include <map>
+#include <memory>
+#include <stack>
+#include <string>
+
+#include "muParserDef.h"
+#include "muParserToken.h"
+
+/** \file
+       \brief This file contains the parser token reader definition.
+*/
+
+
+namespace mu
+{
+       // Forward declaration
+       class ParserBase;
+
+       /** \brief Token reader for the ParserBase class. */
+       class ParserTokenReader final
+       {
+       private:
+
+               typedef ParserToken<value_type, string_type> token_type;
+
+       public:
+
+               ParserTokenReader(ParserBase* a_pParent);
+               ParserTokenReader* Clone(ParserBase* a_pParent) const;
+
+               void AddValIdent(identfun_type a_pCallback);
+               void SetVarCreator(facfun_type a_pFactory, void* pUserData);
+               void SetFormula(const string_type& a_strFormula);
+               void SetArgSep(char_type cArgSep);
+
+               int GetPos() const;
+               const string_type& GetExpr() const;
+               varmap_type& GetUsedVar();
+               char_type GetArgSep() const;
+
+               void IgnoreUndefVar(bool bIgnore);
+               void ReInit();
+               token_type ReadNextToken();
+
+       private:
+
+               /** \brief Syntax codes.
+
+                       The syntax codes control the syntax check done during the first time parsing of
+                       the expression string. They are flags that indicate which tokens are allowed next
+                       if certain tokens are identified.
+               */
+               enum ESynCodes
+               {
+                       noBO = 1 << 0,                  ///< to avoid i.e. "cos(7)(" 
+                       noBC = 1 << 1,                  ///< to avoid i.e. "sin)" or "()"
+                       noVAL = 1 << 2,                 ///< to avoid i.e. "tan 2" or "sin(8)3.14"
+                       noVAR = 1 << 3,                 ///< to avoid i.e. "sin a" or "sin(8)a"
+                       noARG_SEP = 1 << 4,             ///< to avoid i.e. ",," or "+," ...
+                       noFUN = 1 << 5,                 ///< to avoid i.e. "sqrt cos" or "(1)sin"       
+                       noOPT = 1 << 6,                 ///< to avoid i.e. "(+)"
+                       noPOSTOP = 1 << 7,              ///< to avoid i.e. "(5!!)" "sin!"
+                       noINFIXOP = 1 << 8,             ///< to avoid i.e. "++4" "!!4"
+                       noEND = 1 << 9,                 ///< to avoid unexpected end of formula
+                       noSTR = 1 << 10,                ///< to block numeric arguments on string functions
+                       noASSIGN = 1 << 11,             ///< to block assignment to constant i.e. "4=7"
+                       noIF = 1 << 12,
+                       noELSE = 1 << 13,
+                       sfSTART_OF_LINE = noOPT | noBC | noPOSTOP | noASSIGN | noIF | noELSE | noARG_SEP,
+                       noANY = ~0                              ///< All of he above flags set
+               };
+
+               ParserTokenReader(const ParserTokenReader& a_Reader);
+               ParserTokenReader& operator=(const ParserTokenReader& a_Reader);
+               void Assign(const ParserTokenReader& a_Reader);
+
+               void SetParent(ParserBase* a_pParent);
+               int ExtractToken(const char_type* a_szCharSet, string_type& a_strTok, std::size_t a_iPos) const;
+               int ExtractOperatorToken(string_type& a_sTok, std::size_t a_iPos) const;
+
+               bool IsBuiltIn(token_type& a_Tok);
+               bool IsArgSep(token_type& a_Tok);
+               bool IsEOF(token_type& a_Tok);
+               bool IsInfixOpTok(token_type& a_Tok);
+               bool IsFunTok(token_type& a_Tok);
+               bool IsPostOpTok(token_type& a_Tok);
+               bool IsOprt(token_type& a_Tok);
+               bool IsValTok(token_type& a_Tok);
+               bool IsVarTok(token_type& a_Tok);
+               bool IsStrVarTok(token_type& a_Tok);
+               bool IsUndefVarTok(token_type& a_Tok);
+               bool IsString(token_type& a_Tok);
+               void Error(EErrorCodes a_iErrc, int a_iPos = -1, const string_type& a_sTok = string_type()) const;
+
+               token_type& SaveBeforeReturn(const token_type& tok);
+
+               ParserBase* m_pParser;
+               string_type m_strFormula;
+               int  m_iPos;
+               int  m_iSynFlags;
+               bool m_bIgnoreUndefVar;
+
+               const funmap_type* m_pFunDef;
+               const funmap_type* m_pPostOprtDef;
+               const funmap_type* m_pInfixOprtDef;
+               const funmap_type* m_pOprtDef;
+               const valmap_type* m_pConstDef;
+               const strmap_type* m_pStrVarDef;
+
+               varmap_type* m_pVarDef;  ///< The only non const pointer to parser internals
+               facfun_type m_pFactory;
+               void* m_pFactoryData;
+               std::list<identfun_type> m_vIdentFun; ///< Value token identification function
+               varmap_type m_UsedVar;
+               value_type m_fZero;      ///< Dummy value of zero, referenced by undefined variables
+               
+               std::stack<int> m_bracketStack;
+
+               token_type m_lastTok;
+               char_type m_cArgSep;     ///< The character used for separating function arguments
+       };
+} // namespace mu
+
+#endif
+
+
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi.h
new file mode 100644 (file)
index 0000000..009f11d
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+/*
+   thread_mpi is a cross-platform threading library for applications in
+   high-performance computing. It supports:
+
+   - Cross-platform thread primitives (thread creation, mutexes, spinlocks,
+     barriers, thread-local storage, etc.).
+   - Cross-platform atomic operations (compare-and-swap, add-return, etc) for
+     safe lock-free synchronization.
+   - An implementation of (currently, much of) MPI, either as a drop-in
+     replacement, or for use in conjunction with a networked MPI
+     implementation.
+   - Shared-memory allocation and memory management (planned, as of now).
+   - Basic lock-free data structures (planned, as of now).
+
+   Because it can be used as a drop-in replacement for MPI, existing codes
+   using MPI can start using thread_mpi without major changes in the
+   source code, assuming -- and this is a big assumption -- that the code
+   is thread-safe.
+
+   Alternatively, networked MPI calls can be used in conjunction with
+   thread_mpi calls (simply by using
+    "#include <thread_mpi.h>"
+   instead of
+    "#include <tmpi.h>"
+   and pre-fixing all thread_mpi MPI-like calls with tMPI instead of MPI.
+
+   The availability of both MPI calls and shared-memory constructs makes it
+   possible to transition (relatively) seamlessly from an MPI-style code
+   to code that's optimal on multicore CPUs.
+
+   Although MPI-style message passing isn't neccesarily optimal for
+   performance on shared-memory systems, the MPI communicator concept and
+   its emphasis on collective operations makes sense even when computing on
+   one machine with multiple cores. The communicator forms the basis for
+   the shared-memory allocation and lock-free data structure implementations
+   in thread_mpi.
+
+   Although usable as a stand-alone library, thread_mpi is designed to
+   be incorporated in the code tree, eliminating any external build
+   requirements. The BSD-style license that this library is distributed
+   with reflects this.
+
+   The atomic operations (such as compare-and-swap) are supported on:
+   - gcc on x86, x86_64, PowerPC and Itanium.
+   - Intel compilers on x86, x86_64 and Itanium.
+   - xlc on PowerPC.
+   - (partial) HP/UX compilers on Itanium.
+ */
+
+/** \file
+ *
+ * \brief Convenience header file for non-MPI compatibility.
+ *
+ * This file includes the tMPI header file thread_mpi/tmpi.h, as well
+ * as thread_mpi/threads.h and thread_mpi/atomic.h header files. If you'd
+ * like to use the components individually, include the relevant header
+ * files directly.
+ */
+
+#include "thread_mpi/atomic.h"
+#include "thread_mpi/threads.h"
+#include "thread_mpi/numa_malloc.h"
+#include "thread_mpi/barrier.h"
+#include "thread_mpi/event.h"
+#include "thread_mpi/lock.h"
+#include "thread_mpi/tmpi.h"
+#include "thread_mpi/collective.h"
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic.h
new file mode 100644 (file)
index 0000000..5dc7898
--- /dev/null
@@ -0,0 +1,630 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#ifndef TMPI_ATOMIC_H_
+#define TMPI_ATOMIC_H_
+
+/*! \file atomic.h
+ *
+ *  \brief Atomic operations for fast SMP synchronization
+ *
+ *  This file defines atomic integer operations and spinlocks for
+ *  fast synchronization in performance-critical regions.
+ *
+ *  In general, the best option is to use functions without explicit
+ *  locking, e.g. tMPI_Atomic_fetch_add() or tMPI_Atomic_cas().
+ *
+ *  Depending on the architecture/compiler, these operations may either
+ *  be provided as functions or macros; be aware that those macros may
+ *  reference their arguments repeatedly, possibly leading to multiply
+ *  evaluated code with side effects: be careful with what you use as
+ *  arguments.
+ *
+ *  Not all architectures support atomic operations though inline assembly,
+ *  and even if they do it might not be implemented here. In that case
+ *  we use a fallback mutex implementation, so you can always count on
+ *  the function interfaces working.
+ *
+ *  Don't use spinlocks in non-performance-critical regions like file I/O.
+ *  Since they always spin busy they would waste CPU cycles instead of
+ *  properly yielding to a computation thread while waiting for the disk.
+ *
+ *  Finally, note that all our spinlock operations are defined to return
+ *  0 if initialization or locking completes successfully.
+ *  This is the opposite of some other implementations, but the same standard
+ *  as used for pthread mutexes. So, if e.g. are trying to lock a spinlock,
+ *  you will have gotten the lock if the return value is 0.
+ *
+ *  tMPI_Spinlock_islocked(x) obviously still returns 1 if the lock is locked,
+ *  and 0 if it is available, though...
+ */
+/* Se the comments on the non-atomic versions for explanations */
+
+#include <stdio.h>
+
+#include "visibility.h"
+
+/* Setting TMPI_ATOMICS_DISABLED permits the build to enforce that no
+ * atomic operations are used. This is used when building to run
+ * ThreadSanitzer.
+ *
+ * It could also be useful as a temporary measure on some
+ * compiler+hardware for which the detection below fails to produce a
+ * correct result. Performance will be greatly improved by using
+ * whatever atomic operations are available, so make sure such a
+ * measure is only temporary! */
+#ifdef TMPI_ATOMICS_DISABLED
+
+#ifndef DOXYGEN
+#define TMPI_NO_ATOMICS
+#endif
+
+#else
+
+/* first check for gcc/icc platforms.
+   Some compatible compilers, like icc on linux+mac will take this path,
+   too */
+#if ( (defined(__GNUC__) || defined(__PATHSCALE__) || defined(__PGI)) && \
+    (!defined(__xlc__)) && (!defined(_CRAYC)) && (!defined(TMPI_TEST_NO_ATOMICS)) )
+
+#ifdef __GNUC__
+#define TMPI_GCC_VERSION (__GNUC__ * 10000 \
+                          + __GNUC_MINOR__ * 100 \
+                          + __GNUC_PATCHLEVEL__)
+#endif
+
+/* now check specifically for several architectures: */
+#if ((defined(__i386__) || defined(__x86_64__)) && !defined(__OPEN64__))
+/* first x86: */
+#include "atomic/gcc_x86.h"
+
+#elif (defined(__ia64__))
+/* then ia64: */
+#include "atomic/gcc_ia64.h"
+
+/* for now we use gcc intrinsics on gcc: */
+/*#elif (defined(__powerpc__) || (defined(__ppc__)) )*/
+/*#include "atomic/gcc_ppc.h"*/
+
+#elif defined(__FUJITSU) && ( defined(__sparc_v9__) || defined (__sparcv9) )
+
+/* Fujitsu FX10 SPARC compiler */
+#include "atomic/fujitsu_sparc64.h"
+
+#else
+/* otherwise, there's a generic gcc intrinsics version: */
+#include "atomic/gcc.h"
+
+#endif /* end of check for gcc specific architectures */
+
+/* not gcc: */
+#elif (defined(_MSC_VER) && (_MSC_VER >= 1200) && \
+    (!defined(TMPI_TEST_NO_ATOMICS)) )
+
+/* Microsoft Visual C on x86, define taken from FFTW who got it from
+   Morten Nissov. icc on windows will take this path.  */
+#include "atomic/msvc.h"
+
+#elif ( (defined(__IBM_GCC_ASM) || defined(__IBM_STDCPP_ASM))  && \
+    (defined(__powerpc__) || defined(__ppc__)) && \
+    (!defined(TMPI_TEST_NO_ATOMICS)) )
+
+/* PowerPC using xlC intrinsics.  */
+
+#include "atomic/xlc_ppc.h"
+
+#elif ( ( defined(__xlC__)  || defined(__xlc__) ) && \
+    (!defined(TMPI_TEST_NO_ATOMICS)) )
+/* IBM xlC compiler */
+#include "atomic/xlc_ppc.h"
+
+
+#elif (defined (__sun) && (defined(__sparcv9) || defined(__sparc)) && \
+    (!defined(TMPI_TEST_NO_ATOMICS)) )
+/* Solaris on SPARC (Sun C Compiler, Solaris Studio) */
+#include "atomic/suncc-sparc.h"
+
+#elif defined(__FUJITSU) && defined(__sparc__)
+
+/* Fujitsu FX10 SPARC compiler requires gcc compatibility with -Xg */
+#warning Atomics support for Fujitsu FX10 compiler requires -Xg (gcc compatibility)
+#define TMPI_NO_ATOMICS
+
+#elif defined(_CRAYC)
+
+/* Cray compiler */
+#include "atomic/cce.h"
+#else
+
+#ifndef DOXYGEN
+/** Indicates that no support for atomic operations is present. */
+#define TMPI_NO_ATOMICS
+#endif
+
+#endif /* platform-specific checks */
+
+#endif /* TMPI_NO_ATOMICS */
+
+#ifdef TMPI_NO_ATOMICS
+
+/* No atomic operations, use mutex fallback. Documentation is in x86 section */
+
+#ifdef TMPI_CHECK_ATOMICS
+#error No atomic operations implemented for this cpu/compiler combination.
+#endif
+
+
+/** Memory barrier operation
+
+   Modern CPUs rely heavily on out-of-order execution, and one common feature
+   is that load/stores might be reordered. Also, when using inline assembly
+   the compiler might already have loaded the variable we are changing into
+   a register, so any update to memory won't be visible.
+
+   This command creates a memory barrier, i.e. all memory results before
+   it in the code should be visible to all memory operations after it - the
+   CPU cannot propagate load/stores across it.
+
+   This barrier is a full barrier: all load and store operations of
+   instructions before it are completed, while all load and store operations
+   that are in instructions after it won't be done before this barrier.
+
+   \hideinitializer
+ */
+#define tMPI_Atomic_memory_barrier()
+
+/** Memory barrier operation with acquire semantics
+
+   This barrier is a barrier with acquire semantics: the terminology comes
+   from its common use after acquiring a lock: all load/store instructions
+   after this barrier may not be re-ordered to happen before this barrier.
+
+   \hideinitializer
+ */
+#define tMPI_Atomic_memory_barrier_acq()
+
+/** Memory barrier operation with release semantics
+
+   This barrier is a barrier with release semantics: the terminology comes
+   from its common use before releasing a lock: all load/store instructions
+   before this barrier may not be re-ordered to happen after this barrier.
+
+   \hideinitializer
+ */
+#define tMPI_Atomic_memory_barrier_rel()
+
+#ifndef DOXYGEN
+/* signal that they exist */
+#define TMPI_HAVE_ACQ_REL_BARRIERS
+#endif
+
+/** Atomic operations datatype
+ *
+ *  Portable synchronization primitives like mutexes are effective for
+ *  many purposes, but usually not very high performance.
+ *  One of the problem is that you have the overhead of a function call,
+ *  and another is that Mutexes often have extra overhead to make the
+ *  scheduling fair. Finally, if performance is important we don't want
+ *  to suspend the thread if we cannot lock a mutex, but spin-lock at 100%
+ *  CPU usage until the resources is available (e.g. increment a counter).
+ *
+ *  These things can often be implemented with inline-assembly or other
+ *  system-dependent functions, and we provide such functionality for the
+ *  most common platforms. For portability we also have a fallback
+ *  implementation using a mutex for locking.
+ *
+ *  Performance-wise, the fastest solution is always to avoid locking
+ *  completely (obvious, but remember it!). If you cannot do that, the
+ *  next best thing is to use atomic operations that e.g. increment a
+ *  counter without explicit locking. Spinlocks are useful to lock an
+ *  entire region, but leads to more overhead and can be difficult to
+ *  debug - it is up to you to make sure that only the thread owning the
+ *  lock unlocks it!
+ *
+ *  You should normally NOT use atomic operations for things like
+ *  I/O threads. These should yield to other threads while waiting for
+ *  the disk instead of spinning at 100% CPU usage.
+ *
+ *  It is imperative that you use the provided routines for reading
+ *  and writing, since some implementations require memory barriers before
+ *  the CPU or memory sees an updated result. The structure contents is
+ *  only visible here so it can be inlined for performance - it might
+ *  change without further notice.
+ *
+ *  \note No initialization is required for atomic variables.
+ *
+ *  Currently, we have (real) atomic operations for:
+ *
+ *  - gcc version 4.1 and later (all platforms)
+ *  - x86 or x86_64, using GNU compilers
+ *  - x86 or x86_64, using Intel compilers
+ *  - x86 or x86_64, using Pathscale compilers
+ *  - Itanium, using GNU compilers
+ *  - Itanium, using Intel compilers
+ *  - Itanium, using HP compilers
+ *  - PowerPC, using GNU compilers
+ *  - PowerPC, using IBM AIX compilers
+ *  - PowerPC, using IBM compilers >=7.0 under Linux or Mac OS X.
+ *  - Sparc64, using Fujitsu compilers.
+ *
+ * \see
+ * - tMPI_Atomic_get
+ * - tMPI_Atomic_set
+ * - tMPI_Atomic_cas
+ * - tMPI_Atomic_add_return
+ * - tMPI_Atomic_fetch_add
+ */
+typedef struct tMPI_Atomic
+{
+    int value; /**< The atomic value.*/
+}
+tMPI_Atomic_t;
+
+
+/** Atomic pointer type equivalent to tMPI_Atomic_t
+ *
+ * Useful for lock-free and wait-free data structures.
+ * The only operations available for this type are:
+ * \see
+ * - tMPI_Atomic_ptr_get
+ * - tMPI_Atomic_ptr_set
+ * - tMPI_Atomic_ptr_cas
+ */
+typedef struct tMPI_Atomic_ptr
+{
+    void *value; /**< The atomic pointer. */
+}
+tMPI_Atomic_ptr_t;
+
+
+/** Spinlock
+ *
+ *  Spinlocks provide a faster synchronization than mutexes,
+ *  although they consume CPU-cycles while waiting. They are implemented
+ *  with atomic operations and inline assembly whenever possible, and
+ *  otherwise we use a fallback implementation where a spinlock is identical
+ *  to a mutex (this is one of the reasons why you have to initialize them).
+ *
+ *  There are no guarantees whatsoever about fair scheduling or
+ *  debugging if you make a mistake and unlock a variable somebody
+ *  else has locked - performance is the primary goal of spinlocks.
+ *
+ * \see
+ * - tMPI_Spinlock_init
+ * - tMPI_Spinlock_lock
+ * - tMPI_Spinlock_unlock
+ * - tMPI_Spinlock_trylock
+ * - tMPI_Spinlock_wait
+ */
+typedef struct tMPI_Spinlock *tMPI_Spinlock_t;
+
+/*! \def TMPI_SPINLOCK_INITIALIZER
+ * \brief Spinlock static initializer
+ *
+ *  This is used for static spinlock initialization, and has the same
+ *  properties as TMPI_THREAD_MUTEX_INITIALIZER has for mutexes.
+ *  This is only for inlining in the tMPI_Thread.h header file. Whether
+ *  it is 0, 1, or something else when unlocked depends on the platform.
+ *  Don't assume anything about it. It might even be a mutex when using the
+ *  fallback implementation!
+ *
+ *  \hideinitializer
+ */
+#define TMPI_SPINLOCK_INITIALIZER   { NULL }
+
+/* Since mutexes guarantee memory barriers this works fine */
+/** Return value of an atomic integer
+ *
+ *  Also implements proper memory barriers when necessary.
+ *  The actual implementation is system-dependent.
+ *
+ *  \param  a   Atomic variable to read
+ *  \return     Integer value of the atomic variable
+ *
+ *  \hideinitializer
+ */
+TMPI_EXPORT
+int tMPI_Atomic_get(const tMPI_Atomic_t *a);
+
+/** Write value to an atomic integer
+ *
+ *  Also implements proper memory barriers when necessary.
+ *  The actual implementation is system-dependent.
+ *
+ *  \param  a   Atomic variable
+ *  \param  i   Integer to set the atomic variable to.
+ *
+ *  \hideinitializer
+ */
+TMPI_EXPORT
+void tMPI_Atomic_set(tMPI_Atomic_t *a, int i);
+
+
+/** Return value of an atomic pointer
+ *
+ *  Also implements proper memory barriers when necessary.
+ *  The actual implementation is system-dependent.
+ *
+ *  \param  a   Atomic variable to read
+ *  \return     Pointer value of the atomic variable
+ *
+ *  \hideinitializer
+ */
+TMPI_EXPORT
+void* tMPI_Atomic_ptr_get(const tMPI_Atomic_ptr_t *a);
+
+
+
+
+/** Write value to an atomic pointer
+ *
+ *  Also implements proper memory barriers when necessary.
+ *  The actual implementation is system-dependent.
+ *
+ *  \param  a   Atomic variable
+ *  \param  p   Pointer value to set the atomic variable to.
+ *
+ *  \hideinitializer
+ */
+TMPI_EXPORT
+void tMPI_Atomic_ptr_set(tMPI_Atomic_ptr_t *a, void *p);
+
+/** Add integer to atomic variable
+ *
+ *  Also implements proper memory barriers when necessary.
+ *  The actual implementation is system-dependent.
+ *
+ *  \param a   atomic datatype to modify
+ *  \param i   integer to increment with. Use i<0 to subtract atomically.
+ *
+ *  \return The new value (after summation).
+ */
+TMPI_EXPORT
+int tMPI_Atomic_add_return(tMPI_Atomic_t *a, int i);
+#ifndef DOXYGEN
+#define TMPI_ATOMIC_HAVE_NATIVE_ADD_RETURN
+#endif
+
+
+
+/** Add to variable, return the old value.
+ *
+ *  This operation is quite useful for synchronization counters.
+ *  By performing a fetchadd with N, a thread can e.g. reserve a chunk
+ *  with the next N iterations, and the return value is the index
+ *  of the first element to treat.
+ *
+ *  Also implements proper memory barriers when necessary.
+ *  The actual implementation is system-dependent.
+ *
+ *  \param a   atomic datatype to modify
+ *  \param i   integer to increment with. Use i<0 to subtract atomically.
+ *
+ *  \return    The value of the atomic variable before addition.
+ */
+TMPI_EXPORT
+int tMPI_Atomic_fetch_add(tMPI_Atomic_t *a, int i);
+#ifndef DOXYGEN
+#define TMPI_ATOMIC_HAVE_NATIVE_FETCH_ADD
+#endif
+
+
+
+/** Atomic compare-and-swap operation
+ *
+ *   The \a old value is compared with the memory value in the atomic datatype.
+ *   If the are identical, the atomic type is swapped with the new value,
+ *   and otherwise left unchanged.
+ *
+ *   This is *the* synchronization primitive: it has a consensus number of
+ *   infinity, and is available in some form on all modern CPU architectures.
+ *   In the words of Herlihy&Shavit (The art of multiprocessor programming),
+ *   it is the 'king of all wild things'.
+ *
+ *   In practice, use it as follows: You can start by reading a value
+ *   (without locking anything), perform some calculations, and then
+ *   atomically try to update it in memory unless it has changed. If it has
+ *   changed you will get an error return code - reread the new value
+ *   an repeat the calculations in that case.
+ *
+ *   \param a        Atomic datatype ('memory' value)
+ *   \param old_val  Integer value read from the atomic type at an earlier point
+ *   \param new_val  New value to write to the atomic type if it currently is
+ *                   identical to the old value.
+ *
+ *   \return    True (1) if the swap occurred: i.e. if the value in a was equal
+ *              to old_val. False (0) if the swap didn't occur and the value
+ *              was not equal to old_val.
+ *
+ *   \note   The exchange occured if the return value is identical to \a old.
+ */
+TMPI_EXPORT
+int tMPI_Atomic_cas(tMPI_Atomic_t *a, int old_val, int new_val);
+
+
+
+
+/** Atomic pointer compare-and-swap operation
+ *
+ *   The \a old value is compared with the memory value in the atomic datatype.
+ *   If the are identical, the atomic type is swapped with the new value,
+ *   and otherwise left unchanged.
+ *
+ *   This is essential for implementing wait-free lists and other data
+ *   structures. See 'tMPI_Atomic_cas()'.
+ *
+ *   \param a        Atomic datatype ('memory' value)
+ *   \param old_val  Pointer value read from the atomic type at an earlier point
+ *   \param new_val  New value to write to the atomic type if it currently is
+ *                   identical to the old value.
+ *
+ *   \return    True (1) if the swap occurred: i.e. if the value in a was equal
+ *              to old_val. False (0) if the swap didn't occur and the value
+ *              was not equal to old_val.
+ *
+ *   \note   The exchange occured if the return value is identical to \a old.
+ */
+TMPI_EXPORT
+int tMPI_Atomic_ptr_cas(tMPI_Atomic_ptr_t * a, void *old_val,
+                        void *new_val);
+
+/** Atomic swap operation.
+
+   Atomically swaps the data in the tMPI_Atomic_t operand with the value of b.
+   Note: This has no good assembly counterparts on many architectures, so
+         it might not be faster than a repreated CAS.
+
+   \param a  Pointer to atomic type
+   \param b  Value to swap
+   \return the original value of a
+ */
+TMPI_EXPORT
+int tMPI_Atomic_swap(tMPI_Atomic_t *a, int b);
+
+/** Atomic swap pointer operation.
+
+   Atomically swaps the pointer in the tMPI_Atomic_ptr_t operand with the
+   value of b.
+   Note: This has no good assembly counterparts on many architectures, so
+         it might not be faster than a repreated CAS.
+
+   \param a  Pointer to atomic type
+   \param b  Value to swap
+   \return the original value of a
+ */
+TMPI_EXPORT
+void *tMPI_Atomic_ptr_swap(tMPI_Atomic_ptr_t *a, void *b);
+#ifndef DOXYGEN
+#define TMPI_ATOMIC_HAVE_NATIVE_SWAP
+#endif
+
+
+/** Initialize spinlock
+ *
+ *  In theory you can call this from multiple threads, but remember
+ *  that we don't check for errors. If the first thread proceeded to
+ *  lock the spinlock after initialization, the second will happily
+ *  overwrite the contents and unlock it without warning you.
+ *
+ *  \param x      Spinlock pointer.
+ *
+ *  \hideinitializer
+ */
+TMPI_EXPORT
+void tMPI_Spinlock_init( tMPI_Spinlock_t *x);
+#ifndef DOXYGEN
+#define TMPI_ATOMIC_HAVE_NATIVE_SPINLOCK
+#endif
+
+/** Acquire spinlock
+ *
+ *  This routine blocks until the spinlock is available, and
+ *  the locks it again before returning.
+ *
+ *  \param x     Spinlock pointer
+ */
+TMPI_EXPORT
+void tMPI_Spinlock_lock( tMPI_Spinlock_t *x);
+
+
+/** Attempt to acquire spinlock
+ *
+ * This routine acquires the spinlock if possible, but if
+ * already locked it return an error code immediately.
+ *
+ *  \param x     Spinlock pointer
+ *
+ * \return 0 if the mutex was available so we could lock it,
+ *         otherwise a non-zero integer (1) if the lock is busy.
+ */
+TMPI_EXPORT
+int tMPI_Spinlock_trylock( tMPI_Spinlock_t *x);
+
+/** Release spinlock
+ *
+ *  \param x     Spinlock pointer
+ *
+ *  Unlocks the spinlock, regardless if which thread locked it.
+ */
+TMPI_EXPORT
+void tMPI_Spinlock_unlock( tMPI_Spinlock_t *x);
+
+
+
+/** Check if spinlock is locked
+ *
+ *  This routine returns immediately with the lock status.
+ *
+ *  \param x  Spinlock pointer
+ *
+ *  \return 1 if the spinlock is locked, 0 otherwise.
+ */
+TMPI_EXPORT
+int tMPI_Spinlock_islocked( tMPI_Spinlock_t *x);
+
+/** Wait for a spinlock to become available
+ *
+ *  This routine blocks until the spinlock is unlocked,
+ *  but in contrast to tMPI_Spinlock_lock() it returns without
+ *  trying to lock the spinlock.
+ *
+ *  \param x  Spinlock pointer
+ */
+TMPI_EXPORT
+void tMPI_Spinlock_wait(tMPI_Spinlock_t *x);
+
+
+#endif /* TMPI_NO_ATOMICS */
+
+/* now define all the atomics that are not avaible natively. These
+   are done on the assumption that a native CAS does exist. */
+#include "atomic/derived.h"
+
+/* this allows us to use the inline keyword without breaking support for
+   some compilers that don't support it: */
+#ifdef inline_defined_in_atomic
+#undef inline
+#endif
+
+#if !defined(TMPI_NO_ATOMICS) && !defined(TMPI_ATOMICS)
+/* Set it here to make sure the user code can check this without having to have
+   a config.h */
+/** Indicates that support for atomic operations is present. */
+#define TMPI_ATOMICS
+#endif
+
+#endif /* TMPI_ATOMIC_H_ */
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/cce.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/cce.h
new file mode 100644 (file)
index 0000000..9030730
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+   This source code file is part of thread_mpi.
+   Original for gcc written by Sander Pronk, Erik Lindahl, and possibly
+   others. Modified for the Cray compiler by Daniel Landau.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   Copyright 2014, Cray Inc.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#include <intrinsics.h>
+
+#define tMPI_Atomic_memory_barrier() __builtin_ia32_mfence()
+
+
+typedef struct tMPI_Atomic
+{
+    volatile long value;
+}
+tMPI_Atomic_t;
+
+typedef struct tMPI_Atomic_ptr
+{
+    volatile void* value;
+}
+tMPI_Atomic_ptr_t;
+
+
+/* these are guaranteed to be  atomic on x86 and x86_64 */
+#define tMPI_Atomic_get(a)  ((int)( (a)->value) )
+#define tMPI_Atomic_set(a, i)  (((a)->value) = (i))
+
+
+#define tMPI_Atomic_ptr_get(a)  ((void*)((a)->value) )
+#define tMPI_Atomic_ptr_set(a, i)  (((a)->value) = (void*)(i))
+
+
+#include "cce_intrinsics.h"
+
+#include "cce_spinlock.h"
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/cce_intrinsics.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/cce_intrinsics.h
new file mode 100644 (file)
index 0000000..40934e5
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+   This source code file is part of thread_mpi.
+   Original for gcc written by Sander Pronk, Erik Lindahl, and possibly
+   others. Modified for the Cray compiler by Daniel Landau.
+
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   Copyright 2014, Cray Inc.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#include <intrinsics.h>
+
+#define tMPI_Atomic_memory_barrier() __builtin_ia32_mfence()
+
+TMPI_EXPORT
+static inline int tMPI_Atomic_cas(tMPI_Atomic_t *a, int oldval, int newval)
+{
+    return __sync_val_compare_and_swap(&(a->value), oldval, newval) == oldval;
+}
+
+TMPI_EXPORT
+static inline int tMPI_Atomic_ptr_cas(tMPI_Atomic_ptr_t* a, void *oldval,
+                                      void *newval)
+{
+    return __sync_val_compare_and_swap((size_t*)&(a->value), (size_t)oldval, (size_t)newval) == (size_t)oldval;
+}
+
+TMPI_EXPORT
+static inline int tMPI_Atomic_add_return(tMPI_Atomic_t *a, volatile int i)
+{
+    return __sync_add_and_fetch( &(a->value), i);
+}
+#define TMPI_ATOMIC_HAVE_NATIVE_ADD_RETURN
+
+
+TMPI_EXPORT
+static inline int tMPI_Atomic_fetch_add(tMPI_Atomic_t *a, volatile int i)
+{
+    return __sync_fetch_and_add( &(a->value), i);
+}
+#define TMPI_ATOMIC_HAVE_NATIVE_FETCH_ADD
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/cce_spinlock.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/cce_spinlock.h
new file mode 100644 (file)
index 0000000..31de26f
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+   This source code file is part of thread_mpi.
+   Original for gcc written by Sander Pronk, Erik Lindahl, and possibly
+   others. Modified for the Cray compiler by Daniel Landau.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   Copyright 2014, Cray Inc.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#include <intrinsics.h>
+
+typedef struct tMPI_Spinlock
+{
+    volatile long lock /*__attribute__ ((aligned(64)))*/;
+} tMPI_Spinlock_t;
+
+#define TMPI_SPINLOCK_INITIALIZER   { 0 }
+
+#define TMPI_ATOMIC_HAVE_NATIVE_SPINLOCK
+
+
+
+static inline void tMPI_Spinlock_init(tMPI_Spinlock_t *x)
+{
+    x->lock = 0;
+}
+
+
+static inline void tMPI_Spinlock_lock(tMPI_Spinlock_t *x)
+{
+    while (__sync_lock_test_and_set(&(x->lock), 1) == 1)
+    {
+        /* this is nicer on the system bus: */
+        while (x->lock == 1)
+        {
+        }
+    }
+}
+
+
+static inline int tMPI_Spinlock_trylock(tMPI_Spinlock_t *x)
+{
+    return __sync_lock_test_and_set(&(x->lock), 1);
+}
+
+
+static inline void tMPI_Spinlock_unlock(tMPI_Spinlock_t *x)
+{
+    x->lock = 0;
+}
+
+static inline int tMPI_Spinlock_islocked(const tMPI_Spinlock_t *x)
+{
+    return ( x->lock == 1 );
+}
+
+static inline void tMPI_Spinlock_wait(tMPI_Spinlock_t *x)
+{
+    do
+    {
+    }
+    while (x->lock == 1);
+}
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/cycles.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/cycles.h
new file mode 100644 (file)
index 0000000..51ccfef
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * define GMX_USE_RDTSCP=1 to use the serializing rdtscp instruction instead of rdtsc.
+ * This is only supported on newer Intel/AMD hardware, but provides better accuracy.
+ */
+
+/* check for cycle counters on supported platforms */
+#if ((defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__PATHSCALE__)  || defined(__PGIC__)) && (defined(__i386__) || defined(__x86_64__)))
+#define TMPI_CYCLE_COUNT
+/* x86 or x86-64 with GCC inline assembly */
+typedef unsigned long long tMPI_Cycles_t;
+
+static __inline__ tMPI_Cycles_t tMPI_Cycles_read(void)
+{
+    /* x86 with GCC inline assembly - pentium TSC register */
+    tMPI_Cycles_t cycle;
+    unsigned      low, high;
+
+#if GMX_USE_RDTSCP
+    __asm__ __volatile__("rdtscp" : "=a" (low), "=d" (high) :: "ecx" );
+#else
+    __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high));
+#endif
+
+    cycle = ((unsigned long long)low) | (((unsigned long long)high)<<32);
+
+    return cycle;
+}
+#elif (defined(__INTEL_COMPILER) && defined(__ia64__))
+#define TMPI_CYCLE_COUNT
+typedef unsigned long tMPI_Cycles_t;
+static __inline__ tMPI_Cycles_t tMPI_Cycles_read(void)
+{
+    /* Intel compiler on ia64 */
+    return __getReg(_IA64_REG_AR_ITC);
+}
+#elif defined(__GNUC__) && defined(__ia64__)
+#define TMPI_CYCLE_COUNT
+typedef unsigned long tMPI_Cycles_t;
+static __inline__ tMPI_Cycles_t tMPI_Cycles_read(void)
+{
+    /* ia64 with GCC inline assembly */
+    tMPI_Cycles_t ret;
+    __asm__ __volatile__ ("mov %0=ar.itc" : "=r" (ret));
+    return ret;
+}
+#elif defined(_MSC_VER)
+#define TMPI_CYCLE_COUNT
+typedef __int64 tMPI_Cycles_t;
+static __inline tMPI_Cycles_t tMPI_Cycles_read(void)
+{
+#if GMX_USE_RDTSCP
+    unsigned int ui;
+    return __rdtscp(&ui);
+#else
+    return __rdtsc();
+#endif
+}
+#endif
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/derived.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/derived.h
new file mode 100644 (file)
index 0000000..68b0fe5
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2013, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/* These functions are fallback definitions for when there are no native
+   variants for fetch-add, spinlock, etc., but there is a native
+   compare-and-swap. */
+
+
+/* only define this if there were no separate acquire and release barriers */
+#ifndef TMPI_HAVE_ACQ_REL_BARRIERS
+
+/* if they're not defined explicitly, we just make full barriers out of both */
+#define tMPI_Atomic_memory_barrier_acq tMPI_Atomic_memory_barrier
+#define tMPI_Atomic_memory_barrier_rel tMPI_Atomic_memory_barrier
+
+#endif
+
+#ifndef TMPI_ATOMIC_HAVE_NATIVE_FETCH_ADD
+TMPI_EXPORT
+static inline int tMPI_Atomic_fetch_add(tMPI_Atomic_t *a, int i)
+{
+    int newval, oldval;
+    do
+    {
+        tMPI_Atomic_memory_barrier_acq();
+        oldval = tMPI_Atomic_get(a);
+        newval = oldval + i;
+    }
+    while (!tMPI_Atomic_cas(a, oldval, newval));
+    tMPI_Atomic_memory_barrier_rel();
+    return oldval;
+}
+#endif /* TMPI_HAVE_FETCH_ADD */
+
+
+#ifndef TMPI_ATOMIC_HAVE_NATIVE_ADD_RETURN
+TMPI_EXPORT
+static inline int tMPI_Atomic_add_return(tMPI_Atomic_t *a, int i)
+{
+    /* implement in terms of fetch-add */
+    return tMPI_Atomic_fetch_add(a, i) + i;
+}
+#endif /* TMPI_HAVE_ADD_RETURN */
+
+
+
+
+
+/* only do this if there was no better solution */
+#ifndef TMPI_ATOMIC_HAVE_NATIVE_SWAP
+TMPI_EXPORT
+static inline int tMPI_Atomic_swap(tMPI_Atomic_t *a, int b)
+{
+    int oldval;
+    do
+    {
+        oldval = (int)(a->value);
+    }
+    while (!tMPI_Atomic_cas(a, oldval, b));
+    return oldval;
+}
+
+
+TMPI_EXPORT
+static inline void *tMPI_Atomic_ptr_swap(tMPI_Atomic_ptr_t *a, void *b)
+{
+    void *oldval;
+    do
+    {
+        oldval = (void*)(a->value);
+    }
+    while (!tMPI_Atomic_ptr_cas(a, oldval, b));
+    return oldval;
+}
+#endif
+
+
+
+#ifndef TMPI_ATOMIC_HAVE_NATIVE_SPINLOCK
+
+typedef struct tMPI_Spinlock
+{
+    tMPI_Atomic_t a;
+}
+tMPI_Spinlock_t;
+
+#define TMPI_SPINLOCK_INITIALIZER   { 0 }
+
+
+
+TMPI_EXPORT
+static inline void tMPI_Spinlock_init(tMPI_Spinlock_t *x)
+{
+    tMPI_Atomic_set(&(x->a), 0);
+}
+
+
+TMPI_EXPORT
+static inline void tMPI_Spinlock_lock(tMPI_Spinlock_t *x)
+{
+    tMPI_Atomic_memory_barrier_acq();
+    do
+    {
+        while (tMPI_Atomic_get(&(x->a)) == 1)
+        {
+            tMPI_Atomic_memory_barrier_acq();
+        }
+    }
+    while (!tMPI_Atomic_cas(&(x->a), 0, 1));
+    tMPI_Atomic_memory_barrier_acq();
+}
+
+
+TMPI_EXPORT
+static inline int tMPI_Spinlock_trylock(tMPI_Spinlock_t *x)
+{
+    int ret;
+    tMPI_Atomic_memory_barrier_acq();
+    ret = !tMPI_Atomic_cas(&(x->a), 0, 1);
+    return ret;
+}
+
+
+TMPI_EXPORT
+static inline void tMPI_Spinlock_unlock(tMPI_Spinlock_t *x)
+{
+    tMPI_Atomic_memory_barrier_rel();
+    tMPI_Atomic_set(&(x->a), 0);
+    tMPI_Atomic_memory_barrier_rel();
+}
+
+
+TMPI_EXPORT
+static inline int tMPI_Spinlock_islocked(const tMPI_Spinlock_t *x)
+{
+    int ret;
+    tMPI_Atomic_memory_barrier_rel();
+    ret = (tMPI_Atomic_get(&(x->a)) != 0);
+    return ret;
+}
+
+
+TMPI_EXPORT
+static inline void tMPI_Spinlock_wait(tMPI_Spinlock_t *x)
+{
+    do
+    {
+    }
+    while (tMPI_Spinlock_islocked(x));
+}
+#endif /* TMPI_ATOMIC_HAVE_NATIVE_SPINLOCK */
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/fujitsu_sparc64.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/fujitsu_sparc64.h
new file mode 100644 (file)
index 0000000..0566338
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2013, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#define tMPI_Atomic_memory_barrier() { asm ("membar   #StoreStore | #LoadStore | #LoadLoad | #StoreLoad "); }
+#define tMPI_Atomic_memory_barrier_acq() { asm ("membar   #StoreStore | #StoreLoad ");  }
+#define tMPI_Atomic_memory_barrier_rel() { asm ("membar   #LoadStore | #StoreStore ");  }
+#define TMPI_HAVE_ACQ_REL_BARRIERS
+
+
+typedef struct tMPI_Atomic
+{
+    volatile int value __attribute__ ((aligned(64)));
+}
+tMPI_Atomic_t;
+
+
+typedef struct tMPI_Atomic_ptr
+{
+    volatile char* volatile* value __attribute__ ((aligned(64)));  /*!< Volatile, to avoid compiler aliasing */
+}
+tMPI_Atomic_ptr_t;
+
+
+/* On sparc64, aligned 32-bit and 64-bit memory accesses are atomic */
+#define tMPI_Atomic_get(a)   (int)((a)->value)
+#define tMPI_Atomic_set(a, i)  (((a)->value) = (i))
+#define tMPI_Atomic_ptr_get(a)   ((a)->value)
+#define tMPI_Atomic_ptr_set(a, i)  (((a)->value) = (i))
+
+#define TMPI_SPINLOCK_INITIALIZER   { 0 }
+
+/* we just define the CAS operation. Fetch-and-add and spinlocks are
+   implemented through derived.h; this follows the recommendations of the
+   Sparc v9 programming specs. */
+
+static inline int tMPI_Atomic_cas(tMPI_Atomic_t *a, int oldval, int newval)
+{
+    asm ("cas [%2], %1, %0"
+         : "=&r" (newval)
+         : "r" (oldval), "r" (&(a->value)), "0" (newval)
+         : "memory");
+    return newval == oldval;
+}
+
+
+static inline int tMPI_Atomic_ptr_cas(tMPI_Atomic_ptr_t *a, void* oldval,
+                                      void* newval)
+{
+    asm ("casx [%2], %1, %0         "
+         : "=&r" (newval)
+         : "r" (oldval), "r" (&(a->value)), "0" (newval)
+         : "memory");
+    return newval == oldval;
+}
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc.h
new file mode 100644 (file)
index 0000000..39537f3
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/* this is for newer versions of gcc that have built-in intrinsics,
+   on platforms not explicitly supported with inline assembly. */
+
+#define tMPI_Atomic_memory_barrier()  __sync_synchronize()
+
+/* Only gcc and Intel support this check, otherwise set it to true (skip doc) */
+#if (!defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined DOXYGEN)
+#define __builtin_constant_p(i) (1)
+#endif
+
+
+typedef struct tMPI_Atomic
+{
+    volatile int value;
+}
+tMPI_Atomic_t;
+
+typedef struct tMPI_Atomic_ptr
+{
+    volatile void* value;
+}
+tMPI_Atomic_ptr_t;
+
+
+/* for now we simply assume that int and void* assignments are atomic */
+#define tMPI_Atomic_get(a)  ((int)( (a)->value) )
+#define tMPI_Atomic_set(a, i)  (((a)->value) = (i))
+
+
+#define tMPI_Atomic_ptr_get(a)  ((void*)((a)->value) )
+#define tMPI_Atomic_ptr_set(a, i)  (((a)->value) = (void*)(i))
+
+
+#include "gcc_intrinsics.h"
+
+#include "gcc_spinlock.h"
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_ia64.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_ia64.h
new file mode 100644 (file)
index 0000000..f3c3a17
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+/* ia64 with GCC or Intel compilers. Since we need to define everything through
+ * cmpxchg and fetchadd on ia64, we merge the different compilers and only
+ * provide different implementations for that single function.
+ * Documentation? Check the gcc/x86 section.
+ */
+
+
+typedef struct tMPI_Atomic
+{
+    volatile int value; /*!< Volatile, to avoid compiler aliasing */
+}
+tMPI_Atomic_t;
+
+typedef struct tMPI_Atomic_ptr
+{
+    void* volatile value; /*!< Volatile, to avoid compiler aliasing */
+}
+tMPI_Atomic_ptr_t;
+
+
+
+#define tMPI_Atomic_get(a)   ((a)->value)
+#define tMPI_Atomic_set(a, i)  (((a)->value) = (i))
+
+#define tMPI_Atomic_ptr_get(a)   ((a)->value)
+#define tMPI_Atomic_ptr_set(a, i)  (((a)->value) = (i))
+
+
+
+#ifndef __INTEL_COMPILER
+#define TMPI_ATOMIC_HAVE_NATIVE_SWAP
+/* xchg operations: */
+/* ia64 xchg */
+static inline int tMPI_Atomic_swap(tMPI_Atomic_t *a, int b)
+{
+    volatile int res;
+    asm volatile ("xchg4 %0=[%1],%2" :
+                  "=r" (res) : "r" (&a->value), "r" (b) : "memory");
+
+    return res;
+}
+/* ia64 ptr xchg */
+static inline void* tMPI_Atomic_ptr_swap(tMPI_Atomic_ptr_t * a, void *b)
+{
+    void* volatile* res;
+
+
+    asm volatile ("xchg8 %0=[%1],%2" :
+                  "=r" (res) : "r" (&a->value), "r" (b) : "memory");
+    return (void*)res;
+}
+#endif
+
+
+
+/* do the intrinsics. icc on windows doesn't have them. */
+#if ( (TMPI_GCC_VERSION >= 40100) )
+
+#include "gcc_intrinsics.h"
+
+/* our spinlock is not really any better than gcc's based on its intrinsics */
+#include "gcc_spinlock.h"
+#else
+
+
+/* Compiler thingies */
+#ifdef __INTEL_COMPILER
+/* prototypes are neccessary for these intrisics: */
+#include <ia64intrin.h>
+void __memory_barrier(void);
+int _InterlockedCompareExchange(volatile int *dest, int xchg, int comp);
+/*void* _InterlockedCompareExchangePointer(void* volatile **dest, void* xchg,
+                                         void* comp);*/
+unsigned __int64 __fetchadd4_rel(unsigned int *addend, const int increment);
+/* ia64 memory barrier */
+#define tMPI_Atomic_memory_barrier() __sync_synchronize()
+/* ia64 cmpxchg */
+#define tMPI_Atomic_cas(a, oldval, newval) \
+    (_InterlockedCompareExchange(&((a)->value), newval, oldval) == oldval)
+/* ia64 pointer cmpxchg */
+#define tMPI_Atomic_ptr_cas(a, oldval, newval) \
+    (_InterlockedCompareExchangePointer(&((a)->value), newval, oldval) == oldval)
+
+/*#define tMPI_Atomic_ptr_cas(a, oldval, newval) __sync_val_compare_and_swap(&((a)->value),newval,oldval)*/
+
+
+/* ia64 fetchadd, but it only works with increments +/- 1,4,8,16 */
+#define tMPI_ia64_fetchadd(a, inc)  __fetchadd4_rel(a, inc)
+
+#define tMPI_Atomic_swap(a, b) _InterlockedExchange( &((a)->value), (b))
+#define tMPI_Atomic_ptr_swap(a, b) _InterlockedExchangePointer( &((a)->value), (b))
+#define TMPI_ATOMIC_HAVE_NATIVE_SWAP
+
+#elif defined __GNUC__
+
+/* ia64 memory barrier */
+#define tMPI_Atomic_memory_barrier() asm volatile ("mf" ::: "memory")
+
+/* ia64 cmpxchg */
+static inline int tMPI_Atomic_cas(tMPI_Atomic_t *a, int oldval, int newval)
+{
+#if GCC_VERSION < 40200
+    volatile int res;
+    asm volatile ("mov ar.ccv=%0;;" :: "rO" (oldval));
+    asm volatile ("cmpxchg4.acq %0=[%1],%2,ar.ccv" :
+                  "=r" (res) : "r" (&a->value), "r" (newval) : "memory");
+
+    return res == oldval;
+#else
+    return __sync_bool_compare_and_swap( &(a->value), oldval, newval);
+#endif
+}
+
+/* ia64 ptr cmpxchg */
+static inline int tMPI_Atomic_ptr_cas(tMPI_Atomic_ptr_t * a, void *oldval,
+                                      void *newval)
+{
+#if GCC_VERSION < 40200
+    void* volatile* res;
+    asm volatile ("mov ar.ccv=%0;;" :: "rO" (oldval));
+    asm volatile ("cmpxchg8.acq %0=[%1],%2,ar.ccv" :
+                  "=r" (res) : "r" (&a->value), "r" (newval) : "memory");
+
+    return ((void*)res) == oldval;
+#else
+    return __sync_bool_compare_and_swap( &(a->value), oldval, newval);
+#endif
+}
+
+
+/* fetchadd, but on ia64 it only works with increments +/- 1,4,8,16 */
+#define tMPI_ia64_fetchadd(a, inc)                                             \
+    ({  unsigned long res;                                                        \
+        asm volatile ("fetchadd4.rel %0=[%1],%2"                                  \
+                      : "=r" (res) : "r" (a), "r" (inc) : "memory");                \
+        res;                                                        \
+     })
+
+
+
+#else  /* Unknown compiler */
+#  error Unknown ia64 compiler (not GCC or ICC) - modify tMPI_Thread.h!
+#endif /* end of gcc/icc specific section */
+
+
+
+
+static inline int tMPI_Atomic_add_return(tMPI_Atomic_t *a, int i)
+{
+    volatile int oldval, newval;
+    volatile int __i = i;
+
+    /* Use fetchadd if, and only if, the increment value can be determined
+     * at compile time (otherwise this check is optimized away) and it is
+     * a value supported by fetchadd (1,4,8,16,-1,-4,-8,-16).
+     */
+    if (__builtin_constant_p(i) &&
+        ( (__i ==   1) || (__i ==   4)  || (__i ==   8) || (__i ==  16) ||
+          (__i ==  -1) || (__i ==  -4)  || (__i ==  -8) || (__i == -16) ) )
+    {
+        oldval = tMPI_ia64_fetchadd((unsigned int*)&(a->value), __i);
+        newval = oldval + i;
+    }
+    else
+    {
+        /* Use compare-exchange addition that works with any value */
+        do
+        {
+            oldval = tMPI_Atomic_get(a);
+            newval = oldval + i;
+        }
+        while (!tMPI_Atomic_cas(a, oldval, newval));
+    }
+    return (int)newval;
+}
+#define TMPI_ATOMIC_HAVE_NATIVE_ADD_RETURN
+
+
+
+static inline int tMPI_Atomic_fetch_add(tMPI_Atomic_t *a, int i)
+{
+    volatile int oldval, newval;
+    volatile int __i = i;
+
+    /* Use ia64 fetchadd if, and only if, the increment value can be determined
+     * at compile time (otherwise this check is optimized away) and it is
+     * a value supported by fetchadd (1,4,8,16,-1,-4,-8,-16).
+     */
+    if (__builtin_constant_p(i) &&
+        ( (__i ==   1) || (__i ==   4)  || (__i ==   8) || (__i ==  16) ||
+          (__i ==  -1) || (__i ==  -4)  || (__i ==  -8) || (__i == -16) ) )
+    {
+        oldval = tMPI_ia64_fetchadd((unsigned int*)&(a->value), __i);
+        newval = oldval + i;
+    }
+    else
+    {
+        /* Use compare-exchange addition that works with any value */
+        do
+        {
+            oldval = tMPI_Atomic_get(a);
+            newval = oldval + i;
+        }
+        while (!tMPI_Atomic_cas(a, oldval, newval));
+    }
+    return (int)oldval;
+}
+#define TMPI_ATOMIC_HAVE_NATIVE_FETCH_ADD
+
+#endif
+
+#undef tMPI_ia64_fetchadd
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_intrinsics.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_intrinsics.h
new file mode 100644 (file)
index 0000000..f909cb9
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/* this is for newer versions of gcc that have built-in intrinsics */
+
+#define tMPI_Atomic_memory_barrier()  __sync_synchronize()
+
+
+
+TMPI_EXPORT
+static inline int tMPI_Atomic_cas(tMPI_Atomic_t *a, int oldval, int newval)
+{
+    return __sync_bool_compare_and_swap( &(a->value), oldval, newval);
+}
+
+TMPI_EXPORT
+static inline int tMPI_Atomic_ptr_cas(tMPI_Atomic_ptr_t* a, void *oldval,
+                                      void *newval)
+{
+#if !defined(__INTEL_COMPILER) && !defined(__CUDACC__)
+    return __sync_bool_compare_and_swap( &(a->value), oldval, newval);
+#else
+    /* the intel compilers need integer type arguments for compare_and_swap.
+        on the platforms supported by icc, size_t is always the size of
+        a pointer. */
+    return (__sync_bool_compare_and_swap( (size_t*)&(a->value),
+                                          (size_t)oldval,
+                                          (size_t)newval) );
+#endif
+}
+
+TMPI_EXPORT
+static inline int tMPI_Atomic_add_return(tMPI_Atomic_t *a, volatile int i)
+{
+    return __sync_add_and_fetch( &(a->value), i);
+}
+#define TMPI_ATOMIC_HAVE_NATIVE_ADD_RETURN
+
+
+TMPI_EXPORT
+static inline int tMPI_Atomic_fetch_add(tMPI_Atomic_t *a, volatile int i)
+{
+    return __sync_fetch_and_add( &(a->value), i);
+}
+#define TMPI_ATOMIC_HAVE_NATIVE_FETCH_ADD
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_ppc.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_ppc.h
new file mode 100644 (file)
index 0000000..928d770
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/* NOTE:
+
+ ***************************************************************************
+   this file is not used any more. gcc intrinsics take care of the atomics
+ ***************************************************************************
+
+ */
+
+#error included gcc_ppc.h. This file is outdated
+
+
+
+typedef struct tMPI_Atomic
+{
+    volatile int value;   /*!< Volatile, to avoid compiler aliasing */
+}
+tMPI_Atomic_t;
+
+typedef struct tMPI_Atomic_ptr
+{
+    void* volatile* value;   /*!< Volatile, to avoid compiler aliasing */
+}
+tMPI_Atomic_ptr_t;
+
+
+typedef struct tMPI_Spinlock
+{
+    volatile unsigned int lock;   /*!< Volatile, to avoid compiler aliasing */
+}
+tMPI_Spinlock_t;
+#define TMPI_ATOMIC_HAVE_NATIVE_SPINLOCK
+
+
+#define TMPI_SPINLOCK_INITIALIZER   { 0 }
+
+#define tMPI_Atomic_get(a)        ((a)->value)
+#define tMPI_Atomic_set(a, i)     (((a)->value) = (i))
+
+#define tMPI_Atomic_ptr_get(a)    (void*)((a)->value)
+#define tMPI_Atomic_ptr_set(a, i)  (((a)->value) = (void*)(i))
+
+
+#if (TMPI_GCC_VERSION >= 40100)
+
+#include "gcc_intrinsics.h"
+
+#else
+
+/* Compiler-dependent stuff: GCC memory barrier */
+#define tMPI_Atomic_memory_barrier() __asm__ __volatile__("isync" : : : "memory")
+
+
+
+#define TMPI_ATOMIC_HAVE_NATIVE_SWAP
+static inline int tMPI_Atomic_swap(tMPI_Atomic_t *a, int b)
+{
+    int ret;
+
+    __asm__ __volatile__ ("1:    lwarx   %0,0,%2 \n"
+                          "\tstwcx.  %3,0,%2 \n"
+                          "\tbne-    1b\n"
+                          : "=&r" (ret), "=m" (a->value)
+                          : "r" (&(a->value)), "r" (b)
+                          : "cc", "memory");
+
+    return ret;
+}
+
+static inline void* tMPI_Atomic_ptr_swap(tMPI_Atomic_ptr_t *a, void *b)
+{
+    int ret;
+
+#if (!defined(__PPC64__)) && (!defined(__ppc64))
+    __asm__ __volatile__ ("1:    lwarx   %0,0,%2 \n"
+                          "\tstwcx.  %3,0,%2 \n"
+                          "\tbne-    1b\n"
+                          : "=&r" (ret), "=m" (a->value)
+                          : "r" (&(a->value)), "r" (b)
+                          : "cc", "memory");
+#else
+    __asm__ __volatile__ ("1:    ldarx   %0,0,%2 \n"
+                          "\tstdcx.  %3,0,%2 \n"
+                          "\tbne-    1b\n"
+                          : "=&r" (ret), "=m" (a->value)
+                          : "r" (&(a->value)), "r" (b)
+                          : "cc", "memory");
+#endif
+
+    return ret;
+}
+
+
+
+static inline int tMPI_Atomic_cas(tMPI_Atomic_t *a, int oldval, int newval)
+{
+    int prev;
+
+    __asm__ __volatile__ ("1:    lwarx   %0,0,%2 \n"
+                          "\tcmpw    0,%0,%3 \n"
+                          "\tbne     2f \n"
+                          "\tstwcx.  %4,0,%2 \n"
+                          "bne-    1b\n"
+                          "\tsync\n"
+                          "2:\n"
+                          : "=&r" (prev), "=m" (a->value)
+                          : "r" (&a->value), "r" (oldval), "r" (newval),
+                          "m" (a->value)
+                          : "cc", "memory");
+
+    return prev == oldval;
+}
+
+
+static inline int tMPI_Atomic_ptr_cas(tMPI_Atomic_ptr_t *a, void *oldval,
+                                      void *newval)
+{
+    void *prev;
+
+#if (!defined(__PPC64__)) && (!defined(__ppc64))
+    __asm__ __volatile__ ("1:    lwarx   %0,0,%2 \n"
+                          "\tcmpw    0,%0,%3 \n"
+                          "\tbne     2f \n"
+                          "\tstwcx.  %4,0,%2 \n"
+                          "bne-    1b\n"
+                          "\tsync\n"
+                          "2:\n"
+                          : "=&r" (prev), "=m" (a->value)
+                          : "r" (&a->value), "r" (oldval), "r" (newval),
+                          "m" (a->value)
+                          : "cc", "memory");
+#else
+    __asm__ __volatile__ ("1:    ldarx   %0,0,%2 \n"
+                          "\tcmpd    0,%0,%3 \n"
+                          "\tbne     2f \n"
+                          "\tstdcx.  %4,0,%2 \n"
+                          "bne-    1b\n"
+                          "\tsync\n"
+                          "2:\n"
+                          : "=&r" (prev), "=m" (a->value)
+                          : "r" (&a->value), "r" (oldval), "r" (newval),
+                          "m" (a->value)
+                          : "cc", "memory");
+#endif
+    return prev == oldval;
+}
+
+#define TMPI_ATOMIC_HAVE_NATIVE_ADD_RETURN
+static inline int tMPI_Atomic_add_return(tMPI_Atomic_t *a, int i)
+{
+    int t;
+
+    __asm__ __volatile__("1:     lwarx   %0,0,%2\n"
+                         "\tadd     %0,%1,%0\n"
+                         "\tstwcx.  %0,0,%2 \n"
+                         "\tbne-    1b\n"
+                         "\tisync\n"
+                         : "=&r" (t)
+                         : "r" (i), "r" (&a->value)
+                         : "cc", "memory");
+    return t;
+}
+
+
+
+#define TMPI_ATOMIC_HAVE_NATIVE_FETCH_ADD
+static inline int tMPI_Atomic_fetch_add(tMPI_Atomic_t *a, int i)
+{
+    int t;
+
+    __asm__ __volatile__("\teieio\n"
+                         "1:     lwarx   %0,0,%2\n"
+                         "\tadd     %0,%1,%0\n"
+                         "\tstwcx.  %0,0,%2 \n"
+                         "\tbne-    1b\n"
+                         "\tisync\n"
+                         : "=&r" (t)
+                         : "r" (i), "r" (&a->value)
+                         : "cc", "memory");
+
+    return (t - i);
+}
+
+
+#endif
+
+
+static inline void tMPI_Spinlock_init(tMPI_Spinlock_t *x)
+{
+    x->lock = 0;
+}
+
+
+
+static inline void tMPI_Spinlock_lock(tMPI_Spinlock_t *x)
+{
+    unsigned int tmp;
+
+    __asm__ __volatile__("\tb      1f\n"
+                         "2:      lwzx    %0,0,%1\n"
+                         "\tcmpwi   0,%0,0\n"
+                         "\tbne+    2b\n"
+                         "1:      lwarx   %0,0,%1\n"
+                         "\tcmpwi   0,%0,0\n"
+                         "\tbne-    2b\n"
+                         "\tstwcx.  %2,0,%1\n"
+                         "bne-    2b\n"
+                         "\tisync\n"
+                         : "=&r" (tmp)
+                         : "r" (&x->lock), "r" (1)
+                         : "cr0", "memory");
+}
+
+
+static inline int tMPI_Spinlock_trylock(tMPI_Spinlock_t *x)
+{
+    unsigned int           old, t;
+    unsigned int           mask = 1;
+    volatile unsigned int *p    = &x->lock;
+
+    __asm__ __volatile__("\teieio\n"
+                         "1:      lwarx   %0,0,%4 \n"
+                         "\tor      %1,%0,%3 \n"
+                         "\tstwcx.  %1,0,%4 \n"
+                         "\tbne     1b\n"
+                         "\tsync\n"
+                         : "=&r" (old), "=&r" (t), "=m" (*p)
+                         : "r" (mask), "r" (p), "m" (*p)
+                         : "cc", "memory");
+
+    return (old & mask);
+}
+
+
+static inline void tMPI_Spinlock_unlock(tMPI_Spinlock_t *x)
+{
+    __asm__ __volatile__("\teieio\n" : : : "memory");
+    x->lock = 0;
+}
+
+
+static inline int tMPI_Spinlock_islocked(const tMPI_Spinlock_t *x)
+{
+    return ( x->lock != 0);
+}
+
+
+static inline void tMPI_Spinlock_wait(tMPI_Spinlock_t *x)
+{
+    do
+    {
+        tMPI_Atomic_memory_barrier();
+    }
+    while (tMPI_Spinlock_islocked(x));
+}
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_spinlock.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_spinlock.h
new file mode 100644 (file)
index 0000000..84a244d
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/* this is for newer versions of gcc that have built-in intrinsics.
+   These are the generic spinlocks:*/
+
+
+typedef struct tMPI_Spinlock
+{
+    volatile unsigned int lock /*__attribute__ ((aligned(64)))*/;
+} tMPI_Spinlock_t;
+
+#define TMPI_SPINLOCK_INITIALIZER   { 0 }
+
+#define TMPI_ATOMIC_HAVE_NATIVE_SPINLOCK
+
+
+
+static inline void tMPI_Spinlock_init(tMPI_Spinlock_t *x)
+{
+    x->lock = 0;
+}
+
+
+static inline void tMPI_Spinlock_lock(tMPI_Spinlock_t *x)
+{
+#if 1
+    while (__sync_lock_test_and_set(&(x->lock), 1) == 1)
+    {
+        /* this is nicer on the system bus: */
+        while (x->lock == 1)
+        {
+        }
+    }
+#else
+    do
+    {
+    }
+    while (__sync_lock_test_and_set(&(x->lock), 1) == 1);
+#endif
+}
+
+
+static inline int tMPI_Spinlock_trylock(tMPI_Spinlock_t *x)
+{
+    return __sync_lock_test_and_set(&(x->lock), 1);
+}
+
+
+static inline void tMPI_Spinlock_unlock(tMPI_Spinlock_t *x)
+{
+    __sync_lock_release(&(x->lock));
+}
+
+static inline int tMPI_Spinlock_islocked(const tMPI_Spinlock_t *x)
+{
+    __sync_synchronize();
+    return ( x->lock == 1 );
+}
+
+static inline void tMPI_Spinlock_wait(tMPI_Spinlock_t *x)
+{
+    do
+    {
+    }
+    while (x->lock == 1);
+    __sync_synchronize();
+}
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_x86.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/gcc_x86.h
new file mode 100644 (file)
index 0000000..a2d72af
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+
+#include <limits.h>
+#include <stdint.h>
+/* This code is executed for x86 and x86-64, with these compilers:
+ * GNU
+ * Intel
+ * Pathscale
+ * All these support GCC-style inline assembly.
+ * We also use this section for the documentation.
+ */
+
+
+#if 0
+/* Only gcc and Intel support this check, otherwise set it to true (skip doc) */
+#if (!defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined DOXYGEN)
+#define __builtin_constant_p(i) (1)
+#endif
+#endif
+
+/* we put all of these on their own cache line by padding the data structure
+   to the size of a cache line on x86 (64 bytes): */
+#define TMPI_SIZEOF_X86_CACHE_LINE 64
+typedef struct tMPI_Atomic
+{
+    int  value;
+    char padding[TMPI_SIZEOF_X86_CACHE_LINE-sizeof(int)];
+} tMPI_Atomic_t;
+
+typedef struct tMPI_Atomic_ptr
+{
+    void* value;
+    char  padding[TMPI_SIZEOF_X86_CACHE_LINE-sizeof(void*)];
+} tMPI_Atomic_ptr_t;
+
+typedef struct tMPI_Spinlock
+{
+    unsigned int lock;
+    char         padding[TMPI_SIZEOF_X86_CACHE_LINE-sizeof(unsigned int)];
+} tMPI_Spinlock_t;
+
+
+#define TMPI_SPINLOCK_INITIALIZER   { 0 }
+
+#define TMPI_ATOMIC_HAVE_NATIVE_SPINLOCK
+
+
+
+/* these are guaranteed to be  atomic on x86 and x86_64 */
+#define tMPI_Atomic_get(a)  ((a)->value)
+#define tMPI_Atomic_set(a, i)  (((a)->value) = (i))
+
+#define tMPI_Atomic_ptr_get(a)  ((a)->value)
+#define tMPI_Atomic_ptr_set(a, i)  (((a)->value) = (void*)(i))
+
+
+/* do the intrinsics.
+
+   We disable this for 32-bit builds because the target may be 80386,
+   which didn't have cmpxchg, etc (they were introduced as only as 'recently'
+   as the 486, and gcc on some Linux versions still target 80386 by default).
+
+   We also specifically check for icc, because intrinsics are not always
+   supported there.
+
+   llvm has issues with inline assembly and also in 32 bits has support for
+   the gcc intrinsics */
+#if ( ( (TMPI_GCC_VERSION >= 40100) && defined(__x86_64__) &&  \
+    !defined(__INTEL_COMPILER) )  || defined(__llvm__) )
+#include "gcc_intrinsics.h"
+
+#else
+/* older versions of gcc don't support atomic intrinsics */
+
+#define tMPI_Atomic_memory_barrier() __asm__ __volatile__("sfence;" : : : "memory")
+
+#define TMPI_ATOMIC_HAVE_NATIVE_FETCH_ADD
+static inline int tMPI_Atomic_fetch_add(tMPI_Atomic_t *a, int i)
+{
+    volatile int res = i;
+    /* volatile because we read and write back to the same variable in the
+       asm section.  some compilers requires this to be volatile */
+    __asm__ __volatile__("lock ; xaddl %0, %1;"      /* swap-add */
+                         : "=r" (res)                /* with register as
+                                                        output*/
+                         : "m" (a->value), "0" (res) /* and memory as input */
+                         : "memory");
+    return res;
+}
+
+#define TMPI_ATOMIC_HAVE_NATIVE_ADD_RETURN
+static inline int tMPI_Atomic_add_return(tMPI_Atomic_t *a, int i)
+{
+    int          orig = i;
+    volatile int res  = i;
+
+    __asm__ __volatile__("lock ; xaddl %0, %1;"
+                         : "=r" (res)
+                         : "m" (a->value), "0" (res)
+                         :  "memory");
+    return res + orig; /* then add again from the right value */
+}
+
+
+
+static inline int tMPI_Atomic_cas(tMPI_Atomic_t *a, int oldval, int newval)
+{
+    int prev;
+
+    __asm__ __volatile__("lock ; cmpxchgl %1,%2"
+                         : "=a" (prev)
+                         : "q" (newval), "m" (a->value), "0" (oldval)
+                         : "memory");
+
+    return prev == oldval;
+}
+
+static inline int tMPI_Atomic_ptr_cas(tMPI_Atomic_ptr_t *a,
+                                      void              *oldval,
+                                      void              *newval)
+{
+    void* prev;
+#if (defined(__x86_64__) && !defined(__ILP32__))
+    __asm__ __volatile__("lock ; cmpxchgq %1,%2"
+                         : "=a" (prev)
+                         : "q" (newval), "m" (a->value), "0" (oldval)
+                         : "memory");
+#elif (defined(__x86_64__) && defined(__ILP32__)) || defined(__i386__)
+    __asm__ __volatile__("lock ; cmpxchgl %1,%2"
+                         : "=a" (prev)
+                         : "q" (newval), "m" (a->value), "0" (oldval)
+                         : "memory");
+#else
+#    error Cannot detect whether this is a 32-bit or 64-bit x86 build.
+#endif
+    return prev == oldval;
+}
+
+#endif /* end of check for gcc intrinsics */
+
+
+#define TMPI_ATOMIC_HAVE_NATIVE_SWAP
+/* do the swap fns; we told the intrinsics that we have them. */
+static inline int tMPI_Atomic_swap(tMPI_Atomic_t *a, int b)
+{
+    volatile int ret = b;
+    __asm__ __volatile__("\txchgl %0, %1;"
+                         : "+r" (ret), "+m" (a->value)
+                         :
+                         : "memory");
+    return (int)ret;
+}
+
+static inline void *tMPI_Atomic_ptr_swap(tMPI_Atomic_ptr_t *a, void *b)
+{
+    void *volatile *ret = (void* volatile*)b;
+#if (defined(__x86_64__) && !defined(__ILP32__))
+    __asm__ __volatile__("\txchgq %0, %1;"
+                         : "+r" (ret), "+m" (a->value)
+                         :
+                         : "memory");
+#elif (defined(__x86_64__) && defined(__ILP32__)) || defined(__i386__)
+    __asm__ __volatile__("\txchgl %0, %1;"
+                         : "+r" (ret), "+m" (a->value)
+                         :
+                         : "memory");
+#else
+#    error Cannot detect whether this is a 32-bit or 64-bit x86 build.
+#endif
+    return (void*)ret;
+}
+
+
+
+/* spinlocks : */
+
+static inline void tMPI_Spinlock_init(tMPI_Spinlock_t *x)
+{
+    x->lock = 0;
+}
+
+
+
+static inline void tMPI_Spinlock_lock(tMPI_Spinlock_t *x)
+{
+    /* this is a spinlock with a double loop, as recommended by Intel
+       it pauses in the outer loop (the one that just checks for the
+       availability of the lock), and thereby reduces bus contention and
+       prevents the pipeline from flushing. */
+    __asm__ __volatile__("1:\tcmpl $0, %0\n"    /* check the lock */
+                         "\tje 2f\n"            /* try to lock if it is
+                                                   free by jumping forward */
+                         "\tpause\n"            /* otherwise: small pause
+                                                   as recommended by Intel */
+                         "\tjmp 1b\n"           /* and jump back */
+
+                         "2:\tmovl $1, %%eax\n" /* set eax to 1, the locked
+                                                   value of the lock */
+                         "\txchgl %%eax, %0\n"  /* atomically exchange
+                                                   eax with the lock value */
+                         "\tcmpl $0, %%eax\n"   /* compare the exchanged
+                                                   value with 0 */
+                         "\tjne 1b"             /* jump backward if we didn't
+                                                   just lock */
+                         : "+m" (x->lock)       /* input & output var */
+                         :
+                         : "%eax", "memory"     /* we changed memory */
+                         );
+}
+
+
+
+static inline void tMPI_Spinlock_unlock(tMPI_Spinlock_t *x)
+{
+    /* this is apparently all that is needed for unlocking a lock */
+    __asm__ __volatile__(
+        "\n\tmovl $0, %0\n"
+        : "=m" (x->lock) : : "memory" );
+}
+
+
+
+static inline int tMPI_Spinlock_trylock(tMPI_Spinlock_t *x)
+{
+    int old_value = 1;
+
+    __asm__ __volatile__("\tmovl %2, %0\n"     /* set eax to 1, the locked
+                                                  value of the lock */
+                         "\txchgl %0, %1\n"    /* atomically exchange
+                                                  eax with the address in
+                                                  rdx. */
+                         : "+r" (old_value), "+m" (x->lock)
+                         : "i" (1)
+                         : "memory");
+    return (old_value);
+}
+
+
+
+static inline int tMPI_Spinlock_islocked(const tMPI_Spinlock_t *x)
+{
+    return ( (*((volatile int*)(&(x->lock)))) != 0);
+}
+
+
+static inline void tMPI_Spinlock_wait(tMPI_Spinlock_t *x)
+{
+    /* this is the spinlock without the xchg.  */
+    __asm__ __volatile__("1:\tcmpl $0, %0\n" /* check the lock */
+                         "\tje 2f\n"         /* try to lock if it is
+                                                free by jumping forward */
+                         "\tpause\n"         /* otherwise: small pause
+                                                as recommended by Intel */
+                         "\tjmp 1b\n"        /* and jump back */
+                         "2:\tnop\n"         /* jump target for end
+                                                of wait */
+                         : "+m" (x->lock)    /* input & output var */
+                         :
+                         : "memory"          /* we changed memory */
+                         );
+#if 0
+    do
+    {
+        tMPI_Atomic_memory_barrier();
+    }
+    while (tMPI_Spinlock_islocked(x));
+#endif
+}
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/msvc.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/msvc.h
new file mode 100644 (file)
index 0000000..11f6438
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/* we need this for all the data types. We use WIN32_LEAN_AND_MEAN to avoid
+      polluting the global namespace. */
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#undef WIN32_LEAN_AND_MEAN
+
+#define tMPI_Atomic_memory_barrier()
+
+
+typedef struct tMPI_Atomic
+{
+    LONG volatile value;          /*!< Volatile, to avoid compiler aliasing */
+} tMPI_Atomic_t;
+
+typedef struct tMPI_Atomic_ptr
+{
+    void* volatile value;          /*!< Volatile, to avoid compiler aliasing */
+} tMPI_Atomic_ptr_t;
+
+typedef struct tMPI_Spinlock
+{
+    LONG volatile lock;           /*!< Volatile, to avoid compiler aliasing */
+} tMPI_Spinlock_t;
+
+#define TMPI_ATOMIC_HAVE_NATIVE_SPINLOCK
+
+#define TMPI_SPINLOCK_INITIALIZER   { 0 }
+
+
+#define tMPI_Atomic_get(a)  ((a)->value)
+#define tMPI_Atomic_set(a, i)  (((a)->value) = (i))
+
+
+#define tMPI_Atomic_ptr_get(a)    ((a)->value)
+#define tMPI_Atomic_ptr_set(a, i)  (((a)->value) = (void*)(i))
+
+
+#define tMPI_Atomic_fetch_add(a, i)  \
+    InterlockedExchangeAdd((LONG volatile *)(a), (LONG) (i))
+#define TMPI_ATOMIC_HAVE_NATIVE_FETCH_ADD
+
+#define tMPI_Atomic_add_return(a, i)  \
+    ( (i) + InterlockedExchangeAdd((LONG volatile *)(a), (LONG) (i)) )
+#define TMPI_ATOMIC_HAVE_NATIVE_ADD_RETURN
+
+#define tMPI_Atomic_cas(a, oldval, newval) \
+    (InterlockedCompareExchange((LONG volatile *)(a), (LONG) (newval), (LONG) (oldval)) == (LONG)oldval)
+
+#define tMPI_Atomic_ptr_cas(a, oldval, newval) \
+    (InterlockedCompareExchangePointer(&((a)->value), (PVOID) (newval),  \
+                                       (PVOID) (oldval)) == (PVOID)oldval)
+
+#define TMPI_ATOMIC_HAVE_NATIVE_SWAP
+#define tMPI_Atomic_swap(a, b) \
+    InterlockedExchange((LONG volatile *)(a), (LONG) (b))
+
+#define tMPI_Atomic_ptr_swap(a, b) \
+    InterlockedExchangePointer(&((a)->value), (PVOID) (b))
+
+
+
+static inline void tMPI_Spinlock_init(tMPI_Spinlock_t *x)
+{
+    x->lock = 0;
+}
+
+# define tMPI_Spinlock_lock(x)   \
+    while ((InterlockedCompareExchange((LONG volatile *)(x), 1, 0)) != 0)
+
+
+#define tMPI_Spinlock_trylock(x)   \
+    InterlockedCompareExchange((LONG volatile *)(x), 1, 0)
+
+
+static inline void tMPI_Spinlock_unlock(tMPI_Spinlock_t *x)
+{
+    x->lock = 0;
+}
+
+
+static inline int tMPI_Spinlock_islocked(const tMPI_Spinlock_t *x)
+{
+    return (*(volatile signed char *)(&(x)->lock) != 0);
+}
+
+
+static inline void tMPI_Spinlock_wait(tMPI_Spinlock_t *x)
+{
+    while (tMPI_Spinlock_islocked(x))
+    {
+        /*Sleep(0);*/
+    }
+}
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/suncc-sparc.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/suncc-sparc.h
new file mode 100644 (file)
index 0000000..3f8024a
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/* File contributed by Sergey Klyaus */
+
+#include <atomic.h>
+
+/* this is for newer versions of gcc that have built-in intrinsics,
+   on platforms not explicitly supported with inline assembly. */
+
+#define tMPI_Atomic_memory_barrier()  do { membar_consumer(); membar_producer(); } while (0)
+
+/* Only gcc and Intel support this check, otherwise set it to true (skip doc) */
+#if (!defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined DOXYGEN)
+#define __builtin_constant_p(i) (1)
+#endif
+
+
+typedef struct tMPI_Atomic
+{
+    volatile uint_t value;
+}
+tMPI_Atomic_t;
+
+typedef struct tMPI_Atomic_ptr
+{
+    void* volatile value;
+}
+tMPI_Atomic_ptr_t;
+
+
+/* for now we simply assume that int and void* assignments are atomic */
+#define tMPI_Atomic_get(a)  ((int)( (a)->value) )
+#define tMPI_Atomic_set(a, i)  (((a)->value) = (i))
+
+
+#define tMPI_Atomic_ptr_get(a)  ((void*)((a)->value) )
+#define tMPI_Atomic_ptr_set(a, i)  (((a)->value) = (void*)(i))
+
+
+static inline int tMPI_Atomic_cas(tMPI_Atomic_t *a, int oldval, int newval)
+{
+    return (int)atomic_cas_uint(&a->value, (uint_t)oldval, (uint_t)newval);
+}
+
+
+static inline int tMPI_Atomic_ptr_cas(tMPI_Atomic_ptr_t* a, void *oldval,
+                                      void *newval)
+{
+    /*atomic_cas_ptr always returns value stored in a, so*/
+    return atomic_cas_ptr(&(a->value), oldval, newval) == oldval;
+}
+
+static inline int tMPI_Atomic_add_return(tMPI_Atomic_t *a, volatile int i)
+{
+    return (int) atomic_add_int_nv(&a->value, i);
+}
+#define TMPI_ATOMIC_HAVE_NATIVE_ADD_RETURN
+
+static inline int tMPI_Atomic_fetch_add(tMPI_Atomic_t *a, volatile int i)
+{
+    return (int) atomic_add_int_nv(&a->value, i) - i;
+}
+#define TMPI_ATOMIC_HAVE_NATIVE_FETCH_ADD
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/xlc_ppc.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/atomic/xlc_ppc.h
new file mode 100644 (file)
index 0000000..3b22137
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+/* IBM xlC compiler */
+#include <builtins.h>
+
+
+#define TMPI_XLC_INTRINSICS
+
+/* ppc has many memory synchronization instructions */
+/*#define tMPI_Atomic_memory_barrier() { __fence(); __sync(); __fence();}*/
+/*#define tMPI_Atomic_memory_barrier() __isync();*/
+/*#define tMPI_Atomic_memory_barrier() __lwsync();*/
+
+/* for normal memory, this should be enough: */
+#define tMPI_Atomic_memory_barrier() { __fence(); __eieio(); __fence(); }
+#define tMPI_Atomic_memory_barrier_acq() { __eieio(); __fence(); }
+#define tMPI_Atomic_memory_barrier_rel() { __fence(); __eieio(); }
+#define TMPI_HAVE_ACQ_REL_BARRIERS
+
+/*#define tMPI_Atomic_memory_barrier() __eieio();*/
+
+
+typedef struct tMPI_Atomic
+{
+    volatile int value __attribute__ ((aligned(64)));
+}
+tMPI_Atomic_t;
+
+
+typedef struct tMPI_Atomic_ptr
+{
+    /* volatile char* volatile is not a bug, but means a volatile pointer
+       to a volatile value. This is needed for older versions of
+       xlc. */
+    volatile char* volatile value __attribute__ ((aligned(64)));  /*!< Volatile, to avoid compiler aliasing */
+}
+tMPI_Atomic_ptr_t;
+
+
+typedef struct tMPI_Spinlock
+{
+    volatile int lock __attribute__ ((aligned(64)));
+}
+tMPI_Spinlock_t;
+#define TMPI_ATOMIC_HAVE_NATIVE_SPINLOCK
+
+
+
+
+#define tMPI_Atomic_get(a)   (int)((a)->value)
+#define tMPI_Atomic_set(a, i)  (((a)->value) = (i))
+#define tMPI_Atomic_ptr_get(a)   ((a)->value)
+#define tMPI_Atomic_ptr_set(a, i)  (((a)->value) = (i))
+
+#define TMPI_SPINLOCK_INITIALIZER   { 0 }
+
+
+static inline int tMPI_Atomic_cas(tMPI_Atomic_t *a, int oldval, int newval)
+{
+#ifdef TMPI_XLC_INTRINSICS
+    int ret;
+
+    __fence(); /* this one needs to be here to avoid ptr. aliasing issues */
+    __eieio();
+    ret = (__compare_and_swap(&(a->value), &oldval, newval));
+    __isync();
+    __fence(); /* and this one needs to be here to avoid aliasing issues */
+    return ret;
+#else
+    int prev;
+    __asm__ __volatile__ ("1:    lwarx   %0,0,%2 \n"
+                          "\t cmpw    0,%0,%3 \n"
+                          "\t bne     2f \n"
+                          "\t stwcx.  %4,0,%2 \n"
+                          "\t bne-    1b \n"
+                          "\t sync \n"
+                          "2: \n"
+                          : "=&r" (prev), "=m" (a->value)
+                          : "r" (&a->value), "r" (oldval), "r" (newval),
+                          "m" (a->value));
+
+    return prev == oldval;
+#endif
+}
+
+
+static inline int tMPI_Atomic_ptr_cas(tMPI_Atomic_ptr_t *a, void* oldval,
+                                      void* newval)
+{
+    int                     ret;
+    volatile char* volatile oldv = (char*)oldval;
+    volatile char* volatile newv = (char*)newval;
+
+    __fence(); /* this one needs to be here to avoid ptr. aliasing issues */
+    __eieio();
+#if (!defined (__LP64__) ) && (!defined(__powerpc64__) )
+    ret = __compare_and_swap((int *)&(a->value), (int*)&oldv, (int)newv);
+#else
+    ret = __compare_and_swaplp((long *)&(a->value), (long*)&oldv, (long)newv);
+#endif
+    __isync();
+    __fence();
+
+    return ret;
+}
+
+
+
+
+static inline int tMPI_Atomic_add_return(tMPI_Atomic_t *a, int i)
+{
+#ifdef TMPI_XLC_INTRINSICS
+    int oldval, newval;
+
+    do
+    {
+        __fence();
+        __eieio(); /* these memory barriers are neccesary */
+        oldval = tMPI_Atomic_get(a);
+        newval = oldval + i;
+    }
+    /*while(!__compare_and_swap( &(a->value), &oldval, newval));*/
+    while (__check_lock_mp( (int*)&(a->value), oldval, newval));
+
+    /*__isync();*/
+    __fence();
+
+    return newval;
+#else
+    int t;
+
+    __asm__ __volatile__("1:     lwarx   %0,0,%2 \n"
+                         "\t add     %0,%1,%0 \n"
+                         "\t stwcx.  %0,0,%2 \n"
+                         "\t bne-    1b \n"
+                         "\t isync \n"
+                         : "=&r" (t)
+                         : "r" (i), "r" (&a->value) );
+    return t;
+#endif
+}
+#define TMPI_ATOMIC_HAVE_NATIVE_ADD_RETURN
+
+
+
+static inline int tMPI_Atomic_fetch_add(tMPI_Atomic_t *a, int i)
+{
+#ifdef TMPI_XLC_INTRINSICS
+    int oldval, newval;
+
+    do
+    {
+        __fence();
+        __eieio(); /* these memory barriers are neccesary */
+        oldval = tMPI_Atomic_get(a);
+        newval = oldval + i;
+    }
+    /*while(__check_lock_mp((const int*)&(a->value), oldval, newval));*/
+    while (__check_lock_mp( (int*)&(a->value), oldval, newval));
+    /*while(!__compare_and_swap( &(a->value), &oldval, newval));*/
+    /*__isync();*/
+    __fence();
+
+    return oldval;
+#else
+    int t;
+
+    __asm__ __volatile__("\t eieio\n"
+                         "1:     lwarx   %0,0,%2 \n"
+                         "\t add     %0,%1,%0 \n"
+                         "\t stwcx.  %0,0,%2 \n"
+                         "\t bne-    1b \n"
+                         "\t isync \n"
+                         : "=&r" (t)
+                         : "r" (i), "r" (&a->value));
+
+    return (t - i);
+#endif
+}
+#define TMPI_ATOMIC_HAVE_NATIVE_FETCH_ADD
+
+
+static inline void tMPI_Spinlock_init(tMPI_Spinlock_t *x)
+{
+    __fence();
+    __clear_lock_mp((const int*)x, 0);
+    __fence();
+}
+
+
+static inline void tMPI_Spinlock_lock(tMPI_Spinlock_t *x)
+{
+    __fence();
+    do
+    {
+    }
+    while (__check_lock_mp((int*)&(x->lock), 0, 1));
+    tMPI_Atomic_memory_barrier_acq();
+}
+
+
+static inline int tMPI_Spinlock_trylock(tMPI_Spinlock_t *x)
+{
+    int ret;
+    /* Return 0 if we got the lock */
+    __fence();
+    ret = __check_lock_mp((int*)&(x->lock), 0, 1);
+    tMPI_Atomic_memory_barrier_acq();
+    return ret;
+}
+
+
+static inline void tMPI_Spinlock_unlock(tMPI_Spinlock_t *x)
+{
+    tMPI_Atomic_memory_barrier_rel();
+    __clear_lock_mp((int*)&(x->lock), 0);
+}
+
+
+static inline int tMPI_Spinlock_islocked(const tMPI_Spinlock_t *x)
+{
+    int ret;
+    __fence();
+    ret = ((x->lock) != 0);
+    tMPI_Atomic_memory_barrier_acq();
+    return ret;
+}
+
+
+static inline void tMPI_Spinlock_wait(tMPI_Spinlock_t *x)
+{
+    do
+    {
+    }
+    while (tMPI_Spinlock_islocked(x));
+}
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/barrier.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/barrier.h
new file mode 100644 (file)
index 0000000..58899a0
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#ifndef TMPI_BARRIER_H_
+#define TMPI_BARRIER_H_
+
+#include "visibility.h"
+#include "wait.h"
+
+/** Fast (possibly busy-wait-based) barrier type
+ *
+ *  This barrier has the same functionality as the standard
+ *  tMPI_Thread_barrier_t, but since it is based on spinlocks that yield
+ *  to the scheduler in case of waiting, it provides faster synchronization
+ *  at the cost of busy-waiting, while still behaving relatively nicely
+ *  to other processes/threads. This is therefore the preferred type of
+ *  barrier for when waits are expected to be reasonably short.
+ *
+ *  Variables of this type should be initialized by calling
+ *  tMPI_Barrier_init() to set the number of threads
+ *  that should be synchronized.
+ *
+ * \see
+ * - tMPI_Barrier_init
+ * - tMPI_Barrier_wait
+ */
+typedef struct tMPI_Barrier_t tMPI_Barrier_t;
+struct tMPI_Barrier_t
+{
+    tMPI_Atomic_t     count;     /*!< Number of threads remaining     */
+    int               threshold; /*!< Total number of threads         */
+    tMPI_Atomic_t     cycle;     /*!< Current cycle (alternating 0/1) */
+    TMPI_YIELD_WAIT_DATA
+};
+
+
+
+/** Initialize barrier
+ *
+ *  \param barrier  Pointer to _spinlock_ barrier. Note that this is not
+ *                  the same datatype as the full, thread based, barrier.
+ *  \param count    Number of threads to synchronize. All threads
+ *                  will be released after \a count calls to
+ *                  tMPI_Barrier_wait().
+ */
+TMPI_EXPORT
+void tMPI_Barrier_init(tMPI_Barrier_t *barrier, int count);
+
+
+/** Perform yielding, busy-waiting barrier synchronization
+ *
+ *  This function blocks until it has been called N times,
+ *  where N is the count value the barrier was initialized with.
+ *  After N total calls all threads return. The barrier automatically
+ *  cycles, and thus requires another N calls to unblock another time.
+ *
+ *  \param barrier  Pointer to previously created barrier.
+ *
+ *  \return The last thread returns -1, all the others 0.
+ */
+TMPI_EXPORT
+int tMPI_Barrier_wait(tMPI_Barrier_t *barrier);
+
+
+#ifdef DOXYGEN
+/** Get the number of threads to synchronize for a barrier
+ *
+ *  This function returns the total number of threads the barrier
+ *  synchronizes.
+ *
+ *  \param barrier  Pointer to barrier.
+ *
+ *  \return the number of threads to synchronize
+ */
+TMPI_EXPORT
+int tMPI_Barrier_N(tMPI_Barrier_t *barrier);
+#else
+#define tMPI_Barrier_N(barrier)  ((barrier)->threshold)
+#endif
+
+#endif /* TMPI_BARRIER_H_ */
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/collective.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/collective.h
new file mode 100644 (file)
index 0000000..249bf6a
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#ifndef TMPI_COLLECTIVE_H_
+#define TMPI_COLLECTIVE_H_
+
+/** \file
+ *
+ * \brief Collective functions
+ *
+ */
+
+#include "visibility.h"
+
+/** Execute function once over comm
+
+    Executes a given function only once per collective call over comm.
+
+    Collective function.
+
+    \param[in] function     the function to call
+    \param[in] param        the parameter to the function
+    \param[out] was_first   set to 1 if the current thread was the one to
+                            execute the function. unchanged if current thread
+                            was not the one to execute the function; ignored
+                            if NULL.
+    \param[in] comm         The communicator.
+    \returns MPI_SUCCESS on success.
+ */
+TMPI_EXPORT
+int tMPI_Once(tMPI_Comm comm, void (*function)(void*), void *param,
+              int *was_first);
+
+/** Execute function once over comm, and wait for the function to return
+    its value.
+
+    Executes a given function only once per collective call over comm.
+
+    Collective function.
+
+    \param[in] function     the function to call
+    \param[in] param        the parameter to the function
+    \param[out] was_first   set to 1 if the current thread was the one to
+                            execute the function. unchanged if current thread
+                            was not the one to execute the function; ignored
+                            if NULL.
+    \param[in] comm         The communicator.
+    \returns the return value of the function
+ */
+TMPI_EXPORT
+void* tMPI_Once_wait(tMPI_Comm comm, void* (*function)(void*), void *param,
+                     int *was_first);
+
+/** Allocate a shared block of memory as a collective call.
+
+    Collective function.
+
+    \param[in] comm         The communicator
+    \param[in] size         The size in bytes to allocate
+ */
+TMPI_EXPORT
+void* tMPI_Shmalloc(tMPI_Comm comm, size_t size);
+
+
+
+
+
+
+#include "atomic.h"
+
+typedef struct
+{
+    tMPI_Atomic_t n_remaining; /* number of remaining operations */
+    void         *res;         /* result pointer */
+    tMPI_Comm     comm;
+} tMPI_Reduce_req;
+
+/** Allocate data structure for asynchronous reduce. */
+TMPI_EXPORT
+tMPI_Reduce_req *tMPI_Reduce_req_alloc(tMPI_Comm comm);
+
+#endif /* TMPI_COLLECTIVE_H_ */
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/event.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/event.h
new file mode 100644 (file)
index 0000000..058e3a8
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#ifndef TMPI_EVENT_H_
+#define TMPI_EVENT_H_
+
+#include "visibility.h"
+#include "wait.h"
+
+/*! \file
+
+   \brief Event notification wait and signaling functions.
+
+   The event structure offers lightweight signaling and scheduler-yielding
+   (but still spinning) waiting for waits of intermediate durations (i.e.
+   longer than appropriate for a spin lock but shorter than mutex_cond_wait().
+   These functions only take care of the waiting and signaling; the user is
+   responsible for the handling of the actual event structures and condition
+   variables.
+ */
+
+/*! \brief Event notification structure.
+
+   Structure for notifying one thread that something has happened, allowing
+   that thread to wait, and possibly give up its timeslice. This structure
+   only takes care of the notification itself, and will not handle data
+   about incoming events - that is left up to the user.
+
+   This structure allows notification of a single thread by any number of
+   threads*/
+typedef struct tMPI_Event_t tMPI_Event;
+struct tMPI_Event_t
+{
+    tMPI_Atomic_t sync;      /* the event sync counter */
+    int           last_sync; /* the last sync event looked at */
+    TMPI_YIELD_WAIT_DATA     /* data associated with yielding */
+};
+
+
+
+/*! \brief Initialize the event object.
+
+    \param ev The event structure to be intialized. */
+TMPI_EXPORT
+void tMPI_Event_init(tMPI_Event *ev);
+
+/*! \brief Deallocate the internals of the contents of event object.
+
+    \param ev The event structure to be destroyed. */
+TMPI_EXPORT
+void tMPI_Event_destroy(tMPI_Event *ev);
+
+/*! \brief Wait for an event to occur.
+
+   Sets the number of events that had occurred during the wait in N.
+   \param ev The event structure to wait on.
+   \returns  The number of events that have occurred at function
+             return time. */
+TMPI_EXPORT
+int tMPI_Event_wait(tMPI_Event *ev);
+
+#ifdef DOXYGEN
+/*! \brief Signal an event, possibly waking an tMPI_Event_wait().
+
+    \param ev  The event to signal. */
+TMPI_EXPORT
+void tMPI_Event_signal(tMPI_Event *ev);
+#else
+#define tMPI_Event_signal(ev) \
+    { \
+        tMPI_Atomic_memory_barrier_rel(); \
+        tMPI_Atomic_fetch_add( &((ev)->sync), 1); \
+    }
+#endif
+
+#ifdef DOXYGEN
+/*! \brief Signal processing of an event.
+
+   Each event that is handled by the receiver, must be processed through
+   tMPI_Event_process(). Unprocessed events will make tMPI_Event_wait() return
+   immediately.
+
+   \param ev  The event object.
+   \param N   The number of processed events. */
+TMPI_EXPORT
+void tMPI_Event_process(tMPI_Event *ev, int N);
+#else
+#define tMPI_Event_process(ev, N) \
+    { \
+        (ev)->last_sync += N; \
+    }
+#endif
+
+#endif /* TMPI_EVENT_H_ */
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/hwinfo.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/hwinfo.h
new file mode 100644 (file)
index 0000000..c91b8f6
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#ifndef _TMPI_HWINFO_H_
+#define _TMPI_HWINFO_H_
+
+/*! \file
+
+   \brief CPU/core/HT count function.
+
+ */
+
+/*! \brief Determine number of hardware threads that can be run simultaneously
+
+    Returns the total number of cores and SMT threads that can run.
+
+    \returns The maximum number of threads that can run simulataneously.
+         If this number cannot be determined for the current architecture,
+         -1 is returned.
+ */
+TMPI_EXPORT
+int tMPI_Get_hw_nthreads(void);
+
+
+/*! \brief Determine the recommended number of hardware threads to run on
+
+
+    Returns the total number of cores and SMT threads to run on. This is
+    equal to the number of hardware threads available, or 1 if the number
+    can't be determined, or if there are no atomics for this platform.
+
+    \returns The maximum number of threads to run on.
+ */
+TMPI_EXPORT
+int tMPI_Get_recommended_nthreads(void);
+
+
+#endif
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/list.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/list.h
new file mode 100644 (file)
index 0000000..14a8aa1
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#ifndef TMPI_LIST_H_
+#define TMPI_LIST_H_
+
+#include "atomic.h"
+
+
+/** \file
+ *
+ * \brief Lock-free list data structures.
+ *
+ */
+
+
+/**  Lock-free single-ended stack (FIFO)
+
+   Is a list with push, pop and detach operations */
+typedef struct
+{
+    tMPI_Atomic_ptr_t head;          /**< Pointer to the top stack element. */
+} tMPI_Stack;
+
+/**  A single element in stack */
+typedef struct tMPI_Stack_element
+{
+    struct tMPI_Stack_element *next; /**< Pointer to the next stack element. */
+    void                      *data; /**< Pointer to data. */
+} tMPI_Stack_element;
+
+
+/**  Initialize a stack */
+TMPI_EXPORT
+void tMPI_Stack_init(tMPI_Stack *st);
+
+/**  Deallocates a stack */
+TMPI_EXPORT
+void tMPI_Stack_destroy(tMPI_Stack *st);
+
+/**  Pushes a stack element onto a stack */
+TMPI_EXPORT
+void tMPI_Stack_push(tMPI_Stack *st, tMPI_Stack_element *el);
+
+/**  Pops a stack element from  a stack */
+TMPI_EXPORT
+tMPI_Stack_element *tMPI_Stack_pop(tMPI_Stack *st);
+
+/**  Detaches entire stack for use by a single thread */
+TMPI_EXPORT
+tMPI_Stack_element *tMPI_Stack_detach(tMPI_Stack *st);
+
+
+
+#if 0
+/**  Lock-free double-ended queue (FIFO)
+
+   Is a list with enqueue and dequeue operations */
+typedef struct
+{
+    tMPI_Atomic_ptr_t head, tail;
+} tMPI_Queue;
+
+/**  A single element in a queue */
+typedef struct tMPI_Queue_element
+{
+    struct tMPI_Queue_element *next; /**< Pointer to the next queue element. */
+    struct tMPI_Queue_element *prev; /**< Pointer to the prev queue element. */
+    void                      *data; /**< Pointer to data. */
+} tMPI_Queue_element;
+
+/**  Initialize a queue */
+void tMPI_Queue_init(tMPI_Queue *q);
+
+/**  Deallocates a queue */
+void tMPI_Queue_destroy(tMPI_Queue *q);
+
+/**  Enqueue an element onto the head of a queue */
+void tMPI_Queue_enqueue(tMPI_Queue *q, tMPI_Queue_element *qe);
+
+/**  Dequeue an element from the end a queue */
+tMPI_Queue_element *tMPI_Queue_dequeue(tMPI_Queue *q);
+
+
+
+
+
+/**  Lock-free circular doubly linked list */
+typedef struct
+{
+    tMPI_Atomic_ptr_t head;
+} tMPI_List;
+
+/**  Lock-free circular doubly linked list */
+typedef struct tMPI_List_element
+{
+    struct tMPI_List_element *next, *prev;
+    void                     *data;
+} tMPI_List_element;
+
+/**  Initialize a list */
+void tMPI_List_init(tMPI_List *l);
+/**  Deallocates a list */
+void tMPI_List_destroy(tMPI_List *l);
+
+tMPI_List_element* tMPI_List_first(tMPI_List *l);
+tMPI_List_element* tMPI_List_next(tMPI_List         *l,
+                                  tMPI_List_element *le);
+tMPI_List_element* tMPI_List_prev(tMPI_List         *l,
+                                  tMPI_List_element *le);
+
+void tMPI_List_add(tMPI_List *l, tMPI_List_element *le);
+void tMPI_List_insert(tMPI_List *l, tMPI_List_element *after,
+                      tMPI_List_element *le);
+void tMPI_List_remove(tMPI_List *l, tMPI_List_element *le);
+#endif
+
+#endif /* TMPI_LIST_H_ */
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/lock.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/lock.h
new file mode 100644 (file)
index 0000000..0a2bcef
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#ifndef TMPI_FASTLOCK_H_
+#define TMPI_FASTLOCK_H_
+
+#include "visibility.h"
+#include "wait.h"
+#include "atomic.h"
+
+/** Fast (possibly busy-wait-based) lock type
+ *
+ *  This lock type forms an intermediate between the spinlocks and mutexes:
+ *  it is based on a busy-wait loop, but yields to the scheduler if the lock
+ *  is locked.  This is therefore the preferred type of  lock for when waits
+ *  are expected to be reasonably short.
+ *
+ *  Variables of this type should be initialized by calling
+ *  tMPI_Lock_init().
+ *
+ * \see
+ * - tMPI_Lock_init
+ * - tMPI_Lock_lock
+ */
+typedef struct tMPI_Lock tMPI_Lock_t;
+struct tMPI_Lock
+{
+    tMPI_Spinlock_t   lock;      /*!< The underlying spin lock */
+    TMPI_YIELD_WAIT_DATA
+};
+
+
+/** Initialize lock
+ *
+ *  \param lock     Pointer to the new lock.
+ */
+TMPI_EXPORT
+void tMPI_Lock_init(tMPI_Lock_t *lock);
+
+
+/** Perform yielding, busy-waiting locking
+ *
+ *  This function blocks until the lock is locked.
+ *
+ *  \param lock  Pointer to previously created lock.
+ */
+TMPI_EXPORT
+void tMPI_Lock_lock(tMPI_Lock_t *lock);
+
+/** Unlock the lock
+ *
+ *  \param lock  Pointer to previously created lock.
+ */
+TMPI_EXPORT
+void tMPI_Lock_unlock(tMPI_Lock_t *lock);
+
+/** Try to lock the lock but don't block if it is locked.
+ *
+ *  \param lock  Pointer to previously created lock.
+ */
+TMPI_EXPORT
+int tMPI_Lock_trylock(tMPI_Lock_t *lock);
+
+/** Check the status of the lock without affecting its state
+ *
+ *  \param lock  Pointer to previously created lock.
+ */
+TMPI_EXPORT
+int tMPI_Lock_islocked(tMPI_Lock_t *lock);
+
+#endif /* TMPI_FASTLOCK_H_ */
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/mpi_bindings.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/mpi_bindings.h
new file mode 100644 (file)
index 0000000..b663342
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#ifndef TMPI_MPI_BINDINGS_H_
+#define TMPI_MPI_BINDINGS_H_
+
+#include "tmpi.h"
+
+/** \file
+   \brief MPI bindings for thread_mpi/tmpi.h
+
+   This file contains only macros and redefinitions to expose the standard
+   MPI API with thread_mpi calls.
+
+   This is different from the API exposed through thread_mpi/tmpi.h, which
+   uses names like \a tMPI_Send() instead of \a MPI_Send()
+
+   \sa thread_mpi/tmpi.h for documentation of the available data types and
+      functions
+   \sa http://www.mpi-forum.org/docs/docs.html for MPI documentation.
+ */
+
+#ifndef DOXYGEN
+
+/* The MPI_Comm structure contains the group of processes to communicate
+   with (defines the scope for global operations such as broadcast) */
+typedef struct tmpi_comm_ *MPI_Comm;
+/* The group part of the MPI-Comm structure */
+typedef struct tmpi_group_ *MPI_Group;
+/* Request structure for holding data about non-blocking transfers */
+typedef struct tmpi_req_ *MPI_Request;
+/* status of receives */
+typedef struct tmpi_status_ MPI_Status;
+/* data types */
+typedef struct tmpi_datatype_ *MPI_Datatype;
+/* reduce operations */
+typedef tMPI_Op MPI_Op;
+
+
+#define MPI_C_BOOL              TMPI_C_BOOL
+#define MPI_CHAR                TMPI_CHAR
+#define MPI_SHORT               TMPI_SHORT
+#define MPI_INT                 TMPI_INT
+#define MPI_LONG                TMPI_LONG
+#define MPI_LONG_LONG           TMPI_LONG_LONG
+#define MPI_LONG_LONG_INT       TMPI_LONG_LONG_INT
+#define MPI_SIGNED_CHAR         TMPI_SIGNED_CHAR
+#define MPI_UNSIGNED_CHAR       TMPI_UNSIGNED_CHAR
+#define MPI_UNSIGNED_SHORT      TMPI_UNSIGNED_SHORT
+#define MPI_UNSIGNED            TMPI_UNSIGNED
+#define MPI_UNSIGNED_LONG_LONG  TMPI_UNSIGNED_LONG_LONG
+#define MPI_FLOAT               TMPI_FLOAT
+#define MPI_DOUBLE              TMPI_DOUBLE
+#define MPI_LONG_DOUBLE         TMPI_LONG_DOUBLE
+#define MPI_WCHAR               TMPI_WCHAR
+#define MPI_BYTE                TMPI_BYTE
+#define MPI_INT64_T             TMPI_INT64_T
+
+
+#define MPI_SUCCESS                 TMPI_SUCCESS
+#define MPI_ERR_MALLOC              TMPI_ERR_MALLOC
+#define MPI_ERR_INIT                TMPI_ERR_INIT
+#define MPI_ERR_FINALIZE            TMPI_ERR_FINALIZE
+#define MPI_ERR_GROUP               TMPI_ERR_GROUP
+#define MPI_ERR_COMM                TMPI_ERR_COMM
+#define MPI_ERR_STATUS              TMPI_ERR_STATUS
+#define MPI_ERR_GROUP_RANK          TMPI_ERR_GROUP_RANK
+#define MPI_ERR_DIMS                TMPI_ERR_DIMS
+#define MPI_ERR_COORDS              TMPI_ERR_COORDS
+#define MPI_ERR_CART_CREATE_NPROCS  TMPI_ERR_CART_CREATE_NPROCS
+#define MPI_ERR_XFER_COUNTERPART    TMPI_ERR_XFER_COUNTERPART
+#define MPI_ERR_XFER_BUFSIZE        TMPI_ERR_XFER_BUFSIZE
+#define MPI_ERR_XFER_BUF_OVERLAP    TMPI_ERR_XFER_BUF_OVERLAP
+#define MPI_ERR_SEND_DEST           TMPI_ERR_SEND_DEST
+#define MPI_ERR_RECV_SRC            TMPI_ERR_RECV_SRC
+#define MPI_ERR_BUF                 TMPI_ERR_BUF
+#define MPI_ERR_MULTI_MISMATCH      TMPI_ERR_MULTI_MISMATCH
+#define MPI_ERR_OP_FN               TMPI_ERR_OP_FN
+#define MPI_ERR_ENVELOPES           TMPI_ERR_ENVELOPES
+#define MPI_ERR_REQUESTS            TMPI_ERR_REQUESTS
+#define MPI_ERR_IN_STATUS           TMPI_ERR_IN_STATUS
+#define MPI_FAILURE                 TMPI_FAILURE
+#define MPI_ERR_UNKNOWN             TMPI_ERR_UNKNOWN
+#define N_MPI_ERR                   N_TMPI_ERR
+
+#define MPI_MAX_ERROR_STRING        TMPI_MAX_ERROR_STRING
+#define MPI_UNDEFINED               TMPI_UNDEFINED
+
+
+#define MPI_Errhandler_fn           tMPI_Errhandler_fn
+#define MPI_Errhandler              tMPI_Errhandler
+#define MPI_ERRORS_ARE_FATAL        TMPI_ERRORS_ARE_FATAL
+#define MPI_ERRORS_RETURN           TMPI_ERRORS_RETURN
+
+
+
+/* miscelaneous defines */
+#define MPI_ANY_SOURCE          TMPI_ANY_SOURCE
+#define MPI_ANY_TAG             TMPI_ANY_TAG
+
+/* comm_compare defines */
+#define MPI_IDENT               TMPI_IDENT
+#define MPI_CONGRUENT           TMPI_CONGRUENT
+#define MPI_SIMILAR             TMPI_SIMILAR
+#define MPI_UNEQUAL             TMPI_UNEQUAL
+
+
+/* topology test defines */
+#define MPI_CART                TMPI_CART
+#define MPI_GRAPH               TMPI_GRAPH
+
+
+#define MPI_COMM_WORLD          TMPI_COMM_WORLD
+#define MPI_COMM_NULL           TMPI_COMM_NULL
+
+
+#define MPI_GROUP_NULL          TMPI_GROUP_NULL
+#define MPI_GROUP_EMPTY         TMPI_GROUP_EMPTY
+
+#define MPI_MAX_PROCESSOR_NAME  TMPI_MAX_PROCESSOR_NAME
+
+
+/* MPI status */
+#define MPI_STATUS_IGNORE       TMPI_STATUS_IGNORE
+#define MPI_STATUSES_IGNORE     TMPI_STATUSES_IGNORE
+
+#define MPI_SOURCE              TMPI_SOURCE
+#define MPI_TAG                 TMPI_TAG
+#define MPI_ERROR               TMPI_ERROR
+
+#define mpi_status_             tmpi_status_
+
+#define MPI_REQUEST_NULL        TMPI_REQUEST_NULL
+
+#define MPI_IN_PLACE            TMPI_IN_PLACE
+
+
+
+#define MPI_MAX          TMPI_MAX
+#define MPI_MIN          TMPI_MIN
+#define MPI_SUM          TMPI_SUM
+#define MPI_PROD         TMPI_PROD
+#define MPI_LAND         TMPI_LAND
+#define MPI_BAND         TMPI_BAND
+#define MPI_LOR          TMPI_LOR
+#define MPI_BOR          TMPI_BOR
+#define MPI_LXOR         TMPI_LXOR
+#define MPI_BXOR         TMPI_BXOR
+
+/* the functions: */
+#define MPI_Init                    tMPI_Init
+#define MPI_Finalize                tMPI_Finalize
+#define MPI_Abort                   tMPI_Abort
+#define MPI_Initialized             tMPI_Initialized
+#define MPI_Finalized               tMPI_Finalized
+
+#define MPI_Create_errhandler       tMPI_Create_errhandler
+#define MPI_Errhandler_free         tMPI_Errhandler_free
+#define MPI_Comm_set_errhandler     tMPI_Comm_set_errhandler
+#define MPI_Comm_get_errhandler     tMPI_Comm_get_errhandler
+#define MPI_Error_string            tMPI_Error_string
+
+#define MPI_Get_processor_name      tMPI_Get_processor_name
+#define MPI_Wtime                   tMPI_Wtime
+
+#define MPI_Group_size              tMPI_Group_size
+#define MPI_Group_rank              tMPI_Group_rank
+#define MPI_Group_incl              tMPI_Group_incl
+#define MPI_Comm_group              tMPI_Comm_group
+#define MPI_Group_free              tMPI_Group_free
+
+#define MPI_Comm_size               tMPI_Comm_size
+#define MPI_Comm_rank               tMPI_Comm_rank
+#define MPI_Comm_compare            tMPI_Comm_compare
+#define MPI_Comm_free               tMPI_Comm_free
+#define MPI_Comm_create             tMPI_Comm_create
+#define MPI_Comm_split              tMPI_Comm_split
+#define MPI_Comm_dup                tMPI_Comm_dup
+
+#define MPI_Topo_test               tMPI_Topo_test
+#define MPI_Cartdim_get             tMPI_Cartdim_get
+#define MPI_Cart_get                tMPI_Cart_get
+#define MPI_Cart_rank               tMPI_Cart_rank
+#define MPI_Cart_coords             tMPI_Cart_coords
+#define MPI_Cart_map                tMPI_Cart_map
+#define MPI_Cart_create             tMPI_Cart_create
+#define MPI_Cart_sub                tMPI_Cart_sub
+
+#define MPI_Type_contiguous         tMPI_Type_contiguous
+#define MPI_Type_commit             tMPI_Type_commit
+
+#define MPI_Send                    tMPI_Send
+#define MPI_Recv                    tMPI_Recv
+#define MPI_Sendrecv                tMPI_Sendrecv
+#define MPI_Get_count               tMPI_Get_count
+
+#define MPI_Isend                   tMPI_Isend
+#define MPI_Irecv                   tMPI_Irecv
+#define MPI_Test                    tMPI_Test
+#define MPI_Wait                    tMPI_Wait
+#define MPI_Waitall                 tMPI_Waitall
+
+#define MPI_Barrier                 tMPI_Barrier
+
+#define MPI_Bcast                   tMPI_Bcast
+
+#define MPI_Gather                  tMPI_Gather
+#define MPI_Gatherv                 tMPI_Gatherv
+#define MPI_Scatter                 tMPI_Scatter
+#define MPI_Scatterv                tMPI_Scatterv
+#define MPI_Alltoall                tMPI_Alltoall
+#define MPI_Alltoallv               tMPI_Alltoallv
+
+
+#define MPI_Reduce                  tMPI_Reduce
+#define MPI_Allreduce               tMPI_Allreduce
+#define MPI_Scan                    tMPI_Scan
+
+#endif /* DOXYGEN */
+
+#endif /* TMPI_MPI_BINDINGS_H_ */
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/numa_malloc.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/numa_malloc.h
new file mode 100644 (file)
index 0000000..7b50653
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+#ifndef TMPI_NUMA_MALLOC_H_
+#define TMPI_NUMA_MALLOC_H_
+
+/*! \file
+
+    \brief NUMA aware memory allocators.
+
+    This file provides a NUMA aware version of malloc() and friends to
+    force local allocation, preventing expensive 'remote' memory access.
+
+    Note that memory allocated with tMPI_Malloc_local(), etc. MUST be
+    freed with tMPI_Free_numa().
+
+    Currently this is only implemented on Windows. Check for the presence
+    of these functions with
+    \code
+   #ifdef TMPI_NUMA_MALLOC
+    ....
+   #endif
+    \endcode
+ */
+
+
+#include "visibility.h"
+
+
+#if (defined(WIN32) || defined( _WIN32 ) || defined(WIN64) || defined( _WIN64 )) && !defined (__CYGWIN__) && !defined (__CYGWIN32__)
+
+#define TMPI_NUMA_MALLOC
+
+#endif
+
+
+/*! \brief Allocate local memory by size in bytes.
+
+    \see malloc()
+
+    \param[in] size  = size in units of sizeof() of the memory to be allocated
+
+    \return Pointer to allocated memory, or NULL in case of failure
+ */
+TMPI_EXPORT
+void *tMPI_Malloc_local(size_t size);
+
+
+/*! \brief Allocate local memory by array and element size.
+
+    \see calloc()
+
+    \param[in] nmemb  = number of array members
+    \param[in] size  = size in units of sizeof() of a single array element
+
+    \return Pointer to allocated memory, or NULL in case of failure
+ */
+TMPI_EXPORT
+void *tMPI_Calloc_local(size_t nmemb, size_t size);
+
+
+/*! \brief Re-allocate to local memory.
+
+    \see realloc()
+
+    \param[in] ptr   = input pointer of originally allocated memory (can
+                        be allocated with NUMA or non-NUMA malloc())
+    \param[in] size  = new size in units of sizeof().
+
+    \return Pointer to allocated memory, or NULL in case of failure
+ */
+TMPI_EXPORT
+void *tMPI_Realloc_local(void *ptr, size_t size);
+
+
+/*! \brief Free memory allocate with any of the NUMA-aware allocators
+
+    \see calloc()
+
+    \param[in] ptr = pointer to block of memory allocated with a NUMA allocator
+
+    \return Returns debug info: 0 if the memory was allocated with a NUMA
+            allocator, 1 if it was allocated by another allocator.
+ */
+TMPI_EXPORT
+int tMPI_Free_numa(void *ptr);
+
+#endif /* TMPI_NUMA_MALLOC_H_ */
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/threads.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/threads.h
new file mode 100644 (file)
index 0000000..11a459e
--- /dev/null
@@ -0,0 +1,676 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+#ifndef TMPI_THREADS_H_
+#define TMPI_THREADS_H_
+
+/*! \file threads.h
+ *
+ *  \brief Platform-independent multithreading support.
+ *
+ *  This file provides an portable thread interface very similar to POSIX
+ *  threads, as a thin wrapper around the threads provided operating system
+ *  (whether they be POSIX threads or something else).
+ *
+ *  In other words, while the naming conventions are very similar to
+ *  pthreads, you should NOT assume that a thread_mpi thread type
+ *  (thread,mutex,key, etc) is the same as the Pthreads equivalent,
+ *  even on platforms where we are using pthreads.
+ *
+ *  Because the synchronization functions here are similar to the basic
+ *  mutexes/conditions/barriers provided by the operating system,
+ *  performance will most likely be worse than when using the atomic
+ *  synchronization functions of atomic.h. On the other hand, because the
+ *  operating system can schedule out waiting threads using these functions,
+ *  they are the appropriate ones for I/O and initialization.
+ *
+ *  Since this module is merely intended to be a transparent wrapper around
+ *  a system-dependent thread implementation, we simply echo errors to stderr.
+ *  The user should check the return codes\] and take appropriate action
+ *  when using these functions (fat chance, but errors are rare).
+ */
+
+
+
+#include <stdio.h>
+
+#include "visibility.h"
+#include "atomic.h"
+
+
+/*! \brief Thread ID: abstract tMPI_Thread type
+ *
+ *  The contents of this structure depends on the actual threads
+ *  implementation used.
+ */
+typedef struct tMPI_Thread* tMPI_Thread_t;
+
+
+
+/*! \brief Opaque mutex datatype
+ *
+ *  This type is only defined in the header to enable static
+ *  initialization with TMPI_THREAD_MUTEX_INITIALIZER.
+ *  You should _never_ touch the contents or create a variable
+ *  with automatic storage class without calling tMPI_Thread_mutex_init().
+ */
+typedef struct
+{
+    tMPI_Atomic_t      initialized; /*!< Whether \a mutex has been
+                                       initialized. */
+    struct tMPI_Mutex* mutex;       /*!< Actual mutex data structure. */
+}  tMPI_Thread_mutex_t;
+/*! \brief Static initializer for tMPI_Thread_mutex_t
+ *
+ *  See the description of the tMPI_Thread_mutex_t datatype for instructions
+ *  on how to use this. Note that any variables initialized with this value
+ *  MUST have static storage allocation.
+ */
+#define TMPI_THREAD_MUTEX_INITIALIZER { {0}, NULL }
+
+
+
+
+
+/*! \brief Pthread implementation of the abstract tMPI_Thread_key type
+ *
+ *  The contents of this structure depends on the actual threads
+ *  implementation used.
+ */
+typedef struct
+{
+    tMPI_Atomic_t           initialized; /*!< Whether \a key has been
+                                            initialized. */
+    struct tMPI_Thread_key *key;         /*!< Actual key data structure. */
+} tMPI_Thread_key_t;
+
+
+
+
+
+/*! \brief One-time initialization data for thread
+ *
+ *  This is an opaque datatype which is necessary for tMPI_Thread_once(),
+ *  but since it needs to be initialized statically it must be defined
+ *  in the header. You will be sorry if you touch the contents.
+ *  Variables of this type should always be initialized statically to
+ *  TMPI_THREAD_ONCE_INIT.
+ *
+ *  This type is used as control data for single-time initialization.
+ *  The most common example is a mutex at file scope used when calling
+ *  a non-threadsafe function, e.g. the FFTW initialization routines.
+ *
+ */
+typedef struct
+{
+    tMPI_Atomic_t once; /*!< Whether the operation has been performed. */
+} tMPI_Thread_once_t;
+/*! \brief Static initializer for tMPI_Thread_once_t
+ *
+ *  See the description of the tMPI_Thread_once_t datatype for instructions
+ *  on how to use this. Normally, all variables of that type should be
+ *  initialized statically to this value.
+ */
+#define TMPI_THREAD_ONCE_INIT { {0} }
+
+
+
+
+/*! \brief Condition variable handle for threads
+ *
+ *  Condition variables are useful for synchronization together
+ *  with a mutex: Lock the mutex and check if our thread is the last
+ *  to the barrier. If no, wait for the condition to be signaled.
+ *  If yes, reset whatever data you want and then signal the condition.
+ *
+ *  This should be considered an opaque structure, but since it is sometimes
+ *  useful to initialize it statically it must go in the header.
+ *  You will be sorry if you touch the contents.
+ *
+ *  There are two alternatives: Either initialize it as a static variable
+ *  with TMPI_THREAD_COND_INITIALIZER, or call tMPI_Thread_cond_init()
+ *  before using it.
+ */
+typedef struct
+{
+    tMPI_Atomic_t            initialized; /*!< Whether \a condp has been
+                                             initialized. */
+    struct tMPI_Thread_cond* condp;       /*!< Actual condition variable data
+                                             structure. */
+} tMPI_Thread_cond_t;
+/*! \brief Static initializer for tMPI_Thread_cond_t
+ *
+ *  See the description of the tMPI_Thread_cond_t datatype for instructions
+ *  on how to use this. Note that any variables initialized with this value
+ *  MUST have static storage allocation.
+ */
+#define TMPI_THREAD_COND_INITIALIZER { {0}, NULL}
+
+
+
+
+
+
+/*! \brief Pthread implementation of barrier type.
+ *
+ *  The contents of this structure depends on the actual threads
+ *  implementation used.
+ */
+typedef struct
+{
+    tMPI_Atomic_t               initialized; /*!< Whether \a barrierp has been initialized. */
+    struct tMPI_Thread_barrier* barrierp;    /*!< Actual barrier data structure. */
+    volatile int                threshold;   /*!< Total number of members in barrier     */
+    volatile int                count;       /*!< Remaining count before completion      */
+    volatile int                cycle;       /*!< Alternating 0/1 to indicate round      */
+}tMPI_Thread_barrier_t;
+/*! \brief Static initializer for tMPI_Thread_barrier_t
+ *
+ *  See the description of the tMPI_Thread_barrier_t datatype for instructions
+ *  on how to use this. Note that variables initialized with this value
+ *  MUST have static storage allocation.
+ *
+ * \param count  Threshold for barrier
+ */
+#define TMPI_THREAD_BARRIER_INITIALIZER(count)   { \
+        NULL, count, count, 0 \
+}
+
+
+
+
+
+
+/** Thread support status enumeration */
+enum tMPI_Thread_support
+{
+    TMPI_THREAD_SUPPORT_NO  = 0, /*!< Starting threads will fail */
+    TMPI_THREAD_SUPPORT_YES = 1  /*!< Thread support available   */
+};
+
+
+/** Thread setaffinity support status enumeration */
+enum tMPI_Thread_setaffinity_support
+{
+    TMPI_SETAFFINITY_SUPPORT_NO  = 0, /*!< Setting thread affinity not
+                                           supported */
+    TMPI_SETAFFINITY_SUPPORT_YES = 1  /*!< Setting thread affinity supported */
+};
+
+
+
+
+/** handle a fatal error.
+
+   \param file     source code file name of error.
+   \param line     source code line number of error.
+   \param message  format string for error message.
+ */
+TMPI_EXPORT
+void tMPI_Fatal_error(const char *file, int line, const char *message, ...);
+/** Convenience macro for the first two arguments to tMPI_Fatal_error(). */
+#define TMPI_FARGS __FILE__, __LINE__
+
+
+
+/*! \name Thread creation, destruction, and inspection
+ \{ */
+/** Check if threads are supported
+ *
+ *  This routine provides a cleaner way to check if threads are supported
+ *  instead of sprinkling your code with preprocessor conditionals.
+ *
+ *  All thread functions are still available even without thread support,
+ *  but some of them might return failure error codes, for instance if you try
+ *  to start a thread.
+ *
+ *  \return 1 if threads are supported, 0 if not.
+ */
+TMPI_EXPORT
+enum tMPI_Thread_support tMPI_Thread_support(void);
+
+
+/** Get the number of hardware threads that can be run simultaneously.
+
+    Returns the total number of cores and SMT threads that can run.
+
+    \returns The maximum number of threads that can run simulataneously.
+        If this number cannot be determined for the current architecture,
+        0 is returned.
+ */
+TMPI_EXPORT
+int tMPI_Thread_get_hw_number(void);
+
+
+/** Create a new thread
+ *
+ *  The new thread will call start_routine() with the argument arg.
+ *
+ *  Please be careful not to change arg after calling this function.
+ *
+ *  \param[out] thread          Pointer to thread ID
+ *  \param[in] start_routine    The function to call in the new thread
+ *  \param[in] arg              Argument to call with
+ *
+ *  \return Status - 0 on success, or an error code.
+ */
+TMPI_EXPORT
+int tMPI_Thread_create(tMPI_Thread_t *thread,
+                       void           * (*start_routine)(void *),
+                       void         * arg);
+
+
+
+
+/** Wait for a specific thread to finish executing
+ *
+ *  If the thread has already finished the routine returns immediately.
+ *
+ *  \param[in] thread       Pointer to thread ID
+ *  \param[out] value_ptr   Pointer to location where to store pointer to
+ *                          exit value from threads that called
+ *                          tMPI_Thread_exit().
+ *
+ *  \return 0 if the join went ok, or a non-zero error code.
+ */
+TMPI_EXPORT
+int tMPI_Thread_join(tMPI_Thread_t thread, void **value_ptr);
+
+
+/** Terminate calling thread
+ *
+ *  Die voluntarily.
+ *
+ *  \param value_ptr   Pointer to a return value. Threads waiting for us to
+ *                     join them can read this value if they try.
+ *  \return
+ */
+TMPI_EXPORT
+void tMPI_Thread_exit(void *value_ptr);
+
+
+
+/** Ask a thread to exit
+ *
+ *  This routine tries to end the execution of another thread, but there are
+ *  no guarantees it will succeed.
+ *
+ *  \param thread     Handle to thread we want to see dead.
+ *  \return 0 or a non-zero error message.
+ */
+TMPI_EXPORT
+int tMPI_Thread_cancel(tMPI_Thread_t thread);
+
+
+
+
+/** Get a thread ID of the calling thread.
+ *
+ * This function also works on threads not started with tMPI_Thread_create,
+ * or any other function in thread_mpi. This makes it possible to, for
+ * example assign thread affinities to any thread.
+ *
+ * \return A thread ID of the calling thread */
+TMPI_EXPORT
+tMPI_Thread_t tMPI_Thread_self(void);
+
+
+
+/** Check whether two thread pointers point to the same thread
+ *
+ * \param[in]  t1  Thread ID 1
+ * \param[in]  t2  Thread ID 2
+ * \return non-zero if the thread structs refer to the same thread,
+            0 if the threads are different*/
+TMPI_EXPORT
+int tMPI_Thread_equal(tMPI_Thread_t t1, tMPI_Thread_t t2);
+
+
+/** Check whether this platform supports setting of thread affinity
+ *
+ * This function returns TMPI_SETAFFINITY_SUPPORT_YES if setting thread
+ * affinity is supported by the platform, and TMPI_SETAFFINITY_SUPPORT_NO
+ * if not. If this function returns 0, the function
+ * tMPI_Thread_setaffinity_single will simply return 0 itself, effectively
+ * ignoring the request.
+ *
+ * \return TMPI_SETAFFINITY_SUPPORT_YES if setting affinity is supported,
+            TMPI_SETAFFINITY_SUPPORT_NO otherwise */
+TMPI_EXPORT
+enum tMPI_Thread_setaffinity_support tMPI_Thread_setaffinity_support(void);
+
+
+/** Set thread affinity to a single core
+ *
+ * This function sets the thread affinity of a thread to a a specific
+ * numbered processor. This only works if the underlying operating system
+ * supports it. The processor number must be between 0 and the number returned
+ * by tMPI_Thread_get_hw_number().
+ *
+ * \param[in] thread   Thread ID of the thread to set affinity for
+ * \param[in] nr       Processor number to set affinity to
+ * \return zero on success, non-zero on error */
+TMPI_EXPORT
+int tMPI_Thread_setaffinity_single(tMPI_Thread_t thread, unsigned int nr);
+
+
+/*! \} */
+/*! \name Mutexes
+ \{ */
+
+
+/** Initialize a new mutex
+ *
+ *  This routine must be called before using any mutex not initialized
+ *  with static storage class and TMPI_THREAD_MUTEX_INITIALIZER.
+ *
+ *  \param mtx   Pointer to a mutex opaque type.
+ *  \return      0 or an error code.
+ */
+TMPI_EXPORT
+int tMPI_Thread_mutex_init(tMPI_Thread_mutex_t *mtx);
+
+
+
+
+/** Kill a mutex you no longer need
+ *
+ *  Note that this call only frees resources allocated inside the mutex. It
+ *  does not free the tMPI_Thread_mutex_t memory area itself if you created it
+ *  with dynamic memory allocation.
+ *
+ *  \param mtx  Pointer to a mutex variable to get rid of.
+ *  \return 0 or a non-zero error code.
+ */
+TMPI_EXPORT
+int tMPI_Thread_mutex_destroy(tMPI_Thread_mutex_t *mtx);
+
+
+
+
+/** Wait for exclusive access to a mutex
+ *
+ *  This routine does not return until the mutex has been acquired.
+ *
+ *  \param mtx  Pointer to the mutex to lock
+ *  \return 0 or a non-zero error code.
+ */
+TMPI_EXPORT
+int tMPI_Thread_mutex_lock(tMPI_Thread_mutex_t *mtx);
+
+
+
+
+/** Try to lock a mutex, return if busy
+ *
+ *  This routine always returns directly.
+ *
+ *  \param mtx  Pointer to the mutex to try and lock
+ *  \return If the mutex was available and we successfully locked it,
+ *     we return 0. If the mutex was unavailable because it was
+ *     already locked by another thread, we return non-zero. If the
+ *     mutex was already held by this thread, the return value is
+ *     implementation-defined (pthreads non-zero, winthreads zero).
+ */
+TMPI_EXPORT
+int tMPI_Thread_mutex_trylock(tMPI_Thread_mutex_t *mtx);
+
+
+
+
+/** Release the exclusive access to a mutex
+ *
+ *  \param mtx  Pointer to the mutex to release
+ *  \return 0 or a non-zero error code.
+ */
+TMPI_EXPORT
+int tMPI_Thread_mutex_unlock(tMPI_Thread_mutex_t *mtx);
+
+
+
+/*! \} */
+/*! \name Thread-specific storage
+ \{ */
+
+
+/** Initialize thread-specific-storage handle
+ *
+ *  The tMPI_Thread_key_t handle must always be initialized dynamically with
+ *  this routine. If you need to initialize it statically in a file, use the
+ *  tMPI_Thread_once() routine and corresponding data to initialize the
+ *  thread-specific-storage key the first time you access it.
+ *
+ *  \param  key          Pointer to opaque thread key type.
+ *  \param  destructor   Routine to call (to free memory of key) when we quit
+ *
+ *  \return status - 0 on sucess or a standard error code.
+ *
+ */
+TMPI_EXPORT
+int tMPI_Thread_key_create(tMPI_Thread_key_t *key, void (*destructor)(void *));
+
+
+
+
+/** Delete thread-specific-storage handle
+ *
+ *  Calling this routine will kill the handle, and invoke the automatic
+ *  destructor routine for each non-NULL value pointed to by key.
+ *
+ *  \param  key  Opaque key type to destroy
+ *  \return 0 or a non-zero error message.
+ */
+TMPI_EXPORT
+int tMPI_Thread_key_delete(tMPI_Thread_key_t key);
+
+
+
+
+/** Get value for thread-specific-storage in this thread
+ *
+ *  If it has not yet been set, NULL is returned.
+ *
+ *  \param key   Thread-specific-storage handle.
+ *  \return Pointer-to-void, the value of the data in this thread.
+ */
+TMPI_EXPORT
+void * tMPI_Thread_getspecific(tMPI_Thread_key_t key);
+
+
+
+/** Set value for thread-specific-storage in this thread
+ *
+ *  \param key     Thread-specific-storage handle.
+ *  \param value   What to set the data to (pointer-to-void).
+ *  \return 0 or a non-zero error message.
+ */
+TMPI_EXPORT
+int tMPI_Thread_setspecific(tMPI_Thread_key_t key, void *value);
+
+
+/*! \} */
+/*! \name Run-once
+ \{ */
+
+
+/** Run the provided routine exactly once
+ *
+ *  The control data must have been initialized before calling this routine,
+ *  but you can do it with the static initialzer TMPI_THREAD_ONCE_INIT.
+ *
+ *  tMPI_Thread_once() will not return to any of the calling routines until
+ *  the initialization function has been completed.
+ *
+ *  \param once_data     Initialized one-time execution data
+ *  \param init_routine  Function to call exactly once
+ *  \return 0 or a non-zero error message.
+ */
+TMPI_EXPORT
+int tMPI_Thread_once(tMPI_Thread_once_t *once_data,
+                     void                (*init_routine)(void));
+
+
+/*! \} */
+/*! \name Condition variables
+ \{ */
+
+/** Initialize condition variable
+ *
+ *  This routine must be called before using any condition variable
+ *  not initialized with static storage class and TMPI_THREAD_COND_INITIALIZER.
+ *
+ *  \param cond  Pointer to previously allocated condition variable
+ *  \return      0 or a non-zero error message.
+ */
+TMPI_EXPORT
+int tMPI_Thread_cond_init(tMPI_Thread_cond_t *cond);
+
+
+
+/** Destroy condition variable
+ *
+ *  This routine should be called when you are done with a condition variable.
+ *  Note that it only releases memory allocated internally, not the
+ *  tMPI_Thread_cond_t structure you provide a pointer to.
+ *
+ *  \param cond Pointer to condition variable.
+ *  \return 0 or a non-zero error message.
+ */
+TMPI_EXPORT
+int tMPI_Thread_cond_destroy(tMPI_Thread_cond_t *cond);
+
+
+
+/** Wait for a condition to be signaled
+ *
+ *  This routine releases the mutex, and waits for the condition to be
+ *  signaled by another thread before it returns.
+ *
+ *  Note that threads waiting for conditions with tMPI_Thread_cond_wait
+ *  may be subject to spurious wakeups: use this function in a while loop
+ *  and check the state of a predicate associated with the wakeup
+ *  before leaving the loop.
+ *
+ * \param cond  condition variable
+ *  \param mtx  Mutex protecting the condition variable
+ *
+ *  \return 0 or a non-zero error message.
+ */
+TMPI_EXPORT
+int tMPI_Thread_cond_wait(tMPI_Thread_cond_t  *cond,
+                          tMPI_Thread_mutex_t *mtx);
+
+
+
+
+/** Unblock one waiting thread
+ *
+ *  This routine signals a condition variable to one
+ *  thread (if any) waiting for it after calling
+ *  tMPI_Thread_cond_wait().
+ *
+ * \param cond  condition variable
+ *
+ *  \return 0 or a non-zero error message.
+ */
+TMPI_EXPORT
+int tMPI_Thread_cond_signal(tMPI_Thread_cond_t *cond);
+
+
+/** Unblock all waiting threads
+ *
+ *  This routine signals a condition variable to all
+ *  (if any) threads that are waiting for it after calling
+ *  tMPI_Thread_cond_wait().
+ *
+ * \param cond  condition variable
+ *
+ *  \return 0 or a non-zero error message.
+ */
+TMPI_EXPORT
+int tMPI_Thread_cond_broadcast(tMPI_Thread_cond_t *cond);
+
+
+
+/*! \} */
+/*! \name Barriers
+ \{ */
+
+
+/** Initialize a synchronization barrier type
+ *
+ *  You only need to initialize a barrier once. They cycle
+ *  automatically, so after release it is immediately ready
+ *  to accept waiting threads again.
+ *
+ *  \param barrier  Pointer to previously allocated barrier type
+ *  \param count    Number of threads to synchronize. All threads
+ *                  will be released after \a count calls to
+ *                  tMPI_Thread_barrier_wait().
+ */
+TMPI_EXPORT
+int tMPI_Thread_barrier_init(tMPI_Thread_barrier_t *barrier, int count);
+
+
+
+/** Release data in a barrier datatype
+ *
+ *  \param barrier  Pointer to previously
+ *                  initialized barrier.
+ */
+TMPI_EXPORT
+int tMPI_Thread_barrier_destroy(tMPI_Thread_barrier_t *barrier);
+
+
+/** Perform barrier synchronization
+ *
+ *  This routine blocks until it has been called N times,
+ *  where N is the count value the barrier was initialized with.
+ *  After N total calls all threads return. The barrier automatically
+ *  cycles, and thus requires another N calls to unblock another time.
+ *
+ *  \param barrier  Pointer to previously create barrier.
+ *
+ *  \return The last thread returns -1, all the others 0.
+ */
+TMPI_EXPORT
+int tMPI_Thread_barrier_wait(tMPI_Thread_barrier_t *barrier);
+
+/*! \} */
+
+#endif /* TMPI_THREADS_H_ */
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/tmpi.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/tmpi.h
new file mode 100644 (file)
index 0000000..b4371da
--- /dev/null
@@ -0,0 +1,1389 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009,2016,2018,2019, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#ifndef TMPI_TMPI_H_
+#define TMPI_TMPI_H_
+
+/** \file
+ *
+ * \brief Partial implementation of MPI using only threads.
+ *
+ * See the MPI specification at
+ * http://www.mpi-forum.org/docs/docs.html
+ * for an explanation of what these functions do.
+ *
+ * Because this is a thread-based library, be very careful with global
+ * variables and static variables in functions: they will be shared across
+ * all threads and lead to conflicts if not properly mutex-ed or barrier-ed
+ * out.
+ *
+ * \sa http://www.mpi-forum.org/docs/docs.html for MPI documentation.
+ */
+
+
+/* for size_t, include stddef.h - which is in C89. This is done
+   regardless of whether we're compiling C++ or C code because the base
+   library for this is in C. */
+#include <stddef.h>
+
+#include "visibility.h"
+
+/** tMPI definition.
+
+   Use this to check for thread_mpi with the preprocessor. */
+#define TMPI
+
+
+/** tMPI initialization thread affinity strategy.
+
+    Used in the tMPI_Init_affinity() and  tMPI_Init_fn_affinity() functions,
+    to control how affinity is set. The default tMPI_Init() and tMPI_Init_fn()
+    functions use the TMPI_AFFINITY_ALL_CORES strategy.
+
+    These strategies are fairly basic. For more flexibility, use the
+    tMPI_Set_affinity() function.*/
+typedef enum
+{
+    TMPI_AFFINITY_NONE = 0,     /**< Do not set any thread affinity */
+    TMPI_AFFINITY_ALL_CORES,    /**< Only set affinity if the number of threads
+                                     is equal to the number of hardware threads
+                                     (cores + hyperthreads). This is the only
+                                     safe way to set thread affinity,
+                                     without clashes between multiple
+                                     instances of the same program. */
+} tMPI_Affinity_strategy;
+
+
+
+/** tMPI Communicator
+
+   Holds the group of processes to communicate
+   with, and defines the scope for global operations such as broadcast. */
+typedef struct tmpi_comm_ *tMPI_Comm;
+
+/** tMPI Group
+
+   The group structure. Contains a list of threads. */
+typedef struct tmpi_group_ *tMPI_Group;
+
+/** tMPI Request
+
+   Request structure for holding data about non-blocking transfers. */
+typedef struct tmpi_req_ *tMPI_Request;
+
+
+/** tMPI datatype
+
+   tMPI data type structure. Holds info about datatypes. */
+typedef struct tmpi_datatype_ *tMPI_Datatype;
+
+
+/*! \name tMPI Data types
+    These are MPI data types as specified by the MPI standard.
+    Note that not all are available.  */
+/*! \{ */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_C_BOOL;             /**< bool */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_CHAR;               /**< char */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_SHORT;              /**< short */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_INT;                /**< int */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_LONG;               /**< long */
+#ifdef SIZEOF_LONG_LONG_INT
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_LONG_LONG;          /**< long long */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_LONG_LONG_INT;      /**< long long int */
+#endif
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_SIGNED_CHAR;        /**< signed char */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_UNSIGNED_CHAR;      /**< unsigned char */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_UNSIGNED_SHORT;     /**< unsigned short */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_UNSIGNED;           /**< unsigned int */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_UNSIGNED_LONG;      /**< unsigned long */
+#ifdef SIZEOF_LONG_LONG_INT
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_UNSIGNED_LONG_LONG; /**< unsigned long long */
+#endif
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_FLOAT;              /**< float */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_DOUBLE;             /**< double */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_LONG_DOUBLE;        /**< long double */
+/*extern tMPI_Datatype tMPI_UNSIGNED_WCHAR */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_BYTE;               /**< byte (for binary
+                                                               xmissions) */
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_POINTER;            /**< pointer (thread_mpi
+                                                                  specific) */
+
+TMPI_EXPORT
+extern const tMPI_Datatype TMPI_INT64_T;            /**< int64_t */
+
+/*! \} */
+
+
+/** Error codes */
+enum
+{
+    TMPI_SUCCESS = 0,            /*!< No error */
+    TMPI_ERR_NO_MEM,             /*!< Out of memory */
+    TMPI_ERR_IO,                 /*!< I/O Error (used for system errors) */
+    TMPI_ERR_INIT,               /*!< Initialization error */
+    TMPI_ERR_FINALIZE,           /*!< Finalize error */
+    TMPI_ERR_GROUP,              /*!< Group error */
+    TMPI_ERR_COMM,               /*!< Comm error */
+    TMPI_ERR_STATUS,             /*!< Status error */
+    TMPI_ERR_GROUP_RANK,         /*!< Group rank error */
+    TMPI_ERR_DIMS,               /*!< Invalid topology dimensions */
+    TMPI_ERR_COORDS,             /*!< Invalid topology coordinates */
+    TMPI_ERR_CART_CREATE_NPROCS, /*!< Not enough processes for topology*/
+    TMPI_ERR_XFER_COUNTERPART,   /*!< Invalid counterpart for xfer */
+    TMPI_ERR_XFER_BUFSIZE,       /*!< buffer size too small*/
+    TMPI_ERR_XFER_BUF_OVERLAP,   /*!< buffer overlaps (thread error?)*/
+    TMPI_ERR_SEND_DEST,          /*!< Faulty send destination */
+    TMPI_ERR_RECV_SRC,           /*!< Faulty receive source */
+    TMPI_ERR_BUF,                /*!< Invalid buffer */
+    TMPI_ERR_MULTI_MISMATCH,     /*!< Comm not the same in collective call*/
+    TMPI_ERR_OP_FN,              /*!< Invalid reduce operator*/
+    TMPI_ERR_ENVELOPES,          /*!< out of envelopes (tMPI internal) */
+    TMPI_ERR_REQUESTS,           /*!< out of requests (tMPI internal) */
+    TMPI_ERR_COPY_NBUFFERS,      /*!< out of copy buffers (tMPI internal)*/
+    TMPI_ERR_COPY_BUFFER_SIZE,   /*!< copy buffer size err (tMPI internal)*/
+    TMPI_ERR_IN_STATUS,          /*!< error code in tMPI_Status */
+    TMPI_ERR_PROCNR,             /*!< Hardware processor number (such as for
+                                      thread affinity) error */
+    TMPI_FAILURE,                /*!< Transmission failure */
+    TMPI_ERR_UNKNOWN,            /*!< Unknown error */
+    N_TMPI_ERR                   /* this must be the last one */
+};
+
+/** Maximum length of error string for tMPI_Error_string() */
+#define TMPI_MAX_ERROR_STRING            256
+
+/** default code for undefined value,
+
+    For example for undefined color in tMPI_Split(). */
+#define TMPI_UNDEFINED -1
+
+/** error handler function */
+typedef void (*tMPI_Errhandler_fn)(tMPI_Comm*, int*);
+/** error handler object */
+typedef struct tmpi_errhandler_ *tMPI_Errhandler;
+
+/** pre-defined error handler that abort()s on every error */
+extern tMPI_Errhandler TMPI_ERRORS_ARE_FATAL;
+/** pre-defined error handler that tries to continue on every error */
+extern tMPI_Errhandler TMPI_ERRORS_RETURN;
+
+/*! \name tMPI_Comm_compare() return codes */
+/*! \{ */
+/** Identical comms*/
+#define TMPI_IDENT 0
+/** Comms with the same members in the same order*/
+#define TMPI_CONGRUENT 1
+/** Comms with the same members in the different order*/
+#define TMPI_SIMILAR 2
+/** Comms with the different  members */
+#define TMPI_UNEQUAL 3
+/*! \} */
+
+
+/** Source number wildcard so tMPI_Recv(), etc. can receive from
+           any source. */
+#define TMPI_ANY_SOURCE -1
+/** Tag number wildcard so tMPI_Recv(), etc. can receive messages with
+           any tag. */
+#define TMPI_ANY_TAG -1
+
+/** Return code for Cartesian topology with tMPI_Topo_test().  */
+#define TMPI_CART 1
+/** Return code for graph topology with tMPI_Topo_test().  */
+#define TMPI_GRAPH 2
+
+
+/** Pre-initialized communicator with all available threads. */
+TMPI_EXPORT
+extern tMPI_Comm TMPI_COMM_WORLD;
+
+
+/** A pre-defined NULL communicator to compare against, to check comm
+           validity */
+#define TMPI_COMM_NULL nullptr
+/** A pre-defined NULL group to compare against, to check group
+           validity */
+#define TMPI_GROUP_NULL nullptr
+
+/** the empty group */
+extern tMPI_Group TMPI_GROUP_EMPTY;
+
+
+/** The maximum processor name returned using tMPI_Get_processor_name(). */
+#define TMPI_MAX_PROCESSOR_NAME 128
+
+
+/** Used as NULL status for tMPI_Recv(), etc. */
+#define TMPI_STATUS_IGNORE NULL
+/** Used as NULL status list for tMPI_Waitall(), etc. */
+#define TMPI_STATUSES_IGNORE NULL
+
+/** tMPI Status.
+
+   Holds status info (tag, sender, amount of data transmitted) for receives.
+   The status object is user-maintained. */
+typedef struct tmpi_status_
+{
+    int    TMPI_SOURCE;     /**< Message source rank. */
+    int    TMPI_TAG;        /**< Message source tag. */
+    int    TMPI_ERROR;      /**< Message error. */
+    size_t transferred;     /**< Number of transferred bytes */
+    int    cancelled;       /**< Whether the transmission was canceled */
+} tMPI_Status;
+/*typedef struct tmpi_status_ tMPI_Status;*/
+
+/** NULL request */
+#define TMPI_REQUEST_NULL NULL
+
+/** collective communication special to signify that the send
+           buffer is to function as receive buffer.
+
+           Used, for example in tMPI_Reduce. */
+#define TMPI_IN_PLACE NULL
+
+
+/** tMPI_Reduce operators.
+
+    These all work (except obviously bad combinations like bitwise
+    and/or/xor on floats, etc): */
+typedef enum
+{
+    TMPI_MAX,       /**< calculate maximum value */
+    TMPI_MIN,       /**< calculate minimum value */
+    TMPI_SUM,       /**< calculate sum */
+    TMPI_PROD,      /**< calculate product */
+    TMPI_LAND,      /**< calculate logical and */
+    TMPI_BAND,      /**< calculate binary and */
+    TMPI_LOR,       /**< calculate logical or */
+    TMPI_BOR,       /**< calculate binary or */
+    TMPI_LXOR,      /**< calculate logical xor */
+    TMPI_BXOR       /**< calculate binary xor */
+} tMPI_Op;
+
+#ifndef DOXYGEN
+/* function to obtain tMPI_COMM_SELF */
+tMPI_Comm tMPI_Get_comm_self(void);
+#endif
+/** The thread-specific comm containing only the thread itself.
+
+    \hideinitializer
+    \return the self comm object associated with the thread. */
+#define TMPI_COMM_SELF (tMPI_Get_comm_self())
+
+
+
+
+
+
+
+/* functions: */
+
+/*! \name Initialization and exit functions
+ \{ */
+/** Traditional MPI initializer; spawns threads that start at the given
+    function.
+
+    Seeks the argument '-nt n', where n is the number of
+    threads that will be created. If n==0, the number of threads will
+    be the recommended number of threads for this platform as obtained
+    from tMPI_Get_recommended_ntreads().
+
+    The new threads then run the function start_function, with the original
+    argc and argv. This function could be main(), or any other function;
+    calling this function again - whether from the started threads or from
+    the main thread - has no effect.
+
+    On platforms that support thread affinity setting, this function will
+    use the 'all-cores' affinity strategy: it will only set thread affinity
+    if the number of threads is equal to the number of hardware threads
+    (cores + hyperthreads).
+
+    \param[in] argc             argc of original main() invocation, or NULL
+    \param[in] argv             argv of original main() invocation, or NULL.
+    \param[in] start_function   Starting function of type
+                                int start_function(int argc, char *argv[]);
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Init(int *argc, char ***argv,
+              int (*start_function)(int, char**));
+
+
+/** Generic init function thread MPI intializer and thread spawner.
+
+    Creates N threads (including main thread)
+    that run the function start_function, which takes a void* argument,
+    given by arg. The function start_function also gets called by the main
+    thread. When the function start_function returns it, will behave
+    as if tMPI_Finalize is called, and if it's a sub-thread it will
+    stop running.
+
+    If N==0, the number of threads will be the recommended number of
+    threads for this platform as obtained from tMPI_Get_recommended_ntreads().
+
+    Note that thread affinity strategy only has an effect when this is
+    supported by the underlying platform. As of yet (2012), this is not the
+    case for Mac OS X, for example.
+
+    \param[in]  main_thread_returns   whether the control in the main thread
+                                      should return immediately (if true), or
+                                      the start_function() should be called
+                                      from the main thread, too (if false).
+    \param[in] N                      The number of threads to start (or 0 to
+                                      automatically determine this).
+    \param[in] aff_strategy           The thread affinity strategy to use.
+    \param[in] start_function         The function to start threads at
+                                      (including main thread if
+                                      main_thread_returns).
+    \param[in] arg                    An optional argument for start_function().
+
+    \return  TMPI_FAILURE on failure, TMPI_SUCCESS on succes (after all
+             threads have finished if main_thread_returns=true).  */
+TMPI_EXPORT
+int tMPI_Init_fn(int main_thread_returns, int N,
+                 tMPI_Affinity_strategy aff_strategy,
+                 void (*start_function)(const void*), const void *arg);
+
+
+
+
+
+/** get the number of threads from the command line
+
+    can be called before tMPI_Init()
+
+    \param[in]  argc                    argc from main()
+    \param[in]  argv                    argv from main()
+    \param[in]  optname                 name of the argument specifying the
+                                        number of threads to run. If this is
+                                        NULL, this function will read the first
+                                        argument and interpret it as the number
+                                        of threads.
+    \param[out] nthreads                the number of threads
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Get_N(int *argc, char ***argv, const char *optname, int *nthreads);
+
+
+
+/** Waits for all other threads to finish and cleans up
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Finalize(void);
+
+
+/** Just kills all threads.
+
+    Not really neccesary because exit() would do that for us anyway.
+
+    \param[in] comm         Comm to kill threads for
+    \param[in] errorcode    Error code to exit with
+
+    \return Never returns. */
+TMPI_EXPORT
+int tMPI_Abort(tMPI_Comm comm, int errorcode);
+
+/** whether tMPI_Init, but not yet tMPI_Finalize, has been run
+
+    \param[out] flag     Set to TRUE if tMPI_Init() has been called,
+                         FALSE if not.
+
+    \return     always returns TMPI_SUCCESS. */
+TMPI_EXPORT
+int tMPI_Initialized(int *flag);
+
+/** Determine whether tMPI_Finalize has been run.
+
+    \param[out] flag        Set to TRUE if tMPI_Finalize() has been
+                            called, FALSE if not.
+
+    \return     always returns TMPI_SUCCESS.  */
+TMPI_EXPORT
+int tMPI_Finalized(int *flag);
+/** \} */
+
+
+
+
+
+
+
+
+
+/*! \name Error handling functions
+ \{ */
+/** Create an error handler object from a function.
+
+    \param[in]  function        The function to make an error handler of.
+    \param[out] errhandler      The error handler.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Create_errhandler(tMPI_Errhandler_fn *function,
+                           tMPI_Errhandler    *errhandler);
+
+
+/** Free the error handler object.
+
+    \param[in] errhandler       The error handler.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Errhandler_free(tMPI_Errhandler *errhandler);
+
+/** Set the error handler.
+
+    \param[in] comm         the communicator to set the error handler for.
+    \param[in] errhandler   the error handler.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Comm_set_errhandler(tMPI_Comm comm, tMPI_Errhandler errhandler);
+
+/** get the error handler.
+
+    Gets the error handler associated with a comm
+
+    \param[in]  comm         the communicator to get the error handler for.
+    \param[out] errhandler   the error handler.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Comm_get_errhandler(tMPI_Comm comm, tMPI_Errhandler *errhandler);
+
+/** get the error string associated with an error code.
+
+    The length of the error string will never exceed TMPI_MAX_ERROR_STRING.
+
+    \param[in]  errorcode   The error code.
+    \param[out] string      The pre-allocated char pointer to output to.
+    \param[out] resultlen   The length of the error string. Will
+                            never be longer than TMPI_MAX_ERROR_STRING.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Error_string(int errorcode, char *string, size_t *resultlen);
+/** \} */
+
+
+
+
+
+
+
+
+/*! \name Environment query functions
+ \{ */
+/** returns string with thread number.
+
+    \param[out] name        Pre-allocated string to output name to (will not
+                            be longer than TMPI_MAX_PROCESSOR_NAME).
+    \param[out] resultlen   The length of the output. Note that this is an
+                            int instead of a size_t because the MPI standard
+                            for some reason defines all sizes as int
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Get_processor_name(char *name, int *resultlen);
+
+/** get a time value as a double, in seconds.
+
+    \return time value.
+ */
+TMPI_EXPORT
+double tMPI_Wtime(void);
+/** get the resolution of tMPI_Wtime as a double, in seconds
+
+    \return time resolution. */
+TMPI_EXPORT
+double tMPI_Wtick(void);
+
+#ifndef DOXYGEN
+#define tMPI_This_threadnr() (int)(tMPI_Get_current() - threads)
+#else
+/** Get the thread number of this thread.
+    Mostly for debugging.
+
+    \return the global thread number. */
+int tMPI_This_Threadnr(void);
+#endif
+
+/** \} */
+
+
+
+
+
+
+
+
+
+/*! \name tMPI_Group functions
+ \{ */
+/** Get the size (number of members) of a group.
+
+    \param[in]  group       The group.
+    \param[out] size        Size.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Group_size(tMPI_Group group, int *size);
+
+/** Get the rank of a thread in a group
+
+    \param[in]  group       The group.
+    \param[out] rank        Variable for the rank, or TMPI_UNDEFINED
+                            if not in group.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Group_rank(tMPI_Group group, int *rank);
+
+/** Create a new group as a the collection of threads with given ranks.
+
+    \param[in] group        The group from which the ranks are taken.
+    \param[in] n            The number of new group members.
+    \param[in] ranks        The ranks of the threads to add to the new group.
+    \param[out] newgroup    The new group.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Group_incl(tMPI_Group group, int n, int *ranks, tMPI_Group *newgroup);
+
+/** Get a pointer to the group in the comm.
+
+    \param[in] comm         The comm from which to take the group.
+    \param[out] group       The comm's group.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Comm_group(tMPI_Comm comm, tMPI_Group *group);
+
+/** De-allocate a group
+
+    \param[in] group        The group to de-allocate.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Group_free(tMPI_Group *group);
+/*! \} */
+
+
+
+
+
+
+
+/*! \name tMPI_Comm functions
+ \{ */
+/** Get the comm size (nr. of threads).
+
+    \param[in] comm         The comm to query.
+    \param[out] size        The comm size.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Comm_size(tMPI_Comm comm, int *size);
+
+/** get the rank in comm of the current process
+
+    \param[in]  comm        The comm to query.
+    \param[out] rank        Thread rank in comm.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Comm_rank(tMPI_Comm comm, int *rank);
+
+/** Compare two comms. Returns TMPI_IDENT if the two comms point to
+    the same underlying comm structure, TMPI_CONGRUENT if all
+    members appear in the both comms in the same order, TMPI_SIMILAR
+    if both comms have the smae members but not in the same order, or
+    TMPI_UNEQUAL if the comms have different members.
+
+    \param[in]  comm1        The first comm to compare.
+    \param[in]  comm2        The second comm to compare.
+    \param[out] result       The output result, one of the values
+                             described above.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Comm_compare(tMPI_Comm comm1, tMPI_Comm comm2, int *result);
+
+
+/** De-allocate a comm
+
+    Collective function.
+
+    \param[in] comm         The comm to free.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Comm_free(tMPI_Comm *comm);
+
+/** Create a comm based on group membership.
+
+    Collective function that creates a new comm containing only proceses
+    that are members of the given group.
+
+    \param[in]  comm        The originating comm.
+    \param[in]  group       The group of threads to create a comm from.
+    \param[out] newcomm     The new comm.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Comm_create(tMPI_Comm comm, tMPI_Group group, tMPI_Comm *newcomm);
+
+/** Split up a group into same-colored sub-groups ordered by key.
+
+    This is the main comm creation function: it's a collective call that takes
+    a color and a key from each process, and arranges all threads that
+    call tMPI_Split() withe the same color together into a comm.
+
+    The rank in the new group will be based on the value given in key.
+
+    Passing TMPI_UNDEFINED as a color will result in the thread not being
+    part of any group, and getting TMPI_COMM_NULL back in newcomm.
+
+    \param[in]  comm        The originating comm.
+    \param[in]  color       This thread's color (determines which comm it will
+                            be in). Giving TMPI_UNDEFINED will result in
+                            this thread not being in any group.
+    \param[in]  key         This thread's key (determines rank).
+    \param[out] newcomm     The new comm.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Comm_split(tMPI_Comm comm, int color, int key, tMPI_Comm *newcomm);
+
+/** Make a duplicate of a comm.
+
+    Collective function.
+
+    \param[in] comm         The originating comm.
+    \param[in] newcomm      The new comm.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Comm_dup(tMPI_Comm comm, tMPI_Comm *newcomm);
+/*! \} */
+
+
+
+
+
+
+
+
+/*! \name Topology functions
+ \{ */
+/* topology functions */
+/** Check what type of topology the comm has.
+
+    \param[in] comm         The comm to query
+    \param[out] status      The type of topology.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Topo_test(tMPI_Comm comm, int *status);
+
+/** Get the dimensionality of a comm with a topology.
+
+    \param[in] comm         The comm to query.
+    \param[out] ndims       The number of dimensions.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+
+TMPI_EXPORT
+int tMPI_Cartdim_get(tMPI_Comm comm, int *ndims);
+/** Get the size and pbc a of a comm with a Cartesian topology has.
+
+    \param[in]  comm        The comm to query.
+    \param[in]  maxdims     The maximum number of dimensions in the periods
+                            and coords parameter.
+    \param[out] dims        The number of dimensions.
+    \param[out] periods     The periodicity in each dimension.
+    \param[out] coords      The number of coordinates in each dimension.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+
+TMPI_EXPORT
+int tMPI_Cart_get(tMPI_Comm comm, int maxdims, int *dims, int *periods,
+                  int *coords);
+
+
+/** Get rank that a specific set of process coordinates has in
+    a Cartesian topology.
+
+    \param[in]  comm        The comm to query.
+    \param[in]  coords      The coordinates in each dimension.
+    \param[out] rank        The rank associated with the coordinates.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Cart_rank(tMPI_Comm comm, int *coords, int *rank);
+
+/** Get coordinates of a process rank in a Cartesian topology.
+
+    \param[in]  comm        The comm to query.
+    \param[in]  rank        The rank associated with the coordinates.
+    \param[in]  maxdims     The maximum number of dimensions in the coords
+                            parameter.
+    \param[out] coords      The coordinates in each dimension.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Cart_coords(tMPI_Comm comm, int rank, int maxdims, int *coords);
+
+/** Get optimal rank this process would have in a Cartesian topology.
+
+    \param[in]  comm        The comm to query.
+    \param[in]  ndims       The number of dimensions.
+    \param[in]  dims        The size in each dimension.
+    \param[in]  periods     The periodicity in each dimension.
+
+    \param[out] newrank     The rank the thread would have given the topology.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Cart_map(tMPI_Comm comm, int ndims, int *dims, int *periods,
+                  int *newrank);
+
+/** Create a comm with a Cartesian topology.
+
+    \param[in]  comm_old    The originating comm.
+    \param[in]  ndims       The number of dimensions.
+    \param[in]  dims        The size in each dimension.
+    \param[in]  periods     The periodicity in each dimension.
+    \param[in]  reorder     Whether to allow reordering of the threads
+                            according to tMPI_Cart_map().
+    \param[out] comm_cart   The new comm with Cartesian topology.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Cart_create(tMPI_Comm comm_old, int ndims, int *dims, int *periods,
+                     int reorder, tMPI_Comm *comm_cart);
+
+/** Create a comms that are sub-spaces of the Cartesian topology communicator.
+    Works like a MPI_Comm_split() for the Cartesian dimensions specified
+    as false in remain_dims.
+
+    \param[in]  comm        The originating comm with Cartesian topology.
+    \param[in]  remain_dims An Boolean array that decides whether a specific
+                            dimensionality should remain in newcomm (if true),
+                            or should be split up (if false).
+    \param[out] newcomm     The new split communicator
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Cart_sub(tMPI_Comm comm, int *remain_dims, tMPI_Comm *newcomm);
+
+/*! \} */
+
+
+
+
+
+
+
+
+/*! \name Data type manipulation functions
+ \{ */
+/** Create a contiguous data type (the only type possible right now).
+
+    Creates a datatype that is a vector of oldtype.
+
+    \param[in]  count       The number of oldtype types in the new type.
+    \param[in]  oldtype     The old data type.
+    \param[out] newtype     The new data type (still needs to be committed).
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Type_contiguous(int count, tMPI_Datatype oldtype,
+                         tMPI_Datatype *newtype);
+
+
+/** Make a data type ready for use.
+
+    \param[in,out] datatype  The new datatype.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Type_commit(tMPI_Datatype *datatype);
+/*! \} */
+
+
+
+
+
+
+
+
+/*! \name Point-to-point communication functions
+ \{ */
+
+/* blocking transfers. The actual transfer (copy) is done on the receiving end
+    (so that the receiver's cache already contains the data that it presumably
+     will use soon).  */
+/** Send message; blocks until buf is reusable.
+
+    \param[in]  buf         The buffer with data to send.
+    \param[in]  count       The number of items to send.
+    \param[in]  datatype    The data type of the items in buf.
+    \param[in]  dest        The rank of the destination thread.
+    \param[in]  tag         The message tag.
+    \param[in]  comm        The shared communicator.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Send(const void* buf, int count, tMPI_Datatype datatype, int dest,
+              int tag, tMPI_Comm comm);
+
+/** Receive message; blocks until buf is filled.
+
+    \param[out] buf         The buffer for data to receive.
+    \param[in]  count       The maximum number of items to receive.
+    \param[in]  datatype    The data type of the items in buf.
+    \param[in]  source      The rank of the source thread (or TMPI_ANY_SOURCE).
+    \param[in]  tag         The message tag (or TMPI_ANY_TAG).
+    \param[in]  comm        The shared communicator.
+    \param[out] status      The message status.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Recv(void* buf, int count, tMPI_Datatype datatype, int source,
+              int tag, tMPI_Comm comm, tMPI_Status *status);
+
+/** Send & receive message at the same time.
+
+    Blocks until recvbuf is filled, and sendbuf is ready for reuse.
+
+    \param[in]  sendbuf     The buffer with data to send.
+    \param[in]  sendcount   The number of items to send.
+    \param[in]  sendtype    The data type of the items in send buf.
+    \param[in]  dest        The rank of the destination thread.
+    \param[in]  sendtag     The send message tag.
+    \param[out] recvbuf     The buffer for data to receive.
+    \param[in]  recvcount   The maximum number of items to receive.
+    \param[in]  recvtype    The data type of the items in recvbuf.
+    \param[in]  source      The rank of the source thread (or TMPI_ANY_SOURCE).
+    \param[in]  recvtag     The recveive message tag (or TMPI_ANY_TAG).
+    \param[in]  comm        The shared communicator.
+    \param[out] status      The received message status.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Sendrecv(const void *sendbuf, int sendcount, tMPI_Datatype sendtype,
+                  int dest, int sendtag, void *recvbuf, int recvcount,
+                  tMPI_Datatype recvtype, int source, int recvtag,
+                  tMPI_Comm comm, tMPI_Status *status);
+
+/* async send/recv. The actual transfer is done on the receiving
+    end, during tMPI_Wait, tMPI_Waitall or tMPI_Test. For tMPI_Waitall,
+    the incoming messages are processed in the order they come in.  */
+
+/** Initiate sending a message, non-blocking.
+
+    This makes the buffer available to be received. The contents of buf
+    should not be touched before the transmission is finished with
+    tMPI_Wait(), tMPI_Test() or tMPI_Waitall().
+
+
+    \param[in]  buf         The buffer with data to send.
+    \param[in]  count       The number of items to send.
+    \param[in]  datatype    The data type of the items in buf.
+    \param[in]  dest        The rank of the destination thread.
+    \param[in]  tag         The message tag.
+    \param[in]  comm        The shared communicator.
+    \param[out] request     The request object that can be used in tMPI_Wait(),
+                            tMPI_Test, etc.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Isend(const void* buf, int count, tMPI_Datatype datatype, int dest,
+               int tag, tMPI_Comm comm, tMPI_Request *request);
+
+/** Initiate receiving a message.
+
+    This makes the buffer available to be filled with data. The contents of
+    buf should not be relied on before the transmission is finished with
+    tMPI_Wait(), tMPI_Test() or tMPI_Waitall().
+
+    \param[out] buf         The buffer for data to receive.
+    \param[in]  count       The maximum number of items to receive.
+    \param[in]  datatype    The data type of the items in buf.
+    \param[in]  source      The rank of the source thread (or TMPI_ANY_SOURCE).
+    \param[in]  tag         The message tag (or TMPI_ANY_TAG).
+    \param[in]  comm        The shared communicator.
+    \param[out] request     The request object that can be used in tMPI_Wait(),
+                            tMPI_Test, etc.
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Irecv(void* buf, int count, tMPI_Datatype datatype, int source,
+               int tag, tMPI_Comm comm, tMPI_Request *request);
+
+
+
+
+/** Test whether a message is transferred.
+
+    \param[in,out]  request The request obtained wit tMPI_Isend()/tMPI_Irecv().
+    \param[out]     flag    A flag set to TRUE(1) if the request is finished,
+                            FALSE(0) otherwise.
+    \param[out]     status  Message status (can be set to TMPI_STATUS_IGNORE).
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Test(tMPI_Request *request, int *flag, tMPI_Status *status);
+
+/** Wait until a message is transferred.
+
+    \param[in,out]  request The request obtained wit tMPI_Isend()/tMPI_Irecv().
+    \param[out]     status  Message status.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Wait(tMPI_Request *request, tMPI_Status *status);
+
+
+
+
+/** Wait until several messages are transferred.
+
+    \param[in]      count               The number of requests
+    \param[in,out]  array_of_requests   List of count requests obtained with
+                                        tMPI_Isend()/tMPI_Irecv().
+    \param[out]     array_of_statuses   List of count message statuses (can
+                                        be set to TMPI_STATUSES_IGNORE).
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Waitall(int count, tMPI_Request *array_of_requests,
+                 tMPI_Status *array_of_statuses);
+
+/** Test whether several messages are transferred.
+
+    \param[in]      count               The number of requests
+    \param[in,out]  array_of_requests   List of count requests obtained with
+                                        tMPI_Isend()/tMPI_Irecv().
+    \param[out]     flag                Whether all requests have completed.
+    \param[out]     array_of_statuses   List of count message statuses (can
+                                        be set to TMPI_STATUSES_IGNORE).
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Testall(int count, tMPI_Request *array_of_requests, int *flag,
+                 tMPI_Status *array_of_statuses);
+
+/** Wait until one of several messages is transferred.
+
+    \param[in]      count               The number of requests
+    \param[in,out]  array_of_requests   List of count requests obtained with
+                                        tMPI_Isend()/tMPI_Irecv().
+    \param[out]     index               Index of the request that has
+                                        completed.
+    \param[out]     status              Pointer to tMPI_Status object
+                                        associated with completed request.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Waitany(int count, tMPI_Request *array_of_requests,
+                 int *index, tMPI_Status *status);
+
+/** Test whether one of several messages is transferred.
+
+    \param[in]      count               The number of requests
+    \param[in,out]  array_of_requests   List of count requests obtained with
+                                        tMPI_Isend()/tMPI_Irecv().
+    \param[out]     index               Index of the request that has
+                                        completed.
+    \param[out]     flag                Whether any request has completed.
+    \param[out]     status              Pointer to tMPI_Status object
+                                        associated with completed request.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Testany(int count, tMPI_Request *array_of_requests,
+                 int *index, int *flag, tMPI_Status *status);
+
+/** Wait until some of several messages are transferred. Waits until at least
+    one message is transferred.
+
+    \param[in]      incount             The number of requests
+    \param[in,out]  array_of_requests   List of count requests obtained with
+                                        tMPI_Isend()/tMPI_Irecv().
+    \param[out]     outcount            Number of completed requests
+    \param[out]     array_of_indices    Array of ints that gets filled with
+                                        the indices of the completed requests.
+    \param[out]     array_of_statuses   List of count message statuses (can
+                                        be set to TMPI_STATUSES_IGNORE).
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Waitsome(int incount, tMPI_Request *array_of_requests,
+                  int *outcount, int *array_of_indices,
+                  tMPI_Status *array_of_statuses);
+
+/** Test whether some of several messages are transferred.
+
+    \param[in]      incount             The number of requests
+    \param[in,out]  array_of_requests   List of count requests obtained with
+                                        tMPI_Isend()/tMPI_Irecv().
+    \param[out]     outcount            Number of completed requests
+    \param[out]     array_of_indices    Array of ints that gets filled with
+                                        the indices of the completed requests.
+    \param[out]     array_of_statuses   List of count message statuses (can
+                                        be set to TMPI_STATUSES_IGNORE).
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Testsome(int incount, tMPI_Request *array_of_requests,
+                  int *outcount, int *array_of_indices,
+                  tMPI_Status *array_of_statuses);
+
+
+
+
+
+
+/** get the number of actually transferred items from a receive
+    status.
+
+    \param[in]  status      The status.
+    \param[in]  datatype    The data type which was received.
+    \param[out] count       The number of items actually received.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Get_count(tMPI_Status *status, tMPI_Datatype datatype, int *count);
+/*! \} */
+
+
+
+
+
+
+
+
+/*! \name Synchronization functions
+ \{ */
+/** Block until all threads in the comm call this function.
+
+    \param[in]  comm    The comm object.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Barrier(tMPI_Comm comm);
+/*! \} */
+
+
+
+
+
+
+
+/*! \name Multicast communication functions
+ \{ */
+/** Broadcast from one thread to all others in comm.
+
+    Collective function; data is transferred from root's buffer to all others'
+    buffer.
+
+    \param[in,out]  buffer      The buffer to send from (root)/receive from
+                                (other threads).
+    \param[in]      count       The number of items to send/receive.
+    \param[in]      datatype    The type of the items to send/receive.
+    \param[in]      root        The rank of the sending thread.
+    \param[in]      comm        The communicator.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Bcast(void* buffer, int count, tMPI_Datatype datatype, int root,
+               tMPI_Comm comm);
+
+/** Gather data from all threads in comm to root.
+
+    Collective function; assumes that all data is received in blocks of
+    recvcount.
+
+    \param[in]      sendbuf     The send buffer for all threads (root may
+                                specify TMPI_IN_PLACE, in which case it
+                                transfers nothing to itself).
+    \param[in]      sendcount   The number of items to send for all threads.
+    \param[in]      sendtype    The type of the items to send.
+    \param[out]     recvbuf     The receiving buffer (for root thread).
+    \param[in]      recvcount   The number of items to receive (for root).
+    \param[in]      recvtype    The type of the items to receive (for root).
+    \param[in]      root        The rank of root.
+    \param[in]      comm        The communicator.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Gather(const void* sendbuf, int sendcount, tMPI_Datatype sendtype,
+                void* recvbuf, int recvcount, tMPI_Datatype recvtype, int root,
+                tMPI_Comm comm);
+
+
+/** Gather irregularly laid out data from all processes in comm to root.
+
+    Collective function.
+
+    \param[in]      sendbuf     The send buffer for all threads (root may
+                                specify TMPI_IN_PLACE, in which case it
+                                transfers nothing to itself).
+    \param[in]      sendcount   The number of items to send for all threads.
+    \param[in]      sendtype    The type of the items to send.
+    \param[out]     recvbuf     The receiving buffer (for root thread).
+    \param[in]      recvcounts  The list of number of items to receive (for
+                                root).
+    \param[in]      displs      The list of displacements in recvbuf to
+                                receive data in (for root).
+    \param[in]      recvtype    The type of the items to receive (for root).
+    \param[in]      root        The rank of root.
+    \param[in]      comm        The communicator.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Gatherv(const void* sendbuf, int sendcount, tMPI_Datatype sendtype,
+                 void* recvbuf, int *recvcounts, int *displs,
+                 tMPI_Datatype recvtype, int root, tMPI_Comm comm);
+
+
+/** Spread parts of sendbuf to all processes in comm from root.
+
+    Collective function.
+
+    \param[in]      sendbuf     The send buffer for root.
+    \param[in]      sendcount   The number of items for root to send to each
+                                thread.
+    \param[in]      sendtype    The type of the items root sends.
+    \param[out]     recvbuf     The receiving buffer for all receiving threads
+                                (root may specify TMPI_IN_PLACE, in which case
+                                it transmits nothing to itself).
+    \param[in]      recvcount   The number of items recvbuf can receive.
+    \param[in]      recvtype    The type of items to receive.
+    \param[in]      root        The rank of root.
+    \param[in]      comm        The communicator.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Scatter(const void* sendbuf, int sendcount, tMPI_Datatype sendtype,
+                 void* recvbuf, int recvcount, tMPI_Datatype recvtype, int root,
+                 tMPI_Comm comm);
+
+/** Spread irregularly laid out parts of sendbuf to all processes
+            in comm from root.
+
+    Collective function.
+
+    \param[in]      sendbuf     The send buffer for root.
+    \param[in]      sendcounts  List of the number of items for root to send
+                                to each thread.
+    \param[in]      displs      List of displacements in sendbuf from which
+                                to start transmission to each thread.
+    \param[in]      sendtype    The type of the items root sends.
+    \param[out]     recvbuf     The receiving buffer for all receiving threads
+                                (root may specify TMPI_IN_PLACE, in which case
+                                it transmits nothing to itself).
+    \param[in]      recvcount   The number of items recvbuf can receive.
+    \param[in]      recvtype    The type of items to receive.
+    \param[in]      root        The rank of root.
+    \param[in]      comm        The communicator.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Scatterv(const void* sendbuf, int *sendcounts, int *displs,
+                  tMPI_Datatype sendtype, void* recvbuf, int recvcount,
+                  tMPI_Datatype recvtype, int root, tMPI_Comm comm);
+
+
+/** Spread out parts of sendbuf to all processes from all processes in
+           comm.
+
+    Collective function.
+
+    \param[in]      sendbuf     The send buffer.
+    \param[in]      sendcount   The number of items for to send to each thread.
+    \param[in]      sendtype    The type of the items to send.
+    \param[out]     recvbuf     The receive buffer for all threads.
+    \param[in]      recvcount   The number of items recvbuf can receive per
+                                thread.
+    \param[in]      recvtype    The type of items to receive.
+    \param[in]      comm        The communicator.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Alltoall(void* sendbuf, int sendcount, tMPI_Datatype sendtype,
+                  void* recvbuf, int recvcount, tMPI_Datatype recvtype,
+                  tMPI_Comm comm);
+
+
+/** Spread out irregularly laid out parts of sendbuf to all
+           processes from all processes in comm.
+
+    Collective function.
+
+    \param[in]      sendbuf     The send buffer.
+    \param[in]      sendcounts  List of the number of items for to send to
+                                each thread.
+    \param[in]      sdispls     List of the displacements in sendbuf of items
+                                to send to each thread.
+    \param[in]      sendtype    The type of the items to send.
+    \param[out]     recvbuf     The receive buffer for all threads.
+    \param[in]      recvcounts  List of the number of items recvbuf can
+                                receive from each thread.
+    \param[in]      rdispls     List of the displacements in recvbuf of items
+                                to receive from each thread.
+    \param[in]      recvtype    The type of items to receive.
+    \param[in]      comm        The communicator.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Alltoallv(void* sendbuf, int *sendcounts, int *sdispls,
+                   tMPI_Datatype sendtype, void* recvbuf, int *recvcounts,
+                   int *rdispls, tMPI_Datatype recvtype, tMPI_Comm comm);
+
+/*! \} */
+
+
+
+
+
+
+
+
+
+/*! \name Reduce functions
+ \{ */
+/** Do an operation between all locally held buffers on all items in the
+    buffers, and send the results to root.
+
+    Collective function.
+
+    \param[in]  sendbuf     The operand parameters. Root may specify
+                            TMPI_IN_PLACE, in which case recvbuf will hold
+                            the operand parameters.
+    \param[out] recvbuf     The result buffer at root.
+    \param[in]  count       The number of items to do operation on.
+    \param[in]  datatype    The data type of the items.
+    \param[in]  op          The operation to perform.
+    \param[in]  root        The root thread (which is to receive the results).
+    \param[in]  comm        The communicator.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Reduce(void* sendbuf, void* recvbuf, int count,
+                tMPI_Datatype datatype, tMPI_Op op, int root, tMPI_Comm comm);
+
+
+
+/** Do an operation between all locally held buffers on all items in the
+    buffers and broadcast the results.
+
+    Collective function.
+
+
+    \param[in]  sendbuf     The operand parameters. Any process may specify
+                            TMPI_IN_PLACE, in which case recvbuf will hold
+                            the operand parameters for that process.
+    \param[in,out] recvbuf  The result buffer.
+    \param[in]  count       The number of items to do operation on.
+    \param[in]  datatype    The data type of the items.
+    \param[in]  op          The operation to perform.
+    \param[in]  comm        The communicator.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Allreduce(void* sendbuf, void* recvbuf, int count,
+                   tMPI_Datatype datatype, tMPI_Op op, tMPI_Comm comm);
+
+/** Do an tMPI_Reduce, but with the following assumption:
+    recvbuf points to a valid buffer in all calling threads, or
+    sendbuf has the value TMPI_IN_PLACE (in which case the values of
+    sendbuf may be changed in that thread).
+
+    This avoids unnecesary memory allocations associated with the normal
+    tMPI_Reduce.
+
+    Collective function.
+
+    \param[in]      sendbuf     The operand parameters (or TMPI_IN_PLACE,
+                                in which case the operand parameters will
+                                be in recvbuf).
+    \param[in,out]  recvbuf     The result buffer.
+    \param[in]      count       The number of items to do operation on.
+    \param[in]      datatype    The data type of the items.
+    \param[in]      op          The operation to perform.
+    \param[in]      root        The root thread (which is to receive
+                                the final results).
+    \param[in]      comm        The communicator.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Reduce_fast(void* sendbuf, void* recvbuf, int count,
+                     tMPI_Datatype datatype, tMPI_Op op, int root,
+                     tMPI_Comm comm);
+
+/** Do a partial reduce operation, based on rank: the results of the
+    reduction operation of ranks 0 - i will be put in the recvbuf of
+    rank i.
+
+    Collective function.
+
+    \param[in]     sendbuf     The operand parameters. All ranks may specify
+                               TMPI_IN_PLACE, in which case recvbuf will hold
+                               the operand parameters.
+    \param[in,out] recvbuf     The result buffer.
+    \param[in]     count       The number of items to do operation on.
+    \param[in]     datatype    The data type of the items.
+    \param[in]     op          The operation to perform.
+    \param[in]     comm        The communicator.
+
+    \return  TMPI_SUCCESS on success, TMPI_FAILURE on failure.  */
+TMPI_EXPORT
+int tMPI_Scan(void* sendbuf, void* recvbuf, int count,
+              tMPI_Datatype datatype, tMPI_Op op, tMPI_Comm comm);
+
+
+/*! \} */
+
+
+
+#endif /* TMPI_TMPI_H_ */
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/visibility.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/visibility.h
new file mode 100644 (file)
index 0000000..1cffaaa
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+#ifndef TMPI_VISIBILITY_H_
+#define TMPI_VISIBILITY_H_
+
+/** \file
+ *
+ * \brief Visibility macros
+ *
+ * These macros enable dynamic library visibility support. Either set the
+ * 'TMPI_USE_VISIBILITY', or set the TMPI_EXPORT to the right export
+ * statement, when incorporating thread_mpi into a larger project.
+ *
+ * All exported functions and classes will be tagged by the visibility macro.
+ *
+ * \sa http://gcc.gnu.org/wiki/Visibility
+ *
+ */
+
+#ifdef TMPI_USE_VISIBILITY  /* off by default */
+
+/* only set if the macro hasn't been set elsewhere */
+#ifndef TMPI_EXPORT
+
+/* gcc-like */
+#if defined(__GNUC__)
+
+#define TMPI_EXPORT __attribute__((__visibility__("default")))
+
+#elif defined _WIN32 || defined __CYGWIN__ || defined WINDOWS
+
+#ifdef TMPI_EXPORTS
+#define TMPI_EXPORT __declspec(dllexport)
+#else
+#define TMPI_EXPORT __declspec(dllimport)
+#endif
+
+#else /* no viable visibility */
+
+#define TMPI_EXPORT
+
+#endif /* compiler check */
+
+#endif /* TMPI_EXPORT */
+
+#else  /* TMPI_USE_VISIBILITY */
+
+#define TMPI_EXPORT
+
+#endif /* TMPI_USE_VISIBILITY */
+
+#endif /* TMPI_VISIBILITY_H_ */
diff --git a/src/include/gromacs/external/thread_mpi/include/thread_mpi/wait.h b/src/include/gromacs/external/thread_mpi/include/thread_mpi/wait.h
new file mode 100644 (file)
index 0000000..ae29422
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+#ifndef TMPI_WAIT_H_
+#define TMPI_WAIT_H_
+
+#if TMPI_WAIT_FOR_NO_ONE
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#if GMX_FAHCORE
+// This lets F@H throttle CPU usage
+#define TMPI_YIELD_WAIT_DATA
+#define TMPI_YIELD_WAIT_DATA_INIT(data)
+#define TMPI_YIELD_WAIT(data) fcYieldWait()
+
+#elif !(defined( _WIN32 ) || defined( _WIN64 ) )
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SCHED_H
+#include <sched.h>
+#endif
+
+/* for now we just do sched_yield(). It's in POSIX. */
+/* the data associated with waiting. */
+#define TMPI_YIELD_WAIT_DATA
+/* the initialization  associated with waiting. */
+#define TMPI_YIELD_WAIT_DATA_INIT(data)
+
+/* the waiting macro */
+#define TMPI_YIELD_WAIT(data)  sched_yield()
+
+#else
+/* and in Windows, we do SwitchToThread() alternated with Sleep(0). This
+   is apparently recommende practice (SwitchToThread() alone just gives
+   up the slice for threads on the current core, and Sleep(0) alone could
+   lead to starvation. This mixed approach actually gives better real-world
+   performance in the test program.*/
+/* the data associated with waiting. */
+#define TMPI_YIELD_WAIT_DATA  int yield_wait_counter;
+/* the initialization  associated with waiting. */
+#define TMPI_YIELD_WAIT_DATA_INIT(data) { (data)->yield_wait_counter = 0; }
+
+/* the waiting macro is so complicated because using SwitchToThread only schedules */
+#define TMPI_YIELD_WAIT(data)  { \
+        if ( ((data)->yield_wait_counter++)%100 == 0) \
+        { \
+            SwitchToThread(); \
+        } \
+        else \
+        { \
+            Sleep(0); \
+        } \
+}
+
+#endif
+
+
+#else /* !TMPI_WAIT_FOR_NO_ONE */
+
+/* the data associated with waiting. */
+#define TMPI_YIELD_WAIT_DATA
+/* the initialization  associated with waiting. */
+#define TMPI_YIELD_WAIT_DATA_INIT(data)
+
+/* the waiting macro */
+#define TMPI_YIELD_WAIT(data)  tMPI_Atomic_memory_barrier()
+
+
+#endif /* !TMPI_WAIT_FOR_NO_ONE */
+
+#endif /* TMPI_WAIT_H_ */
diff --git a/src/include/gromacs/external/thread_mpi/include/tmpi.h b/src/include/gromacs/external/thread_mpi/include/tmpi.h
new file mode 100644 (file)
index 0000000..57bc368
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/*!
+   \if THREAD_MPI_MAIN
+   \mainpage thread_mpi thread_mpi threading library
+   \else
+   \page thread_mpi thread_mpi threading library
+   \endif
+
+   thread_mpi is a cross-platform threading library for applications in
+   high-performance computing. It supports:
+
+   - Cross-platform thread primitives (thread creation, mutexes, spinlocks,
+     barriers, thread-local storage, etc.).
+   - Cross-platform atomic operations (compare-and-swap, add-return, etc) for
+     safe lock-free synchronization.
+   - An implementation of (currently, much of) MPI, either as a drop-in
+     replacement, or for use in conjunction with a networked MPI
+     implementation.
+   - Shared-memory allocation and memory management (planned, as of now).
+   - Basic lock-free data structures (planned, as of now).
+
+   Because it can be used as a drop-in replacement for MPI, existing codes
+   using MPI can start using thread_mpi without major changes in the
+   source code, assuming -- and this is a big assumption -- that the code
+   is thread-safe.
+
+   Alternatively, networked MPI calls can be used in conjunction with
+   thread_mpi calls (simply by using
+    "#include <thread_mpi.h>"
+   instead of
+    "#include <tmpi.h>"
+   and pre-fixing all thread_mpi MPI-like calls with tMPI instead of MPI.
+
+   The availability of both MPI calls and shared-memory constructs makes it
+   possible to transition (relatively) seamlessly from an MPI-style code
+   to code that's optimal on multicore CPUs.
+
+   Although MPI-style message passing isn't neccesarily optimal for
+   performance on shared-memory systems, the MPI communicator concept and
+   its emphasis on collective operations makes sense even when computing on
+   one machine with multiple cores. The communicator forms the basis for
+   the shared-memory allocation and lock-free data structure implementations
+   in thread_mpi.
+
+   Although usable as a stand-alone library, thread_mpi is designed to
+   be incorporated in the code tree, eliminating any external build
+   requirements. The BSD-style license that this library is distributed
+   with reflects this.
+
+   Thread primitives and the atomic operations are cpu and operating system
+   dependent - thread_mpi attempts to make them available with the same
+   interface independently of the platform it's run on.
+   Currently the thread primitives are supported on:
+   - any operating system supporting POSIX threads
+   - Windows (XP and later).
+
+   The atomic operations (such as compare-and-swap) are supported on:
+   - gcc on x86, x86_64, PowerPC and Itanium.
+   - Intel compilers on x86, x86_64 and Itanium.
+   - xlc on PowerPC.
+   - (partial) HP/UX compilers on Itanium.
+
+   Detailed descriptions of the parts of the API can be found in:
+   - thread_mpi/threads.h for threading fundamentals.
+   - thread_mpi/atomic.h for atomic operations.
+   - thread_mpi/tmpi.h for the MPI functions as tMPI_-prefixed functions.
+   - thread_mpi/mpi_bindings.h for the MPI bindings.
+ */
+
+
+/** \file
+ *
+ * \brief Convenience header file for MPI compatibility.
+ *
+ * This file includes the tMPI header file thread_mpi/tmpi.h and the true
+ * MPI-style bindings of thread_mpi/mpi.h, as well as thread_mpi/threads.h and
+ * thread_mpi/atomic.h header files. If you'd like to use the components
+ * individually, or be able to use a networked MPI together with thread_mpi,
+ * include the relevant header files directly.
+ */
+
+
+#include "thread_mpi/atomic.h"
+#include "thread_mpi/numa_malloc.h"
+#include "thread_mpi/threads.h"
+#include "thread_mpi/barrier.h"
+#include "thread_mpi/event.h"
+#include "thread_mpi/lock.h"
+#include "thread_mpi/tmpi.h"
+#include "thread_mpi/collective.h"
+
+#include "thread_mpi/mpi_bindings.h"
diff --git a/src/include/gromacs/external/thread_mpi/src/collective.h b/src/include/gromacs/external/thread_mpi/src/collective.h
new file mode 100644 (file)
index 0000000..162fda6
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/* get a pointer the next coll_env once it's ready */
+struct coll_env *tMPI_Get_cev(tMPI_Comm comm, int myrank, int *synct);
+
+/* post the availability of data in a cev.
+   cev         = the collective comm environment
+   myrank      = my rank
+   index       = the buffer index
+   tag         = the tag
+   datatype    = the datatype
+   busize      = the buffer size
+   buf         = the buffer to xfer
+   n_remaining = the number of remaining threads that need to transfer
+   synct       = the multicast sync number
+   dest        = -1 for all theads, or a specific rank number.
+ */
+int tMPI_Post_multi(struct coll_env *cev, int myrank, int index,
+                    int tag, tMPI_Datatype datatype,
+                    size_t bufsize, void *buf, int n_remaining,
+                    int synct, int dest);
+
+/* transfer data from cev->met[rank] to recvbuf */
+void tMPI_Mult_recv(tMPI_Comm comm, struct coll_env *cev, int rank,
+                    int index, int expected_tag, tMPI_Datatype recvtype,
+                    size_t recvsize, void *recvbuf, int *ret);
+
+/* do a root transfer (from root send buffer to root recv buffer) */
+void tMPI_Coll_root_xfer(tMPI_Comm comm,
+                         tMPI_Datatype sendtype, tMPI_Datatype recvtype,
+                         size_t sendsize, size_t recvsize,
+                         void* sendbuf, void* recvbuf, int *ret);
+
+/* wait for other processes to copy data from my cev */
+void tMPI_Wait_for_others(struct coll_env *cev, int myrank);
+/* wait for data to become available from a specific rank */
+void tMPI_Wait_for_data(struct tmpi_thread *cur, struct coll_env *cev,
+                        int myrank);
+/*int rank, int myrank, int synct);*/
diff --git a/src/include/gromacs/external/thread_mpi/src/impl.h b/src/include/gromacs/external/thread_mpi/src/impl.h
new file mode 100644 (file)
index 0000000..ebe60c2
--- /dev/null
@@ -0,0 +1,833 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009,2018, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/* this is the header file for the implementation side of the thread_mpi
+   library. It contains the definitions for all the internal data structures
+   and the prototypes for all the internal functions that aren't static.  */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if defined( _WIN32 ) || defined( _WIN64 )
+#include <windows.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+#include "settings.h"
+#include "thread_mpi/atomic.h"
+#include "thread_mpi/threads.h"
+#include "thread_mpi/event.h"
+#include "thread_mpi/tmpi.h"
+#include "thread_mpi/collective.h"
+#include "thread_mpi/barrier.h"
+#include "thread_mpi/lock.h"
+#ifdef TMPI_PROFILE
+#include "profile.h"
+#endif
+
+
+
+/**************************************************************************
+
+   BASIC DEFINITIONS
+
+ **************************************************************************/
+
+
+typedef int tmpi_bool;
+#define TRUE 1
+#define FALSE 0
+
+
+
+#ifdef USE_COLLECTIVE_COPY_BUFFER
+/**************************************************************************
+
+   PRE-ALLOCATED COMMUNICATION BUFFERS
+
+ **************************************************************************/
+
+
+/* Buffer structure for collective communications. Every thread structure
+   has several of these ready to be used when the collective data
+   transmission is small enough for double copying to occur (i.e. the size
+   of the transmission is less than N*MAX_COPY_BUFFER_SIZE, where N is the
+   number of receiving threads).  */
+struct copy_buffer
+{
+    void               *buf;  /* the actual buffer */
+    struct copy_buffer *next; /* pointer to next free buffer in buffer_list */
+    size_t              size; /* allocated size of buffer */
+};
+
+/* a list of copy_buffers of a specific size. */
+struct copy_buffer_list
+{
+    struct copy_buffer *cb;       /* pointer to the first copy_buffer */
+    size_t              size;     /* allocated size of buffers in this list */
+    struct copy_buffer *cb_alloc; /* list as allocated */
+    int                 Nbufs;    /* number of allocated buffers */
+};
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************************
+
+   POINT-TO-POINT COMMUNICATION DATA STRUCTURES
+
+ **************************************************************************/
+
+/* the message envelopes (as described in the MPI standard).
+   These fully describes the message, and make each message unique (enough).
+
+   Transmitting data works by having the sender put a pointer to an envelope
+   onto the receiver's new envelope list corresponding to the originating
+   thread.
+   The sender then waits until the receiver finishes the transmission, while
+   matching all incoming new envelopes against its own list of receive
+   envelopes.
+
+   The receiver either directly matches its receiving envelope against
+   all previously un-matched sending envelopes, or, if no suitable envelope
+   is found, it puts the receive envelope on a receive list.
+   Once waiting for completion, the receiver matches against all incoming
+   new envelopes.  */
+
+/* the state of an individual point-to-point transmission */
+enum envelope_state
+{
+    env_unmatched       = 0, /* the envelope has not had a match yet */
+    env_copying         = 1, /* busy copying (only used for send envelope
+                                by receiver if using_cpbuf is true,
+                                but cb was still NULL).  */
+    env_cb_available    = 2, /* the copy buffer is available. Set by
+                                the sender on a send_buffer. */
+    env_finished        = 3  /* the transmission has finished */
+};
+
+
+/* the envelope. Held in tmpi_thread->evs[src_thread] for send envelopes,
+   or in tmpi_thread->evl for receive envelopes */
+struct envelope
+{
+    int       tag;                  /* the tag */
+    tMPI_Comm comm;                 /* this is a structure shared across threads, so we
+                                       can test easily whether two threads are talking
+                                       about the same comm. */
+
+    struct tmpi_thread *src, *dest; /* these are pretty obvious */
+
+    void               *buf;        /* buffer to be sent  */
+    size_t              bufsize;    /* the size of the data to be transmitted */
+    tMPI_Datatype       datatype;   /* the data type */
+
+    tmpi_bool           nonblock;   /* whether the receiver is non-blocking */
+
+    /* state, values from enum_envelope_state .
+       (there's a few busy-waits relying on this flag).
+       status=env_unmatched  is the initial state.*/
+    tMPI_Atomic_t state;
+
+    /* the error condition */
+    int error;
+
+    /* the message status */
+    /*tMPI_Status *status;*/
+
+    /* prev and next envelopes in the send/recv_envelope_list linked list  */
+    struct envelope *prev, *next;
+
+    tmpi_bool        send; /* whether this is a send envelope (if TRUE), or a receive
+                              envelope (if FALSE) */
+#ifdef USE_SEND_RECV_COPY_BUFFER
+    tmpi_bool using_cb;    /* whether a copy buffer is (going to be) used */
+    void    * cb;          /* the allocated copy buffer pointer */
+#endif
+    /* the next and previous envelopes in the request list */
+    struct envelope *prev_req, *next_req;
+
+    /* the list I'm in */
+    struct recv_envelope_list *rlist;
+    struct send_envelope_list *slist;
+};
+
+
+
+/* singly linked lists of free send & receive envelopes belonging to a
+   thread. */
+struct free_envelope_list
+{
+    struct envelope *head_recv;       /* the first element in the linked list */
+    struct envelope *recv_alloc_head; /* the allocated recv list */
+};
+
+/* collection of send envelopes to a specific thread */
+struct send_envelope_list
+{
+    struct envelope *head_free;  /* singly linked list with free send
+                                    envelopes. A single-thread LIFO.*/
+#ifdef TMPI_LOCK_FREE_LISTS
+    tMPI_Atomic_ptr_t head_new;  /* singly linked list with the new send
+                                    envelopes (i.e. those that are put there by
+                                    the sending thread, but not yet checked by
+                                    the receiving thread). This is a lock-free
+                                    shared detachable list.*/
+    tMPI_Atomic_ptr_t head_rts;  /* singly linked list with free send
+                                    envelopes returned by the other thread.
+                                    This is a lock-free shared LIFO.*/
+#else
+    struct envelope *head_new;   /* singly linked list with the new send
+                                    envelopes (i.e. those that are put there by
+                                    the sending thread, but not yet checked by
+                                    the receiving thread). */
+    struct envelope *head_rts;   /* singly linked list with free send envelopes */
+    tMPI_Spinlock_t  lock_new;   /* this locks head_new */
+    tMPI_Spinlock_t  lock_rts;   /* this locks head_rts */
+#endif
+    struct envelope *head_old;   /* the old send envelopes, in a circular doubly
+                                    linked list. These have been checked by the
+                                    receiving thread against the existing
+                                    recv_envelope_list. */
+
+    struct envelope *alloc_head; /* the allocated send list */
+    size_t           Nalloc;     /* number of allocted sends */
+};
+
+struct recv_envelope_list
+{
+    struct envelope *head;  /* first envelope in this list */
+    struct envelope  dummy; /* the dummy element for the list */
+};
+
+
+/* the request object for asynchronious operations. */
+struct tmpi_req_
+{
+    tmpi_bool           finished;    /* whether it's finished */
+    struct envelope    *ev;          /* the envelope */
+
+    struct tmpi_thread *source;      /* the message source (for receives) */
+    tMPI_Comm           comm;        /* the comm */
+    int                 tag;         /* the tag */
+    int                 error;       /* error code */
+    size_t              transferred; /* the number of transferred bytes */
+    tmpi_bool           cancelled;   /* whether the transmission was canceled */
+
+    struct tmpi_req_   *next, *prev; /* next,prev request in linked list,
+                                        used in the req_list, but also in
+                                        tMPI_Test_mult().  */
+};
+
+/* pre-allocated  request object list */
+struct req_list
+{
+    struct tmpi_req_ *head;       /* pre-allocated singly linked list of requests.
+                                     (i.e. reqs->prev is undefined). */
+    struct tmpi_req_ *alloc_head; /* the allocated block */
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************************
+
+   MULTICAST COMMUNICATION DATA STRUCTURES
+
+ **************************************************************************/
+
+/* these are data structures meant for keeping track of multicast operations
+   (tMPI_Bcast, tMPI_Gather, etc.). Because these operations are all collective
+   across the comm, and are always blocking, the protocol can be much simpler
+   than that for point-to-point communication through tMPI_Send/Recv, etc. */
+
+/* unique tags for multicast & collective operations */
+#define TMPI_BCAST_TAG      1
+#define TMPI_GATHER_TAG     2
+#define TMPI_GATHERV_TAG    3
+#define TMPI_SCATTER_TAG    4
+#define TMPI_SCATTERV_TAG   5
+#define TMPI_REDUCE_TAG     6
+#define TMPI_ALLTOALL_TAG   7
+#define TMPI_ALLTOALLV_TAG  8
+
+
+/* thread-specific part of the coll_env */
+struct coll_env_thread
+{
+    tMPI_Atomic_t current_sync; /* sync counter value for the current
+                                   communication */
+    tMPI_Atomic_t n_remaining;  /* remaining threads count for each thread */
+
+    int           tag;          /* collective communication type */
+    tMPI_Datatype datatype;     /* datatype */
+
+    void        **buf;          /* array of send/recv buffer values */
+    size_t       *bufsize;      /* array of number of bytes to send/recv */
+
+#ifdef USE_COLLECTIVE_COPY_BUFFER
+    tmpi_bool     using_cb;      /* whether a copy buffer is (going to be) used */
+    tMPI_Atomic_t buf_readcount; /* Number of threads reading from buf
+                                    while using_cpbuf is true, but cpbuf
+                                    is still NULL.  */
+    tMPI_Atomic_ptr_t  *cpbuf;   /* copy_buffer pointers. */
+    struct copy_buffer *cb;      /* the copy buffer cpbuf points to */
+#endif
+
+    tMPI_Event send_ev;   /* event associated with being the sending thread.
+                             Triggered when last receiving thread is ready,
+                             and the coll_env_thread is ready for re-use. */
+    tMPI_Event recv_ev;   /* event associated with being a receiving thread. */
+
+    tmpi_bool *read_data; /* whether we read data from a specific thread. */
+};
+
+/* Collective communications once sync. These run in parallel with
+   the collection of coll_env_threads*/
+struct coll_env_coll
+{
+    /* collective sync data */
+    tMPI_Atomic_t current_sync; /* sync counter value for the current
+                                   communication */
+    tMPI_Atomic_t n_remaining;  /* remaining threads count */
+
+    void         *res;          /* result data for once calls. */
+};
+
+/* the collective communication envelope. There's a few of these per
+   comm, and each one stands for one collective communication call.  */
+struct coll_env
+{
+    struct coll_env_thread *met; /* thread-specific collective envelope data.*/
+
+    struct coll_env_coll    coll;
+    int                     N;
+};
+
+/* multicast synchronization data structure. There's one of these for
+   each thread in each tMPI_Comm structure */
+struct coll_sync
+{
+    int         synct;  /* sync counter for coll_env_thread.  */
+    int         syncs;  /* sync counter for coll_env_coll.  */
+
+    tMPI_Event *events; /* One event for each other thread */
+    int         N;      /* the number of threads */
+};
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************************
+
+   THREAD DATA STRUCTURES
+
+ **************************************************************************/
+
+/* information about a running thread. This structure is put in a
+   globally available array; the envelope exchange, etc. are all done through
+   the elements of this array.*/
+struct tmpi_thread
+{
+    tMPI_Thread_t thread_id; /* this thread's id */
+
+    /* p2p communication structures: */
+
+    /* the receive envelopes posted for other threads to check */
+    struct recv_envelope_list  evr;
+    /* the send envelopes posted by other threadas */
+    struct send_envelope_list *evs;
+    /* free send and receive envelopes */
+    struct free_envelope_list  envelopes;
+    /* number of finished send envelopes */
+    tMPI_Atomic_t              ev_outgoing_received;
+    /* the p2p communication events (incoming envelopes + finished send
+       envelopes generate events) */
+    tMPI_Event      p2p_event;
+    TMPI_YIELD_WAIT_DATA /* data associated with waiting */
+    struct req_list rql; /* list of pre-allocated requests */
+
+    /* collective communication structures: */
+#ifdef USE_COLLECTIVE_COPY_BUFFER
+    /* copy buffer list for multicast communications */
+    struct copy_buffer_list cbl_multi;
+#endif
+
+    /* miscellaneous data: */
+
+    tMPI_Comm self_comm; /* comms for MPI_COMM_SELF */
+#ifdef TMPI_PROFILE
+    /* the per-thread profile structure that keeps call counts & wait times. */
+    struct tmpi_profile profile;
+#endif
+    /* The start function (or NULL, if a main()-style start function is to
+       be called) */
+    void  (*start_fn)(const void*);
+    /* The main()-style start function */
+    int   (*start_fn_main)(int, char**);
+    /* the argument to the start function, if it's not main()*/
+    const void *start_arg;
+
+    /* we copy these for each thread (providing these to main() is not
+       required by the MPI standard, but it's convenient). Note that we copy,
+       because some programs (like Gromacs) like to manipulate these. */
+    int    argc;
+    char **argv;
+};
+
+
+
+
+
+
+/**************************************************************************
+
+   ERROR HANDLER DATA STRUCTURES
+
+ **************************************************************************/
+
+
+/* the error handler  */
+struct tmpi_errhandler_
+{
+    int                err;
+    tMPI_Errhandler_fn fn;
+};
+
+/* standard error handler functions */
+void tmpi_errors_are_fatal_fn(tMPI_Comm *comm, int *err);
+void tmpi_errors_return_fn(tMPI_Comm *comm, int *err);
+
+
+
+
+
+/**************************************************************************
+
+   GLOBAL DATA STRUCTURE
+
+ **************************************************************************/
+
+/* global MPI information */
+struct tmpi_global
+{
+    /* list of pointers to all user-defined types */
+    struct tmpi_datatype_ **usertypes;
+    int                     N_usertypes;
+    int                     Nalloc_usertypes;
+
+    /* spinlock/mutex for manipulating tmpi_user_types */
+    tMPI_Spinlock_t datatype_lock;
+
+    /* Lock to prevent multiple threads manipulating the linked list of comm
+       structures.*/
+    tMPI_Thread_mutex_t comm_link_lock;
+
+    /* barrier for tMPI_Finalize(), etc. */
+    tMPI_Thread_barrier_t barrier;
+
+    /* the timer for tMPI_Wtime() */
+    tMPI_Thread_mutex_t timer_mutex;
+#if !(defined( _WIN32 ) || defined( _WIN64 ) )
+    /* the time at initialization. */
+    struct timeval timer_init;
+#else
+    /* the time at initialization. */
+    DWORD timer_init;
+#endif
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**************************************************************************
+
+   COMMUNICATOR DATA STRUCTURES
+
+ **************************************************************************/
+
+
+struct tmpi_group_
+{
+    int                  N;     /* the number of threads */
+    struct tmpi_thread **peers; /* the list of peers to communicate with */
+#if 0
+    int                  Nrefs; /* the number of references to this structure */
+#endif
+};
+
+
+/* the communicator objects are globally shared. */
+struct tmpi_comm_
+{
+    struct tmpi_group_ grp; /* the communicator group */
+
+    /* the barrier for tMPI_Barrier() */
+    tMPI_Barrier_t barrier;
+
+
+    /* List of barriers for reduce operations.
+       reduce_barrier[0] contains a list of N/2 barriers for N threads
+       reduce_barrier[1] contains a list of N/4 barriers for N/2 threads
+       reduce_barrier[2] contains a list of N/8 barriers for N/4 threads
+       and so on. (until N/x reaches 1)
+       This is to facilitate tree-based algorithms for tMPI_Reduce, etc.  */
+    tMPI_Barrier_t **reduce_barrier;
+    int             *N_reduce;      /* the number of barriers in each iteration */
+    int              N_reduce_iter; /* the number of iterations */
+
+
+    struct coll_env  *cev;   /* list of multicast envelope objecs */
+    struct coll_sync *csync; /* list of multicast sync objecs */
+
+    /* lists of globally shared send/receive buffers for tMPI_Reduce. */
+    tMPI_Atomic_ptr_t *reduce_sendbuf, *reduce_recvbuf;
+
+    /* mutex for communication object creation. Traditional mutexes are
+       better here because communicator creation should not be done in
+       time-critical sections of code.   */
+    tMPI_Thread_mutex_t comm_create_lock;
+    tMPI_Thread_cond_t  comm_create_prep;
+    tMPI_Thread_cond_t  comm_create_finish;
+
+    tMPI_Comm          *new_comm; /* newly created communicators */
+
+    /* the split structure is shared among the comm threads and is
+       allocated & deallocated during tMPI_Comm_split */
+    struct tmpi_split *split;
+
+    /* the topologies (only cartesian topology is currently implemented */
+    struct cart_topol *cart;
+    /*struct tmpi_graph_topol_ *graph;*/
+
+    tMPI_Errhandler erh;
+
+    /* links for a global circular list of all comms that starts at
+       TMPI_COMM_WORLD. Used to de-allocate the comm structures after
+       tMPI_Finalize(). */
+    struct tmpi_comm_ *next, *prev;
+
+    /* A counter that counts up to N before the comm is freed. */
+    tMPI_Atomic_t destroy_counter;
+};
+
+
+
+/* specific for tMPI_Split: */
+struct tmpi_split
+{
+    volatile int       Ncol_init;
+    volatile int       Ncol_destroy;
+    volatile tmpi_bool can_finish;
+    volatile int      *colors;
+    volatile int      *keys;
+};
+
+/* cartesian topology */
+struct cart_topol
+{
+    int  ndims;   /* number of dimensions */
+    int *dims;    /* procs per coordinate */
+    int *periods; /* whether the grid is periodic, per dimension */
+};
+
+#if 0
+/* graph topology */
+struct tmpi_graph_topol_
+{
+};
+#endif
+
+
+
+
+
+/**************************************************************************
+
+   DATA TYPE DATA STRUCTURES
+
+ **************************************************************************/
+
+/* tMPI_Reduce Op functions */
+typedef void (*tMPI_Op_fn)(void*, void*, void*, int);
+
+
+struct tmpi_datatype_component
+{
+    struct tmpi_datatype_ *type;
+    unsigned int           count;
+};
+
+/* we don't support datatypes with holes (yet)  */
+struct tmpi_datatype_
+{
+    size_t                          size;         /* full extent of type. */
+    tMPI_Op_fn                     *op_functions; /* array of op functions for this datatype */
+    int                             N_comp;       /* number of components */
+    struct tmpi_datatype_component *comps;        /* the components */
+    tmpi_bool                       committed;    /* whether the data type is committed */
+};
+/* just as a shorthand:  */
+typedef struct tmpi_datatype_ tmpi_dt;
+
+
+
+
+
+
+
+
+/**************************************************************************
+
+   GLOBAL VARIABLES
+
+ **************************************************************************/
+
+
+/* the threads themselves (tmpi_comm only contains lists of pointers to this
+         structure */
+extern struct tmpi_thread *threads;
+extern int                 Nthreads;
+
+/* thread info */
+extern tMPI_Thread_key_t id_key; /* the key to get the thread id */
+
+/* misc. global information about MPI */
+extern struct tmpi_global *tmpi_global;
+
+
+
+
+
+
+
+
+/**************************************************************************
+
+   FUNCTION PROTOTYPES & MACROS
+
+ **************************************************************************/
+
+#ifdef TMPI_TRACE
+void tMPI_Trace_print(const char *fmt, ...);
+#endif
+
+/* error-checking malloc/realloc: */
+void *tMPI_Malloc(size_t size);
+void *tMPI_Realloc(void *p, size_t size);
+void tMPI_Free(void *p);
+
+
+/* get the current thread structure pointer */
+#define tMPI_Get_current() ((struct tmpi_thread*) \
+                            tMPI_Thread_getspecific(id_key))
+
+/* get the number of this thread */
+/*#define tMPI_This_threadnr() (tMPI_Get_current() - threads)*/
+
+/* get the number of a specific thread. We convert to the resulting size_t to
+   int, which is unlikely to cause problems in the foreseeable future. */
+#define tMPI_Threadnr(th) (int)(th - threads)
+
+/* get thread associated with rank  */
+#define tMPI_Get_thread(comm, rank) (comm->grp.peers[rank])
+
+
+#if 0
+/* get the current thread structure pointer */
+struct tmpi_thread *tMPI_Get_current(void);
+/* get the thread belonging to comm with rank rank */
+struct tmpi_thread *tMPI_Get_thread(tMPI_Comm comm, int rank);
+
+#endif
+
+/* handle an error, returning the errorcode */
+int tMPI_Error(tMPI_Comm comm, int tmpi_errno);
+
+
+
+/* check whether we're the main thread */
+tmpi_bool tMPI_Is_master(void);
+/* check whether the current process is in a group */
+tmpi_bool tMPI_In_group(tMPI_Group group);
+
+/* find the rank of a thread in a comm */
+int tMPI_Comm_seek_rank(tMPI_Comm comm, struct tmpi_thread *th);
+/* find the size of a comm */
+int tMPI_Comm_N(tMPI_Comm comm);
+
+/* allocate a comm object, making space for N threads */
+int tMPI_Comm_alloc(tMPI_Comm *newcomm, tMPI_Comm parent, int N);
+/* de-allocate a comm object */
+int tMPI_Comm_destroy(tMPI_Comm comm, tmpi_bool do_link_lock);
+/* allocate a group object */
+tMPI_Group tMPI_Group_alloc(void);
+
+/* topology functions */
+/* de-allocate a cartesian topology structure. (it is allocated with
+   the internal function tMPI_Cart_init()) */
+void tMPI_Cart_destroy(struct cart_topol *top);
+
+
+
+
+
+
+/* initialize a free envelope list with N envelopes */
+int tMPI_Free_env_list_init(struct free_envelope_list *evl, int N);
+/* destroy a free envelope list */
+void tMPI_Free_env_list_destroy(struct free_envelope_list *evl);
+
+
+/* initialize a send envelope list */
+int tMPI_Send_env_list_init(struct send_envelope_list *evl, int N);
+/* destroy a send envelope list */
+void tMPI_Send_env_list_destroy(struct send_envelope_list *evl);
+
+
+
+
+
+
+/* initialize a recv envelope list */
+int tMPI_Recv_env_list_init(struct recv_envelope_list *evl);
+/* destroy a recv envelope list */
+void tMPI_Recv_env_list_destroy(struct recv_envelope_list *evl);
+
+
+
+
+/* initialize request list */
+int tMPI_Req_list_init(struct req_list *rl, int N_reqs);
+/* destroy request list */
+void tMPI_Req_list_destroy(struct req_list *rl);
+
+
+
+/* collective data structure ops */
+
+
+/* initialize a coll env structure */
+int tMPI_Coll_env_init(struct coll_env *mev, int N);
+/* destroy a coll env structure */
+void tMPI_Coll_env_destroy(struct coll_env *mev);
+
+/* initialize a coll sync structure */
+int tMPI_Coll_sync_init(struct coll_sync *msc, int N);
+/* destroy a coll sync structure */
+void tMPI_Coll_sync_destroy(struct coll_sync *msc);
+
+#ifdef USE_COLLECTIVE_COPY_BUFFER
+/* initialize a copy_buffer_list */
+int tMPI_Copy_buffer_list_init(struct copy_buffer_list *cbl, int Nbufs,
+                               size_t size);
+/* initialize a copy_buffer_list */
+void tMPI_Copy_buffer_list_destroy(struct copy_buffer_list *cbl);
+/* get a copy buffer from a list */
+struct copy_buffer *tMPI_Copy_buffer_list_get(struct copy_buffer_list *cbl);
+/* return a copy buffer to a list */
+void tMPI_Copy_buffer_list_return(struct copy_buffer_list *cbl,
+                                  struct copy_buffer      *cb);
+/* initialize a copy buffer */
+int tMPI_Copy_buffer_init(struct copy_buffer *cb, size_t size);
+void tMPI_Copy_buffer_destroy(struct copy_buffer *cb);
+#endif
+
+
+/* reduce ops: run a single iteration of a reduce operation on a, b -> dest */
+int tMPI_Reduce_run_op(void *dest, void *src_a, void *src_b,
+                       tMPI_Datatype datatype, int count, tMPI_Op op,
+                       tMPI_Comm comm);
+
+
+/* and we need this prototype */
+int main(int argc, char **argv);
diff --git a/src/include/gromacs/external/thread_mpi/src/p2p.h b/src/include/gromacs/external/thread_mpi/src/p2p.h
new file mode 100644 (file)
index 0000000..d3a8b8c
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/* request list: */
+/* get a request from the thread's pre-allocated request list */
+struct tmpi_req_ *tMPI_Get_req(struct req_list *rl);
+/* return a request to the thread's pre-allocated request list */
+void tMPI_Return_req(struct req_list *rl, struct tmpi_req_ *req);
+
+/* initialize a request with sensible values */
+void tMPI_Req_init(struct tmpi_req_ *rq, struct envelope *ev);
+
+/* wait for incoming connections (and the completion of outgoing connections
+   if spin locks are disabled), and handle them. */
+void tMPI_Wait_process_incoming(struct tmpi_thread *th);
+
+
+
+
+
+/* check for the completion of a single request */
+tmpi_bool tMPI_Test_single(struct tmpi_thread *cur,
+                           struct tmpi_req_   *rq);
+/* check and wait for the completion of a single request */
+void tMPI_Wait_single(struct tmpi_thread *cur, struct tmpi_req_ *rq);
+
+/* check for the completion of a NULL-delimited doubly linked list of
+   requests */
+tmpi_bool tMPI_Test_multi(struct tmpi_thread *cur, struct tmpi_req_ *rqs,
+                          tmpi_bool *any_done);
+
+
+
+/* set a request status */
+void tMPI_Set_status(struct tmpi_req_ *req, tMPI_Status *st);
+
+
+/* post a send envelope */
+struct envelope *tMPI_Post_send(struct tmpi_thread *cur,
+                                tMPI_Comm comm,
+                                struct tmpi_thread *dest,
+                                void *send_buf, int send_count,
+                                tMPI_Datatype datatype, int tag,
+                                tmpi_bool nonblock);
+
+/* post and match a receive envelope */
+struct envelope* tMPI_Post_match_recv(struct tmpi_thread *cur,
+                                      tMPI_Comm comm,
+                                      struct tmpi_thread *src,
+                                      void *recv_buf, int recv_count,
+                                      tMPI_Datatype datatype,
+                                      int tag, tmpi_bool nonblock);
diff --git a/src/include/gromacs/external/thread_mpi/src/profile.h b/src/include/gromacs/external/thread_mpi/src/profile.h
new file mode 100644 (file)
index 0000000..b4066df
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/* the profiling functions. Many of these are macros, so they're inlined
+   forcibly. Profiling is turned on by defining TMPI_PROFILE, but the most
+   useful parts depend on the cycle counter, which currently only works for
+   x86, x86_64 and ia64. */
+#ifdef TMPI_PROFILE
+
+#include "thread_mpi/atomic/cycles.h"
+
+struct tmpi_thread;
+
+enum tmpi_functions
+{
+    TMPIFN_Send = 0, /* first the point-to-point comm functions */
+    TMPIFN_Recv,
+    TMPIFN_Sendrecv,
+    TMPIFN_Isend,
+    TMPIFN_Irecv,
+    TMPIFN_Wait,
+    TMPIFN_Test,
+    TMPIFN_Waitall,
+    TMPIFN_Testall,
+    TMPIFN_Waitany,
+    TMPIFN_Testany,
+    TMPIFN_Waitsome,
+    TMPIFN_Testsome,
+
+    TMPIFN_Barrier, /* then the barrier */
+
+    TMPIFN_Bcast,   /* and now the collective comm functions */
+    TMPIFN_Gather,
+    TMPIFN_Gatherv,
+    TMPIFN_Scatter,
+    TMPIFN_Scatterv,
+    TMPIFN_Alltoall,
+    TMPIFN_Alltoallv,
+
+    TMPIFN_Reduce,
+    TMPIFN_Allreduce,
+    TMPIFN_Scan,
+
+    TMPIFN_Nfunctions
+};
+
+enum tmpi_wait_functions
+{
+    TMPIWAIT_P2p,        /* p2p send wait */
+    TMPIWAIT_P2p_signal, /* p2p signaling wait */
+    TMPIWAIT_Coll_send,  /* collective recv wait */
+    TMPIWAIT_Coll_recv,  /* collective recv wait */
+    TMPIWAIT_Barrier,    /* collective recv wait */
+    TMPIWAIT_Reduce,     /* collective (all)reduce wait */
+
+    TMPIWAIT_N
+};
+
+
+/* thread-specific profiling data structure */
+struct tmpi_profile
+{
+    unsigned long int mpifn_calls[TMPIFN_Nfunctions]; /* array of counters */
+
+    unsigned long int buffered_p2p_xfers;             /* number of buffered p2p transfers */
+    unsigned long int total_p2p_xfers;                /* total number of p2p transfers */
+
+    unsigned long int buffered_coll_xfers;            /* number of buffered collective
+                                                         transfers */
+    unsigned long int total_coll_xfers;               /* total number of collective
+                                                         transfers */
+
+#ifdef TMPI_CYCLE_COUNT
+    /* cycle counters */
+    tMPI_Cycles_t mpifn_cycles[TMPIFN_Nfunctions]; /* array of cycle counters */
+    tMPI_Cycles_t wait_cycles[TMPIWAIT_N];         /* the wait cycles */
+
+    tMPI_Cycles_t global_start, global_stop;       /* timing start and stop times */
+    tMPI_Cycles_t mpifn_start;                     /* individual timing start times for profiling
+                                                      function call times.  This can be here
+                                                      because tmpi_profile is thread-specific. */
+    enum tmpi_functions fn;                        /* the function being cycle-counted */
+
+
+    tMPI_Cycles_t wait_start; /* individual timing start times for profiling
+                                 wait times. */
+
+    double totals;            /* totals counter for reporting end results */
+#endif
+};
+
+extern int tMPI_Profile_started;
+
+/* initialize the profile counter */
+int tMPI_Profile_init(struct tmpi_profile *prof);
+
+#if 0
+/* deallocations */
+void tMPI_Profile_destroy(struct tmpi_profile *prof);
+#endif
+
+/* stop counting */
+void tMPI_Profile_stop(struct tmpi_profile *prof);
+
+
+
+/* counter functions */
+/* start */
+#ifdef TMPI_CYCLE_COUNT
+/*void tMPI_Profile_count_start(struct tmpi_thread *th);*/
+#define tMPI_Profile_count_start(th) { th->profile.mpifn_start = tMPI_Cycles_read(); }
+#else
+#define tMPI_Profile_count_start(th) {}
+#endif
+
+/* end. this is where the counting actually happens */
+/*void tMPI_Profile_count_stop(struct tmpi_thread *th, enum tmpi_functions fn);*/
+#ifdef TMPI_CYCLE_COUNT
+#define tMPI_Profile_count_stop(th, fn) \
+    { \
+        tMPI_Cycles_t stop = tMPI_Cycles_read(); \
+        th->profile.mpifn_cycles[fn] += (stop - th->profile.mpifn_start); \
+        (th->profile.mpifn_calls[fn])++; \
+    }
+#else
+#define tMPI_Profile_count_stop(th, fn) \
+    { \
+        (th->profile.mpifn_calls[fn])++; \
+    }
+#endif
+
+
+
+
+
+
+
+/* wait functions */
+#ifdef TMPI_CYCLE_COUNT
+/* start waiting cycle count */
+/*void tMPI_Profile_wait_start(struct tmpi_thread *th);*/
+#define tMPI_Profile_wait_start(th) \
+    { \
+        th->profile.wait_start = tMPI_Cycles_read(); \
+    }
+
+/* stop waiting cycle count */
+/*void tMPI_Profile_wait_stop(struct tmpi_thread *th,
+                            enum tmpi_wait_functions fn);*/
+#define tMPI_Profile_wait_stop(th, fn) \
+    { \
+        tMPI_Cycles_t wait_stop = tMPI_Cycles_read(); \
+        th->profile.wait_cycles[fn] += (wait_stop - th->profile.wait_start); \
+    }
+#else
+#define tMPI_Profile_wait_start(th) {}
+#define tMPI_Profile_wait_stop(th, fn) {}
+#endif
+
+
+/* count the number of transfers at the receiving end. */
+/*void tMPI_Profile_count_buffered_p2p_xfer(struct tmpi_thread *th);
+   void tMPI_Profile_count_p2p_xfer(struct tmpi_thread *th);
+   void tMPI_Profile_count_buffered_coll_xfer(struct tmpi_thread *th);
+   void tMPI_Profile_count_coll_xfer(struct tmpi_thread *th);*/
+#define tMPI_Profile_count_buffered_p2p_xfer(th) \
+    { \
+        (th->profile.buffered_p2p_xfers)++; \
+    }
+
+#define tMPI_Profile_count_p2p_xfer(th) \
+    { \
+        (th->profile.total_p2p_xfers)++; \
+    }
+
+#define tMPI_Profile_count_buffered_coll_xfer(th) \
+    { \
+        (th->profile.buffered_coll_xfers)++; \
+    }
+
+#define tMPI_Profile_count_coll_xfer(th) \
+    { \
+        (th->profile.total_coll_xfers)++; \
+    }
+
+
+
+/* output functions */
+void tMPI_Profiles_summarize(int Nthreads, struct tmpi_thread *threads);
+
+#endif
diff --git a/src/include/gromacs/external/thread_mpi/src/pthreads.h b/src/include/gromacs/external/thread_mpi/src/pthreads.h
new file mode 100644 (file)
index 0000000..78905aa
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+/* the types that were defined in include/thread_mpi/threads.h */
+
+struct tMPI_Thread
+{
+    pthread_t th;              /*!< The POSIX thread ID */
+    int       started_by_tmpi; /*!< whether the thread is started by tMPI */
+};
+
+struct tMPI_Thread_key
+{
+    pthread_key_t pkey;
+};
+
+struct tMPI_Mutex
+{
+    pthread_mutex_t mtx;
+};
+
+struct tMPI_Thread_cond
+{
+    pthread_cond_t cond;
+};
+
+struct tMPI_Thread_barrier
+{
+    tMPI_Atomic_t     initialized;
+    pthread_mutex_t   mutex;     /*!< Lock for the barrier contents          */
+    pthread_cond_t    cv;        /*!< Condition to signal barrier completion */
+    int               threshold; /*!< Total number of members in barrier     */
+    int               count;     /*!< Remaining count before completion      */
+    int               cycle;     /*!< Alternating 0/1 to indicate round      */
+};
diff --git a/src/include/gromacs/external/thread_mpi/src/settings.h b/src/include/gromacs/external/thread_mpi/src/settings.h
new file mode 100644 (file)
index 0000000..d44e973
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+/*#define TMPI_DEBUG*/
+
+
+/* If this is defined, thread_mpi will print a message when for every MPI
+   call is called or returns. Useful for debugging MPI-related issues
+   in the calling program. */
+/*#define TMPI_TRACE*/
+
+/* if this is defined, MPI will warn/hang/crash on practices that don't conform
+   to the MPI standard (such as not calling tMPI_Comm_free on all threads that
+   are part of the comm being freed). */
+#define TMPI_STRICT
+
+/* whether to warn if there are mallocs at performance-critical sections
+   (due to preallocations being too small) */
+#ifdef TMPI_WARNINGS
+#define TMPI_WARN_MALLOC
+#else
+/*#define TMPI_WARN_MALLOC*/
+#endif
+
+
+/* the number of envelopes to allocate per thread-to-thread path */
+#define N_EV_ALLOC 16
+
+/* the normal maximum number of threads for pre-defined arrays
+   (if the actual number of threads is bigger than this, it'll
+    allocate/deallocate arrays, so no problems will arise). */
+#define MAX_PREALLOC_THREADS 64
+
+/* Whether to use lock-free lists using compare-and-swap (cmpxchg on x86)
+   pointer functions. Message passing using blocking Send/Recv, and multicasts
+   are is still blocking, of course. */
+#define TMPI_LOCK_FREE_LISTS
+
+/* Whether to disable yielding to the OS scheduler during waits. Disabling
+   this improves performance very slightly if Nthreads<=Ncores on an
+   otherwise idle system because waits have slightly lower latencies, but
+   causes very poor performance if threads are competing for CPU time (for
+   example, when Nthreads>Ncores, or another process is running on the
+   system.
+
+   This option can be set with cmake. */
+/*#define TMPI_WAIT_FOR_NO_ONE 1 */
+
+
+
+/* whether to enable double-copying (where the sender copies data to an
+   intermediate buffer for small enough buffers, allowing it to return
+   from a blocking send call early. The receiver is free to copy from the
+   original buffer while the sender is copying, possibly allowing them to
+   work in parallel).
+
+   This option can be set with cmake. */
+/*#define TMPI_COPY_BUFFER*/
+
+
+/* The size (in bytes) of the maximum transmission size for which double
+   copying is allowed (i.e. the sender doesn't wait for the receiver to
+   become ready, but posts a copied buffer in its envelope).
+
+   A size of 8192 bytes was chosen after some testing with Gromacs. */
+#define COPY_BUFFER_SIZE 8192
+#ifdef TMPI_COPY_BUFFER
+/* We can separately specify whether we want copy buffers for send/recv or
+   multicast communications: */
+#define USE_SEND_RECV_COPY_BUFFER
+#define USE_COLLECTIVE_COPY_BUFFER
+#endif
+
+
+/* The number of collective envelopes per comm object. This is the maximum
+   number of simulataneous collective communications that can
+   take place per comm object. If TMPI_NO_COPY_BUFFER is set, simultaneous
+   collective communications don't happen and 2 is the right value.  */
+#ifdef USE_COLLECTIVE_COPY_BUFFER
+#define N_COLL_ENV 12
+#else
+#define N_COLL_ENV 2
+#endif
+
+
+/* Whether to do profiling of the number of MPI communication calls. A
+    report with the total number of calls for each communication function
+    will be generated at MPI_Finalize().
+
+    This option can be set with cmake.*/
+/*#define TMPI_PROFILE*/
+
+
+/* whether to turn on thread affinity (required for NUMA optimizations)
+   if the number of threads to spawn is equal to the number of processors. */
+#define TMPI_THREAD_AFFINITY
diff --git a/src/include/gromacs/external/thread_mpi/src/tmpi_ops.h b/src/include/gromacs/external/thread_mpi/src/tmpi_ops.h
new file mode 100644 (file)
index 0000000..d454d4d
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+
+#ifdef THREAD_MPI_OPS
+
+/* cpp wizardry follows...
+
+   This file is #included directly from thread_mpi.c, and constructs
+   MPI_Reduce operators.
+
+   What this does is create the min, max, sum, prod, etc. functions for a given
+   datatype (pre-defined as TYPE, with identifier name TYPENM) and puts pointers
+   to these functions in an array called oplist_TYPENM.
+
+   gmx_thread_mpi_reduce.c includes this file once for each type used by MPI,
+   and thus builds up a set of arrays of function pointers, that then get used
+   in the mpi_datatype_ structure. This way, each operation/datatype entry
+   that makes sense can be extracted easily. Note that we don't (yet) support
+   user-defined ops */
+
+#define FNAMEr(tp, fn) tMPI_ ## tp ## _ ## fn
+#define FNAME(tp, fn) FNAMEr(tp, fn)
+
+/* macros to define functions and prototypes based on a name and an operation */
+#define FNr(tp, fname, fn) \
+    static void tMPI_ ## tp ## _ ## fname  (void *dest, void *src_a, void *src_b, \
+                                            int count) \
+    { \
+        /*printf("in function %s, count=%d\n", __FUNCTION__, count);*/ \
+        TYPE *a = (TYPE*)src_a; \
+        TYPE *b = (TYPE*)src_b; \
+        TYPE *d = (TYPE*)dest; \
+        int   i; \
+        for (i = 0; i < count; i++) { \
+            d[i] = (TYPE)(fn(a[i], b[i])); } \
+    }
+
+#define FN(tp, fname, fn) FNr(tp, fname, fn)
+
+#define OPFNr(tp, fname, operator)  \
+              static void tMPI_ ## tp ## _ ## fname  (void *dest, void *src_a, void *src_b, \
+                                                      int count) \
+              { \
+                  /*printf("in function %s, count=%d\n", __FUNCTION__, count);*/ \
+                  TYPE *a = (TYPE*)src_a; \
+                  TYPE *b = (TYPE*)src_b; \
+                  TYPE *d = (TYPE*)dest; \
+                  int i; \
+                  for (i = 0; i < count; i++) { \
+                      d[i] = (TYPE)(a[i] operator b[i]); } \
+              }
+
+#define OPFN(tp, fname, operator) OPFNr(tp, fname, operator)
+
+
+/* these are the function prototypes + definitions: */
+#if TYPECATEGORY!=LOGICALTYPE
+#define MAX(a, b)  (( (a) > (b) ) ? (a) : (b))
+FN(TYPENM, max, MAX)
+#undef MAX
+#define MIN(a, b)  (( (a) < (b) ) ? (a) : (b))
+FN(TYPENM, min, MIN)
+#undef MIN
+OPFN(TYPENM, sum, +)
+OPFN(TYPENM, prod, *)
+#endif
+#if TYPECATEGORY!=FLOATTYPE
+OPFN(TYPENM, land, &&)
+OPFN(TYPENM, lor, ||)
+#define XOR(a, b)  ( (!a) ^ (!b) )
+FN(TYPENM, lxor, XOR)
+#undef XOR
+#endif
+#if TYPECATEGORY==INTTYPE
+OPFN(TYPENM, band, &)
+OPFN(TYPENM, bor, |)
+OPFN(TYPENM, bxor, ^)
+#endif
+
+#define OPARRAYr(tp) oplist_ ## tp
+#define OPARRAY(tp) OPARRAYr(tp)
+
+tMPI_Op_fn OPARRAY(TYPENM)[] =
+{
+#if TYPECATEGORY==LOGICALTYPE
+    0,
+    0,
+    0,
+    0,
+    FNAME(TYPENM, land),
+    0,
+    FNAME(TYPENM, lor),
+    0,
+    FNAME(TYPENM, lxor),
+    0,
+#else
+    FNAME(TYPENM, max),
+    FNAME(TYPENM, min),
+    FNAME(TYPENM, sum),
+    FNAME(TYPENM, prod),
+#if TYPECATEGORY==INTTYPE
+    FNAME(TYPENM, land),
+    FNAME(TYPENM, band),
+    FNAME(TYPENM, lor),
+    FNAME(TYPENM, bor),
+    FNAME(TYPENM, lxor),
+    FNAME(TYPENM, bxor)
+#else
+    0,
+    0,
+    0,
+    0,
+    0,
+    0
+#endif
+#endif
+};
+
+
+#undef FNAME
+#undef FNAMEr
+#undef OPARRAYr
+#undef OPARRAY
+#undef FN
+#undef FNr
+#undef OPFN
+#undef OPFNr
+
+#undef TYPE
+#undef TYPENM
+#undef TYPECATEGORY
+#endif
diff --git a/src/include/gromacs/external/thread_mpi/src/unused.h b/src/include/gromacs/external/thread_mpi/src/unused.h
new file mode 100644 (file)
index 0000000..6287c67
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+/*
+ * These attributes suppress compiler warnings about unused function arguments
+ * by marking them as possibly unused. Some arguments are unused but
+ * have to be retained to preserve a function signature
+ * that must match that of another function.
+ * Some arguments are only used in *some* code paths (e.g. MPI)
+ */
+
+#ifndef tmpi_unused
+#ifdef __GNUC__
+/* GCC, clang, and some ICC pretending to be GCC */
+#  define tmpi_unused __attribute__ ((unused))
+#elif (defined(__INTEL_COMPILER) || defined(__ECC)) && !defined(_MSC_VER)
+/* ICC on *nix */
+#  define tmpi_unused __attribute__ ((unused))
+#elif defined _MSC_VER
+/* MSVC */
+#  define tmpi_unused /*@unused@*/
+#elif defined(__xlC__)
+/* IBM */
+#  define tmpi_unused __attribute__ ((unused))
+#else
+#  define tmpi_unused
+#endif
+#endif
diff --git a/src/include/gromacs/external/thread_mpi/src/winthreads.h b/src/include/gromacs/external/thread_mpi/src/winthreads.h
new file mode 100644 (file)
index 0000000..ba97d7e
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+   This source code file is part of thread_mpi.
+   Written by Sander Pronk, Erik Lindahl, and possibly others.
+
+   Copyright (c) 2009, Sander Pronk, Erik Lindahl.
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+   1) Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   2) Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   3) Neither the name of the copyright holders nor the
+   names of its contributors may be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY US ''AS IS'' AND ANY
+   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL WE BE LIABLE FOR ANY
+   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   If you want to redistribute modifications, please consider that
+   scientific software is very special. Version control is crucial -
+   bugs must be traceable. We will be happy to consider code for
+   inclusion in the official distribution, but derived work should not
+   be called official thread_mpi. Details are found in the README & COPYING
+   files.
+ */
+
+/* the types that were defined in include/thread_mpi/threads.h */
+
+
+struct tMPI_Thread
+{
+    HANDLE th;              /* the thread handle */
+    DWORD  id;              /* the thread ID */
+    int    started_by_tmpi; /* whether this thread was started by tmpi */
+};
+
+struct tMPI_Thread_key
+{
+    DWORD wkey;
+};
+
+struct tMPI_Mutex
+{
+    CRITICAL_SECTION cs;
+};
+
+struct tMPI_Thread_once
+{
+    int dum;
+};
+
+struct tMPI_Thread_cond
+{
+#if 0
+    /* this works since Windows Vista: */
+    CONDITION_VARIABLE cv;
+#else
+    /* this data structure and its algorithms are based on
+       'Strategies for Implementing POSIX Condition Variables on Win32'
+       by
+       Douglas C. Schmidt and Irfan Pyarali
+       Department of Computer Science
+       Washington University, St. Louis, Missouri
+       http://www.cs.wustl.edu/~schmidt/win32-cv-1.html */
+    int              Nwaiters; /* number of waiting threads */
+    CRITICAL_SECTION wtr_lock; /* lock for Nwaiters */
+    int              Nrelease; /* number of threads to release in broadcast/signal */
+    int              cycle;    /* cycle number so threads can't steal signals */
+    HANDLE           ev;       /* the event used to trigger WaitForSingleObject.
+                                  Is a manual reset event.  */
+#endif
+};
+
+struct tMPI_Thread_barrier
+{
+#if 0
+    /* use this once Vista is the oldest supported windows version: */
+    CRITICAL_SECTION   cs;    /*!< Lock for the barrier
+                                 contents          */
+    CONDITION_VARIABLE cv;    /*!< Condition to signal barrier
+                                 completion */
+#else
+    tMPI_Thread_mutex_t cs;   /*!< Lock for the barrier contents          */
+    tMPI_Thread_cond_t  cv;   /*!< Condition to signal barrier completion */
+#endif
+};
diff --git a/src/include/gromacs/external/tinyxml2/tinyxml2.h b/src/include/gromacs/external/tinyxml2/tinyxml2.h
new file mode 100755 (executable)
index 0000000..a4769c8
--- /dev/null
@@ -0,0 +1,2130 @@
+/*
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#ifndef TINYXML2_INCLUDED
+#define TINYXML2_INCLUDED
+
+#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__)
+#   include <ctype.h>
+#   include <limits.h>
+#   include <stdio.h>
+#   include <stdlib.h>
+#   include <string.h>
+#   include <stdarg.h>
+#else
+#   include <cctype>
+#   include <climits>
+#   include <cstdio>
+#   include <cstdlib>
+#   include <cstring>
+#   include <cstdarg>
+#endif
+
+/*
+   TODO: intern strings instead of allocation.
+*/
+/*
+       gcc:
+        g++ -Wall -DDEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe
+
+    Formatting, Artistic Style:
+        AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h
+*/
+
+#if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__)
+#   ifndef DEBUG
+#       define DEBUG
+#   endif
+#endif
+
+#ifdef _MSC_VER
+#   pragma warning(push)
+#   pragma warning(disable: 4251)
+#endif
+
+#ifdef _WIN32
+#   ifdef TINYXML2_EXPORT
+#       define TINYXML2_LIB __declspec(dllexport)
+#   elif defined(TINYXML2_IMPORT)
+#       define TINYXML2_LIB __declspec(dllimport)
+#   else
+#       define TINYXML2_LIB
+#   endif
+#else
+#   define TINYXML2_LIB
+#endif
+
+
+#if defined(DEBUG)
+#   if defined(_MSC_VER)
+#       // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like
+#       define TIXMLASSERT( x )           if ( !((void)0,(x))) { __debugbreak(); } //if ( !(x)) WinDebugBreak()
+#   elif defined (ANDROID_NDK)
+#       include <android/log.h>
+#       define TIXMLASSERT( x )           if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); }
+#   else
+#       include <assert.h>
+#       define TIXMLASSERT                assert
+#   endif
+#   else
+#       define TIXMLASSERT( x )           {}
+#endif
+
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE)
+// Microsoft visual studio, version 2005 and higher.
+/*int _snprintf_s(
+   char *buffer,
+   size_t sizeOfBuffer,
+   size_t count,
+   const char *format [,
+         argument] ...
+);*/
+inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... )
+{
+    va_list va;
+    va_start( va, format );
+    int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va );
+    va_end( va );
+    return result;
+}
+#define TIXML_SSCANF   sscanf_s
+#elif defined WINCE
+#define TIXML_SNPRINTF _snprintf
+#define TIXML_SSCANF   sscanf
+#else
+// GCC version 3 and higher
+//#warning( "Using sn* functions." )
+#define TIXML_SNPRINTF snprintf
+#define TIXML_SSCANF   sscanf
+#endif
+
+/* Versioning, past 1.0.14:
+       http://semver.org/
+*/
+static const int TIXML2_MAJOR_VERSION = 3;
+static const int TIXML2_MINOR_VERSION = 0;
+static const int TIXML2_PATCH_VERSION = 0;
+
+namespace tinyxml2
+{
+class XMLDocument;
+class XMLElement;
+class XMLAttribute;
+class XMLComment;
+class XMLText;
+class XMLDeclaration;
+class XMLUnknown;
+class XMLPrinter;
+
+/*
+       A class that wraps strings. Normally stores the start and end
+       pointers into the XML file itself, and will apply normalization
+       and entity translation if actually read. Can also store (and memory
+       manage) a traditional char[]
+*/
+class StrPair
+{
+public:
+    enum {
+        NEEDS_ENTITY_PROCESSING                        = 0x01,
+        NEEDS_NEWLINE_NORMALIZATION            = 0x02,
+        COLLAPSE_WHITESPACE                    = 0x04,
+
+        TEXT_ELEMENT                           = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
+        TEXT_ELEMENT_LEAVE_ENTITIES            = NEEDS_NEWLINE_NORMALIZATION,
+        ATTRIBUTE_NAME                         = 0,
+        ATTRIBUTE_VALUE                                = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION,
+        ATTRIBUTE_VALUE_LEAVE_ENTITIES         = NEEDS_NEWLINE_NORMALIZATION,
+        COMMENT                                        = NEEDS_NEWLINE_NORMALIZATION
+    };
+
+    StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {}
+    ~StrPair();
+
+    void Set( char* start, char* end, int flags ) {
+        Reset();
+        _start  = start;
+        _end    = end;
+        _flags  = flags | NEEDS_FLUSH;
+    }
+
+    const char* GetStr();
+
+    bool Empty() const {
+        return _start == _end;
+    }
+
+    void SetInternedStr( const char* str ) {
+        Reset();
+        _start = const_cast<char*>(str);
+    }
+
+    void SetStr( const char* str, int flags=0 );
+
+    char* ParseText( char* in, const char* endTag, int strFlags );
+    char* ParseName( char* in );
+
+    void TransferTo( StrPair* other );
+
+private:
+    void Reset();
+    void CollapseWhitespace();
+
+    enum {
+        NEEDS_FLUSH = 0x100,
+        NEEDS_DELETE = 0x200
+    };
+
+    // After parsing, if *_end != 0, it can be set to zero.
+    int     _flags;
+    char*   _start;
+    char*   _end;
+
+    StrPair( const StrPair& other );   // not supported
+    void operator=( StrPair& other );  // not supported, use TransferTo()
+};
+
+
+/*
+       A dynamic array of Plain Old Data. Doesn't support constructors, etc.
+       Has a small initial memory pool, so that low or no usage will not
+       cause a call to new/delete
+*/
+template <class T, int INIT>
+class DynArray
+{
+public:
+    DynArray() {
+        _mem = _pool;
+        _allocated = INIT;
+        _size = 0;
+    }
+
+    ~DynArray() {
+        if ( _mem != _pool ) {
+            delete [] _mem;
+        }
+    }
+
+    void Clear() {
+        _size = 0;
+    }
+
+    void Push( T t ) {
+        TIXMLASSERT( _size < INT_MAX );
+        EnsureCapacity( _size+1 );
+        _mem[_size++] = t;
+    }
+
+    T* PushArr( int count ) {
+        TIXMLASSERT( count >= 0 );
+        TIXMLASSERT( _size <= INT_MAX - count );
+        EnsureCapacity( _size+count );
+        T* ret = &_mem[_size];
+        _size += count;
+        return ret;
+    }
+
+    T Pop() {
+        TIXMLASSERT( _size > 0 );
+        return _mem[--_size];
+    }
+
+    void PopArr( int count ) {
+        TIXMLASSERT( _size >= count );
+        _size -= count;
+    }
+
+    bool Empty() const                                 {
+        return _size == 0;
+    }
+
+    T& operator[](int i)                               {
+        TIXMLASSERT( i>= 0 && i < _size );
+        return _mem[i];
+    }
+
+    const T& operator[](int i) const   {
+        TIXMLASSERT( i>= 0 && i < _size );
+        return _mem[i];
+    }
+
+    const T& PeekTop() const            {
+        TIXMLASSERT( _size > 0 );
+        return _mem[ _size - 1];
+    }
+
+    int Size() const                                   {
+        TIXMLASSERT( _size >= 0 );
+        return _size;
+    }
+
+    int Capacity() const                               {
+        return _allocated;
+    }
+
+    const T* Mem() const                               {
+        return _mem;
+    }
+
+    T* Mem()                                                   {
+        return _mem;
+    }
+
+private:
+    DynArray( const DynArray& ); // not supported
+    void operator=( const DynArray& ); // not supported
+
+    void EnsureCapacity( int cap ) {
+        TIXMLASSERT( cap > 0 );
+        if ( cap > _allocated ) {
+            TIXMLASSERT( cap <= INT_MAX / 2 );
+            int newAllocated = cap * 2;
+            T* newMem = new T[newAllocated];
+            memcpy( newMem, _mem, sizeof(T)*_size );   // warning: not using constructors, only works for PODs
+            if ( _mem != _pool ) {
+                delete [] _mem;
+            }
+            _mem = newMem;
+            _allocated = newAllocated;
+        }
+    }
+
+    T*  _mem;
+    T   _pool[INIT];
+    int _allocated;            // objects allocated
+    int _size;                 // number objects in use
+};
+
+
+/*
+       Parent virtual class of a pool for fast allocation
+       and deallocation of objects.
+*/
+class MemPool
+{
+public:
+    MemPool() {}
+    virtual ~MemPool() {}
+
+    virtual int ItemSize() const = 0;
+    virtual void* Alloc() = 0;
+    virtual void Free( void* ) = 0;
+    virtual void SetTracked() = 0;
+    virtual void Clear() = 0;
+};
+
+
+/*
+       Template child class to create pools of the correct type.
+*/
+template< int SIZE >
+class MemPoolT : public MemPool
+{
+public:
+    MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0)       {}
+    ~MemPoolT() {
+        Clear();
+    }
+    
+    void Clear() {
+        // Delete the blocks.
+        while( !_blockPtrs.Empty()) {
+            Block* b  = _blockPtrs.Pop();
+            delete b;
+        }
+        _root = 0;
+        _currentAllocs = 0;
+        _nAllocs = 0;
+        _maxAllocs = 0;
+        _nUntracked = 0;
+    }
+
+    virtual int ItemSize() const       {
+        return SIZE;
+    }
+    int CurrentAllocs() const          {
+        return _currentAllocs;
+    }
+
+    virtual void* Alloc() {
+        if ( !_root ) {
+            // Need a new block.
+            Block* block = new Block();
+            _blockPtrs.Push( block );
+
+            for( int i=0; i<COUNT-1; ++i ) {
+                block->chunk[i].next = &block->chunk[i+1];
+            }
+            block->chunk[COUNT-1].next = 0;
+            _root = block->chunk;
+        }
+        void* result = _root;
+        _root = _root->next;
+
+        ++_currentAllocs;
+        if ( _currentAllocs > _maxAllocs ) {
+            _maxAllocs = _currentAllocs;
+        }
+        _nAllocs++;
+        _nUntracked++;
+        return result;
+    }
+    
+    virtual void Free( void* mem ) {
+        if ( !mem ) {
+            return;
+        }
+        --_currentAllocs;
+        Chunk* chunk = static_cast<Chunk*>( mem );
+#ifdef DEBUG
+        memset( chunk, 0xfe, sizeof(Chunk) );
+#endif
+        chunk->next = _root;
+        _root = chunk;
+    }
+    void Trace( const char* name ) {
+        printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n",
+                name, _maxAllocs, _maxAllocs*SIZE/1024, _currentAllocs, SIZE, _nAllocs, _blockPtrs.Size() );
+    }
+
+    void SetTracked() {
+        _nUntracked--;
+    }
+
+    int Untracked() const {
+        return _nUntracked;
+    }
+
+       // This number is perf sensitive. 4k seems like a good tradeoff on my machine.
+       // The test file is large, 170k.
+       // Release:             VS2010 gcc(no opt)
+       //              1k:             4000
+       //              2k:             4000
+       //              4k:             3900    21000
+       //              16k:    5200
+       //              32k:    4300
+       //              64k:    4000    21000
+    enum { COUNT = (4*1024)/SIZE }; // Some compilers do not accept to use COUNT in private part if COUNT is private
+
+private:
+    MemPoolT( const MemPoolT& ); // not supported
+    void operator=( const MemPoolT& ); // not supported
+
+    union Chunk {
+        Chunk*  next;
+        char    mem[SIZE];
+    };
+    struct Block {
+        Chunk chunk[COUNT];
+    };
+    DynArray< Block*, 10 > _blockPtrs;
+    Chunk* _root;
+
+    int _currentAllocs;
+    int _nAllocs;
+    int _maxAllocs;
+    int _nUntracked;
+};
+
+
+
+/**
+       Implements the interface to the "Visitor pattern" (see the Accept() method.)
+       If you call the Accept() method, it requires being passed a XMLVisitor
+       class to handle callbacks. For nodes that contain other nodes (Document, Element)
+       you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs
+       are simply called with Visit().
+
+       If you return 'true' from a Visit method, recursive parsing will continue. If you return
+       false, <b>no children of this node or its siblings</b> will be visited.
+
+       All flavors of Visit methods have a default implementation that returns 'true' (continue
+       visiting). You need to only override methods that are interesting to you.
+
+       Generally Accept() is called on the XMLDocument, although all nodes support visiting.
+
+       You should never change the document from a callback.
+
+       @sa XMLNode::Accept()
+*/
+class TINYXML2_LIB XMLVisitor
+{
+public:
+    virtual ~XMLVisitor() {}
+
+    /// Visit a document.
+    virtual bool VisitEnter( const XMLDocument& /*doc*/ )                      {
+        return true;
+    }
+    /// Visit a document.
+    virtual bool VisitExit( const XMLDocument& /*doc*/ )                       {
+        return true;
+    }
+
+    /// Visit an element.
+    virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ )   {
+        return true;
+    }
+    /// Visit an element.
+    virtual bool VisitExit( const XMLElement& /*element*/ )                    {
+        return true;
+    }
+
+    /// Visit a declaration.
+    virtual bool Visit( const XMLDeclaration& /*declaration*/ )                {
+        return true;
+    }
+    /// Visit a text node.
+    virtual bool Visit( const XMLText& /*text*/ )                                      {
+        return true;
+    }
+    /// Visit a comment node.
+    virtual bool Visit( const XMLComment& /*comment*/ )                                {
+        return true;
+    }
+    /// Visit an unknown node.
+    virtual bool Visit( const XMLUnknown& /*unknown*/ )                                {
+        return true;
+    }
+};
+
+// WARNING: must match XMLDocument::_errorNames[]
+enum XMLError {
+    XML_SUCCESS = 0,
+    XML_NO_ERROR = 0,
+    XML_NO_ATTRIBUTE,
+    XML_WRONG_ATTRIBUTE_TYPE,
+    XML_ERROR_FILE_NOT_FOUND,
+    XML_ERROR_FILE_COULD_NOT_BE_OPENED,
+    XML_ERROR_FILE_READ_ERROR,
+    XML_ERROR_ELEMENT_MISMATCH,
+    XML_ERROR_PARSING_ELEMENT,
+    XML_ERROR_PARSING_ATTRIBUTE,
+    XML_ERROR_IDENTIFYING_TAG,
+    XML_ERROR_PARSING_TEXT,
+    XML_ERROR_PARSING_CDATA,
+    XML_ERROR_PARSING_COMMENT,
+    XML_ERROR_PARSING_DECLARATION,
+    XML_ERROR_PARSING_UNKNOWN,
+    XML_ERROR_EMPTY_DOCUMENT,
+    XML_ERROR_MISMATCHED_ELEMENT,
+    XML_ERROR_PARSING,
+    XML_CAN_NOT_CONVERT_TEXT,
+    XML_NO_TEXT_NODE,
+
+       XML_ERROR_COUNT
+};
+
+
+/*
+       Utility functionality.
+*/
+class XMLUtil
+{
+public:
+    static const char* SkipWhiteSpace( const char* p ) {
+        TIXMLASSERT( p );
+        while( IsWhiteSpace(*p) ) {
+            ++p;
+        }
+        TIXMLASSERT( p );
+        return p;
+    }
+    static char* SkipWhiteSpace( char* p )                             {
+        return const_cast<char*>( SkipWhiteSpace( const_cast<const char*>(p) ) );
+    }
+
+    // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't
+    // correct, but simple, and usually works.
+    static bool IsWhiteSpace( char p )                                 {
+        return !IsUTF8Continuation(p) && isspace( static_cast<unsigned char>(p) );
+    }
+    
+    inline static bool IsNameStartChar( unsigned char ch ) {
+        if ( ch >= 128 ) {
+            // This is a heuristic guess in attempt to not implement Unicode-aware isalpha()
+            return true;
+        }
+        if ( isalpha( ch ) ) {
+            return true;
+        }
+        return ch == ':' || ch == '_';
+    }
+    
+    inline static bool IsNameChar( unsigned char ch ) {
+        return IsNameStartChar( ch )
+               || isdigit( ch )
+               || ch == '.'
+               || ch == '-';
+    }
+
+    inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX )  {
+        if ( p == q ) {
+            return true;
+        }
+        int n = 0;
+        while( *p && *q && *p == *q && n<nChar ) {
+            ++p;
+            ++q;
+            ++n;
+        }
+        if ( (n == nChar) || ( *p == 0 && *q == 0 ) ) {
+            return true;
+        }
+        return false;
+    }
+    
+    inline static bool IsUTF8Continuation( const char p ) {
+        return ( p & 0x80 ) != 0;
+    }
+
+    static const char* ReadBOM( const char* p, bool* hasBOM );
+    // p is the starting location,
+    // the UTF-8 value of the entity will be placed in value, and length filled in.
+    static const char* GetCharacterRef( const char* p, char* value, int* length );
+    static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
+
+    // converts primitive types to strings
+    static void ToStr( int v, char* buffer, int bufferSize );
+    static void ToStr( unsigned v, char* buffer, int bufferSize );
+    static void ToStr( bool v, char* buffer, int bufferSize );
+    static void ToStr( float v, char* buffer, int bufferSize );
+    static void ToStr( double v, char* buffer, int bufferSize );
+
+    // converts strings to primitive types
+    static bool        ToInt( const char* str, int* value );
+    static bool ToUnsigned( const char* str, unsigned* value );
+    static bool        ToBool( const char* str, bool* value );
+    static bool        ToFloat( const char* str, float* value );
+    static bool ToDouble( const char* str, double* value );
+};
+
+
+/** XMLNode is a base class for every object that is in the
+       XML Document Object Model (DOM), except XMLAttributes.
+       Nodes have siblings, a parent, and children which can
+       be navigated. A node is always in a XMLDocument.
+       The type of a XMLNode can be queried, and it can
+       be cast to its more defined type.
+
+       A XMLDocument allocates memory for all its Nodes.
+       When the XMLDocument gets deleted, all its Nodes
+       will also be deleted.
+
+       @verbatim
+       A Document can contain: Element (container or leaf)
+                                                       Comment (leaf)
+                                                       Unknown (leaf)
+                                                       Declaration( leaf )
+
+       An Element can contain: Element (container or leaf)
+                                                       Text    (leaf)
+                                                       Attributes (not on tree)
+                                                       Comment (leaf)
+                                                       Unknown (leaf)
+
+       @endverbatim
+*/
+class TINYXML2_LIB XMLNode
+{
+    friend class XMLDocument;
+    friend class XMLElement;
+public:
+
+    /// Get the XMLDocument that owns this XMLNode.
+    const XMLDocument* GetDocument() const     {
+        return _document;
+    }
+    /// Get the XMLDocument that owns this XMLNode.
+    XMLDocument* GetDocument()                         {
+        return _document;
+    }
+
+    /// Safely cast to an Element, or null.
+    virtual XMLElement*                ToElement()             {
+        return 0;
+    }
+    /// Safely cast to Text, or null.
+    virtual XMLText*           ToText()                {
+        return 0;
+    }
+    /// Safely cast to a Comment, or null.
+    virtual XMLComment*                ToComment()             {
+        return 0;
+    }
+    /// Safely cast to a Document, or null.
+    virtual XMLDocument*       ToDocument()    {
+        return 0;
+    }
+    /// Safely cast to a Declaration, or null.
+    virtual XMLDeclaration*    ToDeclaration() {
+        return 0;
+    }
+    /// Safely cast to an Unknown, or null.
+    virtual XMLUnknown*                ToUnknown()             {
+        return 0;
+    }
+
+    virtual const XMLElement*          ToElement() const               {
+        return 0;
+    }
+    virtual const XMLText*                     ToText() const                  {
+        return 0;
+    }
+    virtual const XMLComment*          ToComment() const               {
+        return 0;
+    }
+    virtual const XMLDocument*         ToDocument() const              {
+        return 0;
+    }
+    virtual const XMLDeclaration*      ToDeclaration() const   {
+        return 0;
+    }
+    virtual const XMLUnknown*          ToUnknown() const               {
+        return 0;
+    }
+
+    /** The meaning of 'value' changes for the specific type.
+       @verbatim
+       Document:       empty
+       Element:        name of the element
+       Comment:        the comment text
+       Unknown:        the tag contents
+       Text:           the text string
+       @endverbatim
+    */
+    const char* Value() const;
+
+    /** Set the Value of an XML node.
+       @sa Value()
+    */
+    void SetValue( const char* val, bool staticMem=false );
+
+    /// Get the parent of this node on the DOM.
+    const XMLNode*     Parent() const                  {
+        return _parent;
+    }
+
+    XMLNode* Parent()                                          {
+        return _parent;
+    }
+
+    /// Returns true if this node has no children.
+    bool NoChildren() const                                    {
+        return !_firstChild;
+    }
+
+    /// Get the first child node, or null if none exists.
+    const XMLNode*  FirstChild() const         {
+        return _firstChild;
+    }
+
+    XMLNode*           FirstChild()                    {
+        return _firstChild;
+    }
+
+    /** Get the first child element, or optionally the first child
+        element with the specified name.
+    */
+    const XMLElement* FirstChildElement( const char* value=0 ) const;
+
+    XMLElement* FirstChildElement( const char* value=0 )       {
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->FirstChildElement( value ));
+    }
+
+    /// Get the last child node, or null if none exists.
+    const XMLNode*     LastChild() const                                               {
+        return _lastChild;
+    }
+
+    XMLNode*           LastChild()                                                             {
+        return const_cast<XMLNode*>(const_cast<const XMLNode*>(this)->LastChild() );
+    }
+
+    /** Get the last child element or optionally the last child
+        element with the specified name.
+    */
+    const XMLElement* LastChildElement( const char* value=0 ) const;
+
+    XMLElement* LastChildElement( const char* value=0 )        {
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->LastChildElement(value) );
+    }
+
+    /// Get the previous (left) sibling node of this node.
+    const XMLNode*     PreviousSibling() const                                 {
+        return _prev;
+    }
+
+    XMLNode*   PreviousSibling()                                                       {
+        return _prev;
+    }
+
+    /// Get the previous (left) sibling element of this node, with an optionally supplied name.
+    const XMLElement*  PreviousSiblingElement( const char* value=0 ) const ;
+
+    XMLElement*        PreviousSiblingElement( const char* value=0 ) {
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->PreviousSiblingElement( value ) );
+    }
+
+    /// Get the next (right) sibling node of this node.
+    const XMLNode*     NextSibling() const                                             {
+        return _next;
+    }
+
+    XMLNode*   NextSibling()                                                           {
+        return _next;
+    }
+
+    /// Get the next (right) sibling element of this node, with an optionally supplied name.
+    const XMLElement*  NextSiblingElement( const char* value=0 ) const;
+
+    XMLElement*        NextSiblingElement( const char* value=0 )       {
+        return const_cast<XMLElement*>(const_cast<const XMLNode*>(this)->NextSiblingElement( value ) );
+    }
+
+    /**
+       Add a child node as the last (right) child.
+               If the child node is already part of the document,
+               it is moved from its old location to the new location.
+               Returns the addThis argument or 0 if the node does not
+               belong to the same document.
+    */
+    XMLNode* InsertEndChild( XMLNode* addThis );
+
+    XMLNode* LinkEndChild( XMLNode* addThis )  {
+        return InsertEndChild( addThis );
+    }
+    /**
+       Add a child node as the first (left) child.
+               If the child node is already part of the document,
+               it is moved from its old location to the new location.
+               Returns the addThis argument or 0 if the node does not
+               belong to the same document.
+    */
+    XMLNode* InsertFirstChild( XMLNode* addThis );
+    /**
+       Add a node after the specified child node.
+               If the child node is already part of the document,
+               it is moved from its old location to the new location.
+               Returns the addThis argument or 0 if the afterThis node
+               is not a child of this node, or if the node does not
+               belong to the same document.
+    */
+    XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis );
+
+    /**
+       Delete all the children of this node.
+    */
+    void DeleteChildren();
+
+    /**
+       Delete a child of this node.
+    */
+    void DeleteChild( XMLNode* node );
+
+    /**
+       Make a copy of this node, but not its children.
+       You may pass in a Document pointer that will be
+       the owner of the new Node. If the 'document' is
+       null, then the node returned will be allocated
+       from the current Document. (this->GetDocument())
+
+       Note: if called on a XMLDocument, this will return null.
+    */
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0;
+
+    /**
+       Test if 2 nodes are the same, but don't test children.
+       The 2 nodes do not need to be in the same Document.
+
+       Note: if called on a XMLDocument, this will return false.
+    */
+    virtual bool ShallowEqual( const XMLNode* compare ) const = 0;
+
+    /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the
+       XML tree will be conditionally visited and the host will be called back
+       via the XMLVisitor interface.
+
+       This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse
+       the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this
+       interface versus any other.)
+
+       The interface has been based on ideas from:
+
+       - http://www.saxproject.org/
+       - http://c2.com/cgi/wiki?HierarchicalVisitorPattern
+
+       Which are both good references for "visiting".
+
+       An example of using Accept():
+       @verbatim
+       XMLPrinter printer;
+       tinyxmlDoc.Accept( &printer );
+       const char* xmlcstr = printer.CStr();
+       @endverbatim
+    */
+    virtual bool Accept( XMLVisitor* visitor ) const = 0;
+
+    // internal
+    virtual char* ParseDeep( char*, StrPair* );
+
+protected:
+    XMLNode( XMLDocument* );
+    virtual ~XMLNode();
+
+    XMLDocument*       _document;
+    XMLNode*           _parent;
+    mutable StrPair    _value;
+
+    XMLNode*           _firstChild;
+    XMLNode*           _lastChild;
+
+    XMLNode*           _prev;
+    XMLNode*           _next;
+
+private:
+    MemPool*           _memPool;
+    void Unlink( XMLNode* child );
+    static void DeleteNode( XMLNode* node );
+    void InsertChildPreamble( XMLNode* insertThis ) const;
+
+    XMLNode( const XMLNode& ); // not supported
+    XMLNode& operator=( const XMLNode& );      // not supported
+};
+
+
+/** XML text.
+
+       Note that a text node can have child element nodes, for example:
+       @verbatim
+       <root>This is <b>bold</b></root>
+       @endverbatim
+
+       A text node can have 2 ways to output the next. "normal" output
+       and CDATA. It will default to the mode it was parsed from the XML file and
+       you generally want to leave it alone, but you can change the output mode with
+       SetCData() and query it with CData().
+*/
+class TINYXML2_LIB XMLText : public XMLNode
+{
+    friend class XMLBase;
+    friend class XMLDocument;
+public:
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    virtual XMLText* ToText()                  {
+        return this;
+    }
+    virtual const XMLText* ToText() const      {
+        return this;
+    }
+
+    /// Declare whether this should be CDATA or standard text.
+    void SetCData( bool isCData )                      {
+        _isCData = isCData;
+    }
+    /// Returns true if this is a CDATA text element.
+    bool CData() const                                         {
+        return _isCData;
+    }
+
+    char* ParseDeep( char*, StrPair* endTag );
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    XMLText( XMLDocument* doc )        : XMLNode( doc ), _isCData( false )     {}
+    virtual ~XMLText()                                                                                         {}
+
+private:
+    bool _isCData;
+
+    XMLText( const XMLText& ); // not supported
+    XMLText& operator=( const XMLText& );      // not supported
+};
+
+
+/** An XML Comment. */
+class TINYXML2_LIB XMLComment : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual XMLComment*        ToComment()                                     {
+        return this;
+    }
+    virtual const XMLComment* ToComment() const                {
+        return this;
+    }
+
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    char* ParseDeep( char*, StrPair* endTag );
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    XMLComment( XMLDocument* doc );
+    virtual ~XMLComment();
+
+private:
+    XMLComment( const XMLComment& );   // not supported
+    XMLComment& operator=( const XMLComment& );        // not supported
+};
+
+
+/** In correct XML the declaration is the first entry in the file.
+       @verbatim
+               <?xml version="1.0" standalone="yes"?>
+       @endverbatim
+
+       TinyXML-2 will happily read or write files without a declaration,
+       however.
+
+       The text of the declaration isn't interpreted. It is parsed
+       and written as a string.
+*/
+class TINYXML2_LIB XMLDeclaration : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual XMLDeclaration*    ToDeclaration()                                 {
+        return this;
+    }
+    virtual const XMLDeclaration* ToDeclaration() const                {
+        return this;
+    }
+
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    char* ParseDeep( char*, StrPair* endTag );
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    XMLDeclaration( XMLDocument* doc );
+    virtual ~XMLDeclaration();
+
+private:
+    XMLDeclaration( const XMLDeclaration& );   // not supported
+    XMLDeclaration& operator=( const XMLDeclaration& );        // not supported
+};
+
+
+/** Any tag that TinyXML-2 doesn't recognize is saved as an
+       unknown. It is a tag of text, but should not be modified.
+       It will be written back to the XML, unchanged, when the file
+       is saved.
+
+       DTD tags get thrown into XMLUnknowns.
+*/
+class TINYXML2_LIB XMLUnknown : public XMLNode
+{
+    friend class XMLDocument;
+public:
+    virtual XMLUnknown*        ToUnknown()                                     {
+        return this;
+    }
+    virtual const XMLUnknown* ToUnknown() const                {
+        return this;
+    }
+
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    char* ParseDeep( char*, StrPair* endTag );
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+protected:
+    XMLUnknown( XMLDocument* doc );
+    virtual ~XMLUnknown();
+
+private:
+    XMLUnknown( const XMLUnknown& );   // not supported
+    XMLUnknown& operator=( const XMLUnknown& );        // not supported
+};
+
+
+
+/** An attribute is a name-value pair. Elements have an arbitrary
+       number of attributes, each with a unique name.
+
+       @note The attributes are not XMLNodes. You may only query the
+       Next() attribute in a list.
+*/
+class TINYXML2_LIB XMLAttribute
+{
+    friend class XMLElement;
+public:
+    /// The name of the attribute.
+    const char* Name() const;
+
+    /// The value of the attribute.
+    const char* Value() const;
+
+    /// The next attribute in the list.
+    const XMLAttribute* Next() const {
+        return _next;
+    }
+
+    /** IntValue interprets the attribute as an integer, and returns the value.
+        If the value isn't an integer, 0 will be returned. There is no error checking;
+       use QueryIntValue() if you need error checking.
+    */
+    int                 IntValue() const                               {
+        int i=0;
+        QueryIntValue( &i );
+        return i;
+    }
+    /// Query as an unsigned integer. See IntValue()
+    unsigned UnsignedValue() const                     {
+        unsigned i=0;
+        QueryUnsignedValue( &i );
+        return i;
+    }
+    /// Query as a boolean. See IntValue()
+    bool        BoolValue() const                              {
+        bool b=false;
+        QueryBoolValue( &b );
+        return b;
+    }
+    /// Query as a double. See IntValue()
+    double      DoubleValue() const                    {
+        double d=0;
+        QueryDoubleValue( &d );
+        return d;
+    }
+    /// Query as a float. See IntValue()
+    float       FloatValue() const                             {
+        float f=0;
+        QueryFloatValue( &f );
+        return f;
+    }
+
+    /** QueryIntValue interprets the attribute as an integer, and returns the value
+       in the provided parameter. The function will return XML_NO_ERROR on success,
+       and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful.
+    */
+    XMLError QueryIntValue( int* value ) const;
+    /// See QueryIntValue
+    XMLError QueryUnsignedValue( unsigned int* value ) const;
+    /// See QueryIntValue
+    XMLError QueryBoolValue( bool* value ) const;
+    /// See QueryIntValue
+    XMLError QueryDoubleValue( double* value ) const;
+    /// See QueryIntValue
+    XMLError QueryFloatValue( float* value ) const;
+
+    /// Set the attribute to a string value.
+    void SetAttribute( const char* value );
+    /// Set the attribute to value.
+    void SetAttribute( int value );
+    /// Set the attribute to value.
+    void SetAttribute( unsigned value );
+    /// Set the attribute to value.
+    void SetAttribute( bool value );
+    /// Set the attribute to value.
+    void SetAttribute( double value );
+    /// Set the attribute to value.
+    void SetAttribute( float value );
+
+private:
+    enum { BUF_SIZE = 200 };
+
+    XMLAttribute() : _next( 0 ), _memPool( 0 ) {}
+    virtual ~XMLAttribute()    {}
+
+    XMLAttribute( const XMLAttribute& );       // not supported
+    void operator=( const XMLAttribute& );     // not supported
+    void SetName( const char* name );
+
+    char* ParseDeep( char* p, bool processEntities );
+
+    mutable StrPair _name;
+    mutable StrPair _value;
+    XMLAttribute*   _next;
+    MemPool*        _memPool;
+};
+
+
+/** The element is a container class. It has a value, the element name,
+       and can contain other elements, text, comments, and unknowns.
+       Elements also contain an arbitrary number of attributes.
+*/
+class TINYXML2_LIB XMLElement : public XMLNode
+{
+    friend class XMLBase;
+    friend class XMLDocument;
+public:
+    /// Get the name of an element (which is the Value() of the node.)
+    const char* Name() const           {
+        return Value();
+    }
+    /// Set the name of the element.
+    void SetName( const char* str, bool staticMem=false )      {
+        SetValue( str, staticMem );
+    }
+
+    virtual XMLElement* ToElement()                            {
+        return this;
+    }
+    virtual const XMLElement* ToElement() const {
+        return this;
+    }
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    /** Given an attribute name, Attribute() returns the value
+       for the attribute of that name, or null if none
+       exists. For example:
+
+       @verbatim
+       const char* value = ele->Attribute( "foo" );
+       @endverbatim
+
+       The 'value' parameter is normally null. However, if specified,
+       the attribute will only be returned if the 'name' and 'value'
+       match. This allow you to write code:
+
+       @verbatim
+       if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar();
+       @endverbatim
+
+       rather than:
+       @verbatim
+       if ( ele->Attribute( "foo" ) ) {
+               if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar();
+       }
+       @endverbatim
+    */
+    const char* Attribute( const char* name, const char* value=0 ) const;
+
+    /** Given an attribute name, IntAttribute() returns the value
+       of the attribute interpreted as an integer. 0 will be
+       returned if there is an error. For a method with error
+       checking, see QueryIntAttribute()
+    */
+    int                 IntAttribute( const char* name ) const         {
+        int i=0;
+        QueryIntAttribute( name, &i );
+        return i;
+    }
+    /// See IntAttribute()
+    unsigned UnsignedAttribute( const char* name ) const {
+        unsigned i=0;
+        QueryUnsignedAttribute( name, &i );
+        return i;
+    }
+    /// See IntAttribute()
+    bool        BoolAttribute( const char* name ) const        {
+        bool b=false;
+        QueryBoolAttribute( name, &b );
+        return b;
+    }
+    /// See IntAttribute()
+    double      DoubleAttribute( const char* name ) const      {
+        double d=0;
+        QueryDoubleAttribute( name, &d );
+        return d;
+    }
+    /// See IntAttribute()
+    float       FloatAttribute( const char* name ) const       {
+        float f=0;
+        QueryFloatAttribute( name, &f );
+        return f;
+    }
+
+    /** Given an attribute name, QueryIntAttribute() returns
+       XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion
+       can't be performed, or XML_NO_ATTRIBUTE if the attribute
+       doesn't exist. If successful, the result of the conversion
+       will be written to 'value'. If not successful, nothing will
+       be written to 'value'. This allows you to provide default
+       value:
+
+       @verbatim
+       int value = 10;
+       QueryIntAttribute( "foo", &value );             // if "foo" isn't found, value will still be 10
+       @endverbatim
+    */
+    XMLError QueryIntAttribute( const char* name, int* value ) const                           {
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryIntValue( value );
+    }
+    /// See QueryIntAttribute()
+    XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const     {
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryUnsignedValue( value );
+    }
+    /// See QueryIntAttribute()
+    XMLError QueryBoolAttribute( const char* name, bool* value ) const                         {
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryBoolValue( value );
+    }
+    /// See QueryIntAttribute()
+    XMLError QueryDoubleAttribute( const char* name, double* value ) const                     {
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryDoubleValue( value );
+    }
+    /// See QueryIntAttribute()
+    XMLError QueryFloatAttribute( const char* name, float* value ) const                       {
+        const XMLAttribute* a = FindAttribute( name );
+        if ( !a ) {
+            return XML_NO_ATTRIBUTE;
+        }
+        return a->QueryFloatValue( value );
+    }
+
+       
+    /** Given an attribute name, QueryAttribute() returns
+       XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion
+       can't be performed, or XML_NO_ATTRIBUTE if the attribute
+       doesn't exist. It is overloaded for the primitive types,
+               and is a generally more convenient replacement of
+               QueryIntAttribute() and related functions.
+               
+               If successful, the result of the conversion
+       will be written to 'value'. If not successful, nothing will
+       be written to 'value'. This allows you to provide default
+       value:
+
+       @verbatim
+       int value = 10;
+       QueryAttribute( "foo", &value );                // if "foo" isn't found, value will still be 10
+       @endverbatim
+    */
+       int QueryAttribute( const char* name, int* value ) const {
+               return QueryIntAttribute( name, value );
+       }
+
+       int QueryAttribute( const char* name, unsigned int* value ) const {
+               return QueryUnsignedAttribute( name, value );
+       }
+
+       int QueryAttribute( const char* name, bool* value ) const {
+               return QueryBoolAttribute( name, value );
+       }
+
+       int QueryAttribute( const char* name, double* value ) const {
+               return QueryDoubleAttribute( name, value );
+       }
+
+       int QueryAttribute( const char* name, float* value ) const {
+               return QueryFloatAttribute( name, value );
+       }
+
+       /// Sets the named attribute to value.
+    void SetAttribute( const char* name, const char* value )   {
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, int value )                   {
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, unsigned value )              {
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, bool value )                  {
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, double value )                {
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+    /// Sets the named attribute to value.
+    void SetAttribute( const char* name, float value )         {
+        XMLAttribute* a = FindOrCreateAttribute( name );
+        a->SetAttribute( value );
+    }
+
+    /**
+       Delete an attribute.
+    */
+    void DeleteAttribute( const char* name );
+
+    /// Return the first attribute in the list.
+    const XMLAttribute* FirstAttribute() const {
+        return _rootAttribute;
+    }
+    /// Query a specific attribute in the list.
+    const XMLAttribute* FindAttribute( const char* name ) const;
+
+    /** Convenience function for easy access to the text inside an element. Although easy
+       and concise, GetText() is limited compared to getting the XMLText child
+       and accessing it directly.
+
+       If the first child of 'this' is a XMLText, the GetText()
+       returns the character string of the Text node, else null is returned.
+
+       This is a convenient method for getting the text of simple contained text:
+       @verbatim
+       <foo>This is text</foo>
+               const char* str = fooElement->GetText();
+       @endverbatim
+
+       'str' will be a pointer to "This is text".
+
+       Note that this function can be misleading. If the element foo was created from
+       this XML:
+       @verbatim
+               <foo><b>This is text</b></foo>
+       @endverbatim
+
+       then the value of str would be null. The first child node isn't a text node, it is
+       another element. From this XML:
+       @verbatim
+               <foo>This is <b>text</b></foo>
+       @endverbatim
+       GetText() will return "This is ".
+    */
+    const char* GetText() const;
+
+    /** Convenience function for easy access to the text inside an element. Although easy
+       and concise, SetText() is limited compared to creating an XMLText child
+       and mutating it directly.
+
+       If the first child of 'this' is a XMLText, SetText() sets its value to
+               the given string, otherwise it will create a first child that is an XMLText.
+
+       This is a convenient method for setting the text of simple contained text:
+       @verbatim
+       <foo>This is text</foo>
+               fooElement->SetText( "Hullaballoo!" );
+       <foo>Hullaballoo!</foo>
+               @endverbatim
+
+       Note that this function can be misleading. If the element foo was created from
+       this XML:
+       @verbatim
+               <foo><b>This is text</b></foo>
+       @endverbatim
+
+       then it will not change "This is text", but rather prefix it with a text element:
+       @verbatim
+               <foo>Hullaballoo!<b>This is text</b></foo>
+       @endverbatim
+               
+               For this XML:
+       @verbatim
+               <foo />
+       @endverbatim
+       SetText() will generate
+       @verbatim
+               <foo>Hullaballoo!</foo>
+       @endverbatim
+    */
+       void SetText( const char* inText );
+    /// Convenience method for setting text inside and element. See SetText() for important limitations.
+    void SetText( int value );
+    /// Convenience method for setting text inside and element. See SetText() for important limitations.
+    void SetText( unsigned value );  
+    /// Convenience method for setting text inside and element. See SetText() for important limitations.
+    void SetText( bool value );  
+    /// Convenience method for setting text inside and element. See SetText() for important limitations.
+    void SetText( double value );  
+    /// Convenience method for setting text inside and element. See SetText() for important limitations.
+    void SetText( float value );  
+
+    /**
+       Convenience method to query the value of a child text node. This is probably best
+       shown by example. Given you have a document is this form:
+       @verbatim
+               <point>
+                       <x>1</x>
+                       <y>1.4</y>
+               </point>
+       @endverbatim
+
+       The QueryIntText() and similar functions provide a safe and easier way to get to the
+       "value" of x and y.
+
+       @verbatim
+               int x = 0;
+               float y = 0;    // types of x and y are contrived for example
+               const XMLElement* xElement = pointElement->FirstChildElement( "x" );
+               const XMLElement* yElement = pointElement->FirstChildElement( "y" );
+               xElement->QueryIntText( &x );
+               yElement->QueryFloatText( &y );
+       @endverbatim
+
+       @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted
+                        to the requested type, and XML_NO_TEXT_NODE if there is no child text to query.
+
+    */
+    XMLError QueryIntText( int* ival ) const;
+    /// See QueryIntText()
+    XMLError QueryUnsignedText( unsigned* uval ) const;
+    /// See QueryIntText()
+    XMLError QueryBoolText( bool* bval ) const;
+    /// See QueryIntText()
+    XMLError QueryDoubleText( double* dval ) const;
+    /// See QueryIntText()
+    XMLError QueryFloatText( float* fval ) const;
+
+    // internal:
+    enum {
+        OPEN,          // <foo>
+        CLOSED,                // <foo/>
+        CLOSING                // </foo>
+    };
+    int ClosingType() const {
+        return _closingType;
+    }
+    char* ParseDeep( char* p, StrPair* endTag );
+    virtual XMLNode* ShallowClone( XMLDocument* document ) const;
+    virtual bool ShallowEqual( const XMLNode* compare ) const;
+
+private:
+    XMLElement( XMLDocument* doc );
+    virtual ~XMLElement();
+    XMLElement( const XMLElement& );   // not supported
+    void operator=( const XMLElement& );       // not supported
+
+    XMLAttribute* FindAttribute( const char* name ) {
+        return const_cast<XMLAttribute*>(const_cast<const XMLElement*>(this)->FindAttribute( name ));
+    }
+    XMLAttribute* FindOrCreateAttribute( const char* name );
+    //void LinkAttribute( XMLAttribute* attrib );
+    char* ParseAttributes( char* p );
+    static void DeleteAttribute( XMLAttribute* attribute );
+
+    enum { BUF_SIZE = 200 };
+    int _closingType;
+    // The attribute list is ordered; there is no 'lastAttribute'
+    // because the list needs to be scanned for dupes before adding
+    // a new attribute.
+    XMLAttribute* _rootAttribute;
+};
+
+
+enum Whitespace {
+    PRESERVE_WHITESPACE,
+    COLLAPSE_WHITESPACE
+};
+
+
+/** A Document binds together all the functionality.
+       It can be saved, loaded, and printed to the screen.
+       All Nodes are connected and allocated to a Document.
+       If the Document is deleted, all its Nodes are also deleted.
+*/
+class TINYXML2_LIB XMLDocument : public XMLNode
+{
+    friend class XMLElement;
+public:
+    /// constructor
+    XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE );
+    ~XMLDocument();
+
+    virtual XMLDocument* ToDocument()                          {
+        return this;
+    }
+    virtual const XMLDocument* ToDocument() const      {
+        return this;
+    }
+
+    /**
+       Parse an XML file from a character string.
+       Returns XML_NO_ERROR (0) on success, or
+       an errorID.
+
+       You may optionally pass in the 'nBytes', which is
+       the number of bytes which will be parsed. If not
+       specified, TinyXML-2 will assume 'xml' points to a
+       null terminated string.
+    */
+    XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) );
+
+    /**
+       Load an XML file from disk.
+       Returns XML_NO_ERROR (0) on success, or
+       an errorID.
+    */
+    XMLError LoadFile( const char* filename );
+
+    /**
+       Load an XML file from disk. You are responsible
+       for providing and closing the FILE*. 
+     
+        NOTE: The file should be opened as binary ("rb")
+        not text in order for TinyXML-2 to correctly
+        do newline normalization.
+
+       Returns XML_NO_ERROR (0) on success, or
+       an errorID.
+    */
+    XMLError LoadFile( FILE* );
+
+    /**
+       Save the XML file to disk.
+       Returns XML_NO_ERROR (0) on success, or
+       an errorID.
+    */
+    XMLError SaveFile( const char* filename, bool compact = false );
+
+    /**
+       Save the XML file to disk. You are responsible
+       for providing and closing the FILE*.
+
+       Returns XML_NO_ERROR (0) on success, or
+       an errorID.
+    */
+    XMLError SaveFile( FILE* fp, bool compact = false );
+
+    bool ProcessEntities() const               {
+        return _processEntities;
+    }
+    Whitespace WhitespaceMode() const  {
+        return _whitespace;
+    }
+
+    /**
+       Returns true if this document has a leading Byte Order Mark of UTF8.
+    */
+    bool HasBOM() const {
+        return _writeBOM;
+    }
+    /** Sets whether to write the BOM when writing the file.
+    */
+    void SetBOM( bool useBOM ) {
+        _writeBOM = useBOM;
+    }
+
+    /** Return the root element of DOM. Equivalent to FirstChildElement().
+        To get the first node, use FirstChild().
+    */
+    XMLElement* RootElement()                          {
+        return FirstChildElement();
+    }
+    const XMLElement* RootElement() const      {
+        return FirstChildElement();
+    }
+
+    /** Print the Document. If the Printer is not provided, it will
+        print to stdout. If you provide Printer, this can print to a file:
+       @verbatim
+       XMLPrinter printer( fp );
+       doc.Print( &printer );
+       @endverbatim
+
+       Or you can use a printer to print to memory:
+       @verbatim
+       XMLPrinter printer;
+       doc.Print( &printer );
+       // printer.CStr() has a const char* to the XML
+       @endverbatim
+    */
+    void Print( XMLPrinter* streamer=0 ) const;
+    virtual bool Accept( XMLVisitor* visitor ) const;
+
+    /**
+       Create a new Element associated with
+       this Document. The memory for the Element
+       is managed by the Document.
+    */
+    XMLElement* NewElement( const char* name );
+    /**
+       Create a new Comment associated with
+       this Document. The memory for the Comment
+       is managed by the Document.
+    */
+    XMLComment* NewComment( const char* comment );
+    /**
+       Create a new Text associated with
+       this Document. The memory for the Text
+       is managed by the Document.
+    */
+    XMLText* NewText( const char* text );
+    /**
+       Create a new Declaration associated with
+       this Document. The memory for the object
+       is managed by the Document.
+
+       If the 'text' param is null, the standard
+       declaration is used.:
+       @verbatim
+               <?xml version="1.0" encoding="UTF-8"?>
+       @endverbatim
+    */
+    XMLDeclaration* NewDeclaration( const char* text=0 );
+    /**
+       Create a new Unknown associated with
+       this Document. The memory for the object
+       is managed by the Document.
+    */
+    XMLUnknown* NewUnknown( const char* text );
+
+    /**
+       Delete a node associated with this document.
+       It will be unlinked from the DOM.
+    */
+    void DeleteNode( XMLNode* node );
+
+    void SetError( XMLError error, const char* str1, const char* str2 );
+
+    /// Return true if there was an error parsing the document.
+    bool Error() const {
+        return _errorID != XML_NO_ERROR;
+    }
+    /// Return the errorID.
+    XMLError  ErrorID() const {
+        return _errorID;
+    }
+       const char* ErrorName() const;
+
+    /// Return a possibly helpful diagnostic location or string.
+    const char* GetErrorStr1() const {
+        return _errorStr1;
+    }
+    /// Return a possibly helpful secondary diagnostic location or string.
+    const char* GetErrorStr2() const {
+        return _errorStr2;
+    }
+    /// If there is an error, print it to stdout.
+    void PrintError() const;
+    
+    /// Clear the document, resetting it to the initial state.
+    void Clear();
+
+    // internal
+    char* Identify( char* p, XMLNode** node );
+
+    virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const   {
+        return 0;
+    }
+    virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const      {
+        return false;
+    }
+
+private:
+    XMLDocument( const XMLDocument& ); // not supported
+    void operator=( const XMLDocument& );      // not supported
+
+    bool        _writeBOM;
+    bool        _processEntities;
+    XMLError    _errorID;
+    Whitespace  _whitespace;
+    const char* _errorStr1;
+    const char* _errorStr2;
+    char*       _charBuffer;
+
+    MemPoolT< sizeof(XMLElement) >      _elementPool;
+    MemPoolT< sizeof(XMLAttribute) > _attributePool;
+    MemPoolT< sizeof(XMLText) >                 _textPool;
+    MemPoolT< sizeof(XMLComment) >      _commentPool;
+
+       static const char* _errorNames[XML_ERROR_COUNT];
+
+    void Parse();
+};
+
+
+/**
+       A XMLHandle is a class that wraps a node pointer with null checks; this is
+       an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2
+       DOM structure. It is a separate utility class.
+
+       Take an example:
+       @verbatim
+       <Document>
+               <Element attributeA = "valueA">
+                       <Child attributeB = "value1" />
+                       <Child attributeB = "value2" />
+               </Element>
+       </Document>
+       @endverbatim
+
+       Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
+       easy to write a *lot* of code that looks like:
+
+       @verbatim
+       XMLElement* root = document.FirstChildElement( "Document" );
+       if ( root )
+       {
+               XMLElement* element = root->FirstChildElement( "Element" );
+               if ( element )
+               {
+                       XMLElement* child = element->FirstChildElement( "Child" );
+                       if ( child )
+                       {
+                               XMLElement* child2 = child->NextSiblingElement( "Child" );
+                               if ( child2 )
+                               {
+                                       // Finally do something useful.
+       @endverbatim
+
+       And that doesn't even cover "else" cases. XMLHandle addresses the verbosity
+       of such code. A XMLHandle checks for null pointers so it is perfectly safe
+       and correct to use:
+
+       @verbatim
+       XMLHandle docHandle( &document );
+       XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement();
+       if ( child2 )
+       {
+               // do something useful
+       @endverbatim
+
+       Which is MUCH more concise and useful.
+
+       It is also safe to copy handles - internally they are nothing more than node pointers.
+       @verbatim
+       XMLHandle handleCopy = handle;
+       @endverbatim
+
+       See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects.
+*/
+class TINYXML2_LIB XMLHandle
+{
+public:
+    /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
+    XMLHandle( XMLNode* node )                                                                                         {
+        _node = node;
+    }
+    /// Create a handle from a node.
+    XMLHandle( XMLNode& node )                                                                                         {
+        _node = &node;
+    }
+    /// Copy constructor
+    XMLHandle( const XMLHandle& ref )                                                                          {
+        _node = ref._node;
+    }
+    /// Assignment
+    XMLHandle& operator=( const XMLHandle& ref )                                                       {
+        _node = ref._node;
+        return *this;
+    }
+
+    /// Get the first child of this handle.
+    XMLHandle FirstChild()                                                                                                     {
+        return XMLHandle( _node ? _node->FirstChild() : 0 );
+    }
+    /// Get the first child element of this handle.
+    XMLHandle FirstChildElement( const char* value=0 )                                         {
+        return XMLHandle( _node ? _node->FirstChildElement( value ) : 0 );
+    }
+    /// Get the last child of this handle.
+    XMLHandle LastChild()                                                                                                      {
+        return XMLHandle( _node ? _node->LastChild() : 0 );
+    }
+    /// Get the last child element of this handle.
+    XMLHandle LastChildElement( const char* _value=0 )                                         {
+        return XMLHandle( _node ? _node->LastChildElement( _value ) : 0 );
+    }
+    /// Get the previous sibling of this handle.
+    XMLHandle PreviousSibling()                                                                                                {
+        return XMLHandle( _node ? _node->PreviousSibling() : 0 );
+    }
+    /// Get the previous sibling element of this handle.
+    XMLHandle PreviousSiblingElement( const char* _value=0 )                           {
+        return XMLHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 );
+    }
+    /// Get the next sibling of this handle.
+    XMLHandle NextSibling()                                                                                                    {
+        return XMLHandle( _node ? _node->NextSibling() : 0 );
+    }
+    /// Get the next sibling element of this handle.
+    XMLHandle NextSiblingElement( const char* _value=0 )                                       {
+        return XMLHandle( _node ? _node->NextSiblingElement( _value ) : 0 );
+    }
+
+    /// Safe cast to XMLNode. This can return null.
+    XMLNode* ToNode()                                                  {
+        return _node;
+    }
+    /// Safe cast to XMLElement. This can return null.
+    XMLElement* ToElement()                                    {
+        return ( ( _node == 0 ) ? 0 : _node->ToElement() );
+    }
+    /// Safe cast to XMLText. This can return null.
+    XMLText* ToText()                                                  {
+        return ( ( _node == 0 ) ? 0 : _node->ToText() );
+    }
+    /// Safe cast to XMLUnknown. This can return null.
+    XMLUnknown* ToUnknown()                                    {
+        return ( ( _node == 0 ) ? 0 : _node->ToUnknown() );
+    }
+    /// Safe cast to XMLDeclaration. This can return null.
+    XMLDeclaration* ToDeclaration()                    {
+        return ( ( _node == 0 ) ? 0 : _node->ToDeclaration() );
+    }
+
+private:
+    XMLNode* _node;
+};
+
+
+/**
+       A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the
+       same in all regards, except for the 'const' qualifiers. See XMLHandle for API.
+*/
+class TINYXML2_LIB XMLConstHandle
+{
+public:
+    XMLConstHandle( const XMLNode* node )                                                                                      {
+        _node = node;
+    }
+    XMLConstHandle( const XMLNode& node )                                                                                      {
+        _node = &node;
+    }
+    XMLConstHandle( const XMLConstHandle& ref )                                                                                {
+        _node = ref._node;
+    }
+
+    XMLConstHandle& operator=( const XMLConstHandle& ref )                                                     {
+        _node = ref._node;
+        return *this;
+    }
+
+    const XMLConstHandle FirstChild() const                                                                                    {
+        return XMLConstHandle( _node ? _node->FirstChild() : 0 );
+    }
+    const XMLConstHandle FirstChildElement( const char* value=0 ) const                                {
+        return XMLConstHandle( _node ? _node->FirstChildElement( value ) : 0 );
+    }
+    const XMLConstHandle LastChild()   const                                                                           {
+        return XMLConstHandle( _node ? _node->LastChild() : 0 );
+    }
+    const XMLConstHandle LastChildElement( const char* _value=0 ) const                                {
+        return XMLConstHandle( _node ? _node->LastChildElement( _value ) : 0 );
+    }
+    const XMLConstHandle PreviousSibling() const                                                                       {
+        return XMLConstHandle( _node ? _node->PreviousSibling() : 0 );
+    }
+    const XMLConstHandle PreviousSiblingElement( const char* _value=0 ) const          {
+        return XMLConstHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 );
+    }
+    const XMLConstHandle NextSibling() const                                                                           {
+        return XMLConstHandle( _node ? _node->NextSibling() : 0 );
+    }
+    const XMLConstHandle NextSiblingElement( const char* _value=0 ) const                      {
+        return XMLConstHandle( _node ? _node->NextSiblingElement( _value ) : 0 );
+    }
+
+
+    const XMLNode* ToNode() const                              {
+        return _node;
+    }
+    const XMLElement* ToElement() const                        {
+        return ( ( _node == 0 ) ? 0 : _node->ToElement() );
+    }
+    const XMLText* ToText() const                              {
+        return ( ( _node == 0 ) ? 0 : _node->ToText() );
+    }
+    const XMLUnknown* ToUnknown() const                        {
+        return ( ( _node == 0 ) ? 0 : _node->ToUnknown() );
+    }
+    const XMLDeclaration* ToDeclaration() const        {
+        return ( ( _node == 0 ) ? 0 : _node->ToDeclaration() );
+    }
+
+private:
+    const XMLNode* _node;
+};
+
+
+/**
+       Printing functionality. The XMLPrinter gives you more
+       options than the XMLDocument::Print() method.
+
+       It can:
+       -# Print to memory.
+       -# Print to a file you provide.
+       -# Print XML without a XMLDocument.
+
+       Print to Memory
+
+       @verbatim
+       XMLPrinter printer;
+       doc.Print( &printer );
+       SomeFunction( printer.CStr() );
+       @endverbatim
+
+       Print to a File
+
+       You provide the file pointer.
+       @verbatim
+       XMLPrinter printer( fp );
+       doc.Print( &printer );
+       @endverbatim
+
+       Print without a XMLDocument
+
+       When loading, an XML parser is very useful. However, sometimes
+       when saving, it just gets in the way. The code is often set up
+       for streaming, and constructing the DOM is just overhead.
+
+       The Printer supports the streaming case. The following code
+       prints out a trivially simple XML file without ever creating
+       an XML document.
+
+       @verbatim
+       XMLPrinter printer( fp );
+       printer.OpenElement( "foo" );
+       printer.PushAttribute( "foo", "bar" );
+       printer.CloseElement();
+       @endverbatim
+*/
+class TINYXML2_LIB XMLPrinter : public XMLVisitor
+{
+public:
+    /** Construct the printer. If the FILE* is specified,
+       this will print to the FILE. Else it will print
+       to memory, and the result is available in CStr().
+       If 'compact' is set to true, then output is created
+       with only required whitespace and newlines.
+    */
+    XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 );
+    virtual ~XMLPrinter()      {}
+
+    /** If streaming, write the BOM and declaration. */
+    void PushHeader( bool writeBOM, bool writeDeclaration );
+    /** If streaming, start writing an element.
+        The element must be closed with CloseElement()
+    */
+    void OpenElement( const char* name, bool compactMode=false );
+    /// If streaming, add an attribute to an open element.
+    void PushAttribute( const char* name, const char* value );
+    void PushAttribute( const char* name, int value );
+    void PushAttribute( const char* name, unsigned value );
+    void PushAttribute( const char* name, bool value );
+    void PushAttribute( const char* name, double value );
+    /// If streaming, close the Element.
+    virtual void CloseElement( bool compactMode=false );
+
+    /// Add a text node.
+    void PushText( const char* text, bool cdata=false );
+    /// Add a text node from an integer.
+    void PushText( int value );
+    /// Add a text node from an unsigned.
+    void PushText( unsigned value );
+    /// Add a text node from a bool.
+    void PushText( bool value );
+    /// Add a text node from a float.
+    void PushText( float value );
+    /// Add a text node from a double.
+    void PushText( double value );
+
+    /// Add a comment
+    void PushComment( const char* comment );
+
+    void PushDeclaration( const char* value );
+    void PushUnknown( const char* value );
+
+    virtual bool VisitEnter( const XMLDocument& /*doc*/ );
+    virtual bool VisitExit( const XMLDocument& /*doc*/ )                       {
+        return true;
+    }
+
+    virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute );
+    virtual bool VisitExit( const XMLElement& element );
+
+    virtual bool Visit( const XMLText& text );
+    virtual bool Visit( const XMLComment& comment );
+    virtual bool Visit( const XMLDeclaration& declaration );
+    virtual bool Visit( const XMLUnknown& unknown );
+
+    /**
+       If in print to memory mode, return a pointer to
+       the XML file in memory.
+    */
+    const char* CStr() const {
+        return _buffer.Mem();
+    }
+    /**
+       If in print to memory mode, return the size
+       of the XML file in memory. (Note the size returned
+       includes the terminating null.)
+    */
+    int CStrSize() const {
+        return _buffer.Size();
+    }
+    /**
+       If in print to memory mode, reset the buffer to the
+       beginning.
+    */
+    void ClearBuffer() {
+        _buffer.Clear();
+        _buffer.Push(0);
+    }
+
+protected:
+       virtual bool CompactMode( const XMLElement& )   { return _compactMode; }
+
+       /** Prints out the space before an element. You may override to change
+           the space and tabs used. A PrintSpace() override should call Print().
+       */
+    virtual void PrintSpace( int depth );
+    void Print( const char* format, ... );
+
+    void SealElementIfJustOpened();
+    bool _elementJustOpened;
+    DynArray< const char*, 10 > _stack;
+
+private:
+    void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities.
+
+    bool _firstElement;
+    FILE* _fp;
+    int _depth;
+    int _textDepth;
+    bool _processEntities;
+       bool _compactMode;
+
+    enum {
+        ENTITY_RANGE = 64,
+        BUF_SIZE = 200
+    };
+    bool _entityFlag[ENTITY_RANGE];
+    bool _restrictedEntityFlag[ENTITY_RANGE];
+
+    DynArray< char, 20 > _buffer;
+};
+
+
+}      // tinyxml2
+
+#if defined(_MSC_VER)
+#   pragma warning(pop)
+#endif
+
+#endif // TINYXML2_INCLUDED
diff --git a/src/include/gromacs/external/tng_io/external/zlib/crc32.h b/src/include/gromacs/external/tng_io/external/zlib/crc32.h
new file mode 100644 (file)
index 0000000..9e0c778
--- /dev/null
@@ -0,0 +1,441 @@
+/* crc32.h -- tables for rapid CRC calculation
+ * Generated automatically by crc32.c
+ */
+
+local const z_crc_t FAR crc_table[TBLS][256] =
+{
+  {
+    0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL,
+    0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL,
+    0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL,
+    0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL,
+    0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL,
+    0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL,
+    0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL,
+    0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL,
+    0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL,
+    0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL,
+    0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL,
+    0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL,
+    0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL,
+    0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL,
+    0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL,
+    0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL,
+    0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL,
+    0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL,
+    0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL,
+    0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL,
+    0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL,
+    0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL,
+    0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL,
+    0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL,
+    0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL,
+    0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL,
+    0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL,
+    0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL,
+    0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL,
+    0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL,
+    0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL,
+    0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL,
+    0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL,
+    0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL,
+    0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL,
+    0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL,
+    0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL,
+    0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL,
+    0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL,
+    0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL,
+    0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL,
+    0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL,
+    0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL,
+    0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL,
+    0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL,
+    0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL,
+    0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL,
+    0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL,
+    0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL,
+    0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL,
+    0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL,
+    0x2d02ef8dUL
+#ifdef BYFOUR
+  },
+  {
+    0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL,
+    0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL,
+    0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL,
+    0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL,
+    0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL,
+    0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL,
+    0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL,
+    0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL,
+    0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL,
+    0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL,
+    0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL,
+    0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL,
+    0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL,
+    0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL,
+    0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL,
+    0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL,
+    0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL,
+    0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL,
+    0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL,
+    0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL,
+    0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL,
+    0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL,
+    0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL,
+    0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL,
+    0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL,
+    0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL,
+    0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL,
+    0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL,
+    0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL,
+    0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL,
+    0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL,
+    0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL,
+    0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL,
+    0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL,
+    0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL,
+    0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL,
+    0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL,
+    0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL,
+    0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL,
+    0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL,
+    0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL,
+    0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL,
+    0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL,
+    0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL,
+    0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL,
+    0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL,
+    0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL,
+    0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL,
+    0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL,
+    0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL,
+    0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL,
+    0x9324fd72UL
+  },
+  {
+    0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL,
+    0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL,
+    0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL,
+    0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL,
+    0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL,
+    0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL,
+    0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL,
+    0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL,
+    0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL,
+    0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL,
+    0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL,
+    0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL,
+    0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL,
+    0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL,
+    0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL,
+    0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL,
+    0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL,
+    0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL,
+    0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL,
+    0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL,
+    0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL,
+    0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL,
+    0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL,
+    0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL,
+    0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL,
+    0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL,
+    0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL,
+    0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL,
+    0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL,
+    0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL,
+    0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL,
+    0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL,
+    0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL,
+    0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL,
+    0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL,
+    0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL,
+    0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL,
+    0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL,
+    0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL,
+    0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL,
+    0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL,
+    0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL,
+    0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL,
+    0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL,
+    0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL,
+    0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL,
+    0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL,
+    0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL,
+    0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL,
+    0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL,
+    0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL,
+    0xbe9834edUL
+  },
+  {
+    0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL,
+    0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL,
+    0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL,
+    0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL,
+    0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL,
+    0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL,
+    0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL,
+    0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL,
+    0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL,
+    0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL,
+    0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL,
+    0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL,
+    0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL,
+    0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL,
+    0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL,
+    0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL,
+    0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL,
+    0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL,
+    0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL,
+    0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL,
+    0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL,
+    0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL,
+    0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL,
+    0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL,
+    0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL,
+    0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL,
+    0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL,
+    0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL,
+    0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL,
+    0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL,
+    0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL,
+    0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL,
+    0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL,
+    0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL,
+    0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL,
+    0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL,
+    0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL,
+    0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL,
+    0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL,
+    0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL,
+    0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL,
+    0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL,
+    0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL,
+    0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL,
+    0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL,
+    0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL,
+    0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL,
+    0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL,
+    0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL,
+    0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL,
+    0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL,
+    0xde0506f1UL
+  },
+  {
+    0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL,
+    0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL,
+    0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL,
+    0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL,
+    0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL,
+    0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL,
+    0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL,
+    0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL,
+    0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL,
+    0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL,
+    0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL,
+    0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL,
+    0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL,
+    0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL,
+    0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL,
+    0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL,
+    0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL,
+    0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL,
+    0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL,
+    0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL,
+    0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL,
+    0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL,
+    0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL,
+    0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL,
+    0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL,
+    0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL,
+    0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL,
+    0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL,
+    0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL,
+    0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL,
+    0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL,
+    0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL,
+    0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL,
+    0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL,
+    0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL,
+    0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL,
+    0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL,
+    0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL,
+    0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL,
+    0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL,
+    0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL,
+    0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL,
+    0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL,
+    0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL,
+    0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL,
+    0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL,
+    0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL,
+    0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL,
+    0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL,
+    0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL,
+    0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL,
+    0x8def022dUL
+  },
+  {
+    0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL,
+    0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL,
+    0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL,
+    0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL,
+    0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL,
+    0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL,
+    0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL,
+    0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL,
+    0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL,
+    0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL,
+    0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL,
+    0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL,
+    0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL,
+    0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL,
+    0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL,
+    0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL,
+    0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL,
+    0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL,
+    0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL,
+    0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL,
+    0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL,
+    0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL,
+    0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL,
+    0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL,
+    0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL,
+    0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL,
+    0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL,
+    0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL,
+    0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL,
+    0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL,
+    0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL,
+    0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL,
+    0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL,
+    0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL,
+    0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL,
+    0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL,
+    0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL,
+    0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL,
+    0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL,
+    0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL,
+    0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL,
+    0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL,
+    0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL,
+    0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL,
+    0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL,
+    0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL,
+    0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL,
+    0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL,
+    0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL,
+    0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL,
+    0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL,
+    0x72fd2493UL
+  },
+  {
+    0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL,
+    0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL,
+    0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL,
+    0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL,
+    0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL,
+    0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL,
+    0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL,
+    0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL,
+    0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL,
+    0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL,
+    0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL,
+    0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL,
+    0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL,
+    0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL,
+    0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL,
+    0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL,
+    0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL,
+    0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL,
+    0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL,
+    0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL,
+    0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL,
+    0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL,
+    0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL,
+    0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL,
+    0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL,
+    0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL,
+    0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL,
+    0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL,
+    0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL,
+    0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL,
+    0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL,
+    0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL,
+    0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL,
+    0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL,
+    0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL,
+    0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL,
+    0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL,
+    0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL,
+    0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL,
+    0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL,
+    0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL,
+    0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL,
+    0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL,
+    0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL,
+    0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL,
+    0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL,
+    0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL,
+    0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL,
+    0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL,
+    0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL,
+    0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL,
+    0xed3498beUL
+  },
+  {
+    0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL,
+    0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL,
+    0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL,
+    0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL,
+    0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL,
+    0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL,
+    0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL,
+    0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL,
+    0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL,
+    0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL,
+    0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL,
+    0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL,
+    0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL,
+    0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL,
+    0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL,
+    0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL,
+    0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL,
+    0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL,
+    0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL,
+    0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL,
+    0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL,
+    0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL,
+    0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL,
+    0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL,
+    0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL,
+    0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL,
+    0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL,
+    0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL,
+    0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL,
+    0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL,
+    0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL,
+    0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL,
+    0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL,
+    0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL,
+    0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL,
+    0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL,
+    0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL,
+    0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL,
+    0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL,
+    0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL,
+    0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL,
+    0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL,
+    0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL,
+    0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL,
+    0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL,
+    0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL,
+    0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL,
+    0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL,
+    0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL,
+    0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL,
+    0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL,
+    0xf10605deUL
+#endif
+  }
+};
diff --git a/src/include/gromacs/external/tng_io/external/zlib/deflate.h b/src/include/gromacs/external/tng_io/external/zlib/deflate.h
new file mode 100644 (file)
index 0000000..ce0299e
--- /dev/null
@@ -0,0 +1,346 @@
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995-2012 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef DEFLATE_H
+#define DEFLATE_H
+
+#include "zutil.h"
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+   trailer creation by deflate().  NO_GZIP would be used to avoid linking in
+   the crc code when it is not needed.  For shared libraries, gzip encoding
+   should be left enabled. */
+#ifndef NO_GZIP
+#  define GZIP
+#endif
+
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+#define LENGTH_CODES 29
+/* number of length codes, not counting the special END_BLOCK code */
+
+#define LITERALS  256
+/* number of literal bytes 0..255 */
+
+#define L_CODES (LITERALS+1+LENGTH_CODES)
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+#define D_CODES   30
+/* number of distance codes */
+
+#define BL_CODES  19
+/* number of codes used to transfer the bit lengths */
+
+#define HEAP_SIZE (2*L_CODES+1)
+/* maximum heap size */
+
+#define MAX_BITS 15
+/* All codes must not exceed MAX_BITS bits */
+
+#define Buf_size 16
+/* size of bit buffer in bi_buf */
+
+#define INIT_STATE    42
+#define EXTRA_STATE   69
+#define NAME_STATE    73
+#define COMMENT_STATE 91
+#define HCRC_STATE   103
+#define BUSY_STATE   113
+#define FINISH_STATE 666
+/* Stream status */
+
+
+/* Data structure describing a single value and its code string. */
+typedef struct ct_data_s {
+    union {
+        ush  freq;       /* frequency count */
+        ush  code;       /* bit string */
+    } fc;
+    union {
+        ush  dad;        /* father node in Huffman tree */
+        ush  len;        /* length of bit string */
+    } dl;
+} FAR ct_data;
+
+#define Freq fc.freq
+#define Code fc.code
+#define Dad  dl.dad
+#define Len  dl.len
+
+typedef struct static_tree_desc_s  static_tree_desc;
+
+typedef struct tree_desc_s {
+    ct_data *dyn_tree;           /* the dynamic tree */
+    int     max_code;            /* largest code with non zero frequency */
+    static_tree_desc *stat_desc; /* the corresponding static tree */
+} FAR tree_desc;
+
+typedef ush Pos;
+typedef Pos FAR Posf;
+typedef unsigned IPos;
+
+/* A Pos is an index in the character window. We use short instead of int to
+ * save space in the various tables. IPos is used only for parameter passing.
+ */
+
+typedef struct internal_state {
+    z_streamp strm;      /* pointer back to this zlib stream */
+    int   status;        /* as the name implies */
+    Bytef *pending_buf;  /* output still pending */
+    ulg   pending_buf_size; /* size of pending_buf */
+    Bytef *pending_out;  /* next pending byte to output to the stream */
+    uInt   pending;      /* nb of bytes in the pending buffer */
+    int   wrap;          /* bit 0 true for zlib, bit 1 true for gzip */
+    gz_headerp  gzhead;  /* gzip header information to write */
+    uInt   gzindex;      /* where in extra, name, or comment */
+    Byte  method;        /* can only be DEFLATED */
+    int   last_flush;    /* value of flush param for previous deflate call */
+
+                /* used by deflate.c: */
+
+    uInt  w_size;        /* LZ77 window size (32K by default) */
+    uInt  w_bits;        /* log2(w_size)  (8..16) */
+    uInt  w_mask;        /* w_size - 1 */
+
+    Bytef *window;
+    /* Sliding window. Input bytes are read into the second half of the window,
+     * and move to the first half later to keep a dictionary of at least wSize
+     * bytes. With this organization, matches are limited to a distance of
+     * wSize-MAX_MATCH bytes, but this ensures that IO is always
+     * performed with a length multiple of the block size. Also, it limits
+     * the window size to 64K, which is quite useful on MSDOS.
+     * To do: use the user input buffer as sliding window.
+     */
+
+    ulg window_size;
+    /* Actual size of window: 2*wSize, except when the user input buffer
+     * is directly used as sliding window.
+     */
+
+    Posf *prev;
+    /* Link to older string with same hash index. To limit the size of this
+     * array to 64K, this link is maintained only for the last 32K strings.
+     * An index in this array is thus a window index modulo 32K.
+     */
+
+    Posf *head; /* Heads of the hash chains or NIL. */
+
+    uInt  ins_h;          /* hash index of string to be inserted */
+    uInt  hash_size;      /* number of elements in hash table */
+    uInt  hash_bits;      /* log2(hash_size) */
+    uInt  hash_mask;      /* hash_size-1 */
+
+    uInt  hash_shift;
+    /* Number of bits by which ins_h must be shifted at each input
+     * step. It must be such that after MIN_MATCH steps, the oldest
+     * byte no longer takes part in the hash key, that is:
+     *   hash_shift * MIN_MATCH >= hash_bits
+     */
+
+    long block_start;
+    /* Window position at the beginning of the current output block. Gets
+     * negative when the window is moved backwards.
+     */
+
+    uInt match_length;           /* length of best match */
+    IPos prev_match;             /* previous match */
+    int match_available;         /* set if previous match exists */
+    uInt strstart;               /* start of string to insert */
+    uInt match_start;            /* start of matching string */
+    uInt lookahead;              /* number of valid bytes ahead in window */
+
+    uInt prev_length;
+    /* Length of the best match at previous step. Matches not greater than this
+     * are discarded. This is used in the lazy match evaluation.
+     */
+
+    uInt max_chain_length;
+    /* To speed up deflation, hash chains are never searched beyond this
+     * length.  A higher limit improves compression ratio but degrades the
+     * speed.
+     */
+
+    uInt max_lazy_match;
+    /* Attempt to find a better match only when the current match is strictly
+     * smaller than this value. This mechanism is used only for compression
+     * levels >= 4.
+     */
+#   define max_insert_length  max_lazy_match
+    /* Insert new strings in the hash table only if the match length is not
+     * greater than this length. This saves time but degrades compression.
+     * max_insert_length is used only for compression levels <= 3.
+     */
+
+    int level;    /* compression level (1..9) */
+    int strategy; /* favor or force Huffman coding*/
+
+    uInt good_match;
+    /* Use a faster search when the previous match is longer than this */
+
+    int nice_match; /* Stop searching when current match exceeds this */
+
+                /* used by trees.c: */
+    /* Didn't use ct_data typedef below to suppress compiler warning */
+    struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */
+    struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+    struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */
+
+    struct tree_desc_s l_desc;               /* desc. for literal tree */
+    struct tree_desc_s d_desc;               /* desc. for distance tree */
+    struct tree_desc_s bl_desc;              /* desc. for bit length tree */
+
+    ush bl_count[MAX_BITS+1];
+    /* number of codes at each bit length for an optimal tree */
+
+    int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */
+    int heap_len;               /* number of elements in the heap */
+    int heap_max;               /* element of largest frequency */
+    /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+     * The same heap array is used to build all trees.
+     */
+
+    uch depth[2*L_CODES+1];
+    /* Depth of each subtree used as tie breaker for trees of equal frequency
+     */
+
+    uchf *l_buf;          /* buffer for literals or lengths */
+
+    uInt  lit_bufsize;
+    /* Size of match buffer for literals/lengths.  There are 4 reasons for
+     * limiting lit_bufsize to 64K:
+     *   - frequencies can be kept in 16 bit counters
+     *   - if compression is not successful for the first block, all input
+     *     data is still in the window so we can still emit a stored block even
+     *     when input comes from standard input.  (This can also be done for
+     *     all blocks if lit_bufsize is not greater than 32K.)
+     *   - if compression is not successful for a file smaller than 64K, we can
+     *     even emit a stored file instead of a stored block (saving 5 bytes).
+     *     This is applicable only for zip (not gzip or zlib).
+     *   - creating new Huffman trees less frequently may not provide fast
+     *     adaptation to changes in the input data statistics. (Take for
+     *     example a binary file with poorly compressible code followed by
+     *     a highly compressible string table.) Smaller buffer sizes give
+     *     fast adaptation but have of course the overhead of transmitting
+     *     trees more frequently.
+     *   - I can't count above 4
+     */
+
+    uInt last_lit;      /* running index in l_buf */
+
+    ushf *d_buf;
+    /* Buffer for distances. To simplify the code, d_buf and l_buf have
+     * the same number of elements. To use different lengths, an extra flag
+     * array would be necessary.
+     */
+
+    ulg opt_len;        /* bit length of current block with optimal trees */
+    ulg static_len;     /* bit length of current block with static trees */
+    uInt matches;       /* number of string matches in current block */
+    uInt insert;        /* bytes at end of window left to insert */
+
+#ifdef DEBUG
+    ulg compressed_len; /* total bit length of compressed file mod 2^32 */
+    ulg bits_sent;      /* bit length of compressed data sent mod 2^32 */
+#endif
+
+    ush bi_buf;
+    /* Output buffer. bits are inserted starting at the bottom (least
+     * significant bits).
+     */
+    int bi_valid;
+    /* Number of valid bits in bi_buf.  All bits above the last valid bit
+     * are always zero.
+     */
+
+    ulg high_water;
+    /* High water mark offset in window for initialized bytes -- bytes above
+     * this are set to zero in order to avoid memory check warnings when
+     * longest match routines access bytes past the input.  This is then
+     * updated to the new high water mark.
+     */
+
+} FAR deflate_state;
+
+/* Output a byte on the stream.
+ * IN assertion: there is enough room in pending_buf.
+ */
+#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);}
+
+
+#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1)
+/* Minimum amount of lookahead, except at the end of the input file.
+ * See deflate.c for comments about the MIN_MATCH+1.
+ */
+
+#define MAX_DIST(s)  ((s)->w_size-MIN_LOOKAHEAD)
+/* In order to simplify the code, particularly on 16 bit machines, match
+ * distances are limited to MAX_DIST instead of WSIZE.
+ */
+
+#define WIN_INIT MAX_MATCH
+/* Number of bytes after end of data in window to initialize in order to avoid
+   memory checker errors from longest match routines */
+
+        /* in trees.c */
+void ZLIB_INTERNAL _tr_init OF((deflate_state *s));
+int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc));
+void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf,
+                        ulg stored_len, int last));
+void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s));
+void ZLIB_INTERNAL _tr_align OF((deflate_state *s));
+void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf,
+                        ulg stored_len, int last));
+
+#define d_code(dist) \
+   ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)])
+/* Mapping from a distance to a distance code. dist is the distance - 1 and
+ * must not have side effects. _dist_code[256] and _dist_code[257] are never
+ * used.
+ */
+
+#ifndef DEBUG
+/* Inline versions of _tr_tally for speed: */
+
+#if defined(GEN_TREES_H) || !defined(STDC)
+  extern uch ZLIB_INTERNAL _length_code[];
+  extern uch ZLIB_INTERNAL _dist_code[];
+#else
+  extern const uch ZLIB_INTERNAL _length_code[];
+  extern const uch ZLIB_INTERNAL _dist_code[];
+#endif
+
+# define _tr_tally_lit(s, c, flush) \
+  { uch cc = (c); \
+    s->d_buf[s->last_lit] = 0; \
+    s->l_buf[s->last_lit++] = cc; \
+    s->dyn_ltree[cc].Freq++; \
+    flush = (s->last_lit == s->lit_bufsize-1); \
+   }
+# define _tr_tally_dist(s, distance, length, flush) \
+  { uch len = (length); \
+    ush dist = (distance); \
+    s->d_buf[s->last_lit] = dist; \
+    s->l_buf[s->last_lit++] = len; \
+    dist--; \
+    s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \
+    s->dyn_dtree[d_code(dist)].Freq++; \
+    flush = (s->last_lit == s->lit_bufsize-1); \
+  }
+#else
+# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c)
+# define _tr_tally_dist(s, distance, length, flush) \
+              flush = _tr_tally(s, distance, length)
+#endif
+
+#endif /* DEFLATE_H */
diff --git a/src/include/gromacs/external/tng_io/external/zlib/inffast.h b/src/include/gromacs/external/tng_io/external/zlib/inffast.h
new file mode 100644 (file)
index 0000000..e5c1aa4
--- /dev/null
@@ -0,0 +1,11 @@
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995-2003, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+void ZLIB_INTERNAL inflate_fast OF((z_streamp strm, unsigned start));
diff --git a/src/include/gromacs/external/tng_io/external/zlib/inffixed.h b/src/include/gromacs/external/tng_io/external/zlib/inffixed.h
new file mode 100644 (file)
index 0000000..d628327
--- /dev/null
@@ -0,0 +1,94 @@
+    /* inffixed.h -- table for decoding fixed codes
+     * Generated automatically by makefixed().
+     */
+
+    /* WARNING: this file should *not* be used by applications.
+       It is part of the implementation of this library and is
+       subject to change. Applications should only use zlib.h.
+     */
+
+    static const code lenfix[512] = {
+        {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48},
+        {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128},
+        {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59},
+        {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176},
+        {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20},
+        {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100},
+        {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8},
+        {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216},
+        {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76},
+        {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114},
+        {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2},
+        {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148},
+        {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42},
+        {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86},
+        {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15},
+        {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236},
+        {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62},
+        {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142},
+        {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31},
+        {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162},
+        {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25},
+        {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105},
+        {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4},
+        {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202},
+        {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69},
+        {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125},
+        {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13},
+        {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195},
+        {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35},
+        {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91},
+        {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19},
+        {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246},
+        {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55},
+        {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135},
+        {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99},
+        {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190},
+        {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16},
+        {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96},
+        {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6},
+        {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209},
+        {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72},
+        {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116},
+        {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4},
+        {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153},
+        {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44},
+        {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82},
+        {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11},
+        {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229},
+        {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58},
+        {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138},
+        {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51},
+        {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173},
+        {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30},
+        {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110},
+        {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0},
+        {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195},
+        {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65},
+        {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121},
+        {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9},
+        {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258},
+        {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37},
+        {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93},
+        {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23},
+        {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251},
+        {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51},
+        {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131},
+        {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67},
+        {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183},
+        {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23},
+        {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103},
+        {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9},
+        {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223},
+        {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79},
+        {0,9,255}
+    };
+
+    static const code distfix[32] = {
+        {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025},
+        {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193},
+        {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385},
+        {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577},
+        {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073},
+        {22,5,193},{64,5,0}
+    };
diff --git a/src/include/gromacs/external/tng_io/external/zlib/inflate.h b/src/include/gromacs/external/tng_io/external/zlib/inflate.h
new file mode 100644 (file)
index 0000000..95f4986
--- /dev/null
@@ -0,0 +1,122 @@
+/* inflate.h -- internal inflate state definition
+ * Copyright (C) 1995-2009 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* define NO_GZIP when compiling if you want to disable gzip header and
+   trailer decoding by inflate().  NO_GZIP would be used to avoid linking in
+   the crc code when it is not needed.  For shared libraries, gzip decoding
+   should be left enabled. */
+#ifndef NO_GZIP
+#  define GUNZIP
+#endif
+
+/* Possible inflate modes between inflate() calls */
+typedef enum {
+    HEAD,       /* i: waiting for magic header */
+    FLAGS,      /* i: waiting for method and flags (gzip) */
+    TIME,       /* i: waiting for modification time (gzip) */
+    OS,         /* i: waiting for extra flags and operating system (gzip) */
+    EXLEN,      /* i: waiting for extra length (gzip) */
+    EXTRA,      /* i: waiting for extra bytes (gzip) */
+    NAME,       /* i: waiting for end of file name (gzip) */
+    COMMENT,    /* i: waiting for end of comment (gzip) */
+    HCRC,       /* i: waiting for header crc (gzip) */
+    DICTID,     /* i: waiting for dictionary check value */
+    DICT,       /* waiting for inflateSetDictionary() call */
+        TYPE,       /* i: waiting for type bits, including last-flag bit */
+        TYPEDO,     /* i: same, but skip check to exit inflate on new block */
+        STORED,     /* i: waiting for stored size (length and complement) */
+        COPY_,      /* i/o: same as COPY below, but only first time in */
+        COPY,       /* i/o: waiting for input or output to copy stored block */
+        TABLE,      /* i: waiting for dynamic block table lengths */
+        LENLENS,    /* i: waiting for code length code lengths */
+        CODELENS,   /* i: waiting for length/lit and distance code lengths */
+            LEN_,       /* i: same as LEN below, but only first time in */
+            LEN,        /* i: waiting for length/lit/eob code */
+            LENEXT,     /* i: waiting for length extra bits */
+            DIST,       /* i: waiting for distance code */
+            DISTEXT,    /* i: waiting for distance extra bits */
+            MATCH,      /* o: waiting for output space to copy string */
+            LIT,        /* o: waiting for output space to write literal */
+    CHECK,      /* i: waiting for 32-bit check value */
+    LENGTH,     /* i: waiting for 32-bit length (gzip) */
+    DONE,       /* finished check, done -- remain here until reset */
+    BAD,        /* got a data error -- remain here until reset */
+    MEM,        /* got an inflate() memory error -- remain here until reset */
+    SYNC        /* looking for synchronization bytes to restart inflate() */
+} inflate_mode;
+
+/*
+    State transitions between above modes -
+
+    (most modes can go to BAD or MEM on error -- not shown for clarity)
+
+    Process header:
+        HEAD -> (gzip) or (zlib) or (raw)
+        (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME -> COMMENT ->
+                  HCRC -> TYPE
+        (zlib) -> DICTID or TYPE
+        DICTID -> DICT -> TYPE
+        (raw) -> TYPEDO
+    Read deflate blocks:
+            TYPE -> TYPEDO -> STORED or TABLE or LEN_ or CHECK
+            STORED -> COPY_ -> COPY -> TYPE
+            TABLE -> LENLENS -> CODELENS -> LEN_
+            LEN_ -> LEN
+    Read deflate codes in fixed or dynamic block:
+                LEN -> LENEXT or LIT or TYPE
+                LENEXT -> DIST -> DISTEXT -> MATCH -> LEN
+                LIT -> LEN
+    Process trailer:
+        CHECK -> LENGTH -> DONE
+ */
+
+/* state maintained between inflate() calls.  Approximately 10K bytes. */
+struct inflate_state {
+    inflate_mode mode;          /* current inflate mode */
+    int last;                   /* true if processing last block */
+    int wrap;                   /* bit 0 true for zlib, bit 1 true for gzip */
+    int havedict;               /* true if dictionary provided */
+    int flags;                  /* gzip header method and flags (0 if zlib) */
+    unsigned dmax;              /* zlib header max distance (INFLATE_STRICT) */
+    unsigned long check;        /* protected copy of check value */
+    unsigned long total;        /* protected copy of output count */
+    gz_headerp head;            /* where to save gzip header information */
+        /* sliding window */
+    unsigned wbits;             /* log base 2 of requested window size */
+    unsigned wsize;             /* window size or zero if not using window */
+    unsigned whave;             /* valid bytes in the window */
+    unsigned wnext;             /* window write index */
+    unsigned char FAR *window;  /* allocated sliding window, if needed */
+        /* bit accumulator */
+    unsigned long hold;         /* input bit accumulator */
+    unsigned bits;              /* number of bits in "in" */
+        /* for string and stored block copying */
+    unsigned length;            /* literal or length of data to copy */
+    unsigned offset;            /* distance back to copy string from */
+        /* for table and code decoding */
+    unsigned extra;             /* extra bits needed */
+        /* fixed and dynamic code tables */
+    code const FAR *lencode;    /* starting table for length/literal codes */
+    code const FAR *distcode;   /* starting table for distance codes */
+    unsigned lenbits;           /* index bits for lencode */
+    unsigned distbits;          /* index bits for distcode */
+        /* dynamic table building */
+    unsigned ncode;             /* number of code length code lengths */
+    unsigned nlen;              /* number of length code lengths */
+    unsigned ndist;             /* number of distance code lengths */
+    unsigned have;              /* number of code lengths in lens[] */
+    code FAR *next;             /* next available space in codes[] */
+    unsigned short lens[320];   /* temporary storage for code lengths */
+    unsigned short work[288];   /* work area for code table building */
+    code codes[ENOUGH];         /* space for code tables */
+    int sane;                   /* if false, allow invalid distance too far */
+    int back;                   /* bits back of last unprocessed length/lit */
+    unsigned was;               /* initial length of match */
+};
diff --git a/src/include/gromacs/external/tng_io/external/zlib/inftrees.h b/src/include/gromacs/external/tng_io/external/zlib/inftrees.h
new file mode 100644 (file)
index 0000000..baa53a0
--- /dev/null
@@ -0,0 +1,62 @@
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995-2005, 2010 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* Structure for decoding tables.  Each entry provides either the
+   information needed to do the operation requested by the code that
+   indexed that table entry, or it provides a pointer to another
+   table that indexes more bits of the code.  op indicates whether
+   the entry is a pointer to another table, a literal, a length or
+   distance, an end-of-block, or an invalid code.  For a table
+   pointer, the low four bits of op is the number of index bits of
+   that table.  For a length or distance, the low four bits of op
+   is the number of extra bits to get after the code.  bits is
+   the number of bits in this code or part of the code to drop off
+   of the bit buffer.  val is the actual byte to output in the case
+   of a literal, the base length or distance, or the offset from
+   the current table to the next table.  Each entry is four bytes. */
+typedef struct {
+    unsigned char op;           /* operation, extra bits, table bits */
+    unsigned char bits;         /* bits in this part of the code */
+    unsigned short val;         /* offset in table or code value */
+} code;
+
+/* op values as set by inflate_table():
+    00000000 - literal
+    0000tttt - table link, tttt != 0 is the number of table index bits
+    0001eeee - length or distance, eeee is the number of extra bits
+    01100000 - end of block
+    01000000 - invalid code
+ */
+
+/* Maximum size of the dynamic table.  The maximum number of code structures is
+   1444, which is the sum of 852 for literal/length codes and 592 for distance
+   codes.  These values were found by exhaustive searches using the program
+   examples/enough.c found in the zlib distribtution.  The arguments to that
+   program are the number of symbols, the initial root table size, and the
+   maximum bit length of a code.  "enough 286 9 15" for literal/length codes
+   returns returns 852, and "enough 30 6 15" for distance codes returns 592.
+   The initial root table size (9 or 6) is found in the fifth argument of the
+   inflate_table() calls in inflate.c and infback.c.  If the root table size is
+   changed, then these maximum sizes would be need to be recalculated and
+   updated. */
+#define ENOUGH_LENS 852
+#define ENOUGH_DISTS 592
+#define ENOUGH (ENOUGH_LENS+ENOUGH_DISTS)
+
+/* Type of code to build for inflate_table() */
+typedef enum {
+    CODES,
+    LENS,
+    DISTS
+} codetype;
+
+int ZLIB_INTERNAL inflate_table OF((codetype type, unsigned short FAR *lens,
+                             unsigned codes, code FAR * FAR *table,
+                             unsigned FAR *bits, unsigned short FAR *work));
diff --git a/src/include/gromacs/external/tng_io/external/zlib/trees.h b/src/include/gromacs/external/tng_io/external/zlib/trees.h
new file mode 100644 (file)
index 0000000..d35639d
--- /dev/null
@@ -0,0 +1,128 @@
+/* header created automatically with -DGEN_TREES_H */
+
+local const ct_data static_ltree[L_CODES+2] = {
+{{ 12},{  8}}, {{140},{  8}}, {{ 76},{  8}}, {{204},{  8}}, {{ 44},{  8}},
+{{172},{  8}}, {{108},{  8}}, {{236},{  8}}, {{ 28},{  8}}, {{156},{  8}},
+{{ 92},{  8}}, {{220},{  8}}, {{ 60},{  8}}, {{188},{  8}}, {{124},{  8}},
+{{252},{  8}}, {{  2},{  8}}, {{130},{  8}}, {{ 66},{  8}}, {{194},{  8}},
+{{ 34},{  8}}, {{162},{  8}}, {{ 98},{  8}}, {{226},{  8}}, {{ 18},{  8}},
+{{146},{  8}}, {{ 82},{  8}}, {{210},{  8}}, {{ 50},{  8}}, {{178},{  8}},
+{{114},{  8}}, {{242},{  8}}, {{ 10},{  8}}, {{138},{  8}}, {{ 74},{  8}},
+{{202},{  8}}, {{ 42},{  8}}, {{170},{  8}}, {{106},{  8}}, {{234},{  8}},
+{{ 26},{  8}}, {{154},{  8}}, {{ 90},{  8}}, {{218},{  8}}, {{ 58},{  8}},
+{{186},{  8}}, {{122},{  8}}, {{250},{  8}}, {{  6},{  8}}, {{134},{  8}},
+{{ 70},{  8}}, {{198},{  8}}, {{ 38},{  8}}, {{166},{  8}}, {{102},{  8}},
+{{230},{  8}}, {{ 22},{  8}}, {{150},{  8}}, {{ 86},{  8}}, {{214},{  8}},
+{{ 54},{  8}}, {{182},{  8}}, {{118},{  8}}, {{246},{  8}}, {{ 14},{  8}},
+{{142},{  8}}, {{ 78},{  8}}, {{206},{  8}}, {{ 46},{  8}}, {{174},{  8}},
+{{110},{  8}}, {{238},{  8}}, {{ 30},{  8}}, {{158},{  8}}, {{ 94},{  8}},
+{{222},{  8}}, {{ 62},{  8}}, {{190},{  8}}, {{126},{  8}}, {{254},{  8}},
+{{  1},{  8}}, {{129},{  8}}, {{ 65},{  8}}, {{193},{  8}}, {{ 33},{  8}},
+{{161},{  8}}, {{ 97},{  8}}, {{225},{  8}}, {{ 17},{  8}}, {{145},{  8}},
+{{ 81},{  8}}, {{209},{  8}}, {{ 49},{  8}}, {{177},{  8}}, {{113},{  8}},
+{{241},{  8}}, {{  9},{  8}}, {{137},{  8}}, {{ 73},{  8}}, {{201},{  8}},
+{{ 41},{  8}}, {{169},{  8}}, {{105},{  8}}, {{233},{  8}}, {{ 25},{  8}},
+{{153},{  8}}, {{ 89},{  8}}, {{217},{  8}}, {{ 57},{  8}}, {{185},{  8}},
+{{121},{  8}}, {{249},{  8}}, {{  5},{  8}}, {{133},{  8}}, {{ 69},{  8}},
+{{197},{  8}}, {{ 37},{  8}}, {{165},{  8}}, {{101},{  8}}, {{229},{  8}},
+{{ 21},{  8}}, {{149},{  8}}, {{ 85},{  8}}, {{213},{  8}}, {{ 53},{  8}},
+{{181},{  8}}, {{117},{  8}}, {{245},{  8}}, {{ 13},{  8}}, {{141},{  8}},
+{{ 77},{  8}}, {{205},{  8}}, {{ 45},{  8}}, {{173},{  8}}, {{109},{  8}},
+{{237},{  8}}, {{ 29},{  8}}, {{157},{  8}}, {{ 93},{  8}}, {{221},{  8}},
+{{ 61},{  8}}, {{189},{  8}}, {{125},{  8}}, {{253},{  8}}, {{ 19},{  9}},
+{{275},{  9}}, {{147},{  9}}, {{403},{  9}}, {{ 83},{  9}}, {{339},{  9}},
+{{211},{  9}}, {{467},{  9}}, {{ 51},{  9}}, {{307},{  9}}, {{179},{  9}},
+{{435},{  9}}, {{115},{  9}}, {{371},{  9}}, {{243},{  9}}, {{499},{  9}},
+{{ 11},{  9}}, {{267},{  9}}, {{139},{  9}}, {{395},{  9}}, {{ 75},{  9}},
+{{331},{  9}}, {{203},{  9}}, {{459},{  9}}, {{ 43},{  9}}, {{299},{  9}},
+{{171},{  9}}, {{427},{  9}}, {{107},{  9}}, {{363},{  9}}, {{235},{  9}},
+{{491},{  9}}, {{ 27},{  9}}, {{283},{  9}}, {{155},{  9}}, {{411},{  9}},
+{{ 91},{  9}}, {{347},{  9}}, {{219},{  9}}, {{475},{  9}}, {{ 59},{  9}},
+{{315},{  9}}, {{187},{  9}}, {{443},{  9}}, {{123},{  9}}, {{379},{  9}},
+{{251},{  9}}, {{507},{  9}}, {{  7},{  9}}, {{263},{  9}}, {{135},{  9}},
+{{391},{  9}}, {{ 71},{  9}}, {{327},{  9}}, {{199},{  9}}, {{455},{  9}},
+{{ 39},{  9}}, {{295},{  9}}, {{167},{  9}}, {{423},{  9}}, {{103},{  9}},
+{{359},{  9}}, {{231},{  9}}, {{487},{  9}}, {{ 23},{  9}}, {{279},{  9}},
+{{151},{  9}}, {{407},{  9}}, {{ 87},{  9}}, {{343},{  9}}, {{215},{  9}},
+{{471},{  9}}, {{ 55},{  9}}, {{311},{  9}}, {{183},{  9}}, {{439},{  9}},
+{{119},{  9}}, {{375},{  9}}, {{247},{  9}}, {{503},{  9}}, {{ 15},{  9}},
+{{271},{  9}}, {{143},{  9}}, {{399},{  9}}, {{ 79},{  9}}, {{335},{  9}},
+{{207},{  9}}, {{463},{  9}}, {{ 47},{  9}}, {{303},{  9}}, {{175},{  9}},
+{{431},{  9}}, {{111},{  9}}, {{367},{  9}}, {{239},{  9}}, {{495},{  9}},
+{{ 31},{  9}}, {{287},{  9}}, {{159},{  9}}, {{415},{  9}}, {{ 95},{  9}},
+{{351},{  9}}, {{223},{  9}}, {{479},{  9}}, {{ 63},{  9}}, {{319},{  9}},
+{{191},{  9}}, {{447},{  9}}, {{127},{  9}}, {{383},{  9}}, {{255},{  9}},
+{{511},{  9}}, {{  0},{  7}}, {{ 64},{  7}}, {{ 32},{  7}}, {{ 96},{  7}},
+{{ 16},{  7}}, {{ 80},{  7}}, {{ 48},{  7}}, {{112},{  7}}, {{  8},{  7}},
+{{ 72},{  7}}, {{ 40},{  7}}, {{104},{  7}}, {{ 24},{  7}}, {{ 88},{  7}},
+{{ 56},{  7}}, {{120},{  7}}, {{  4},{  7}}, {{ 68},{  7}}, {{ 36},{  7}},
+{{100},{  7}}, {{ 20},{  7}}, {{ 84},{  7}}, {{ 52},{  7}}, {{116},{  7}},
+{{  3},{  8}}, {{131},{  8}}, {{ 67},{  8}}, {{195},{  8}}, {{ 35},{  8}},
+{{163},{  8}}, {{ 99},{  8}}, {{227},{  8}}
+};
+
+local const ct_data static_dtree[D_CODES] = {
+{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}},
+{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}},
+{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}},
+{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}},
+{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}},
+{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}}
+};
+
+const uch ZLIB_INTERNAL _dist_code[DIST_CODE_LEN] = {
+ 0,  1,  2,  3,  4,  4,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,  8,  8,
+ 8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9, 10, 10, 10, 10, 10, 10, 10, 10,
+10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,  0,  0, 16, 17,
+18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
+28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29
+};
+
+const uch ZLIB_INTERNAL _length_code[MAX_MATCH-MIN_MATCH+1]= {
+ 0,  1,  2,  3,  4,  5,  6,  7,  8,  8,  9,  9, 10, 10, 11, 11, 12, 12, 12, 12,
+13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16,
+17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19,
+19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22,
+22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23,
+23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25,
+25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28
+};
+
+local const int base_length[LENGTH_CODES] = {
+0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56,
+64, 80, 96, 112, 128, 160, 192, 224, 0
+};
+
+local const int base_dist[D_CODES] = {
+    0,     1,     2,     3,     4,     6,     8,    12,    16,    24,
+   32,    48,    64,    96,   128,   192,   256,   384,   512,   768,
+ 1024,  1536,  2048,  3072,  4096,  6144,  8192, 12288, 16384, 24576
+};
+
diff --git a/src/include/gromacs/external/tng_io/external/zlib/zconf.h b/src/include/gromacs/external/tng_io/external/zlib/zconf.h
new file mode 100644 (file)
index 0000000..c90fb53
--- /dev/null
@@ -0,0 +1,367 @@
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995-2013 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZCONF_H
+#define ZCONF_H
+
+#if defined(__MSDOS__) && !defined(MSDOS)
+#  define MSDOS
+#endif
+#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
+#  define OS2
+#endif
+#if defined(_WINDOWS) && !defined(WINDOWS)
+#  define WINDOWS
+#endif
+#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
+#  ifndef WIN32
+#    define WIN32
+#  endif
+#endif
+#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
+#  if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
+#    ifndef SYS16BIT
+#      define SYS16BIT
+#    endif
+#  endif
+#endif
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ */
+#ifdef SYS16BIT
+#  define MAXSEG_64K
+#endif
+#ifdef MSDOS
+#  define UNALIGNED_OK
+#endif
+
+#ifdef __STDC_VERSION__
+#  ifndef STDC
+#    define STDC
+#  endif
+#  if __STDC_VERSION__ >= 199901L
+#    ifndef STDC99
+#      define STDC99
+#    endif
+#  endif
+#endif
+#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
+#  define STDC
+#endif
+#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
+#  define STDC
+#endif
+
+#if defined(__OS400__) && !defined(STDC)    /* iSeries (formerly AS/400). */
+#  define STDC
+#endif
+
+#ifndef STDC
+#  ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
+#    define const       /* note: need a more gentle solution here */
+#  endif
+#endif
+
+#if defined(ZLIB_CONST) && !defined(z_const)
+#  define z_const const
+#else
+#  define z_const
+#endif
+
+/* Some Mac compilers merge all .h files incorrectly: */
+#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
+#  define NO_DUMMY_DECL
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+#  ifdef MAXSEG_64K
+#    define MAX_MEM_LEVEL 8
+#  else
+#    define MAX_MEM_LEVEL 9
+#  endif
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2.
+ * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
+ * created by gzip. (Files created by minigzip can still be extracted by
+ * gzip.)
+ */
+#ifndef MAX_WBITS
+#  define MAX_WBITS   15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+            (1 << (windowBits+2)) +  (1 << (memLevel+9))
+ that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+   The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+                        /* Type declarations */
+
+#ifndef OF /* function prototypes */
+#  ifdef STDC
+#    define OF(args)  args
+#  else
+#    define OF(args)  ()
+#  endif
+#endif
+
+#ifndef Z_ARG /* function prototypes for stdarg */
+#  if defined(STDC) || defined(Z_HAVE_STDARG_H)
+#    define Z_ARG(args)  args
+#  else
+#    define Z_ARG(args)  ()
+#  endif
+#endif
+
+/* The following definitions for FAR are needed only for MSDOS mixed
+ * model programming (small or medium model with some far allocations).
+ * This was tested only with MSC; for other MSDOS compilers you may have
+ * to define NO_MEMCPY in zutil.h.  If you don't need the mixed model,
+ * just define FAR to be empty.
+ */
+#ifdef SYS16BIT
+#  if defined(M_I86SM) || defined(M_I86MM)
+     /* MSC small or medium model */
+#    define SMALL_MEDIUM
+#    ifdef _MSC_VER
+#      define FAR _far
+#    else
+#      define FAR far
+#    endif
+#  endif
+#  if (defined(__SMALL__) || defined(__MEDIUM__))
+     /* Turbo C small or medium model */
+#    define SMALL_MEDIUM
+#    ifdef __BORLANDC__
+#      define FAR _far
+#    else
+#      define FAR far
+#    endif
+#  endif
+#endif
+
+#if defined(WINDOWS) || defined(WIN32)
+   /* If building or using zlib as a DLL, define ZLIB_DLL.
+    * This is not mandatory, but it offers a little performance increase.
+    */
+#  ifdef ZLIB_DLL
+#    if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
+#      ifdef ZLIB_INTERNAL
+#        define ZEXTERN extern __declspec(dllexport)
+#      else
+#        define ZEXTERN extern __declspec(dllimport)
+#      endif
+#    endif
+#  endif  /* ZLIB_DLL */
+   /* If building or using zlib with the WINAPI/WINAPIV calling convention,
+    * define ZLIB_WINAPI.
+    * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
+    */
+#  ifdef ZLIB_WINAPI
+#    ifdef FAR
+#      undef FAR
+#    endif
+#    include <windows.h>
+     /* No need for _export, use ZLIB.DEF instead. */
+     /* For complete Windows compatibility, use WINAPI, not __stdcall. */
+#    define ZEXPORT WINAPI
+#    ifdef WIN32
+#      define ZEXPORTVA WINAPIV
+#    else
+#      define ZEXPORTVA FAR CDECL
+#    endif
+#  endif
+#endif
+
+#if defined (__BEOS__)
+#  ifdef ZLIB_DLL
+#    ifdef ZLIB_INTERNAL
+#      define ZEXPORT   __declspec(dllexport)
+#      define ZEXPORTVA __declspec(dllexport)
+#    else
+#      define ZEXPORT   __declspec(dllimport)
+#      define ZEXPORTVA __declspec(dllimport)
+#    endif
+#  endif
+#endif
+
+#ifndef ZEXTERN
+#  define ZEXTERN extern
+#endif
+#ifndef ZEXPORT
+#  define ZEXPORT
+#endif
+#ifndef ZEXPORTVA
+#  define ZEXPORTVA
+#endif
+
+#ifndef FAR
+#  define FAR
+#endif
+
+#if !defined(__MACTYPES__)
+typedef unsigned char  Byte;  /* 8 bits */
+#endif
+typedef unsigned int   uInt;  /* 16 bits or more */
+typedef unsigned long  uLong; /* 32 bits or more */
+
+#ifdef SMALL_MEDIUM
+   /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
+#  define Bytef Byte FAR
+#else
+   typedef Byte  FAR Bytef;
+#endif
+typedef char  FAR charf;
+typedef int   FAR intf;
+typedef uInt  FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+   typedef void const *voidpc;
+   typedef void FAR   *voidpf;
+   typedef void       *voidp;
+#else
+   typedef Byte const *voidpc;
+   typedef Byte FAR   *voidpf;
+   typedef Byte       *voidp;
+#endif
+
+#if !defined(Z_U4) && !defined(Z_SOLO) && defined(STDC)
+#  include <limits.h>
+#  if (UINT_MAX == 0xffffffffUL)
+#    define Z_U4 unsigned
+#  elif (ULONG_MAX == 0xffffffffUL)
+#    define Z_U4 unsigned long
+#  elif (USHRT_MAX == 0xffffffffUL)
+#    define Z_U4 unsigned short
+#  endif
+#endif
+
+#ifdef Z_U4
+   typedef Z_U4 z_crc_t;
+#else
+   typedef unsigned long z_crc_t;
+#endif
+
+#ifdef HAVE_UNISTD_H    /* may be set to #if 1 by ./configure */
+#  define Z_HAVE_UNISTD_H
+#endif
+
+#ifdef HAVE_STDARG_H    /* may be set to #if 1 by ./configure */
+#  define Z_HAVE_STDARG_H
+#endif
+
+#ifdef STDC
+#  ifndef Z_SOLO
+#    include <sys/types.h>      /* for off_t */
+#  endif
+#endif
+
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+#  ifndef Z_SOLO
+#    include <stdarg.h>         /* for va_list */
+#  endif
+#endif
+
+#ifdef _WIN32
+#  ifndef Z_SOLO
+#    include <stddef.h>         /* for wchar_t */
+#  endif
+#endif
+
+/* a little trick to accommodate both "#define _LARGEFILE64_SOURCE" and
+ * "#define _LARGEFILE64_SOURCE 1" as requesting 64-bit operations, (even
+ * though the former does not conform to the LFS document), but considering
+ * both "#undef _LARGEFILE64_SOURCE" and "#define _LARGEFILE64_SOURCE 0" as
+ * equivalently requesting no 64-bit operations
+ */
+#if defined(_LARGEFILE64_SOURCE) && -_LARGEFILE64_SOURCE - -1 == 1
+#  undef _LARGEFILE64_SOURCE
+#endif
+
+#if defined(__WATCOMC__) && !defined(Z_HAVE_UNISTD_H)
+#  define Z_HAVE_UNISTD_H
+#endif
+#ifndef Z_SOLO
+#  if defined(Z_HAVE_UNISTD_H) || defined(_LARGEFILE64_SOURCE)
+#    include <unistd.h>         /* for SEEK_*, off_t, and _LFS64_LARGEFILE */
+#    ifdef VMS
+#      include <unixio.h>       /* for off_t */
+#    endif
+#    ifndef z_off_t
+#      define z_off_t off_t
+#    endif
+#  endif
+#endif
+
+#if defined(_LFS64_LARGEFILE) && _LFS64_LARGEFILE-0
+#  define Z_LFS64
+#endif
+
+#if defined(_LARGEFILE64_SOURCE) && defined(Z_LFS64)
+#  define Z_LARGE64
+#endif
+
+#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS-0 == 64 && defined(Z_LFS64)
+#  define Z_WANT64
+#endif
+
+#if !defined(SEEK_SET) && !defined(Z_SOLO)
+#  define SEEK_SET        0       /* Seek from beginning of file.  */
+#  define SEEK_CUR        1       /* Seek from current position.  */
+#  define SEEK_END        2       /* Set file pointer to EOF plus "offset" */
+#endif
+
+#ifndef z_off_t
+#  define z_off_t long
+#endif
+
+#if !defined(_WIN32) && defined(Z_LARGE64)
+#  define z_off64_t off64_t
+#else
+#  if defined(_WIN32) && !defined(__GNUC__) && !defined(Z_SOLO)
+#    define z_off64_t __int64
+#  else
+#    define z_off64_t z_off_t
+#  endif
+#endif
+
+/* MVS linker does not support external names larger than 8 bytes */
+#if defined(__MVS__)
+  #pragma map(deflateInit_,"DEIN")
+  #pragma map(deflateInit2_,"DEIN2")
+  #pragma map(deflateEnd,"DEEND")
+  #pragma map(deflateBound,"DEBND")
+  #pragma map(inflateInit_,"ININ")
+  #pragma map(inflateInit2_,"ININ2")
+  #pragma map(inflateEnd,"INEND")
+  #pragma map(inflateSync,"INSY")
+  #pragma map(inflateSetDictionary,"INSEDI")
+  #pragma map(compressBound,"CMBND")
+  #pragma map(inflate_table,"INTABL")
+  #pragma map(inflate_fast,"INFA")
+  #pragma map(inflate_copyright,"INCOPY")
+#endif
+
+#endif /* ZCONF_H */
diff --git a/src/include/gromacs/external/tng_io/external/zlib/zlib.h b/src/include/gromacs/external/tng_io/external/zlib/zlib.h
new file mode 100644 (file)
index 0000000..565d0ed
--- /dev/null
@@ -0,0 +1,1769 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 1.2.8, April 28th, 2013
+
+  Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+
+  The data format used by the zlib library is described by RFCs (Request for
+  Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
+  (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
+*/
+
+#ifndef ZLIB_H
+#define ZLIB_H
+
+#include "zconf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZLIB_VERSION "1.2.8"
+#define ZLIB_VERNUM 0x1280
+#define ZLIB_VER_MAJOR 1
+#define ZLIB_VER_MINOR 2
+#define ZLIB_VER_REVISION 8
+#define ZLIB_VER_SUBREVISION 0
+
+/*
+    The 'zlib' compression library provides in-memory compression and
+  decompression functions, including integrity checks of the uncompressed data.
+  This version of the library supports only one compression method (deflation)
+  but other algorithms will be added later and will have the same stream
+  interface.
+
+    Compression can be done in a single step if the buffers are large enough,
+  or can be done by repeated calls of the compression function.  In the latter
+  case, the application must provide more input and/or consume the output
+  (providing more output space) before each call.
+
+    The compressed data format used by default by the in-memory functions is
+  the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped
+  around a deflate stream, which is itself documented in RFC 1951.
+
+    The library also supports reading and writing files in gzip (.gz) format
+  with an interface similar to that of stdio using the functions that start
+  with "gz".  The gzip format is different from the zlib format.  gzip is a
+  gzip wrapper, documented in RFC 1952, wrapped around a deflate stream.
+
+    This library can optionally read and write gzip streams in memory as well.
+
+    The zlib format was designed to be compact and fast for use in memory
+  and on communications channels.  The gzip format was designed for single-
+  file compression on file systems, has a larger header than zlib to maintain
+  directory information, and uses a different, slower check method than zlib.
+
+    The library does not install any signal handler.  The decoder checks
+  the consistency of the compressed data, so the library should never crash
+  even in case of corrupted input.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void   (*free_func)  OF((voidpf opaque, voidpf address));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+    z_const Bytef *next_in;     /* next input byte */
+    uInt     avail_in;  /* number of bytes available at next_in */
+    uLong    total_in;  /* total number of input bytes read so far */
+
+    Bytef    *next_out; /* next output byte should be put there */
+    uInt     avail_out; /* remaining free space at next_out */
+    uLong    total_out; /* total number of bytes output so far */
+
+    z_const char *msg;  /* last error message, NULL if no error */
+    struct internal_state FAR *state; /* not visible by applications */
+
+    alloc_func zalloc;  /* used to allocate the internal state */
+    free_func  zfree;   /* used to free the internal state */
+    voidpf     opaque;  /* private data object passed to zalloc and zfree */
+
+    int     data_type;  /* best guess about the data type: binary or text */
+    uLong   adler;      /* adler32 value of the uncompressed data */
+    uLong   reserved;   /* reserved for future use */
+} z_stream;
+
+typedef z_stream FAR *z_streamp;
+
+/*
+     gzip header information passed to and from zlib routines.  See RFC 1952
+  for more details on the meanings of these fields.
+*/
+typedef struct gz_header_s {
+    int     text;       /* true if compressed data believed to be text */
+    uLong   time;       /* modification time */
+    int     xflags;     /* extra flags (not used when writing a gzip file) */
+    int     os;         /* operating system */
+    Bytef   *extra;     /* pointer to extra field or Z_NULL if none */
+    uInt    extra_len;  /* extra field length (valid if extra != Z_NULL) */
+    uInt    extra_max;  /* space at extra (only when reading header) */
+    Bytef   *name;      /* pointer to zero-terminated file name or Z_NULL */
+    uInt    name_max;   /* space at name (only when reading header) */
+    Bytef   *comment;   /* pointer to zero-terminated comment or Z_NULL */
+    uInt    comm_max;   /* space at comment (only when reading header) */
+    int     hcrc;       /* true if there was or will be a header crc */
+    int     done;       /* true when done reading gzip header (not used
+                           when writing a gzip file) */
+} gz_header;
+
+typedef gz_header FAR *gz_headerp;
+
+/*
+     The application must update next_in and avail_in when avail_in has dropped
+   to zero.  It must update next_out and avail_out when avail_out has dropped
+   to zero.  The application must initialize zalloc, zfree and opaque before
+   calling the init function.  All other fields are set by the compression
+   library and must not be updated by the application.
+
+     The opaque value provided by the application will be passed as the first
+   parameter for calls of zalloc and zfree.  This can be useful for custom
+   memory management.  The compression library attaches no meaning to the
+   opaque value.
+
+     zalloc must return Z_NULL if there is not enough memory for the object.
+   If zlib is used in a multi-threaded application, zalloc and zfree must be
+   thread safe.
+
+     On 16-bit systems, the functions zalloc and zfree must be able to allocate
+   exactly 65536 bytes, but will not be required to allocate more than this if
+   the symbol MAXSEG_64K is defined (see zconf.h).  WARNING: On MSDOS, pointers
+   returned by zalloc for objects of exactly 65536 bytes *must* have their
+   offset normalized to zero.  The default allocation function provided by this
+   library ensures this (see zutil.c).  To reduce memory requirements and avoid
+   any allocation of 64K objects, at the expense of compression ratio, compile
+   the library with -DMAX_WBITS=14 (see zconf.h).
+
+     The fields total_in and total_out can be used for statistics or progress
+   reports.  After compression, total_in holds the total size of the
+   uncompressed data and may be saved for use in the decompressor (particularly
+   if the decompressor wants to decompress everything in a single step).
+*/
+
+                        /* constants */
+
+#define Z_NO_FLUSH      0
+#define Z_PARTIAL_FLUSH 1
+#define Z_SYNC_FLUSH    2
+#define Z_FULL_FLUSH    3
+#define Z_FINISH        4
+#define Z_BLOCK         5
+#define Z_TREES         6
+/* Allowed flush values; see deflate() and inflate() below for details */
+
+#define Z_OK            0
+#define Z_STREAM_END    1
+#define Z_NEED_DICT     2
+#define Z_ERRNO        (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR   (-3)
+#define Z_MEM_ERROR    (-4)
+#define Z_BUF_ERROR    (-5)
+#define Z_VERSION_ERROR (-6)
+/* Return codes for the compression/decompression functions. Negative values
+ * are errors, positive values are used for special but normal events.
+ */
+
+#define Z_NO_COMPRESSION         0
+#define Z_BEST_SPEED             1
+#define Z_BEST_COMPRESSION       9
+#define Z_DEFAULT_COMPRESSION  (-1)
+/* compression levels */
+
+#define Z_FILTERED            1
+#define Z_HUFFMAN_ONLY        2
+#define Z_RLE                 3
+#define Z_FIXED               4
+#define Z_DEFAULT_STRATEGY    0
+/* compression strategy; see deflateInit2() below for details */
+
+#define Z_BINARY   0
+#define Z_TEXT     1
+#define Z_ASCII    Z_TEXT   /* for compatibility with 1.2.2 and earlier */
+#define Z_UNKNOWN  2
+/* Possible values of the data_type field (though see inflate()) */
+
+#define Z_DEFLATED   8
+/* The deflate compression method (the only one supported in this version) */
+
+#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */
+
+#define zlib_version zlibVersion()
+/* for compatibility with versions < 1.0.2 */
+
+
+                        /* basic functions */
+
+ZEXTERN const char * ZEXPORT zlibVersion OF((void));
+/* The application can compare zlibVersion and ZLIB_VERSION for consistency.
+   If the first character differs, the library code actually used is not
+   compatible with the zlib.h header file used by the application.  This check
+   is automatically made by deflateInit and inflateInit.
+ */
+
+/*
+ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level));
+
+     Initializes the internal stream state for compression.  The fields
+   zalloc, zfree and opaque must be initialized before by the caller.  If
+   zalloc and zfree are set to Z_NULL, deflateInit updates them to use default
+   allocation functions.
+
+     The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9:
+   1 gives best speed, 9 gives best compression, 0 gives no compression at all
+   (the input data is simply copied a block at a time).  Z_DEFAULT_COMPRESSION
+   requests a default compromise between speed and compression (currently
+   equivalent to level 6).
+
+     deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if level is not a valid compression level, or
+   Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible
+   with the version assumed by the caller (ZLIB_VERSION).  msg is set to null
+   if there is no error message.  deflateInit does not perform any compression:
+   this will be done by deflate().
+*/
+
+
+ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
+/*
+    deflate compresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full.  It may introduce
+  some output latency (reading input without producing any output) except when
+  forced to flush.
+
+    The detailed semantics are as follows.  deflate performs one or both of the
+  following actions:
+
+  - Compress more input starting at next_in and update next_in and avail_in
+    accordingly.  If not all input can be processed (because there is not
+    enough room in the output buffer), next_in and avail_in are updated and
+    processing will resume at this point for the next call of deflate().
+
+  - Provide more output starting at next_out and update next_out and avail_out
+    accordingly.  This action is forced if the parameter flush is non zero.
+    Forcing flush frequently degrades the compression ratio, so this parameter
+    should be set only when necessary (in interactive applications).  Some
+    output may be provided even if flush is not set.
+
+    Before the call of deflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming more
+  output, and updating avail_in or avail_out accordingly; avail_out should
+  never be zero before the call.  The application can consume the compressed
+  output when it wants, for example when the output buffer is full (avail_out
+  == 0), or after each call of deflate().  If deflate returns Z_OK and with
+  zero avail_out, it must be called again after making room in the output
+  buffer because there might be more output pending.
+
+    Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to
+  decide how much data to accumulate before producing output, in order to
+  maximize compression.
+
+    If the parameter flush is set to Z_SYNC_FLUSH, all pending output is
+  flushed to the output buffer and the output is aligned on a byte boundary, so
+  that the decompressor can get all input data available so far.  (In
+  particular avail_in is zero after the call if enough output space has been
+  provided before the call.) Flushing may degrade compression for some
+  compression algorithms and so it should be used only when necessary.  This
+  completes the current deflate block and follows it with an empty stored block
+  that is three bits plus filler bits to the next byte, followed by four bytes
+  (00 00 ff ff).
+
+    If flush is set to Z_PARTIAL_FLUSH, all pending output is flushed to the
+  output buffer, but the output is not aligned to a byte boundary.  All of the
+  input data so far will be available to the decompressor, as for Z_SYNC_FLUSH.
+  This completes the current deflate block and follows it with an empty fixed
+  codes block that is 10 bits long.  This assures that enough bytes are output
+  in order for the decompressor to finish the block before the empty fixed code
+  block.
+
+    If flush is set to Z_BLOCK, a deflate block is completed and emitted, as
+  for Z_SYNC_FLUSH, but the output is not aligned on a byte boundary, and up to
+  seven bits of the current block are held to be written as the next byte after
+  the next deflate block is completed.  In this case, the decompressor may not
+  be provided enough bits at this point in order to complete decompression of
+  the data provided so far to the compressor.  It may need to wait for the next
+  block to be emitted.  This is for advanced applications that need to control
+  the emission of deflate blocks.
+
+    If flush is set to Z_FULL_FLUSH, all output is flushed as with
+  Z_SYNC_FLUSH, and the compression state is reset so that decompression can
+  restart from this point if previous compressed data has been damaged or if
+  random access is desired.  Using Z_FULL_FLUSH too often can seriously degrade
+  compression.
+
+    If deflate returns with avail_out == 0, this function must be called again
+  with the same value of the flush parameter and more output space (updated
+  avail_out), until the flush is complete (deflate returns with non-zero
+  avail_out).  In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that
+  avail_out is greater than six to avoid repeated flush markers due to
+  avail_out == 0 on return.
+
+    If the parameter flush is set to Z_FINISH, pending input is processed,
+  pending output is flushed and deflate returns with Z_STREAM_END if there was
+  enough output space; if deflate returns with Z_OK, this function must be
+  called again with Z_FINISH and more output space (updated avail_out) but no
+  more input data, until it returns with Z_STREAM_END or an error.  After
+  deflate has returned Z_STREAM_END, the only possible operations on the stream
+  are deflateReset or deflateEnd.
+
+    Z_FINISH can be used immediately after deflateInit if all the compression
+  is to be done in a single step.  In this case, avail_out must be at least the
+  value returned by deflateBound (see below).  Then deflate is guaranteed to
+  return Z_STREAM_END.  If not enough output space is provided, deflate will
+  not return Z_STREAM_END, and it must be called again as described above.
+
+    deflate() sets strm->adler to the adler32 checksum of all input read
+  so far (that is, total_in bytes).
+
+    deflate() may update strm->data_type if it can make a good guess about
+  the input data type (Z_BINARY or Z_TEXT).  In doubt, the data is considered
+  binary.  This field is only for information purposes and does not affect the
+  compression algorithm in any manner.
+
+    deflate() returns Z_OK if some progress has been made (more input
+  processed or more output produced), Z_STREAM_END if all input has been
+  consumed and all output has been produced (only when flush is set to
+  Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example
+  if next_in or next_out was Z_NULL), Z_BUF_ERROR if no progress is possible
+  (for example avail_in or avail_out was zero).  Note that Z_BUF_ERROR is not
+  fatal, and deflate() can be called again with more input and more output
+  space to continue compressing.
+*/
+
+
+ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any pending
+   output.
+
+     deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the
+   stream state was inconsistent, Z_DATA_ERROR if the stream was freed
+   prematurely (some input or output was discarded).  In the error case, msg
+   may be set but then points to a static string (which must not be
+   deallocated).
+*/
+
+
+/*
+ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm));
+
+     Initializes the internal stream state for decompression.  The fields
+   next_in, avail_in, zalloc, zfree and opaque must be initialized before by
+   the caller.  If next_in is not Z_NULL and avail_in is large enough (the
+   exact value depends on the compression method), inflateInit determines the
+   compression method from the zlib header and allocates all data structures
+   accordingly; otherwise the allocation will be deferred to the first call of
+   inflate.  If zalloc and zfree are set to Z_NULL, inflateInit updates them to
+   use default allocation functions.
+
+     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+   version assumed by the caller, or Z_STREAM_ERROR if the parameters are
+   invalid, such as a null pointer to the structure.  msg is set to null if
+   there is no error message.  inflateInit does not perform any decompression
+   apart from possibly reading the zlib header if present: actual decompression
+   will be done by inflate().  (So next_in and avail_in may be modified, but
+   next_out and avail_out are unused and unchanged.) The current implementation
+   of inflateInit() does not process any header information -- that is deferred
+   until inflate() is called.
+*/
+
+
+ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));
+/*
+    inflate decompresses as much data as possible, and stops when the input
+  buffer becomes empty or the output buffer becomes full.  It may introduce
+  some output latency (reading input without producing any output) except when
+  forced to flush.
+
+  The detailed semantics are as follows.  inflate performs one or both of the
+  following actions:
+
+  - Decompress more input starting at next_in and update next_in and avail_in
+    accordingly.  If not all input can be processed (because there is not
+    enough room in the output buffer), next_in is updated and processing will
+    resume at this point for the next call of inflate().
+
+  - Provide more output starting at next_out and update next_out and avail_out
+    accordingly.  inflate() provides as much output as possible, until there is
+    no more input data or no more space in the output buffer (see below about
+    the flush parameter).
+
+    Before the call of inflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming more
+  output, and updating the next_* and avail_* values accordingly.  The
+  application can consume the uncompressed output when it wants, for example
+  when the output buffer is full (avail_out == 0), or after each call of
+  inflate().  If inflate returns Z_OK and with zero avail_out, it must be
+  called again after making room in the output buffer because there might be
+  more output pending.
+
+    The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FINISH,
+  Z_BLOCK, or Z_TREES.  Z_SYNC_FLUSH requests that inflate() flush as much
+  output as possible to the output buffer.  Z_BLOCK requests that inflate()
+  stop if and when it gets to the next deflate block boundary.  When decoding
+  the zlib or gzip format, this will cause inflate() to return immediately
+  after the header and before the first block.  When doing a raw inflate,
+  inflate() will go ahead and process the first block, and will return when it
+  gets to the end of that block, or when it runs out of data.
+
+    The Z_BLOCK option assists in appending to or combining deflate streams.
+  Also to assist in this, on return inflate() will set strm->data_type to the
+  number of unused bits in the last byte taken from strm->next_in, plus 64 if
+  inflate() is currently decoding the last block in the deflate stream, plus
+  128 if inflate() returned immediately after decoding an end-of-block code or
+  decoding the complete header up to just before the first byte of the deflate
+  stream.  The end-of-block will not be indicated until all of the uncompressed
+  data from that block has been written to strm->next_out.  The number of
+  unused bits may in general be greater than seven, except when bit 7 of
+  data_type is set, in which case the number of unused bits will be less than
+  eight.  data_type is set as noted here every time inflate() returns for all
+  flush options, and so can be used to determine the amount of currently
+  consumed input in bits.
+
+    The Z_TREES option behaves as Z_BLOCK does, but it also returns when the
+  end of each deflate block header is reached, before any actual data in that
+  block is decoded.  This allows the caller to determine the length of the
+  deflate block header for later use in random access within a deflate block.
+  256 is added to the value of strm->data_type when inflate() returns
+  immediately after reaching the end of the deflate block header.
+
+    inflate() should normally be called until it returns Z_STREAM_END or an
+  error.  However if all decompression is to be performed in a single step (a
+  single call of inflate), the parameter flush should be set to Z_FINISH.  In
+  this case all pending input is processed and all pending output is flushed;
+  avail_out must be large enough to hold all of the uncompressed data for the
+  operation to complete.  (The size of the uncompressed data may have been
+  saved by the compressor for this purpose.) The use of Z_FINISH is not
+  required to perform an inflation in one step.  However it may be used to
+  inform inflate that a faster approach can be used for the single inflate()
+  call.  Z_FINISH also informs inflate to not maintain a sliding window if the
+  stream completes, which reduces inflate's memory footprint.  If the stream
+  does not complete, either because not all of the stream is provided or not
+  enough output space is provided, then a sliding window will be allocated and
+  inflate() can be called again to continue the operation as if Z_NO_FLUSH had
+  been used.
+
+     In this implementation, inflate() always flushes as much output as
+  possible to the output buffer, and always uses the faster approach on the
+  first call.  So the effects of the flush parameter in this implementation are
+  on the return value of inflate() as noted below, when inflate() returns early
+  when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of
+  memory for a sliding window when Z_FINISH is used.
+
+     If a preset dictionary is needed after this call (see inflateSetDictionary
+  below), inflate sets strm->adler to the Adler-32 checksum of the dictionary
+  chosen by the compressor and returns Z_NEED_DICT; otherwise it sets
+  strm->adler to the Adler-32 checksum of all output produced so far (that is,
+  total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described
+  below.  At the end of the stream, inflate() checks that its computed adler32
+  checksum is equal to that saved by the compressor and returns Z_STREAM_END
+  only if the checksum is correct.
+
+    inflate() can decompress and check either zlib-wrapped or gzip-wrapped
+  deflate data.  The header type is detected automatically, if requested when
+  initializing with inflateInit2().  Any information contained in the gzip
+  header is not retained, so applications that need that information should
+  instead use raw inflate, see inflateInit2() below, or inflateBack() and
+  perform their own processing of the gzip header and trailer.  When processing
+  gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output
+  producted so far.  The CRC-32 is checked against the gzip trailer.
+
+    inflate() returns Z_OK if some progress has been made (more input processed
+  or more output produced), Z_STREAM_END if the end of the compressed data has
+  been reached and all uncompressed output has been produced, Z_NEED_DICT if a
+  preset dictionary is needed at this point, Z_DATA_ERROR if the input data was
+  corrupted (input stream not conforming to the zlib format or incorrect check
+  value), Z_STREAM_ERROR if the stream structure was inconsistent (for example
+  next_in or next_out was Z_NULL), Z_MEM_ERROR if there was not enough memory,
+  Z_BUF_ERROR if no progress is possible or if there was not enough room in the
+  output buffer when Z_FINISH is used.  Note that Z_BUF_ERROR is not fatal, and
+  inflate() can be called again with more input and more output space to
+  continue decompressing.  If Z_DATA_ERROR is returned, the application may
+  then call inflateSync() to look for a good compression block if a partial
+  recovery of the data is desired.
+*/
+
+
+ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any pending
+   output.
+
+     inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+   was inconsistent.  In the error case, msg may be set but then points to a
+   static string (which must not be deallocated).
+*/
+
+
+                        /* Advanced functions */
+
+/*
+    The following functions are needed only in some special applications.
+*/
+
+/*
+ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm,
+                                     int  level,
+                                     int  method,
+                                     int  windowBits,
+                                     int  memLevel,
+                                     int  strategy));
+
+     This is another version of deflateInit with more compression options.  The
+   fields next_in, zalloc, zfree and opaque must be initialized before by the
+   caller.
+
+     The method parameter is the compression method.  It must be Z_DEFLATED in
+   this version of the library.
+
+     The windowBits parameter is the base two logarithm of the window size
+   (the size of the history buffer).  It should be in the range 8..15 for this
+   version of the library.  Larger values of this parameter result in better
+   compression at the expense of memory usage.  The default value is 15 if
+   deflateInit is used instead.
+
+     windowBits can also be -8..-15 for raw deflate.  In this case, -windowBits
+   determines the window size.  deflate() will then generate raw deflate data
+   with no zlib header or trailer, and will not compute an adler32 check value.
+
+     windowBits can also be greater than 15 for optional gzip encoding.  Add
+   16 to windowBits to write a simple gzip header and trailer around the
+   compressed data instead of a zlib wrapper.  The gzip header will have no
+   file name, no extra data, no comment, no modification time (set to zero), no
+   header crc, and the operating system will be set to 255 (unknown).  If a
+   gzip stream is being written, strm->adler is a crc32 instead of an adler32.
+
+     The memLevel parameter specifies how much memory should be allocated
+   for the internal compression state.  memLevel=1 uses minimum memory but is
+   slow and reduces compression ratio; memLevel=9 uses maximum memory for
+   optimal speed.  The default value is 8.  See zconf.h for total memory usage
+   as a function of windowBits and memLevel.
+
+     The strategy parameter is used to tune the compression algorithm.  Use the
+   value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a
+   filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no
+   string match), or Z_RLE to limit match distances to one (run-length
+   encoding).  Filtered data consists mostly of small values with a somewhat
+   random distribution.  In this case, the compression algorithm is tuned to
+   compress them better.  The effect of Z_FILTERED is to force more Huffman
+   coding and less string matching; it is somewhat intermediate between
+   Z_DEFAULT_STRATEGY and Z_HUFFMAN_ONLY.  Z_RLE is designed to be almost as
+   fast as Z_HUFFMAN_ONLY, but give better compression for PNG image data.  The
+   strategy parameter only affects the compression ratio but not the
+   correctness of the compressed output even if it is not set appropriately.
+   Z_FIXED prevents the use of dynamic Huffman codes, allowing for a simpler
+   decoder for special applications.
+
+     deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_STREAM_ERROR if any parameter is invalid (such as an invalid
+   method), or Z_VERSION_ERROR if the zlib library version (zlib_version) is
+   incompatible with the version assumed by the caller (ZLIB_VERSION).  msg is
+   set to null if there is no error message.  deflateInit2 does not perform any
+   compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm,
+                                             const Bytef *dictionary,
+                                             uInt  dictLength));
+/*
+     Initializes the compression dictionary from the given byte sequence
+   without producing any compressed output.  When using the zlib format, this
+   function must be called immediately after deflateInit, deflateInit2 or
+   deflateReset, and before any call of deflate.  When doing raw deflate, this
+   function must be called either before any call of deflate, or immediately
+   after the completion of a deflate block, i.e. after all input has been
+   consumed and all output has been delivered when using any of the flush
+   options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH.  The
+   compressor and decompressor must use exactly the same dictionary (see
+   inflateSetDictionary).
+
+     The dictionary should consist of strings (byte sequences) that are likely
+   to be encountered later in the data to be compressed, with the most commonly
+   used strings preferably put towards the end of the dictionary.  Using a
+   dictionary is most useful when the data to be compressed is short and can be
+   predicted with good accuracy; the data can then be compressed better than
+   with the default empty dictionary.
+
+     Depending on the size of the compression data structures selected by
+   deflateInit or deflateInit2, a part of the dictionary may in effect be
+   discarded, for example if the dictionary is larger than the window size
+   provided in deflateInit or deflateInit2.  Thus the strings most likely to be
+   useful should be put at the end of the dictionary, not at the front.  In
+   addition, the current implementation of deflate will use at most the window
+   size minus 262 bytes of the provided dictionary.
+
+     Upon return of this function, strm->adler is set to the adler32 value
+   of the dictionary; the decompressor may later use this value to determine
+   which dictionary has been used by the compressor.  (The adler32 value
+   applies to the whole dictionary even if only a subset of the dictionary is
+   actually used by the compressor.) If a raw deflate was requested, then the
+   adler32 value is not computed and strm->adler is not set.
+
+     deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a
+   parameter is invalid (e.g.  dictionary being Z_NULL) or the stream state is
+   inconsistent (for example if deflate has already been called for this stream
+   or if not at a block boundary for raw deflate).  deflateSetDictionary does
+   not perform any compression: this will be done by deflate().
+*/
+
+ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest,
+                                    z_streamp source));
+/*
+     Sets the destination stream as a complete copy of the source stream.
+
+     This function can be useful when several compression strategies will be
+   tried, for example when there are several ways of pre-processing the input
+   data with a filter.  The streams that will be discarded should then be freed
+   by calling deflateEnd.  Note that deflateCopy duplicates the internal
+   compression state which can be quite large, so this strategy is slow and can
+   consume lots of memory.
+
+     deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+   (such as zalloc being Z_NULL).  msg is left unchanged in both source and
+   destination.
+*/
+
+ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm));
+/*
+     This function is equivalent to deflateEnd followed by deflateInit,
+   but does not free and reallocate all the internal compression state.  The
+   stream will keep the same compression level and any other attributes that
+   may have been set by deflateInit2.
+
+     deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being Z_NULL).
+*/
+
+ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm,
+                                      int level,
+                                      int strategy));
+/*
+     Dynamically update the compression level and compression strategy.  The
+   interpretation of level and strategy is as in deflateInit2.  This can be
+   used to switch between compression and straight copy of the input data, or
+   to switch to a different kind of input data requiring a different strategy.
+   If the compression level is changed, the input available so far is
+   compressed with the old level (and may be flushed); the new level will take
+   effect only at the next call of deflate().
+
+     Before the call of deflateParams, the stream state must be set as for
+   a call of deflate(), since the currently available input may have to be
+   compressed and flushed.  In particular, strm->avail_out must be non-zero.
+
+     deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source
+   stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR if
+   strm->avail_out was zero.
+*/
+
+ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm,
+                                    int good_length,
+                                    int max_lazy,
+                                    int nice_length,
+                                    int max_chain));
+/*
+     Fine tune deflate's internal compression parameters.  This should only be
+   used by someone who understands the algorithm used by zlib's deflate for
+   searching for the best matching string, and even then only by the most
+   fanatic optimizer trying to squeeze out the last compressed bit for their
+   specific input data.  Read the deflate.c source code for the meaning of the
+   max_lazy, good_length, nice_length, and max_chain parameters.
+
+     deflateTune() can be called after deflateInit() or deflateInit2(), and
+   returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream.
+ */
+
+ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm,
+                                       uLong sourceLen));
+/*
+     deflateBound() returns an upper bound on the compressed size after
+   deflation of sourceLen bytes.  It must be called after deflateInit() or
+   deflateInit2(), and after deflateSetHeader(), if used.  This would be used
+   to allocate an output buffer for deflation in a single pass, and so would be
+   called before deflate().  If that first deflate() call is provided the
+   sourceLen input bytes, an output buffer allocated to the size returned by
+   deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed
+   to return Z_STREAM_END.  Note that it is possible for the compressed size to
+   be larger than the value returned by deflateBound() if flush options other
+   than Z_FINISH or Z_NO_FLUSH are used.
+*/
+
+ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm,
+                                       unsigned *pending,
+                                       int *bits));
+/*
+     deflatePending() returns the number of bytes and bits of output that have
+   been generated, but not yet provided in the available output.  The bytes not
+   provided would be due to the available output space having being consumed.
+   The number of bits of output not provided are between 0 and 7, where they
+   await more bits to join them in order to fill out a full byte.  If pending
+   or bits are Z_NULL, then those values are not set.
+
+     deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+ */
+
+ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm,
+                                     int bits,
+                                     int value));
+/*
+     deflatePrime() inserts bits in the deflate output stream.  The intent
+   is that this function is used to start off the deflate output with the bits
+   leftover from a previous deflate stream when appending to it.  As such, this
+   function can only be used for raw deflate, and must be used before the first
+   deflate() call after a deflateInit2() or deflateReset().  bits must be less
+   than or equal to 16, and that many of the least significant bits of value
+   will be inserted in the output.
+
+     deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough
+   room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the
+   source stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm,
+                                         gz_headerp head));
+/*
+     deflateSetHeader() provides gzip header information for when a gzip
+   stream is requested by deflateInit2().  deflateSetHeader() may be called
+   after deflateInit2() or deflateReset() and before the first call of
+   deflate().  The text, time, os, extra field, name, and comment information
+   in the provided gz_header structure are written to the gzip header (xflag is
+   ignored -- the extra flags are set according to the compression level).  The
+   caller must assure that, if not Z_NULL, name and comment are terminated with
+   a zero byte, and that if extra is not Z_NULL, that extra_len bytes are
+   available there.  If hcrc is true, a gzip header crc is included.  Note that
+   the current versions of the command-line version of gzip (up through version
+   1.3.x) do not support header crc's, and will report that it is a "multi-part
+   gzip file" and give up.
+
+     If deflateSetHeader is not used, the default gzip header has text false,
+   the time set to zero, and os set to 255, with no extra, name, or comment
+   fields.  The gzip header is returned to the default state by deflateReset().
+
+     deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm,
+                                     int  windowBits));
+
+     This is another version of inflateInit with an extra parameter.  The
+   fields next_in, avail_in, zalloc, zfree and opaque must be initialized
+   before by the caller.
+
+     The windowBits parameter is the base two logarithm of the maximum window
+   size (the size of the history buffer).  It should be in the range 8..15 for
+   this version of the library.  The default value is 15 if inflateInit is used
+   instead.  windowBits must be greater than or equal to the windowBits value
+   provided to deflateInit2() while compressing, or it must be equal to 15 if
+   deflateInit2() was not used.  If a compressed stream with a larger window
+   size is given as input, inflate() will return with the error code
+   Z_DATA_ERROR instead of trying to allocate a larger window.
+
+     windowBits can also be zero to request that inflate use the window size in
+   the zlib header of the compressed stream.
+
+     windowBits can also be -8..-15 for raw inflate.  In this case, -windowBits
+   determines the window size.  inflate() will then process raw deflate data,
+   not looking for a zlib or gzip header, not generating a check value, and not
+   looking for any check values for comparison at the end of the stream.  This
+   is for use with other formats that use the deflate compressed data format
+   such as zip.  Those formats provide their own check values.  If a custom
+   format is developed using the raw deflate format for compressed data, it is
+   recommended that a check value such as an adler32 or a crc32 be applied to
+   the uncompressed data as is done in the zlib, gzip, and zip formats.  For
+   most applications, the zlib format should be used as is.  Note that comments
+   above on the use in deflateInit2() applies to the magnitude of windowBits.
+
+     windowBits can also be greater than 15 for optional gzip decoding.  Add
+   32 to windowBits to enable zlib and gzip decoding with automatic header
+   detection, or add 16 to decode only the gzip format (the zlib format will
+   return a Z_DATA_ERROR).  If a gzip stream is being decoded, strm->adler is a
+   crc32 instead of an adler32.
+
+     inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_VERSION_ERROR if the zlib library version is incompatible with the
+   version assumed by the caller, or Z_STREAM_ERROR if the parameters are
+   invalid, such as a null pointer to the structure.  msg is set to null if
+   there is no error message.  inflateInit2 does not perform any decompression
+   apart from possibly reading the zlib header if present: actual decompression
+   will be done by inflate().  (So next_in and avail_in may be modified, but
+   next_out and avail_out are unused and unchanged.) The current implementation
+   of inflateInit2() does not process any header information -- that is
+   deferred until inflate() is called.
+*/
+
+ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm,
+                                             const Bytef *dictionary,
+                                             uInt  dictLength));
+/*
+     Initializes the decompression dictionary from the given uncompressed byte
+   sequence.  This function must be called immediately after a call of inflate,
+   if that call returned Z_NEED_DICT.  The dictionary chosen by the compressor
+   can be determined from the adler32 value returned by that call of inflate.
+   The compressor and decompressor must use exactly the same dictionary (see
+   deflateSetDictionary).  For raw inflate, this function can be called at any
+   time to set the dictionary.  If the provided dictionary is smaller than the
+   window and there is already data in the window, then the provided dictionary
+   will amend what's there.  The application must insure that the dictionary
+   that was used for compression is provided.
+
+     inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a
+   parameter is invalid (e.g.  dictionary being Z_NULL) or the stream state is
+   inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the
+   expected one (incorrect adler32 value).  inflateSetDictionary does not
+   perform any decompression: this will be done by subsequent calls of
+   inflate().
+*/
+
+ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm,
+                                             Bytef *dictionary,
+                                             uInt  *dictLength));
+/*
+     Returns the sliding dictionary being maintained by inflate.  dictLength is
+   set to the number of bytes in the dictionary, and that many bytes are copied
+   to dictionary.  dictionary must have enough space, where 32768 bytes is
+   always enough.  If inflateGetDictionary() is called with dictionary equal to
+   Z_NULL, then only the dictionary length is returned, and nothing is copied.
+   Similary, if dictLength is Z_NULL, then it is not set.
+
+     inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the
+   stream state is inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm));
+/*
+     Skips invalid compressed data until a possible full flush point (see above
+   for the description of deflate with Z_FULL_FLUSH) can be found, or until all
+   available input is skipped.  No output is provided.
+
+     inflateSync searches for a 00 00 FF FF pattern in the compressed data.
+   All full flush points have this pattern, but not all occurrences of this
+   pattern are full flush points.
+
+     inflateSync returns Z_OK if a possible full flush point has been found,
+   Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point
+   has been found, or Z_STREAM_ERROR if the stream structure was inconsistent.
+   In the success case, the application may save the current current value of
+   total_in which indicates where valid compressed data was found.  In the
+   error case, the application may repeatedly call inflateSync, providing more
+   input each time, until success or end of the input data.
+*/
+
+ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest,
+                                    z_streamp source));
+/*
+     Sets the destination stream as a complete copy of the source stream.
+
+     This function can be useful when randomly accessing a large stream.  The
+   first pass through the stream can periodically record the inflate state,
+   allowing restarting inflate at those points when randomly accessing the
+   stream.
+
+     inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_STREAM_ERROR if the source stream state was inconsistent
+   (such as zalloc being Z_NULL).  msg is left unchanged in both source and
+   destination.
+*/
+
+ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm));
+/*
+     This function is equivalent to inflateEnd followed by inflateInit,
+   but does not free and reallocate all the internal decompression state.  The
+   stream will keep attributes that may have been set by inflateInit2.
+
+     inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being Z_NULL).
+*/
+
+ZEXTERN int ZEXPORT inflateReset2 OF((z_streamp strm,
+                                      int windowBits));
+/*
+     This function is the same as inflateReset, but it also permits changing
+   the wrap and window size requests.  The windowBits parameter is interpreted
+   the same as it is for inflateInit2.
+
+     inflateReset2 returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being Z_NULL), or if
+   the windowBits parameter is invalid.
+*/
+
+ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm,
+                                     int bits,
+                                     int value));
+/*
+     This function inserts bits in the inflate input stream.  The intent is
+   that this function is used to start inflating at a bit position in the
+   middle of a byte.  The provided bits will be used before any bytes are used
+   from next_in.  This function should only be used with raw inflate, and
+   should be used before the first inflate() call after inflateInit2() or
+   inflateReset().  bits must be less than or equal to 16, and that many of the
+   least significant bits of value will be inserted in the input.
+
+     If bits is negative, then the input stream bit buffer is emptied.  Then
+   inflatePrime() can be called again to put bits in the buffer.  This is used
+   to clear out bits leftover after feeding inflate a block description prior
+   to feeding inflate codes.
+
+     inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+ZEXTERN long ZEXPORT inflateMark OF((z_streamp strm));
+/*
+     This function returns two values, one in the lower 16 bits of the return
+   value, and the other in the remaining upper bits, obtained by shifting the
+   return value down 16 bits.  If the upper value is -1 and the lower value is
+   zero, then inflate() is currently decoding information outside of a block.
+   If the upper value is -1 and the lower value is non-zero, then inflate is in
+   the middle of a stored block, with the lower value equaling the number of
+   bytes from the input remaining to copy.  If the upper value is not -1, then
+   it is the number of bits back from the current bit position in the input of
+   the code (literal or length/distance pair) currently being processed.  In
+   that case the lower value is the number of bytes already emitted for that
+   code.
+
+     A code is being processed if inflate is waiting for more input to complete
+   decoding of the code, or if it has completed decoding but is waiting for
+   more output space to write the literal or match data.
+
+     inflateMark() is used to mark locations in the input data for random
+   access, which may be at bit positions, and to note those cases where the
+   output of a code may span boundaries of random access blocks.  The current
+   location in the input stream can be determined from avail_in and data_type
+   as noted in the description for the Z_BLOCK flush parameter for inflate.
+
+     inflateMark returns the value noted above or -1 << 16 if the provided
+   source stream state was inconsistent.
+*/
+
+ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm,
+                                         gz_headerp head));
+/*
+     inflateGetHeader() requests that gzip header information be stored in the
+   provided gz_header structure.  inflateGetHeader() may be called after
+   inflateInit2() or inflateReset(), and before the first call of inflate().
+   As inflate() processes the gzip stream, head->done is zero until the header
+   is completed, at which time head->done is set to one.  If a zlib stream is
+   being decoded, then head->done is set to -1 to indicate that there will be
+   no gzip header information forthcoming.  Note that Z_BLOCK or Z_TREES can be
+   used to force inflate() to return immediately after header processing is
+   complete and before any actual data is decompressed.
+
+     The text, time, xflags, and os fields are filled in with the gzip header
+   contents.  hcrc is set to true if there is a header CRC.  (The header CRC
+   was valid if done is set to one.) If extra is not Z_NULL, then extra_max
+   contains the maximum number of bytes to write to extra.  Once done is true,
+   extra_len contains the actual extra field length, and extra contains the
+   extra field, or that field truncated if extra_max is less than extra_len.
+   If name is not Z_NULL, then up to name_max characters are written there,
+   terminated with a zero unless the length is greater than name_max.  If
+   comment is not Z_NULL, then up to comm_max characters are written there,
+   terminated with a zero unless the length is greater than comm_max.  When any
+   of extra, name, or comment are not Z_NULL and the respective field is not
+   present in the header, then that field is set to Z_NULL to signal its
+   absence.  This allows the use of deflateSetHeader() with the returned
+   structure to duplicate the header.  However if those fields are set to
+   allocated memory, then the application will need to save those pointers
+   elsewhere so that they can be eventually freed.
+
+     If inflateGetHeader is not used, then the header information is simply
+   discarded.  The header is always checked for validity, including the header
+   CRC if present.  inflateReset() will reset the process to discard the header
+   information.  The application would need to call inflateGetHeader() again to
+   retrieve the header from the next gzip stream.
+
+     inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent.
+*/
+
+/*
+ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits,
+                                        unsigned char FAR *window));
+
+     Initialize the internal stream state for decompression using inflateBack()
+   calls.  The fields zalloc, zfree and opaque in strm must be initialized
+   before the call.  If zalloc and zfree are Z_NULL, then the default library-
+   derived memory allocation routines are used.  windowBits is the base two
+   logarithm of the window size, in the range 8..15.  window is a caller
+   supplied buffer of that size.  Except for special applications where it is
+   assured that deflate was used with small window sizes, windowBits must be 15
+   and a 32K byte window must be supplied to be able to decompress general
+   deflate streams.
+
+     See inflateBack() for the usage of these routines.
+
+     inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of
+   the parameters are invalid, Z_MEM_ERROR if the internal state could not be
+   allocated, or Z_VERSION_ERROR if the version of the library does not match
+   the version of the header file.
+*/
+
+typedef unsigned (*in_func) OF((void FAR *,
+                                z_const unsigned char FAR * FAR *));
+typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned));
+
+ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm,
+                                    in_func in, void FAR *in_desc,
+                                    out_func out, void FAR *out_desc));
+/*
+     inflateBack() does a raw inflate with a single call using a call-back
+   interface for input and output.  This is potentially more efficient than
+   inflate() for file i/o applications, in that it avoids copying between the
+   output and the sliding window by simply making the window itself the output
+   buffer.  inflate() can be faster on modern CPUs when used with large
+   buffers.  inflateBack() trusts the application to not change the output
+   buffer passed by the output function, at least until inflateBack() returns.
+
+     inflateBackInit() must be called first to allocate the internal state
+   and to initialize the state with the user-provided window buffer.
+   inflateBack() may then be used multiple times to inflate a complete, raw
+   deflate stream with each call.  inflateBackEnd() is then called to free the
+   allocated state.
+
+     A raw deflate stream is one with no zlib or gzip header or trailer.
+   This routine would normally be used in a utility that reads zip or gzip
+   files and writes out uncompressed files.  The utility would decode the
+   header and process the trailer on its own, hence this routine expects only
+   the raw deflate stream to decompress.  This is different from the normal
+   behavior of inflate(), which expects either a zlib or gzip header and
+   trailer around the deflate stream.
+
+     inflateBack() uses two subroutines supplied by the caller that are then
+   called by inflateBack() for input and output.  inflateBack() calls those
+   routines until it reads a complete deflate stream and writes out all of the
+   uncompressed data, or until it encounters an error.  The function's
+   parameters and return types are defined above in the in_func and out_func
+   typedefs.  inflateBack() will call in(in_desc, &buf) which should return the
+   number of bytes of provided input, and a pointer to that input in buf.  If
+   there is no input available, in() must return zero--buf is ignored in that
+   case--and inflateBack() will return a buffer error.  inflateBack() will call
+   out(out_desc, buf, len) to write the uncompressed data buf[0..len-1].  out()
+   should return zero on success, or non-zero on failure.  If out() returns
+   non-zero, inflateBack() will return with an error.  Neither in() nor out()
+   are permitted to change the contents of the window provided to
+   inflateBackInit(), which is also the buffer that out() uses to write from.
+   The length written by out() will be at most the window size.  Any non-zero
+   amount of input may be provided by in().
+
+     For convenience, inflateBack() can be provided input on the first call by
+   setting strm->next_in and strm->avail_in.  If that input is exhausted, then
+   in() will be called.  Therefore strm->next_in must be initialized before
+   calling inflateBack().  If strm->next_in is Z_NULL, then in() will be called
+   immediately for input.  If strm->next_in is not Z_NULL, then strm->avail_in
+   must also be initialized, and then if strm->avail_in is not zero, input will
+   initially be taken from strm->next_in[0 ..  strm->avail_in - 1].
+
+     The in_desc and out_desc parameters of inflateBack() is passed as the
+   first parameter of in() and out() respectively when they are called.  These
+   descriptors can be optionally used to pass any information that the caller-
+   supplied in() and out() functions need to do their job.
+
+     On return, inflateBack() will set strm->next_in and strm->avail_in to
+   pass back any unused input that was provided by the last in() call.  The
+   return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR
+   if in() or out() returned an error, Z_DATA_ERROR if there was a format error
+   in the deflate stream (in which case strm->msg is set to indicate the nature
+   of the error), or Z_STREAM_ERROR if the stream was not properly initialized.
+   In the case of Z_BUF_ERROR, an input or output error can be distinguished
+   using strm->next_in which will be Z_NULL only if in() returned an error.  If
+   strm->next_in is not Z_NULL, then the Z_BUF_ERROR was due to out() returning
+   non-zero.  (in() will always be called before out(), so strm->next_in is
+   assured to be defined if out() returns non-zero.) Note that inflateBack()
+   cannot return Z_OK.
+*/
+
+ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm));
+/*
+     All memory allocated by inflateBackInit() is freed.
+
+     inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream
+   state was inconsistent.
+*/
+
+/* Declaration not used by TNG is not available */
+/*ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void));*/
+/* Return flags indicating compile-time options.
+
+    Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other:
+     1.0: size of uInt
+     3.2: size of uLong
+     5.4: size of voidpf (pointer)
+     7.6: size of z_off_t
+
+    Compiler, assembler, and debug options:
+     8: DEBUG
+     9: ASMV or ASMINF -- use ASM code
+     10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention
+     11: 0 (reserved)
+
+    One-time table building (smaller code, but not thread-safe if true):
+     12: BUILDFIXED -- build static block decoding tables when needed
+     13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed
+     14,15: 0 (reserved)
+
+    Library content (indicates missing functionality):
+     16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking
+                          deflate code when not needed)
+     17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect
+                    and decode gzip streams (to avoid linking crc code)
+     18-19: 0 (reserved)
+
+    Operation variations (changes in library functionality):
+     20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate
+     21: FASTEST -- deflate algorithm with only one, lowest compression level
+     22,23: 0 (reserved)
+
+    The sprintf variant used by gzprintf (zero is best):
+     24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format
+     25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure!
+     26: 0 = returns value, 1 = void -- 1 means inferred string length returned
+
+    Remainder:
+     27-31: 0 (reserved)
+ */
+
+#ifndef Z_SOLO
+
+                        /* utility functions */
+
+/*
+     The following utility functions are implemented on top of the basic
+   stream-oriented functions.  To simplify the interface, some default options
+   are assumed (compression level and memory usage, standard memory allocation
+   functions).  The source code of these utility functions can be modified if
+   you need special options.
+*/
+
+ZEXTERN int ZEXPORT compress OF((Bytef *dest,   uLongf *destLen,
+                                 const Bytef *source, uLong sourceLen));
+/*
+     Compresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer.  Upon entry, destLen is the total size
+   of the destination buffer, which must be at least the value returned by
+   compressBound(sourceLen).  Upon exit, destLen is the actual size of the
+   compressed buffer.
+
+     compress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer.
+*/
+
+ZEXTERN int ZEXPORT compress2 OF((Bytef *dest,   uLongf *destLen,
+                                  const Bytef *source, uLong sourceLen,
+                                  int level));
+/*
+     Compresses the source buffer into the destination buffer.  The level
+   parameter has the same meaning as in deflateInit.  sourceLen is the byte
+   length of the source buffer.  Upon entry, destLen is the total size of the
+   destination buffer, which must be at least the value returned by
+   compressBound(sourceLen).  Upon exit, destLen is the actual size of the
+   compressed buffer.
+
+     compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough
+   memory, Z_BUF_ERROR if there was not enough room in the output buffer,
+   Z_STREAM_ERROR if the level parameter is invalid.
+*/
+
+ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen));
+/*
+     compressBound() returns an upper bound on the compressed size after
+   compress() or compress2() on sourceLen bytes.  It would be used before a
+   compress() or compress2() call to allocate the destination buffer.
+*/
+
+ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen,
+                                   const Bytef *source, uLong sourceLen));
+/*
+     Decompresses the source buffer into the destination buffer.  sourceLen is
+   the byte length of the source buffer.  Upon entry, destLen is the total size
+   of the destination buffer, which must be large enough to hold the entire
+   uncompressed data.  (The size of the uncompressed data must have been saved
+   previously by the compressor and transmitted to the decompressor by some
+   mechanism outside the scope of this compression library.) Upon exit, destLen
+   is the actual size of the uncompressed buffer.
+
+     uncompress returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory, Z_BUF_ERROR if there was not enough room in the output
+   buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete.  In
+   the case where there is not enough room, uncompress() will fill the output
+   buffer with the uncompressed data up to that point.
+*/
+
+                        /* gzip file access functions */
+
+/*
+     This library supports reading and writing files in gzip (.gz) format with
+   an interface similar to that of stdio, using the functions that start with
+   "gz".  The gzip format is different from the zlib format.  gzip is a gzip
+   wrapper, documented in RFC 1952, wrapped around a deflate stream.
+*/
+
+typedef struct gzFile_s *gzFile;    /* semi-opaque gzip file descriptor */
+
+/*
+ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
+
+     Opens a gzip (.gz) file for reading or writing.  The mode parameter is as
+   in fopen ("rb" or "wb") but can also include a compression level ("wb9") or
+   a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only
+   compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F'
+   for fixed code compression as in "wb9F".  (See the description of
+   deflateInit2 for more information about the strategy parameter.)  'T' will
+   request transparent writing or appending with no compression and not using
+   the gzip format.
+
+     "a" can be used instead of "w" to request that the gzip stream that will
+   be written be appended to the file.  "+" will result in an error, since
+   reading and writing to the same gzip file is not supported.  The addition of
+   "x" when writing will create the file exclusively, which fails if the file
+   already exists.  On systems that support it, the addition of "e" when
+   reading or writing will set the flag to close the file on an execve() call.
+
+     These functions, as well as gzip, will read and decode a sequence of gzip
+   streams in a file.  The append function of gzopen() can be used to create
+   such a file.  (Also see gzflush() for another way to do this.)  When
+   appending, gzopen does not test whether the file begins with a gzip stream,
+   nor does it look for the end of the gzip streams to begin appending.  gzopen
+   will simply append a gzip stream to the existing file.
+
+     gzopen can be used to read a file which is not in gzip format; in this
+   case gzread will directly read from the file without decompression.  When
+   reading, this will be detected automatically by looking for the magic two-
+   byte gzip header.
+
+     gzopen returns NULL if the file could not be opened, if there was
+   insufficient memory to allocate the gzFile state, or if an invalid mode was
+   specified (an 'r', 'w', or 'a' was not provided, or '+' was provided).
+   errno can be checked to determine if the reason gzopen failed was that the
+   file could not be opened.
+*/
+
+ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode));
+/*
+     gzdopen associates a gzFile with the file descriptor fd.  File descriptors
+   are obtained from calls like open, dup, creat, pipe or fileno (if the file
+   has been previously opened with fopen).  The mode parameter is as in gzopen.
+
+     The next call of gzclose on the returned gzFile will also close the file
+   descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor
+   fd.  If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd,
+   mode);.  The duplicated descriptor should be saved to avoid a leak, since
+   gzdopen does not close fd if it fails.  If you are using fileno() to get the
+   file descriptor from a FILE *, then you will have to use dup() to avoid
+   double-close()ing the file descriptor.  Both gzclose() and fclose() will
+   close the associated file descriptor, so they need to have different file
+   descriptors.
+
+     gzdopen returns NULL if there was insufficient memory to allocate the
+   gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not
+   provided, or '+' was provided), or if fd is -1.  The file descriptor is not
+   used until the next gz* read, write, seek, or close operation, so gzdopen
+   will not detect if fd is invalid (unless fd is -1).
+*/
+
+ZEXTERN int ZEXPORT gzbuffer OF((gzFile file, unsigned size));
+/*
+     Set the internal buffer size used by this library's functions.  The
+   default buffer size is 8192 bytes.  This function must be called after
+   gzopen() or gzdopen(), and before any other calls that read or write the
+   file.  The buffer memory allocation is always deferred to the first read or
+   write.  Two buffers are allocated, either both of the specified size when
+   writing, or one of the specified size and the other twice that size when
+   reading.  A larger buffer size of, for example, 64K or 128K bytes will
+   noticeably increase the speed of decompression (reading).
+
+     The new buffer size also affects the maximum length for gzprintf().
+
+     gzbuffer() returns 0 on success, or -1 on failure, such as being called
+   too late.
+*/
+
+ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy));
+/*
+     Dynamically update the compression level or strategy.  See the description
+   of deflateInit2 for the meaning of these parameters.
+
+     gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not
+   opened for writing.
+*/
+
+ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
+/*
+     Reads the given number of uncompressed bytes from the compressed file.  If
+   the input file is not in gzip format, gzread copies the given number of
+   bytes into the buffer directly from the file.
+
+     After reaching the end of a gzip stream in the input, gzread will continue
+   to read, looking for another gzip stream.  Any number of gzip streams may be
+   concatenated in the input file, and will all be decompressed by gzread().
+   If something other than a gzip stream is encountered after a gzip stream,
+   that remaining trailing garbage is ignored (and no error is returned).
+
+     gzread can be used to read a gzip file that is being concurrently written.
+   Upon reaching the end of the input, gzread will return with the available
+   data.  If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then
+   gzclearerr can be used to clear the end of file indicator in order to permit
+   gzread to be tried again.  Z_OK indicates that a gzip stream was completed
+   on the last gzread.  Z_BUF_ERROR indicates that the input file ended in the
+   middle of a gzip stream.  Note that gzread does not return -1 in the event
+   of an incomplete gzip stream.  This error is deferred until gzclose(), which
+   will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip
+   stream.  Alternatively, gzerror can be used before gzclose to detect this
+   case.
+
+     gzread returns the number of uncompressed bytes actually read, less than
+   len for end of file, or -1 for error.
+*/
+
+ZEXTERN int ZEXPORT gzwrite OF((gzFile file,
+                                voidpc buf, unsigned len));
+/*
+     Writes the given number of uncompressed bytes into the compressed file.
+   gzwrite returns the number of uncompressed bytes written or 0 in case of
+   error.
+*/
+
+ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...));
+/*
+     Converts, formats, and writes the arguments to the compressed file under
+   control of the format string, as in fprintf.  gzprintf returns the number of
+   uncompressed bytes actually written, or 0 in case of error.  The number of
+   uncompressed bytes written is limited to 8191, or one less than the buffer
+   size given to gzbuffer().  The caller should assure that this limit is not
+   exceeded.  If it is exceeded, then gzprintf() will return an error (0) with
+   nothing written.  In this case, there may also be a buffer overflow with
+   unpredictable consequences, which is possible only if zlib was compiled with
+   the insecure functions sprintf() or vsprintf() because the secure snprintf()
+   or vsnprintf() functions were not available.  This can be determined using
+   zlibCompileFlags().
+*/
+
+ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s));
+/*
+     Writes the given null-terminated string to the compressed file, excluding
+   the terminating null character.
+
+     gzputs returns the number of characters written, or -1 in case of error.
+*/
+
+ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len));
+/*
+     Reads bytes from the compressed file until len-1 characters are read, or a
+   newline character is read and transferred to buf, or an end-of-file
+   condition is encountered.  If any characters are read or if len == 1, the
+   string is terminated with a null character.  If no characters are read due
+   to an end-of-file or len < 1, then the buffer is left untouched.
+
+     gzgets returns buf which is a null-terminated string, or it returns NULL
+   for end-of-file or in case of error.  If there was an error, the contents at
+   buf are indeterminate.
+*/
+
+ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c));
+/*
+     Writes c, converted to an unsigned char, into the compressed file.  gzputc
+   returns the value that was written, or -1 in case of error.
+*/
+
+ZEXTERN int ZEXPORT gzgetc OF((gzFile file));
+/*
+     Reads one byte from the compressed file.  gzgetc returns this byte or -1
+   in case of end of file or error.  This is implemented as a macro for speed.
+   As such, it does not do all of the checking the other functions do.  I.e.
+   it does not check to see if file is NULL, nor whether the structure file
+   points to has been clobbered or not.
+*/
+
+ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file));
+/*
+     Push one character back onto the stream to be read as the first character
+   on the next read.  At least one character of push-back is allowed.
+   gzungetc() returns the character pushed, or -1 on failure.  gzungetc() will
+   fail if c is -1, and may fail if a character has been pushed but not read
+   yet.  If gzungetc is used immediately after gzopen or gzdopen, at least the
+   output buffer size of pushed characters is allowed.  (See gzbuffer above.)
+   The pushed character will be discarded if the stream is repositioned with
+   gzseek() or gzrewind().
+*/
+
+ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush));
+/*
+     Flushes all pending output into the compressed file.  The parameter flush
+   is as in the deflate() function.  The return value is the zlib error number
+   (see function gzerror below).  gzflush is only permitted when writing.
+
+     If the flush parameter is Z_FINISH, the remaining data is written and the
+   gzip stream is completed in the output.  If gzwrite() is called again, a new
+   gzip stream will be started in the output.  gzread() is able to read such
+   concatented gzip streams.
+
+     gzflush should be called only when strictly necessary because it will
+   degrade compression if called too often.
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file,
+                                   z_off_t offset, int whence));
+
+     Sets the starting position for the next gzread or gzwrite on the given
+   compressed file.  The offset represents a number of bytes in the
+   uncompressed data stream.  The whence parameter is defined as in lseek(2);
+   the value SEEK_END is not supported.
+
+     If the file is opened for reading, this function is emulated but can be
+   extremely slow.  If the file is opened for writing, only forward seeks are
+   supported; gzseek then compresses a sequence of zeroes up to the new
+   starting position.
+
+     gzseek returns the resulting offset location as measured in bytes from
+   the beginning of the uncompressed stream, or -1 in case of error, in
+   particular if the file is opened for writing and the new starting position
+   would be before the current position.
+*/
+
+ZEXTERN int ZEXPORT    gzrewind OF((gzFile file));
+/*
+     Rewinds the given file. This function is supported only for reading.
+
+     gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET)
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT    gztell OF((gzFile file));
+
+     Returns the starting position for the next gzread or gzwrite on the given
+   compressed file.  This position represents a number of bytes in the
+   uncompressed data stream, and is zero when starting, even if appending or
+   reading a gzip stream from the middle of a file using gzdopen().
+
+     gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR)
+*/
+
+/*
+ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile file));
+
+     Returns the current offset in the file being read or written.  This offset
+   includes the count of bytes that precede the gzip stream, for example when
+   appending or when using gzdopen() for reading.  When reading, the offset
+   does not include as yet unused buffered input.  This information can be used
+   for a progress indicator.  On error, gzoffset() returns -1.
+*/
+
+ZEXTERN int ZEXPORT gzeof OF((gzFile file));
+/*
+     Returns true (1) if the end-of-file indicator has been set while reading,
+   false (0) otherwise.  Note that the end-of-file indicator is set only if the
+   read tried to go past the end of the input, but came up short.  Therefore,
+   just like feof(), gzeof() may return false even if there is no more data to
+   read, in the event that the last read request was for the exact number of
+   bytes remaining in the input file.  This will happen if the input file size
+   is an exact multiple of the buffer size.
+
+     If gzeof() returns true, then the read functions will return no more data,
+   unless the end-of-file indicator is reset by gzclearerr() and the input file
+   has grown since the previous end of file was detected.
+*/
+
+ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
+/*
+     Returns true (1) if file is being copied directly while reading, or false
+   (0) if file is a gzip stream being decompressed.
+
+     If the input file is empty, gzdirect() will return true, since the input
+   does not contain a gzip stream.
+
+     If gzdirect() is used immediately after gzopen() or gzdopen() it will
+   cause buffers to be allocated to allow reading the file to determine if it
+   is a gzip file.  Therefore if gzbuffer() is used, it should be called before
+   gzdirect().
+
+     When writing, gzdirect() returns true (1) if transparent writing was
+   requested ("wT" for the gzopen() mode), or false (0) otherwise.  (Note:
+   gzdirect() is not needed when writing.  Transparent writing must be
+   explicitly requested, so the application already knows the answer.  When
+   linking statically, using gzdirect() will include all of the zlib code for
+   gzip file reading and decompression, which may not be desired.)
+*/
+
+ZEXTERN int ZEXPORT    gzclose OF((gzFile file));
+/*
+     Flushes all pending output if necessary, closes the compressed file and
+   deallocates the (de)compression state.  Note that once file is closed, you
+   cannot call gzerror with file, since its structures have been deallocated.
+   gzclose must not be called more than once on the same file, just as free
+   must not be called more than once on the same allocation.
+
+     gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a
+   file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the
+   last read ended in the middle of a gzip stream, or Z_OK on success.
+*/
+
+ZEXTERN int ZEXPORT gzclose_r OF((gzFile file));
+ZEXTERN int ZEXPORT gzclose_w OF((gzFile file));
+/*
+     Same as gzclose(), but gzclose_r() is only for use when reading, and
+   gzclose_w() is only for use when writing or appending.  The advantage to
+   using these instead of gzclose() is that they avoid linking in zlib
+   compression or decompression code that is not used when only reading or only
+   writing respectively.  If gzclose() is used, then both compression and
+   decompression code will be included the application when linking to a static
+   zlib library.
+*/
+
+ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum));
+/*
+     Returns the error message for the last error which occurred on the given
+   compressed file.  errnum is set to zlib error number.  If an error occurred
+   in the file system and not in the compression library, errnum is set to
+   Z_ERRNO and the application may consult errno to get the exact error code.
+
+     The application must not modify the returned string.  Future calls to
+   this function may invalidate the previously returned string.  If file is
+   closed, then the string previously returned by gzerror will no longer be
+   available.
+
+     gzerror() should be used to distinguish errors from end-of-file for those
+   functions above that do not distinguish those cases in their return values.
+*/
+
+ZEXTERN void ZEXPORT gzclearerr OF((gzFile file));
+/*
+     Clears the error and end-of-file flags for file.  This is analogous to the
+   clearerr() function in stdio.  This is useful for continuing to read a gzip
+   file that is being written concurrently.
+*/
+
+#endif /* !Z_SOLO */
+
+                        /* checksum functions */
+
+/*
+     These functions are not related to compression but are exported
+   anyway because they might be useful in applications using the compression
+   library.
+*/
+
+ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len));
+/*
+     Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+   return the updated checksum.  If buf is Z_NULL, this function returns the
+   required initial value for the checksum.
+
+     An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+   much faster.
+
+   Usage example:
+
+     uLong adler = adler32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       adler = adler32(adler, buffer, length);
+     }
+     if (adler != original_adler) error();
+*/
+
+/*
+ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2,
+                                          z_off_t len2));
+
+     Combine two Adler-32 checksums into one.  For two sequences of bytes, seq1
+   and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for
+   each, adler1 and adler2.  adler32_combine() returns the Adler-32 checksum of
+   seq1 and seq2 concatenated, requiring only adler1, adler2, and len2.  Note
+   that the z_off_t type (like off_t) is a signed integer.  If len2 is
+   negative, the result has no meaning or utility.
+*/
+
+ZEXTERN uLong ZEXPORT crc32   OF((uLong crc, const Bytef *buf, uInt len));
+/*
+     Update a running CRC-32 with the bytes buf[0..len-1] and return the
+   updated CRC-32.  If buf is Z_NULL, this function returns the required
+   initial value for the crc.  Pre- and post-conditioning (one's complement) is
+   performed within this function so it shouldn't be done by the application.
+
+   Usage example:
+
+     uLong crc = crc32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       crc = crc32(crc, buffer, length);
+     }
+     if (crc != original_crc) error();
+*/
+
+/*
+ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2));
+
+     Combine two CRC-32 check values into one.  For two sequences of bytes,
+   seq1 and seq2 with lengths len1 and len2, CRC-32 check values were
+   calculated for each, crc1 and crc2.  crc32_combine() returns the CRC-32
+   check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and
+   len2.
+*/
+
+
+                        /* various hacks, don't look :) */
+
+/* deflateInit and inflateInit are macros to allow checking the zlib version
+ * and the compiler's view of z_stream:
+ */
+ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level,
+                                     const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm,
+                                     const char *version, int stream_size));
+ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int  level, int  method,
+                                      int windowBits, int memLevel,
+                                      int strategy, const char *version,
+                                      int stream_size));
+ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int  windowBits,
+                                      const char *version, int stream_size));
+ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits,
+                                         unsigned char FAR *window,
+                                         const char *version,
+                                         int stream_size));
+#define deflateInit(strm, level) \
+        deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream))
+#define inflateInit(strm) \
+        inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream))
+#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \
+        deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\
+                      (strategy), ZLIB_VERSION, (int)sizeof(z_stream))
+#define inflateInit2(strm, windowBits) \
+        inflateInit2_((strm), (windowBits), ZLIB_VERSION, \
+                      (int)sizeof(z_stream))
+#define inflateBackInit(strm, windowBits, window) \
+        inflateBackInit_((strm), (windowBits), (window), \
+                      ZLIB_VERSION, (int)sizeof(z_stream))
+
+#ifndef Z_SOLO
+
+/* gzgetc() macro and its supporting function and exposed data structure.  Note
+ * that the real internal state is much larger than the exposed structure.
+ * This abbreviated structure exposes just enough for the gzgetc() macro.  The
+ * user should not mess with these exposed elements, since their names or
+ * behavior could change in the future, perhaps even capriciously.  They can
+ * only be used by the gzgetc() macro.  You have been warned.
+ */
+struct gzFile_s {
+    unsigned have;
+    unsigned char *next;
+    z_off64_t pos;
+};
+ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file));  /* backward compatibility */
+#ifdef Z_PREFIX_SET
+#  undef z_gzgetc
+#  define z_gzgetc(g) \
+          ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g))
+#else
+#  define gzgetc(g) \
+          ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g))
+#endif
+
+/* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or
+ * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if
+ * both are true, the application gets the *64 functions, and the regular
+ * functions are changed to 64 bits) -- in case these are set on systems
+ * without large file support, _LFS64_LARGEFILE must also be true
+ */
+#ifdef Z_LARGE64
+   ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
+   ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int));
+   ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile));
+   ZEXTERN z_off64_t ZEXPORT gzoffset64 OF((gzFile));
+   ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off64_t));
+   ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t));
+#endif
+
+#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64)
+#  ifdef Z_PREFIX_SET
+#    define z_gzopen z_gzopen64
+#    define z_gzseek z_gzseek64
+#    define z_gztell z_gztell64
+#    define z_gzoffset z_gzoffset64
+#    define z_adler32_combine z_adler32_combine64
+#    define z_crc32_combine z_crc32_combine64
+#  else
+#    define gzopen gzopen64
+#    define gzseek gzseek64
+#    define gztell gztell64
+#    define gzoffset gzoffset64
+#    define adler32_combine adler32_combine64
+#    define crc32_combine crc32_combine64
+#  endif
+#  ifndef Z_LARGE64
+     ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *));
+     ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int));
+     ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile));
+     ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile));
+     ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
+     ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
+#  endif
+#else
+   ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *));
+   ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int));
+   ZEXTERN z_off_t ZEXPORT gztell OF((gzFile));
+   ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile));
+   ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
+   ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
+#endif
+
+#else /* Z_SOLO */
+
+   ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t));
+   ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t));
+
+#endif /* !Z_SOLO */
+
+/* hack for buggy compilers */
+#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL)
+    struct internal_state {int dummy;};
+#endif
+
+/* undocumented functions */
+ZEXTERN const char   * ZEXPORT zError           OF((int));
+ZEXTERN int            ZEXPORT inflateSyncPoint OF((z_streamp));
+ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table    OF((void));
+ZEXTERN int            ZEXPORT inflateUndermine OF((z_streamp, int));
+ZEXTERN int            ZEXPORT inflateResetKeep OF((z_streamp));
+ZEXTERN int            ZEXPORT deflateResetKeep OF((z_streamp));
+#if defined(_WIN32) && !defined(Z_SOLO)
+ZEXTERN gzFile         ZEXPORT gzopen_w OF((const wchar_t *path,
+                                            const char *mode));
+#endif
+#if defined(STDC) || defined(Z_HAVE_STDARG_H)
+#  ifndef Z_SOLO
+ZEXTERN int            ZEXPORTVA gzvprintf Z_ARG((gzFile file,
+                                                  const char *format,
+                                                  va_list va));
+#  endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ZLIB_H */
diff --git a/src/include/gromacs/external/tng_io/external/zlib/zutil.h b/src/include/gromacs/external/tng_io/external/zlib/zutil.h
new file mode 100644 (file)
index 0000000..24ab06b
--- /dev/null
@@ -0,0 +1,253 @@
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995-2013 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* @(#) $Id$ */
+
+#ifndef ZUTIL_H
+#define ZUTIL_H
+
+#ifdef HAVE_HIDDEN
+#  define ZLIB_INTERNAL __attribute__((visibility ("hidden")))
+#else
+#  define ZLIB_INTERNAL
+#endif
+
+#include "zlib.h"
+
+#if defined(STDC) && !defined(Z_SOLO)
+#  if !(defined(_WIN32_WCE) && defined(_MSC_VER))
+#    include <stddef.h>
+#  endif
+#  include <string.h>
+#  include <stdlib.h>
+#endif
+
+#ifdef Z_SOLO
+   typedef long ptrdiff_t;  /* guess -- will be caught if guess is wrong */
+#endif
+
+#ifndef local
+#  define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+typedef unsigned char  uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long  ulg;
+
+extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */
+/* (size given to avoid silly warnings with Visual C++) */
+
+#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)]
+
+#define ERR_RETURN(strm,err) \
+  return (strm->msg = ERR_MSG(err), (err))
+/* To be used only when the state is known to be valid */
+
+        /* common constants */
+
+#ifndef DEF_WBITS
+#  define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+#  define DEF_MEM_LEVEL 8
+#else
+#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES    2
+/* The three kinds of block type */
+
+#define MIN_MATCH  3
+#define MAX_MATCH  258
+/* The minimum and maximum match lengths */
+
+#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */
+
+        /* target dependencies */
+
+#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32))
+#  define OS_CODE  0x00
+#  ifndef Z_SOLO
+#    if defined(__TURBOC__) || defined(__BORLANDC__)
+#      if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__))
+         /* Allow compilation with ANSI keywords only enabled */
+         void _Cdecl farfree( void *block );
+         void *_Cdecl farmalloc( unsigned long nbytes );
+#      else
+#        include <alloc.h>
+#      endif
+#    else /* MSC or DJGPP */
+#      include <malloc.h>
+#    endif
+#  endif
+#endif
+
+#ifdef AMIGA
+#  define OS_CODE  0x01
+#endif
+
+#if defined(VAXC) || defined(VMS)
+#  define OS_CODE  0x02
+#  define F_OPEN(name, mode) \
+     fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512")
+#endif
+
+#if defined(ATARI) || defined(atarist)
+#  define OS_CODE  0x05
+#endif
+
+#ifdef OS2
+#  define OS_CODE  0x06
+#  if defined(M_I86) && !defined(Z_SOLO)
+#    include <malloc.h>
+#  endif
+#endif
+
+#if defined(MACOS) || defined(TARGET_OS_MAC)
+#  define OS_CODE  0x07
+#  ifndef Z_SOLO
+#    if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os
+#      include <unix.h> /* for fdopen */
+#    else
+#      ifndef fdopen
+#        define fdopen(fd,mode) NULL /* No fdopen() */
+#      endif
+#    endif
+#  endif
+#endif
+
+#ifdef TOPS20
+#  define OS_CODE  0x0a
+#endif
+
+#ifdef WIN32
+#  ifndef __CYGWIN__  /* Cygwin is Unix, not Win32 */
+#    define OS_CODE  0x0b
+#  endif
+#endif
+
+#ifdef __50SERIES /* Prime/PRIMOS */
+#  define OS_CODE  0x0f
+#endif
+
+#if defined(_BEOS_) || defined(RISCOS)
+#  define fdopen(fd,mode) NULL /* No fdopen() */
+#endif
+
+#if (defined(_MSC_VER) && (_MSC_VER > 600)) && !defined __INTERIX
+#  if defined(_WIN32_WCE)
+#    define fdopen(fd,mode) NULL /* No fdopen() */
+#    ifndef _PTRDIFF_T_DEFINED
+       typedef int ptrdiff_t;
+#      define _PTRDIFF_T_DEFINED
+#    endif
+#  else
+#    define fdopen(fd,type)  _fdopen(fd,type)
+#  endif
+#endif
+
+#if defined(__BORLANDC__) && !defined(MSDOS)
+  #pragma warn -8004
+  #pragma warn -8008
+  #pragma warn -8066
+#endif
+
+/* provide prototypes for these when building zlib without LFS */
+#if !defined(_WIN32) && \
+    (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0)
+    ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t));
+    ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t));
+#endif
+
+        /* common defaults */
+
+#ifndef OS_CODE
+#  define OS_CODE  0x03  /* assume Unix */
+#endif
+
+#ifndef F_OPEN
+#  define F_OPEN(name, mode) fopen((name), (mode))
+#endif
+
+         /* functions */
+
+#if defined(pyr) || defined(Z_SOLO)
+#  define NO_MEMCPY
+#endif
+#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__)
+ /* Use our own functions for small and medium model with MSC <= 5.0.
+  * You may have to use the same strategy for Borland C (untested).
+  * The __SC__ check is for Symantec.
+  */
+#  define NO_MEMCPY
+#endif
+#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY)
+#  define HAVE_MEMCPY
+#endif
+#ifdef HAVE_MEMCPY
+#  ifdef SMALL_MEDIUM /* MSDOS small or medium model */
+#    define zmemcpy _fmemcpy
+#    define zmemcmp _fmemcmp
+#    define zmemzero(dest, len) _fmemset(dest, 0, len)
+#  else
+#    define zmemcpy memcpy
+#    define zmemcmp memcmp
+#    define zmemzero(dest, len) memset(dest, 0, len)
+#  endif
+#else
+   void ZLIB_INTERNAL zmemcpy OF((Bytef* dest, const Bytef* source, uInt len));
+   int ZLIB_INTERNAL zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len));
+   void ZLIB_INTERNAL zmemzero OF((Bytef* dest, uInt len));
+#endif
+
+/* Diagnostic functions */
+#ifdef DEBUG
+#  include <stdio.h>
+   extern int ZLIB_INTERNAL z_verbose;
+   extern void ZLIB_INTERNAL z_error OF((char *m));
+#  define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+#  define Trace(x) {if (z_verbose>=0) fprintf x ;}
+#  define Tracev(x) {if (z_verbose>0) fprintf x ;}
+#  define Tracevv(x) {if (z_verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+#ifndef Z_SOLO
+   voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items,
+                                    unsigned size));
+   void ZLIB_INTERNAL zcfree  OF((voidpf opaque, voidpf ptr));
+#endif
+
+#define ZALLOC(strm, items, size) \
+           (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr)  (*((strm)->zfree))((strm)->opaque, (voidpf)(addr))
+#define TRY_FREE(s, p) {if (p) ZFREE(s, p);}
+
+/* Reverse the bytes in a 32-bit value */
+#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \
+                    (((q) & 0xff00) << 8) + (((q) & 0xff) << 24))
+
+#endif /* ZUTIL_H */
diff --git a/src/include/gromacs/external/tng_io/include/compression/bwlzh.h b/src/include/gromacs/external/tng_io/include/compression/bwlzh.h
new file mode 100644 (file)
index 0000000..ce08f69
--- /dev/null
@@ -0,0 +1,72 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef BWLZH_H
+#define BWLZH_H
+
+/* Compress the integers (positive, small integers are preferable)
+   using bwlzh compression.  The unsigned char *output should be
+   allocated to be able to hold worst case. You can obtain this length
+   conveniently by calling comp_get_buflen()
+*/
+void DECLSPECDLLEXPORT bwlzh_compress(unsigned int *vals, const int nvals,
+                 unsigned char *output, int *output_len);
+
+void DECLSPECDLLEXPORT bwlzh_compress_no_lz77(unsigned int *vals, const int nvals,
+                 unsigned char *output, int *output_len);
+
+int DECLSPECDLLEXPORT bwlzh_get_buflen(const int nvals);
+
+void DECLSPECDLLEXPORT bwlzh_decompress(unsigned char *input, const int nvals,
+                   unsigned int *vals);
+
+
+/* The routines below are mostly useful for testing, and for internal
+   use by the library. */
+
+void DECLSPECDLLEXPORT bwlzh_compress_verbose(unsigned int *vals, const int nvals,
+                         unsigned char *output, int *output_len);
+
+void DECLSPECDLLEXPORT bwlzh_compress_no_lz77_verbose(unsigned int *vals, const int nvals,
+                 unsigned char *output, int *output_len);
+
+void DECLSPECDLLEXPORT bwlzh_decompress_verbose(unsigned char *input, const int nvals,
+                           unsigned int *vals);
+
+/* Compress the integers (positive, small integers are preferable)
+   using huffman coding, with automatic selection of how to handle the
+   huffman dictionary.  The unsigned char *huffman should be allocated
+   to be able to hold worst case. You can obtain this length
+   conveniently by calling comp_huff_buflen()
+*/
+void Ptngc_comp_huff_compress(unsigned int *vals, const int nvals,
+                       unsigned char *huffman, int *huffman_len);
+
+int Ptngc_comp_huff_buflen(const int nvals);
+
+void Ptngc_comp_huff_decompress(unsigned char *huffman, const int huffman_len,
+                         unsigned int *vals);
+
+
+/* the value pointed to by chosen_algo should be
+   sent as -1 for autodetect. */
+void Ptngc_comp_huff_compress_verbose(unsigned int *vals, int nvals,
+                               unsigned char *huffman, int *huffman_len,
+                               int *huffdatalen,
+                               int *huffman_lengths,int *chosen_algo,
+                               const int isvals16);
+
+#define N_HUFFMAN_ALGO 3
+char *Ptngc_comp_get_huff_algo_name(const int algo);
+char *Ptngc_comp_get_algo_name(const int algo);
+
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/bwt.h b/src/include/gromacs/external/tng_io/include/compression/bwt.h
new file mode 100644 (file)
index 0000000..fedfc3d
--- /dev/null
@@ -0,0 +1,26 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef BWT_H
+#define BWT_H
+
+void Ptngc_comp_to_bwt(unsigned int *vals, const int nvals,
+                unsigned int *output, int *index);
+
+void Ptngc_comp_from_bwt(unsigned int *input, const int nvals, int index,
+                  unsigned int *vals);
+
+void Ptngc_bwt_merge_sort_inner(int *indices, const int nvals, unsigned int *vals,
+                                const int start, const int end,
+                                unsigned int *nrepeat,
+                                int *workarray);
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/coder.h b/src/include/gromacs/external/tng_io/include/compression/coder.h
new file mode 100644 (file)
index 0000000..d714c82
--- /dev/null
@@ -0,0 +1,48 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifndef CODER_H
+#define CODER_H
+
+#ifndef DECLSPECDLLEXPORT
+#ifdef USE_WINDOWS
+#define DECLSPECDLLEXPORT __declspec(dllexport)
+#else /* USE_WINDOWS */
+#define DECLSPECDLLEXPORT
+#endif /* USE_WINDOWS */
+#endif /* DECLSPECDLLEXPORT */
+
+struct coder
+{
+    unsigned int pack_temporary;
+    int pack_temporary_bits;
+    int stat_overflow;
+    int stat_numval;
+};
+
+struct coder DECLSPECDLLEXPORT *Ptngc_coder_init(void);
+void DECLSPECDLLEXPORT Ptngc_coder_deinit(struct coder *coder);
+unsigned char DECLSPECDLLEXPORT *Ptngc_pack_array(struct coder *coder,int *input, int *length, const int coding, const int coding_parameter, const int natoms, const int speed);
+int DECLSPECDLLEXPORT Ptngc_unpack_array(struct coder *coder,unsigned char *packed,int *output, const int length, const int coding, const int coding_parameter, const int natoms);
+unsigned char DECLSPECDLLEXPORT *Ptngc_pack_array_xtc2(struct coder *coder,int *input, int *length);
+int DECLSPECDLLEXPORT Ptngc_unpack_array_xtc2(struct coder *coder, unsigned char *packed, int *output, const int length);
+unsigned char DECLSPECDLLEXPORT *Ptngc_pack_array_xtc3(int *input, int *length, int natoms, int speed);
+int DECLSPECDLLEXPORT Ptngc_unpack_array_xtc3(unsigned char *packed,int *output, int length, int natoms);
+
+void DECLSPECDLLEXPORT Ptngc_out8bits(struct coder *coder, unsigned char **output);
+void DECLSPECDLLEXPORT Ptngc_pack_flush(struct coder *coder,unsigned char **output);
+void DECLSPECDLLEXPORT Ptngc_write_pattern(struct coder *coder,unsigned int pattern, int nbits, unsigned char **output);
+
+void DECLSPECDLLEXPORT Ptngc_writebits(struct coder *coder, unsigned int value, const int nbits, unsigned char **output_ptr);
+void DECLSPECDLLEXPORT Ptngc_write32bits(struct coder *coder,unsigned int value,int nbits, unsigned char **output_ptr);
+void DECLSPECDLLEXPORT Ptngc_writemanybits(struct coder *coder,unsigned char *value,int nbits, unsigned char **output_ptr);
+
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/dict.h b/src/include/gromacs/external/tng_io/include/compression/dict.h
new file mode 100644 (file)
index 0000000..26eed27
--- /dev/null
@@ -0,0 +1,21 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef DICT_H
+#define DICT_H
+
+void Ptngc_comp_canonical_dict(unsigned int *dict, int *ndict);
+
+void Ptngc_comp_make_dict_hist(unsigned int *vals, const int nvals,
+                        unsigned int *dict, int *ndict,
+                        unsigned int *hist);
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/fixpoint.h b/src/include/gromacs/external/tng_io/include/compression/fixpoint.h
new file mode 100644 (file)
index 0000000..7b6a667
--- /dev/null
@@ -0,0 +1,39 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifndef FIXPOINT_H
+#define FIXPOINT_H
+
+#include "../compression/my64bit.h"
+
+/* There are at least 32 bits available in a long. */
+typedef unsigned long fix_t;
+
+/* Positive double to 32 bit fixed point value */
+fix_t Ptngc_ud_to_fix_t(double d, const double max);
+
+/* double to signed 32 bit fixed point value */
+fix_t Ptngc_d_to_fix_t(double d, const double max);
+
+/* 32 bit fixed point value to positive double */
+double Ptngc_fix_t_to_ud(fix_t f, const double max);
+
+/* signed 32 bit fixed point value to double */
+double Ptngc_fix_t_to_d(fix_t f, const double max);
+
+/* Convert a floating point variable to two 32 bit integers with range
+   -2.1e9 to 2.1e9 and precision to somewhere around 1e-9. */
+void Ptngc_d_to_i32x2(double d, fix_t *hi, fix_t *lo);
+
+/* Convert two 32 bit integers to a floating point variable
+   -2.1e9 to 2.1e9 and precision to somewhere around 1e-9. */
+double Ptngc_i32x2_to_d(fix_t hi, fix_t lo);
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/huffman.h b/src/include/gromacs/external/tng_io/include/compression/huffman.h
new file mode 100644 (file)
index 0000000..f2ce187
--- /dev/null
@@ -0,0 +1,33 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef HUFFMAN_H
+#define HUFFMAN_H
+
+void Ptngc_comp_conv_to_huffman(unsigned int *vals, const int nvals,
+                         unsigned int *dict, const int ndict,
+                         unsigned int *prob,
+                         unsigned char *huffman,
+                         int *huffman_len,
+                         unsigned char *huffman_dict,
+                         int *huffman_dictlen,
+                         unsigned int *huffman_dict_unpacked,
+                         int *huffman_dict_unpackedlen);
+
+void Ptngc_comp_conv_from_huffman(unsigned char *huffman,
+                           unsigned int *vals, const int nvals,
+                           const int ndict,
+                           unsigned char *huffman_dict,
+                           const int huffman_dictlen,
+                           unsigned int *huffman_dict_unpacked,
+                           const int huffman_dict_unpackedlen);
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/lz77.h b/src/include/gromacs/external/tng_io/include/compression/lz77.h
new file mode 100644 (file)
index 0000000..d4a4beb
--- /dev/null
@@ -0,0 +1,25 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef LZ77_H
+#define LZ77_H
+
+void Ptngc_comp_to_lz77(unsigned int *vals, const int nvals,
+                 unsigned int *data, int *ndata,
+                 unsigned int *len, int *nlens,
+                 unsigned int *offsets, int *noffsets);
+
+void Ptngc_comp_from_lz77(unsigned int *data, const int ndata,
+                   unsigned int *len, const int nlens,
+                   unsigned int *offsets, const int noffsets,
+                   unsigned int *vals, const int nvals);
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/merge_sort.h b/src/include/gromacs/external/tng_io/include/compression/merge_sort.h
new file mode 100644 (file)
index 0000000..f8aaeb7
--- /dev/null
@@ -0,0 +1,20 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef MERGE_SORT_H
+#define MERGE_SORT_H
+
+void Ptngc_merge_sort(void *base, const size_t nmemb, const size_t size,
+               int (*compar)(const void *v1,const void *v2,const void *private),
+               void *private);
+
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/mtf.h b/src/include/gromacs/external/tng_io/include/compression/mtf.h
new file mode 100644 (file)
index 0000000..3dc9ace
--- /dev/null
@@ -0,0 +1,35 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef MTF_H
+#define MTF_H
+
+void Ptngc_comp_conv_to_mtf(unsigned int *vals, const int nvals,
+                     unsigned int *dict, const int ndict,
+                     unsigned int *valsmtf);
+
+void Ptngc_comp_conv_from_mtf(unsigned int *valsmtf, const int nvals,
+                       unsigned int *dict, const int ndict,
+                       unsigned int *vals);
+
+void Ptngc_comp_conv_to_mtf_partial(unsigned int *vals, const int nvals,
+                             unsigned int *valsmtf);
+
+void Ptngc_comp_conv_from_mtf_partial(unsigned int *valsmtf, const int nvals,
+                               unsigned int *vals);
+
+void Ptngc_comp_conv_to_mtf_partial3(unsigned int *vals, const int nvals,
+                              unsigned char *valsmtf);
+
+void Ptngc_comp_conv_from_mtf_partial3(unsigned char *valsmtf, const int nvals,
+                                unsigned int *vals);
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/my64bit.h b/src/include/gromacs/external/tng_io/include/compression/my64bit.h
new file mode 100644 (file)
index 0000000..c17a4a9
--- /dev/null
@@ -0,0 +1,33 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifndef MY64BIT_H
+#define MY64BIT_H
+
+#ifdef USE_STD_INTTYPES_H
+#include <inttypes.h>
+typedef int64_t my_int64_t;
+typedef uint64_t my_uint64_t;
+#define HAVE64BIT
+#else /* USE_STD_INTTYPES */
+/* The USE_WINDOWS symbol should be automatically defined in tng_compress.h */
+#include "../compression/tng_compress.h"
+#ifdef USE_WINDOWS
+typedef __int64 my_int64_t;
+typedef unsigned __int64 my_uint64_t;
+#define HAVE64BIT
+#else  /* USE_WINDOWS */
+/* Fall back to assume that we have unsigned long long */
+typedef unsigned long long my_uint64_t;
+#define HAVE64BIT
+#endif /* USE_WINDOWS */
+#endif /* USE_STD_INTTYPES */
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/rle.h b/src/include/gromacs/external/tng_io/include/compression/rle.h
new file mode 100644 (file)
index 0000000..3665dd0
--- /dev/null
@@ -0,0 +1,22 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef RLE_H
+#define RLE_H
+
+void Ptngc_comp_conv_to_rle(unsigned int *vals, const int nvals,
+                                   unsigned int *rle, int *nrle,
+                                   const int min_rle);
+
+void Ptngc_comp_conv_from_rle(unsigned int *rle,
+                                         unsigned int *vals, const int nvals);
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/tng_compress.h b/src/include/gromacs/external/tng_io/include/compression/tng_compress.h
new file mode 100644 (file)
index 0000000..0082ba3
--- /dev/null
@@ -0,0 +1,223 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifndef TNG_COMPRESS_H
+#define TNG_COMPRESS_H
+
+#ifndef USE_WINDOWS
+#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
+#define USE_WINDOWS
+#endif /* win32... */
+#endif /* not defined USE_WINDOWS */
+
+#ifndef DECLSPECDLLEXPORT
+#ifdef USE_WINDOWS
+#define DECLSPECDLLEXPORT __declspec(dllexport)
+#else /* USE_WINDOWS */
+#define DECLSPECDLLEXPORT
+#endif /* USE_WINDOWS */
+#endif /* DECLSPECDLLEXPORT */
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* tng_compress_pos expects positions to have the order:
+   first xyz, then sorted in atom order
+   then all the frames repeated, i.e.:
+   nframes * [
+    natoms* [
+      x, y, z
+    ]
+   ]
+   desired_precision what to round the numbers to, i.e. integers will be created as:
+   round(pos[]/desired_precision).
+
+   algo should first be determined by calling
+   tng_compress_pos_find_algo
+
+   The compressed data is returned in a malloced pointer (so free can
+   be called to free the memory), the number of chars in the compressed
+   data is put into *nitems.
+
+   If too large values are input (compared to the precision), NULL is returned.
+*/
+
+char DECLSPECDLLEXPORT *tng_compress_pos(double *pos, const int natoms, const int nframes,
+                                        const double desired_precision,
+                                        const int speed, int *algo,
+                                        int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_pos_float(float *pos, const int natoms, const int nframes,
+                                              const float desired_precision,
+                                              const int speed, int *algo,
+                                              int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_pos_int(int *pos, const int natoms, const int nframes,
+                                            const unsigned long prec_hi, const unsigned long prec_lo,
+                                            int speed,int *algo,
+                                            int *nitems);
+
+/* The tng_compress_pos_find_algo works the same as tng_compress_pos, but
+   it performs benchmarking to find the algorithms with the best
+   compression ratio.
+   The search is controlled by giving speed:
+   speed=1:  Fast algorithms only. This excludes all BWLZH algorithms and
+             the XTC3 algorithm.
+   speed=2:  Same as 1 and also includes the XTC3 algorithm using base compression
+             only.
+   speed=3:  Same as 2 and also includes the XTC3 algorithm which will use BWLZH
+             compression when it seems likely to give better
+             compression. Also includes the interframe BWLZH algorithm for
+             coordinates and velocities.
+   speed=4:  Enable the inter frame BWLZH algorithm for the coordinates.
+             The one-to-one BWLZH algorithm is enabled for velocities.
+   speed=5:  Enable the LZ77 part of the BWLZH algorithm.
+   speed=6:  Enable the intra frame BWLZH algorithm for the coordinates. Always try
+             the BWLZH compression in the XTC3 algorithm.
+
+   Set speed=0 to allow tng_compression to set the default speed (which is currently 2).
+   For very good compression it makes sense to choose speed=4 or speed=5
+
+   The number of items required in the algorithm array can be found
+   by calling tng_compress_nalgo
+*/
+
+char DECLSPECDLLEXPORT *tng_compress_pos_find_algo(double *pos, const int natoms, const int nframes,
+                                                  const double desired_precision,
+                                                  const int speed,
+                                                  int *algo,
+                                                  int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_pos_float_find_algo(float *pos, const int natoms, const int nframes,
+                                                        const float desired_precision,
+                                                        const int speed,
+                                                        int *algo,
+                                                        int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_pos_int_find_algo(int *pos, const int natoms, const int nframes,
+                                                      const unsigned long prec_hi, const unsigned long prec_lo,
+                                                      const int speed, int *algo,
+                                                      int *nitems);
+
+/* This returns the number of integers required for the storage of the algorithm
+   with the best compression ratio. */
+int DECLSPECDLLEXPORT tng_compress_nalgo(void);
+
+/* The following two routines does the same as the compression of the
+   positions, but compresses velocities instead. The algorithm
+   selection for velocities is different, so the position and
+   velocities routines should not be mixed. */
+
+char DECLSPECDLLEXPORT *tng_compress_vel(double *vel, const int natoms, const int nframes,
+                                        const double desired_precision,
+                                        const int speed, int *algo,
+                                        int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_vel_float(float *vel, const int natoms, const int nframes,
+                                              const float desired_precision,
+                                              const int speed, int *algo,
+                                              int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_vel_int(int *vel, const int natoms, const int nframes,
+                                            const unsigned long prec_hi, const unsigned long prec_lo,
+                                            int speed, int *algo,
+                                            int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_vel_find_algo(double *vel, const int natoms, const int nframes,
+                                                  const double desired_precision,
+                                                  const int speed,
+                                                  int *algo,
+                                                  int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_vel_float_find_algo(float *vel, const int natoms, const int nframes,
+                                                        const float desired_precision,
+                                                        const int speed,
+                                                        int *algo,
+                                                        int *nitems);
+
+char DECLSPECDLLEXPORT *tng_compress_vel_int_find_algo(int *vel, const int natoms, const int nframes,
+                                                      const unsigned long prec_hi, const unsigned long prec_lo,
+                                                      const int speed,
+                                                      int *algo,
+                                                      int *nitems);
+
+/* From a compressed block, obtain information about
+   whether it is a position or velocity block:
+   *vel=1 means velocity block, *vel=0 means position block.
+   It also gives info about the number of atoms,
+   frames, and the precision used to compress the block, and the algorithms used to
+   compress the block. The return value=0 if the block looks like a tng compressed block,
+   and 1 otherwise. If the return value is 1 no information is returned. */
+int DECLSPECDLLEXPORT tng_compress_inquire(char *data,int *vel, int *natoms,
+                                          int *nframes, double *precision,
+                                          int *algo);
+
+/* Uncompresses any tng compress block, positions or velocities. It determines whether it is positions or velocities from the data buffer. The return value is 0 if ok, and 1 if not.
+*/
+int DECLSPECDLLEXPORT tng_compress_uncompress(char *data,double *posvel);
+
+int DECLSPECDLLEXPORT tng_compress_uncompress_float(char *data,float *posvel);
+
+int DECLSPECDLLEXPORT tng_compress_uncompress_int(char *data,int *posvel, unsigned long *prec_hi, unsigned long *prec_lo);
+
+/* This converts a block of integers, as obtained from tng_compress_uncompress_int, to floating point values
+   either double precision or single precision. */
+void DECLSPECDLLEXPORT tng_compress_int_to_double(int *posvel_int, const unsigned long prec_hi, const unsigned long prec_lo,
+                                                 const int natoms, const int nframes,
+                                                 double *posvel_double);
+
+void DECLSPECDLLEXPORT tng_compress_int_to_float(int *posvel_int, const unsigned long prec_hi, const unsigned long prec_lo,
+                                                const int natoms, const int nframes,
+                                                float *posvel_float);
+
+
+/* Compression algorithms (matching the original trajng
+   assignments) The compression backends require that some of the
+   algorithms must have the same value. */
+
+#define TNG_COMPRESS_ALGO_STOPBIT 1
+#define TNG_COMPRESS_ALGO_TRIPLET 2
+#define TNG_COMPRESS_ALGO_BWLZH1  8
+#define TNG_COMPRESS_ALGO_BWLZH2  9
+
+#define TNG_COMPRESS_ALGO_POS_STOPBIT_INTER     TNG_COMPRESS_ALGO_STOPBIT
+#define TNG_COMPRESS_ALGO_POS_TRIPLET_INTER     TNG_COMPRESS_ALGO_TRIPLET
+#define TNG_COMPRESS_ALGO_POS_TRIPLET_INTRA     3
+#define TNG_COMPRESS_ALGO_POS_XTC2              5
+#define TNG_COMPRESS_ALGO_POS_TRIPLET_ONETOONE  7
+#define TNG_COMPRESS_ALGO_POS_BWLZH_INTER       TNG_COMPRESS_ALGO_BWLZH1
+#define TNG_COMPRESS_ALGO_POS_BWLZH_INTRA       TNG_COMPRESS_ALGO_BWLZH2
+#define TNG_COMPRESS_ALGO_POS_XTC3              10
+#define TNG_COMPRESS_ALGO_VEL_STOPBIT_ONETOONE  TNG_COMPRESS_ALGO_STOPBIT
+#define TNG_COMPRESS_ALGO_VEL_TRIPLET_INTER     TNG_COMPRESS_ALGO_TRIPLET
+#define TNG_COMPRESS_ALGO_VEL_TRIPLET_ONETOONE  3
+#define TNG_COMPRESS_ALGO_VEL_STOPBIT_INTER     6
+#define TNG_COMPRESS_ALGO_VEL_BWLZH_INTER       TNG_COMPRESS_ALGO_BWLZH1
+#define TNG_COMPRESS_ALGO_VEL_BWLZH_ONETOONE    TNG_COMPRESS_ALGO_BWLZH2
+#define TNG_COMPRESS_ALGO_MAX 11
+
+
+
+/* Obtain strings describing the actual algorithms. These point to static memory, so should
+   not be freed. */
+char DECLSPECDLLEXPORT *tng_compress_initial_pos_algo(int *algo);
+char DECLSPECDLLEXPORT *tng_compress_pos_algo(int *algo);
+char DECLSPECDLLEXPORT *tng_compress_initial_vel_algo(int *algo);
+char DECLSPECDLLEXPORT *tng_compress_vel_algo(int *algo);
+
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/vals16.h b/src/include/gromacs/external/tng_io/include/compression/vals16.h
new file mode 100644 (file)
index 0000000..ba1b8fb
--- /dev/null
@@ -0,0 +1,21 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef VALS16_H
+#define VALS16_H
+
+void Ptngc_comp_conv_to_vals16(unsigned int *vals, const int nvals,
+                               unsigned int *vals16, int *nvals16);
+
+void Ptngc_comp_conv_from_vals16(unsigned int *vals16, const int nvals16,
+                                 unsigned int *vals, int *nvals);
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/warnmalloc.h b/src/include/gromacs/external/tng_io/include/compression/warnmalloc.h
new file mode 100644 (file)
index 0000000..aa63111
--- /dev/null
@@ -0,0 +1,26 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef WARNMALLOC_H
+#define WARNMALLOC_H
+
+#include "../compression/tng_compress.h"
+
+void DECLSPECDLLEXPORT *Ptngc_warnmalloc_x(const size_t size, char *file, const int line);
+
+#define warnmalloc(size) Ptngc_warnmalloc_x(size,__FILE__,__LINE__)
+
+void DECLSPECDLLEXPORT *Ptngc_warnrealloc_x(void *old, const size_t size, char *file, const int line);
+
+#define warnrealloc(old,size) Ptngc_warnrealloc_x(old,size,__FILE__,__LINE__)
+
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/compression/widemuldiv.h b/src/include/gromacs/external/tng_io/include/compression/widemuldiv.h
new file mode 100644 (file)
index 0000000..dfa905b
--- /dev/null
@@ -0,0 +1,24 @@
+/* This code is part of the tng compression routines.
+ *
+ * Written by Daniel Spangberg
+ * Copyright (c) 2010, 2013, The GROMACS development team.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+
+#ifndef WIDEMULDIV_H
+#define WIDEMULDIV_H
+
+/* Add a unsigned int to a largeint. */
+void Ptngc_largeint_add(const unsigned int v1, unsigned int *largeint, const int n);
+
+/* Multiply v1 with largeint_in and return result in largeint_out */
+void Ptngc_largeint_mul(const unsigned int v1, unsigned int *largeint_in, unsigned int *largeint_out, const int n);
+
+/* Return the remainder from dividing largeint_in with v1. Result of the division is returned in largeint_out */
+unsigned int Ptngc_largeint_div(const unsigned int v1, unsigned int *largeint_in, unsigned int *largeint_out, const int n);
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/tng/md5.h b/src/include/gromacs/external/tng_io/include/tng/md5.h
new file mode 100644 (file)
index 0000000..80fbf6c
--- /dev/null
@@ -0,0 +1,113 @@
+
+/* This file has been modified in the TNG library distribution. Modifications
+ * are marked below. */
+
+/*
+  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+       http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Removed support for non-ANSI compilers; removed
+       references to Ghostscript; clarified derivation from RFC 1321;
+       now handles byte order either statically or dynamically.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+       added conditionalization for C++ compilation from Martin
+       Purschke <purschke@bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#  define md5_INCLUDED
+
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+    md5_word_t count[2];       /* message length in bits, lsw first */
+    md5_word_t abcd[4];                /* digest buffer */
+    md5_byte_t buf[64];                /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* The USE_WINDOWS define below was added in TNG library distribution of this
+ * file in order to compile properly in MSVC */
+#ifndef USE_WINDOWS
+#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
+#define USE_WINDOWS
+#endif /* win32... */
+#endif /* not defined USE_WINDOWS */
+
+/* The DECLSPECDLLEXPORT define below was added in the TNG library distribution
+ * of this file. It is also used in the function declarations. */
+#ifndef DECLSPECDLLEXPORT
+#ifdef USE_WINDOWS
+#define DECLSPECDLLEXPORT __declspec(dllexport)
+#else /* USE_WINDOWS */
+#define DECLSPECDLLEXPORT
+#endif /* USE_WINDOWS */
+#endif /* DECLSPECDLLEXPORT */
+
+/* Initialize the algorithm. */
+void DECLSPECDLLEXPORT md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void DECLSPECDLLEXPORT md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void DECLSPECDLLEXPORT md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/src/include/gromacs/external/tng_io/include/tng/tng_io.h b/src/include/gromacs/external/tng_io/include/tng/tng_io.h
new file mode 100644 (file)
index 0000000..167cca6
--- /dev/null
@@ -0,0 +1,4961 @@
+/* This code is part of the tng binary trajectory format.
+ *
+ * Written by Magnus Lundborg
+ * Copyright (c) 2012-2017, The GROMACS development team.
+ * Check out http://www.gromacs.org for more information.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+/** @file tng_io.h
+ *  @brief API for input and output of tng trajectory files
+ *  @mainpage TNG: A flexible binary trajectory format
+ *  @section intro_sec Introduction
+ *
+ * The TNG format is developed as part of the ScalaLife EU project.
+ * It is flexible by design to allow parallel writing, custom data blocks,
+ * different output frequencies and different compression algorithms.
+ *
+ * Each block can contain MD5 hashes to verify data integrity and the file
+ * can be signed by the user to ensure that the origin is correct.
+ *
+ * The intention is that the API and ABI should be stable, but it is
+ * still possible that future changes might make that impossible, in which
+ * case that will be clarified.
+ *
+ * The API and all examples are released without any warranties. Use them at
+ * your own risk.
+ *
+ * @section authors_sec Authors
+ *
+ * The TNG trajectory format is developed by:
+ *
+ * Magnus Lundborg magnus.lundborg@scilifelab.se
+ *
+ * Daniel Spångberg daniels@mkem.uu.se
+ *
+ * Rossen Apostolov rossen@kth.se
+ *
+ * The API is implemented mainly by:
+ *
+ * Magnus Lundborg
+ *
+ * @section License
+ *
+ * Copyright (c) 2012, The GROMACS development team.
+ * check out http://www.gromacs.org for more information.
+ *
+ * The TNG API is released under the Revised BSD License and is free to
+ * redistribute according to that license.
+ *
+ * A license file (named COPYING) should be included with each copy of the API.
+ *
+ * @section install_sec Installation
+ *
+ * \code
+ * mkdir build
+ *
+ * cd build
+ *
+ * cmake ..
+ *
+ * make
+ *
+ * make install
+ * \endcode
+ * Test by running:
+ * \code
+ * bin/tests/tng_testing
+ * \endcode
+ *
+ * @section change_sec Change Log
+ *
+ * See git log for full revision history.
+ *
+ * Revisions
+ * 
+ * v. 1.8 - Added GROMACS energy block IDs.
+ *        - Rewritten build system for the main library.
+ *        - Added block ID for atom (or generic particle) mass.
+ *        - Fixed bugs, such as:
+ *           - Do not switch endianness when reading and writing TNG compressed data.
+ *           - Update pointers to residues in the chain when writing multiple chains in one molecule.
+ *           - Update frame set pointers when appending to file.
+ *
+ * v. 1.7 - Fifth stable release of the API
+ *
+ *        - Added function tng_util_num_frames_with_data_of_block_id_get().
+ *        - Merged some functions and data structures
+ *          to make less difference between data blocks.
+ *        - Bugs fixed
+ *
+ * v. 1.6 - Fourth stable release of the API.
+ *
+ *        - Removed OpenMP option when building.
+ *        - Functionality for migrating data blocks.
+ *        - Improved handling of molecules.
+ *        - Improved installation of TNG documentation.
+ *        - Enhancements to CMake usage.
+ *        - Required CMake version raised to 2.8.8.
+ *        - Bugs fixed.
+ *
+ * v. 1.5 - Third stable release of the API.
+ *
+ *        - Fortran wrapper split into separate file
+ *        - Added more block IDs.
+ *        - Some new functions and utility functions added.
+ *        - Improved compression precision settings.
+ *        - Improved tests.
+ *        - Make appending to file work better.
+ *        - Modified CMake settings
+ *        - Bugs fixed
+ *
+ * v. 1.4 - Changed from LGPL to the Revised BSD License.
+ *
+ *        - More flexible support for digital signatures in header.
+ *        - Block ID numbers changed.
+ *
+ * v. 1.3 - Second stable release of the API.
+ *
+ *      - Added multiplication factor for coordinate units to general info.
+ *      - Added time stamps and time per frame in frame sets.
+ *      - High-level API functions added (not for managing molecules yet)
+ *      - Added functions for reading data blocks into 1D arrays.
+ *      - TNG compression added.
+ *      - C++ interface added.
+ *      - Avoid memory allocation if no data is submitted when adding data
+ *        blocks.
+ *      - Added function tng_num_frames_per_frame_set_set
+ *      - Added data block IDs for charges, b-factors and occupancy.
+ *      - GZIP compression added.
+ *      - Fixed bug when updating MD5 hashes of data blocks.
+ *      - Fixed bug in chain_name_of_particle_get(...)
+ *      - Update frame set pointers properly.
+ *      - Moved fortran wrapper from header file to source file.
+ *      - Write sparse data in mdrun examples.
+ *      - Fixed bugs related to reading and writing sparse data.
+ *      - Fixed memory leak for non-trajectory particle data blocks.
+ *      - Fixed bug when writing data blocks.
+ *      - Fixed wrong values in dependency constants
+ *      - Write box shape, partial charges and annotation data in tng_testing
+ *      - Bug fixes in tng_testing (frame sets not written before)
+ *
+ * v. 1.0 - First stable release of the API.
+ *
+ *
+ * @section examples_sec Examples
+ *
+ * There are some examples of how to use the library located in src/tests/
+ *
+ * @subsection tng_subsec TNG files
+ *
+ * The build directory contains an example_files directory, which in turn
+ * contains a very short example of a TNG file containing a few water molecules,
+ * a box shape description and positions in 10 frames.
+ *
+ * It is also possible to run the bin/examples/md_openmp_util
+ * (see src/tests/md_openmp_util.c)
+ * testing program, which will save MD simulations output to a new file
+ * (saved in the example_files directory).
+ *
+ * These files can be read using the bin/examples/tng_io_read_pos_util
+ * program.
+ *
+ * @subsection c_subsec C
+ *
+ * Example writing data to a TNG file (just an excerpt):
+ * \code
+ *     for ( step = 1; step < step_num; step++ )
+ *     {
+ *         compute ( np, nd, pos, vel, mass, force, &potential, &kinetic );
+ *
+ *         if(step % step_save == 0)
+ *         {
+ *             // Write positions, velocities and forces
+ *             if(tng_util_pos_write(traj, step, pos) != TNG_SUCCESS)
+ *             {
+ *                 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+ *                 break;
+ *             }
+ *             if(tng_util_vel_write(traj, step, vel) != TNG_SUCCESS)
+ *             {
+ *                 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+ *                 break;
+ *             }
+ *             if(tng_util_force_write(traj, step, force) != TNG_SUCCESS)
+ *             {
+ *                 printf("Error adding data. %s: %d\n", __FILE__, __LINE__);
+ *                 break;
+ *             }
+ *         }
+ *         update ( np, nd, pos, vel, force, acc, mass, dt );
+ *     }
+ * \endcode
+ *
+ * Example reading positions from a TNG file:
+ * \code
+ * #include <stdlib.h>
+ * #include <stdio.h>
+ * #include "tng/tng_io.h"
+ *
+ * int main(int argc, char **argv)
+ * {
+ *     tng_trajectory_t traj;
+ *     // Assume that the data is stored as floats. The data is placed in 1-D
+ *     // arrays
+ *     float *positions = 0, *box_shape = 0;
+ *     int64_t n_particles, n_frames, tot_n_frames, stride_length, i, j;
+ *     // Set a default frame range
+ *     int64_t first_frame = 0, last_frame = 5000;
+ *     int k;
+ *
+ *     // A reference must be passed to allocate memory
+ *     tng_util_trajectory_open(argv[1], 'r', &traj);
+ *
+ *     if(tng_num_frames_get(traj, &tot_n_frames) != TNG_SUCCESS)
+ *     {
+ *         printf("Cannot determine the number of frames in the file\n");
+ *         tng_util_trajectory_close(&traj);
+ *         exit(1);
+ *     }
+ *
+ *     if(tng_num_particles_get(traj, &n_particles) != TNG_SUCCESS)
+ *     {
+ *         printf("Cannot determine the number of particles in the file\n");
+ *         tng_util_trajectory_close(&traj);
+ *         exit(1);
+ *     }
+ *
+ *     printf("%"PRId64" frames in file\n", tot_n_frames);
+ *
+ *     if(last_frame > tot_n_frames - 1)
+ *     {
+ *         last_frame = tot_n_frames - 1;
+ *     }
+ *
+ *     if(tng_util_box_shape_read(traj, &box_shape, &stride_length) ==
+ *         TNG_SUCCESS)
+ *     {
+ *         printf("Simulation box shape: ");
+ *         for(i=0; i < 9; i++)
+ *         {
+ *             printf("%f ", box_shape[i]);
+ *         }
+ *         printf("\n");
+ *     }
+ *     else
+ *     {
+ *         printf("Simulation box shape not set in the file (or could not be read)\n");
+ *     }
+ *
+ *     n_frames = last_frame - first_frame + 1;
+ *
+ *
+ *     // Get the positions of all particles in the requested frame range.
+ *     // The positions are stored in the positions array.
+ *     // N.B. No proper error checks.
+ *     if(tng_util_pos_read_range(traj, 0, last_frame, &positions, &stride_length)
+ *        == TNG_SUCCESS)
+ *     {
+ *         // Print the positions of the wanted particle (zero based)
+ *         for(i=0; i < n_frames; i += stride_length)
+ *         {
+ *             printf("\nFrame %"PRId64":\n", first_frame + i);
+ *             for(j=0; j < n_particles; j++)
+ *             {
+ *                 printf("Atom nr: %"PRId64"", j);
+ *                 for(k=0; k < 3; k++)
+ *                 {
+ *                     printf("\t%f", positions[i/stride_length*n_particles*
+ *                                              3+j*3+k]);
+ *                 }
+ *                 printf("\n");
+ *             }
+ *         }
+ *     }
+ *     else
+ *     {
+ *         printf("Cannot read positions\n");
+ *     }
+ *
+ *     // Free memory
+ *     if(positions)
+ *     {
+ *         free(positions);
+ *     }
+ *     tng_util_trajectory_close(&traj);
+ *
+ *     return(0);
+ * }
+ *
+ * \endcode
+ *
+ * @subsection fortran_subsec Fortran
+ *
+ * The TNG library can be used from Fortran. It requires cray pointers, which
+ * are not part of the Fortran 77 standard, but available in most compilers.
+ *
+ * To compile the fortran example -DTNG_BUILD_FORTRAN=ON needs to be specified when
+ * running cmake.
+ *
+ */
+
+#ifndef TNG_IO_H
+#define TNG_IO_H     1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "tng_io_fwd.h"
+
+#ifdef USE_STD_INTTYPES_H
+#include <inttypes.h>
+#else
+/* Visual Studio does not contain inttypes.h and stdint.h. Some defines and
+ * typedefs are used from the GNU C Library */
+#ifdef _MSC_VER
+
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+#else
+#include <stdint.h>
+#endif /* _MSC_VER */
+
+/* This is from inttypes.h  (GNU C Library) */
+/* The ISO C99 standard specifies that these macros must only be
+   defined if explicitly requested.  */
+#if !defined __cplusplus || defined __STDC_FORMAT_MACROS
+
+# if __WORDSIZE == 64
+#  define __PRI64_PREFIX        "l"
+#  define __PRIPTR_PREFIX       "l"
+# else
+#  define __PRI64_PREFIX        "ll"
+#  define __PRIPTR_PREFIX
+# endif
+
+/* From stdint.h (GNU C Library) */
+/* Macros for printing format specifiers. */
+/* Decimal notation.  */
+#ifndef PRId64
+# define PRId64         __PRI64_PREFIX "d"
+#endif
+
+#ifndef PRIu64
+# define PRIu64         __PRI64_PREFIX "u"
+#endif
+
+#ifndef PRIuPTR
+# define PRIuPTR         __PRIPTR_PREFIX "u"
+#endif
+
+#endif
+
+#endif /* USE_STD_INTTYPES_H */
+
+#ifndef USE_WINDOWS
+#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64)
+#define USE_WINDOWS
+#endif /* win32... */
+#endif /* not defined USE_WINDOWS */
+
+#ifndef DECLSPECDLLEXPORT
+#ifdef USE_WINDOWS
+#define DECLSPECDLLEXPORT __declspec(dllexport)
+#else /* USE_WINDOWS */
+#define DECLSPECDLLEXPORT
+#endif /* USE_WINDOWS */
+#endif /* DECLSPECDLLEXPORT */
+
+/** Flag to indicate frame dependent data. */
+#define TNG_FRAME_DEPENDENT 1
+/** Flag to indicate particle dependent data. */
+#define TNG_PARTICLE_DEPENDENT 2
+
+/** The maximum length of a date string */
+#define TNG_MAX_DATE_STR_LEN 24
+/** The length of an MD5 hash */
+#define TNG_MD5_HASH_LEN 16
+/** The maximum allowed length of a string */
+#define TNG_MAX_STR_LEN 1024
+
+#ifndef NDEBUG
+#define TNG_ASSERT(cnd, msg) if(!(cnd)) {printf("%s\n", msg); assert(cnd);}
+#else
+#define TNG_ASSERT(cnd, msg) (void)0;
+#endif
+
+/** Flag to specify the endianness of a TNG file */
+typedef enum {TNG_BIG_ENDIAN,
+              TNG_LITTLE_ENDIAN} tng_file_endianness;
+
+/** Flag to specify the endianness of 32 bit values of the current architecture. */
+typedef enum {TNG_BIG_ENDIAN_32,
+              TNG_LITTLE_ENDIAN_32,
+              TNG_BYTE_PAIR_SWAP_32} tng_endianness_32;
+
+/** Flag to specify the endianness of 64 bit values of the current architecture. */
+typedef enum {TNG_BIG_ENDIAN_64,
+              TNG_LITTLE_ENDIAN_64,
+              TNG_QUAD_SWAP_64,
+              TNG_BYTE_PAIR_SWAP_64,
+              TNG_BYTE_SWAP_64} tng_endianness_64;
+
+/** Compression mode is specified in each data block */
+typedef enum {TNG_UNCOMPRESSED,
+              TNG_XTC_COMPRESSION,
+              TNG_TNG_COMPRESSION,
+              TNG_GZIP_COMPRESSION} tng_compression;
+
+/** Hash types */
+typedef enum {TNG_NO_HASH,
+              TNG_MD5,
+              TNG_SHA256} tng_hash_type;
+
+/** Non trajectory blocks come before the first frame set block */
+typedef enum {TNG_NON_TRAJECTORY_BLOCK, TNG_TRAJECTORY_BLOCK} tng_block_type;
+
+/** @defgroup def1 Standard non-trajectory blocks
+ *  Block IDs of standard non-trajectory blocks.
+ * @{
+ */
+#define TNG_GENERAL_INFO                0x0000000000000000LL
+#define TNG_MOLECULES                   0x0000000000000001LL
+#define TNG_TRAJECTORY_FRAME_SET        0x0000000000000002LL
+#define TNG_PARTICLE_MAPPING            0x0000000000000003LL
+/** @} */
+
+/** @defgroup def2 Standard trajectory blocks
+ * Block IDs of standard trajectory blocks. Box shape and partial charges can
+ * be either trajectory blocks or non-trajectory blocks
+ * @{
+ */
+#define TNG_TRAJ_BOX_SHAPE              0x0000000010000000LL
+#define TNG_TRAJ_POSITIONS              0x0000000010000001LL
+#define TNG_TRAJ_VELOCITIES             0x0000000010000002LL
+#define TNG_TRAJ_FORCES                 0x0000000010000003LL
+#define TNG_TRAJ_PARTIAL_CHARGES        0x0000000010000004LL
+#define TNG_TRAJ_FORMAL_CHARGES         0x0000000010000005LL
+#define TNG_TRAJ_B_FACTORS              0x0000000010000006LL
+#define TNG_TRAJ_ANISOTROPIC_B_FACTORS  0x0000000010000007LL
+#define TNG_TRAJ_OCCUPANCY              0x0000000010000008LL
+#define TNG_TRAJ_GENERAL_COMMENTS       0x0000000010000009LL
+#define TNG_TRAJ_MASSES                 0x0000000010000010LL
+/** @} */
+
+
+/** @defgroup def3 GROMACS data block IDs
+ *  Block IDs of data blocks specific to GROMACS.
+ * @{
+ */
+#define TNG_GMX_LAMBDA                  0x1000000010000000LL
+#define TNG_GMX_ENERGY_ANGLE            0x1000000010000001LL
+#define TNG_GMX_ENERGY_RYCKAERT_BELL    0x1000000010000002LL
+#define TNG_GMX_ENERGY_LJ_14            0x1000000010000003LL
+#define TNG_GMX_ENERGY_COULOMB_14       0x1000000010000004LL
+#define TNG_GMX_ENERGY_LJ_(SR)          0x1000000010000005LL
+#define TNG_GMX_ENERGY_COULOMB_(SR)     0x1000000010000006LL
+#define TNG_GMX_ENERGY_COUL_RECIP       0x1000000010000007LL
+#define TNG_GMX_ENERGY_POTENTIAL        0x1000000010000008LL
+#define TNG_GMX_ENERGY_KINETIC_EN       0x1000000010000009LL
+#define TNG_GMX_ENERGY_TOTAL_ENERGY     0x1000000010000010LL
+#define TNG_GMX_ENERGY_TEMPERATURE      0x1000000010000011LL
+#define TNG_GMX_ENERGY_PRESSURE         0x1000000010000012LL
+#define TNG_GMX_ENERGY_CONSTR_RMSD      0x1000000010000013LL
+#define TNG_GMX_ENERGY_CONSTR2_RMSD     0x1000000010000014LL
+#define TNG_GMX_ENERGY_BOX_X            0x1000000010000015LL
+#define TNG_GMX_ENERGY_BOX_Y            0x1000000010000016LL
+#define TNG_GMX_ENERGY_BOX_Z            0x1000000010000017LL
+#define TNG_GMX_ENERGY_BOXXX            0x1000000010000018LL
+#define TNG_GMX_ENERGY_BOXYY            0x1000000010000019LL
+#define TNG_GMX_ENERGY_BOXZZ            0x1000000010000020LL
+#define TNG_GMX_ENERGY_BOXYX            0x1000000010000021LL
+#define TNG_GMX_ENERGY_BOXZX            0x1000000010000022LL
+#define TNG_GMX_ENERGY_BOXZY            0x1000000010000023LL
+#define TNG_GMX_ENERGY_BOXVELXX         0x1000000010000024LL
+#define TNG_GMX_ENERGY_BOXVELYY         0x1000000010000025LL
+#define TNG_GMX_ENERGY_BOXVELZZ         0x1000000010000026LL
+#define TNG_GMX_ENERGY_BOXVELYX         0x1000000010000027LL
+#define TNG_GMX_ENERGY_BOXVELZX         0x1000000010000028LL
+#define TNG_GMX_ENERGY_BOXVELZY         0x1000000010000029LL
+#define TNG_GMX_ENERGY_VOLUME           0x1000000010000030LL
+#define TNG_GMX_ENERGY_DENSITY          0x1000000010000031LL
+#define TNG_GMX_ENERGY_PV               0x1000000010000032LL
+#define TNG_GMX_ENERGY_ENTHALPY         0x1000000010000033LL
+#define TNG_GMX_ENERGY_VIR_XX           0x1000000010000034LL
+#define TNG_GMX_ENERGY_VIR_XY           0x1000000010000035LL
+#define TNG_GMX_ENERGY_VIR_XZ           0x1000000010000036LL
+#define TNG_GMX_ENERGY_VIR_YX           0x1000000010000037LL
+#define TNG_GMX_ENERGY_VIR_YY           0x1000000010000038LL
+#define TNG_GMX_ENERGY_VIR_YZ           0x1000000010000039LL
+#define TNG_GMX_ENERGY_VIR_ZX           0x1000000010000040LL
+#define TNG_GMX_ENERGY_VIR_ZY           0x1000000010000041LL
+#define TNG_GMX_ENERGY_VIR_ZZ           0x1000000010000042LL
+#define TNG_GMX_ENERGY_SHAKEVIR_XX      0x1000000010000043LL
+#define TNG_GMX_ENERGY_SHAKEVIR_XY      0x1000000010000044LL
+#define TNG_GMX_ENERGY_SHAKEVIR_XZ      0x1000000010000045LL
+#define TNG_GMX_ENERGY_SHAKEVIR_YX      0x1000000010000046LL
+#define TNG_GMX_ENERGY_SHAKEVIR_YY      0x1000000010000047LL
+#define TNG_GMX_ENERGY_SHAKEVIR_YZ      0x1000000010000048LL
+#define TNG_GMX_ENERGY_SHAKEVIR_ZX      0x1000000010000049LL
+#define TNG_GMX_ENERGY_SHAKEVIR_ZY      0x1000000010000050LL
+#define TNG_GMX_ENERGY_SHAKEVIR_ZZ      0x1000000010000051LL
+#define TNG_GMX_ENERGY_FORCEVIR_XX      0x1000000010000052LL
+#define TNG_GMX_ENERGY_FORCEVIR_XY      0x1000000010000053LL
+#define TNG_GMX_ENERGY_FORCEVIR_XZ      0x1000000010000054LL
+#define TNG_GMX_ENERGY_FORCEVIR_YX      0x1000000010000055LL
+#define TNG_GMX_ENERGY_FORCEVIR_YY      0x1000000010000056LL
+#define TNG_GMX_ENERGY_FORCEVIR_YZ      0x1000000010000057LL
+#define TNG_GMX_ENERGY_FORCEVIR_ZX      0x1000000010000058LL
+#define TNG_GMX_ENERGY_FORCEVIR_ZY      0x1000000010000059LL
+#define TNG_GMX_ENERGY_FORCEVIR_ZZ      0x1000000010000060LL
+#define TNG_GMX_ENERGY_PRES_XX          0x1000000010000061LL
+#define TNG_GMX_ENERGY_PRES_XY          0x1000000010000062LL
+#define TNG_GMX_ENERGY_PRES_XZ          0x1000000010000063LL
+#define TNG_GMX_ENERGY_PRES_YX          0x1000000010000064LL
+#define TNG_GMX_ENERGY_PRES_YY          0x1000000010000065LL
+#define TNG_GMX_ENERGY_PRES_YZ          0x1000000010000066LL
+#define TNG_GMX_ENERGY_PRES_ZX          0x1000000010000067LL
+#define TNG_GMX_ENERGY_PRES_ZY          0x1000000010000068LL
+#define TNG_GMX_ENERGY_PRES_ZZ          0x1000000010000069LL
+#define TNG_GMX_ENERGY_SURFXSURFTEN     0x1000000010000070LL
+#define TNG_GMX_ENERGY_MUX              0x1000000010000071LL
+#define TNG_GMX_ENERGY_MUY              0x1000000010000072LL
+#define TNG_GMX_ENERGY_MUZ              0x1000000010000073LL
+#define TNG_GMX_ENERGY_VCOS             0x1000000010000074LL
+#define TNG_GMX_ENERGY_VISC             0x1000000010000075LL
+#define TNG_GMX_ENERGY_BAROSTAT         0x1000000010000076LL
+#define TNG_GMX_ENERGY_T_SYSTEM         0x1000000010000077LL
+#define TNG_GMX_ENERGY_LAMB_SYSTEM      0x1000000010000078LL
+#define TNG_GMX_SELECTION_GROUP_NAMES   0x1000000010000079LL
+#define TNG_GMX_ATOM_SELECTION_GROUP    0x1000000010000080LL
+/** @} */
+
+/** Flag to specify if a data block contains data related to particles or not.*/
+typedef enum {TNG_NON_PARTICLE_BLOCK_DATA,
+              TNG_PARTICLE_BLOCK_DATA} tng_particle_dependency;
+
+
+typedef enum {TNG_FALSE, TNG_TRUE} tng_bool;
+
+/** Flag to specify if the number of atoms change throughout the trajectory or
+ *  if it is constant. */
+typedef enum {TNG_CONSTANT_N_ATOMS, TNG_VARIABLE_N_ATOMS}
+             tng_variable_n_atoms_flag;
+
+/** Return values of API functions. TNG_SUCCESS means that the operation
+ *  was successful. TNG_FAILURE means that the operation failed for some
+ *  reason, but it is possible to try to continue anyhow. TNG_CRITICAL
+ *  means that the error is irrecoverable. */
+typedef enum {TNG_SUCCESS, TNG_FAILURE, TNG_CRITICAL} tng_function_status;
+
+/** If tng_hash_mode == TNG_USE_HASH md5 hashes will be written to output files
+ *  and when reading a file the md5 hashes of the contents will be compared to
+ *  those in the file (for each block) in order to ensure data integrity */
+typedef enum {TNG_SKIP_HASH, TNG_USE_HASH} tng_hash_mode;
+
+/** Possible formats of data block contents */
+typedef enum {TNG_CHAR_DATA,
+              TNG_INT_DATA,
+              TNG_FLOAT_DATA,
+              TNG_DOUBLE_DATA} tng_data_type;
+
+
+struct tng_trajectory;
+struct tng_molecule;
+struct tng_chain;
+struct tng_residue;
+struct tng_atom;
+struct tng_bond;
+struct tng_gen_block;
+struct tng_particle_mapping;
+struct tng_trajectory_frame_set;
+struct tng_particle_data;
+struct tng_non_particle_data;
+
+/** Data can be either double, float, int or a string */
+union data_values {
+    double d;
+    float f;
+    int64_t i;
+    char *c;
+};
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** @defgroup group1 Low-level API
+ *  These functions give detailed control of the TNG data management. Most
+ *  things can be done using the more convenient high-level API functions
+ *  instead.
+ *  @{
+ */
+
+/**
+ * @brief Get the major version of the TNG library.
+ * @param tng_data is a trajectory data container, it does not have
+ * to be initialized beforehand.
+ * @param version is pointing to a value set to the major version of
+ * the library.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_version_major
+                (const tng_trajectory_t tng_data,
+                 int *version);
+
+/**
+ * @brief Get the minor version of the TNG library.
+ * @param tng_data is a trajectory data container, it does not have
+ * to be initialized beforehand.
+ * @param version is pointing to a value set to the minor version of
+ * the library.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_version_minor
+                (const tng_trajectory_t tng_data,
+                 int *version);
+
+/**
+ * @brief Get the patch level of the TNG library.
+ * @param tng_data is a trajectory data container, it does not have
+ * to be initialized beforehand.
+ * @param patch_level is the string to fill with the full version,
+ * memory must be allocated before.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_version_patchlevel
+                (const tng_trajectory_t tng_data,
+                 int *patch_level);
+
+/**
+ * @brief Get the full version string of the TNG library.
+ * @param tng_data is a trajectory data container, it does not have
+ * to be initialized beforehand.
+ * @param version is pointing to a value set to the major version of
+ * the library.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for version. This includes \0 terminating character.
+ * @pre \code version != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_version
+                (const tng_trajectory_t tng_data,
+                 char *version,
+                 const int max_len);
+
+/**
+ * @brief Setup a trajectory data container.
+ * @param tng_data_p a pointer to memory to initialise as a trajectory.
+ * @pre tng_data_p must not be pointing at a reserved memory block.
+ * @details Memory is allocated during initialisation.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_trajectory_init
+                (tng_trajectory_t *tng_data_p);
+
+/**
+ * @brief Clean up a trajectory data container.
+ * @param tng_data_p a pointer to the trajectory data to destroy.
+ * @details All allocated memory in the data structure is freed, as well as
+ * tng_data_p itself.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_trajectory_destroy
+                (tng_trajectory_t *tng_data_p);
+
+/**
+ * @brief Copy a trajectory data container (dest is setup as well).
+ * @details This initialises dest and copies only what is absolute necessary for
+ * parallel i/o. This can be used inside pragma omp for setting up a thread
+ * local copy of src. It can be freed (using tng_trajectory_destroy) at the
+ * end of the parallel block.
+ * @param src the original trajectory.
+ * @param dest_p a pointer to memory to initialise as a trajectory.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre tng_data_p must not be pointing at a reserved memory block.
+ * @details Memory is allocated during initialisation.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_trajectory_init_from_src
+                (const tng_trajectory_t src, tng_trajectory_t *dest_p);
+
+/**
+ * @brief Get the name of the input file.
+ * @param tng_data the trajectory of which to get the input file name.
+ * @param file_name the string to fill with the name of the input file,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for file_name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code file_name != 0 \endcode The pointer to the file name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_input_file_get
+                (const tng_trajectory_t tng_data,
+                 char *file_name, const int max_len);
+
+/**
+ * @brief Set the name of the input file.
+ * @param tng_data the trajectory of which to set the input file name.
+ * @param file_name the name of the input file.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code file_name != 0 \endcode The pointer to the file name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_input_file_set
+                (const tng_trajectory_t tng_data,
+                 const char *file_name);
+
+/**
+ * @brief Get the name of the output file.
+ * @param tng_data the trajectory of which to get the input file name.
+ * @param file_name the string to fill with the name of the output file,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for file_name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code file_name != 0 \endcode The pointer to the file name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_output_file_get
+                (const tng_trajectory_t tng_data,
+                 char *file_name, const int max_len);
+
+/**
+ * @brief Set the name of the output file.
+ * @param tng_data the trajectory of which to set the output file name.
+ * @param file_name the name of the output file.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code file_name != 0 \endcode The pointer to the file name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_output_file_set
+                (const tng_trajectory_t tng_data,
+                 const char *file_name);
+
+/**
+ * @brief Set the name of the output file for appending. The output file
+ * will not be overwritten.
+ * @param tng_data the trajectory of which to set the output file name.
+ * @param file_name the name of the output file to append to.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code file_name != 0 \endcode The pointer to the file name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_output_append_file_set
+                (const tng_trajectory_t tng_data,
+                 const char *file_name);
+
+/**
+ * @brief Get the endianness of the output file.
+ * @param tng_data the trajectory of which to get the endianness of the current
+ * output file.
+ * @param endianness will contain the enumeration of the endianness.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code endianness != 0 \endcode The pointer to the endianness container
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if the endianness
+ * could not be retrieved.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_output_file_endianness_get
+                (const tng_trajectory_t tng_data, tng_file_endianness *endianness);
+
+/**
+ * @brief Set the endianness of the output file.
+ * @param tng_data the trajectory of which to set the endianness of the current
+ * output file.
+ * @param endianness the enumeration of the endianness, can be either
+ * TNG_BIG_ENDIAN (0) or TNG_LITTLE_ENDIAN (1).
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details The endianness cannot be changed after file output has started.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if the endianness
+ * could not be set.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_output_file_endianness_set
+                (const tng_trajectory_t tng_data,
+                 const tng_file_endianness endianness);
+
+/**
+ * @brief Get the name of the program used when creating the trajectory.
+ * @param tng_data the trajectory of which to get the program name.
+ * @param name the string to fill with the name of the program,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_program_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the program used when creating the trajectory.
+ * @param tng_data the trajectory of which to set the program name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_program_name_set
+                (const tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the name of the program used when last modifying the trajectory.
+ * @param tng_data the trajectory of which to get the program name.
+ * @param name the string to fill with the name of the program,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_program_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the program used when last modifying the trajectory.
+ * @param tng_data the trajectory of which to set the program name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_program_name_set
+                (const tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the name of the user who created the trajectory.
+ * @param tng_data the trajectory of which to get the user name.
+ * @param name the string to fill with the name of the user,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_user_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the user who created the trajectory.
+ * @param tng_data the trajectory of which to set the user name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_user_name_set
+                (const tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the name of the user who last modified the trajectory.
+ * @param tng_data the trajectory of which to get the user name.
+ * @param name the string to fill with the name of the user,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_user_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the user who last modified the trajectory.
+ * @param tng_data the trajectory of which to set the user name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_user_name_set
+                (const tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the name of the computer used when creating the trajectory.
+ * @param tng_data the trajectory of which to get the computer name.
+ * @param name the string to fill with the name of the computer,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_computer_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the computer used when creating the trajectory.
+ * @param tng_data the trajectory of which to set the computer name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_computer_name_set
+                (const tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the name of the computer used when last modifying the trajectory.
+ * @param tng_data the trajectory of which to get the computer name.
+ * @param name the string to fill with the name of the computer,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_computer_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the computer used when last modifying the trajectory.
+ * @param tng_data the trajectory of which to set the computer name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_computer_name_set
+                (const tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the pgp_signature of the user creating the trajectory.
+ * @param tng_data the trajectory of which to get the computer name.
+ * @param signature the string to fill with the signature,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code signature != 0 \endcode The pointer to the signature
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_signature_get
+                (const tng_trajectory_t tng_data,
+                 char *signature, const int max_len);
+
+/**
+ * @brief Set the pgp_signature of the user creating the trajectory.
+ * @param tng_data the trajectory of which to set the computer name.
+ * @param signature is a string containing the pgp_signature.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code signature != 0 \endcode The pointer to the signature
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_signature_set
+                (const tng_trajectory_t tng_data,
+                 const char *signature);
+
+/**
+ * @brief Get the pgp_signature of the user last modifying the trajectory.
+ * @param tng_data the trajectory of which to get the computer name.
+ * @param signature the string to fill with the signature,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code signature != 0 \endcode The pointer to the signature
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_signature_get
+                (const tng_trajectory_t tng_data,
+                 char *signature, const int max_len);
+
+/**
+ * @brief Set the pgp_signature of the user last modifying the trajectory.
+ * @param tng_data the trajectory of which to set the computer name.
+ * @param signature is a string containing the pgp_signature.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code signature != 0 \endcode The pointer to the signature
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_last_signature_set
+                (const tng_trajectory_t tng_data,
+                 const char *signature);
+
+/**
+ * @brief Get the name of the forcefield used in the trajectory.
+ * @param tng_data the trajectory of which to get the forcefield name.
+ * @param name the string to fill with the name of the forcefield,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_forcefield_name_get
+                (const tng_trajectory_t tng_data,
+                 char *name, const int max_len);
+
+/**
+ * @brief Set the name of the forcefield used in the trajectory.
+ * @param tng_data the trajectory of which to set the forcefield name.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the new_name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_forcefield_name_set
+                (const tng_trajectory_t tng_data,
+                 const char *new_name);
+
+/**
+ * @brief Get the medium stride length of the trajectory.
+ * @param tng_data is the trajectory from which to get the stride length.
+ * @param len is pointing to a value set to the stride length.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code len != 0 \endcode The pointer to len must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_medium_stride_length_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *len);
+
+/**
+ * @brief Set the medium stride length of the trajectory.
+ * @param tng_data is the trajectory of which to set the stride length.
+ * @param len is the wanted medium stride length.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_medium_stride_length_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t len);
+
+/**
+ * @brief Get the long stride length of the trajectory.
+ * @param tng_data is the trajectory from which to get the stride length.
+ * @param len is pointing to a value set to the stride length.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code len != 0 \endcode The pointer to len must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_long_stride_length_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *len);
+
+/**
+ * @brief Set the long stride length of the trajectory.
+ * @param tng_data is the trajectory of which to set the stride length.
+ * @param len is the wanted long stride length.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_long_stride_length_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t len);
+
+/**
+ * @brief Get the current time per frame of the trajectory.
+ * @param tng_data is the trajectory from which to get the time per frame.
+ * @param time is pointing to a value set to the time per frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code time != 0 \endcode The pointer to time must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_time_per_frame_get
+                (const tng_trajectory_t tng_data,
+                 double *time);
+
+/**
+ * @brief Set the time per frame of the trajectory.
+ * @param tng_data is the trajectory of which to set the time per frame.
+ * @param time is the new time per frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code time > 0 \endcode The time per frame must be >= 0.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_time_per_frame_set
+                (const tng_trajectory_t tng_data,
+                 const double time);
+
+/**
+ * @brief Get the length of the input file.
+ * @param tng_data is the trajectory from which to get the input file length.
+ * @param len is pointing to a value set to the file length.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code len != 0 \endcode The pointer to len must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_input_file_len_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *len);
+
+/**
+ * @brief Get the number of frames in the trajectory
+ * @param tng_data is the trajectory of which to get the number of frames.
+ * @param n is pointing to a value set to the number of frames.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code tng_data->input_file != 0 \endcode An input file must be open
+ * to find the next frame set.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (could not find last frame set).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_frames_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n);
+
+/**
+ * @brief Get the precision of lossy compression.
+ * @param tng_data is the trajectory of which to get the compression precision.
+ * @param precision will be pointing to the retrieved compression precision.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details A compression precision of 0.001 (the default) means that the
+ * compressed values are accurate to the third decimal. This function does
+ * not check actual precision of compressed data, but just returns what has
+ * previously been set using tng_compression_precision_set().
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_compression_precision_get
+                (const tng_trajectory_t tng_data,
+                 double *precision);
+
+/**
+ * @brief Set the precision of lossy compression.
+ * @param tng_data is the trajectory of which to set the compression precision.
+ * @param precision is the new compression precision.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details A compression precision of 0.001 (the default) means that the
+ * compressed values are accurate to the third decimal.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_compression_precision_set
+                (const tng_trajectory_t tng_data,
+                 const double precision);
+
+/**
+ * @brief Set the number of particles, in the case no molecular system is used.
+ * @param tng_data is the trajectory of which to get the number of particles.
+ * @param n is the number of particles to use.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details When creating a molecular system the number of particles are set
+ * automatically. This should only be used when there is no molecular system
+ * specified or if the number of atoms needs to be overridden for some reason.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_implicit_num_particles_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t n);
+
+/**
+ * @brief Get the current number of particles.
+ * @param tng_data is the trajectory from which to get the number of particles.
+ * @param n is pointing to a value set to the number of particles.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @details If variable number of particles are used this function will return
+ * the number of particles in the current frame set.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_particles_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n);
+
+/**
+ * @brief Get if the number of particle can be varied during the simulation.
+ * @param tng_data is the trajectory from which to get the number of particles.
+ * @param variable is pointing to a value set to TNG_CONSTANT_N_ATOMS if the
+ * number of particles cannot change or TNG_VARIABLE_N_ATOMS if the number of
+ * particles can change.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code variable != 0 \endcode The pointer to variable must not be
+ * a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_particles_variable_get
+                (const tng_trajectory_t tng_data,
+                 char *variable);
+
+/**
+ * @brief Get the number of molecule types (length of tng_data->molecules).
+ * @param tng_data is the trajectory from which to get the number of molecules.
+ * @param n is pointing to a value set to the number of molecule types.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_molecule_types_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n);
+
+/**
+ * @brief Get the current total number of molecules.
+ * @param tng_data is the trajectory from which to get the number of molecules.
+ * @param n is pointing to a value set to the number of molecules.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @details If variable number of particles are used this function will return
+ * the total number of molecules in the current frame set.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_molecules_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n);
+
+/** @brief Get the list of the count of each molecule.
+ * @param tng_data is the trajectory from which to get the molecule count list.
+ * @param mol_cnt_list is a list of the count of each molecule in the
+ * mol system. This is a pointer to the list in the TNG container, which
+ * means that it should be handled carefully, e.g. not freed.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE(1) if the list of
+ * molecule counts was not valid.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_cnt_list_get
+                (const tng_trajectory_t tng_data,
+                 int64_t **mol_cnt_list);
+
+/**
+ * @brief Get the exponent used for distances in the trajectory.
+ * @param tng_data is the trajectory from which to get the information.
+ * @param exp is pointing to a value set to the distance unit exponent.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code exp != 0 \endcode The pointer to exp must not be a NULL pointer.
+ * @details Example: If the distances are specified in nm (default) exp is -9.
+ * If the distances are specified in Å exp is -10.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_distance_unit_exponential_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *exp);
+
+/**
+ * @brief Set the exponent used for distances in the trajectory.
+ * @param tng_data is the trajectory of which to set the unit exponent.
+ * @param exp is the distance unit exponent to use.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details Example: If the distances are specified in nm (default) exp is -9.
+ * If the distances are specified in Å exp is -10.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_distance_unit_exponential_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t exp);
+
+/**
+ * @brief Get the number of frames per frame set.
+ * @param tng_data is the trajectory from which to get the number of frames
+ * per frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @param n is pointing to a value set to the number of frames per frame set.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_frames_per_frame_set_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n);
+
+/**
+ * @brief Set the number of frames per frame set.
+ * @param tng_data is the trajectory of which to set the number of frames
+ * per frame set.
+ * @param n is the number of frames per frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details This does not affect already existing frame sets. For
+ * consistency the number of frames per frame set should be set
+ * betfore creating any frame sets.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_frames_per_frame_set_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t n);
+
+/**
+ * @brief Get the number of frame sets.
+ * @details This updates tng_data->n_trajectory_frame_sets before returning it.
+ * @param tng_data is the trajectory from which to get the number of frame sets.
+ * @param n is pointing to a value set to the number of frame sets.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_num_frame_sets_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n);
+
+/**
+ * @brief Get the current trajectory frame set.
+ * @param tng_data is the trajectory from which to get the frame set.
+ * @param frame_set_p will be set to point at the memory position of
+ * the found frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_current_frame_set_get
+                (const tng_trajectory_t tng_data,
+                 tng_trajectory_frame_set_t *frame_set_p);
+
+/**
+ * @brief Find the requested frame set number.
+ * @param tng_data is the trajectory from which to get the frame set.
+ * @param nr is the frame set number to search for.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code nr >= 0 \endcode The frame set number (nr) must be >= 0.
+ * @details tng_data->current_trajectory_frame_set will contain the
+ * found trajectory if successful.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_nr_find
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr);
+
+/**
+ * @brief Find the frame set containing a specific frame.
+ * @param tng_data is the trajectory from which to get the frame set.
+ * @param frame is the frame number to search for.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame >= 0 \endcode The frame number must be >= 0.
+ * @details tng_data->current_trajectory_frame_set will contain the
+ * found trajectory if successful.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_of_frame_find
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame);
+
+/**
+ * @brief Get the file position of the next frame set in the input file.
+ * @param tng_data is a trajectory data container.
+ * @param frame_set is the frame set of which to get the position of the
+ * following frame set.
+ * @param pos is pointing to a value set to the file position.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code pos != 0 \endcode The pointer to pos must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_next_frame_set_file_pos_get
+                (const tng_trajectory_t tng_data,
+                 const tng_trajectory_frame_set_t frame_set,
+                 int64_t *pos);
+
+/**
+ * @brief Get the file position of the previous frame set in the input file.
+ * @param tng_data is a trajectory data container.
+ * @param frame_set is the frame set of which to get the position of the
+ * previous frame set.
+ * @param pos is pointing to a value set to the file position.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code pos != 0 \endcode The pointer to pos must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_prev_frame_set_file_pos_get
+                (const tng_trajectory_t tng_data,
+                 const tng_trajectory_frame_set_t frame_set,
+                 int64_t *pos);
+
+/**
+ * @brief Get the first and last frames of the frame set.
+ * @param tng_data is a trajectory data container.
+ * @param frame_set is the frame set of which to get the frame range.
+ * @param first_frame is set to the first frame of the frame set.
+ * @param last_frame is set to the last frame of the frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code first_frame != 0 \endcode The pointer to first_frame must
+ * not be a NULL pointer.
+ * @pre \code last_frame != 0 \endcode The pointer to last_frame must
+ * not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_frame_range_get
+                (const tng_trajectory_t tng_data,
+                 const tng_trajectory_frame_set_t frame_set,
+                 int64_t *first_frame,
+                 int64_t *last_frame);
+
+/**
+ * @brief Allocate memory for and setup a molecule container.
+ * @param tng_data is a trajectory data container.
+ * @param molecule_p is a pointer to molecule to allocate and initialise.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_alloc(const tng_trajectory_t tng_data,
+                                                         tng_molecule_t *molecule_p);
+
+/**
+ * @brief Clean up a molecule container and free its allocated memory.
+ * @param tng_data is a trajectory data container.
+ * @param molecule_p is the molecule to destroy.
+ * @details All allocated memory in the data structure is freed and also the memory
+ * of the molecule itself.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_free(const tng_trajectory_t tng_data,
+                                                        tng_molecule_t *molecule_p);
+
+/**
+ * @brief Setup a molecule container.
+ * @param tng_data is a trajectory data container.
+ * @param molecule is the molecule to initialise. Memory must be preallocated.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_init
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule);
+
+/**
+ * @brief Clean up a molecule container.
+ * @param tng_data is a trajectory data container.
+ * @param molecule is the molecule to destroy.
+ * @details All allocated memory in the data structure is freed, but not the
+ * memory of molecule itself.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_destroy
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule);
+
+/**
+ * @brief Add a molecule to the trajectory.
+ * @param tng_data is the trajectory data container containing the block..
+ * @param name is a pointer to the string containing the name of the new molecule.
+ * @param molecule is a pointer to the newly created molecule.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_add
+                (const tng_trajectory_t tng_data,
+                 const char *name,
+                 tng_molecule_t *molecule);
+
+/**
+ * @brief Add a molecule with a specific ID to the trajectory.
+ * @param tng_data is the trajectory data container containing the block..
+ * @param name is a pointer to the string containing the name of the new molecule.
+ * @param id is the ID of the created molecule.
+ * @param molecule is a pointer to the newly created molecule.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_w_id_add
+                (const tng_trajectory_t tng_data,
+                 const char *name,
+                 const int64_t id,
+                 tng_molecule_t *molecule);
+
+/**
+ * @brief Add an existing molecule (from a molecule container) to the trajectory.
+ * @param tng_data is the trajectory data container containing the block..
+ * @param molecule is a pointer to the molecule to add to the trajectory and will
+ * afterwards point to the molecule in the trajectory.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_existing_add
+                (const tng_trajectory_t tng_data,
+                 tng_molecule_t *molecule);
+
+/**
+ * @brief Get the name of a molecule.
+ * @param tng_data the trajectory containing the molecule.
+ * @param molecule the molecule of which to get the name.
+ * @param name the string to fill with the name of the molecule,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code molecule != 0 \endcode The molecule must not be NULL.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_name_get
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 char *name,
+                 const int max_len);
+
+/**
+ * @brief Set the name of a molecule.
+ * @param tng_data is the trajectory data container containing the molecule..
+ * @param molecule is the molecule to rename.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_name_set
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 const char *new_name);
+
+/**
+ * @brief Get the count of a molecule.
+ * @param tng_data is the trajectory data container containing the molecule..
+ * @param molecule is the molecule of which to get the count.
+ * @param cnt is a pointer to the variable to be populated with the count.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code cnt != 0 \endcode The pointer to the molecule count
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_cnt_get
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 int64_t *cnt);
+
+/**
+ * @brief Set the count of a molecule.
+ * @param tng_data is the trajectory data container containing the molecule..
+ * @param molecule is the molecule of which to set the count.
+ * @param cnt is the number of instances of this molecule.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_cnt_set
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 const int64_t cnt);
+
+/**
+ * @brief Find a molecule.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param name is a string containing the name of the molecule. If name is empty
+ * only id will be used for finding the molecule.
+ * @param id is the id of the molecule to look for. If id is -1 only the name of
+ * the molecule will be used for finding the molecule.
+ * @param molecule is a pointer to the molecule if it was found - otherwise 0.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the molecule is found or TNG_FAILURE (1) if the
+ * molecule is not found.
+ * @details If name is an empty string and id == -1 the first residue will
+ * be found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_find
+                (const tng_trajectory_t tng_data,
+                 const char *name,
+                 const int64_t id,
+                 tng_molecule_t *molecule);
+
+/**
+ * @brief Retrieve the molecule with specified index in the list of molecules.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param index is the index (in tng_data->molecules) of the molecule to return
+ * @param molecule is a pointer to the molecule if it was found - otherwise 0.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code molecule != 0 \endcode molecule must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the molecule is found or TNG_FAILURE (1) if the
+ * molecule is not found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_of_index_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t index,
+                 tng_molecule_t *molecule);
+
+/**
+ * @brief Copy all molecules and the molecule counts from one TNG trajectory
+ * to another.
+ * @param tng_data_src is the source trajectory containing the molecular
+ * system to copy.
+ * @param tng_data_dest is the destination trajectory.
+ * @pre \code tng_data_src != 0 \endcode The trajectory container (tng_data_src)
+ * must be initialised before using it.
+ * @pre \code tng_data_dest != 0 \endcode The trajectory container (tng_data_dest)
+ * must be initialised before using it.
+ * @details The molecular system in tng_data_dest will be overwritten.
+ * @return TNG_SUCCESS(0) if the copying is successful, TNG_FAILURE if a minor
+ * error has occured or TNG_CRITICAL(2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_system_copy(const tng_trajectory_t tng_data_src,
+                                                               const tng_trajectory_t tng_data_dest);
+
+/**
+ * @brief Get the number of chains in a molecule.
+ * @param tng_data is the trajectory containing the molecule.
+ * @param molecule is the molecule of which to get the number of chains.
+ * @param n is pointing to a value set to the number of chains.
+ * @pre \code molecule != 0 \endcode The molecule must not be NULL.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_num_chains_get
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 int64_t *n);
+
+/**
+ * @brief Retrieve the chain of a molecule with specified index in the list
+ * of chains.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param index is the index (in molecule->chains) of the chain to return
+ * @param molecule is the molecule from which to get the chain.
+ * @param chain is a pointer to the chain if it was found - otherwise 0.
+ * @pre \code molecule != 0 \endcode molecule must not be a NULL pointer.
+ * @pre \code chain != 0 \endcode chain must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the chain is found or TNG_FAILURE (1) if the
+ * chain is not found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_of_index_get
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 const int64_t index,
+                 tng_chain_t *chain);
+
+/**
+ * @brief Get the number of residues in a molecule.
+ * @param tng_data is the trajectory containing the molecule.
+ * @param molecule is the molecule of which to get the number residues.
+ * @param n is pointing to a value set to the number of residues.
+ * @pre \code molecule != 0 \endcode The molecule must not be NULL.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_num_residues_get
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 int64_t *n);
+
+/**
+ * @brief Retrieve the residue of a molecule with specified index in the list
+ * of chains.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param index is the index (in molecule->residues) of the residue to return
+ * @param molecule is the molecule from which to get the residue.
+ * @param residue is a pointer to the residue if it was found - otherwise 0.
+ * @pre \code molecule != 0 \endcode molecule must not be a NULL pointer.
+ * @pre \code residue != 0 \endcode residue must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the residue is found or TNG_FAILURE (1) if the
+ * residue is not found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_residue_of_index_get
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 const int64_t index,
+                 tng_residue_t *residue);
+
+/**
+ * @brief Get the number of atoms in a molecule.
+ * @param tng_data is the trajectory containing the molecule.
+ * @param molecule is the molecule of which to get the number of atoms.
+ * @param n is pointing to a value set to the number of atoms.
+ * @pre \code molecule != 0 \endcode The molecule must not be NULL.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_num_atoms_get
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 int64_t *n);
+
+/**
+ * @brief Retrieve the atom of a molecule with specified index in the list
+ * of atoms.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param index is the index (in molecule->atoms) of the atom to return
+ * @param molecule is the molecule from which to get the atom.
+ * @param atom is a pointer to the atom if it was found - otherwise 0.
+ * @pre \code molecule != 0 \endcode molecule must not be a NULL pointer.
+ * @pre \code atom != 0 \endcode atom must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the atom is found or TNG_FAILURE (1) if the
+ * atom is not found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_atom_of_index_get
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 const int64_t index,
+                 tng_atom_t *atom);
+
+/**
+ * @brief Find a chain in a molecule.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param molecule is the molecule in which to search for the chain.
+ * @param name is a string containing the name of the chain. If name is empty
+ * only id will be used for finding the chain.
+ * @param id is the id of the chain to look for. If id is -1 only the name of
+ * the chain will be used for finding the chain.
+ * @param chain is a pointer to the chain if it was found - otherwise 0.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the chain is found or TNG_FAILURE (1) if the
+ * chain is not found.
+ * @details If name is an empty string and id == -1 the first residue will
+ * be found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_find
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 const char *name,
+                 const int64_t id,
+                 tng_chain_t *chain);
+
+/**
+ * @brief Add a chain to a molecule.
+ * @param tng_data is the trajectory data container containing the molecule..
+ * @param molecule is the molecule to add a chain to.
+ * @param name is a string containing the name of the chain.
+ * @param chain is a pointer to the newly created chain.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_add
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 const char *name,
+                 tng_chain_t *chain);
+
+/**
+ * @brief Add a chain with a specific id to a molecule.
+ * @param tng_data is the trajectory data container containing the molecule..
+ * @param molecule is the molecule to add a chain to.
+ * @param name is a string containing the name of the chain.
+ * @param id is the ID of the created chain.
+ * @param chain is a pointer to the newly created chain.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_chain_w_id_add
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 const char *name,
+                 const int64_t id,
+                 tng_chain_t *chain);
+
+/**
+ * @brief Add a bond between two atoms to a molecule.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param molecule is the molecule containing the atoms to connect.
+ * @param from_atom_id is the id of one of the two atoms in the bond.
+ * @param to_atom_id is the id of the other atom in the bond.
+ * @param bond is a pointer to the newly created bond.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (!) if a minor error
+ * has occured or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_bond_add
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 const int64_t from_atom_id,
+                 const int64_t to_atom_id,
+                 tng_bond_t *bond);
+
+/**
+ * @brief Find an atom in a molecule.
+ * @param tng_data is the trajectory data container containing the molecule.
+ * @param molecule is the molecule in which to search for the atom.
+ * @param name is a string containing the name of the atom. If name is an
+ * empty string only id will be used for searching.
+ * @param id is the id of the atom to find. If id == -1 the first atom
+ * that matches the specified name will be found.
+ * @param atom is a pointer to the atom if it was found - otherwise 0.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the atom is found or TNG_FAILURE (1) if the
+ * atom is not found.
+ * @details If name is an empty string and id == -1 the first residue will
+ * be found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_atom_find
+                (const tng_trajectory_t tng_data,
+                 const tng_molecule_t molecule,
+                 const char *name,
+                 const int64_t id,
+                 tng_atom_t *atom);
+
+/**
+ * @brief Get the name of a chain.
+ * @param tng_data the trajectory containing the chain.
+ * @param chain the chain of which to get the name.
+ * @param name the string to fill with the name of the chain,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code chain != 0 \endcode The chain must not be NULL.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_name_get
+                (const tng_trajectory_t tng_data,
+                 const tng_chain_t chain,
+                 char *name,
+                 const int max_len);
+
+/**
+ * @brief Set the name of a chain.
+ * @param tng_data is the trajectory data container containing the atom..
+ * @param chain is the chain to rename.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code new_name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_name_set
+                (const tng_trajectory_t tng_data,
+                 const tng_chain_t chain,
+                 const char *new_name);
+
+/**
+ * @brief Get the number of residues in a molecule chain.
+ * @param tng_data is the trajectory containing the chain.
+ * @param chain is the chain of which to get the number of residues.
+ * @param n is pointing to a value set to the number of residues.
+ * @pre \code chain != 0 \endcode The chain must not be NULL.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_num_residues_get
+                (const tng_trajectory_t tng_data,
+                 const tng_chain_t chain,
+                 int64_t *n);
+
+/**
+ * @brief Retrieve the residue of a chain with specified index in the list
+ * of residues.
+ * @param tng_data is the trajectory data container containing the chain.
+ * @param index is the index (in chain->residues) of the residue to return
+ * @param chain is the chain from which to get the residue.
+ * @param residue is a pointer to the residue if it was found - otherwise 0.
+ * @pre \code chain != 0 \endcode chain must not be a NULL pointer.
+ * @pre \code residue != 0 \endcode residue must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the residue is found or TNG_FAILURE (1) if the
+ * residue is not found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_of_index_get
+                (const tng_trajectory_t tng_data,
+                 const tng_chain_t chain,
+                 const int64_t index,
+                 tng_residue_t *residue);
+
+/**
+ * @brief Find a residue in a chain.
+ * @param tng_data is the trajectory data container containing the chain.
+ * @param chain is the chain in which to search for the residue.
+ * @param name is a string containing the name of the residue.  If name is an
+ * empty string only id will be used for searching.
+ * @param id is the id of the residue to find. If id == -1 the first residue
+ * that matches the specified name will be found.
+ * @param residue is a pointer to the residue if it was found - otherwise 0.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the residue is found or TNG_FAILURE (1) if the
+ * residue is not found.
+ * @details If name is an empty string and id == -1 the first residue will
+ * be found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_find
+                (const tng_trajectory_t tng_data,
+                 const tng_chain_t chain,
+                 const char *name,
+                 const int64_t id,
+                 tng_residue_t *residue);
+
+/**
+ * @brief Add a residue to a chain.
+ * @param tng_data is the trajectory data container containing the chain..
+ * @param chain is the chain to add a residue to.
+ * @param name is a string containing the name of the residue.
+ * @param residue is a pointer to the newly created residue.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_add
+                (const tng_trajectory_t tng_data,
+                 const tng_chain_t chain,
+                 const char *name,
+                 tng_residue_t *residue);
+
+/**
+ * @brief Add a residue with a specific ID to a chain.
+ * @param tng_data is the trajectory data container containing the chain..
+ * @param chain is the chain to add a residue to.
+ * @param name is a string containing the name of the residue.
+ * @param id is the ID of the created residue.
+ * @param residue is a pointer to the newly created residue.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_residue_w_id_add
+                (const tng_trajectory_t tng_data,
+                 const tng_chain_t chain,
+                 const char *name,
+                 const int64_t id,
+                 tng_residue_t *residue);
+
+/**
+ * @brief Get the name of a residue.
+ * @param tng_data the trajectory containing the residue.
+ * @param residue the residue of which to get the name.
+ * @param name the string to fill with the name of the residue,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code residue != 0 \endcode The residue must not be NULL.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_name_get
+                (const tng_trajectory_t tng_data,
+                 const tng_residue_t residue,
+                 char *name,
+                 const int max_len);
+
+/**
+ * @brief Set the name of a residue.
+ * @param tng_data is the trajectory data container containing the residue.
+ * @param residue is the residue to rename.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The new name to set (new_name) must
+ * not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_name_set
+                (const tng_trajectory_t tng_data,
+                 const tng_residue_t residue,
+                 const char *new_name);
+
+/**
+ * @brief Get the number of atoms in a residue.
+ * @param tng_data is the trajectory containing the residue.
+ * @param residue is the residue of which to get the number atoms.
+ * @param n is pointing to a value set to the number of atoms.
+ * @pre \code residue != 0 \endcode The residue must not be NULL.
+ * @pre \code n != 0 \endcode The pointer to n must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_num_atoms_get
+                (const tng_trajectory_t tng_data,
+                 const tng_residue_t residue,
+                 int64_t *n);
+
+/**
+ * @brief Retrieve the atom of a residue with specified index in the list
+ * of atoms.
+ * @param tng_data is the trajectory data container containing the residue.
+ * @param index is the index (in residue->atoms) of the atom to return
+ * @param residue is the residue from which to get the atom.
+ * @param atom is a pointer to the atom if it was found - otherwise 0.
+ * @pre \code residue != 0 \endcode residue must not be a NULL pointer.
+ * @pre \code atom != 0 \endcode atom must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the atom is found or TNG_FAILURE (1) if the
+ * atom is not found.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_atom_of_index_get
+                (const tng_trajectory_t tng_data,
+                 const tng_residue_t residue,
+                 const int64_t index,
+                 tng_atom_t *atom);
+
+/**
+ * @brief Add an atom to a residue.
+ * @param tng_data is the trajectory containing the residue.
+ * @param residue is the residue to add an atom to.
+ * @param atom_name is a string containing the name of the atom.
+ * @param atom_type is a string containing the atom type of the atom.
+ * @param atom is a pointer to the newly created atom.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code atom_name != 0 \endcode The pointer to the atom name string
+ * must not be a NULL pointer.
+ * @pre \code atom_type != 0 \endcode The pointer to the atom_type string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_atom_add
+                (const tng_trajectory_t tng_data,
+                 const tng_residue_t residue,
+                 const char *atom_name,
+                 const char *atom_type,
+                 tng_atom_t *atom);
+
+/**
+ * @brief Add an atom with a specific ID to a residue.
+ * @param tng_data is the trajectory containing the residue.
+ * @param residue is the residue to add an atom to.
+ * @param atom_name is a string containing the name of the atom.
+ * @param atom_type is a string containing the atom type of the atom.
+ * @param id is the ID of the created atom.
+ * @param atom is a pointer to the newly created atom.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code atom_name != 0 \endcode The pointer to the atom name string
+ * must not be a NULL pointer.
+ * @pre \code atom_type != 0 \endcode The pointer to the atom_type string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+ * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_atom_w_id_add
+                (const tng_trajectory_t tng_data,
+                 const tng_residue_t residue,
+                 const char *atom_name,
+                 const char *atom_type,
+                 const int64_t id,
+                 tng_atom_t *atom);
+
+/**
+ * @brief Get the residue of an atom.
+ * @param tng_data the trajectory containing the atom.
+ * @param atom the atom of which to get the name.
+ * @param residue is set to the residue of the atom.
+ * @pre \code atom != 0 \endcode The atom must not be NULL.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_residue_get
+                (const tng_trajectory_t tng_data,
+                 const tng_atom_t atom,
+                 tng_residue_t *residue);
+
+/**
+ * @brief Get the name of an atom.
+ * @param tng_data the trajectory containing the atom.
+ * @param atom the atom of which to get the name.
+ * @param name the string to fill with the name of the atom,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for name. This includes \0 terminating character.
+ * @pre \code atom != 0 \endcode The atom must not be NULL.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_name_get
+                (const tng_trajectory_t tng_data,
+                 const tng_atom_t atom,
+                 char *name,
+                 const int max_len);
+
+/**
+ * @brief Set the name of an atom.
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param atom is the atom to rename.
+ * @param new_name is a string containing the wanted name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_name_set
+                (const tng_trajectory_t tng_data,
+                 const tng_atom_t atom,
+                 const char *new_name);
+
+/**
+ * @brief Get the type of an atom.
+ * @param tng_data the trajectory containing the atom.
+ * @param atom the atom of which to get the type.
+ * @param type the string to fill with the type of the atom,
+ * memory must be allocated before.
+ * @param max_len maximum char length of the string, i.e. how much memory has
+ * been reserved for type. This includes \0 terminating character.
+ * @pre \code atom != 0 \endcode The atom must not be NULL.
+ * @pre \code type != 0 \endcode The pointer to the type string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred (source string longer than destination string).
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_type_get
+                (const tng_trajectory_t tng_data,
+                 const tng_atom_t atom,
+                 char *type,
+                 const int max_len);
+
+/**
+ * @brief Set the atom type of an atom.
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param atom is the atom to change.
+ * @param new_type is a string containing the atom type.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code new_type != 0 \endcode The pointer to the atom type string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_type_set
+                (const tng_trajectory_t tng_data,
+                 const tng_atom_t atom,
+                 const char *new_type);
+
+/**
+ * @brief Get the molecule name of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param name is a string, which is set to the name of the molecule. Memory
+ * must be reserved beforehand.
+ * @param max_len is the maximum length of name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_name_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 const int max_len);
+
+/**
+ * @brief Get the molecule id of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param id is will be set to the id of the molecule.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code id != 0 \endcode The pointer to id must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molecule_id_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 int64_t *id);
+
+/**
+ * @brief Get the bonds of the current molecular system.
+ * @param tng_data is the trajectory data container containing the molecular
+ * system.
+ * @param n_bonds is set to the number of bonds in the molecular system and
+ * thereby also the lengths of the two lists: from_atoms and to_atoms.
+ * @param from_atoms is a list (memory reserved by this function) of atoms
+ * (number of atom in mol system) in bonds.
+ * @param to_atoms is a list (memory reserved by this function) of atoms
+ * (number of atom in mol system) in bonds.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_bonds != 0 \endcode The pointer to n_bonds must not be a
+ * NULL pointer.
+ * @pre \code from_atoms != 0 \endcode The pointer to from_atoms must not
+ * be a NULL pointer.
+ * @pre \code to_atoms != 0 \endcode The pointer to to_atoms must not
+ * be a NULL pointer.
+ * @details The two lists of atoms use the same index, i.e. from_atoms[0]
+ * and to_atoms[0] are linked with a bond. Since memory is reserved in
+ * this function it must be freed afterwards.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_molsystem_bonds_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n_bonds,
+                 int64_t **from_atoms,
+                 int64_t **to_atoms);
+
+/**
+ * @brief Get the chain name of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param name is a string, which is set to the name of the chain. Memory
+ * must be reserved beforehand.
+ * @param max_len is the maximum length of name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_chain_name_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 const int max_len);
+
+/**
+ * @brief Get the residue name of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param name is a string, which is set to the name of the residue. Memory
+ * must be reserved beforehand.
+ * @param max_len is the maximum length of name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_name_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 const int max_len);
+
+/**
+ * @brief Get the residue id (local to molecule) of real particle number
+ * (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param id is a pointer to the variable, which will be set to the ID.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code id != 0 \endcode The pointer to id must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_residue_id_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 int64_t *id);
+
+/**
+ * @brief Get the residue id (based on other molecules and molecule counts)
+ * of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param id is a pointer to the variable, which will be set to the ID.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code id != 0 \endcode The pointer to id must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_global_residue_id_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 int64_t *id);
+
+/**
+ * @brief Get the atom name of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param name is a string, which is set to the name of the atom. Memory
+ * must be reserved beforehand.
+ * @param max_len is the maximum length of name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_name_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *name,
+                 const int max_len);
+
+/**
+ * @brief Get the atom type of real particle number (number in mol system).
+ * @param tng_data is the trajectory data container containing the atom.
+ * @param nr is the real number of the particle in the molecular system.
+ * @param type is a string, which is set to the type of the atom. Memory
+ * must be reserved beforehand.
+ * @param max_len is the maximum length of type.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code type != 0 \endcode The pointer to the type string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_atom_type_of_particle_nr_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t nr,
+                 char *type,
+                 const int max_len);
+
+/**
+ * @brief Add a particle mapping table.
+ * @details Each particle mapping table will be written as a separate block,
+ * followed by the data blocks for the corresponding particles. In most cases
+ * there is one particle mapping block for each thread writing the trajectory.
+ * @param tng_data is the trajectory, with the frame set to which to add
+ * the mapping block.
+ * @details The mapping information is added to the currently active frame set
+ * of tng_data
+ * @param num_first_particle is the first particle number of this mapping
+ * block.
+ * @param n_particles is the number of particles in this mapping block.
+ * @param mapping_table is a list of the real particle numbers (i.e. the numbers
+ * used in the molecular system). The list is n_particles long.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details mapping_table[0] is the real particle number of the first particle
+ * in the following data blocks.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_mapping_add
+                (const tng_trajectory_t tng_data,
+                 const int64_t num_first_particle,
+                 const int64_t n_particles,
+                 const int64_t *mapping_table);
+
+/**
+ * @brief Remove all particle mappings (in memory) from the current frame set.
+ * @details Clears the currently setup particle mappings of the current frame
+ * set.
+ * @param tng_data is the trajectory, with the frame set of which to clear
+ * all particle mappings.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_particle_mapping_free
+                (const tng_trajectory_t tng_data);
+
+/**
+ * @brief Read the header blocks from the input_file of tng_data.
+ * @details The trajectory blocks must be read separately and iteratively in chunks
+ * to fit in memory.
+ * @param tng_data is a trajectory data container.
+ * @details tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_file_headers_read
+                (const tng_trajectory_t tng_data,
+                 const char hash_mode);
+
+/**
+ * @brief Write the header blocks to the output_file of tng_data.
+ * @details The trajectory blocks must be written separately and iteratively in chunks
+ * to fit in memory.
+ * @param tng_data is a trajectory data container.
+ * @details tng_data->output_file_path
+ * specifies which file to write to. If the file (output_file) is not open it
+ * will be opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash for each header block will be generated.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_file_headers_write
+                (const tng_trajectory_t tng_data,
+                 const char hash_mode);
+
+/**
+ * @brief Read one (the next) block (of any kind) from the input_file of tng_data.
+ * @param tng_data is a trajectory data container.
+ * @details tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_data is a pointer to the struct which will be populated with the
+ * data.
+ * @details If block_data->input_file_pos > 0 it is the position from where the
+ * reading starts otherwise it starts from the current position.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code block != 0 \endcode The block container (block) must be
+ * initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_block_read_next
+                (const tng_trajectory_t tng_data,
+                 const tng_gen_block_t block_data,
+                 const char hash_mode);
+
+/**
+ * @brief Read one frame set, including all particle mapping blocks and data
+ * blocks, starting from the current file position.
+ * @param tng_data is a trajectory data container.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read
+                (const tng_trajectory_t tng_data,
+                 const char hash_mode);
+
+/**
+ * @brief Read data from the current frame set from the input_file. Only read
+ * particle mapping and data blocks matching the specified block_id.
+ * @param tng_data is a trajectory data container.
+ * @details  tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @param block_id is the ID of the data block to read from file.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read_current_only_data_from_block_id
+                (const tng_trajectory_t tng_data,
+                 const char hash_mode,
+                 const int64_t block_id);
+
+/**
+ * @brief Read one (the next) frame set, including particle mapping and related data blocks
+ * from the input_file of tng_data.
+ * @param tng_data is a trajectory data container.
+ * @details  tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read_next
+                (const tng_trajectory_t tng_data,
+                 const char hash_mode);
+
+/**
+ * @brief Read one (the next) frame set, including particle mapping and data blocks with a
+ * specific block id from the input_file of tng_data.
+ * @param tng_data is a trajectory data container.
+ * @details  tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @param block_id is the ID number of the blocks that should be read from file.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_read_next_only_data_from_block_id
+                (const tng_trajectory_t tng_data,
+                 const char hash_mode,
+                 const int64_t block_id);
+
+/**
+ * @brief Write one frame set, including mapping and related data blocks
+ * to the output_file of tng_data.
+ * @param tng_data is a trajectory data container.
+ * @details  tng_data->output_file_path specifies
+ * which file to write to. If the file (output_file) is not open it will be
+ * opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash for each header block will be generated.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_write
+                (const tng_trajectory_t tng_data,
+                 const char hash_mode);
+
+/**
+ * @brief Write one frame set even if it does not have as many frames as
+ * expected. The function also writes mapping and related data blocks
+ * to the output_file of tng_data.
+ * @param tng_data is a trajectory data container.
+ * @details  tng_data->output_file_path specifies
+ * which file to write to. If the file (output_file) is not open it will be
+ * opened.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH an md5 hash for each header block will be generated.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @details The number of frames in the frame set is set to the number of
+ * frames of the data blocks before writing it to disk.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_premature_write
+                (const tng_trajectory_t tng_data,
+                 const char hash_mode);
+
+/**
+ * @brief Create and initialise a frame set.
+ * @details Particle mappings are retained from previous frame set (if any).
+ * To explicitly clear particle mappings use tng_frame_set_particle_mapping_free().
+ * @param tng_data is the trajectory data container in which to add the frame
+ * set.
+ * @param first_frame is the first frame of the frame set.
+ * @param n_frames is the number of frames in the frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code first_frame >= 0 \endcode The first frame must not be negative.
+ * @pre \code n_frames >= 0 \endcode The number of frames must not be negative.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_new
+                (const tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t n_frames);
+
+/**
+ * @brief Create and initialise a frame set with the time of the first frame
+ * specified.
+ * @param tng_data is the trajectory data container in which to add the frame
+ * set.
+ * @param first_frame is the first frame of the frame set.
+ * @param n_frames is the number of frames in the frame set.
+ * @param first_frame_time is the time stamp of the first frame (in seconds).
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code first_frame >= 0 \endcode The first frame must not be negative.
+ * @pre \code n_frames >= 0 \endcode The number of frames must not be negative.
+ * @pre \code first_frame_time >= 0 \endcode The time stamp of the first frame
+ * must not be negative.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_with_time_new
+                (const tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t n_frames,
+                 const double first_frame_time);
+
+/**
+ * @brief Set the time stamp of the first frame of the current frame set.
+ * @param tng_data is the trajectory containing the frame set.
+ * @param first_frame_time is the time stamp of the first frame in the
+ * frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code first_frame_time >= 0 \endcode The time stamp of the first frame
+ * must not be negative.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_set_first_frame_time_set
+                (const tng_trajectory_t tng_data,
+                 const double first_frame_time);
+
+/**
+ * @brief Read the number of the first frame of the next frame set.
+ * @param tng_data is the trajectory containing the frame set.
+ * @param frame is set to the frame number of the first frame in the
+ * next frame set.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code tng_data->input_file != 0 \endcode An input file must be open
+ * to find the next frame set.
+ * @pre \code frame != 0 \endcode The pointer to the frame must not be a NULL
+ * pointer.
+ * @return TNG_SUCCESS(0) if successful, TNG_FAILURE(1) if there is no next
+ * frame set or TNG_CRITICAL(2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_first_frame_nr_of_next_frame_set_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *frame);
+
+/**
+ * @brief Add a non-particle dependent data block.
+ * @param tng_data is the trajectory data container in which to add the data
+ * block
+ * @param id is the block ID of the block to add.
+ * @param block_name is a descriptive name of the block to add
+ * @param datatype is the datatype of the data in the block (e.g. int/float)
+ * @param block_type_flag indicates if this is a non-trajectory block (added
+ * directly to tng_data) or if it is a trajectory block (added to the
+ * frame set)
+ * @param n_frames is the number of frames of the data block (automatically
+ * set to 1 if adding a non-trajectory data block)
+ * @param n_values_per_frame is how many values a stored each frame (e.g. 9
+ * for a box shape block)
+ * @param stride_length is how many frames are between each entry in the
+ * data block
+ * @param codec_id is the ID of the codec to compress the data.
+ * @param new_data is an array of data values to add.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code block_name != 0 \endcode The pointer to the block name must
+ * not be a NULL pointer.
+ * @pre \code n_values_per_frame > 0 \endcode n_values_per_frame must be
+ * a positive integer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_block_add
+                (const tng_trajectory_t tng_data,
+                 const int64_t id,
+                 const char *block_name,
+                 const char datatype,
+                 const char block_type_flag,
+                 int64_t n_frames,
+                 const int64_t n_values_per_frame,
+                 int64_t stride_length,
+                 const int64_t codec_id,
+                 void *new_data);
+
+/**
+ * @brief Add a particle dependent data block.
+ * @param tng_data is the trajectory data container in which to add the data
+ * block
+ * @param id is the block ID of the block to add.
+ * @param block_name is a descriptive name of the block to add
+ * @param datatype is the datatype of the data in the block (e.g. int/float)
+ * @param block_type_flag indicates if this is a non-trajectory block (added
+ * directly to tng_data) or if it is a trajectory block (added to the
+ * frame set)
+ * @param n_frames is the number of frames of the data block (automatically
+ * set to 1 if adding a non-trajectory data block)
+ * @param n_values_per_frame is how many values a stored each frame (e.g. 9
+ * for a box shape block)
+ * @param stride_length is how many frames are between each entry in the
+ * data block
+ * @param num_first_particle is the number of the first particle stored
+ * in this data block
+ * @param n_particles is the number of particles stored in this data block
+ * @param codec_id is the ID of the codec to compress the data.
+ * @param new_data is an array of data values to add.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code block_name != 0 \endcode The pointer to the block name must
+ * not be a NULL pointer.
+ * @pre \code n_values_per_frame > 0 \endcode n_values_per_frame must be
+ * a positive integer.
+ * @pre \code num_first_particle >= 0 \endcode The number of the
+ * first particle must be >= 0.
+ * @pre \code n_particles >= 0 \endcode n_particles must be >= 0.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_block_add
+                (const tng_trajectory_t tng_data,
+                 const int64_t id,
+                 const char *block_name,
+                 const char datatype,
+                 const char block_type_flag,
+                 int64_t n_frames,
+                 const int64_t n_values_per_frame,
+                 int64_t stride_length,
+                 const int64_t num_first_particle,
+                 const int64_t n_particles,
+                 const int64_t codec_id,
+                 void *new_data);
+
+/** @brief Get the name of a data block of a specific ID.
+ * @param tng_data is the trajectory data container.
+ * @param block_id is the ID of the data block of which to get the name.
+ * @param name is a string, which is set to the name of the data block.
+ * Memory must be reserved beforehand.
+ * @param max_len is the maximum length of name.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code name != 0 \endcode The pointer to the name string
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the data block is found, TNG_FAILURE (1)
+ * if a minor error has occured or the data block is not found or
+ * TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_block_name_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 char *name,
+                 const int max_len);
+
+/** @brief Get the dependency of a data block of a specific ID.
+ * @param tng_data is the trajectory data container.
+ * @param block_id is the ID of the data block of which to get the name.
+ * @param block_dependency is a pointer to the dependency of the data block.
+ * If the block is frame dependent it will be set to TNG_FRAME_DEPENDENT,
+ * if it is particle dependent it will be set to TNG_PARTICLE_DEPENDENT and
+ * if it is both it will be set to TNG_FRAME_DEPENDENT & TNG_PARTICLE_DEPENDENT.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code block_dependency != 0 \endcode The pointer to the block dependency
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the data block is found, TNG_FAILURE (1)
+ * if a minor error has occured or the data block is not found or
+ * TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_block_dependency_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 int *block_dependency);
+
+/** @brief Get the number of values per frame of a data block of a specific ID.
+ * @param tng_data is the trajectory data container.
+ * @param block_id is the ID of the data block of which to get the name.
+ * @param n_values_per_frame is a pointer set to the number of values per frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of values
+ * per frame must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if the data block is found, TNG_FAILURE (1)
+ * if a minor error has occured or the data block is not found or
+ * TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_block_num_values_per_frame_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 int64_t *n_values_per_frame);
+
+/**
+ * @brief Write data of one trajectory frame to the output_file of tng_data.
+ * @param tng_data is a trajectory data container. tng_data->output_file_path
+ * specifies which file to write to. If the file (output_file) is not open it
+ * will be opened.
+ * @param frame_nr is the index number of the frame to write.
+ * @param block_id is the ID of the data block to write the data to.
+ * @param values is an array of data to write. The length of the array should
+ * equal n_values_per_frame.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code values != 0 \endcode The pointer to the values must not be a NULL
+ * pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_data_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const int64_t block_id,
+                 const void *values,
+                 const char hash_mode);
+
+/**
+ * @brief Write particle data of one trajectory frame to the output_file of
+ * tng_data.
+ * @param tng_data is a trajectory data container. tng_data->output_file_path
+ * specifies which file to write to. If the file (output_file) is not open it
+ * will be opened.
+ * @param frame_nr is the index number of the frame to write.
+ * @param block_id is the ID of the data block to write the data to.
+ * @param val_first_particle is the number of the first particle in the data
+ * array.
+ * @param val_n_particles is the number of particles in the data array.
+ * @param values is a 1D-array of data to write. The length of the array should
+ * equal n_particles * n_values_per_frame.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code val_first_particle >= 0 \endcode The number of the
+ * first particle must be >= 0.
+ * @pre \code val_n_particles >= 0 \endcode The number of particles must be >= 0.
+ * @pre \code values != 0 \endcode The pointer to the values must not be a NULL
+ * pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_frame_particle_data_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const int64_t block_id,
+                 const int64_t val_first_particle,
+                 const int64_t val_n_particles,
+                 const void *values,
+                 const char hash_mode);
+
+/**
+ * @brief Free data of an array of values (2D).
+ * @param tng_data is a trajectory data container.
+ * @param values is the 2D array to free and will be set to 0 afterwards.
+ * @param n_frames is the number of frames in the data array.
+ * @param n_values_per_frame is the number of values per frame in the data array.
+ * @param type is the data type of the data in the array (e.g. int/float/char).
+ * @details This function should not be used. The data_values union is obsolete.
+ * This function also causes memory leaks, but its signature cannot be changed
+ * without disturbing the API.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_values_free
+                (const tng_trajectory_t tng_data,
+                 union data_values **values,
+                 const int64_t n_frames,
+                 const int64_t n_values_per_frame,
+                 const char type);
+
+/**
+ * @brief Free data of an array of values (3D).
+ * @param tng_data is a trajectory data container.
+ * @param values is the array to free and will be set to 0 afterwards.
+ * @param n_frames is the number of frames in the data array.
+ * @param n_particles is the number of particles in the data array.
+ * @param n_values_per_frame is the number of values per frame in the data array.
+ * @param type is the data type of the data in the array (e.g. int/float/char).
+ * @details This function should not be used. The data_values union is obsolete.
+ * This function also causes memory leaks, but its signature cannot be changed
+ * without disturbing the API.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_values_free
+                (const tng_trajectory_t tng_data,
+                 union data_values ***values,
+                 const int64_t n_frames,
+                 const int64_t n_particles,
+                 const int64_t n_values_per_frame,
+                 const char type);
+
+/**
+ * @brief Retrieve non-particle data, from the last read frame set. Obsolete!
+ * @param tng_data is a trajectory data container. tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param values is a pointer to a 2-dimensional array (memory unallocated), which
+ * will be filled with data. The array will be sized
+ * (n_frames * n_values_per_frame).
+ * Since ***values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param n_frames is set to the number of frames in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_frames != 0 \endcode The pointer to the number of frames
+ * must not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This function is obsolete and only retained for compatibility. Use
+ * tng_data_vector_get() instead.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_get(const tng_trajectory_t tng_data,
+                                                   const int64_t block_id,
+                                                   union data_values ***values,
+                                                   int64_t *n_frames,
+                                                   int64_t *n_values_per_frame,
+                                                   char *type);
+
+/**
+ * @brief Retrieve a vector (1D array) of non-particle data, from the last read frame set.
+ * @param tng_data is a trajectory data container. tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+ * will be filled with data. The length of the array will be
+ * (n_frames_with_data (see stride_length) * n_values_per_frame).
+ * Since **values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param n_frames is set to the number of particles in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param stride_length is set to the stride length of the returned data.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_frames != 0 \endcode The pointer to the number of frames
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This does only work for numerical (int, float, double) data.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_vector_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 void **values,
+                 int64_t *n_frames,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Read and retrieve non-particle data, in a specific interval. Obsolete!
+ * @param tng_data is a trajectory data container. tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param start_frame_nr is the index number of the first frame to read.
+ * @param end_frame_nr is the index number of the last frame to read.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @param values is a pointer to a 2-dimensional array (memory unallocated), which
+ * will be filled with data. The array will be sized
+ * (n_frames * n_values_per_frame).
+ * Since ***values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This function is obsolete and only retained for compatibility. Use
+ * tng_data_vector_interval_get() instead.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_interval_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 union data_values ***values,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Read and retrieve a vector (1D array) of non-particle data,
+ * in a specific interval.
+ * @param tng_data is a trajectory data container. tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param start_frame_nr is the index number of the first frame to read.
+ * @param end_frame_nr is the index number of the last frame to read.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+ * will be filled with data. The length of the array will be
+ * (n_frames_with_data (see stride_length) * n_values_per_frame).
+ * Since **values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param stride_length is set to the stride length (writing interval) of
+ * the data.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This does only work for numerical (int, float, double) data.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_vector_interval_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 void **values,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Retrieve particle data, from the last read frame set. Obsolete!
+ * @details The particle dimension of the returned values array is translated
+ * to real particle numbering, i.e. the numbering of the actual molecular
+ * system.
+ * @param tng_data is a trajectory data container. tng_data->input_file_path
+ * specifies which file to read from. If the file (input_file) is not open it
+ * will be opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param values is a pointer to a 3-dimensional array (memory unallocated), which
+ * will be filled with data. The array will be sized
+ * (n_frames * n_particles * n_values_per_frame).
+ * Since ****values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param n_frames is set to the number of frames in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param n_particles is set to the number of particles in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_frames != 0 \endcode The pointer to the number of frames
+ * must not be a NULL pointer.
+ * @pre \code n_particles != 0 \endcode The pointer to the number of particles must
+ * not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This function is obsolete and only retained for compatibility. Use
+ * tng_particle_data_vector_get() instead.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 union data_values ****values,
+                 int64_t *n_frames,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Retrieve a vector (1D array) of particle data, from the last read frame set.
+ * @details The particle dimension of the returned values array is translated
+ * to real particle numbering, i.e. the numbering of the actual molecular
+ * system.
+ * @param tng_data is a trajectory data container. tng_data->input_file_path
+ * specifies which file to read from. If the file (input_file) is not open it
+ * will be opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+ * will be filled with data. The length of the array will be
+ * (n_frames_with_data (see stride_length) * n_particles * n_values_per_frame).
+ * Since **values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param n_frames is set to the number of frames in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param stride_length is set to the stride length of the returned data.
+ * @param n_particles is set to the number of particles in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_particles != 0 \endcode The pointer to the number of particles must
+ * not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This does only work for numerical (int, float, double) data.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_vector_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 void **values,
+                 int64_t *n_frames,
+                 int64_t *stride_length,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Read and retrieve particle data, in a specific interval. Obsolete!
+ * @details The particle dimension of the returned values array is translated
+ * to real particle numbering, i.e. the numbering of the actual molecular
+ * system.
+ * @param tng_data is a trajectory data container. tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param start_frame_nr is the index number of the first frame to read.
+ * @param end_frame_nr is the index number of the last frame to read.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @param values is a pointer to a 3-dimensional array (memory unallocated), which
+ * will be filled with data. The array will be sized
+ * (n_frames * n_particles * n_values_per_frame).
+ * Since ****values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param n_particles is set to the number of particles in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_frames != 0 \endcode The pointer to the number of frames
+ * must not be a NULL pointer.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code n_particles != 0 \endcode The pointer to the number of particles must
+ * not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This function is obsolete and only retained for compatibility. Use
+ * tng_particle_data_vector_interval_get() instead.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_interval_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 union data_values ****values,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Read and retrieve a vector (1D array) particle data, in a
+ * specific interval.
+ * @details The particle dimension of the returned values array is translated
+ * to real particle numbering, i.e. the numbering of the actual molecular
+ * system.
+ * @param tng_data is a trajectory data container. tng_data->input_file_path specifies
+ * which file to read from. If the file (input_file) is not open it will be
+ * opened.
+ * @param block_id is the id number of the particle data block to read.
+ * @param start_frame_nr is the index number of the first frame to read.
+ * @param end_frame_nr is the index number of the last frame to read.
+ * @param hash_mode is an option to decide whether to use the md5 hash or not.
+ * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+ * compared to the md5 hash of the read contents to ensure valid data.
+ * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+ * will be filled with data. The length of the array will be
+ * (n_frames_with_data (see stride_length) * n_particles * n_values_per_frame).
+ * Since **values is allocated in this function it is the callers
+ * responsibility to free the memory.
+ * @param stride_length is set to the stride length (writing interval) of
+ * the data.
+ * @param n_particles is set to the number of particles in the returned data. This is
+ * needed to properly reach and/or free the data afterwards.
+ * @param n_values_per_frame is set to the number of values per frame in the data.
+ * This is needed to properly reach and/or free the data afterwards.
+ * @param type is set to the data type of the data in the array.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code n_particles != 0 \endcode The pointer to the number of particles must
+ * not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @pre \code n_values_per_frame != 0 \endcode The pointer to the number of
+ * values per frame must not be a NULL pointer.
+ * @pre \code type != 0 \endcode The pointer to the data type must not
+ * be a NULL pointer.
+ * @details This does only work for numerical (int, float, double) data.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_particle_data_vector_interval_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 void **values,
+                 int64_t *n_particles,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type);
+
+/**
+ * @brief Get the stride length of a specific data (particle dependency does not matter)
+ * block, either in the current frame set or of a specific frame.
+ * @param tng_data is the trajectory data container.
+ * @param block_id is the block ID of the data block, of which to retrieve the
+ * stride length of the data.
+ * @param frame is the frame from which to get the stride length. If frame is set to -1
+ * no specific frame will be used, but instead the first frame, starting from the last read
+ * frame set, containing the data block will be used.
+ * @param stride_length is set to the value of the stride length of the data block.
+ * @return  TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occurred or TNG_CRITICAL (2) if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_data_get_stride_length
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 int64_t frame,
+                 int64_t *stride_length);
+
+/**
+ * @brief Get the date and time of initial file creation in ISO format (string).
+ * @param tng_data is a trajectory data container.
+ * @param time is a pointer to the string in which the date will be stored. Memory
+ * must be reserved beforehand.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code time != 0 \endcode The pointer to the time must not be a NULL
+ * pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_time_get_str
+                (const tng_trajectory_t tng_data,
+                 char *time);
+/** @} */ /* end of group1 */
+
+/** @defgroup group2 High-level API
+ *  These functions make it easier to access and output TNG data. They
+ *  are recommended unless there is a special reason to use the more
+ *  detailed functions available in the low-level API.
+ *  @{
+ */
+
+/**
+ * @brief High-level function for opening and initializing a TNG trajectory.
+ * @param filename is a string containing the name of the trajectory to open.
+ * @param mode specifies the file mode of the trajectory. Can be set to 'r',
+ * 'w' or 'a' for reading, writing or appending respectively.
+ * @param tng_data_p is a pointer to the opened trajectory. This will be
+ * allocated by the TNG library. The trajectory must be
+ * closed by the user, whereby memory is freed.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code filename != 0 \endcode The pointer to the filename must not be a
+ * NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_open
+                (const char *filename,
+                 const char mode,
+                 tng_trajectory_t *tng_data_p);
+
+/**
+ * @brief High-level function for closing a TNG trajectory.
+ * @param tng_data_p is a pointer to the trajectory to close. The memory
+ * will be freed after finalising the writing.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_close
+                (tng_trajectory_t *tng_data_p);
+
+/**
+ * @brief High-level function for getting the time (in seconds) of a frame.
+ * @param tng_data is the trajectory containing the frame.
+ * @param frame_nr is the frame number of which to get the time.
+ * @param time is set to the time (in seconds) of the specified frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code time != 0 \endcode The pointer to the time must not be a
+ * NULL pointer.
+ * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if a
+ * minor error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_time_of_frame_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 double *time);
+
+/*
+ * @brief High-level function for getting the molecules in the mol system.
+ * @param tng_data is the trajectory containing the mol system.
+ * @param n_mols is set to the number of molecules in the system.
+ * @param molecule_cnt_list will be pointing to the list of counts of each molecule
+ * in the mol system.
+ * @param mols pointing to the list of molecules in the mol system.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_mols != 0 \endcode The pointer to the number of molecules must
+ * not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful.
+ */
+/*tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_molecules_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n_mols,
+                 int64_t **molecule_cnt_list,
+                 tng_molecule_t *mols);
+*/
+/*
+ * @brief High-level function for adding a molecule to the mol system.
+ * @param tng_data is the trajectory containing the mol system.
+ * @param name is the name of the molecule to add.
+ * @param cnt is the count of the molecule.
+ * @param mol is set to point to the newly created molecule.
+ * @pre \code name != 0 \endcode The pointer to the name must not be a
+ * NULL pointer.
+ * @pre \code cnt >= 0 \endcode The requested count must be >= 0.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured or TNG_CRITICAL (2) if a major error has occured.
+ */
+/*tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_molecule_add
+                (const tng_trajectory_t tng_data,
+                 const char *name,
+                 const int64_t cnt,
+                 tng_molecule_t *mol);
+*/
+/*
+// tng_function_status DECLSPECDLLEXPORT tng_util_molecule_particles_get
+//                 (const tng_trajectory_t tng_data,
+//                  const tng_molecule_t mol,
+//                  int64_t *n_particles,
+//                  char ***names,
+//                  char ***types,
+//                  char ***res_names,
+//                  int64_t **res_ids,
+//                  char ***chain_names,
+//                  int64_t **chain_ids);
+//
+// tng_function_status DECLSPECDLLEXPORT tng_util_molecule_particles_set
+//                 (const tng_trajectory_t tng_data,
+//                  tng_molecule_t mol,
+//                  const int64_t n_particles,
+//                  const char **names,
+//                  const char **types,
+//                  const char **res_names,
+//                  const int64_t *res_ids,
+//                  const char **chain_names,
+//                  const int64_t *chain_ids);
+*/
+/**
+ * @brief High-level function for reading the positions of all particles
+ * from all frames.
+ * @param tng_data is the trajectory to read from.
+ * @param positions will be set to point at a 1-dimensional array of floats,
+ * which will contain the positions. The data is stored sequentially in order
+ * of frames. For each frame the positions (x, y and z coordinates) are stored.
+ * The variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code positions != 0 \endcode The pointer to the positions array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_read
+                (const tng_trajectory_t tng_data,
+                 float **positions,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the velocities of all particles
+ * from all frames.
+ * @param tng_data is the trajectory to read from.
+ * @param velocities will be set to point at a 1-dimensional array of floats,
+ * which will contain the velocities. The data is stored sequentially in order
+ * of frames. For each frame the velocities (in x, y and z) are stored. The
+ * variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code velocities != 0 \endcode The pointer to the velocities array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_read
+                (const tng_trajectory_t tng_data,
+                 float **velocities,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the forces of all particles
+ * from all frames.
+ * @param tng_data is the trajectory to read from.
+ * @param forces will be set to point at a 1-dimensional array of floats,
+ * which will contain the forces. The data is stored sequentially in order
+ * of frames. For each frame the forces (in x, y and z) are stored. The
+ * variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code forces != 0 \endcode The pointer to the forces array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_read
+                (const tng_trajectory_t tng_data,
+                 float **forces,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the box shape from all frames.
+ * @param tng_data is the trajectory to read from.
+ * @param box_shape will be set to point at a 1-dimensional array of floats,
+ * which will contain the box shape. The data is stored sequentially in order
+ * of frames. The variable may point at already allocated memory or be a NULL pointer.
+ * If the box shape is not modified during the trajectory, but as general data,
+ * that will be returned instead.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @details This function should only be used if number of values used to specify
+ * the box shape is known (by default TNG uses 9 values) since it does not
+ * return the number of values in the array. It is recommended to use
+ * tng_data_vector_interval_get() instead.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code box_shape != 0 \endcode The pointer to the box_shape array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_read
+                (const tng_trajectory_t tng_data,
+                 float **box_shape,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the next frame of particle-dependent
+ * data of a specific type.
+ * @param tng_data is the trajectory to read from.
+ * @param block_id is the ID number of the block containing the data of interest.
+ * @param values will be set to point at a 1-dimensional array containing the
+ * requested data. The variable may point at already allocated memory (which will
+ * be reallocated with realloc()), or be a
+ * NULL pointer. The calling code must free the memory afterwards.
+ * @param data_type will be pointing to a character indicating the size of the
+ * data of the returned values, e.g. TNG_INT_DATA, TNG_FLOAT_DATA or TNG_DOUBLE_DATA.
+ * @param retrieved_frame_number will be pointing at the frame number of the
+ * returned frame.
+ * @param retrieved_time will be pointing at the time stamp of the returned
+ * frame.
+ * @details If no frame has been read before the first frame of the trajectory
+ * is read.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code values != 0 \endcode The pointer to the values array
+ * must not be a NULL pointer.
+ * @pre \code data_type != 0 \endcode The pointer to the data type of the
+ * returned data must not be a NULL pointer.
+ * @pre \code retrieved_frame_number != 0 \endcode The pointer to the frame
+ * number of the returned data must not be a NULL pointer.
+ * @pre \code retrieved_time != 0 \endcode The pointer to the time of the
+ * returned data must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_particle_data_next_frame_read
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 void **values,
+                 char *data_type,
+                 int64_t *retrieved_frame_number,
+                 double *retrieved_time);
+
+/**
+ * @brief High-level function for reading the next frame of non-particle-dependent
+ * data of a specific type.
+ * @param tng_data is the trajectory to read from.
+ * @param block_id is the ID number of the block containing the data of interest.
+ * @param values will be set to point at a 1-dimensional array containing the
+ * requested data. The variable may point at already allocated memory or be a
+ * NULL pointer. The memory must be freed afterwards.
+ * @param data_type will be pointing to a character indicating the size of the
+ * data of the returned values, e.g. TNG_INT_DATA, TNG_FLOAT_DATA or TNG_DOUBLE_DATA.
+ * @param retrieved_frame_number will be pointing at the frame number of the
+ * returned frame.
+ * @param retrieved_time will be pointing at the time stamp of the returned
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code values != 0 \endcode The pointer to the values array
+ * must not be a NULL pointer.
+ * @pre \code data_type != 0 \endcode The pointer to the data type of the
+ * returned data must not be a NULL pointer.
+ * @pre \code retrieved_frame_number != 0 \endcode The pointer to the frame
+ * number of the returned data must not be a NULL pointer.
+ * @pre \code retrieved_time != 0 \endcode The pointer to the time of the
+ * returned data must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_non_particle_data_next_frame_read
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 void **values,
+                 char *data_type,
+                 int64_t *retrieved_frame_number,
+                 double *retrieved_time);
+
+/**
+ * @brief High-level function for reading the positions of all particles
+ * from a specific range of frames.
+ * @param tng_data is the trajectory to read from.
+ * @param first_frame is the first frame to return position data from.
+ * @param last_frame is the last frame to return position data from.
+ * @param positions will be set to point at a 1-dimensional array of floats,
+ * which will contain the positions. The data is stored sequentially in order
+ * of frames. For each frame the positions (x, y and z coordinates) are stored.
+ * The variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code positions != 0 \endcode The pointer to the positions array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_read_range
+                (const tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t last_frame,
+                 float **positions,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the velocities of all particles
+ * from a specific range of frames.
+ * @param tng_data is the trajectory to read from.
+ * @param first_frame is the first frame to return position data from.
+ * @param last_frame is the last frame to return position data from.
+ * @param velocities will be set to point at a 1-dimensional array of floats,
+ * which will contain the velocities. The data is stored sequentially in order
+ * of frames. For each frame the velocities (in x, y and z) are stored. The
+ * variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code velocities != 0 \endcode The pointer to the velocities array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_read_range
+                (const tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t last_frame,
+                 float **velocities,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the forces of all particles
+ * from a specific range of frames.
+ * @param tng_data is the trajectory to read from.
+ * @param first_frame is the first frame to return position data from.
+ * @param last_frame is the last frame to return position data from.
+ * @param forces will be set to point at a 1-dimensional array of floats,
+ * which will contain the forces. The data is stored sequentially in order
+ * of frames. For each frame the forces (in x, y and z) are stored. The
+ * variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code forces != 0 \endcode The pointer to the forces array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_read_range
+                (const tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t last_frame,
+                 float **forces,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for reading the box shape
+ * from a specific range of frames.
+ * @param tng_data is the trajectory to read from.
+ * @param first_frame is the first frame to return position data from.
+ * @param last_frame is the last frame to return position data from.
+ * @param box_shape will be set to point at a 1-dimensional array of floats,
+ * which will contain the box shape. The data is stored sequentially in order
+ * of frames.
+ * If the box shape is not modified during the trajectory, but as general data,
+ * that will be returned instead. The
+ * variable may point at already allocated memory or be a NULL pointer.
+ * The memory must be freed afterwards.
+ * @param stride_length will be set to the writing interval of the stored data.
+ * @details This function should only be used if number of values used to specify
+ * the box shape is known (by default TNG uses 9 values) since it does not
+ * return the number of values in the array. It is recommended to use
+ * tng_data_vector_interval_get() instead.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code start_frame_nr <= end_frame_nr \endcode The first frame must be before
+ * the last frame.
+ * @pre \code box_shape != 0 \endcode The pointer to the box_shape array
+ * must not be a NULL pointer.
+ * @pre \code stride_length != 0 \endcode The pointer to the stride length
+ * must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_read_range
+                (const tng_trajectory_t tng_data,
+                 const int64_t first_frame,
+                 const int64_t last_frame,
+                 float **box_shape,
+                 int64_t *stride_length);
+
+/**
+ * @brief High-level function for setting the writing interval of data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write_interval_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for setting the writing interval of data blocks
+ * containing double precision data.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write_interval_double_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for setting the writing interval of data blocks.
+ * Obsolete! Use tng_util_generic_write_interval_set()
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * This function is replaced by the more correcly named
+ * tng_util_generic_write_interval_set(), but is kept for compatibility.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write_frequency_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for setting the writing interval of position
+ * data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a positions data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write_interval_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of position
+ * data blocks containing double precision data.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a positions data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write_interval_double_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of position
+ * data blocks. Obsolete! Use tng_util_pos_write_interval_set()
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a positions data block if none exists.
+ * This function is replaced by the more correcly named
+ * tng_util_pos_write_interval_set(), but is kept for compatibility.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write_frequency_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of velocity
+ * data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a velocities data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write_interval_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of velocity
+ * data blocks containing double precision data.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a velocities data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write_interval_double_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of velocity
+ * data blocks. Obsolete! Use tng_util_vel_write_interval_set()
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a velocities data block if none exists.
+ * This function is replaced by the more correcly named
+ * tng_util_vel_write_interval_set(), but is kept for compatibility.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write_frequency_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of force
+ * data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a forces data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write_interval_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of force
+ * data blocks containing double precision data.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a forces data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write_interval_double_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of force
+ * data blocks. Obsolete! Use tng_util_force_write_interval_set()
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a forces data block if none exists.
+ * This function is replaced by the more correcly named
+ * tng_util_force_write_interval_set(), but is kept for compatibility.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write_frequency_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of box shape
+ * data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a box shape data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write_interval_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of box shape
+ * data blocks containing double precision data.
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code i >= 0 \endcode The writing interval must be >= 0.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a box shape data block if none exists.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write_interval_double_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for setting the writing interval of velocity
+ * data blocks. Obsolete! Use tng_util_box_shape_write_interval_set()
+ * @param tng_data is the trajectory to use.
+ * @param i is the output interval, i.e. i == 10 means data written every 10th
+ * frame.
+ * @details This function uses tng_util_generic_write_interval_set() and will
+ * create a box shape data block if none exists.
+ * This function is replaced by the more correcly named
+ * tng_util_box_shape_write_interval_set(), but is kept for compatibility.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write_frequency_set
+                (const tng_trajectory_t tng_data,
+                 const int64_t i);
+
+/**
+ * @brief High-level function for writing data of one frame to a data block.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data. If frame_nr < 0 the
+ * data is written as non-trajectory data.
+ * @param values is a 1D array of data to add. The array should be of length
+ * n_particles * n_values_per_frame if writing particle related data, otherwise
+ * it should be n_values_per_frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code values != 0 \endcode The pointer to the values array must not
+ * be a NULL pointer.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * N.b. Data is written a whole block at a time. The data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *values,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for writing data of one frame to a double precision
+ * data block.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data. If frame_nr < 0 the
+ * data is written as non-trajectory data.
+ * @param values is a 1D array of data to add. The array should be of length
+ * n_particles * n_values_per_frame if writing particle related data, otherwise
+ * it should be n_values_per_frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code values != 0 \endcode The pointer to the values array must not
+ * be a NULL pointer.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * N.b. Data is written a whole block at a time. The data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_double_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *values,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for adding data to positions data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data. If frame_nr < 0 the
+ * data is written as non-trajectory data.
+ * @param positions is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code positions != 0 \endcode The pointer to the positions array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a positions data block if none exists. Positions are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *positions);
+
+/**
+ * @brief High-level function for adding data to positions data blocks at double
+ * precision.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data. If frame_nr < 0 the
+ * data is written as non-trajectory data.
+ * @param positions is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code positions != 0 \endcode The pointer to the positions array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a positions data block if none exists. Positions are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_double_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *positions);
+
+/**
+ * @brief High-level function for adding data to velocities data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data. If frame_nr < 0 the
+ * data is written as non-trajectory data.
+ * @param velocities is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code velocities != 0 \endcode The pointer to the velocities array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a velocities data block if none exists. Velocities are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *velocities);
+
+/**
+ * @brief High-level function for adding data to velocities data blocks at double
+ * precision.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data. If frame_nr < 0 the
+ * data is written as non-trajectory data.
+ * @param velocities is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code velocities != 0 \endcode The pointer to the velocities array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a velocities data block if none exists. Velocities are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_double_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *velocities);
+
+/**
+ * @brief High-level function for adding data to forces data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data. If frame_nr < 0 the
+ * data is written as non-trajectory data.
+ * @param forces is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code forces != 0 \endcode The pointer to the forces array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a forces data block if none exists. Forces are stored as three
+ * values per frame and compressed using gzip compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *forces);
+
+/**
+ * @brief High-level function for adding data to forces data blocks at double
+ * precision.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data. If frame_nr < 0 the
+ * data is written as non-trajectory data.
+ * @param forces is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code forces != 0 \endcode The pointer to the forces array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a forces data block if none exists. Forces are stored as three
+ * values per frame and compressed using gzip compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_double_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *forces);
+
+/**
+ * @brief High-level function for adding data to box shape data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data. If frame_nr < 0 the
+ * data is written as non-trajectory data.
+ * @param box_shape is a 1D array of data to add. The array should be of length 9.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code box_shape != 0 \endcode The pointer to the box_shape array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a box shape data block if none exists. Box shapes are stored as 9
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const float *box_shape);
+
+/**
+ * @brief High-level function for adding data to box shape data blocks at double
+ * precision.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data. If frame_nr < 0 the
+ * data is written as non-trajectory data.
+ * @param box_shape is a 1D array of data to add. The array should be of length 9.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code box_shape != 0 \endcode The pointer to the box_shape array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_write() and will
+ * create a box shape data block if none exists. Box shapes are stored as 9
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_double_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double *box_shape);
+
+/**
+ * @brief High-level function for writing data of one frame to a data block.
+ * If the frame is at the beginning of a frame set the time stamp of the frame
+ * set is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param values is a 1D array of data to add. The array should be of length
+ * n_particles * n_values_per_frame if writing particle related data, otherwise
+ * it should be n_values_per_frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code values != 0 \endcode The pointer to the values array must not
+ * be a NULL pointer.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * N.b. Data is written a whole block at a time. The data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_with_time_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *values,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for writing data of one frame to a double precision
+ * data block. If the frame is at the beginning of a frame set the time stamp of
+ * the frame set is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param values is a 1D array of data to add. The array should be of length
+ * n_particles * n_values_per_frame if writing particle related data, otherwise
+ * it should be n_values_per_frame.
+ * @param n_values_per_frame is the number of values to store per frame. If the
+ * data is particle dependent there will be n_values_per_frame stored per
+ * particle each frame.
+ * @param block_id is the ID of the block, of which to set the output interval.
+ * @param block_name is a string that will be used as name of the block. Only
+ * required if the block did not exist, i.e. a new block is created.
+ * @param particle_dependency should be TNG_NON_PARTICLE_BLOCK_DATA (0) if the
+ * data is not related to specific particles (e.g. box shape) or
+ * TNG_PARTICLE_BLOCK_DATA (1) is it is related to specific particles (e.g.
+ * positions). Only required if the block did not exist, i.e. a new block is
+ * created.
+ * @param compression is the compression routine to use when writing the data.
+ * Only required if the block did not exist, i.e. a new block is created.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code values != 0 \endcode The pointer to the values array must not
+ * be a NULL pointer.
+ * @details n_values_per_frame, block_name, particle_dependency and
+ * compression are only used if the data block did not exist before calling
+ * this function, in which case it is created.
+ * N.b. Data is written a whole block at a time. The data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_generic_with_time_double_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *values,
+                 const int64_t n_values_per_frame,
+                 const int64_t block_id,
+                 const char *block_name,
+                 const char particle_dependency,
+                 const char compression);
+
+/**
+ * @brief High-level function for adding data to positions data blocks. If the
+ * frame is at the beginning of a frame set the time stamp of the frame set
+ * is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param positions is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code positions != 0 \endcode The pointer to the positions array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_write() and will
+ * create a positions data block if none exists. Positions are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_with_time_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *positions);
+
+/**
+ * @brief High-level function for adding data to positions data blocks at double
+ * precision. If the frame is at the beginning of a frame set the time stamp of
+ * the frame set is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param positions is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code positions != 0 \endcode The pointer to the positions array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_double_write() and will
+ * create a positions data block if none exists. Positions are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_pos_with_time_double_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *positions);
+
+/**
+ * @brief High-level function for adding data to velocities data blocks. If the
+ * frame is at the beginning of a frame set the time stamp of the frame set
+ * is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param velocities is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code velocities != 0 \endcode The pointer to the velocities array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_write() and will
+ * create a velocities data block if none exists. Velocities are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_with_time_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *velocities);
+
+/**
+ * @brief High-level function for adding data to velocities data blocks at
+ * double precision. If the frame is at the beginning of a frame set the
+ * time stamp of the frame set is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param velocities is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code velocities != 0 \endcode The pointer to the velocities array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_double_write() and will
+ * create a velocities data block if none exists. Velocities are stored as three
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_vel_with_time_double_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *velocities);
+
+/**
+ * @brief High-level function for adding data to forces data blocks. If the
+ * frame is at the beginning of a frame set the time stamp of the frame set
+ * is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param forces is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code forces != 0 \endcode The pointer to the forces array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_write() and will
+ * create a forces data block if none exists. Forces are stored as three
+ * values per frame and compressed using gzip compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_with_time_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *forces);
+
+/**
+ * @brief High-level function for adding data to forces data blocks at
+ * double precision. If the frame is at the beginning of a frame set
+ * the time stamp of the frame set is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param forces is a 1D array of data to add. The array should be of length
+ * n_particles * 3.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code forces != 0 \endcode The pointer to the forces array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_double_write() and will
+ * create a forces data block if none exists. Forces are stored as three
+ * values per frame and compressed using gzip compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_force_with_time_double_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *forces);
+
+/**
+ * @brief High-level function for adding data to box shape data blocks. If the
+ * frame is at the beginning of a frame set the time stamp of the frame set
+ * is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param box_shape is a 1D array of data to add. The array should be of length 9.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code box_shape != 0 \endcode The pointer to the box_shape array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_write() and will
+ * create a box shape data block if none exists. Box shapes are stored as 9
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_with_time_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const float *box_shape);
+
+/**
+ * @brief High-level function for adding data to box shape data blocks at
+ * double precision. If the frame is at the beginning of a frame set the
+ * time stamp of the frame set is set.
+ * @param tng_data is the trajectory to use.
+ * @param frame_nr is the frame number of the data.
+ * @param time is the time stamp of the frame (in seconds).
+ * @param box_shape is a 1D array of data to add. The array should be of length 9.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code frame_nr >= 0 \endcode The frame number to write must be >= 0.
+ * @pre \code time >= 0 \endcode The time stamp must be >= 0.
+ * @pre \code box_shape != 0 \endcode The pointer to the box_shape array must not
+ * be a NULL pointer.
+ * @details This function uses tng_util_generic_with_time_double_write() and will
+ * create a box shape data block if none exists. Box shapes are stored as 9
+ * values per frame and compressed using TNG compression.
+ * N.b. Since compressed data is written a whole block at a time the data is not
+ * actually written to disk until the frame set is finished or the TNG
+ * trajectory is closed.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_box_shape_with_time_double_write
+                (const tng_trajectory_t tng_data,
+                 const int64_t frame_nr,
+                 const double time,
+                 const double *box_shape);
+
+/**
+ * @brief High-level function for getting the compression method and
+ * multiplication factor of the last read frame of a specific data block.
+ * @param tng_data is the trajectory to use.
+ * @param block_id is the ID number of the block containing the data of
+ * interest.
+ * @param codec_id will be set to the value of the codec_id of the
+ * compression of the data block. See tng_compression for more details.
+ * @param factor will be set to the multiplication factor applied to
+ * the values before compression, in order to get integers from them.
+ * factor is 1/precision.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code codec_id != 0 \endcode  The pointer to the returned codec id
+ * must not be a NULL pointer.
+ * @pre \code factor != 0 \endcode The pointer to the returned multiplication
+ * factor must not be a NULL pointer.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as invalid mode) or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_frame_current_compression_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 int64_t *codec_id,
+                 double  *factor);
+
+/** @brief High-level function for determining the next frame with data and what
+ * data blocks have data for that frame. The search can be limited to certain
+ * data blocks.
+ * @param tng_data is the trajectory to use.
+ * @param current_frame is the frame that was last read, from where to start
+ * looking for data.
+ * @param n_requested_data_block_ids is the number of data blocks listed in
+ * requested_data_block_ids. If this is 0 all data blocks will be taken into
+ * account.
+ * @param requested_data_block_ids is an array of data blocks to look for.
+ * @param next_frame will be set to the next frame with data.
+ * @param n_data_blocks_in_next_frame is set to the number of data blocks with
+ * data for next_frame.
+ * @param data_block_ids_in_next_frame is set to an array (of length
+ * n_data_blocks_in_next_frame) that lists the data block IDs with data for
+ * next_frame. It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is reallocated by this function using realloc().
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code next_frame != 0 \endcode The pointer to the next frame must not
+ * be NULL.
+ * @pre \code n_data_blocks_in_next_frame != 0 \endcode The pointer to
+ * n_data_blocks_in_next_frame must not be NULL.
+ * @pre \code *data_block_ids_in_next_frame != 0 \endcode The pointer to the
+ * list of data block IDs must not be NULL.
+ * @pre \code n_requested_data_block_ids == 0 || requested_data_block_ids != 0 \endcode
+ * If the number of requested data blocks != 0 then the array of data block IDs must not be NULL.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_next_frame_present_data_blocks_find
+                (const tng_trajectory_t tng_data,
+                 int64_t current_frame,
+                 const int64_t n_requested_data_block_ids,
+                 const int64_t *requested_data_block_ids,
+                 int64_t *next_frame,
+                 int64_t *n_data_blocks_in_next_frame,
+                 int64_t **data_block_ids_in_next_frame);
+
+/* @brief High-level function for getting all data block ids and their names
+ * and stride lengths.
+ * @param tng_data is the trajectory to use.
+ * @param n_data_blocks is set to the number of data blocks in the trajectory.
+ * @param data_block_ids is set to an array (of length
+ * n_data_blocks) that lists the data block IDs in the trajectory.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @param data_block_names is set to an array (of length
+ * n_data_blocks) that contains the names of the data blocks.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @param stride_lengths is set to an array (of length
+ * n_data_blocks) that lists the stride lengths of the data blocks.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @param n_values_per_frame is set to an array (of length
+ * n_data_blocks) that lists the number of values per frame of the data blocks.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @param block_types is set to an array (of length
+ * n_data_blocks) that lists the block types of the data blocks.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @param dependencies is set to an array (of length
+ * n_data_blocks) that lists the dependencies of the data blocks.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @param compressions is set to an array (of length
+ * n_data_blocks) that lists the compressions of the data blocks.
+ * It must be pointing at NULL or previously allocated memory.
+ * Memory for the array is allocated by this function.
+ * The memory must be freed by the client afterwards or
+ * there will be a memory leak.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code n_data_blocks != 0 \endcode The pointer to
+ * n_data_blocks must not be NULL.
+ * @pre \code data_block_ids != 0 \endcode The pointer to the
+ * list of data block IDs must not be NULL.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured or TNG_CRITICAL (2) if a major error
+ * has occured.
+ */
+/*
+tng_function_status DECLSPECDLLEXPORT tng_util_trajectory_all_data_block_types_get
+                (const tng_trajectory_t tng_data,
+                 int64_t *n_data_blocks,
+                 int64_t **data_block_ids,
+                 char ***data_block_names,
+                 int64_t **stride_lengths,
+                 int64_t **n_values_per_frame,
+                 char **block_types,
+                 char **dependencies,
+                 char **compressions);
+*/
+
+/** @brief Finds the frame set of the specified frame in order to prepare for writing
+ * after it.
+ * @param tng_data is the trajectory to use.
+ * @param prev_frame is the frame after which to start appending.
+ * @pre \code tng_data != 0 \endcode The trajectory container (tng_data)
+ * must be initialised before using it.
+ * @pre \code prev_frame >= 0 \endcode The previous frame must not be negative.
+ * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+ * has occured (such as not finding the requested frame) or TNG_CRITICAL (2)
+ * if a major error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_prepare_append_after_frame
+                (const tng_trajectory_t tng_data,
+                 const int64_t prev_frame);
+
+
+/** @brief Get the number of frames containing data of a specific type.
+ * @param tng_data is the trajectory to use.
+ * @param block_id is the id of the block of the data type.
+ * @param n_frames is set to the number of frames containing data of
+ * the requested data type.
+ * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+ * error has occured.
+ */
+tng_function_status DECLSPECDLLEXPORT tng_util_num_frames_with_data_of_block_id_get
+                (const tng_trajectory_t tng_data,
+                 const int64_t block_id,
+                 int64_t *n_frames);
+/** @} */ /* end of group2 */
+
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* TNG_IO_H */
diff --git a/src/include/gromacs/external/tng_io/include/tng/tng_io.hpp b/src/include/gromacs/external/tng_io/include/tng/tng_io.hpp
new file mode 100644 (file)
index 0000000..12172e5
--- /dev/null
@@ -0,0 +1,1703 @@
+/* This code is part of the tng binary trajectory format.
+ *
+ * Written by Anders Gärdenäs
+ * Copyright (c) 2012-2013, The GROMACS development team.
+ * Check out http://www.gromacs.org for more information.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifndef TNG_IO_HPP
+#define TNG_IO_HPP
+
+#include "tng_io.h"
+
+namespace Tng
+{
+class Trajectory;
+class Atom;
+class Residue;
+class Chain;
+class Molecule;
+typedef class Molecule * Molecule_t;
+
+
+class Trajectory {
+private:
+    tng_trajectory_t traj;
+    tng_function_status status;
+public:
+    /**
+    * @brief Add a molecule to the trajectory.
+    * @param name is a pointer to the string containing the name of the new molecule.
+    * @param molecule is a pointer to the newly created molecule.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+
+    tng_function_status addMolecule(const char *, Molecule_t);
+    tng_function_status addMoleculeWithId(const char *, int64_t id, Molecule_t);
+    tng_function_status findMolecule(const char *name, int64_t id, Molecule_t molecule);
+    friend class Atom;
+    friend class Residue;
+    friend class Chain;
+    friend class Molecule;
+
+    //! Normal constructor
+    Trajectory()
+    { status = tng_trajectory_init(&traj); }
+
+    //! Copy constructor
+    Trajectory(Trajectory * src)
+    { status = tng_trajectory_init_from_src(traj,&src->traj); }
+
+    //! Detructor
+    ~Trajectory()
+    { status = tng_trajectory_destroy(&traj); }
+
+    //! Status
+    tng_function_status getStatus()
+    { return status; }
+
+
+    /**
+    * @brief Get the name of the input file.
+    * @param file_name the string to fill with the name of the input file,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for file_name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getInputFile (char *file_name, const int max_len)
+    {
+        return status = tng_input_file_get(traj, file_name,   max_len);
+    }
+
+    /**
+    * @brief Set the name of the input file.
+    * @param file_name the name of the input file.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setInputFile(const char *file_name)
+    {
+        return status = tng_input_file_set(traj, file_name);
+    }
+
+
+    /**
+    * @brief Get the name of the output file.
+    * @param file_name the string to fill with the name of the output file,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for file_name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getOutputFile(char *file_name, const int max_len)
+    {
+        return status = tng_output_file_get(traj, file_name, max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the output file.
+    * @param file_name the name of the output file.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setOutputFile(const char *file_name)
+    {
+        return status = tng_output_file_set(traj, file_name);
+    }
+
+    /**
+    * @brief Get the endianness of the output file.
+    * current output file.
+    * @param endianness will contain the enumeration of the endianness.
+    * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if the endianness
+    * could not be retrieved.
+    */
+    tng_function_status getOutputFileEndianness
+                    (tng_file_endianness *endianness)
+    {
+        return status = tng_output_file_endianness_get(traj, endianness);
+    }
+
+    /**
+    * @brief Set the endianness of the output file.
+    * @param endianness the enumeration of the endianness, can be either
+    * TNG_BIG_ENDIAN (0) or TNG_LITTLE_ENDIAN (1).
+    * @details The endianness cannot be changed after file output has started.
+    * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (1) if the endianness
+    * could not be set.
+    */
+    tng_function_status setOutputFileEndianness
+                    (const tng_file_endianness endianness)
+    {
+        return status = tng_output_file_endianness_set(traj, endianness);
+    }
+
+    /**
+    * @brief Get the name of the program used when creating the trajectory.
+    * @param name the string to fill with the name of the program,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getFirstProgramName(char *name, const int max_len)
+    {
+        return status = tng_first_program_name_get(traj,name,max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the program used when creating the trajectory..
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setFirstProgramName(const char *new_name)
+    {
+        return status = tng_first_program_name_set(traj, new_name);
+    }
+
+
+    /**
+    * @brief Get the name of the program used when last modifying the trajectory.
+    * @param name the string to fill with the name of the program,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getLastProgramName(char *name, const int max_len)
+    {
+        return status = tng_last_program_name_get(traj, name, max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the program used when last modifying the trajectory.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setLastProgramName(const char *new_name)
+    {
+        return status = tng_last_program_name_set(traj, new_name);
+    }
+
+
+    /**
+    * @brief Get the name of the user who created the trajectory.
+    * @param name the string to fill with the name of the user,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getFirstUserName(char *name, const int max_len)
+    {
+        return status = tng_first_user_name_get(traj,name, max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the user who created the trajectory.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setFirstUserName(const char *new_name)
+    {
+        return status = tng_first_user_name_set(traj, new_name);
+    }
+
+
+    /**
+    * @brief Get the name of the user who last modified the trajectory.
+    * @param name the string to fill with the name of the user,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getLastUserName(char *name, const int max_len)
+    {
+        return status = tng_last_user_name_get(traj,name,max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the user who last modified the trajectory.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setLastUserName(const char *new_name)
+    {
+        return status = tng_last_user_name_set(traj,new_name);
+    }
+
+
+
+    /**
+    * @brief Get the name of the computer used when creating the trajectory.
+    * @param name the string to fill with the name of the computer,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getFirstComputerName(char *name, const int max_len)
+    {
+        return status = tng_first_computer_name_get(traj, name, max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the computer used when creating the trajectory.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setFirstComputerName(const char *new_name)
+    {
+        return status = tng_first_computer_name_set(traj, new_name);
+    }
+
+
+    /**
+    * @brief Get the name of the computer used when last modifying the trajectory.
+    * @param name the string to fill with the name of the computer,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getLastComputerName(char *name, const int max_len)
+    {
+        return status = tng_last_computer_name_get(traj,name,max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the computer used when last modifying the trajectory.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setLastComputerName(const char *new_name)
+    {
+        return status = tng_last_computer_name_set(traj,new_name);
+    }
+
+
+    /**
+    * @brief Get the pgp_signature of the user creating the trajectory.
+    * @param signature the string to fill with the signature,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getFirstSignature(char *signature, const int max_len)
+    {
+        return status = tng_last_computer_name_get(traj, signature,max_len);
+    }
+
+
+    /**
+    * @brief Set the pgp_signature of the user creating the trajectory.
+    * @param signature is a string containing the pgp_signature.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setFirstSignature(const char *signature)
+    {
+        return status = tng_first_signature_set(traj, signature);
+    }
+
+
+    /**
+    * @brief Get the pgp_signature of the user last modifying the trajectory.
+    * @param signature the string to fill with the signature,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getLastSignature(char *signature, const int max_len)
+    {
+        return status = tng_first_signature_get(traj, signature, max_len);
+    }
+
+
+    /**
+    * @brief Set the pgp_signature of the user last modifying the trajectory.
+    * @param signature is a string containing the pgp_signature.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setLastSignature(const char *signature)
+    {
+        return status = tng_last_signature_set(traj, signature);
+    }
+
+
+    /**
+    * @brief Get the name of the forcefield used in the trajectory.
+    * @param name the string to fill with the name of the forcefield,
+    * memory must be allocated before.
+    * @param max_len maximum char length of the string, i.e. how much memory has
+    * been reserved for name. This includes \0 terminating character.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (source string longer than destination string).
+    */
+    tng_function_status getForcefieldName(char *name, const int max_len)
+    {
+        return status = tng_last_signature_get(traj,name,max_len);
+    }
+
+
+    /**
+    * @brief Set the name of the forcefield used in the trajectory.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setForcefieldName(const char *new_name)
+    {
+        return status = tng_forcefield_name_set(traj, new_name);
+    }
+
+
+    /**
+    * @brief Get the medium stride length of the trajectory.
+    * @param len is pointing to a value set to the stride length.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getMediumStrideLength(int64_t *len)
+    {
+        return status = tng_medium_stride_length_get(traj,len);
+    }
+
+
+    /**
+    * @brief Set the medium stride length of the trajectory.
+    * @param len is the wanted medium stride length.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred.
+    */
+    tng_function_status setMediumStrideLength(const int64_t len)
+    {
+        return status = tng_medium_stride_length_set(traj,len);
+    }
+
+
+    /**
+    * @brief Get the long stride length of the trajectory.
+    * @param len is pointing to a value set to the stride length.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getLongStrideLength(int64_t *len)
+    {
+        return status = tng_long_stride_length_get(traj, len);
+    }
+
+
+    /**
+    * @brief Set the long stride length of the trajectory.
+    * @param len is the wanted long stride length.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred.
+    */
+    tng_function_status setLongStrideLength(const int64_t len)
+    {
+        return status = tng_long_stride_length_set(traj,len);
+    }
+
+
+    /**
+    * @brief Get the current time per frame of the trajectory.
+    * @param len is pointing to a value set to the time per frame.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getTimePerFrame(double *time)
+    {
+        return status = tng_time_per_frame_get(traj, time);
+    }
+
+
+    /**
+    * @brief Set the time per frame of the trajectory.
+    * @param len is the new time per frame.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred.
+    */
+    tng_function_status setTimePerFrame(const double time)
+    {
+        return status = tng_time_per_frame_set(traj, time);
+    }
+
+
+    /**
+    * @brief Get the length of the input file.
+    * @param len is pointing to a value set to the file length.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getInputFileLen(int64_t *len)
+    {
+        return status = tng_input_file_len_get(traj, len);
+    }
+
+
+    /**
+    * @brief Get the number of frames in the trajectory
+    * @param n is pointing to a value set to the number of frames.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred (could not find last frame set).
+    */
+    tng_function_status getNumFrames(int64_t *n)
+    {
+        return status = tng_num_frames_get(traj, n);
+    }
+
+    /**
+    * @brief Get the current number of particles.
+    * @param n is pointing to a value set to the number of particles.
+    * @details If variable number of particles are used this function will return
+    * the number of particles in the current frame set.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getNumParticles(int64_t *n)
+    {
+        return status = tng_num_particles_get(traj, n);
+    }
+
+
+
+
+    /**
+    * @brief Get the current total number of molecules.
+    * @param n is pointing to a value set to the number of molecules.
+    * @details If variable number of particles are used this function will return
+    * the total number of molecules in the current frame set.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getNumMolecules(int64_t *n)
+    {
+        return status = tng_num_molecules_get(traj,n);
+    }
+
+    /**
+    * @brief Get the exponential used for distances in the trajectory.
+    * @param exp is pointing to a value set to the distance unit exponential.
+    * @details Example: If the distances are specified in nm (default) exp is -9.
+    * If the distances are specified in Å exp is -10.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getDistanceUnitExponential
+                    (int64_t *exp)
+    {
+        return status = tng_distance_unit_exponential_get(traj, exp);
+    }
+
+    /**
+    * @brief Set the exponential used for distances in the trajectory.
+    * @param exp is the distance unit exponential to use.
+    * @details Example: If the distances are specified in nm (default) exp is -9.
+    * If the distances are specified in Å exp is -10.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status setDistanceUnitExponential
+                    (int64_t exp)
+    {
+        return status = tng_distance_unit_exponential_set(traj, exp);
+    }
+
+
+    /**
+    * @brief Get the number of frames per frame set.
+    * per frame set.
+    * @param n is pointing to a value set to the number of frames per frame set.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getNumFramesPerFrameSet(int64_t *n)
+    {
+        return status = tng_num_frames_per_frame_set_get(traj,n);
+    }
+
+    /**
+    * @brief Set the number of frames per frame set.
+    * @param n is the number of frames per frame set.
+    * @details This does not affect already existing frame sets. For
+    * consistency the number of frames per frame set should be set
+    * betfore creating any frame sets.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status setNumFramesPerFrameSet(const int64_t n)
+    {
+        return status = tng_num_frames_per_frame_set_set(traj,n);
+    }
+
+    /**
+    * @brief Get the number of frame sets.
+    * @param n is pointing to a value set to the number of frame sets.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getNumFrameSets(int64_t *n)
+    {
+        return status = tng_num_frame_sets_get(traj, n);
+    }
+
+
+    /**
+    * @brief Get the current trajectory frame set.
+    * @param frame_set_p will be set to point at the memory position of
+    * the found frame set.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getCurrentFrameSet(tng_trajectory_frame_set_t *frame_set_p)
+    {
+        return status = tng_current_frame_set_get(traj, frame_set_p);
+    }
+
+
+    /**
+    * @brief Find the requested frame set number.
+    * @param nr is the frame set number to search for.
+    * @details tng_data->current_trajectory_frame_set will contain the
+    * found trajectory if successful.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status findFrameSetNr(const int64_t nr)
+    {
+        return status = tng_frame_set_nr_find(traj,nr);
+    }
+
+
+    /**
+    * @brief Find the frame set containing a specific frame.
+    * @param frame is the frame number to search for.
+    * @details tng_data->current_trajectory_frame_set will contain the
+    * found trajectory if successful.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status findFrameSetOfFrame(const int64_t frame)
+    {
+        return status = tng_frame_set_of_frame_find(traj, frame);
+    }
+
+
+    /**
+    * @brief Get the file position of the next frame set in the input file.
+    * @param frame_set is the frame set of which to get the position of the
+    * following frame set.
+    * @param pos is pointing to a value set to the file position.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getNextFrameSetFilePos
+        (const tng_trajectory_frame_set_t frame_set,int64_t *pos)
+    {
+        return status = tng_frame_set_next_frame_set_file_pos_get(traj,frame_set,pos );
+    }
+
+    /**
+    * @brief Get the file position of the previous frame set in the input file.
+    * @param frame_set is the frame set of which to get the position of the
+    * previous frame set.
+    * @param pos is pointing to a value set to the file position.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getPrevFrameSetFilePos
+        (const tng_trajectory_frame_set_t frame_set,int64_t *pos)
+    {
+        return status = tng_frame_set_prev_frame_set_file_pos_get(traj, frame_set, pos);
+    }
+
+
+    /**
+    * @brief Get the first and last frames of the frame set.
+    * @param frame_set is the frame set of which to get the frame range.
+    * @param first_frame is set to the first frame of the frame set.
+    * @param last_frame is set to the last frame of the frame set.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getFrameSetFrameRange
+        (const tng_trajectory_frame_set_t frame_set,
+        int64_t *first_frame,
+        int64_t *last_frame)
+    {
+        return status = tng_frame_set_frame_range_get(traj,frame_set, first_frame, last_frame);
+    }
+
+
+    /**
+    * @brief Get the molecume name of real particle number (number in mol system).
+    * @param nr is the real number of the particle in the molecular system.
+    * @param name is a string, which is set to the name of the molecule. Memory
+    * must be reserved beforehand.
+    * @param max_len is the maximum length of name.
+    * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+    * has occured.
+    */
+    tng_function_status getMoleculeNameOfParticleNr
+        (const int64_t nr,char *name,int max_len)
+    {
+        return status = tng_molecule_name_of_particle_nr_get(traj,nr,name,max_len);
+    }
+
+
+    /**
+    * @brief Get the chain name of real particle number (number in mol system).
+    * @param nr is the real number of the particle in the molecular system.
+    * @param name is a string, which is set to the name of the chain. Memory
+    * must be reserved beforehand.
+    * @param max_len is the maximum length of name.
+    * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+    * has occured.
+    */
+    tng_function_status getChainNameOfParticleNr
+        (const int64_t nr,char *name,int max_len)
+    {
+        return status = tng_chain_name_of_particle_nr_get(traj, nr, name, max_len);
+    }
+
+
+    /**
+    * @brief Get the residue name of real particle number (number in mol system).
+    * @param nr is the real number of the particle in the molecular system.
+    * @param name is a string, which is set to the name of the residue. Memory
+    * must be reserved beforehand.
+    * @param max_len is the maximum length of name.
+    * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+    * has occured.
+    */
+    tng_function_status getResidueNameOfParticleNr
+        (const int64_t nr,char *name,int max_len)
+    {
+        return status = tng_residue_name_of_particle_nr_get(traj,nr,name,max_len);
+    }
+
+
+    /**
+    * @brief Get the atom name of real particle number (number in mol system).
+    * @param nr is the real number of the particle in the molecular system.
+    * @param name is a string, which is set to the name of the atom. Memory
+    * must be reserved beforehand.
+    * @param max_len is the maximum length of name.
+    * @return TNG_SUCCESS (0) if successful or TNG_FAILURE (!) if a minor error
+    * has occured.
+    */
+    tng_function_status getAtomNameOfParticleNr
+        (const int64_t nr,char *name,int max_len)
+    {
+        return status = tng_atom_name_of_particle_nr_get(traj, nr,name,max_len);
+    }
+
+
+    /**
+    * @brief Add a particle mapping table.
+    * @details Each particle mapping table will be written as a separate block,
+    * followed by the data blocks for the corresponding particles. In most cases
+    * there is one particle mapping block for each thread writing the trajectory.
+    * @details The mapping information is added to the currently active frame set
+    * of tng_data
+    * @param first_particle_number is the first particle number of this mapping
+    * block.
+    * @param n_particles is the number of particles in this mapping block.
+    * @param mapping_table is a list of the real particle numbers (i.e. the numbers
+    * used in the molecular system). The list is n_particles long.
+    * @details mapping_table[0] is the real particle number of the first particle
+    * in the following data blocks.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status addParticleMapping
+        (const int64_t first_particle_number,
+        const int64_t n_particles,
+        const int64_t *mapping_table)
+    {
+        return status = tng_particle_mapping_add(traj,first_particle_number,n_particles,mapping_table );
+    }
+
+
+    /**
+    * @brief Read the header blocks from the input_file of tng_data.
+    * @details The trajectory blocks must be read separately and iteratively in chunks
+    * to fit in memory.
+    * @details tng_data->input_file_path specifies
+    * which file to read from. If the file (input_file) is not open it will be
+    * opened.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status readFileHeaders(const tng_hash_mode hash_mode)
+    {
+        return status = tng_file_headers_read(traj, hash_mode);
+    }
+
+
+    /**
+    * @brief Write the header blocks to the output_file of tng_data.
+    * @details The trajectory blocks must be written separately and iteratively in chunks
+    * to fit in memory.
+    * @details tng_data->output_file_path
+    * specifies which file to write to. If the file (output_file) is not open it
+    * will be opened.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH an md5 hash for each header block will be generated.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status writeFileHeaders(const tng_hash_mode hash_mode)
+    {
+        return status = tng_file_headers_write(traj, hash_mode);
+    }
+
+
+
+    /**
+    * @brief Read one (the next) block (of any kind) from the input_file of tng_data.
+    * which file to read from. If the file (input_file) is not open it will be
+    * opened.
+    * @param block_data is a pointer to the struct which will be populated with the
+    * data.
+    * @details If block_data->input_file_pos > 0 it is the position from where the
+    * reading starts otherwise it starts from the current position.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status readNextBlock(const tng_hash_mode hash_mode, tng_gen_block_t block_data)
+    {
+        return status = tng_block_read_next(traj,block_data, hash_mode);
+    }
+
+
+
+    /**
+    * @brief Read one (the next) frame set, including mapping and related data blocks
+    * from the input_file of tng_data.
+    * which file to read from. If the file (input_file) is not open it will be
+    * opened.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status readNextFrameSet(const tng_hash_mode hash_mode)
+    {
+        return status = tng_frame_set_read_next(traj, hash_mode);
+    }
+
+
+    /**
+    * @brief Write one frame set, including mapping and related data blocks
+    * to the output_file of tng_data.
+    * @details  tng_data->output_file_path specifies
+    * which file to write to. If the file (output_file) is not open it will be
+    * opened.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH an md5 hash for each header block will be generated.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status writeFrameSet(const tng_hash_mode hash_mode)
+    {
+        return status = tng_frame_set_write(traj, hash_mode);
+    }
+
+
+    /**
+    * @brief Create and initialise a frame set.
+    * @param first_frame is the first frame of the frame set.
+    * @param n_frames is the number of frames in the frame set.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status newFrameSet(const int64_t first_frame,
+        const int64_t n_frames)
+    {
+        return status =  tng_frame_set_new(traj, first_frame, n_frames);
+    }
+
+
+    /**
+    * @brief Create and initialise a frame set with the time of the first frame
+    * specified.
+    * @param first_frame is the first frame of the frame set.
+    * @param n_frames is the number of frames in the frame set.
+    * @param first_frame_time is the time stamp of the first frame (in seconds).
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status newFrameSetWithTime
+                (const int64_t first_frame,
+                 const int64_t n_frames,
+                 const double first_frame_time)
+    {
+        return status =  tng_frame_set_with_time_new(traj,
+                                                     first_frame, n_frames,
+                                                     first_frame_time);
+    }
+
+
+    /**
+    * @brief Set the time stamp of the first frame of the current frame set.
+    * @param first_frame_time is the time stamp of the first frame in the
+    * frame set.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status setTimeOfFirstFrameOfFrameSet
+                    (const double first_frame_time)
+    {
+        return status = tng_frame_set_first_frame_time_set(traj,
+                                                           first_frame_time);
+    }
+
+    /**
+    * @brief Add a non-particle dependent data block.
+    * @param id is the block ID of the block to add.
+    * @param block_name is a descriptive name of the block to add
+    * @param datatype is the datatype of the data in the block (e.g. int/float)
+    * @param block_type_flag indicates if this is a non-trajectory block (added
+    * directly to tng_data) or if it is a trajectory block (added to the
+    * frame set)
+    * @param n_frames is the number of frames of the data block (automatically
+    * set to 1 if adding a non-trajectory data block)
+    * @param n_values_per_frame is how many values a stored each frame (e.g. 9
+    * for a box shape block)
+    * @param stride_length is how many frames are between each entry in the
+    * data block
+    * @param codec_id is the ID of the codec to compress the data.
+    * @param new_data is an array of data values to add.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status addDataBlock(const int64_t id,
+        const char *block_name,
+        const tng_data_type datatype,
+        const tng_block_type block_type_flag,
+        int64_t n_frames,
+        const int64_t n_values_per_frame,
+        const int64_t stride_length,
+        const int64_t codec_id,
+        void *new_data)
+    {
+        return status = tng_data_block_add(traj, id,block_name,
+            datatype,block_type_flag, n_frames,
+            n_values_per_frame, stride_length,
+            codec_id, new_data);
+    }
+
+
+    /**
+    * @brief Add a particle dependent data block.
+    * @param id is the block ID of the block to add.
+    * @param block_name is a descriptive name of the block to add
+    * @param datatype is the datatype of the data in the block (e.g. int/float)
+    * @param block_type_flag indicates if this is a non-trajectory block (added
+    * directly to tng_data) or if it is a trajectory block (added to the
+    * frame set)
+    * @param n_frames is the number of frames of the data block (automatically
+    * set to 1 if adding a non-trajectory data block)
+    * @param n_values_per_frame is how many values a stored each frame (e.g. 9
+    * for a box shape block)
+    * @param stride_length is how many frames are between each entry in the
+    * data block
+    * @param first_particle_number is the number of the first particle stored
+    * in this data block
+    * @param n_particles is the number of particles stored in this data block
+    * @param codec_id is the ID of the codec to compress the data.
+    * @param new_data is an array of data values to add.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status addParticleDataBlock(const int64_t id,
+        const char *block_name,
+        const tng_data_type datatype,
+        const tng_block_type block_type_flag,
+        int64_t n_frames,
+        const int64_t n_values_per_frame,
+        const int64_t stride_length,
+        const int64_t first_particle_number,
+        const int64_t n_particles,
+        const int64_t codec_id,
+        void *new_data)
+    {
+        return status = tng_particle_data_block_add(traj,id,    block_name,
+            datatype, block_type_flag, n_frames,n_values_per_frame,
+            stride_length,first_particle_number,n_particles,
+            codec_id, new_data);
+    }
+
+
+    /**
+    * @brief Write data of one trajectory frame to the output_file of tng_data.
+    * @param frame_nr is the index number of the frame to write.
+    * @param block_id is the ID of the data block to write the data to.
+    * @param data is an array of data to write. The length of the array should
+    * equal n_values_per_frame.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status writeFrameData(const int64_t frame_nr,
+        const int64_t block_id,
+        const void *data,
+        const tng_hash_mode hash_mode)
+    {
+        return status = tng_frame_data_write(traj,frame_nr,block_id,data,hash_mode);
+    }
+
+
+    /**
+    * @brief Write particle data of one trajectory frame to the output_file of
+    * tng_data.
+    * @param frame_nr is the index number of the frame to write.
+    * @param block_id is the ID of the data block to write the data to.
+    * @param val_first_particle is the number of the first particle in the data
+    * array.
+    * @param val_n_particles is the number of particles in the data array.
+    * @param data is a 1D-array of data to write. The length of the array should
+    * equal n_particles * n_values_per_frame.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the written md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status writeFrameParticleData(const int64_t frame_nr,
+        const int64_t block_id,
+        const int64_t val_first_particle,
+        const int64_t val_n_particles,
+        const void *data,
+        const tng_hash_mode hash_mode)
+    {
+        return status = tng_frame_particle_data_write(traj,frame_nr,block_id,val_first_particle,val_n_particles,data,hash_mode);
+    }
+
+
+    /**
+    * @brief Free data is an array of values (2D).
+    * @param values is the 2D array to free and will be set to 0 afterwards.
+    * @param n_frames is the number of frames in the data array.
+    * @param n_values_per_frame is the number of values per frame in the data array.
+    * @param type is the data type of the data in the array (e.g. int/float/char).
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status freeDataValues(union data_values **values,
+        const int64_t n_frames,
+        const int64_t n_values_per_frame,
+        const tng_data_type type)
+    {
+        return status = tng_data_values_free(traj, values, n_frames,n_values_per_frame,type);
+    }
+
+
+    /**
+    * @brief Free data is an array of values (3D).
+    * @param values is the array to free and will be set to 0 afterwards.
+    * @param n_frames is the number of frames in the data array.
+    * @param n_particles is the number of particles in the data array.
+    * @param n_values_per_frame is the number of values per frame in the data array.
+    * @param type is the data type of the data in the array (e.g. int/float/char).
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status freeParticleDataValues(union data_values ***values,
+        const int64_t n_frames,
+        const int64_t n_particles,
+        const int64_t n_values_per_frame,
+        const tng_data_type type)
+    {
+        return status = tng_particle_data_values_free(traj, values,n_frames,n_particles,n_values_per_frame,type);
+    }
+
+
+    /**
+    * @brief Retrieve non-particle data, from the last read frame set. Obsolete!
+    * which file to read from. If the file (input_file) is not open it will be
+    * opened.
+    * @param block_id is the id number of the particle data block to read.
+    * @param values is a pointer to a 2-dimensional array (memory unallocated), which
+    * will be filled with data. The array will be sized
+    * (n_frames * n_values_per_frame).
+    * Since ***values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param n_frames is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getData(const int64_t block_id,
+        union data_values ***values,
+        int64_t *n_frames,
+        int64_t *n_values_per_frame,
+        char *type)
+    {
+        return status = tng_data_get(traj, block_id, values, n_frames,
+                                     n_values_per_frame, type);
+    }
+
+    /**
+    * @brief Retrieve a vector (1D array) of non-particle data, from the last read frame set.
+    * @param block_id is the id number of the particle data block to read.
+    * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+    * will be filled with data. The length of the array will be (n_frames * n_values_per_frame).
+    * Since **values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param n_frames is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param stride_length is set to the stride length of the returned data.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @details This does only work for numerical (int, float, double) data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getDataVector
+                (const int64_t block_id,
+                 void **values,
+                 int64_t *n_frames,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type)
+    {
+        return status = tng_data_vector_get(traj, block_id, values, n_frames,
+                                            stride_length, n_values_per_frame,
+                                            type);
+    }
+
+    /**
+    * @brief Read and retrieve non-particle data, in a specific interval. Obsolete!
+    * which file to read from. If the file (input_file) is not open it will be
+    * opened.
+    * @param block_id is the id number of the particle data block to read.
+    * @param start_frame_nr is the index number of the first frame to read.
+    * @param end_frame_nr is the index number of the last frame to read.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @param values is a pointer to a 2-dimensional array (memory unallocated), which
+    * will be filled with data. The array will be sized
+    * (n_frames * n_values_per_frame).
+    * Since ***values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getDataInterval(const int64_t block_id,
+        const int64_t start_frame_nr,
+        const int64_t end_frame_nr,
+        const tng_hash_mode hash_mode,
+        union data_values ***values,
+        int64_t *n_values_per_frame,
+        char *type)
+    {
+        return status = tng_data_interval_get(traj, block_id, start_frame_nr,
+                                              end_frame_nr, hash_mode, values,
+                                              n_values_per_frame, type);
+    }
+
+
+    /**
+    * @brief Read and retrieve a vector (1D array) of non-particle data,
+    * in a specific interval.
+    * @param block_id is the id number of the particle data block to read.
+    * @param start_frame_nr is the index number of the first frame to read.
+    * @param end_frame_nr is the index number of the last frame to read.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+    * will be filled with data. The length of the array will be (n_frames * n_values_per_frame).
+    * Since **values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param stride_length is set to the stride length (writing frequency) of
+    * the data.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @details This does only work for numerical (int, float, double) data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getDataVectorInterval
+                (const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const char hash_mode,
+                 void **values,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type)
+    {
+        return status = tng_data_vector_interval_get(traj, block_id,
+                                                     start_frame_nr,
+                                                     end_frame_nr,
+                                                     hash_mode, values,
+                                                     stride_length,
+                                                     n_values_per_frame,
+                                                     type);
+    }
+
+    /**
+    * @brief Retrieve particle data, from the last read frame set. Obsolete!
+    * @details The particle dimension of the returned values array is translated
+    * to real particle numbering, i.e. the numbering of the actual molecular
+    * system.
+    * specifies which file to read from. If the file (input_file) is not open it
+    * will be opened.
+    * @param block_id is the id number of the particle data block to read.
+    * @param values is a pointer to a 3-dimensional array (memory unallocated), which
+    * will be filled with data. The array will be sized
+    * (n_frames * n_particles * n_values_per_frame).
+    * Since ****values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param n_frames is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param n_particles is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getParticleData(const int64_t block_id,
+        union data_values ****values,
+        int64_t *n_frames,
+        int64_t *n_particles,
+        int64_t *n_values_per_frame,
+        char *type)
+    {
+        return status = (tng_particle_data_get(traj, block_id, values, n_frames,
+            n_particles, n_values_per_frame, type));
+    }
+
+
+    /**
+    * @brief Retrieve a vector (1D array) of particle data, from the last read frame set.
+    * @details The particle dimension of the returned values array is translated
+    * to real particle numbering, i.e. the numbering of the actual molecular
+    * system.
+    * @param block_id is the id number of the particle data block to read.
+    * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+    * will be filled with data. The length of the array will be
+    * (n_frames * n_particles * n_values_per_frame).
+    * Since **values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param n_frames is set to the number of frames in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param stride_length is set to the stride length of the returned data.
+    * @param n_particles is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @details This does only work for numerical (int, float, double) data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getParticleDataVector
+                (const int64_t block_id,
+                 void **values,
+                 int64_t *n_frames,
+                 int64_t *stride_length,
+                 int64_t *n_particles,
+                 int64_t *n_values_per_frame,
+                 char *type)
+    {
+        return status = tng_particle_data_vector_get(traj, block_id,
+                                                     values, n_frames,
+                                                     stride_length,
+                                                     n_particles,
+                                                     n_values_per_frame, type);
+    }
+
+
+    /**
+    * @brief Read and retrieve particle data, in a specific interval. Obsolete!
+    * @details The particle dimension of the returned values array is translated
+    * to real particle numbering, i.e. the numbering of the actual molecular
+    * system.
+    * @param block_id is the id number of the particle data block to read.
+    * @param start_frame_nr is the index number of the first frame to read.
+    * @param end_frame_nr is the index number of the last frame to read.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @param values is a pointer to a 3-dimensional array (memory unallocated), which
+    * will be filled with data. The array will be sized
+    * (n_frames * n_particles * n_values_per_frame).
+    * Since ****values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param n_particles is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getParticleDataInterval(const int64_t block_id,
+        const int64_t start_frame_nr,
+        const int64_t end_frame_nr,
+        const tng_hash_mode hash_mode,
+        union data_values ****values,
+        int64_t *n_particles,
+        int64_t *n_values_per_frame,
+        char *type)
+    {
+        return status = (tng_particle_data_interval_get(traj, block_id, start_frame_nr,
+            end_frame_nr, hash_mode, values,
+            n_particles, n_values_per_frame,
+            type));
+    }
+
+
+    /**
+    * @brief Read and retrieve a vector (1D array) particle data, in a
+    * specific interval.
+    * @details The particle dimension of the returned values array is translated
+    * to real particle numbering, i.e. the numbering of the actual molecular
+    * system.
+    * @param block_id is the id number of the particle data block to read.
+    * @param start_frame_nr is the index number of the first frame to read.
+    * @param end_frame_nr is the index number of the last frame to read.
+    * @param hash_mode is an option to decide whether to use the md5 hash or not.
+    * If hash_mode == TNG_USE_HASH the md5 hash in the file will be
+    * compared to the md5 hash of the read contents to ensure valid data.
+    * @param values is a pointer to a 1-dimensional array (memory unallocated), which
+    * will be filled with data. The length of the array will be
+    * (n_frames * n_particles * n_values_per_frame).
+    * Since **values is allocated in this function it is the callers
+    * responsibility to free the memory.
+    * @param stride_length is set to the stride length (writing frequency) of
+    * the data.
+    * @param n_particles is set to the number of particles in the returned data. This is
+    * needed to properly reach and/or free the data afterwards.
+    * @param n_values_per_frame is set to the number of values per frame in the data.
+    * This is needed to properly reach and/or free the data afterwards.
+    * @param type is set to the data type of the data in the array.
+    * @details This does only work for numerical (int, float, double) data.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getParticleDataVectorInterval
+                (const int64_t block_id,
+                 const int64_t start_frame_nr,
+                 const int64_t end_frame_nr,
+                 const tng_hash_mode hash_mode,
+                 void **values,
+                 int64_t *n_particles,
+                 int64_t *stride_length,
+                 int64_t *n_values_per_frame,
+                 char *type)
+    {
+        return status = tng_particle_data_vector_interval_get(traj, block_id,
+                                                              start_frame_nr,
+                                                              end_frame_nr,
+                                                              hash_mode,
+                                                              values,
+                                                              n_particles,
+                                                              stride_length,
+                                                              n_values_per_frame,
+                                                              type);
+    }
+
+
+    /** @brief Get the date and time of initial file creation in ISO format (string).
+    *  @param time is a pointer to the string in which the date will be stored. Memory
+    must be reserved beforehand.
+    * @return TNG_SUCCESS (0) if successful.
+    */
+    tng_function_status getTimeStr(char *time)
+    {
+        return status = tng_time_get_str(traj, time);
+    }
+
+
+};
+
+
+
+
+
+class Molecule
+{
+private:
+
+    tng_molecule_t mol;
+    Trajectory * traj;
+    tng_function_status status;
+public:
+    tng_function_status addChain(const char *name, Chain *chain);
+    tng_function_status findChain(const char *name, int64_t id, Chain *chain);
+    friend class Trajectory;
+    //Constructor
+    Molecule(Trajectory * trajectory)
+    {
+        traj = trajectory;
+
+        //status = tng_molecule_init(traj->traj,mol);
+    }
+    /**
+     *@Dose nothing, use ~TngMolecule()
+    */
+    ~Molecule()
+    {
+        status = tng_molecule_destroy(traj->traj,mol);
+    }
+        //! Status
+    tng_function_status getStatus()
+    { return status; }
+
+
+    /**
+    * @brief Set the name of a molecule.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setName(const char *new_name)
+    {
+        return status = tng_molecule_name_set(traj->traj,mol,new_name);
+    }
+
+    /**
+    * @brief Get the count of a molecule.
+    * @param cnt is a pointer to the variable to be populated with the count.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status getCnt(int64_t *cnt)
+    {
+        return status = tng_molecule_cnt_get(traj->traj,mol,cnt);
+    }
+
+    /**
+    * @brief Set the count of a molecule.
+    * @param cnt is the number of instances of this molecule.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if a minor error
+    * has occurred or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status setCnt(int64_t cnt)
+    {
+        return status = tng_molecule_cnt_set(traj->traj,mol,cnt);
+    }
+
+};
+
+    tng_function_status Trajectory::addMolecule(const char *name, Molecule_t molecule)
+{
+    return status = tng_molecule_add(traj,name, &molecule->mol);
+}
+
+    tng_function_status Trajectory::addMoleculeWithId
+                (const char *name,
+                 const int64_t id,
+                 Molecule_t molecule)
+{
+    return status = tng_molecule_w_id_add(traj, name, id, &molecule->mol);
+}
+
+/**
+* @brief Find a molecule.
+* @param tng_data is the trajectory data container containing the molecule.
+* @param name is a string containing the name of the molecule. If name is empty
+* only id will be used for finding the molecule.
+* @param id is the id of the molecule to look for. If id is -1 only the name of
+* the molecule will be used for finding the molecule.
+* @param molecule is a pointer to the molecule if it was found - otherwise 0.
+* @return TNG_SUCCESS (0) if the molecule is found or TNG_FAILURE (1) if the
+* molecule is not found.
+* @details If name is an empty string and id is -1 the first molecule will be
+* found.
+*/
+tng_function_status Trajectory::findMolecule
+                (const char *name,
+                int64_t id,
+                Molecule_t molecule)
+{
+    return status = tng_molecule_find(traj, name, id,
+                            &molecule->mol);
+}
+
+
+class Atom
+{
+private:
+    tng_atom_t atom;
+    Trajectory * traj;
+    tng_function_status status;
+public:
+    friend class Residue;
+    //constructor
+    Atom(Trajectory * trajectory)
+    {
+        traj = trajectory;
+    }
+    //deonstructor
+    /**
+     *@Dose nothing, use ~TngMolecule()
+    */
+        ~Atom()
+    {
+        //delete atom;
+    }
+            //! Status
+    tng_function_status getStatus()
+    { return status; }
+    /**
+    * @brief Set the name of an atom.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setName(const char *new_name)
+    {
+        return status = tng_atom_name_set(traj->traj, atom , new_name);
+    }
+
+    /**
+    * @param new_type is a string containing the atom type.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setType(const char *new_type)
+    {
+        return status = tng_atom_type_set(traj->traj, atom, new_type);
+    }
+};
+
+class Residue
+{
+    private:
+    tng_residue_t residue;
+    Trajectory * traj;
+    tng_function_status status;
+public:
+    friend class Chain;
+    //constructor
+    Residue(Trajectory  * trajectory)
+    {
+        traj = trajectory;
+    }
+    //deonstructor
+    /**
+     *@Dose nothing, use ~TngMolecule()
+    */
+        ~Residue()
+    {
+        //delete residue;
+    }
+    //! Status
+    tng_function_status getStatus()
+    { return status; }
+    /**
+    * @brief Set the name of a residue.
+    * @param residue is the residue to rename.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setName(const char *new_name)
+    {
+        return status = tng_residue_name_set(traj->traj, residue,new_name);
+    }
+
+    /**
+    * @brief Add an atom to a residue.
+    * @param atom_name is a string containing the name of the atom.
+    * @param atom_type is a string containing the atom type of the atom.
+    * @param atom is a pointer to the newly created atom.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status addAtom(const char *atom_name,
+        const char *atom_type,
+        Atom * atom)
+    {
+        return status = tng_residue_atom_add(traj->traj, residue, atom_name,
+                                             atom_type, &atom->atom);
+    }
+
+
+    /**
+    * @brief Add an atom with a specific ID to a residue.
+    * @param atom_name is a string containing the name of the atom.
+    * @param atom_type is a string containing the atom type of the atom.
+    * @param id is the ID of the created atom.
+    * @param atom is a pointer to the newly created atom.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+    * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status addAtomWithId
+                (const char *atom_name,
+                 const char *atom_type,
+                 const int64_t id,
+                 Atom * atom)
+    {
+        return status = tng_residue_atom_w_id_add(traj->traj, residue,
+                                                  atom_name, atom_type,
+                                                  id, &atom->atom);
+    }
+
+};
+
+class Chain
+{
+    private:
+    tng_chain_t chain;
+    Trajectory * traj;
+    tng_function_status status;
+public:
+    friend class Molecule;
+    //constructor
+    Chain(Trajectory * trajectory)
+    {
+        traj = trajectory;
+    }
+    //deonstructor
+    /**
+     *@Dose nothing, use ~TngMolecule()
+    */
+        ~Chain()
+    {
+        //delete chain;
+    }
+    //! Status
+    tng_function_status getStatus()
+    { return status; }
+    /**
+    * @brief Set the name of a chain.
+    * @param new_name is a string containing the wanted name.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status setName(const char *new_name)
+    {
+        return status  = tng_chain_name_set(traj->traj, chain, new_name);
+    }
+
+
+    /**
+    * @brief Find a residue in a chain.
+    * @param name is a string containing the name of the residue.
+    * @param id is the id of the residue to find. If id == -1 the first residue
+    * that matches the specified name will be found.
+    * @param residue is a pointer to the residue if it was found - otherwise 0.
+    * @return TNG_SUCCESS (0) if the residue is found or TNG_FAILURE (1) if the
+    * residue is not found.
+    * @details If name is an empty string the first residue will be found.
+    */
+    tng_function_status findResidue
+                (const char *name,
+                 int64_t id,
+                 Residue *residue)
+    {
+        return status = tng_chain_residue_find(traj->traj, chain, name,
+                                               id, &residue->residue);
+    }
+
+    /**
+    * @brief Add a residue to a chain.
+    * @param name is a string containing the name of the residue.
+    * @param residue is a pointer to the newly created residue.
+    * @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+    * error has occured.
+    */
+    tng_function_status addResidue(const char *name,
+        Residue * residue)
+    {
+        return status = tng_chain_residue_add(traj->traj, chain,
+                                              name, &residue->residue);
+    }
+
+    /**
+    * @brief Add a residue with a specific ID to a chain.
+    * @param name is a string containing the name of the residue.
+    * @param id is the ID of the created residue.
+    * @param residue is a pointer to the newly created residue.
+    * @return TNG_SUCCESS (0) if successful, TNG_FAILURE (1) if the ID could
+    * not be set properly or TNG_CRITICAL (2) if a major error has occured.
+    */
+    tng_function_status addResidueWithId
+                (const char *name,
+                 const int64_t id,
+                 Residue * residue)
+    {
+        return status = tng_chain_residue_w_id_add(traj->traj, chain,
+                                                   name, id, &residue->residue);
+    }
+};
+
+/**
+* @brief Add a chain to a molecule.
+* @param name is a string containing the name of the chain.
+* @param chain s a pointer to the newly created chain.
+* @return TNG_SUCCESS (0) if successful or TNG_CRITICAL (2) if a major
+* error has occured.
+*/
+tng_function_status Molecule::addChain(const char *name, Chain *chain)
+{
+    return status = tng_molecule_chain_add(traj->traj,mol,name,&chain->chain);
+}
+
+/**
+* @brief Find a chain in a molecule.
+* @param name is a string containing the name of the chain. If name is empty
+* only id will be used for finding the chain.
+* @param id is the id of the chain to look for. If id is -1 only the name of
+* the chain will be used for finding the chain.
+* @param chain is a pointer to the chain if it was found - otherwise 0.
+* @return TNG_SUCCESS (0) if the chain is found or TNG_FAILURE (1) if the
+* chain is not found.
+* @details If name is an empty string and id is -1 the first chain will be
+* found.
+*/
+tng_function_status Molecule::findChain
+                (const char *name,
+                int64_t id,
+                Chain *chain)
+{
+    return status = tng_molecule_chain_find(traj->traj, mol, name, id,
+                                            &chain->chain);
+}
+
+}
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/tng/tng_io_fwd.h b/src/include/gromacs/external/tng_io/include/tng/tng_io_fwd.h
new file mode 100644 (file)
index 0000000..68bcafd
--- /dev/null
@@ -0,0 +1,37 @@
+/* This code is part of the tng binary trajectory format.
+ *
+ * Written by Magnus Lundborg
+ * Copyright (c) 2012-2013, The GROMACS development team.
+ * Check out http://www.gromacs.org for more information.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the Revised BSD License.
+ */
+
+#ifndef TNG_IO_FWD_H
+#define TNG_IO_FWD_H     1
+
+/** A pointer to the main trajectory data storage. */
+typedef struct tng_trajectory *tng_trajectory_t;
+/** A pointer to a molecule description. */
+typedef struct tng_molecule *tng_molecule_t;
+/** A pointer to a molecular chain description. */
+typedef struct tng_chain *tng_chain_t;
+/** A pointer to a molecular residue description. */
+typedef struct tng_residue *tng_residue_t;
+/** A pointer to a molecular atom description. */
+typedef struct tng_atom *tng_atom_t;
+/** A pointer to a bond between two atoms. */
+typedef struct tng_bond *tng_bond_t;
+/** A pointer to a structure containing data common to all trajectory blocks,
+ *  such as header and contents. */
+typedef struct tng_gen_block *tng_gen_block_t;
+/** A pointer to particle mapping information. */
+typedef struct tng_particle_mapping *tng_particle_mapping_t;
+/** A pointer to a structure containing frame set information. */
+typedef struct tng_trajectory_frame_set *tng_trajectory_frame_set_t;
+/** A pointer to a data container. */
+typedef struct tng_data *tng_data_t;
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/include/tng/version.h.in b/src/include/gromacs/external/tng_io/include/tng/version.h.in
new file mode 100644 (file)
index 0000000..662e68f
--- /dev/null
@@ -0,0 +1,16 @@
+#ifndef VERSION_CONFIG_H
+#define VERSION_CONFIG_H
+
+/* define the API version (integer) */
+#define TNG_API_VERSION @TNG_API_VERSION@
+
+/* define the major and minor versions
+   of the library */
+#define TNG_VERSION_MAJOR @TNG_MAJOR_VERSION@
+#define TNG_VERSION_MINOR @TNG_MINOR_VERSION@
+/* define the patchlevel of the library */
+#define TNG_VERSION_PATCHLEVEL @TNG_VERSION_PATCH_LEVEL@
+/* define the full version of the library (string) */
+#define TNG_VERSION "@TNG_IO_VERSION@"
+
+#endif
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test1.h b/src/include/gromacs/external/tng_io/src/tests/compression/test1.h
new file mode 100644 (file)
index 0000000..b45abc4
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Intra frame triple algorithm. Cubic cell."
+#define FILENAME "test1.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2776230.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test10.h b/src/include/gromacs/external/tng_io/src/tests/compression/test10.h
new file mode 100644 (file)
index 0000000..d5135eb
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. Triple intraframe algorithm. Cubic cell."
+#define FILENAME "test10.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 3
+#define CODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2728492.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test11.h b/src/include/gromacs/external/tng_io/src/tests/compression/test11.h
new file mode 100644 (file)
index 0000000..596d63c
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. Triple one-to-one algorithm. Cubic cell."
+#define FILENAME "test11.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 7
+#define CODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 4293415.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test12.h b/src/include/gromacs/external/tng_io/src/tests/compression/test12.h
new file mode 100644 (file)
index 0000000..43eb15d
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. BWLZH interframe algorithm. Cubic cell."
+#define FILENAME "test12.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 8
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 894421.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test13.h b/src/include/gromacs/external/tng_io/src/tests/compression/test13.h
new file mode 100644 (file)
index 0000000..72aa99e
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. BWLZH intraframe algorithm. Cubic cell."
+#define FILENAME "test13.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 9
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 840246.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test14.h b/src/include/gromacs/external/tng_io/src/tests/compression/test14.h
new file mode 100644 (file)
index 0000000..382c70e
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. XTC3 algorithm. Cubic cell."
+#define FILENAME "test14.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 10 
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 1401016.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test15.h b/src/include/gromacs/external/tng_io/src/tests/compression/test15.h
new file mode 100644 (file)
index 0000000..c593bf5
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Automatic selection of algorithms. Cubic cell."
+#define FILENAME "test15.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING -1
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2776230.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test16.h b/src/include/gromacs/external/tng_io/src/tests/compression/test16.h
new file mode 100644 (file)
index 0000000..a29c5e5
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Coding. Automatic selection of algorithms. Cubic cell."
+#define FILENAME "test16.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING -1
+#define INITIALCODINGPARAMETER -1
+#define CODING -1
+#define CODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define SPEED 6
+#define EXPECTED_FILESIZE 838168.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test17.h b/src/include/gromacs/external/tng_io/src/tests/compression/test17.h
new file mode 100644 (file)
index 0000000..9787976
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Initial coding of velocities. Stopbits one-to-one . Cubic cell."
+#define FILENAME "test17.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 7336171.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test18.h b/src/include/gromacs/external/tng_io/src/tests/compression/test18.h
new file mode 100644 (file)
index 0000000..5a329ad
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Initial coding of velocities. Triplet one-to-one. Cubic cell."
+#define FILENAME "test18.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 7089695.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test19.h b/src/include/gromacs/external/tng_io/src/tests/compression/test19.h
new file mode 100644 (file)
index 0000000..b298309
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Initial coding of velocities. BWLZH one-to-one. Cubic cell."
+#define FILENAME "test19.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 9
+#define INITIALVELCODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 50
+#define EXPECTED_FILESIZE 208809.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test2.h b/src/include/gromacs/external/tng_io/src/tests/compression/test2.h
new file mode 100644 (file)
index 0000000..0e2c3b0
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. XTC2 algorithm. Cubic cell."
+#define FILENAME "test2.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2796171.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test20.h b/src/include/gromacs/external/tng_io/src/tests/compression/test20.h
new file mode 100644 (file)
index 0000000..4a45135
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding of velocities. Stopbit one-to-one. Cubic cell."
+#define FILENAME "test20.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 1
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 7237102.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test21.h b/src/include/gromacs/external/tng_io/src/tests/compression/test21.h
new file mode 100644 (file)
index 0000000..773c1da
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding of velocities. Triplet inter. Cubic cell."
+#define FILENAME "test21.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 2
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 6214307.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test22.h b/src/include/gromacs/external/tng_io/src/tests/compression/test22.h
new file mode 100644 (file)
index 0000000..1d8cfa9
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding of velocities. Triplet one-to-one. Cubic cell."
+#define FILENAME "test22.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 6988699.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test23.h b/src/include/gromacs/external/tng_io/src/tests/compression/test23.h
new file mode 100644 (file)
index 0000000..950f7ee
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding of velocities. Stopbit interframe. Cubic cell."
+#define FILENAME "test23.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 6
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 6494602.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test24.h b/src/include/gromacs/external/tng_io/src/tests/compression/test24.h
new file mode 100644 (file)
index 0000000..4b20897
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding of velocities. BWLZH interframe. Cubic cell."
+#define FILENAME "test24.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 25
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 8
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 50
+#define EXPECTED_FILESIZE 153520.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test25.h b/src/include/gromacs/external/tng_io/src/tests/compression/test25.h
new file mode 100644 (file)
index 0000000..37d777a
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding of velocities. BWLZH one-to-one. Cubic cell."
+#define FILENAME "test25.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 25
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 9
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 50
+#define EXPECTED_FILESIZE 154753.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test26.h b/src/include/gromacs/external/tng_io/src/tests/compression/test26.h
new file mode 100644 (file)
index 0000000..2c3b304
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "XTC2 algorithm. Orthorhombic cell."
+#define FILENAME "test26.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 9
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 20000
+#define INTMAX2 10000
+#define INTMAX3 30000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2861948.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test27.h b/src/include/gromacs/external/tng_io/src/tests/compression/test27.h
new file mode 100644 (file)
index 0000000..af7c9ff
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "XTC3 algorithm. Orthorhombic cell."
+#define FILENAME "test27.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 10
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 9
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 20000
+#define INTMAX2 10000
+#define INTMAX3 30000
+#define NFRAMES 200
+#define EXPECTED_FILESIZE 282600.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test28.h b/src/include/gromacs/external/tng_io/src/tests/compression/test28.h
new file mode 100644 (file)
index 0000000..99a70fc
--- /dev/null
@@ -0,0 +1,26 @@
+#define TESTNAME "Initial coding. Autoselect algorithm. Repetitive molecule. Cubic cell."
+#define FILENAME "test28.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define REGULAR
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING -1
+#define INITIALCODINGPARAMETER -1
+#define CODING 0
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 0
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 1677619.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test29.h b/src/include/gromacs/external/tng_io/src/tests/compression/test29.h
new file mode 100644 (file)
index 0000000..be6a8bf
--- /dev/null
@@ -0,0 +1,26 @@
+#define TESTNAME "Position coding. Autoselect algorithm. Repetitive molecule. Cubic cell."
+#define FILENAME "test29.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define REGULAR
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING -1
+#define INITIALCODINGPARAMETER -1
+#define CODING -1
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 0
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 228148.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test3.h b/src/include/gromacs/external/tng_io/src/tests/compression/test3.h
new file mode 100644 (file)
index 0000000..768a208
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Triplet one-to-one algorithm. Cubic cell."
+#define FILENAME "test3.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 7
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 4356773.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test30.h b/src/include/gromacs/external/tng_io/src/tests/compression/test30.h
new file mode 100644 (file)
index 0000000..b719c7b
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Intra frame triple algorithm. Large system. Cubic cell."
+#define FILENAME "test30.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 1
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 280198420.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test31.h b/src/include/gromacs/external/tng_io/src/tests/compression/test31.h
new file mode 100644 (file)
index 0000000..c0763e8
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. XTC2 algorithm. Large system. Cubic cell."
+#define FILENAME "test31.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 1
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 301463456.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test32.h b/src/include/gromacs/external/tng_io/src/tests/compression/test32.h
new file mode 100644 (file)
index 0000000..5f2e2f9
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. XTC3 algorithm. Large system. Cubic cell."
+#define FILENAME "test32.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 1
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 2
+#define EXPECTED_FILESIZE 31668187.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test33.h b/src/include/gromacs/external/tng_io/src/tests/compression/test33.h
new file mode 100644 (file)
index 0000000..3d08a65
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Intra frame BWLZH algorithm. Large system. Cubic cell."
+#define FILENAME "test33.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 1
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 9
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 2
+#define EXPECTED_FILESIZE 7121047.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test34.h b/src/include/gromacs/external/tng_io/src/tests/compression/test34.h
new file mode 100644 (file)
index 0000000..4242c18
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Stop bits algorithm. Large system. Cubic cell."
+#define FILENAME "test34.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 250247372.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test35.h b/src/include/gromacs/external/tng_io/src/tests/compression/test35.h
new file mode 100644 (file)
index 0000000..8f742b7
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Inter frame triple algorithm. Large system. Cubic cell."
+#define FILENAME "test35.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 2
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 243598962.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test36.h b/src/include/gromacs/external/tng_io/src/tests/compression/test36.h
new file mode 100644 (file)
index 0000000..fba6feb
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Intra frame triple algorithm. Large system. Cubic cell."
+#define FILENAME "test36.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 3
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 290800607.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test37.h b/src/include/gromacs/external/tng_io/src/tests/compression/test37.h
new file mode 100644 (file)
index 0000000..d2722bf
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. XTC2 algorithm. Large system. Cubic cell."
+#define FILENAME "test37.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 301463256.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test38.h b/src/include/gromacs/external/tng_io/src/tests/compression/test38.h
new file mode 100644 (file)
index 0000000..26a6740
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. XTC3 algorithm. Large system. Cubic cell."
+#define FILENAME "test38.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 10
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 4
+#define EXPECTED_FILESIZE 63482016.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test39.h b/src/include/gromacs/external/tng_io/src/tests/compression/test39.h
new file mode 100644 (file)
index 0000000..db09a18
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Intra frame BWLZH algorithm. Large system. Cubic cell."
+#define FILENAME "test39.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 9
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 4
+#define EXPECTED_FILESIZE 67631371.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test4.h b/src/include/gromacs/external/tng_io/src/tests/compression/test4.h
new file mode 100644 (file)
index 0000000..5254a07
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. BWLZH intra algorithm. Cubic cell."
+#define FILENAME "test4.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 9
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2572043.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test40.h b/src/include/gromacs/external/tng_io/src/tests/compression/test40.h
new file mode 100644 (file)
index 0000000..c7511e5
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Inter frame BWLZH algorithm. Large system. Cubic cell."
+#define FILENAME "test40.tng_compress"
+#define ALGOTEST
+#define NATOMS 5000000
+#define CHUNKY 2
+#define SCALE 1.
+#define PRECISION 1.
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 8
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 -536870911
+#define INTMIN2 -536870911
+#define INTMIN3 -536870911
+#define INTMAX1 536870911
+#define INTMAX2 536870911
+#define INTMAX3 536870911
+#define NFRAMES 4
+#define EXPECTED_FILESIZE 63822378.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test41.h b/src/include/gromacs/external/tng_io/src/tests/compression/test41.h
new file mode 100644 (file)
index 0000000..dab390c
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Intra frame triple algorithm. High accuracy. Cubic cell."
+#define FILENAME "test41.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 1
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 53179342.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test42.h b/src/include/gromacs/external/tng_io/src/tests/compression/test42.h
new file mode 100644 (file)
index 0000000..4452924
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. XTC2 algorithm. High accuracy. Cubic cell."
+#define FILENAME "test42.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 1
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 57283715.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test43.h b/src/include/gromacs/external/tng_io/src/tests/compression/test43.h
new file mode 100644 (file)
index 0000000..915d58e
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. XTC3 algorithm. High accuracy. Cubic cell."
+#define FILENAME "test43.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 1
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 3783912.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test44.h b/src/include/gromacs/external/tng_io/src/tests/compression/test44.h
new file mode 100644 (file)
index 0000000..f749cce
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. Intra frame BWLZH algorithm. High accuracy. Cubic cell."
+#define FILENAME "test44.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 1
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 9
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 10
+#define EXPECTED_FILESIZE 1436901.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test45.h b/src/include/gromacs/external/tng_io/src/tests/compression/test45.h
new file mode 100644 (file)
index 0000000..e558083
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Stop bits algorithm. High accuracy. Cubic cell."
+#define FILENAME "test45.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 36794379.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test46.h b/src/include/gromacs/external/tng_io/src/tests/compression/test46.h
new file mode 100644 (file)
index 0000000..dde45ab
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Inter frame triple algorithm. High accuracy. Cubic cell."
+#define FILENAME "test46.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 2
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 34508770.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test47.h b/src/include/gromacs/external/tng_io/src/tests/compression/test47.h
new file mode 100644 (file)
index 0000000..a668946
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding.  Intra frame triple algorithm. High accuracy. Cubic cell."
+#define FILENAME "test47.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 1610612736
+#define INTMAX2 1610612736
+#define INTMAX3 1610612736
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 53174711.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test48.h b/src/include/gromacs/external/tng_io/src/tests/compression/test48.h
new file mode 100644 (file)
index 0000000..3b2be95
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. XTC2 algorithm. High accuracy. Cubic cell."
+#define FILENAME "test48.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 55638414.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test49.h b/src/include/gromacs/external/tng_io/src/tests/compression/test49.h
new file mode 100644 (file)
index 0000000..6c618a0
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. XTC3 algorithm. High accuracy. Cubic cell."
+#define FILENAME "test49.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 10
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 20
+#define EXPECTED_FILESIZE 3585605.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test5.h b/src/include/gromacs/external/tng_io/src/tests/compression/test5.h
new file mode 100644 (file)
index 0000000..4c6a638
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Initial coding. XTC3 algorithm. Cubic cell."
+#define FILENAME "test5.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 1
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 3346179.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test50.h b/src/include/gromacs/external/tng_io/src/tests/compression/test50.h
new file mode 100644 (file)
index 0000000..130f1b9
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Intra frame BWLZH algorithm. High accuracy. Cubic cell."
+#define FILENAME "test50.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 9
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 20
+#define EXPECTED_FILESIZE 3143379.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test51.h b/src/include/gromacs/external/tng_io/src/tests/compression/test51.h
new file mode 100644 (file)
index 0000000..c3a57bf
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Position coding. Inter frame BWLZH algorithm. High accuracy. Cubic cell."
+#define FILENAME "test51.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 8
+#define CODINGPARAMETER 0
+#define VELCODING 4
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 20
+#define EXPECTED_FILESIZE 2897696.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test52.h b/src/include/gromacs/external/tng_io/src/tests/compression/test52.h
new file mode 100644 (file)
index 0000000..97f37c7
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Velocity coding. Stop bits algorithm. High accuracy. Cubic cell."
+#define FILENAME "test52.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 1
+#define VELINTMUL 100000
+#define VELPRECISION 1e-8
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 1
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 173083705.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test53.h b/src/include/gromacs/external/tng_io/src/tests/compression/test53.h
new file mode 100644 (file)
index 0000000..583e0b2
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Velocity coding. Triple algorithm. High accuracy. Cubic cell."
+#define FILENAME "test53.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 1
+#define VELINTMUL 100000
+#define VELPRECISION 1e-8
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 168548573.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test54.h b/src/include/gromacs/external/tng_io/src/tests/compression/test54.h
new file mode 100644 (file)
index 0000000..50b051f
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Velocity coding. Interframe triple algorithm. High accuracy. Cubic cell."
+#define FILENAME "test54.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 1
+#define VELINTMUL 100000
+#define VELPRECISION 1e-8
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 2
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 161798573.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test55.h b/src/include/gromacs/external/tng_io/src/tests/compression/test55.h
new file mode 100644 (file)
index 0000000..f8c98f9
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Velocity coding. Interframe stop-bits algorithm. High accuracy. Cubic cell."
+#define FILENAME "test55.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 1
+#define VELINTMUL 100000
+#define VELPRECISION 1e-8
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 6
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 166298533.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test56.h b/src/include/gromacs/external/tng_io/src/tests/compression/test56.h
new file mode 100644 (file)
index 0000000..7fb9b23
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Velocity coding. Intraframe BWLZH algorithm. High accuracy. Cubic cell."
+#define FILENAME "test56.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 1
+#define VELINTMUL 100000
+#define VELPRECISION 1e-8
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 9
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 20
+#define EXPECTED_FILESIZE 23390767.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test57.h b/src/include/gromacs/external/tng_io/src/tests/compression/test57.h
new file mode 100644 (file)
index 0000000..5c626d8
--- /dev/null
@@ -0,0 +1,24 @@
+#define TESTNAME "Velocity coding. Interframe BWLZH algorithm. High accuracy. Cubic cell."
+#define FILENAME "test57.tng_compress"
+#define ALGOTEST
+#define NATOMS 100000
+#define CHUNKY 10
+#define SCALE 0.5
+#define PRECISION 1e-8
+#define WRITEVEL 1
+#define VELINTMUL 100000
+#define VELPRECISION 1e-8
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 8
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 805306368
+#define INTMAX2 805306368
+#define INTMAX3 805306368
+#define NFRAMES 20
+#define EXPECTED_FILESIZE 13817974.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test58.h b/src/include/gromacs/external/tng_io/src/tests/compression/test58.h
new file mode 100644 (file)
index 0000000..9988106
--- /dev/null
@@ -0,0 +1,26 @@
+#define TESTNAME "Coding. Test float."
+#define FILENAME "test58.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define TEST_FLOAT
+#define EXPECTED_FILESIZE 6986313.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test59.h b/src/include/gromacs/external/tng_io/src/tests/compression/test59.h
new file mode 100644 (file)
index 0000000..9817976
--- /dev/null
@@ -0,0 +1,28 @@
+#define TESTNAME "Coding. Test write float, read double."
+#define FILENAME "test59.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#ifdef GEN
+#define TEST_FLOAT
+#endif
+#define EXPECTED_FILESIZE 6986313.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test6.h b/src/include/gromacs/external/tng_io/src/tests/compression/test6.h
new file mode 100644 (file)
index 0000000..ecbc443
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. XTC2 algorithm. Cubic cell."
+#define FILENAME "test6.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2736662.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test60.h b/src/include/gromacs/external/tng_io/src/tests/compression/test60.h
new file mode 100644 (file)
index 0000000..cf94e3b
--- /dev/null
@@ -0,0 +1,28 @@
+#define TESTNAME "Coding. Test write double, read float."
+#define FILENAME "test60.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#ifndef GEN
+#define TEST_FLOAT
+#endif
+#define EXPECTED_FILESIZE 6986313.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test61.h b/src/include/gromacs/external/tng_io/src/tests/compression/test61.h
new file mode 100644 (file)
index 0000000..373456d
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Recompression test. Stage 1: Generate"
+#define FILENAME "test61.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 698801.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test62.h b/src/include/gromacs/external/tng_io/src/tests/compression/test62.h
new file mode 100644 (file)
index 0000000..c8a2b48
--- /dev/null
@@ -0,0 +1,26 @@
+#define TESTNAME "Coding. Recompression test. Stage 2: Recompress"
+#define FILENAME "test62.tng_compress"
+#define RECOMPRESS "test61.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 10
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER 0
+#define VELCODING 8
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 151226.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test63.h b/src/include/gromacs/external/tng_io/src/tests/compression/test63.h
new file mode 100644 (file)
index 0000000..65a4768
--- /dev/null
@@ -0,0 +1,26 @@
+#define TESTNAME "Coding. Read int and convert to double."
+#define FILENAME "test63.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 100
+#define INTTODOUBLE
+#define EXPECTED_FILESIZE 698801.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test64.h b/src/include/gromacs/external/tng_io/src/tests/compression/test64.h
new file mode 100644 (file)
index 0000000..02f99b5
--- /dev/null
@@ -0,0 +1,27 @@
+#define TESTNAME "Coding. Read int and convert to float."
+#define FILENAME "test64.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 100
+#define INTTOFLOAT
+#define TEST_FLOAT
+#define EXPECTED_FILESIZE 698801.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test65.h b/src/include/gromacs/external/tng_io/src/tests/compression/test65.h
new file mode 100644 (file)
index 0000000..c7136ee
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions intra frame triple"
+#define FILENAME "test65.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 6315.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test66.h b/src/include/gromacs/external/tng_io/src/tests/compression/test66.h
new file mode 100644 (file)
index 0000000..95597c8
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions xtc2"
+#define FILENAME "test66.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 5
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 4465.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test67.h b/src/include/gromacs/external/tng_io/src/tests/compression/test67.h
new file mode 100644 (file)
index 0000000..ca8fe4b
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions triple one-to-one"
+#define FILENAME "test67.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 7
+#define INITIALCODINGPARAMETER -1
+#define CODING 7
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 6315.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test68.h b/src/include/gromacs/external/tng_io/src/tests/compression/test68.h
new file mode 100644 (file)
index 0000000..00d8032
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions BWLZH intra"
+#define FILENAME "test68.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 9
+#define INITIALCODINGPARAMETER 0
+#define CODING 9
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 321.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test69.h b/src/include/gromacs/external/tng_io/src/tests/compression/test69.h
new file mode 100644 (file)
index 0000000..7ee385d
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions xtc3"
+#define FILENAME "test69.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 10
+#define INITIALCODINGPARAMETER 0
+#define CODING 10
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 867.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test7.h b/src/include/gromacs/external/tng_io/src/tests/compression/test7.h
new file mode 100644 (file)
index 0000000..d0e3532
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. Stopbit interframe algorithm. Cubic cell."
+#define FILENAME "test7.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2545049.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test70.h b/src/include/gromacs/external/tng_io/src/tests/compression/test70.h
new file mode 100644 (file)
index 0000000..b3b4b25
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions stopbit interframe"
+#define FILENAME "test70.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 7548.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test71.h b/src/include/gromacs/external/tng_io/src/tests/compression/test71.h
new file mode 100644 (file)
index 0000000..c7d0673
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions triple interframe"
+#define FILENAME "test71.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 2
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 6315.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test72.h b/src/include/gromacs/external/tng_io/src/tests/compression/test72.h
new file mode 100644 (file)
index 0000000..633bb50
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. positions BWLZH inter"
+#define FILENAME "test72.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 8
+#define CODINGPARAMETER 0
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 257.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test73.h b/src/include/gromacs/external/tng_io/src/tests/compression/test73.h
new file mode 100644 (file)
index 0000000..e64af34
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. velocities stopbits one-to-one"
+#define FILENAME "test73.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 1
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 13863.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test74.h b/src/include/gromacs/external/tng_io/src/tests/compression/test74.h
new file mode 100644 (file)
index 0000000..73ada23
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. velocities triplet one-to-one"
+#define FILENAME "test74.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 3
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 3
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 12622.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test75.h b/src/include/gromacs/external/tng_io/src/tests/compression/test75.h
new file mode 100644 (file)
index 0000000..4fc004b
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. velocities BWLZH one-to-one"
+#define FILENAME "test75.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 9
+#define INITIALVELCODINGPARAMETER 0
+#define VELCODING 9
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 6628.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test76.h b/src/include/gromacs/external/tng_io/src/tests/compression/test76.h
new file mode 100644 (file)
index 0000000..4a51ecb
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. velocities triplet inter"
+#define FILENAME "test76.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 2
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 12630.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test77.h b/src/include/gromacs/external/tng_io/src/tests/compression/test77.h
new file mode 100644 (file)
index 0000000..d9394d8
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. velocities stopbits inter"
+#define FILENAME "test77.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 6
+#define VELCODINGPARAMETER -1
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 13863.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test78.h b/src/include/gromacs/external/tng_io/src/tests/compression/test78.h
new file mode 100644 (file)
index 0000000..f70592e
--- /dev/null
@@ -0,0 +1,25 @@
+#define TESTNAME "Coding. Compress zeros test. velocities BWLZH inter"
+#define FILENAME "test78.tng_compress"
+#define ALGOTEST
+#define NATOMS 100
+#define CHUNKY 100
+#define SCALE 0.
+#define PRECISION 0.01
+#define WRITEVEL 1
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 3
+#define CODINGPARAMETER -1
+#define INITIALVELCODING 1
+#define INITIALVELCODINGPARAMETER -1
+#define VELCODING 8
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 0
+#define INTMAX2 0
+#define INTMAX3 0
+#define NFRAMES 100
+#define EXPECTED_FILESIZE 6572.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test8.h b/src/include/gromacs/external/tng_io/src/tests/compression/test8.h
new file mode 100644 (file)
index 0000000..2ce103c
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. Stopbit interframe algorithm with intraframe compression as initial. Cubic cell."
+#define FILENAME "test8.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 3
+#define INITIALCODINGPARAMETER -1
+#define CODING 1
+#define CODINGPARAMETER -1
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2544876.
diff --git a/src/include/gromacs/external/tng_io/src/tests/compression/test9.h b/src/include/gromacs/external/tng_io/src/tests/compression/test9.h
new file mode 100644 (file)
index 0000000..f0672e2
--- /dev/null
@@ -0,0 +1,23 @@
+#define TESTNAME "Coding. Triple interframe algorithm. Cubic cell."
+#define FILENAME "test9.tng_compress"
+#define ALGOTEST
+#define NATOMS 1000
+#define CHUNKY 100
+#define SCALE 0.1
+#define PRECISION 0.01
+#define WRITEVEL 0
+#define VELPRECISION 0.1
+#define INITIALCODING 5
+#define INITIALCODINGPARAMETER 0
+#define CODING 2
+#define CODINGPARAMETER -1 
+#define VELCODING 0
+#define VELCODINGPARAMETER 0
+#define INTMIN1 0
+#define INTMIN2 0
+#define INTMIN3 0
+#define INTMAX1 10000
+#define INTMAX2 10000
+#define INTMAX3 10000
+#define NFRAMES 1000
+#define EXPECTED_FILESIZE 2418212.
diff --git a/src/include/gromacs/external/vmd_molfile/molfile_plugin.h b/src/include/gromacs/external/vmd_molfile/molfile_plugin.h
new file mode 100644 (file)
index 0000000..c81f391
--- /dev/null
@@ -0,0 +1,813 @@
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ * 
+ * This file is part of Gromacs        Copyright (c) 1991-2008
+ * David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org
+ * 
+ * And Hey:
+ * Gnomes, ROck Monsters And Chili Sauce
+ */
+
+/***************************************************************************
+ *cr
+ *cr            (C) Copyright 1995-2006 The Board of Trustees of the
+ *cr                        University of Illinois
+ *cr                         All Rights Reserved
+ *cr
+
+Developed by:           Theoretical and Computational Biophysics Group
+                        University of Illinois at Urbana-Champaign
+                        http://www.ks.uiuc.edu/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the Software), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to
+do so, subject to the following conditions:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimers.
+
+Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimers in the documentation
+and/or other materials provided with the distribution.
+
+Neither the names of Theoretical and Computational Biophysics Group,
+University of Illinois at Urbana-Champaign, nor the names of its contributors
+may be used to endorse or promote products derived from this Software without
+specific prior written permission.
+
+THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS WITH THE SOFTWARE.
+ ***************************************************************************/
+
+/***************************************************************************
+ * RCS INFORMATION:
+ *
+ *      $RCSfile: molfile_plugin.h,v $
+ *      $Author: saam $       $Locker:  $             $State: Exp $
+ *      $Revision: 1.91 $       $Date: 2009/07/28 21:54:30 $
+ *
+ ***************************************************************************/
+
+/** @file 
+ * API for C extensions to define a way to load structure, coordinate,
+ * trajectory, and volumetric data files  
+ */ 
+
+#ifndef MOL_FILE_PLUGIN_H
+#define MOL_FILE_PLUGIN_H
+
+#include "vmdplugin.h"
+
+/**
+ * Define a common plugin type to be used when registering the plugin.
+ */
+#define MOLFILE_PLUGIN_TYPE "mol file reader"
+
+/**
+ * File converter plugins use the same API  but register under a different
+ * type so that regular file readers can have priority.
+ */
+#define MOLFILE_CONVERTER_PLUGIN_TYPE "mol file converter"
+
+/* File plugin symbolic constants for better code readability */
+#define MOLFILE_SUCCESS           0   /**< succeeded in reading file      */
+#define MOLFILE_EOF              (-1)   /**< end of file                    */
+#define MOLFILE_ERROR            (-1)   /**< error reading/opening a file   */
+#define MOLFILE_NOSTRUCTUREDATA  (-2)   /**< no structure data in this file */
+
+#define MOLFILE_NUMATOMS_UNKNOWN (-1)   /**< unknown number of atoms       */
+#define MOLFILE_NUMATOMS_NONE     0   /**< no atoms in this file type    */
+
+/**
+ * Maximum string size macro
+ */
+#define MOLFILE_BUFSIZ           81   /**< maximum chars in string data  */
+#define MOLFILE_BIGBUFSIZ        4096 /**< maximum chars in long strings */
+
+#define MOLFILE_MAXWAVEPERTS     25   /**< maximum number of wavefunctions
+                                       *   per timestep */
+
+#define MOLFILE_QM_STATUS_UNKNOWN  (-1)
+#define MOLFILE_QM_OPT_CONVERGED    0
+#define MOLFILE_QM_SCF_NOT_CONV     1
+#define MOLFILE_QM_OPT_NOT_CONV     2
+#define MOLFILE_QM_FILE_TRUNCATED   3
+
+
+/**
+ * File level comments, origin information, and annotations.
+ */
+typedef struct {
+  char database[81];   /**< database of origin, if any        */
+  char accession[81];  /**< database accession code, if any   */
+  char date[81];       /**< date/time stamp for this data     */
+  char title[81];      /**< brief title for this data         */
+  int remarklen;       /**< length of remarks string          */
+  char *remarks;       /**< free-form remarks about data      */
+} molfile_metadata_t;
+
+
+/* 
+ * Struct for specifying atoms in a molecular structure.  The first 
+ * six components are required, the rest are optional and their presence is 
+ * indicating by setting the corresponding bit in optsflag.  When omitted,
+ * the application (for read_structure) or plugin (for write_structure) 
+ * must be able to supply default values if the missing parameters are 
+ * part of its internal data structure.
+ * Note that it is not possible to specify coordinates with this structure.
+ * This is intentional; all coordinate I/O is done with the read_timestep and 
+ * write_timestep functions. 
+ */
+
+/**
+ * Per-atom attributes and information.
+ */
+typedef struct {
+  /* these fields absolutely must be set or initialized to empty */
+  char name[16];      /**< required atom name string             */
+  char type[16];      /**< required atom type string             */
+  char resname[8];    /**< required residue name string          */
+  int resid;          /**< required integer residue ID           */
+  char segid[8];      /**< required segment name string, or ""   */
+  char chain[2];      /**< required chain name, or ""            */
+
+  /* rest are optional; use optflags to specify what's present   */
+  char altloc[2];     /**< optional PDB alternate location code  */
+  char insertion[2];  /**< optional PDB insertion code           */
+  float occupancy;    /**< optional occupancy value              */
+  float bfactor;      /**< optional B-factor value               */
+  float mass;         /**< optional mass value                   */
+  float charge;       /**< optional charge value                 */
+  float radius;       /**< optional radius value                 */
+  int atomicnumber;   /**< optional element atomic number        */
+} molfile_atom_t;
+
+/*@{*/
+/** Plugin optional data field availability flag */
+#define MOLFILE_NOOPTIONS     0x0000 /**< no optional data                 */
+#define MOLFILE_INSERTION     0x0001 /**< insertion codes provided         */
+#define MOLFILE_OCCUPANCY     0x0002 /**< occupancy data provided          */
+#define MOLFILE_BFACTOR       0x0004 /**< B-factor data provided           */
+#define MOLFILE_MASS          0x0008 /**< Atomic mass provided             */
+#define MOLFILE_CHARGE        0x0010 /**< Atomic charge provided           */
+#define MOLFILE_RADIUS        0x0020 /**< Atomic VDW radius provided       */
+#define MOLFILE_ALTLOC        0x0040 /**< Multiple conformations present   */
+#define MOLFILE_ATOMICNUMBER  0x0080 /**< Atomic element number provided   */
+#define MOLFILE_BONDSSPECIAL  0x0100 /**< Only non-standard bonds provided */
+#define MOLFILE_BADOPTIONS    0xFFFFFFFF /**< Detect badly behaved plugins */
+                              
+/*@}*/
+
+/*@{*/
+/** Plugin optional data field availability flag */
+#define MOLFILE_QMTS_NOOPTIONS     0x0000 /**< no optional data               */
+#define MOLFILE_QMTS_GRADIENT      0x0001 /**< energy gradients provided      */
+#define MOLFILE_QMTS_SCFITER       0x0002
+/*@}*/
+
+#if vmdplugin_ABIVERSION > 10
+typedef struct molfile_timestep_metadata {
+  unsigned int count;                  /**< total # timesteps; -1 if unknown */
+  unsigned int avg_bytes_per_timestep; /** bytes per timestep                */
+  int has_velocities;                  /**< if timesteps have velocities     */
+} molfile_timestep_metadata_t;
+#endif
+
+#if vmdplugin_ABIVERSION > 11
+typedef struct molfile_qm_timestep_metadata {
+  unsigned int count;                  /**< total # timesteps; -1 if unknown */
+  unsigned int avg_bytes_per_timestep; /** bytes per timestep                */
+  int has_gradient;                    /**< if timestep contains gradient    */
+  int num_scfiter;                     /**< # scf iterations for this ts     */
+  int num_orbitals_per_wavef[MOLFILE_MAXWAVEPERTS]; /**< # orbitals for each wavefunction */
+  int has_orben_per_wavef[MOLFILE_MAXWAVEPERTS]; /**< orbital energy flags */
+  int has_occup_per_wavef[MOLFILE_MAXWAVEPERTS]; /**< orbital occupancy flags */
+  int num_wavef ;                      /**< # wavefunctions in this ts     */
+  int wavef_size;                      /**< size of one wavefunction 
+                                        *   (# of gaussian basis fctns)    */
+  int num_charge_sets;            /**< # of charge values per atom */
+} molfile_qm_timestep_metadata_t;
+#endif
+
+/*
+ * Per-timestep atom coordinates and periodic cell information
+ */ 
+typedef struct {
+  float *coords;        /**< coordinates of all atoms, arranged xyzxyzxyz   */
+#if vmdplugin_ABIVERSION > 10
+  float *velocities;    /**< space for velocities of all atoms; same layout */
+                        /**< NULL unless has_velocities is set              */
+#endif
+
+  /*@{*/   
+  /**
+   * Unit cell specification of the form A, B, C, alpha, beta, gamma.
+   * notes: A, B, C are side lengths of the unit cell
+   * alpha = angle between b and c
+   *  beta = angle between a and c
+   * gamma = angle between a and b
+   */ 
+  float A, B, C, alpha, beta, gamma; 
+  /*@}*/   
+
+#if vmdplugin_ABIVERSION > 10
+  double physical_time; /**< physical time point associated with this frame */
+#endif
+} molfile_timestep_t;
+
+
+/**
+ * Metadata for volumetric datasets, read initially and used for subsequent
+ * memory allocations and file loading.  
+ */
+typedef struct {
+  char dataname[256];   /**< name of volumetric data set                    */
+  float origin[3];      /**< origin: origin of volume (x=0, y=0, z=0 corner */
+
+  /*
+   * x/y/z axis:
+   * These the three cell sides, providing both direction and length
+   * (not unit vectors) for the x, y, and z axes.  In the simplest
+   * case, these would be <size,0,0> <0,size,0> and <0,0,size) for 
+   * an orthogonal cubic volume set.  For other cell shapes these
+   * axes can be oriented non-orthogonally, and the parallelpiped
+   * may have different side lengths, not just a cube/rhombus.
+   */
+  float xaxis[3];       /**< direction (and length) for X axis              */ 
+  float yaxis[3];       /**< direction (and length) for Y axis              */
+  float zaxis[3];       /**< direction (and length) for Z axis              */
+
+  /*
+   * x/y/z size: 
+   * Number of grid cells along each axis.  This is _not_ the
+   * physical size of the box, this is the number of voxels in each
+   * direction, independent of the shape of the volume set. 
+   */
+  int xsize;            /**< number of grid cells along the X axis          */
+  int ysize;            /**< number of grid cells along the Y axis          */
+  int zsize;            /**< number of grid cells along the Z axis          */
+
+  int has_color;        /**< flag indicating presence of voxel color data   */
+} molfile_volumetric_t;
+
+
+#if vmdplugin_ABIVERSION > 9
+
+/**
+ * Sizes of various QM-related data arrays which must be allocated by
+ * the caller (VMD) so that the plugin can fill in the arrays with data.
+ */
+typedef struct {
+  /* hessian data */
+  int nimag;                    /**< number of imaginary modes */
+  int nintcoords;               /**< number internal coordinates */
+  int ncart;                    /**< number cartesian coordinates */
+
+  /* orbital/basisset data */
+  int num_basis_funcs;          /**< number of uncontracted basis functions in basis array */
+  int num_basis_atoms;          /**< number of atoms in basis set */
+  int num_shells;               /**< total number of atomic shells */
+  int wavef_size;               /**< size of the wavefunction
+                                 *   i.e. size of secular eq. or
+                                 *   # of cartesian contracted
+                                 *   gaussian basis functions */
+
+  /* everything else */
+  int have_sysinfo;
+  int have_carthessian;         /**< hessian in cartesian coords available  */
+  int have_inthessian;          /**< hessian in internal coords available  */
+  int have_normalmodes;         /**< normal modes available  */
+} molfile_qm_metadata_t;
+
+
+/**
+ * struct holding the data of hessian/normal mode runs
+ * needed to calculate bond/angle/dihedral force constants
+ * XXX: do we really need doubles here??
+ */
+typedef struct {
+  double *carthessian;  /**< hessian matrix in cartesian coordinates (ncart)*(ncart)
+                         *   as a single array of doubles (row(1), ...,row(natoms)) */
+  int    *imag_modes;   /**< list(nimag) of imaginary modes */
+  double *inthessian;   /**< hessian matrix in internal coordinates
+                         *   (nintcoords*nintcoords) as a single array of
+                         *   doubles (row(1), ...,row(nintcoords)) */
+  float *wavenumbers;   /**< array(ncart) of wavenumbers of normal modes */
+  float *intensities;   /**< array(ncart) of intensities of normal modes */
+  float *normalmodes;   /**< matrix(ncart*ncart) of normal modes  */
+} molfile_qm_hessian_t;
+
+
+/**
+ * struct holding the data for wavefunction/orbitals
+ * needed to generate the volumetric orbital data
+ */
+typedef struct {
+  int *num_shells_per_atom; /**< number of shells per atom */
+  int *num_prim_per_shell;  /**< number of shell primitives shell */
+
+  float *basis;             /**< contraction coeffients and exponents for
+                             *   the basis functions in the form
+                             *   { exp(1), c-coeff(1), exp(2), c-coeff(2), ....};
+                             *   size=2*num_basis_funcs */
+  int *atomic_number;       /**< atomic numbers (chem. element) of atoms in basis set */
+  int *angular_momentum;    /**< 3 ints per wave function coefficient do describe the 
+                             *   cartesian components of the angular momentum.
+                             *   E.g. S={0 0 0}, Px={1 0 0}, Dxy={1 1 0}, or Fyyz={0 2 1}. 
+                             */
+  int *shell_symmetry;      /**< symmetry type per shell in basis */
+} molfile_qm_basis_t;
+
+
+/**
+ * QM run info. Parameters that stay unchanged during a single file.
+ */ 
+typedef struct {
+  int nproc;             /**< number of processors used. XXX:? */
+  int memory;            /**< amount of memory used in Mbyte. XXX:? */ 
+  int runtype;           /**< flag indicating the calculation method. */
+  int scftype;           /**< SCF type: RHF, UHF, ROHF, GVB or MCSCF wfn. */
+  int status;            /**< indicates whether SCF and geometry optimization
+                          *   have converged properly. */
+  int num_electrons;     /**< number of electrons.    XXX: can be fractional in some DFT codes */
+  int totalcharge;       /**< total charge of system. XXX: can be fractional in some DFT codes */
+  int num_occupied_A;    /**< number of occupied alpha orbitals */
+  int num_occupied_B;    /**< number of occupied beta orbitals */
+
+  double *nuc_charge;    /**< array(natom) containing the nuclear charge of atom i */
+
+  char basis_string[MOLFILE_BUFSIZ];    /**< basis name as "nice" string. */
+  char runtitle[MOLFILE_BIGBUFSIZ];     /**< title of run.                */
+  char geometry[MOLFILE_BUFSIZ];        /**< type of provided geometry,   XXX: remove?
+                                         * e.g. UNIQUE, ZMT, CART, ...    */
+  char version_string[MOLFILE_BUFSIZ];  /**< QM code version information. */
+} molfile_qm_sysinfo_t;
+
+
+typedef struct {
+  int   type;               /**< CANONICAL, LOCALIZED, OTHER */
+  int   spin;               /**< 1 for alpha, -1 for beta */
+  int   excitation;         /**< 0 for ground state, 1,2,3,... for excited states */
+  int   multiplicity;       /**< spin multiplicity of the state, zero if unknown */
+  char info[MOLFILE_BUFSIZ]; /**< string for additional type info */
+
+  double energy;            /**< energy of the electronic state.
+                             *   i.e. HF-SCF energy, CI state energy,
+                             *   MCSCF energy, etc. */
+
+  float *wave_coeffs;       /**< expansion coefficients for wavefunction in the
+                             *   form {orbital1(c1),orbital1(c2),.....,orbitalM(cN)} */
+  float *orbital_energies;  /**< list of orbital energies for wavefunction */
+  float *occupancies;       /**< orbital occupancies */
+  int   *orbital_ids;       /**< orbital ID numbers; If NULL then VMD will
+                             *   assume 1,2,3,...num_orbs.     */
+} molfile_qm_wavefunction_t;
+
+
+/**
+ * QM per trajectory timestep info
+ */
+typedef struct {
+  molfile_qm_wavefunction_t *wave; /**< array of wavefunction objects */
+  float  *gradient;         /**< force on each atom (=gradient of energy) */
+
+  double *scfenergies;      /**< scfenergy per trajectory point. */
+  double *charges;          /**< per-atom charges */
+  int    *charge_types;     /**< type of each charge set */
+} molfile_qm_timestep_t;
+
+
+/**
+ * QM wavefunctions, and related information 
+ */
+typedef struct {
+  molfile_qm_hessian_t hess;            /* hessian info */
+  molfile_qm_basis_t   basis;           /* basis set info */
+  molfile_qm_sysinfo_t run;             /* system info  */
+} molfile_qm_t;
+
+
+/**
+ *  Enumeration of all of the wavefunction types that can be read
+ *  from QM file reader plugins.
+ *
+ * CANON    = canonical (i.e diagonalized) wavefunction
+ * GEMINAL  = GVB-ROHF geminal pairs
+ * MCSCFNAT = Multi-Configuration SCF natural orbitals
+ * MCSCFOPT = Multi-Configuration SCF optimized orbitals
+ * CINATUR  = Configuration-Interaction natural orbitals
+ * BOYS     = Boys localization
+ * RUEDEN   = Ruedenberg localization
+ * PIPEK    = Pipek-Mezey population localization
+ *
+ * NBO related localizations:
+ * --------------------------
+ * NAO      = Natural Atomic Orbitals
+ * PNAO     = pre-orthogonal NAOs
+ * NBO      = Natural Bond Orbitals
+ * PNBO     = pre-orthogonal NBOs
+ * NHO      = Natural Hybrid Orbitals
+ * PNHO     = pre-orthogonal NHOs
+ * NLMO     = Natural Localized Molecular Orbitals
+ * PNLMO    = pre-orthogonal NLMOs
+ *
+ * UNKNOWN  = Use this for any type not listed here
+ *            You can use the string field for description
+ */
+enum molfile_qm_wavefunc_type {
+  MOLFILE_WAVE_CANON,    MOLFILE_WAVE_GEMINAL,
+  MOLFILE_WAVE_MCSCFNAT, MOLFILE_WAVE_MCSCFOPT,
+  MOLFILE_WAVE_CINATUR,
+  MOLFILE_WAVE_PIPEK,  MOLFILE_WAVE_BOYS, MOLFILE_WAVE_RUEDEN,
+  MOLFILE_WAVE_NAO,    MOLFILE_WAVE_PNAO, MOLFILE_WAVE_NHO, 
+  MOLFILE_WAVE_PNHO,   MOLFILE_WAVE_NBO,  MOLFILE_WAVE_PNBO, 
+  MOLFILE_WAVE_PNLMO,  MOLFILE_WAVE_NLMO, MOLFILE_WAVE_MOAO, 
+  MOLFILE_WAVE_NATO,   MOLFILE_WAVE_UNKNOWN
+};
+
+enum molfile_qm_charge_type {
+  MOLFILE_QMCHARGE_MULLIKEN, MOLFILE_QMCHARGE_LOWDIN,
+  MOLFILE_QMCHARGE_ESP, MOLFILE_QMCHARGE_NPA,
+  MOLFILE_QMCHARGE_UNKNOWN
+};
+#endif
+
+
+/**
+ *  Enumeration of all of the supported graphics objects that can be read
+ *  from graphics file reader plugins.
+ */
+enum molfile_graphics_type {
+  MOLFILE_POINT,  MOLFILE_TRIANGLE, MOLFILE_TRINORM, MOLFILE_NORMS, 
+  MOLFILE_LINE,   MOLFILE_CYLINDER, MOLFILE_CAPCYL,  MOLFILE_CONE,    
+  MOLFILE_SPHERE, MOLFILE_TEXT,     MOLFILE_COLOR,   MOLFILE_TRICOLOR
+};
+
+/**
+ *  Individual graphics object/element data
+ */ 
+typedef struct {
+  int type;             /* One of molfile_graphics_type */
+  int style;            /* A general style parameter    */
+  float size;           /* A general size parameter     */
+  float data[9];        /* All data for the element     */
+} molfile_graphics_t;
+
+
+/*
+ * Types for raw graphics elements stored in files.  Data for each type
+ * should be stored by the plugin as follows:
+
+type        data                                     style       size
+----        ----                                     -----       ----
+point       x, y, z                                              pixel size
+triangle    x1,y1,z1,x2,y2,z2,x3,y3,z3                 
+trinorm     x1,y1,z1,x2,y2,z2,x3,y3,z3                 
+            the next array element must be NORMS
+tricolor    x1,y1,z1,x2,y2,z2,x3,y3,z3                 
+            the next array elements must be NORMS
+            the following element must be COLOR, with three RGB triples
+norms       x1,y1,z1,x2,y2,z2,x3,y3,z3                 
+line        x1,y1,z1,x2,y2,z2                        0=solid     pixel width
+                                                     1=stippled
+cylinder    x1,y1,z1,x2,y2,z2                        resolution  radius
+capcyl      x1,y1,z1,x2,y2,z2                        resolution  radius
+sphere      x1,y1,z1                                 resolution  radius
+text        x, y, z, up to 24 bytes of text                      pixel size
+color       r, g, b
+*/
+
+
+/**
+ * Main file reader API.  Any function in this struct may be NULL
+ * if not implemented by the plugin; the application checks this to determine
+ * what functionality is present in the plugin. 
+ */ 
+typedef struct {
+  /**
+   * Required header 
+   */
+  vmdplugin_HEAD;
+
+  /**
+   * Filename extension for this file type.  May be NULL if no filename 
+   * extension exists and/or is known.  For file types that match several
+   * common extensions, list them in a comma separated list such as:
+   *  "pdb,ent,foo,bar,baz,ban"
+   * The comma separated list will be expanded when filename extension matching
+   * is performed.  If multiple plugins solicit the same filename extensions,
+   * the one that lists the extension earliest in its list is selected. In the 
+   * case of a "tie", the first one tried/checked "wins".
+   */
+  const char *filename_extension;
+
+  /**
+   * Try to open the file for reading.  Return an opaque handle, or NULL on
+   * failure. Set the number of atoms; if the number of atoms cannot be 
+   * determined, set natoms to MOLFILE_NUMATOMS_UNKNOWN. 
+   * Filetype should be the name under which this plugin was registered;
+   * this is provided so that plugins can provide the same function pointer
+   * to handle multiple file types.
+   */
+  void *(* open_file_read)(const char *filepath, const char *filetype, 
+      int *natoms);
+  
+  /**
+   * Read molecular structure from the given file handle.  atoms is allocated
+   * by the caller and points to space for natoms.
+   * On success, place atom information in the passed-in pointer.  
+   * optflags specifies which optional fields in the atoms will be set by
+   * the plugin.
+   */
+  int (*read_structure)(void *, int *optflags, molfile_atom_t *atoms);
+
+  /**
+   * Read bond information for the molecule.  On success the arrays from
+   * and to should point to the (one-based) indices of bonded atoms.
+   * Each unique bond should be specified only once, so file formats that list
+   * bonds twice will need post-processing before the results are returned to
+   * the caller.
+   * If the plugin provides bond information, but the file loaded doesn't 
+   * actually contain any bond info, the nbonds parameter should be
+   * set to 0 and from/to should be set to NULL to indicate that no bond
+   * information was actually present, and automatic bond search should be
+   * performed.  
+   *
+   * If the plugin provides bond order information, the bondorder array
+   * will contain the bond order for each from/to pair.  If not, the bondorder
+   * pointer should be set to NULL, in which case the caller will provide a 
+   * default bond order value of 1.0.
+   *
+   * If the plugin provides bond type information, the bondtype array
+   * will contain the bond type index for each from/to pair. These numbers
+   * are consecutive integers starting from 0.
+   * the bondtypenames list, contains the corresponding names, if available,
+   * as a NULL string terminated list. nbondtypes is provided for convenience
+   * and consistency checking.
+   *
+   * These arrays must be freed by the plugin in the close_file_read function.
+   * This function can be called only after read_structure().  
+   * Return MOLFILE_SUCCESS if no errors occur. 
+   */
+#if vmdplugin_ABIVERSION > 14
+  int (*read_bonds)(void *, int *nbonds, int **from, int **to, float **bondorder, 
+                    int **bondtype, int *nbondtypes, char ***bondtypename);
+#else
+  int (*read_bonds)(void *, int *nbonds, int **from, int **to, float **bondorder);
+#endif
+
+  /**
+   * XXX this function will be augmented and possibly superceded by a 
+   *     new QM-capable version named read_timestep(), when finished.
+   *
+   * Read the next timestep from the file.  Return MOLFILE_SUCCESS, or 
+   * MOLFILE_EOF on EOF.  If the molfile_timestep_t argument is NULL, then 
+   * the frame should be skipped.  Otherwise, the application must prepare 
+   * molfile_timestep_t by allocating space in coords for the corresponding 
+   * number of coordinates.  
+   * The natoms parameter exists because some coordinate file formats 
+   * (like CRD) cannot determine for themselves how many atoms are in a 
+   * timestep; the app must therefore obtain this information elsewhere
+   * and provide it to the plugin.
+   */
+  int (* read_next_timestep)(void *, int natoms, molfile_timestep_t *);
+
+  /**
+   * Close the file and release all data.  The handle cannot be reused.
+   */
+  void (* close_file_read)(void *);
+   
+  /**
+   * Open a coordinate file for writing using the given header information.
+   * Return an opaque handle, or NULL on failure.  The application must
+   * specify the number of atoms to be written. 
+   * filetype should be the name under which this plugin was registered.
+   */
+  void *(* open_file_write)(const char *filepath, const char *filetype, 
+      int natoms);
+  
+  /**
+   * Write structure information.  Return success.
+   */
+  int (* write_structure)(void *, int optflags, const molfile_atom_t *atoms);
+
+  /**
+   * Write a timestep to the coordinate file.  Return MOLFILE_SUCCESS if no
+   * errors occur.  If the file contains structure information in each 
+   * timestep (like a multi-entry PDB), it will have to cache the information 
+   * from the initial calls from write_structure.
+   */
+  int (* write_timestep)(void *, const molfile_timestep_t *);
+  
+  /**
+   * Close the file and release all data.  The handle cannot be reused.
+   */
+  void (* close_file_write)(void *);
+
+  /**
+   * Retrieve metadata pertaining to volumetric datasets in this file.
+   * Set nsets to the number of volumetric data sets, and set *metadata
+   * to point to an array of molfile_volumetric_t.  The array is owned by
+   * the plugin and should be freed by close_file_read().  The application
+   * may call this function any number of times.
+   */
+  int (* read_volumetric_metadata)(void *, int *nsets, 
+        molfile_volumetric_t **metadata);
+
+  /** 
+   * Read the specified volumetric data set into the space pointed to by 
+   * datablock.  The set is specified with a zero-based index.  The space 
+   * allocated for the datablock must be equal to
+   * xsize * ysize * zsize.  No space will be allocated for colorblock 
+   * unless has_color is nonzero; in that case, colorblock should be
+   * filled in with three RGB floats per datapoint.
+   */
+  int (* read_volumetric_data)(void *, int set, float *datablock, 
+        float *colorblock);
+
+  /**
+   * Read raw graphics data stored in this file.   Return the number of data
+   * elements and the data itself as an array of molfile_graphics_t in the 
+   * pointer provided by the application.  The plugin is responsible for 
+   * freeing the data when the file is closed.
+   */
+  int (* read_rawgraphics)(void *, int *nelem, const molfile_graphics_t **data);
+
+  /**
+   * Read molecule metadata such as what database (if any) this file/data
+   * came from, what the accession code for the database is, textual remarks
+   * and other notes pertaining to the contained structure/trajectory/volume
+   * and anything else that's informative at the whole file level.
+   */ 
+  int (* read_molecule_metadata)(void *, molfile_metadata_t **metadata);
+  
+  /**
+   * Write bond information for the molecule.  The arrays from
+   * and to point to the (one-based) indices of bonded atoms.
+   * Each unique bond will be specified only once by the caller. 
+   * File formats that list bonds twice will need to emit both the 
+   * from/to and to/from versions of each.
+   * This function must be called before write_structure().  
+   *
+   * Like the read_bonds() routine, the bondorder pointer is set to NULL
+   * if the caller doesn't have such information, in which case the 
+   * plugin should assume a bond order of 1.0 if the file format requires
+   * bond order information.
+   *
+   * Support for bond types follows the bondorder rules. bondtype is
+   * an integer array of the size nbonds that contains the bond type
+   * index (consecutive integers starting from 0) and bondtypenames
+   * contain the corresponding strings, in case the naming/numbering
+   * scheme is different from the index numbers.
+   * if the pointers are set to NULL, then this information is not available.
+   * bondtypenames can only be used of bondtypes is also given.
+   * Return MOLFILE_SUCCESS if no errors occur. 
+   */
+#if vmdplugin_ABIVERSION > 14
+  int (* write_bonds)(void *, int nbonds, int *from, int *to, float *bondorder, 
+                     int *bondtype, int nbondtypes, char **bondtypename);
+#else
+  int (* write_bonds)(void *, int nbonds, int *from, int *to, float *bondorder);
+#endif
+
+#if vmdplugin_ABIVERSION > 9
+  /**
+   * Write the specified volumetric data set into the space pointed to by 
+   * datablock.  The * allocated for the datablock must be equal to
+   * xsize * ysize * zsize.  No space will be allocated for colorblock 
+   * unless has_color is nonzero; in that case, colorblock should be
+   * filled in with three RGB floats per datapoint.
+   */
+  int (* write_volumetric_data)(void *, molfile_volumetric_t *metadata,
+                                float *datablock, float *colorblock);
+
+#if vmdplugin_ABIVERSION > 15
+  /** 
+   * Read in Angles, Dihedrals, Impropers, and Cross Terms and optionally types.
+   * (Cross terms pertain to the CHARMM/NAMD CMAP feature) 
+   */
+  int (* read_angles)(void *handle, int *numangles, int **angles, int **angletypes,
+                      int *numangletypes, char ***angletypenames, int *numdihedrals,
+                      int **dihedrals, int **dihedraltypes, int *numdihedraltypes,
+                      char ***dihedraltypenames, int *numimpropers, int **impropers,        
+                      int **impropertypes, int *numimpropertypes, char ***impropertypenames,
+                      int *numcterms, int **cterms, int *ctermcols, int *ctermrows);
+
+  /** 
+   * Write out Angles, Dihedrals, Impropers, and Cross Terms
+   * (Cross terms pertain to the CHARMM/NAMD CMAP feature) 
+   */
+  int (* write_angles)(void *handle, int numangles, const int *angles, const int *angletypes,
+                       int numangletypes, const char **angletypenames, int numdihedrals,
+                       const int *dihedrals, const int *dihedraltypes, int numdihedraltypes,
+                       const char **dihedraltypenames, int numimpropers, 
+                       const int *impropers, const int *impropertypes, int numimpropertypes,
+                       const char **impropertypenames, int numcterms,  const int *cterms, 
+                       int ctermcols, int ctermrows);
+#else
+  /** 
+   * Read in Angles, Dihedrals, Impropers, and Cross Terms
+   * Forces are in Kcal/mol
+   * (Cross terms pertain to the CHARMM/NAMD CMAP feature, forces are given
+   *  as a 2-D matrix)
+   */
+  int (* read_angles)(void *,
+                int *numangles,    int **angles,    double **angleforces,
+                int *numdihedrals, int **dihedrals, double **dihedralforces,
+                int *numimpropers, int **impropers, double **improperforces,
+                int *numcterms,    int **cterms, 
+                int *ctermcols,    int *ctermrows,  double **ctermforces);
+
+  /** 
+   * Write out Angles, Dihedrals, Impropers, and Cross Terms
+   * Forces are in Kcal/mol
+   * (Cross terms pertain to the CHARMM/NAMD CMAP feature, forces are given
+   *  as a 2-D matrix)
+   */
+  int (* write_angles)(void *,
+        int numangles,    const int *angles,    const double *angleforces,
+        int numdihedrals, const int *dihedrals, const double *dihedralforces,
+        int numimpropers, const int *impropers, const double *improperforces,
+        int numcterms,   const int *cterms,    
+        int ctermcols, int ctermrows, const double *ctermforces);
+#endif
+
+  /**
+   * Retrieve metadata pertaining to QM datasets in this file.
+   */
+  int (* read_qm_metadata)(void *, molfile_qm_metadata_t *metadata);
+
+  /**
+   * Read QM data
+   */
+  int (* read_qm_rundata)(void *, molfile_qm_t *qmdata);
+
+  /**
+   * Read the next timestep from the file.  Return MOLFILE_SUCCESS, or 
+   * MOLFILE_EOF on EOF.  If the molfile_timestep_t or molfile_qm_metadata_t
+   * arguments are NULL, then the coordinate or qm data should be skipped.  
+   * Otherwise, the application must prepare molfile_timestep_t and 
+   * molfile_qm_timestep_t by allocating space for the corresponding 
+   * number of coordinates, orbital wavefunction coefficients, etc.
+   * Since it is common for users to want to load only the final timestep 
+   * data from a QM run, the application may provide any combination of
+   * valid, or NULL pointers for the molfile_timestep_t and 
+   * molfile_qm_timestep_t parameters, depending on what information the
+   * user is interested in.
+   * The natoms and qm metadata parameters exist because some file formats 
+   * cannot determine for themselves how many atoms etc are in a 
+   * timestep; the app must therefore obtain this information elsewhere
+   * and provide it to the plugin.
+   */
+  int (* read_timestep)(void *, int natoms, molfile_timestep_t *,
+                        molfile_qm_metadata_t *, molfile_qm_timestep_t *);
+#endif
+
+#if vmdplugin_ABIVERSION > 10
+  int (* read_timestep_metadata)(void *, molfile_timestep_metadata_t *);
+#endif
+#if vmdplugin_ABIVERSION > 11
+  int (* read_qm_timestep_metadata)(void *, molfile_qm_timestep_metadata_t *);
+#endif
+
+#if vmdplugin_ABIVERSION > 13
+  /**
+   *  Console output, READ-ONLY function pointer.
+   *  Function pointer that plugins can use for printing to the host
+   *  application's text console.  This provides a clean way for plugins
+   *  to send message strings back to the calling application, giving the
+   *  caller the ability to prioritize, buffer, and redirect console messages
+   *  to an appropriate output channel, window, etc.  This enables the use of
+   *  graphical consoles like TkCon without losing console output from plugins.
+   *  If the function pointer is NULL, no console output service is provided
+   *  by the calling application, and the output should default to stdout
+   *  stream.  If the function pointer is non-NULL, all output will be
+   *  subsequently dealt with by the calling application.
+   *
+   *  XXX this should really be put into a separate block of
+   *      application-provided read-only function pointers for any
+   *      application-provided services
+   */
+  int (* cons_fputs)(const int, const char*);
+#endif
+
+} molfile_plugin_t;
+
+#endif
+
diff --git a/src/include/gromacs/external/vmd_molfile/vmddlopen.h b/src/include/gromacs/external/vmd_molfile/vmddlopen.h
new file mode 100644 (file)
index 0000000..e18e65f
--- /dev/null
@@ -0,0 +1,109 @@
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ * 
+ * This file is part of Gromacs        Copyright (c) 1991-2008
+ * David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org
+ * 
+ * And Hey:
+ * Gnomes, ROck Monsters And Chili Sauce
+ */
+
+/***************************************************************************
+ *cr
+ *cr            (C) Copyright 1995-2009 The Board of Trustees of the
+ *cr                        University of Illinois
+ *cr                         All Rights Reserved
+ *cr
+Developed by:           Theoretical and Computational Biophysics Group
+                        University of Illinois at Urbana-Champaign
+                        http://www.ks.uiuc.edu/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the Software), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to
+do so, subject to the following conditions:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimers.
+
+Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimers in the documentation
+and/or other materials provided with the distribution.
+
+Neither the names of Theoretical and Computational Biophysics Group,
+University of Illinois at Urbana-Champaign, nor the names of its contributors
+may be used to endorse or promote products derived from this Software without
+specific prior written permission.
+
+THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS WITH THE SOFTWARE.
+ ***************************************************************************/
+
+/***************************************************************************
+ * RCS INFORMATION:
+ *
+ *      $RCSfile: vmddlopen.h,v $
+ *      $Author: johns $        $Locker:  $             $State: Exp $
+ *      $Revision: 1.9 $      $Date: 2009/07/07 02:40:05 $
+ *
+ ***************************************************************************
+ * DESCRIPTION:
+ *   Routines for loading dynamic link libraries and shared object files
+ *   on various platforms, abstracting from machine dependent APIs.
+ *
+ * LICENSE:
+ *   UIUC Open Source License 
+ *   http://www.ks.uiuc.edu/Research/vmd/plugins/pluginlicense.html
+ *
+ ***************************************************************************/
+
+/*
+ * vmddlopen: thin multi-platform wrapper around dlopen/LoadLibrary
+ */
+
+#ifndef VMD_DLOPEN__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Try to open the specified library.  All symbols must be resolved or the 
+ * load will fail (RTLD_NOW).  
+ */
+void *vmddlopen(const char *fname);
+
+/* Try to load the specified symbol using the given handle.  Returns NULL if 
+ * the symbol cannot be loaded.
+ */
+void *vmddlsym(void *h, const char *sym);
+
+/* Unload the library.  Return 0 on success, nonzero on error. 
+ */
+int vmddlclose(void *h);
+
+/* Return last error from any of the above functions.  Not thread-safe on
+ * Windows due to static buffer in our code. 
+ */ 
+const char *vmddlerror(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/include/gromacs/external/vmd_molfile/vmdplugin.h b/src/include/gromacs/external/vmd_molfile/vmdplugin.h
new file mode 100644 (file)
index 0000000..62ec64b
--- /dev/null
@@ -0,0 +1,239 @@
+/* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
+ *
+ * 
+ * This file is part of Gromacs        Copyright (c) 1991-2008
+ * David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org
+ * 
+ * And Hey:
+ * Gnomes, ROck Monsters And Chili Sauce
+ */
+
+/***************************************************************************
+ *cr
+ *cr            (C) Copyright 1995-2006 The Board of Trustees of the
+ *cr                        University of Illinois
+ *cr                         All Rights Reserved
+ *cr
+Developed by:           Theoretical and Computational Biophysics Group
+                        University of Illinois at Urbana-Champaign
+                        http://www.ks.uiuc.edu/
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the Software), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to
+do so, subject to the following conditions:
+
+Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimers.
+
+Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimers in the documentation
+and/or other materials provided with the distribution.
+
+Neither the names of Theoretical and Computational Biophysics Group,
+University of Illinois at Urbana-Champaign, nor the names of its contributors
+may be used to endorse or promote products derived from this Software without
+specific prior written permission.
+
+THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS WITH THE SOFTWARE.
+ ***************************************************************************/
+
+/***************************************************************************
+ * RCS INFORMATION:
+ *
+ *      $RCSfile: vmdplugin.h,v $
+ *      $Author: johns $       $Locker:  $             $State: Exp $
+ *      $Revision: 1.32 $       $Date: 2009/02/24 05:12:35 $
+ *
+ ***************************************************************************/
+
+/** @file
+ * This header must be included by every VMD plugin library.  It defines the
+ * API for every plugin so that VMD can organize the plugins it finds.  
+ */
+
+#ifndef VMD_PLUGIN_H
+#define VMD_PLUGIN_H
+
+
+/* 
+ * Preprocessor tricks to make it easier for us to redefine the names of
+ * functions when building static plugins.
+ */
+#if !defined(VMDPLUGIN)
+/** 
+  * macro defining VMDPLUGIN if it hasn't already been set to the name of 
+  * a static plugin that is being compiled.  This is the catch-all case.
+  */
+#define VMDPLUGIN vmdplugin
+#endif
+/** concatenation macro, joins args x and y together as a single string */
+#define xcat(x, y) cat(x, y)
+/** concatenation macro, joins args x and y together as a single string */
+#define cat(x, y) x ## y 
+
+/*
+ *  macros to correctly define plugin function names depending on whether 
+ *  the plugin is being compiled for static linkage or dynamic loading. 
+ *  When compiled for static linkage, each plugin needs to have unique
+ *  function names for all of its entry points.  When compiled for dynamic
+ *  loading, the plugins must name their entry points consistently so that
+ *  the plugin loading mechanism can find the register, register_tcl, init,
+ *  and fini routines via dlopen() or similar operating system interfaces.
+ */
+/*@{*/
+/** Macro names entry points correctly for static linkage or dynamic loading */
+#define VMDPLUGIN_register     xcat(VMDPLUGIN, _register)
+#define VMDPLUGIN_register_tcl xcat(VMDPLUGIN, _register_tcl)
+#define VMDPLUGIN_init         xcat(VMDPLUGIN, _init)
+#define VMDPLUGIN_fini         xcat(VMDPLUGIN, _fini)
+/*@}*/
+
+
+/** "WIN32" is defined on both WIN32 and WIN64 platforms... */
+#if (defined(WIN32)) 
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#if !defined(STATIC_PLUGIN)
+#if defined(VMDPLUGIN_EXPORTS)
+/** 
+ *  Only define DllMain for plugins, not in VMD or in statically linked plugins
+ *  VMDPLUGIN_EXPORTS is only defined when compiling dynamically loaded plugins
+ */
+BOOL APIENTRY DllMain( HANDLE hModule,
+                       DWORD ul_reason_for_call,
+                       LPVOID lpReserved
+                     )
+{
+  return TRUE;
+}
+
+#define VMDPLUGIN_API __declspec(dllexport)
+#else
+#define VMDPLUGIN_API __declspec(dllimport)
+#endif /* VMDPLUGIN_EXPORTS */
+#else  /* ! STATIC_PLUGIN */
+#define VMDPLUGIN_API
+#endif /* ! STATIC_PLUGIN */
+#else
+/** If we're not compiling on Windows, then this macro is defined empty */
+#define VMDPLUGIN_API 
+#endif
+
+/** define plugin linkage correctly for both C and C++ based plugins */
+#ifdef __cplusplus
+#define VMDPLUGIN_EXTERN extern "C" VMDPLUGIN_API
+#else
+#define VMDPLUGIN_EXTERN extern VMDPLUGIN_API
+#endif  /* __cplusplus */
+
+/* 
+ * Plugin API functions start here 
+ */
+
+
+/** 
+ * Init routine: called the first time the library is loaded by the 
+ * application and before any other API functions are referenced.
+ * Return 0 on success.
+ */
+VMDPLUGIN_EXTERN int VMDPLUGIN_init(void);
+
+/**
+ * Macro for creating a struct header used in all plugin structures.
+ * 
+ * This header should be placed at the top of every plugin API definition 
+ * so that it can be treated as a subtype of the base plugin type.
+ *
+ * abiversion: Defines the ABI for the base plugin type (not for other plugins)
+ * type: A string descriptor of the plugin type.
+ * name: A name for the plugin.
+ * author: A string identifier, possibly including newlines.
+ * Major and minor version.  
+ * is_reentrant: Whether this library can be run concurrently with itself.
+ */
+#define vmdplugin_HEAD \
+  int abiversion; \
+  const char *type; \
+  const char *name; \
+  const char *prettyname; \
+  const char *author; \
+  int majorv; \
+  int minorv; \
+  int is_reentrant
+
+/** 
+  * Typedef for generic plugin header, individual plugins can
+  * make their own structures as long as the header info remains 
+  * the same as the generic plugin header, most easily done by 
+  * using the vmdplugin_HEAD macro.
+  */
+typedef struct {
+  vmdplugin_HEAD;
+} vmdplugin_t;
+
+/**
+ * Use this macro to initialize the abiversion member of each plugin
+ */
+#define vmdplugin_ABIVERSION  16
+
+/*@{*/
+/** Use this macro to indicate a plugin's thread-safety at registration time */
+#define VMDPLUGIN_THREADUNSAFE 0
+#define VMDPLUGIN_THREADSAFE   1
+/*@}*/
+
+/*@{*/
+/** Error return code for use in the plugin registration and init functions */
+#define VMDPLUGIN_SUCCESS      0
+#define VMDPLUGIN_ERROR       (-1)
+/*@}*/
+
+/** 
+ * Function pointer typedef for register callback functions
+ */
+typedef int (*vmdplugin_register_cb)(void *, vmdplugin_t *);
+
+/**
+ * Allow the library to register plugins with the application.
+ * The callback should be called using the passed-in void pointer, which
+ * should not be interpreted in any way by the library.  Each vmdplugin_t
+ * pointer passed to the application should point to statically-allocated
+ * or heap-allocated memory and should never be later modified by the plugin.
+ * Applications must be permitted to retain only a copy of the the plugin
+ * pointer, without making any deep copy of the items in the struct.
+ */
+VMDPLUGIN_EXTERN int VMDPLUGIN_register(void *, vmdplugin_register_cb);
+
+/**
+ * Allow the library to register Tcl extensions.  
+ * This API is optional; if found by dlopen, it will be called after first
+ * calling init and register.  
+ */
+VMDPLUGIN_EXTERN int VMDPLUGIN_register_tcl(void *, void *tcl_interp, 
+    vmdplugin_register_cb);
+
+/**
+ * The Fini method is called when the application will no longer use 
+ * any plugins in the library.  
+ */
+VMDPLUGIN_EXTERN int VMDPLUGIN_fini(void);
+
+#endif   /* VMD_PLUGIN_H */
diff --git a/src/include/gromacs/fft/calcgrid.h b/src/include/gromacs/fft/calcgrid.h
new file mode 100644 (file)
index 0000000..5f20a8d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FFT_CALCGRID_H
+#define GMX_FFT_CALCGRID_H
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+real calcFftGrid(FILE* fp, const matrix box, real gridSpacing, int minGridPointsPerDim, int* nx, int* ny, int* nz);
+/* Sets the number of grid points for each zero n* to the first reasonable
+ * number which gives a spacing equal to or smaller than gridSpacing
+ * and is >= minGridPointsPerDim.
+ * Returns the maximum grid spacing.
+ */
+
+#endif
diff --git a/src/include/gromacs/fft/fft.h b/src/include/gromacs/fft/fft.h
new file mode 100644 (file)
index 0000000..46c5f89
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2003 David van der Spoel, Erik Lindahl, University of Groningen.
+ * Copyright (c) 2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ *  \brief Fast Fourier Transforms.
+ *
+ *  This file provides an abstract Gromacs interface to Fourier transforms,
+ *  including multi-dimensional and real-to-complex transforms.
+ *
+ *  Internally it is implemented as wrappers to external libraries such
+ *  as FFTW or the Intel Math Kernel Library, but we also have a built-in
+ *  version of FFTPACK in case the faster alternatives are unavailable.
+ *
+ *  We also provide our own multi-dimensional transform setups even when
+ *  the underlying library does not support it directly.
+ *
+ * \inpublicapi
+ * \ingroup module_fft
+ */
+#ifndef GMX_FFT_FFT_H
+#define GMX_FFT_FFT_H
+
+#include <stdio.h>
+
+#include "gromacs/math/gmxcomplex.h"
+#include "gromacs/utility/real.h"
+
+/*! \brief Datatype for FFT setup
+ *
+ *  The gmx_fft_t type contains all the setup information, e.g. twiddle
+ *  factors, necessary to perform an FFT. Internally it is mapped to
+ *  whatever FFT library we are using, or the built-in FFTPACK if no fast
+ *  external library is available.
+ *
+ *  Since some of the libraries (e.g. MKL) store work array data in their
+ *  handles this datatype should only be used for one thread at a time, i.e.
+ *  they should allocate one instance each when executing in parallel.
+ */
+typedef struct gmx_fft* gmx_fft_t;
+
+
+/*! \brief Specifier for FFT direction.
+ *
+ *  The definition of the 1D forward transform from input x[] to output y[] is
+ *  \f[
+ *  y_{k} = \sum_{j=0}^{N-1} x_{j} \exp{-i 2 \pi j k /N}
+ *  \f]
+ *
+ *  while the corresponding backward transform is
+ *
+ *  \f[
+ *  y_{k} = \sum_{j=0}^{N-1} x_{j} \exp{i 2 \pi j k /N}
+ *  \f]
+ *
+ *  A forward-backward transform pair will this result in data scaled by N.
+ *
+ *  For complex-to-complex transforms you can only use one of
+ *  GMX_FFT_FORWARD or GMX_FFT_BACKWARD, and for real-complex transforms you
+ *  can only use GMX_FFT_REAL_TO_COMPLEX or GMX_FFT_COMPLEX_TO_REAL.
+ */
+enum gmx_fft_direction
+{
+    GMX_FFT_FORWARD,         /**< Forward complex-to-complex transform  */
+    GMX_FFT_BACKWARD,        /**< Backward complex-to-complex transform */
+    GMX_FFT_REAL_TO_COMPLEX, /**< Real-to-complex valued FFT            */
+    GMX_FFT_COMPLEX_TO_REAL  /**< Complex-to-real valued FFT            */
+};
+
+/*! \brief Specifier for FFT flags.
+ *
+ *  Some FFT libraries (FFTW, in particular) can do timings and other
+ *  tricks to try and optimize the FFT for the current architecture. However,
+ *  this can also lead to results that differ between consecutive runs with
+ *  identical input.
+ *  To avoid this, the conservative flag will attempt to disable such
+ *  optimization, but there are no guarantees since we cannot control what
+ *  the FFT libraries do internally.
+ */
+
+typedef int gmx_fft_flag;
+/** Macro to indicate no special flags for FFT routines. */
+static const int GMX_FFT_FLAG_NONE = 0;
+/** Flag to disable FFT optimizations based on timings, see ::gmx_fft_flag. */
+static const int GMX_FFT_FLAG_CONSERVATIVE = (1 << 0);
+
+/*! \brief Setup a 1-dimensional complex-to-complex transform
+ *
+ *  \param fft    Pointer to opaque Gromacs FFT datatype
+ *  \param nx     Length of transform
+ *  \param flags  FFT options
+ *
+ *  \return status - 0 or a standard error message.
+ *
+ *  \note Since some of the libraries (e.g. MKL) store work array data in their
+ *        handles this datatype should only be used for one thread at a time,
+ *        i.e. you should create one copy per thread when executing in parallel.
+ */
+int gmx_fft_init_1d(gmx_fft_t* fft, int nx, gmx_fft_flag flags);
+
+
+/*! \brief Setup multiple 1-dimensional complex-to-complex transform
+ *
+ *  \param fft    Pointer to opaque Gromacs FFT datatype
+ *  \param nx     Length of transform
+ *  \param howmany Howmany 1D FFT
+ *  \param flags  FFT options
+ *
+ *  \return status - 0 or a standard error message.
+ *
+ *  \note Since some of the libraries (e.g. MKL) store work array data in their
+ *        handles this datatype should only be used for one thread at a time,
+ *        i.e. you should create one copy per thread when executing in parallel.
+ */
+int gmx_fft_init_many_1d(gmx_fft_t* fft, int nx, int howmany, gmx_fft_flag flags);
+
+
+/*! \brief Setup a 1-dimensional real-to-complex transform
+ *
+ *  \param fft    Pointer to opaque Gromacs FFT datatype
+ *  \param nx     Length of transform in real space
+ *  \param flags  FFT options
+ *
+ *  \return status - 0 or a standard error message.
+ *
+ *  \note Since some of the libraries (e.g. MKL) store work array data in their
+ *        handles this datatype should only be used for one thread at a time,
+ *        i.e. you should create one copy per thread when executing in parallel.
+ */
+int gmx_fft_init_1d_real(gmx_fft_t* fft, int nx, gmx_fft_flag flags);
+
+
+/*! \brief Setup multiple 1-dimensional real-to-complex transform
+ *
+ *  \param fft    Pointer to opaque Gromacs FFT datatype
+ *  \param nx     Length of transform in real space
+ *  \param howmany Homany 1D FFTs
+ *  \param flags  FFT options
+ *
+ *  \return status - 0 or a standard error message.
+ *
+ *  \note Since some of the libraries (e.g. MKL) store work array data in their
+ *        handles this datatype should only be used for one thread at a time,
+ *        i.e. you should create one copy per thread when executing in parallel.
+ */
+int gmx_fft_init_many_1d_real(gmx_fft_t* fft, int nx, int howmany, gmx_fft_flag flags);
+
+
+/*! \brief Setup a 2-dimensional real-to-complex transform
+ *
+ *  \param fft    Pointer to opaque Gromacs FFT datatype
+ *  \param nx     Length of transform in first dimension
+ *  \param ny     Length of transform in second dimension
+ *  \param flags  FFT options
+ *
+ *  The normal space is assumed to be real, while the values in
+ *  frequency space are complex.
+ *
+ *  \return status - 0 or a standard error message.
+ *
+ *  \note Since some of the libraries (e.g. MKL) store work array data in their
+ *        handles this datatype should only be used for one thread at a time,
+ *        i.e. you should create one copy per thread when executing in parallel.
+ */
+int gmx_fft_init_2d_real(gmx_fft_t* fft, int nx, int ny, gmx_fft_flag flags);
+
+
+/*! \brief Perform a 1-dimensional complex-to-complex transform
+ *
+ *  Performs an instance of a transform previously initiated.
+ *
+ *  \param setup     Setup returned from gmx_fft_init_1d()
+ *  \param dir       Forward or Backward
+ *  \param in_data   Input grid data. This should be allocated with gmx_new()
+ *                   to make it 16-byte aligned for better performance.
+ *  \param out_data  Output grid data. This should be allocated with gmx_new()
+ *                   to make it 16-byte aligned for better performance.
+ *                   You can provide the same pointer for in_data and out_data
+ *                   to perform an in-place transform.
+ *
+ * \return 0 on success, or an error code.
+ *
+ * \note Data pointers are declared as void, to avoid casting pointers
+ *       depending on your grid type.
+ */
+int gmx_fft_1d(gmx_fft_t setup, enum gmx_fft_direction dir, void* in_data, void* out_data);
+
+
+/*! \brief Perform many 1-dimensional complex-to-complex transforms
+ *
+ *  Performs many instances of a transform previously initiated.
+ *
+ *  \param setup     Setup returned from gmx_fft_init_1d()
+ *  \param dir       Forward or Backward
+ *  \param in_data   Input grid data. This should be allocated with gmx_new()
+ *                   to make it 16-byte aligned for better performance.
+ *  \param out_data  Output grid data. This should be allocated with gmx_new()
+ *                   to make it 16-byte aligned for better performance.
+ *                   You can provide the same pointer for in_data and out_data
+ *                   to perform an in-place transform.
+ *
+ * \return 0 on success, or an error code.
+ *
+ * \note Data pointers are declared as void, to avoid casting pointers
+ *       depending on your grid type.
+ */
+int gmx_fft_many_1d(gmx_fft_t setup, enum gmx_fft_direction dir, void* in_data, void* out_data);
+
+
+/*! \brief Perform a 1-dimensional real-to-complex transform
+ *
+ *  Performs an instance of a transform previously initiated.
+ *
+ *  \param setup     Setup returned from gmx_fft_init_1d_real()
+ *  \param dir       Real-to-complex or complex-to-real
+ *  \param in_data   Input grid data. This should be allocated with gmx_new()
+ *                   to make it 16-byte aligned for better performance.
+ *  \param out_data  Output grid data. This should be allocated with gmx_new()
+ *                   to make it 16-byte aligned for better performance.
+ *                   You can provide the same pointer for in_data and out_data
+ *                   to perform an in-place transform.
+ *
+ * If you are doing an in-place transform, the array must be padded up to
+ * an even integer length so n/2 complex numbers can fit. Out-of-place arrays
+ * should not be padded (although it doesn't matter in 1d).
+ *
+ * \return 0 on success, or an error code.
+ *
+ * \note Data pointers are declared as void, to avoid casting pointers
+ *       depending on transform direction.
+ */
+int gmx_fft_1d_real(gmx_fft_t setup, enum gmx_fft_direction dir, void* in_data, void* out_data);
+
+/*! \brief Perform many 1-dimensional real-to-complex transforms
+ *
+ *  Performs many instances of a transform previously initiated.
+ *
+ *  \param setup     Setup returned from gmx_fft_init_1d_real()
+ *  \param dir       Real-to-complex or complex-to-real
+ *  \param in_data   Input grid data. This should be allocated with gmx_new()
+ *                   to make it 16-byte aligned for better performance.
+ *  \param out_data  Output grid data. This should be allocated with gmx_new()
+ *                   to make it 16-byte aligned for better performance.
+ *                   You can provide the same pointer for in_data and out_data
+ *                   to perform an in-place transform.
+ *
+ * If you are doing an in-place transform, the array must be padded up to
+ * an even integer length so n/2 complex numbers can fit. Out-of-place arrays
+ * should not be padded (although it doesn't matter in 1d).
+ *
+ * \return 0 on success, or an error code.
+ *
+ * \note Data pointers are declared as void, to avoid casting pointers
+ *       depending on transform direction.
+ */
+int gmx_fft_many_1d_real(gmx_fft_t setup, enum gmx_fft_direction dir, void* in_data, void* out_data);
+
+/*! \brief Perform a 2-dimensional real-to-complex transform
+ *
+ *  Performs an instance of a transform previously initiated.
+ *
+ *  \param setup     Setup returned from gmx_fft_init_1d_real()
+ *  \param dir       Real-to-complex or complex-to-real
+ *  \param in_data   Input grid data. This should be allocated with gmx_new()
+ *                   to make it 16-byte aligned for better performance.
+ *  \param out_data  Output grid data. This should be allocated with gmx_new()
+ *                   to make it 16-byte aligned for better performance.
+ *                   You can provide the same pointer for in_data and out_data
+ *                   to perform an in-place transform.
+ *
+ * \return 0 on success, or an error code.
+ *
+ * \note If you are doing an in-place transform, the last dimension of the
+ * array MUST be padded up to an even integer length so n/2 complex numbers can
+ * fit. Thus, if the real grid e.g. has dimension 5*3, you must allocate it as
+ * a 5*4 array, where the last element in the second dimension is padding.
+ * The complex data will be written to the same array, but since that dimension
+ * is 5*2 it will now fill the entire array. Reverse complex-to-real in-place
+ * transformation will produce the same sort of padded array.
+ *
+ * The padding does NOT apply to out-of-place transformation. In that case the
+ * input array will simply be 5*3 of real, while the output is 5*2 of complex.
+ *
+ * \note Data pointers are declared as void, to avoid casting pointers
+ *       depending on transform direction.
+ */
+int gmx_fft_2d_real(gmx_fft_t setup, enum gmx_fft_direction dir, void* in_data, void* out_data);
+
+/*! \brief Release an FFT setup structure
+ *
+ *  Destroy setup and release all allocated memory.
+ *
+ *  \param setup Setup returned from gmx_fft_init_1d(), or one
+ *              of the other initializers.
+ *
+ */
+void gmx_fft_destroy(gmx_fft_t setup);
+
+/*! \brief Release a many FFT setup structure
+ *
+ *  Destroy setup and release all allocated memory.
+ *
+ *  \param setup Setup returned from gmx_fft_init_1d(), or one
+ *              of the other initializers.
+ *
+ */
+void gmx_many_fft_destroy(gmx_fft_t setup);
+
+
+/*! \brief Transpose 2d complex matrix, in-place or out-of-place.
+ *
+ * This routines works when the matrix is non-square, i.e. nx!=ny too,
+ * without allocating an entire matrix of work memory, which is important
+ * for huge FFT grids.
+ *
+ * \param in_data    Input data, to be transposed
+ * \param out_data   Output, transposed data. If this is identical to
+ *                   in_data, an in-place transpose is performed.
+ * \param nx         Number of rows before transpose
+ * \param ny         Number of columns before transpose
+ *
+ * \return GMX_SUCCESS, or an error code from gmx_errno.h
+ */
+int gmx_fft_transpose_2d(t_complex* in_data, t_complex* out_data, int nx, int ny);
+
+/*! \brief Cleanup global data of FFT
+ *
+ *  Any plans are invalid after this function. Should be called
+ *  after all plans have been destroyed.
+ */
+void gmx_fft_cleanup();
+
+#endif
diff --git a/src/include/gromacs/fft/fft5d.h b/src/include/gromacs/fft/fft5d.h
new file mode 100644 (file)
index 0000000..b4e5d00
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009-2017, The GROMACS development team.
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_FFT_FFT5D_H
+#define GMX_FFT_FFT5D_H
+
+#include "config.h"
+
+#ifdef NOGMX
+/*#define GMX_MPI*/
+/*#define GMX_FFT_FFTW3*/
+FILE* debug;
+#endif
+
+#include "gromacs/fft/fft.h"
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/gmxcomplex.h"
+#include "gromacs/utility/gmxmpi.h"
+
+#if !GMX_MPI
+double MPI_Wtime();
+#endif
+
+/*currently only special optimization for FFTE*/
+#if GMX_FFT_FFTW3
+#    include <fftw3.h>
+#endif
+
+#if !GMX_DOUBLE
+#    define FFTW(x) fftwf_##x
+#else
+#    define FFTW(x) fftw_##x
+#endif
+
+#ifdef NOGMX
+struct fft5d_time_t
+{
+    double fft, local, mpi1, mpi2;
+};
+typedef struct fft5d_time_t* fft5d_time;
+#else
+#    include "gromacs/timing/wallcycle.h"
+typedef gmx_wallcycle* fft5d_time;
+#endif
+
+namespace gmx
+{
+enum class PinningPolicy : int;
+} // namespace gmx
+
+typedef enum fft5d_flags_t
+{
+    FFT5D_ORDER_YZ    = 1,
+    FFT5D_BACKWARD    = 2,
+    FFT5D_REALCOMPLEX = 4,
+    FFT5D_DEBUG       = 8,
+    FFT5D_NOMEASURE   = 16,
+    FFT5D_INPLACE     = 32,
+    FFT5D_NOMALLOC    = 64
+} fft5d_flags;
+
+struct fft5d_plan_t
+{
+    t_complex* lin;
+    t_complex *lout, *lout2, *lout3;
+    gmx_fft_t* p1d[3]; /*1D plans*/
+#if GMX_FFT_FFTW3
+    FFTW(plan) p2d; /*2D plan: used for 1D decomposition if FFT supports transposed output*/
+    FFTW(plan) p3d; /*3D plan: used for 0D decomposition if FFT supports transposed output*/
+    FFTW(plan) mpip[2];
+#endif
+    MPI_Comm cart[2];
+
+    int  N[3], M[3], K[3]; /*local length in transposed coordinate system (if not divisisable max)*/
+    int  pN[3], pM[3], pK[3]; /*local length - not max but length for this processor*/
+    int  oM[3], oK[3];        /*offset for current processor*/
+    int *iNin[3], *oNin[3], *iNout[3],
+            *oNout[3]; /*size for each processor (if divisisable=max) for out(=split)
+                          and in (=join) and offsets in transposed coordinate system*/
+    int C[3], rC[3];   /*global length (of the one global axes) */
+    /* C!=rC for real<->complex. then C=rC/2 but with potential padding*/
+    int P[2]; /*size of processor grid*/
+              /*  int fftorder;*/
+              /*  int direction;*/
+              /*  int realcomplex;*/
+    int flags;
+    /*int N0,N1,M0,M1,K0,K1;*/
+    int NG, MG, KG;
+    /*int P[2];*/
+    int                coor[2];
+    int                nthreads;
+    gmx::PinningPolicy pinningPolicy;
+};
+
+typedef struct fft5d_plan_t* fft5d_plan;
+
+void       fft5d_execute(fft5d_plan plan, int thread, fft5d_time times);
+fft5d_plan fft5d_plan_3d(int         N,
+                         int         M,
+                         int         K,
+                         MPI_Comm    comm[2],
+                         int         flags,
+                         t_complex** lin,
+                         t_complex** lin2,
+                         t_complex** lout2,
+                         t_complex** lout3,
+                         int         nthreads,
+                         gmx::PinningPolicy realGridAllocationPinningPolicy = gmx::PinningPolicy::CannotBePinned);
+void       fft5d_local_size(fft5d_plan plan, int* N1, int* M0, int* K0, int* K1, int** coor);
+void       fft5d_destroy(fft5d_plan plan);
+fft5d_plan fft5d_plan_3d_cart(int         N,
+                              int         M,
+                              int         K,
+                              MPI_Comm    comm,
+                              int         P0,
+                              int         flags,
+                              t_complex** lin,
+                              t_complex** lin2,
+                              t_complex** lout2,
+                              t_complex** lout3,
+                              int         nthreads);
+void       fft5d_compare_data(const t_complex* lin, const t_complex* in, fft5d_plan plan, int bothLocal, int normarlize);
+
+#endif
diff --git a/src/include/gromacs/fft/gpu_3dfft.h b/src/include/gromacs/fft/gpu_3dfft.h
new file mode 100644 (file)
index 0000000..729dac1
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Declares the GPU 3D FFT routines.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Gaurav Garg <gaugarg@nvidia.com>
+ *  \ingroup module_fft
+ */
+
+#ifndef GMX_FFT_GPU_3DFFT_H
+#define GMX_FFT_GPU_3DFFT_H
+
+#include <memory>
+
+#include "gromacs/fft/fft.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/utility/gmxmpi.h"
+
+class DeviceContext;
+class DeviceStream;
+
+namespace gmx
+{
+
+template<typename T>
+class ArrayRef;
+
+/*! \internal \brief
+ * Enum specifying all GPU FFT backends supported by GROMACS
+ * Some of the backends support only single GPU, some only multi-node, multi-GPU
+ */
+enum class FftBackend
+{
+    Cufft, // supports only single-GPU
+    Ocl,   // supports only single-GPU
+    HeFFTe_CUDA,
+    SyclMkl,    // supports only single-GPU
+    SyclRocfft, // supports only single-GPU
+    Sycl,       // stubs for not supported configurations
+    Count
+};
+
+/*! \internal \brief
+ * A 3D FFT class for performing R2C/C2R transforms
+ */
+class Gpu3dFft
+{
+public:
+    /*! \brief
+     * Construct 3D FFT object for given backend
+     *
+     * \param[in]  backend                      FFT backend to be instantiated
+     * \param[in]  allocateGrids                True if fft grids are to be allocated, false if pre-allocated
+     * \param[in]  comm                         MPI communicator, used with distributed-FFT backends
+     * \param[in]  gridSizesInXForEachRank      Number of grid points used with each rank in X-dimension
+     * \param[in]  gridSizesInYForEachRank      Number of grid points used with each rank in Y-dimension
+     * \param[in]  nz                           Grid dimension in Z
+     * \param[in]  performOutOfPlaceFFT         Whether the FFT will be performed out-of-place
+     * \param[in]  context                      GPU context.
+     * \param[in]  pmeStream                    GPU stream for PME.
+     * \param[in,out]  realGridSize             Dimensions of the local real grid, out if allocateGrids=true
+     * \param[in,out]  realGridSizePadded       Dimensions of the local real grid with padding, out if allocateGrids=true
+     * \param[in,out]  complexGridSizePadded    Dimensions of the local complex grid with padding, out if allocateGrids=true
+     * \param[in,out]  realGrid                 Device buffer of floats for the local real grid, out if allocateGrids=true
+     * \param[in,out]  complexGrid              Device buffer of complex floats for the local complex grid, out if allocateGrids=true
+     */
+    Gpu3dFft(FftBackend           backend,
+             bool                 allocateGrids,
+             MPI_Comm             comm,
+             ArrayRef<const int>  gridSizesInXForEachRank,
+             ArrayRef<const int>  gridSizesInYForEachRank,
+             int                  nz,
+             bool                 performOutOfPlaceFFT,
+             const DeviceContext& context,
+             const DeviceStream&  pmeStream,
+             ivec                 realGridSize,
+             ivec                 realGridSizePadded,
+             ivec                 complexGridSizePadded,
+             DeviceBuffer<float>* realGrid,
+             DeviceBuffer<float>* complexGrid);
+
+    /*! \brief Destroys the FFT plans. */
+    ~Gpu3dFft();
+    /*! \brief Performs the FFT transform in given direction
+     *
+     * \param[in]  dir           FFT transform direction specifier
+     * \param[out] timingEvent   pointer to the timing event where timing data is recorded
+     */
+    void perform3dFft(gmx_fft_direction dir, CommandEvent* timingEvent);
+
+private:
+    class Impl;
+    class ImplCuFft;
+    class ImplOcl;
+    class ImplSyclMkl;
+    class ImplSyclRocfft;
+    class ImplSycl;
+
+    template<typename backend_tag>
+    class ImplHeFfte;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/fft/gpu_3dfft_cufft.h b/src/include/gromacs/fft/gpu_3dfft_cufft.h
new file mode 100644 (file)
index 0000000..5ebdd16
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Declares the GPU 3D FFT routines.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Gaurav Garg <gaugarg@nvidia.com>
+ *  \ingroup module_fft
+ */
+
+#ifndef GMX_FFT_GPU_3DFFT_CUFFT_H
+#define GMX_FFT_GPU_3DFFT_CUFFT_H
+
+#include <memory>
+
+#include "gromacs/fft/fft.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/utility/gmxmpi.h"
+#include "gpu_3dfft_impl.h"
+
+#include <cufft.h>
+
+class DeviceContext;
+class DeviceStream;
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * A 3D FFT wrapper class for performing R2C/C2R transforms using cuFFT
+ */
+class Gpu3dFft::ImplCuFft : public Gpu3dFft::Impl
+{
+public:
+    //! \copydoc Gpu3dFft::Impl::Impl
+    ImplCuFft(bool                 allocateGrids,
+              MPI_Comm             comm,
+              ArrayRef<const int>  gridSizesInXForEachRank,
+              ArrayRef<const int>  gridSizesInYForEachRank,
+              int                  nz,
+              bool                 performOutOfPlaceFFT,
+              const DeviceContext& context,
+              const DeviceStream&  pmeStream,
+              ivec                 realGridSize,
+              ivec                 realGridSizePadded,
+              ivec                 complexGridSizePadded,
+              DeviceBuffer<float>* realGrid,
+              DeviceBuffer<float>* complexGrid);
+
+    //! \copydoc Gpu3dFft::Impl::~Impl
+    ~ImplCuFft() override;
+
+    //! \copydoc Gpu3dFft::Impl::perform3dFft
+    void perform3dFft(gmx_fft_direction dir, CommandEvent* timingEvent) override;
+
+private:
+    cufftHandle   planR2C_;
+    cufftHandle   planC2R_;
+    cufftReal*    realGrid_;
+    cufftComplex* complexGrid_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/fft/gpu_3dfft_heffte.h b/src/include/gromacs/fft/gpu_3dfft_heffte.h
new file mode 100644 (file)
index 0000000..ebc92e5
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Declares the GPU 3D FFT routines.
+ *  \author Gaurav Garg <gaugarg@nvidia.com>
+ *  \ingroup module_fft
+ */
+
+#ifndef GMX_FFT_GPU_3DFFT_HEFFTE_H
+#define GMX_FFT_GPU_3DFFT_HEFFTE_H
+
+#include <memory>
+
+#include "gromacs/fft/fft.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/utility/gmxmpi.h"
+#include "gpu_3dfft_impl.h"
+
+#include <heffte.h>
+
+class DeviceContext;
+class DeviceStream;
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * A 3D FFT wrapper class for performing R2C/C2R transforms using clFFT
+ */
+template<typename backend_tag>
+class Gpu3dFft::ImplHeFfte : public Gpu3dFft::Impl
+{
+public:
+    //! \copydoc Gpu3dFft::Impl::Impl
+    ImplHeFfte(bool                 allocateGrids,
+               MPI_Comm             comm,
+               ArrayRef<const int>  gridSizesInXForEachRank,
+               ArrayRef<const int>  gridSizesInYForEachRank,
+               int                  nz,
+               bool                 performOutOfPlaceFFT,
+               const DeviceContext& context,
+               const DeviceStream&  pmeStream,
+               ivec                 realGridSize,
+               ivec                 realGridSizePadded,
+               ivec                 complexGridSizePadded,
+               DeviceBuffer<float>* realGrid,
+               DeviceBuffer<float>* complexGrid);
+
+    /*! \brief Destroys the FFT plans. */
+    ~ImplHeFfte() override = default;
+
+    /*! \brief Performs the FFT transform in given direction
+     *
+     * \param[in]  dir           FFT transform direction specifier
+     * \param[out] timingEvent   pointer to the timing event where timing data is recorded
+     */
+    void perform3dFft(gmx_fft_direction dir, CommandEvent* timingEvent) override;
+
+private:
+    heffte::gpu::vector<float>               localRealGrid_;
+    heffte::gpu::vector<std::complex<float>> localComplexGrid_;
+    heffte::gpu::vector<std::complex<float>> workspace_;
+
+    std::unique_ptr<heffte::fft3d_r2c<backend_tag, int>> fftPlan_;
+
+    const DeviceStream& stream_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/fft/gpu_3dfft_impl.h b/src/include/gromacs/fft/gpu_3dfft_impl.h
new file mode 100644 (file)
index 0000000..2e95cc0
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Declares the GPU 3D FFT routines.
+ *
+ *  \author Gaurav Garg <gaugarg@nvidia.com>
+ *  \ingroup module_fft
+ */
+
+#ifndef GMX_FFT_GPU_3DFFT_IMPL_H
+#define GMX_FFT_GPU_3DFFT_IMPL_H
+
+#include "gromacs/fft/fft.h"
+#include "gromacs/fft/gpu_3dfft.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gputraits.h"
+
+
+namespace gmx
+{
+/*! \internal \brief
+ * Impl base class for all FFT backends
+ */
+class Gpu3dFft::Impl
+{
+public:
+    //! Default constructor
+    Impl() = default;
+
+    /*! \brief
+     * Constructs GPU FFT plans for performing 3D FFT on a PME grid.
+     *
+     * \param[in]  allocateGrids                True if fft grids are to be allocated, false if pre-allocated
+     * \param[in]  comm                         MPI communicator, used with distributed-FFT backends
+     * \param[in]  gridSizesInXForEachRank      Number of grid points used with each rank in X-dimension
+     * \param[in]  gridSizesInYForEachRank      Number of grid points used with each rank in Y-dimension
+     * \param[in]  nz                           Grid dimension in Z
+     * \param[in]  performOutOfPlaceFFT         Whether the FFT will be performed out-of-place
+     * \param[in]  context                      GPU context.
+     * \param[in]  pmeStream                    GPU stream for PME.
+     * \param[in,out]  realGridSize             Dimensions of the local real grid, out if allocateGrids=true
+     * \param[in,out]  realGridSizePadded       Dimensions of the local real grid with padding, out if allocateGrids=true
+     * \param[in,out]  complexGridSizePadded    Dimensions of the local complex grid with padding, out if allocateGrids=true
+     * \param[in,out]  realGrid                 Device buffer of floats for the local real grid, out if allocateGrids=true
+     * \param[in,out]  complexGrid              Device buffer of complex floats for the local complex grid, out if allocateGrids=true
+     */
+    Impl(bool                 allocateGrids,
+         MPI_Comm             comm,
+         ArrayRef<const int>  gridSizesInXForEachRank,
+         ArrayRef<const int>  gridSizesInYForEachRank,
+         int                  nz,
+         bool                 performOutOfPlaceFFT,
+         const DeviceContext& context,
+         const DeviceStream&  pmeStream,
+         ivec                 realGridSize,
+         ivec                 realGridSizePadded,
+         ivec                 complexGridSizePadded,
+         DeviceBuffer<float>* realGrid,
+         DeviceBuffer<float>* complexGrid);
+
+    /*! \brief Default destructor */
+    virtual ~Impl() = default;
+
+    //! \copydoc Gpu3dFft::perform3dFft
+    virtual void perform3dFft(gmx_fft_direction dir, CommandEvent* timingEvent) = 0;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/fft/gpu_3dfft_ocl.h b/src/include/gromacs/fft/gpu_3dfft_ocl.h
new file mode 100644 (file)
index 0000000..0ffb042
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Declares the GPU 3D FFT routines.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Gaurav Garg <gaugarg@nvidia.com>
+ *  \ingroup module_fft
+ */
+
+#ifndef GMX_FFT_GPU_3DFFT_OCL_H
+#define GMX_FFT_GPU_3DFFT_OCL_H
+
+#include "gpu_3dfft_impl.h"
+
+#include "gromacs/fft/fft.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxmpi.h"
+
+#include <clFFT.h>
+
+class DeviceContext;
+class DeviceStream;
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * A 3D FFT wrapper class for performing R2C/C2R transforms using clFFT
+ */
+class Gpu3dFft::ImplOcl : public Gpu3dFft::Impl
+{
+public:
+    //! \copydoc Gpu3dFft::Impl::Impl
+    ImplOcl(bool                 allocateGrids,
+            MPI_Comm             comm,
+            ArrayRef<const int>  gridSizesInXForEachRank,
+            ArrayRef<const int>  gridSizesInYForEachRank,
+            int                  nz,
+            bool                 performOutOfPlaceFFT,
+            const DeviceContext& context,
+            const DeviceStream&  pmeStream,
+            ivec                 realGridSize,
+            ivec                 realGridSizePadded,
+            ivec                 complexGridSizePadded,
+            DeviceBuffer<float>* realGrid,
+            DeviceBuffer<float>* complexGrid);
+
+    //! \copydoc Gpu3dFft::Impl::~Impl
+    ~ImplOcl() override;
+
+    //! \copydoc Gpu3dFft::Impl::perform3dFft
+    void perform3dFft(gmx_fft_direction dir, CommandEvent* timingEvent) override;
+
+private:
+    clfftPlanHandle               planR2C_;
+    clfftPlanHandle               planC2R_;
+    std::vector<cl_command_queue> commandStreams_;
+    cl_mem                        realGrid_;
+    cl_mem                        complexGrid_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/fft/gpu_3dfft_sycl.h b/src/include/gromacs/fft/gpu_3dfft_sycl.h
new file mode 100644 (file)
index 0000000..8bc398b
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Declares the GPU 3D FFT routines.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Gaurav Garg <gaugarg@nvidia.com>
+ *  \ingroup module_fft
+ */
+
+#ifndef GMX_FFT_GPU_3DFFT_SYCL_H
+#define GMX_FFT_GPU_3DFFT_SYCL_H
+
+#include "gpu_3dfft_impl.h"
+
+#include "gromacs/fft/fft.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/utility/gmxmpi.h"
+
+class DeviceContext;
+class DeviceStream;
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * A 3D FFT wrapper class for performing R2C/C2R transforms using SYCL. Not yet implemented
+ */
+class Gpu3dFft::ImplSycl : public Gpu3dFft::Impl
+{
+public:
+    //! \copydoc Gpu3dFft::Impl::Impl
+    ImplSycl(bool                 allocateGrids,
+             MPI_Comm             comm,
+             ArrayRef<const int>  gridSizesInXForEachRank,
+             ArrayRef<const int>  gridSizesInYForEachRank,
+             int                  nz,
+             bool                 performOutOfPlaceFFT,
+             const DeviceContext& context,
+             const DeviceStream&  pmeStream,
+             ivec                 realGridSize,
+             ivec                 realGridSizePadded,
+             ivec                 complexGridSizePadded,
+             DeviceBuffer<float>* realGrid,
+             DeviceBuffer<float>* complexGrid);
+
+    //! \copydoc Gpu3dFft::Impl::~Impl
+    ~ImplSycl() override;
+
+    //! \copydoc Gpu3dFft::Impl::perform3dFft
+    void perform3dFft(gmx_fft_direction dir, CommandEvent* timingEvent) override;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/fft/gpu_3dfft_sycl_mkl.h b/src/include/gromacs/fft/gpu_3dfft_sycl_mkl.h
new file mode 100644 (file)
index 0000000..08cd488
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Declares the GPU 3D FFT routines.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Gaurav Garg <gaugarg@nvidia.com>
+ *  \ingroup module_fft
+ */
+
+#ifndef GMX_FFT_GPU_3DFFT_SYCL_MKL_H
+#define GMX_FFT_GPU_3DFFT_SYCL_MKL_H
+
+#include "gpu_3dfft_impl.h"
+
+#include <oneapi/mkl/dfti.hpp>
+
+#include "gromacs/fft/fft.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gmxsycl.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/utility/gmxmpi.h"
+
+class DeviceContext;
+class DeviceStream;
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * A 3D FFT wrapper class for performing R2C/C2R transforms using SYCL + MKL.
+ */
+class Gpu3dFft::ImplSyclMkl : public Gpu3dFft::Impl
+{
+public:
+    //! \copydoc Gpu3dFft::Impl::Impl
+    ImplSyclMkl(bool                 allocateGrids,
+                MPI_Comm             comm,
+                ArrayRef<const int>  gridSizesInXForEachRank,
+                ArrayRef<const int>  gridSizesInYForEachRank,
+                int                  nz,
+                bool                 performOutOfPlaceFFT,
+                const DeviceContext& context,
+                const DeviceStream&  pmeStream,
+                ivec                 realGridSize,
+                ivec                 realGridSizePadded,
+                ivec                 complexGridSizePadded,
+                DeviceBuffer<float>* realGrid,
+                DeviceBuffer<float>* complexGrid);
+
+    //! \copydoc Gpu3dFft::Impl::~Impl
+    ~ImplSyclMkl() override;
+
+    //! \copydoc Gpu3dFft::Impl::perform3dFft
+    void perform3dFft(gmx_fft_direction dir, CommandEvent* timingEvent) override;
+
+private:
+    // Shorthand for the FFT descriptor (a.k.a. plan) type
+    using Descriptor =
+            oneapi::mkl::dft::descriptor<oneapi::mkl::dft::precision::SINGLE, oneapi::mkl::dft::domain::REAL>;
+
+    cl::sycl::buffer<float, 1> realGrid_;
+    cl::sycl::buffer<float, 1> complexGrid_;
+    cl::sycl::queue            queue_;
+    Descriptor                 r2cDescriptor_, c2rDescriptor_;
+
+    static Descriptor initDescriptor(const ivec realGridSize);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/fft/gpu_3dfft_sycl_rocfft.h b/src/include/gromacs/fft/gpu_3dfft_sycl_rocfft.h
new file mode 100644 (file)
index 0000000..9dc428c
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief Declares the GPU 3D FFT routines for hipSYCL via rocFFT.
+ *
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \ingroup module_fft
+ */
+
+#ifndef GMX_FFT_GPU_3DFFT_SYCL_ROCFFT_H
+#define GMX_FFT_GPU_3DFFT_SYCL_ROCFFT_H
+
+#include <memory>
+
+#include "gromacs/fft/fft.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxmpi.h"
+#include "gpu_3dfft_impl.h"
+
+class DeviceContext;
+class DeviceStream;
+
+namespace gmx
+{
+
+/*! \internal \brief A 3D FFT wrapper class for performing R2C/C2R
+ * transforms using rocFFT for hipSYCL targetting ROCm devices.
+ */
+class Gpu3dFft::ImplSyclRocfft : public Gpu3dFft::Impl
+{
+public:
+    //! \copydoc Gpu3dFft::Impl::Impl
+    ImplSyclRocfft(bool                 allocateGrids,
+                   MPI_Comm             comm,
+                   ArrayRef<const int>  gridSizesInXForEachRank,
+                   ArrayRef<const int>  gridSizesInYForEachRank,
+                   int                  nz,
+                   bool                 performOutOfPlaceFFT,
+                   const DeviceContext& context,
+                   const DeviceStream&  pmeStream,
+                   ivec                 realGridSize,
+                   ivec                 realGridSizePadded,
+                   ivec                 complexGridSizePadded,
+                   DeviceBuffer<float>* realGrid,
+                   DeviceBuffer<float>* complexGrid);
+
+    //! \copydoc Gpu3dFft::Impl::~Impl
+    ~ImplSyclRocfft() override;
+
+    //! \copydoc Gpu3dFft::Impl::perform3dFft
+    void perform3dFft(gmx_fft_direction dir, CommandEvent* timingEvent) override;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/fft/parallel_3dfft.h b/src/include/gromacs/fft/parallel_3dfft.h
new file mode 100644 (file)
index 0000000..5652f4c
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2005 David van der Spoel, Erik Lindahl, University of Groningen.
+ * Copyright (c) 2013,2014,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_FFT_PARALLEL_3DFFT_H
+#define GMX_FFT_PARALLEL_3DFFT_H
+
+#include "gromacs/fft/fft.h"
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/gmxcomplex.h"
+#include "gromacs/timing/wallcycle.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxmpi.h"
+#include "gromacs/utility/real.h"
+
+typedef struct gmx_parallel_3dfft* gmx_parallel_3dfft_t;
+
+
+/*! \brief Initialize parallel MPI-based 3D-FFT.
+ *
+ *  This routine performs real-to-complex and complex-to-real parallel 3D FFTs,
+ *  but not complex-to-complex.
+ *
+ *  The routine is optimized for small-to-medium size FFTs used for PME and
+ *  PPPM algorithms, and do allocate extra workspace whenever it might improve
+ *  performance.
+ *
+ *  \param pfft_setup     Pointer to parallel 3dfft setup structure, previously
+ *                        allocated or with automatic storage.
+ *  \param ndata          Number of grid cells in each direction
+ *  \param real_data      Real data. Input for forward and output for backward.
+ *  \param complex_data   Complex data.
+ *  \param comm           MPI communicator for both parallelization axis.
+ *                        Needs to be either initialized or MPI_NULL for
+ *                        no parallelization in that axis.
+ *  \param bReproducible  Try to avoid FFT timing optimizations and other stuff
+ *                        that could make results differ for two runs with
+ *                        identical input (reproducibility for debugging).
+ *  \param nthreads       Run in parallel using n threads
+ *  \param realGridAllocation  Whether to make real grid use allocation pinned for GPU transfers.
+ *                             Only used in PME mixed CPU+GPU mode.
+ *
+ *  \return 0 or a standard error code.
+ */
+int gmx_parallel_3dfft_init(gmx_parallel_3dfft_t* pfft_setup,
+                            const ivec            ndata,
+                            real**                real_data,
+                            t_complex**           complex_data,
+                            MPI_Comm              comm[2],
+                            gmx_bool              bReproducible,
+                            int                   nthreads,
+                            gmx::PinningPolicy realGridAllocation = gmx::PinningPolicy::CannotBePinned);
+
+
+/*! \brief Get direct space grid index limits
+ */
+int gmx_parallel_3dfft_real_limits(gmx_parallel_3dfft_t pfft_setup,
+                                   ivec                 local_ndata,
+                                   ivec                 local_offset,
+                                   ivec                 local_size);
+
+
+/*! \brief Get reciprocal space grid index limits
+ */
+int gmx_parallel_3dfft_complex_limits(gmx_parallel_3dfft_t pfft_setup,
+                                      ivec                 complex_order,
+                                      ivec                 local_ndata,
+                                      ivec                 local_offset,
+                                      ivec                 local_size);
+
+
+int gmx_parallel_3dfft_execute(gmx_parallel_3dfft_t   pfft_setup,
+                               enum gmx_fft_direction dir,
+                               int                    thread,
+                               gmx_wallcycle*         wcycle);
+
+
+/*! \brief Release all data in parallel fft setup
+ *
+ *  All temporary storage and FFT plans are released. The structure itself
+ *  is not released, but the contents is invalid after this call.
+ *
+ *  \param pfft_setup Parallel 3dfft setup.
+ *
+ *  \return 0 or a standard error code.
+ */
+int gmx_parallel_3dfft_destroy(gmx_parallel_3dfft_t pfft_setup);
+
+#endif
diff --git a/src/include/gromacs/fileio/checkpoint.h b/src/include/gromacs/fileio/checkpoint.h
new file mode 100644 (file)
index 0000000..1343b25
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_FILEIO_CHECKPOINT_H
+#define GMX_FILEIO_CHECKPOINT_H
+
+#include <cstdio>
+
+#include <vector>
+
+#include "gromacs/compat/pointers.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxmpi.h"
+#include "gromacs/utility/keyvaluetreebuilder.h"
+
+class energyhistory_t;
+struct gmx_file_position_t;
+struct ObservablesHistory;
+struct t_commrec;
+struct t_fileio;
+struct t_inputrec;
+class t_state;
+struct t_trxframe;
+enum class IntegrationAlgorithm : int;
+enum class SwapType : int;
+enum class LambdaWeightCalculation : int;
+enum class CheckPointVersion : int;
+
+namespace gmx
+{
+
+struct MDModulesNotifiers;
+class KeyValueTreeObject;
+class ReadCheckpointDataHolder;
+class WriteCheckpointDataHolder;
+
+/*! \brief Read to a key-value-tree value used for checkpointing.
+ *
+ * \tparam ValueType
+ *
+ * \param[in] value the value to be checkpointed
+ * \param[in] name name of the value to be checkpointed
+ * \param[in] identifier uniquely identifies the module that is checkpointing
+ *                       typically the module name
+ * \param[in] kvt the key value tree to read from
+ *
+ * \throws InternalError if kvt does not contain requested value.
+ * \note Triggers assertion if value type is not correct.
+ */
+template<typename ValueType>
+void readKvtCheckpointValue(compat::not_null<ValueType*> value,
+                            const std::string&           name,
+                            const std::string&           identifier,
+                            const KeyValueTreeObject&    kvt);
+//! \copydoc readKvtCheckpointValue
+extern template void readKvtCheckpointValue(compat::not_null<std::int64_t*> value,
+                                            const std::string&              name,
+                                            const std::string&              identifier,
+                                            const KeyValueTreeObject&       kvt);
+//! \copydoc readKvtCheckpointValue
+extern template void readKvtCheckpointValue(compat::not_null<real*>   value,
+                                            const std::string&        name,
+                                            const std::string&        identifier,
+                                            const KeyValueTreeObject& kvt);
+
+/*! \brief Write to a key-value-tree used for checkpointing.
+ *
+ * \tparam ValueType
+ *
+ * \param[in] value name of the value to be checkpointed
+ * \param[in] name the value to be checkpointed
+ * \param[in] identifier uniquely identifies the module that is checkpointing
+ *                       typically the module name
+ * \param[in] kvtBuilder the key-value-tree builder used to store the checkpoint values
+ */
+template<typename ValueType>
+void writeKvtCheckpointValue(const ValueType&          value,
+                             const std::string&        name,
+                             const std::string&        identifier,
+                             KeyValueTreeObjectBuilder kvtBuilder);
+//! \copydoc writeKvtCheckpointValue
+extern template void writeKvtCheckpointValue(const std::int64_t&       value,
+                                             const std::string&        name,
+                                             const std::string&        identifier,
+                                             KeyValueTreeObjectBuilder kvtBuilder);
+//! \copydoc writeKvtCheckpointValue
+extern template void writeKvtCheckpointValue(const real&               value,
+                                             const std::string&        name,
+                                             const std::string&        identifier,
+                                             KeyValueTreeObjectBuilder kvtBuilder);
+
+/*! \libinternal
+ * \brief Provides the MDModules with the checkpointed data on the master rank.
+ */
+struct MDModulesCheckpointReadingDataOnMaster
+{
+    //! The data of the MDModules that is stored in the checkpoint file
+    const KeyValueTreeObject& checkpointedData_;
+    //! The version of the read ceckpoint file
+    CheckPointVersion checkpointFileVersion_;
+};
+
+/*! \libinternal
+ * \brief Provides the MDModules with the communication record to broadcast.
+ */
+struct MDModulesCheckpointReadingBroadcast
+{
+    //! The communicator
+    MPI_Comm communicator_;
+    //! Whether the run is executed in parallel
+    bool isParallelRun_;
+    //! The version of the read file version
+    CheckPointVersion checkpointFileVersion_;
+};
+
+/*! \libinternal \brief Writing the MDModules data to a checkpoint file.
+ */
+struct MDModulesWriteCheckpointData
+{
+    //! Builder for the Key-Value-Tree to store the MDModule checkpoint data
+    KeyValueTreeObjectBuilder builder_;
+    //! The version of the read file version
+    CheckPointVersion checkpointFileVersion_;
+};
+
+} // namespace gmx
+
+/* the name of the environment variable to disable fsync failure checks with */
+#define GMX_IGNORE_FSYNC_FAILURE_ENV "GMX_IGNORE_FSYNC_FAILURE"
+
+// TODO Replace this mechanism with std::array<char, 1024> or similar.
+#define CPTSTRLEN 1024
+
+/*! \brief Enum of values that describe the contents of a cpt file
+ * whose format matches a version number
+ *
+ * The enum helps the code be more self-documenting and ensure merges
+ * do not silently resolve when two patches make the same bump. When
+ * adding new functionality, add a new element just above Count
+ * in this enumeration, and write code that does the right thing
+ * according to the value of file_version.
+ */
+enum class CheckPointVersion : int
+{
+    //! Unknown initial version
+    UnknownVersion0,
+    //! Unknown version 1
+    UnknownVersion1,
+    //! Store magic number to validate file.
+    AddMagicNumber,
+    //! Store which sim this is.
+    SafeSimulationPart,
+    //! Store energy data.
+    EkinDataAndFlags,
+    //! Store current step.
+    SafeSteps,
+    //! Cutoff before version 4.5.
+    Version45,
+    //! Unknown version 7.
+    UnknownVersion7,
+    //! Store checksum.
+    FileChecksumAndSize,
+    //! Unknown version 9.
+    UnknownVersion9,
+    //! Safe NH chains for thermostat.
+    NoseHooverThermostat,
+    //! Safe NH chains for barostat.
+    NoseHooverBarostat,
+    //! Safe host info.
+    HostInformation,
+    //! Activate double build.
+    DoublePrecisionBuild,
+    //! Lambda stuff.
+    LambdaStateAndHistory,
+    //! ED stuff.
+    EssentialDynamics,
+    //! Swap state stuff.
+    SwapState,
+    //! AWH history flags added.
+    AwhHistoryFlags,
+    //! Remove functionality that makes mdrun builds non-reproducible.
+    RemoveBuildMachineInformation,
+    //! Allow using COM of previous step as pull group PBC reference.
+    ComPrevStepAsPullGroupReference,
+    //! Added possibility to output average pull force and position.
+    PullAverage,
+    //! Added checkpointing for MDModules.
+    MDModules,
+    //! Added checkpointing for modular simulator.
+    ModularSimulator,
+    //! The total number of checkpoint versions.
+    Count,
+    //! Current version
+    CurrentVersion = Count - 1
+};
+
+
+/*!
+ * \brief
+ * Header explaining the context of a checkpoint file.
+ *
+ * TODO Expand this into being a container of all data for
+ * serialization of a checkpoint, which can be stored by the caller
+ * (e.g. so that mdrun doesn't have to open the checkpoint twice).
+ * This will separate issues of allocation from those of
+ * serialization, help separate comparison from reading, and have
+ * better defined transformation functions to/from trajectory frame
+ * data structures.
+ *
+ * Several fields were once written to checkpoint file headers, but
+ * have been removed. So that old files can continue to be read,
+ * the names of such fields contain the string "_UNUSED" so that it
+ * is clear they should not be used.
+ */
+struct CheckpointHeaderContents
+{
+    //! Version of checkpoint file read from disk.
+    CheckPointVersion file_version;
+    //! Version string.
+    char version[CPTSTRLEN];
+    //! Deprecated string for time.
+    char btime_UNUSED[CPTSTRLEN];
+    //! Deprecated string for user.
+    char buser_UNUSED[CPTSTRLEN];
+    //! Deprecated string for host.
+    char bhost_UNUSED[CPTSTRLEN];
+    //! Value for precision.
+    int double_prec;
+    //! Program string.
+    char fprog[CPTSTRLEN];
+    //! Time string.
+    char ftime[CPTSTRLEN];
+    //! Which integrator is in use.
+    IntegrationAlgorithm eIntegrator;
+    //! Which part of the simulation this is.
+    int simulation_part;
+    //! Which step the checkpoint is at.
+    int64_t step;
+    //! Current simulation time.
+    double t;
+    //! Number of nodes used for simulation,
+    int nnodes;
+    //! Domain decomposition settings?
+    ivec dd_nc;
+    //! Number of separate PME ranks.
+    int npme;
+    //! Number of atoms.
+    int natoms;
+    //! Number of temperature coupling groups.
+    int ngtc;
+    //! Number of Nose-Hoover pressure coupling chains.
+    int nnhpres;
+    //! Length of Nose-Hoover chains.
+    int nhchainlength;
+    //! Current FEP lambda state.
+    int nlambda;
+    //! Current state flags.
+    int flags_state;
+    //! Flags for kinetic energy.
+    int flags_eks;
+    //! Flags for energy history.
+    int flags_enh;
+    //! Flags for pull history.
+    int flagsPullHistory;
+    //! Flags for mystery history.
+    int flags_dfh;
+    //! Flags for AWH history.
+    int flags_awhh;
+    //! Essential dynamics states.
+    int nED;
+    //! Enum for coordinate swapping.
+    SwapType eSwapCoords;
+    //! Whether the checkpoint was written by modular simulator.
+    bool isModularSimulatorCheckpoint = false;
+};
+
+/*! \brief Low-level checkpoint writing function */
+void write_checkpoint_data(t_fileio*                         fp,
+                           CheckpointHeaderContents          headerContents,
+                           gmx_bool                          bExpanded,
+                           LambdaWeightCalculation           elamstats,
+                           t_state*                          state,
+                           ObservablesHistory*               observablesHistory,
+                           const gmx::MDModulesNotifiers&    mdModulesNotifiers,
+                           std::vector<gmx_file_position_t>* outputfiles,
+                           gmx::WriteCheckpointDataHolder*   modularSimulatorCheckpointData);
+
+/* Loads a checkpoint from fn for run continuation.
+ * Generates a fatal error on system size mismatch.
+ * The master node reads the file
+ * and communicates all the modified number of steps,
+ * but not the state itself.
+ * With reproducibilityRequested warns about version, build, #ranks differences.
+ */
+void load_checkpoint(const char*                    fn,
+                     t_fileio*                      logfio,
+                     const t_commrec*               cr,
+                     const ivec                     dd_nc,
+                     t_inputrec*                    ir,
+                     t_state*                       state,
+                     ObservablesHistory*            observablesHistory,
+                     gmx_bool                       reproducibilityRequested,
+                     const gmx::MDModulesNotifiers& mdModulesNotifiers,
+                     gmx::ReadCheckpointDataHolder* modularSimulatorCheckpointData,
+                     bool                           useModularSimulator);
+
+/* Read everything that can be stored in t_trxframe from a checkpoint file */
+void read_checkpoint_trxframe(struct t_fileio* fp, t_trxframe* fr);
+
+/* Print the complete contents of checkpoint file fn to out */
+void list_checkpoint(const char* fn, FILE* out);
+
+/*!\brief Read simulation step and part from a checkpoint file
+ *
+ * Used by tune_pme to handle tuning with a checkpoint file as part of the input.
+ *
+ * \param[in]  filename         Name of checkpoint file
+ * \param[out] simulation_part  The part of the simulation that wrote the checkpoint
+ * \param[out] step             The final step number of the simulation that wrote the checkpoint
+ *
+ * The output variables will both contain 0 if filename is NULL, the file
+ * does not exist, or is not readable. */
+void read_checkpoint_part_and_step(const char* filename, int* simulation_part, int64_t* step);
+
+/*!\brief Return header information from an open checkpoint file.
+ *
+ * Used by mdrun to handle restarts
+ *
+ * \param[in]  fp               Handle to open checkpoint file
+ * \param[out] outputfiles      Container of output file names from the previous run. */
+CheckpointHeaderContents
+read_checkpoint_simulation_part_and_filenames(t_fileio* fp, std::vector<gmx_file_position_t>* outputfiles);
+
+#endif
diff --git a/src/include/gromacs/fileio/enxio.h b/src/include/gromacs/fileio/enxio.h
new file mode 100644 (file)
index 0000000..741d12a
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_ENXIO_H
+#define GMX_FILEIO_ENXIO_H
+
+#include "gromacs/fileio/xdr_datatype.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct SimulationGroups;
+struct t_energy;
+struct t_enxframe;
+struct t_fileio;
+struct t_inputrec;
+class t_state;
+
+/**************************************************************
+ * These are the base datatypes + functions for reading and
+ * writing energy files (.edr). They are either called directly
+ * (as in the processing tools), or indirectly through mdebin.c
+ * during mdrun.
+ *
+ * The routines in the corresponding c-file enxio.c
+ * are based on the lower level routines in gmxfio.c.
+ * The file pointer returned from open_enx
+ * can also be used with the routines in gmxfio.h
+ *
+ **************************************************************/
+
+typedef struct
+{
+    char* name;
+    char* unit;
+} gmx_enxnm_t;
+
+/*
+ * Index for the IDs of additional blocks in the energy file.
+ * Blocks can be added without sacrificing backward and forward
+ * compatibility of the energy files.
+ *
+ * For backward compatibility, the order of these should not be changed.
+ */
+enum
+{
+    enxOR,    /* Time and ensemble averaged data for orientation restraints */
+    enxORI,   /* Instantaneous data for orientation restraints              */
+    enxORT,   /* Order tensor(s) for orientation restraints                 */
+    enxDISRE, /* Distance restraint blocks                                  */
+
+    enxDHCOLL, /* Data about the free energy blocks in this frame.           */
+    enxDHHIST, /* BAR histogram                                              */
+    enxDH,     /* BAR raw delta H data                                       */
+
+    enxAWH, /* AWH data */
+
+    enxNR /* Total number of extra blocks in the current code,
+           * note that the enxio code can read files written by
+           * future code which contain more blocks.
+           */
+};
+
+/* names for the above enum */
+// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
+extern const char* enx_block_id_name[];
+
+
+/* the subblocks that are contained in energy file blocks. Each of these
+   has a number of values of a single data type in a .edr file. */
+struct t_enxsubblock
+{
+    int         nr;   /* number of items in subblock */
+    XdrDataType type; /* the block type */
+
+    /* the values: pointers for each type */
+    float*         fval;
+    double*        dval;
+    int*           ival;
+    int64_t*       lval;
+    unsigned char* cval;
+    char**         sval;
+
+    /* the allocated sizes, defined separately.
+       (nonzero sizes can be free()d later): */
+    int fval_alloc;
+    int dval_alloc;
+    int ival_alloc;
+    int lval_alloc;
+    int cval_alloc;
+    int sval_alloc;
+};
+
+/* the energy file blocks. Each block contains a number of sub-blocks
+   of a single type that contain the actual data. */
+struct t_enxblock
+{
+    int            id;         /* block id, from the enx enums above */
+    int            nsub;       /* number of subblocks */
+    t_enxsubblock* sub;        /* the subblocks */
+    int            nsub_alloc; /* number of allocated subblocks */
+};
+
+/* file handle */
+typedef struct ener_file* ener_file_t;
+
+/*
+ * An energy file is read like this:
+ *
+ * ener_file_t fp;
+ * t_enxframe *fr;
+ *
+ * fp = open_enx(...);
+ * do_enxnms(fp,...);
+ * snew(fr,1);
+ * while (do_enx(fp,fr)) {
+ * ...
+ * }
+ * free_enxframe(fr);
+ * sfree(fr);
+ */
+/* New energy reading and writing interface */
+
+
+/* initialize a pre-allocated frame */
+void init_enxframe(t_enxframe* ef);
+/* delete a frame's memory (except the ef itself) */
+void free_enxframe(t_enxframe* ef);
+
+
+ener_file_t open_enx(const char* fn, const char* mode);
+
+struct t_fileio* enx_file_pointer(const ener_file* ef);
+
+/* Free the contents of ef */
+void close_enx(ener_file_t ef);
+
+/* Free the contents of ef, and ef itself */
+void done_ener_file(ener_file_t ef);
+
+void do_enxnms(ener_file_t ef, int* nre, gmx_enxnm_t** enms);
+
+void free_enxnms(int n, gmx_enxnm_t* nms);
+/* Frees nms and all strings in it */
+
+gmx_bool do_enx(ener_file_t ef, t_enxframe* fr);
+/* Reads enx_frames, memory in fr is (re)allocated if necessary */
+
+void get_enx_state(const char* fn, real t, const SimulationGroups& groups, t_inputrec* ir, t_state* state);
+/*
+ * Reads state variables from enx file fn at time t.
+ * atoms and ir are required for determining which things must be read.
+ * Currently pcoupl and tcoupl state are read from enx.
+ */
+
+
+/* block funtions */
+
+/* allocate n blocks to a frame (if neccesary). Don't touch existing blocks */
+void add_blocks_enxframe(t_enxframe* ef, int n);
+
+/* find a block by id number; if prev!=NULL, it searches from
+   that block's next block.
+   Returns NULL if no block is found with the given id. */
+t_enxblock* find_block_id_enxframe(t_enxframe* ef, int id, t_enxblock* prev);
+
+
+/* allocate n subblocks to a block (if neccesary). Don't touch existing
+   subbblocks. */
+void add_subblocks_enxblock(t_enxblock* eb, int n);
+
+void comp_enx(const char* fn1, const char* fn2, real ftol, real abstol, const char* lastener);
+/* Compare two binary energy files */
+
+#endif
diff --git a/src/include/gromacs/fileio/espio.h b/src/include/gromacs/fileio/espio.h
new file mode 100644 (file)
index 0000000..d888c38
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2005, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_ESPIO_H
+#define GMX_FILEIO_ESPIO_H
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+
+struct t_atoms;
+struct t_symtab;
+
+void gmx_espresso_read_conf(const char* infile,
+                            t_symtab*   symtab,
+                            char**      name,
+                            t_atoms*    atoms,
+                            rvec        x[],
+                            rvec*       v,
+                            matrix      box);
+/* If name is not nullptr, gmx_strdup the title string into
+ * it. Reading a title from espresso format is not , so this will
+ * always be an empty string. */
+
+int get_espresso_coordnum(const char* infile);
+
+void write_espresso_conf_indexed(FILE*          out,
+                                 const char*    title,
+                                 const t_atoms* atoms,
+                                 int            nx,
+                                 const int*     index,
+                                 const rvec*    x,
+                                 const rvec*    v,
+                                 const matrix   box);
+
+#endif
diff --git a/src/include/gromacs/fileio/g96io.h b/src/include/gromacs/fileio/g96io.h
new file mode 100644 (file)
index 0000000..6c421e5
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_G96IO_H
+#define GMX_FILEIO_G96IO_H
+
+#include <stdio.h>
+
+
+struct t_symtab;
+struct t_trxframe;
+
+int read_g96_conf(FILE* fp, const char* infile, char** name, struct t_trxframe* fr, struct t_symtab* symtab, char* line);
+/* read a Gromos96 coordinate or trajectory file,                       *
+ * returns the number of atoms                                          *
+ * sets what's in the frame in info                                     *
+ * read from fp, infile is only needed for error messages               *
+ * nwanted is the number of wanted coordinates,                         *
+ * set this to -1 if you want to know the number of atoms in the file   *
+ * title, atoms, x, v can all be NULL, in which case they won't be read *
+ * line holds the previous line for trajectory reading                  *
+ *
+ * symtab only needs to be valid if fr->atoms is valid
+ *
+ * If name is not nullptr, gmx_strdup the first g96 title string into it. */
+
+void write_g96_conf(FILE* out, const char* title, const t_trxframe* fr, int nindex, const int* index);
+/* write a Gromos96 coordinate file or trajectory frame *
+ * index can be NULL                                    */
+
+#endif
diff --git a/src/include/gromacs/fileio/gmx_internal_xdr.h b/src/include/gromacs/fileio/gmx_internal_xdr.h
new file mode 100644 (file)
index 0000000..2af1d5f
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_FILEIO_GMX_SYSTEM_XDR_H
+#define GMX_FILEIO_GMX_SYSTEM_XDR_H
+
+#include <climits>
+#include <cstdio>
+#include <cstdlib>
+
+/*
+ * This header file is ONLY used on windows systems, since these do
+ * not include the XDR routines present on a unix machine. It will
+ * most probably work on other platforms too, but make sure you
+ * test that the xtc files produced are ok before using it.
+ *
+ * This header file contains Gromacs versions of the definitions for
+ * Sun External Data Representation (XDR) headers and routines.
+ *
+ * On most UNIX systems this is already present as part of your
+ * system libraries, but since we want to make Gromacs portable to
+ * platforms like Microsoft Windows we have created a private version
+ * of the necessary routines and distribute them with the Gromacs source.
+ *
+ * Although the rest of Gromacs is LGPL, you can copy and use the XDR
+ * routines in any way you want as long as you obey Sun's license:
+ *
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+/*
+ * Xdr operations.  XDR_ENCODE causes the type to be encoded into the
+ * stream.  XDR_DECODE causes the type to be extracted from the stream.
+ * XDR_FREE can be used to release the space allocated by an
+ * XDR_DECODE request.
+ */
+
+/* We already have a boolean type in Gromacs, but the XDR library
+ * one has a slightly different name (the calls should be identical).
+ */
+typedef int bool_t;
+
+/*
+ * Aninteger type that is 32 bits wide. Check if int,
+ * long or short is 32 bits and die if none of them is :-)
+ */
+#if (INT_MAX == 2147483647)
+typedef int          xdr_int32_t;
+typedef unsigned int xdr_uint32_t;
+#elif (LONG_MAX == 2147483647L)
+typedef long          xdr_int32_t;
+typedef unsigned long xdr_uint32_t;
+#elif (SHRT_MAX == 2147483647)
+typedef short          xdr_int32_t;
+typedef unsigned short xdr_uint32_t;
+#else
+#    error ERROR: No 32 bit wide integer type found!
+#endif
+
+enum xdr_op
+{
+    XDR_ENCODE = 0,
+    XDR_DECODE = 1,
+    XDR_FREE   = 2
+};
+
+#ifndef FALSE
+#    define FALSE (0)
+#endif
+#ifndef TRUE
+#    define TRUE (1)
+#endif
+
+
+#define BYTES_PER_XDR_UNIT (4)
+/* Macro to round up to units of 4. */
+#define XDR_RNDUP(x) (((x) + BYTES_PER_XDR_UNIT - 1) & ~(BYTES_PER_XDR_UNIT - 1))
+
+
+/*
+ * The XDR handle.
+ * Contains operation which is being applied to the stream,
+ * an operations vector for the particular implementation (e.g. see xdr_mem.c),
+ * and two private fields for the use of the particular implementation.
+ */
+typedef struct XDR XDR;
+struct XDR
+{
+    enum xdr_op x_op; /* operation; fast additional param */
+    struct xdr_ops
+    {
+        bool_t (*x_getbytes)(XDR* __xdrs, char* __addr, unsigned int __len);
+        /* get some bytes from " */
+        bool_t (*x_putbytes)(XDR* __xdrs, char* __addr, unsigned int __len);
+        /* put some bytes to " */
+        unsigned int (*x_getpostn)(XDR* __xdrs);
+        /* returns bytes off from beginning */
+        bool_t (*x_setpostn)(XDR* __xdrs, unsigned int __pos);
+        /* lets you reposition the stream */
+        xdr_int32_t* (*x_inline)(XDR* __xdrs, int __len);
+        /* buf quick ptr to buffered data */
+        void (*x_destroy)(XDR* __xdrs);
+        /* free privates of this xdr_stream */
+        bool_t (*x_getint32)(XDR* __xdrs, xdr_int32_t* __ip);
+        /* get a int from underlying stream */
+        bool_t (*x_putint32)(XDR* __xdrs, xdr_int32_t* __ip);
+        /* put a int to " */
+        bool_t (*x_getuint32)(XDR* __xdrs, xdr_uint32_t* __ip);
+        /* get a unsigned int from underlying stream */
+        bool_t (*x_putuint32)(XDR* __xdrs, xdr_uint32_t* __ip);
+        /* put a int to " */
+    } * x_ops;
+    char* x_public;  /* users' data */
+    char* x_private; /* pointer to private data */
+    char* x_base;    /* private used for position info */
+    int   x_handy;   /* extra private word */
+};
+
+/*
+ * A xdrproc_t exists for each data type which is to be encoded or decoded.
+ *
+ * The second argument to the xdrproc_t is a pointer to an opaque pointer.
+ * The opaque pointer generally points to a structure of the data type
+ * to be decoded.  If this pointer is 0, then the type routines should
+ * allocate dynamic storage of the appropriate size and return it.
+ */
+
+typedef bool_t (*xdrproc_t)(XDR*, void*, ...);
+
+/*
+ * Operations defined on a XDR handle
+ *
+ * XDR          *xdrs;
+ * xdr_int32_t  *int32p;
+ * long         *longp;
+ * char         *addr;
+ * unsigned int  len;
+ * unsigned int  pos;
+ */
+
+
+#define xdr_getint32(xdrs, int32p) (*(xdrs)->x_ops->x_getint32)(xdrs, int32p)
+
+#define xdr_putint32(xdrs, int32p) (*(xdrs)->x_ops->x_putint32)(xdrs, int32p)
+
+#define xdr_getuint32(xdrs, uint32p) (*(xdrs)->x_ops->x_getuint32)(xdrs, uint32p)
+
+#define xdr_putuint32(xdrs, uint32p) (*(xdrs)->x_ops->x_putuint32)(xdrs, uint32p)
+
+#define xdr_getbytes(xdrs, addr, len) (*(xdrs)->x_ops->x_getbytes)(xdrs, addr, len)
+
+#define xdr_putbytes(xdrs, addr, len) (*(xdrs)->x_ops->x_putbytes)(xdrs, addr, len)
+
+#define xdr_getpos(xdrs) (*(xdrs)->x_ops->x_getpostn)(xdrs)
+
+#define xdr_setpos(xdrs, pos) (*(xdrs)->x_ops->x_setpostn)(xdrs, pos)
+
+#define xdr_inline(xdrs, len) (*(xdrs)->x_ops->x_inline)(xdrs, len)
+
+#define xdr_destroy(xdrs)                      \
+    do                                         \
+    {                                          \
+        if ((xdrs)->x_ops->x_destroy)          \
+        {                                      \
+            (*(xdrs)->x_ops->x_destroy)(xdrs); \
+        }                                      \
+    } while (0)
+
+
+bool_t xdr_void();
+bool_t xdr_int(XDR* __xdrs, int* __ip);
+bool_t xdr_u_int(XDR* __xdrs, unsigned int* __ip);
+bool_t xdr_short(XDR* __xdrs, short* __ip);
+bool_t xdr_u_short(XDR* __xdrs, unsigned short* __ip);
+bool_t xdr_bool(XDR* __xdrs, int* __bp);
+bool_t xdr_opaque(XDR* __xdrs, char* __cp, unsigned int __cnt);
+bool_t xdr_string(XDR* __xdrs, char** __cpp, unsigned int __maxsize);
+bool_t xdr_char(XDR* __xdrs, char* __cp);
+bool_t xdr_u_char(XDR* __xdrs, unsigned char* __cp);
+bool_t xdr_vector(XDR* __xdrs, char* __basep, unsigned int __nelem, unsigned int __elemsize, xdrproc_t __xdr_elem);
+bool_t xdr_float(XDR* __xdrs, float* __fp);
+bool_t xdr_double(XDR* __xdrs, double* __dp);
+void   xdrstdio_create(XDR* __xdrs, FILE* __file, enum xdr_op __xop);
+
+/* free memory buffers for xdr */
+void xdr_free(xdrproc_t __proc, char* __objp);
+
+#endif /* GMX_FILEIO_GMX_SYSTEM_XDR_H */
diff --git a/src/include/gromacs/fileio/gmxfio.h b/src/include/gromacs/fileio/gmxfio.h
new file mode 100644 (file)
index 0000000..3472904
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_FILEIO_GMXFIO_H
+#define GMX_FILEIO_GMXFIO_H
+
+#include <stdio.h>
+
+#include <array>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/futil.h"
+#include "gromacs/utility/real.h"
+
+typedef struct t_fileio t_fileio;
+
+/* NOTE ABOUT THREAD SAFETY:
+
+   The functions are all thread-safe, provided that two threads don't
+   do something silly like closing the same file, or one thread
+   accesses a file that has been closed by another.
+ */
+
+/********************************************************
+ * Open and Close
+ ********************************************************/
+
+t_fileio* gmx_fio_open(const char* fn, const char* mode);
+/* Open a new file for reading or writing.
+ * The file type will be deduced from the file name.
+ */
+
+int gmx_fio_close(t_fileio* fp);
+/* Close the file corresponding to fp (if not stdio)
+ * The routine will exit when an invalid fio is handled.
+ * Returns 0 on success.
+ */
+
+int gmx_fio_fp_close(t_fileio* fp);
+/* Close the file corresponding to fp without closing the FIO entry
+ * Needed e.g. for trxio because the FIO entries are used to store
+ * additional data.
+ * NOTE that the fp still needs to be properly closed with gmx_fio_close().
+ * The routine will exit when an invalid fio is handled.
+ * Returns 0 on success.
+ */
+
+
+/* Open a file, return a stream, record the entry in internal FIO object */
+FILE* gmx_fio_fopen(const char* fn, const char* mode);
+
+/* Close a file previously opened with gmx_fio_fopen.
+ * Do not mix these calls with standard fopen/fclose ones!
+ * Returns 0 on success.  */
+int gmx_fio_fclose(FILE* fp);
+
+
+/********************************************************
+ * Change properties of the open file
+ ********************************************************/
+
+char* gmx_fio_getname(t_fileio* fio);
+/* Return the filename corresponding to the fio index */
+
+int gmx_fio_getftp(t_fileio* fio);
+/* Return the filetype corresponding to the fio index.
+    There is as of now no corresponding setftp function because the file
+    was opened as a specific file type and changing that midway is most
+    likely an evil hack. */
+
+gmx_bool gmx_fio_getread(t_fileio* fio);
+/* Return  whether read mode is on in fio  */
+
+/***************************************************
+ * FILE Operations
+ ***************************************************/
+
+void gmx_fio_rewind(t_fileio* fio);
+/* Rewind the file in fio */
+
+int gmx_fio_flush(t_fileio* fio);
+/* Flush the fio, returns 0 on success */
+
+int gmx_fio_fsync(t_fileio* fio);
+/* fsync the fio, returns 0 on success.
+   NOTE: don't use fsync function unless you're absolutely sure you need it
+   because it deliberately interferes with the OS's caching mechanisms and
+   can cause dramatically slowed down IO performance. Some OSes (Linux,
+   for example), may implement fsync as a full sync() point. */
+
+gmx_off_t gmx_fio_ftell(t_fileio* fio);
+/* Return file position if possible */
+
+int gmx_fio_seek(t_fileio* fio, gmx_off_t fpos);
+/* Set file position if possible, quit otherwise */
+
+FILE* gmx_fio_getfp(t_fileio* fio);
+/* Return the file pointer itself */
+
+
+/* Element with information about position in a currently open file.
+ * gmx_off_t should be defined by autoconf if your system does not have it.
+ * If you do not have it on some other platform you do not have largefile
+ * support at all, and you can define it to int (or better, find out how to
+ * enable large files).  */
+struct gmx_file_position_t
+{
+    char                          filename[STRLEN] = { 0 };
+    gmx_off_t                     offset           = 0;
+    std::array<unsigned char, 16> checksum         = { { 0 } };
+    int                           checksumSize     = 0;
+};
+
+/*! \brief Return data about output files.
+ *
+ * This is used for handling data stored in the checkpoint files, so
+ * we can truncate output files upon restart-with-appending. */
+std::vector<gmx_file_position_t> gmx_fio_get_output_file_positions();
+
+t_fileio* gmx_fio_all_output_fsync();
+/* fsync all open output files. This is used for checkpointing, where
+   we need to ensure that all output is actually written out to
+   disk.
+   This is most important in the case of some networked file systems,
+   where data is not synced with the file server until close() or
+   fsync(), so data could remain in cache for days.
+   Note the caveats reported with gmx_fio_fsync().
+
+    returns: NULL if no error occurred, or a pointer to the first file that
+             failed if an error occurred
+ */
+
+
+int gmx_fio_get_file_md5(t_fileio* fio, gmx_off_t offset, std::array<unsigned char, 16>* checksum);
+
+
+int xtc_seek_time(t_fileio* fio, real time, int natoms, gmx_bool bSeekForwardOnly);
+
+
+#endif
diff --git a/src/include/gromacs/fileio/gmxfio_impl.h b/src/include/gromacs/fileio/gmxfio_impl.h
new file mode 100644 (file)
index 0000000..d234cbc
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Internal definitions shared by gmxfio*.c files.
+ */
+#ifndef GMX_FILEIO_GMXFIO_IMPL_H
+#define GMX_FILEIO_GMXFIO_IMPL_H
+
+/* This is the new improved and thread safe version of gmxfio.  */
+
+
+/* WARNING WARNING WARNING WARNING
+   The data types used here are PRIVATE to gmxfio routines. DO NOT use them
+   directly in your own code, but use the external functions provided in
+   include/gmxfio.h
+
+   If you don't heed this warning, your code will suddenly stop working
+   at some point in the not-so-distant future.
+
+   WARNING WARNING WARNING WARNING */
+
+#include "thread_mpi/lock.h"
+
+#include "gromacs/fileio/xdrf.h"
+
+struct t_fileio
+{
+    FILE*    fp;         /* the file pointer */
+    gmx_bool bRead,      /* the file is open for reading */
+            bDouble,     /* write doubles instead of floats */
+            bReadWrite;  /* the file is open for reading and writing */
+    char*       fn;      /* the file name */
+    XDR*        xdr;     /* the xdr data pointer */
+    enum xdr_op xdrmode; /* the xdr mode */
+    int         iFTP;    /* the file type identifier */
+
+    t_fileio *next, *prev; /* next and previous file pointers in the
+                              linked list */
+    tMPI_Lock_t mtx;       /* content locking mutex. This is a fast lock
+                              for performance reasons: in some cases every
+                              single byte that gets read/written requires
+                              a lock */
+};
+
+/** lock the mutex associated with a fio  */
+void gmx_fio_lock(t_fileio* fio);
+/** unlock the mutex associated with a fio  */
+void gmx_fio_unlock(t_fileio* fio);
+
+#endif
diff --git a/src/include/gromacs/fileio/gmxfio_xdr.h b/src/include/gromacs/fileio/gmxfio_xdr.h
new file mode 100644 (file)
index 0000000..defdcf8
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_GMXFIO_XDR_H
+#define GMX_FILEIO_GMXFIO_XDR_H
+
+#include <cstddef>
+
+#include <string>
+
+#include "gromacs/fileio/xdrf.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/iserializer.h"
+#include "gromacs/utility/real.h"
+
+struct t_fileio;
+
+void gmx_fio_setprecision(struct t_fileio* fio, gmx_bool bDouble);
+/* Select the floating point precision for reading and writing files */
+
+bool gmx_fio_is_double(struct t_fileio* fio);
+
+XDR* gmx_fio_getxdr(struct t_fileio* fio);
+/* Return the file pointer itself */
+
+gmx_bool gmx_fio_writee_string(struct t_fileio* fio, const char* item, const char* desc, const char* srcfile, int line);
+
+/* reading or writing, depending on the file's opening mode string */
+gmx_bool gmx_fio_doe_real(struct t_fileio* fio, real* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_float(struct t_fileio* fio, float* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_double(struct t_fileio* fio, double* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_gmx_bool(struct t_fileio* fio, gmx_bool* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_int(struct t_fileio* fio, int* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_int32(struct t_fileio* fio, int32_t* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_int64(struct t_fileio* fio, int64_t* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_uchar(struct t_fileio* fio, unsigned char* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_char(struct t_fileio* fio, char* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_ushort(struct t_fileio* fio,
+                            unsigned short*  item,
+                            const char*      desc,
+                            const char*      srcfile,
+                            int              line);
+gmx_bool gmx_fio_doe_rvec(struct t_fileio* fio, rvec* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_ivec(struct t_fileio* fio, ivec* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_string(struct t_fileio* fio, char* item, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_doe_opaque(struct t_fileio* fio,
+                            char*            item,
+                            std::size_t      size,
+                            const char*      desc,
+                            const char*      srcfile,
+                            int              line);
+
+/* array reading & writing */
+gmx_bool gmx_fio_ndoe_real(struct t_fileio* fio, real* item, int n, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_ndoe_float(struct t_fileio* fio, float* item, int n, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_ndoe_double(struct t_fileio* fio, double* item, int n, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_ndoe_gmx_bool(struct t_fileio* fio,
+                               gmx_bool*        item,
+                               int              n,
+                               const char*      desc,
+                               const char*      srcfile,
+                               int              line);
+gmx_bool gmx_fio_ndoe_int(struct t_fileio* fio, int* item, int n, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_ndoe_int32(struct t_fileio* fio, int32_t* item, int n, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_ndoe_int64(struct t_fileio* fio, int64_t* item, int n, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_ndoe_uchar(struct t_fileio* fio,
+                            unsigned char*   item,
+                            int              n,
+                            const char*      desc,
+                            const char*      srcfile,
+                            int              line);
+gmx_bool gmx_fio_ndoe_char(struct t_fileio* fio, char* item, int n, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_ndoe_ushort(struct t_fileio* fio,
+                             unsigned short*  item,
+                             int              n,
+                             const char*      desc,
+                             const char*      srcfile,
+                             int              line);
+gmx_bool gmx_fio_ndoe_rvec(struct t_fileio* fio, rvec* item, int n, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_ndoe_ivec(struct t_fileio* fio, ivec* item, int n, const char* desc, const char* srcfile, int line);
+gmx_bool gmx_fio_ndoe_string(struct t_fileio* fio, char* item[], int n, const char* desc, const char* srcfile, int line);
+
+
+/* convenience macros */
+#define gmx_fio_write_string(fio, item) \
+    gmx_fio_writee_string(fio, item, (#item), __FILE__, __LINE__)
+
+#define gmx_fio_do_real(fio, item) gmx_fio_doe_real(fio, &(item), (#item), __FILE__, __LINE__)
+#define gmx_fio_do_float(fio, item) gmx_fio_doe_float(fio, &(item), (#item), __FILE__, __LINE__)
+#define gmx_fio_do_double(fio, item) gmx_fio_doe_double(fio, &(item), (#item), __FILE__, __LINE__)
+#define gmx_fio_do_gmx_bool(fio, item) \
+    gmx_fio_doe_gmx_bool(fio, &(item), (#item), __FILE__, __LINE__)
+#define gmx_fio_do_int(fio, item) gmx_fio_doe_int(fio, &(item), (#item), __FILE__, __LINE__)
+#define gmx_fio_do_int32(fio, item) gmx_fio_doe_int32(fio, &(item), (#item), __FILE__, __LINE__)
+#define gmx_fio_do_int64(fio, item) gmx_fio_doe_int64(fio, &(item), (#item), __FILE__, __LINE__)
+#define gmx_fio_do_uchar(fio, item) gmx_fio_doe_uchar(fio, &(item), (#item), __FILE__, __LINE__)
+#define gmx_fio_do_char(fio, item) gmx_fio_doe_char(fio, &(item), (#item), __FILE__, __LINE__)
+#define gmx_fio_do_ushort(fio, item) gmx_fio_doe_ushort(fio, &(item), (#item), __FILE__, __LINE__)
+#define gmx_fio_do_rvec(fio, item) gmx_fio_doe_rvec(fio, &(item), (#item), __FILE__, __LINE__)
+#define gmx_fio_do_ivec(fio, item) gmx_fio_doe_ivec(fio, &(item), (#item), __FILE__, __LINE__)
+#define gmx_fio_do_string(fio, item) gmx_fio_doe_string(fio, item, (#item), __FILE__, __LINE__)
+#define gmx_fio_do_opaque(fio, item, size) \
+    gmx_fio_doe_opaque(fio, item, size, (#item), __FILE__, __LINE__)
+
+
+#define gmx_fio_ndo_real(fio, item, n) gmx_fio_ndoe_real(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_float(fio, item, n) \
+    gmx_fio_ndoe_float(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_double(fio, item, n) \
+    gmx_fio_ndoe_double(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_gmx_bool(fio, item, n) \
+    gmx_fio_ndoe_gmx_bool(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_int(fio, item, n) gmx_fio_ndoe_int(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_int32(fio, item, n) \
+    gmx_fio_ndoe_int32(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_int64(fio, item, n) \
+    gmx_fio_ndoe_int64(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_uchar(fio, item, n) \
+    gmx_fio_ndoe_uchar(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_char(fio, item, n) gmx_fio_ndoe_char(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_ushort(fio, item, n) \
+    gmx_fio_ndoe_ushort(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_rvec(fio, item, n) gmx_fio_ndoe_rvec(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_ivec(fio, item, n) gmx_fio_ndoe_ivec(fio, item, n, (#item), __FILE__, __LINE__)
+#define gmx_fio_ndo_string(fio, item, n) \
+    gmx_fio_ndoe_string(fio, item, n, (#item), __FILE__, __LINE__)
+
+namespace gmx
+{
+/*!\internal \brief
+ * Serializer to read/write XDR data.
+ */
+class FileIOXdrSerializer : public ISerializer
+{
+public:
+    //! Only create with valid file I/O handle.
+    explicit FileIOXdrSerializer(t_fileio* fio);
+
+    //! If file is open in reading mode.
+    bool reading() const override;
+    //! Handle bool I/O.
+    void doBool(bool* value) override;
+    //! Handle unsigned char I/O.
+    void doUChar(unsigned char* value) override;
+    //! Handle char I/O.
+    void doChar(char* value) override;
+    //! Handle unsigned short I/O.
+    void doUShort(unsigned short* value) override;
+    //! Handle default integer I/O.
+    void doInt(int* value) override;
+    //! Handle int32 I/O.
+    void doInt32(int32_t* value) override;
+    //! Handle int64 I/O.
+    void doInt64(int64_t* value) override;
+    //! Handle single precision float I/O.
+    void doFloat(float* value) override;
+    //! Handle double precision float I/O.
+    void doDouble(double* value) override;
+    //! Handle GROMACS floating point number I/O.
+    void doReal(real* value) override;
+    //! Handle I/O of integer vector of size DIM.
+    void doIvec(ivec* value) override;
+    //! Handle I/O of GROMACS real vector of size DIM.
+    void doRvec(rvec* value) override;
+    //! Handle I/O if string.
+    void doString(std::string* value) override;
+    //! Handle opaque data.
+    void doOpaque(char* data, std::size_t size) override;
+    //! Special case for handling I/O of a vector of characters.
+    void doCharArray(char* values, int elements) override;
+    //! Special case for handling I/O of a vector of unsigned characters.
+    void doUCharArray(unsigned char* values, int elements) override;
+    //! Special case for handling I/O of a vector of rvecs.
+    void doRvecArray(rvec* values, int elements) override;
+
+private:
+    //! File I/O handle.
+    t_fileio* fio_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/fileio/groio.h b/src/include/gromacs/fileio/groio.h
new file mode 100644 (file)
index 0000000..c310cf6
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_GROIO_H
+#define GMX_FILEIO_GROIO_H
+
+#include <stdio.h>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_mtop_t;
+struct t_atoms;
+struct t_symtab;
+struct t_trxframe;
+
+void get_coordnum(const char* infile, int* natoms);
+void gmx_gro_read_conf(const char* infile, t_symtab* symtab, char** name, t_atoms* atoms, rvec x[], rvec* v, matrix box);
+/* If name is not nullptr, gmx_strdup the title string into it. */
+
+gmx_bool gro_next_x_or_v(FILE* status, struct t_trxframe* fr);
+int      gro_first_x_or_v(FILE* status, struct t_trxframe* fr);
+/* read first/next x and/or v frame from gro file */
+
+void write_hconf_indexed_p(FILE*          out,
+                           const char*    title,
+                           const t_atoms* atoms,
+                           int            nx,
+                           const int      index[],
+                           const rvec*    x,
+                           const rvec*    v,
+                           const matrix   box);
+
+void write_hconf_mtop(FILE* out, const char* title, const gmx_mtop_t& mtop, const rvec* x, const rvec* v, const matrix box);
+
+void write_hconf_p(FILE* out, const char* title, const t_atoms* atoms, const rvec* x, const rvec* v, const matrix box);
+/* Write a Gromos file with precision ndec: number of decimal places in x,
+ * v has one place more. */
+
+void write_conf_p(const char*    outfile,
+                  const char*    title,
+                  const t_atoms* atoms,
+                  const rvec*    x,
+                  const rvec*    v,
+                  const matrix   box);
+
+#endif
diff --git a/src/include/gromacs/fileio/matio.h b/src/include/gromacs/fileio/matio.h
new file mode 100644 (file)
index 0000000..5c094fe
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_FILEIO_MATIO_H
+#define GMX_FILEIO_MATIO_H
+
+#include <stdio.h>
+
+#include <string>
+#include <vector>
+
+#include "gromacs/fileio/rgb.h"
+#include "gromacs/math/multidimarray.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+/*! \brief Models an XPM element
+ *
+ * \libinternal */
+struct t_xpmelmt
+{
+    //! Should all be non-zero (and printable and not '"')
+    char c1 = 0;
+    /*! \brief Should all be zero (single char color names: smaller xpm's)
+     * or should all be non-zero (double char color names: more colors). */
+    char c2 = 0;
+};
+
+//! Type of a matrix element
+typedef short t_matelmt;
+
+/*! \brief Maps an XPM element to an RGB color and a string description.
+ *
+ * \libinternal */
+struct t_mapping
+{
+    //! XPM element code
+    t_xpmelmt code;
+    //! Description
+    const char* desc = nullptr;
+    //! RGB color
+    t_rgb rgb;
+};
+
+#define MAT_SPATIAL_X (1 << 0)
+#define MAT_SPATIAL_Y (1 << 1)
+/* Defines if x and y are spatial dimensions,
+ * when not, there are n axis ticks at the middle of the elements,
+ * when set, there are n+1 axis ticks at the edges of the elements.
+ */
+
+//! Convenience typedef
+template<typename T>
+using DynamicMatrix2D =
+        gmx::MultiDimArray<std::vector<T>, gmx::extents<gmx::dynamic_extent, gmx::dynamic_extent>>;
+
+/*! \brief A matrix of integers, plus supporting values, such as used in XPM output
+ *
+ * \libinternal
+ *
+ * \todo nx and ny should probably be replaced by operations on the
+ * extent of matrix, but currently there is only limited ability to
+ * iterate over contents of matrix. */
+struct t_matrix
+{
+    //! Defines if x and y are spatial dimensions. See comments on MAT_*.
+    unsigned int flags = 0;
+    //! Size of the matrix in x
+    int nx = 0;
+    //! Size of the matrix in y
+    int ny = 0;
+    //! Matrix title
+    std::string title;
+    //! Label for the continuous legend
+    std::string legend;
+    //! Label for the x-axis
+    std::string label_x;
+    //! Label for the y-axis
+    std::string label_y;
+    //! Whether the quantity described is discrete or continuous.
+    bool bDiscrete = false;
+    //! The x-ticklabels
+    std::vector<real> axis_x;
+    //! The y-ticklabels
+    std::vector<real> axis_y;
+    //! Element x,y is matrix(x, y)
+    DynamicMatrix2D<t_matelmt> matrix;
+    //! Color levels for the output(?)
+    std::vector<t_mapping> map;
+};
+
+//! Seach in the \c map for code \c c and return its entry, or -1 if not found.
+t_matelmt searchcmap(gmx::ArrayRef<const t_mapping> map, t_xpmelmt c);
+
+//! Read the mapping table from fn, return number of entries
+std::vector<t_mapping> readcmap(const char* fn);
+
+void printcmap(FILE* out, int n, t_mapping map[]);
+/* print mapping table to out */
+
+void writecmap(const char* fn, int n, t_mapping map[]);
+/* print mapping table to fn */
+
+//! Reads and returns a number of matrices from .xpm file \c fnm.
+std::vector<t_matrix> read_xpm_matrix(const char* fnm);
+
+real** matrix2real(t_matrix* in, real** out);
+/* Converts an matrix in a t_matrix struct to a matrix of reals
+ * When mat==NULL memory will be allocated
+ * Returns NULL when something went wrong
+ */
+
+void write_xpm_m(FILE* out, t_matrix m);
+/* Writes a t_matrix struct to .xpm file */
+
+void write_xpm3(FILE*              out,
+                unsigned int       flags,
+                const std::string& title,
+                const std::string& legend,
+                const std::string& label_x,
+                const std::string& label_y,
+                int                n_x,
+                int                n_y,
+                real               axis_x[],
+                real               axis_y[],
+                real*              mat[],
+                real               lo,
+                real               mid,
+                real               hi,
+                t_rgb              rlo,
+                t_rgb              rmid,
+                t_rgb              rhi,
+                int*               nlevels);
+/* See write_xpm.
+ * Writes a colormap varying as rlo -> rmid -> rhi.
+ */
+void write_xpm_split(FILE*              out,
+                     unsigned int       flags,
+                     const std::string& title,
+                     const std::string& legend,
+                     const std::string& label_x,
+                     const std::string& label_y,
+                     int                n_x,
+                     int                n_y,
+                     real               axis_x[],
+                     real               axis_y[],
+                     real*              mat[],
+                     real               lo_top,
+                     real               hi_top,
+                     int*               nlevel_top,
+                     t_rgb              rlo_top,
+                     t_rgb              rhi_top,
+                     real               lo_bot,
+                     real               hi_bot,
+                     int*               nlevel_bot,
+                     gmx_bool           bDiscreteColor,
+                     t_rgb              rlo_bot,
+                     t_rgb              rhi_bot);
+/* See write_xpm.
+ * Writes a colormap with separate above and below diagonal colormaps.
+ * If bDiscrete then a colormap with 16 fixed colors is used, first  of
+ * which is white.
+ */
+
+void write_xpm(FILE*              out,
+               unsigned int       flags,
+               const std::string& title,
+               const std::string& legend,
+               const std::string& label_x,
+               const std::string& label_y,
+               int                n_x,
+               int                n_y,
+               real               t_x[],
+               real               t_y[],
+               real*              mat[],
+               real               lo,
+               real               hi,
+               t_rgb              rlo,
+               t_rgb              rhi,
+               int*               nlevels);
+/* out        xpm file
+ * flags      flags, defined types/matrix.h
+ *            MAT_SPATIAL_X
+ *            MAT_SPATIAL_Y
+ *            Defines if x and y are spatial dimensions,
+ *            when not, there are n axis ticks at the middle of the elements,
+ *            when set, there are n+1 axis ticks at the edges of the elements.
+ * title      matrix title
+ * legend     label for the continuous legend
+ * label_x    label for the x-axis
+ * label_y    label for the y-axis
+ * n_x, n_y   size of the matrix
+ * axis_x[]   the x-ticklabels (n_x or n_x+1)
+ * axis_y[]   the y-ticklables (n_y or n_y+1)
+ * *mat[]     element x,y is mat[x][y]
+ * lo         output lower than lo is set to lo
+ * hi         output higher than hi is set to hi
+ * rlo        rgb value for level lo
+ * rhi        rgb value for level hi
+ * nlevels    number of color levels for the output
+ */
+
+real** mk_matrix(int nx, int ny, gmx_bool b1D);
+
+void done_matrix(int nx, real*** m);
+
+#endif /* GMX_FILEIO_MATIO_H */
diff --git a/src/include/gromacs/fileio/md5.h b/src/include/gromacs/fileio/md5.h
new file mode 100644 (file)
index 0000000..945b78b
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2014,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/* This software has been altered by GROMACS for its use, including
+ * the renaming of the functions md5_init, md5_append and md5_finish
+ * to have a gmx_ prefix, and the #include guard md5_INCLUDED to have
+ * a GMX_ prefix (both to avoid name clashes). */
+/*
+   Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
+
+   This software is provided 'as-is', without any express or implied
+   warranty.  In no event will the authors be held liable for any damages
+   arising from the use of this software.
+
+   Permission is granted to anyone to use this software for any purpose,
+   including commercial applications, and to alter it and redistribute it
+   freely, subject to the following restrictions:
+
+   1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+   2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+   3. This notice may not be removed or altered from any source distribution.
+
+   L. Peter Deutsch
+   ghost@aladdin.com
+
+ */
+/*
+   Independent implementation of MD5 (RFC 1321).
+
+   This code implements the MD5 Algorithm defined in RFC 1321, whose
+   text is available at
+    http://www.ietf.org/rfc/rfc1321.txt
+   The code is derived from the text of the RFC, including the test suite
+   (section A.5) but excluding the rest of Appendix A.  It does not include
+   any code or documentation that is identified in the RFC as being
+   copyrighted.
+
+   The original and principal author of md5.h is L. Peter Deutsch
+   <ghost@aladdin.com>.  Other authors are noted in the change history
+   that follows (in reverse chronological order):
+
+   2002-04-13 lpd Removed support for non-ANSI compilers; removed
+    references to Ghostscript; clarified derivation from RFC 1321;
+    now handles byte order either statically or dynamically.
+   1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+   1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+    added conditionalization for C++ compilation from Martin
+    Purschke <purschke@bnl.gov>.
+   1999-05-03 lpd Original version.
+ */
+
+#ifndef GMX_md5_INCLUDED
+#define GMX_md5_INCLUDED
+
+#include <array>
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int  md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s
+{
+    md5_word_t count[2]; /* message length in bits, lsw first */
+    md5_word_t abcd[4];  /* digest buffer */
+    md5_byte_t buf[64];  /* accumulate block */
+} md5_state_t;
+
+/* Initialize the algorithm. */
+void gmx_md5_init(md5_state_t* pms);
+
+/* Append a string to the message. */
+void gmx_md5_append(md5_state_t* pms, const md5_byte_t* data, int nbytes);
+
+/* Finish the message and return the digest. */
+std::array<unsigned char, 16> gmx_md5_finish(md5_state_t* pms);
+
+#endif
diff --git a/src/include/gromacs/fileio/mrcdensitymap.h b/src/include/gromacs/fileio/mrcdensitymap.h
new file mode 100644 (file)
index 0000000..ec1c60a
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declars mrc/ccp4-file format handling.
+ *
+ * \author Christian Blau <blau@kth.se>
+ *
+ * \inlibraryapi
+ * \ingroup module_fileio
+ */
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/math/multidimarray.h"
+#include "gromacs/mdspan/extensions.h"
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+struct MrcDensityMapHeader;
+class ISerializer;
+class TranslateAndScale;
+
+/*! \libinternal \brief Read an mrc/ccp4 file that contains float values.
+ */
+class MrcDensityMapOfFloatReader
+{
+public:
+    /*! \brief Construct from directly de-serializing data into the object.
+     * \throws InternalError if serializer is not reading
+     * \throws InternalError if header is inconsistent
+     * \throws if serializer throws error upon failed reading
+     * \param[in] serializer Serializer to read the object data from
+     */
+    explicit MrcDensityMapOfFloatReader(ISerializer* serializer);
+
+    ~MrcDensityMapOfFloatReader();
+
+    //! Return a view on the data of the density grid
+    ArrayRef<const float> constView() const;
+    //! Return the header
+    const MrcDensityMapHeader& header() const;
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \libinternal \brief Read an mrc density map from a given file.
+ *
+ * Higher level class than MrcDensityMapOfFloatReader that takes a file name
+ * upon construction and returns coordinate transformation into the density
+ * lattice as well as the density data.
+ *
+ * Attempts reading with swapped endianess if header is not sane.
+ *
+ * Performs basic sanity checks on header information and data size.
+ *
+ * \note File reading is completed during construction. When the constructor
+ *       completes succesfully, transformation to density lattice and density
+ *       data are valid, irrespective of the state of the read file.
+ */
+class MrcDensityMapOfFloatFromFileReader
+{
+public:
+    MrcDensityMapOfFloatFromFileReader();
+
+    /*! \brief Read from filename.
+     * \throws FileIOError if file does not exist
+     * \throws FileIOError if read in buffer size does not match file size
+     * \throws FileIOError if header information does not match density
+     *                     data size
+     */
+    explicit MrcDensityMapOfFloatFromFileReader(const std::string& filename);
+
+    ~MrcDensityMapOfFloatFromFileReader();
+
+    //! Return the coordinate transformation into the density
+    TranslateAndScale transformationToDensityLattice() const;
+
+    //! Return a copy of the density data
+    MultiDimArray<std::vector<float>, dynamicExtents3D> densityDataCopy() const;
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \libinternal \brief Write an mrc/ccp4 file that contains float values.
+ */
+class MrcDensityMapOfFloatWriter
+{
+public:
+    /*! \brief Construct by setting the data and the header.
+     *
+     * \throws if the header data description does not match the provided data
+     *
+     * \param[in] header mrc density map header
+     * \param[in] data the density map data
+     */
+    MrcDensityMapOfFloatWriter(const MrcDensityMapHeader& header, ArrayRef<const float> data);
+
+    ~MrcDensityMapOfFloatWriter();
+
+    //! Serialize the mrc density data.
+    void write(ISerializer* serializer) const;
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
diff --git a/src/include/gromacs/fileio/mrcdensitymapheader.h b/src/include/gromacs/fileio/mrcdensitymapheader.h
new file mode 100644 (file)
index 0000000..c6f4713
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Implement mrc/ccp4-file metadata.
+ *
+ * \author Christian Blau <cblau@gwdg.de>
+ *
+ * \inlibraryapi
+ * \ingroup module_fileio
+ */
+#ifndef GMX_FILEIO_MRCDENSITYMAPHEADER_H
+#define GMX_FILEIO_MRCDENSITYMAPHEADER_H
+
+#include <array>
+#include <vector>
+
+#include "gromacs/math/coordinatetransformation.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdspan/extensions.h"
+
+namespace gmx
+{
+
+/*! \brief Space group in three dimensions.
+ *
+ * Currently only "no symmetry" is supported, the complete enum class would hold
+ * 230 symbols.
+ *
+ * Table 12.3.4.1 Standard space-group symbols, pages 824-831,
+ * International Tables for Crystallography, Volume A, fifth edition
+ */
+enum class SpaceGroup : int32_t
+{
+    P1 = 1, //!< no symmetry
+};
+
+/*! \brief The type of density data stored in an mrc file.
+ * As named in
+ * "EMDB Map Distribution Format Description Version 1.01 (c) emdatabank.org 2014"
+ * Modes 0-4 are defined by the standard.
+ * NOTE only mode 2 is currently implemented and used.
+ */
+enum class MrcDataMode : int32_t
+{
+    uInt8   = 0, //!< compressed data mode, 8 bits, signed byte (range -128 to 127, ISO/IEC 10967)
+    int16   = 1, //!< 16 bits, signed integer (range -32768 to 32767, ISO/IEC 10967)
+    float32 = 2, //!< 32 bits, floating point number (IEEE 754)
+    complexInt32   = 3, //!< 32 bits, complex signed integers (ISO/IEC 10967)
+    complexFloat64 = 4, //!< 64 bits, complex floating point numbers (IEEE 754)
+};
+
+/*! \libinternal
+ * \brief Statistics about mrc data arrays.
+ */
+struct MrcDataStatistics
+{
+    float min_ = 0.; //!< Minimum data value scales values in (currently unsupported) compressed data mode.
+    float max_ = 0.; //!< Maximum data value scales values in (currently unsupported) compressed data mode.
+    float mean_ = 0.; //!< mean of the data
+    float rms_  = 0.; //!< rms of the data
+};
+
+/*! \libinternal
+ * \brief Skew matrix and translation.
+ * As named in
+ * "EMDB Map Distribution Format Description Version 1.01 (c) emdatabank.org 2014"
+ */
+struct MrcDensitySkewData
+{
+    bool                        valid_ = false; //!< True if skew matrix is stored.
+    std::array<float, DIM* DIM> matrix_ = {}; //!< Skew matrix for crystallographic unit cell in Ångström
+    std::array<float, DIM> translation_ = {}; //!< Translation of crystallographic unit cell in Ångström
+};
+
+/*! \libinternal
+ * \brief Crystallographic labels for mrc data.
+ */
+struct CrystallographicLabels
+{
+    static constexpr int c_labelSize = 80; //!< Length of crystallographic labels is eighty.
+    //! Number of used crystallographic labels, 0 for imagestacks, 1 for emdb data
+    int32_t numUsedLabels_ = 0;
+
+    //! Crystallographic labels or "::::EMDataBank.org::::EMD-1234::::" for EMDB entries
+    std::array<std::array<unsigned char, c_labelSize>, 10> labels_ = {};
+};
+
+/*! \libinternal
+ * \brief A container for the data in mrc density map file formats.
+ *
+ * Mrc files are a widely used file format in crystallography and cryo electron
+ * microscopy to represent volumetric data. Covers ccp4, map and imod.
+ *
+ * For a detailed description see
+ * "EMDB Map Distribution Format Description Version 1.01 (c) emdatabank.org 2014"
+ */
+struct MrcDensityMapHeader
+{
+
+    //! Space group of stored data.
+    SpaceGroup spaceGroup_ = SpaceGroup::P1;
+    //! Data mode, currently only mode 2 is supported
+    MrcDataMode dataMode_ = MrcDataMode::float32;
+    //! Identifies file format, expected to be "MAP "
+    std::array<unsigned char, 4> formatIdentifier_ = { { 'M', 'A', 'P', ' ' } };
+    //! 15 unspecified float numbers
+    std::array<float, 15> userDefinedFloat_ = {};
+
+    //! Labels for crystallographic data
+    CrystallographicLabels labels_ = {};
+
+    //! Length of the crystallographic unit cell in Ångström
+    std::array<float, DIM> cellLength_ = { { 1., 1., 1. } };
+    //! crystallographic unit cell angles
+    std::array<float, DIM> cellAngles_ = { { 90., 90., 90. } };
+
+    //! Data axis order with columns varying the fastest, and sections the slowest.
+    std::array<int32_t, DIM> columnRowSectionToXyz_ = { { 0, 1, 2 } };
+
+    std::array<int32_t, DIM> numColumnRowSection_   = {}; //!< Column, row and section count
+    std::array<int32_t, DIM> columnRowSectionStart_ = {}; //!< Start of values in grid
+    std::array<int32_t, DIM> extent_ = {}; //!< The number of grid points in the crystall cell
+
+    //! Statistics about the data stored in the file.
+    MrcDataStatistics dataStatistics_ = {};
+    //! Data to perform crystallographic unit cell skewing
+    MrcDensitySkewData skewData_ = {};
+    //! Extended header with symmetry tables
+    std::vector<unsigned char> extendedHeader_ = {};
+};
+
+/*! \brief Return the number of density data items that are expected
+ *         to follow this header.
+ * \throws InternalError if the number of data items cannot be determined
+ * \returns the number of voxels
+ */
+size_t numberOfExpectedDataItems(const MrcDensityMapHeader& header);
+
+/*! \brief Extract the transformation into lattice coordinates.
+ * \note Transformation into lattice coordinates is not treated uniformly
+ *       in different implementations for the mrc format,e.g., vmd, pymol and
+ *       chimera have different conventions. Following the vmd implementation here.
+ *
+ * In determining the density origin coordinates, explicit ORIGIN records
+ * (also called origin2k) in the user defined floats 13 - 15, corresponding to
+ * words 50,51 and 52 in the mrc header, precedence over ColumnRowSectionStart.
+ * Only if above values are zero, using the column, row and section start to
+ * determine the translation vector.
+ *
+ * \param[in] header from which the coordinate transformation is to be extracted
+ * \returns a functor that transforms real space coordinates into the lattice
+ */
+TranslateAndScale getCoordinateTransformationToLattice(const MrcDensityMapHeader& header);
+
+/*! \brief Extract the extents of the density data
+ * \param[in] header from which the extents are to be extracted
+ * \returns density data extents in three dimensions.
+ */
+dynamicExtents3D getDynamicExtents3D(const MrcDensityMapHeader& header);
+
+/*! \brief Checks if the values in the header are sane.
+ *
+ * Checks extents and numbers of columns, rows and sections, as well as unit
+ * cell angles for positivity and to be within bounds.
+ *
+ * Bounds are set generously not to hamper future creative uses of mrc files.
+ *
+ * \returns true if all header values are within resonable albeit generous bounds
+ */
+bool mrcHeaderIsSane(const MrcDensityMapHeader& header);
+
+} // namespace gmx
+#endif /* end of include guard: GMX_FILEIO_MRCDENSITYMAPHEADER_H */
diff --git a/src/include/gromacs/fileio/mrcserializer.h b/src/include/gromacs/fileio/mrcserializer.h
new file mode 100644 (file)
index 0000000..e62a3d7
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Serialization routines for volume data format mrc.
+ *
+ * \author Christian Blau <cblau@gwdg.de>
+ * \ingroup module_fileio
+ */
+
+#ifndef GMX_FILEIO_MRCSERIALIZER_H
+#define GMX_FILEIO_MRCSERIALIZER_H
+namespace gmx
+{
+class ISerializer;
+struct MrcDensityMapHeader;
+
+/*! \brief Serializes an MrcDensityMapHeader from a given serializer.
+ * \param[in] serializer the serializer
+ * \param[in] mrcHeader file header to be serialized
+ */
+void serializeMrcDensityMapHeader(ISerializer* serializer, const MrcDensityMapHeader& mrcHeader);
+/*! \brief Deserializes an MrcDensityMapHeader from a given serializer.
+ * \param[in] serializer the serializer
+ * \returns mrc density map header
+ */
+MrcDensityMapHeader deserializeMrcDensityMapHeader(ISerializer* serializer);
+} // namespace gmx
+#endif /* end of include guard: GMX_FILEIO_MRCSERIALIZER_H */
diff --git a/src/include/gromacs/fileio/mtxio.h b/src/include/gromacs/fileio/mtxio.h
new file mode 100644 (file)
index 0000000..651972c
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/* This module provides routines to read/write sparse or full storage
+ * matrices from/to files. It is normally used for the Hessian matrix
+ * in normal mode analysis.
+ */
+
+#ifndef GMX_FILEIO_MTXIO_H
+#define GMX_FILEIO_MTXIO_H
+
+#include "gromacs/linearalgebra/sparsematrix.h"
+#include "gromacs/utility/real.h"
+
+/* Write a full or sparse matrix to a file.
+ *
+ * You should provide the filename, dimensions (nrow/ncol), and
+ * EITHER a pointer to a full storage matrix or a sparse storage
+ * matrix. If both pointers are non-NULL a fatal error will occur.
+ */
+void gmx_mtxio_write(const char* filename, int nrow, int ncol, real* full_matrix, gmx_sparsematrix_t* sparse_matrix);
+
+
+/* Read a matrix from file.
+ *
+ * This routine will autodetect the matrix format stored in the file
+ * (sparse or full) and set either the full or sparse matrix arguments (ptr to ptr)
+ * to a newly allocated matrix structure. Note that the full storage
+ * structure is simply nrow*ncol floating-point elements. The sparse
+ * matrix structure should be freed with gmx_sparsematrix_destroy() when you are done.
+ *
+ * To determine the format you should set *full_matrix and *sparse_matrix to NULL
+ * before calling this routine, and check which one is non-NULL on return.
+ */
+void gmx_mtxio_read(const char* filename, int* nrow, int* ncol, real** full_matrix, gmx_sparsematrix_t** sparse_matrix);
+
+#endif
diff --git a/src/include/gromacs/fileio/readinp.h b/src/include/gromacs/fileio/readinp.h
new file mode 100644 (file)
index 0000000..3fd3db5
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_READINP_H
+#define GMX_FILEIO_READINP_H
+
+#include <cstring>
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gromacs/fileio/warninp.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class KeyValueTreeObject;
+class TextInputStream;
+class TextOutputStream;
+} // namespace gmx
+
+/* !\brief Input file structure that is populated with entries read from a file.
+ *
+ * This structure contains the information read from the mdp file that is later used
+ * to build the ir from it. It is first constructed with a set of names and values,
+ * and later populated when checking against the available options in readir.
+ * Uses the functions below to both check entries and set the information.
+ */
+struct t_inpfile
+{
+    /*!\brief Minimum allowed constructor sets all elements */
+    t_inpfile(int         count,
+              int         inp_count,
+              bool        bObsolete,
+              bool        bSet,
+              bool        bHandledAsKeyValueTree,
+              std::string name,
+              std::string value) :
+        count_(count),
+        bObsolete_(bObsolete),
+        bSet_(bSet),
+        bHandledAsKeyValueTree_(bHandledAsKeyValueTree),
+        name_(std::move(name)),
+        value_(std::move(value)),
+        inp_count_(inp_count)
+    {
+    }
+    int  count_;                  /* sort order for output  */
+    bool bObsolete_;              /* whether it is an obsolete param value */
+    bool bSet_;                   /* whether it it has been read out */
+    bool bHandledAsKeyValueTree_; /* whether it it has been handled with key-value machinery */
+    std::string name_;            /* name of the parameter */
+    std::string value_;           /* parameter value string */
+    int         inp_count_;       /* number of einps read. Only valid for the first item
+                                                          in the inpfile list. */
+};
+
+/*! \brief Create and return a vector of t_inpfile structs
+ * from "key = value" lines in \c stream corresponding to file \c fn.
+ *
+ * \param[in]  stream          Text stream to read.
+ * \param[in]  fn              Filename corresponding to \c reader.
+ * \param[out] wi              Handler for context-sensitive warnings.
+ * \throws     std::bad_alloc  If out of memory.
+ * \throws     Anything the stream underlying \c reader can throw. */
+std::vector<t_inpfile> read_inpfile(gmx::TextInputStream* stream, const char* fn, warninp_t wi);
+
+gmx::KeyValueTreeObject flatKeyValueTreeFromInpFile(gmx::ArrayRef<const t_inpfile> inp);
+
+enum class WriteMdpHeader
+{
+    no,
+    yes
+};
+
+/*! \brief Write "key = value" lines from \c inp to \c stream.
+ *
+ * \param[in]  stream          Text stream to write.
+ * \param[in]  fn              Filename corresponding to \c stream.
+ * \param[in]  inp             vector of key-value pairs.
+ * \param[in]  bHaltOnUnknown  Whether to issue a fatal error if an unknown key is found.
+ * \param[in]  writeHeader     Whether to write a header recording some context a user might like.
+ * \param[out] wi              Handler for context-sensitive warnings.
+ * \throws     std::bad_alloc  If out of memory.
+ * \throws     Anything the stream underlying \c writer can throw. */
+void write_inpfile(gmx::TextOutputStream*  stream,
+                   const char*             fn,
+                   std::vector<t_inpfile>* inp,
+                   gmx_bool                bHaltOnUnknown,
+                   WriteMdpHeader          writeHeader,
+                   warninp_t               wi);
+/* Write inp to fn, warning (and perhaps halting) if any fields are
+ * unknown. The helpful header contains irreproducible content, so
+ * its writing can be suppressed to make testing more useful. */
+
+void replace_inp_entry(gmx::ArrayRef<t_inpfile> inp, const char* old_entry, const char* new_entry);
+
+int search_einp(gmx::ArrayRef<const t_inpfile> inp, const char* name);
+/* Return the index of an .mdp field with the given name within the
+ * inp vector, if it exists. Return -1 if it does not exist. */
+
+void mark_einp_set(gmx::ArrayRef<t_inpfile> inp, const char* name);
+
+int get_eint(std::vector<t_inpfile>* inp, const char* name, int def, warninp_t wi);
+int get_eint(std::vector<t_inpfile>* inp, const std::string& name, int def, warninp_t wi);
+
+int64_t get_eint64(std::vector<t_inpfile>* inp, const char* name, int64_t def, warninp_t wi);
+int64_t get_eint64(std::vector<t_inpfile>* inp, const std::string& name, int64_t def, warninp_t wi);
+
+double get_ereal(std::vector<t_inpfile>* inp, const char* name, double def, warninp_t wi);
+double get_ereal(std::vector<t_inpfile>* inp, const std::string& name, double def, warninp_t wi);
+
+const char* get_estr(std::vector<t_inpfile>* inp, const char* name, const char* def);
+const char* get_estr(std::vector<t_inpfile>* inp, const std::string& name, const char* def);
+
+int get_eeenum(std::vector<t_inpfile>* inp, const char* name, const char** defs, warninp_t wi);
+/* defs must be NULL terminated */
+int get_eeenum(std::vector<t_inpfile>* inp, const std::string& name, const char** defs, warninp_t wi);
+/* defs must be NULL terminated */
+
+int get_eenum(std::vector<t_inpfile>* inp, const char* name, const char** defs);
+/* defs must be NULL terminated */
+
+//! Get index of option `name`. Exposed here so that `getEnum` can access it.
+int get_einp(std::vector<t_inpfile>* inp, const char* name);
+
+/*! \brief Read option from input and return corresponding enum value
+ *
+ * If the option is not set, return the first value of the enum as default.
+ * Defined here so we don't need to instantiate the templates in the source file.
+ *
+ * \tparam EnumType  The type of enum to be returned
+ * \param[in]  inp   The input file vector
+ * \param[in]  name  The name of the option to be read
+ * \param[out] wi    Handler for context-sensitive warnings.
+ * \return  Enum value corresponding to read input
+ */
+template<typename EnumType>
+EnumType getEnum(std::vector<t_inpfile>* inp, const char* name, warninp* wi)
+{
+    // If there's no valid option, we'll use the EnumType::Default.
+    // Note, this assumes the enum is zero based, which is also assumed by
+    // EnumerationWrapper and EnumerationArray.
+    const auto  defaultEnumValue = EnumType::Default;
+    const auto& defaultName      = enumValueToString(defaultEnumValue);
+    // Get index of option in input
+    const auto ii = get_einp(inp, name);
+    if (ii == -1)
+    {
+        // If the option wasn't set, we return EnumType::Default
+        inp->back().value_.assign(defaultName);
+        return defaultEnumValue;
+    }
+
+    // Check if option string can be mapped to a valid enum value.
+    //
+    // Note that this cannot be replaced with
+    // StringToEnumValueConverter until all instantiations of this
+    // function have a matching enumValueToString, and all of the
+    // latter are in the same namespace. Currently some of those
+    // function declarations are in gmx namespace and some are not.
+    const auto* optionString = (*inp)[ii].value_.c_str();
+    for (auto enumValue : gmx::EnumerationWrapper<EnumType>{})
+    {
+        if (gmx_strcasecmp_min(enumValueToString(enumValue), optionString) == 0)
+        {
+            return enumValue;
+        }
+    }
+
+    // If we get here, the option set is invalid. Print error.
+    std::string errorMessage = gmx::formatString(
+            "Invalid enum '%s' for variable %s, using '%s'\n", optionString, name, defaultName);
+    errorMessage += gmx::formatString("Next time, use one of:");
+    for (auto enumValue : gmx::EnumerationWrapper<EnumType>{})
+    {
+        errorMessage += gmx::formatString(" '%s'", enumValueToString(enumValue));
+    }
+    if (wi != nullptr)
+    {
+        warning_error(wi, errorMessage);
+    }
+    else
+    {
+        fprintf(stderr, "%s\n", errorMessage.c_str());
+    }
+    (*inp)[ii].value_.assign(defaultName);
+    return defaultEnumValue;
+}
+
+
+//! Replace for macro CCTYPE, prints comment string after newline
+void printStringNewline(std::vector<t_inpfile>* inp, const char* line);
+//! Replace for macro CTYPE, prints comment string
+void printStringNoNewline(std::vector<t_inpfile>* inp, const char* line);
+//! Replace for macro STYPE, checks for existing string entry and if possible replaces it
+void setStringEntry(std::vector<t_inpfile>* inp, const char* name, char* newName, const char* def);
+
+/*! \brief
+ * Returns a string value and sets the value in \p inp
+ *
+ * The value is either from \p inp when \p name is found or \p def otherwise.
+ *
+ * \note this is a wrapper function for g_estr()
+ */
+std::string setStringEntry(std::vector<t_inpfile>* inp, const std::string& name, const std::string& def);
+
+#endif
diff --git a/src/include/gromacs/fileio/rgb.h b/src/include/gromacs/fileio/rgb.h
new file mode 100644 (file)
index 0000000..0a273e0
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2011,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_RGB_H
+#define GMX_FILEIO_RGB_H
+
+struct t_rgb
+{
+    double r = 0;
+    double g = 0;
+    double b = 0;
+};
+
+#endif
diff --git a/src/include/gromacs/fileio/timecontrol.h b/src/include/gromacs/fileio/timecontrol.h
new file mode 100644 (file)
index 0000000..651cfbf
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_TIMECONTROL_H
+#define GMX_FILEIO_TIMECONTROL_H
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+/* The code below is to facilitate controlled begin and end of
+ * trajectory reading.
+ */
+enum class TimeControl : int
+{
+    Begin,
+    End,
+    Delta,
+    Count
+};
+
+bool bTimeSet(TimeControl tcontrol);
+
+real rTimeValue(TimeControl tcontrol);
+
+void setTimeValue(TimeControl tcontrol, real value);
+
+#endif
diff --git a/src/include/gromacs/fileio/tngio.h b/src/include/gromacs/fileio/tngio.h
new file mode 100644 (file)
index 0000000..6a33e33
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_FILEIO_TNGIO_H
+#define GMX_FILEIO_TNGIO_H
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_mtop_t;
+struct t_inputrec;
+struct gmx_tng_trajectory;
+typedef struct gmx_tng_trajectory* gmx_tng_trajectory_t;
+struct t_trxframe;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+/*! \brief Open a TNG trajectory file
+ *
+ * \param filename   Name of file to open
+ * \param mode       Can be set to 'r', 'w' or 'a' for reading, writing or appending respectively.
+ * \param tng_data_p Pointer to an allocated gmx_tng_trajectory_t into which a handle to a TNG trajectory will be stored.
+ *
+ * Handles all I/O errors internally via fatal error
+ */
+void gmx_tng_open(const char* filename, char mode, gmx_tng_trajectory_t* tng_data_p);
+
+/*! \brief Finish writing a TNG trajectory file */
+void gmx_tng_close(gmx_tng_trajectory_t* tng);
+
+/*!\brief Add molecular topology information to TNG output (if
+ * available)
+ *
+ * \param tng   Valid handle to a TNG trajectory
+ * \param mtop  Pointer to a topology (can be NULL)
+ */
+void gmx_tng_add_mtop(gmx_tng_trajectory_t tng, const gmx_mtop_t* mtop);
+
+/*! \brief Do all TNG preparation for full-precision whole-system
+ * trajectory writing during MD simulations.
+ *
+ * \param tng   Valid handle to a TNG trajectory
+ * \param mtop  Global topology
+ * \param ir    Input settings (for writing frequencies)
+ */
+void gmx_tng_prepare_md_writing(gmx_tng_trajectory_t tng, const gmx_mtop_t* mtop, const t_inputrec* ir);
+
+/*! \brief Set the default compression precision for TNG writing
+ *
+ * \param tng   Valid handle to a TNG trajectory
+ * \param prec  GROMACS-style precision setting (i.e. 1000 for 3 digits of precision) */
+void gmx_tng_set_compression_precision(gmx_tng_trajectory_t tng, real prec);
+
+/*! \brief Do all TNG preparation for low-precision selection-based
+ * trajectory writing during MD simulations.
+ *
+ * \param tng   Valid handle to a TNG trajectory
+ * \param mtop  Global topology
+ * \param ir    Input settings (for writing frequencies)
+ */
+void gmx_tng_prepare_low_prec_writing(gmx_tng_trajectory_t tng, const gmx_mtop_t* mtop, const t_inputrec* ir);
+
+/*! \brief Write a frame to a TNG file
+ *
+ * \param tng                  Valid handle to a TNG trajectory
+ * \param bUseLossyCompression Whether to use lossy compression
+ * \param step                 MD step number
+ * \param elapsedPicoSeconds   Elapsed MD time
+ * \param lambda               Free-energy lambda value
+ * \param box                  Simulation box
+ * \param nAtoms               Number of atoms (i.e. vector lengths)
+ * \param x                    Vector of position coordinates
+ * \param v                    Vector of elocities
+ * \param f                    Vector of forces
+ *
+ * The pointers tng, x, v, f may be NULL, which triggers not writing
+ * (that component). box can only be NULL if x is also NULL. */
+void gmx_fwrite_tng(gmx_tng_trajectory_t tng,
+                    gmx_bool             bUseLossyCompression,
+                    int64_t              step,
+                    real                 elapsedPicoSeconds,
+                    real                 lambda,
+                    const rvec*          box,
+                    int                  nAtoms,
+                    const rvec*          x,
+                    const rvec*          v,
+                    const rvec*          f);
+
+/*! \brief Write the current frame set to disk. Perform compression
+ * etc.
+ *
+ * \param tng Valid handle to a TNG trajectory
+ */
+void fflush_tng(gmx_tng_trajectory_t tng);
+
+/*! \brief Get the time (in picoseconds) of the final frame in the
+ * trajectory.
+ *
+ * \param tng Valid handle to a TNG trajectory
+ */
+float gmx_tng_get_time_of_final_frame(gmx_tng_trajectory_t tng);
+
+/*! \brief Prepare to write TNG output from trajectory conversion tools */
+void gmx_prepare_tng_writing(const char*              filename,
+                             char                     mode,
+                             gmx_tng_trajectory_t*    in,
+                             gmx_tng_trajectory_t*    out,
+                             int                      nAtoms,
+                             const struct gmx_mtop_t* mtop,
+                             gmx::ArrayRef<const int> index,
+                             const char*              indexGroupName);
+
+/*! \brief Write a trxframe to a TNG file
+ *
+ * \param output Trajectory to write to
+ * \param frame  Frame data to write
+ * \param natoms Number of atoms to actually write
+ *
+ * The natoms field in frame is the number of atoms in the system. The
+ * parameter natoms supports writing an index-group subset of the
+ * atoms.
+ */
+void gmx_write_tng_from_trxframe(gmx_tng_trajectory_t output, const t_trxframe* frame, int natoms);
+
+/*! \brief Creates a molecule containing only the indexed atoms and sets
+ * the number of all other molecules to 0. Works similar to a
+ * selection group. */
+void gmx_tng_setup_atom_subgroup(gmx_tng_trajectory_t tng, gmx::ArrayRef<const int> ind, const char* name);
+
+/*! \brief Read the first/next TNG frame. */
+gmx_bool gmx_read_next_tng_frame(gmx_tng_trajectory_t input,
+                                 struct t_trxframe*   fr,
+                                 int64_t*             requestedIds,
+                                 int                  numRequestedIds);
+
+/*! \brief Print the molecule system to stream */
+void gmx_print_tng_molecule_system(gmx_tng_trajectory_t input, FILE* stream);
+
+/*! \brief Get a list of block IDs present in the next frame with data. */
+gmx_bool gmx_get_tng_data_block_types_of_next_frame(gmx_tng_trajectory_t input,
+                                                    int                  frame,
+                                                    int                  nRequestedIds,
+                                                    int64_t*             requestedIds,
+                                                    int64_t*             nextFrame,
+                                                    int64_t*             nBlocks,
+                                                    int64_t**            blockIds);
+
+/*! \brief Get data of the next frame with data from the data block
+ * with the specified block ID. */
+gmx_bool gmx_get_tng_data_next_frame_of_block_type(gmx_tng_trajectory_t input,
+                                                   int64_t              blockId,
+                                                   real**               values,
+                                                   int64_t*             frameNumber,
+                                                   double*              frameTime,
+                                                   int64_t*             nValuesPerFrame,
+                                                   int64_t*             nAtoms,
+                                                   real*                prec,
+                                                   char*                name,
+                                                   int                  maxLen,
+                                                   gmx_bool*            bOK);
+
+/*! \brief Get the output interval of box size.
+ *
+ * \return The box output interval, or -1 when TNG support is not available. */
+int gmx_tng_get_box_output_interval(gmx_tng_trajectory_t gmx_tng);
+
+/*! \brief Get the output interval of lambda.
+ *
+ * \return The box output interval, or -1 when TNG support is not available. */
+int gmx_tng_get_lambda_output_interval(gmx_tng_trajectory_t gmx_tng);
+
+#endif /* GMX_FILEIO_TNGIO_H */
diff --git a/src/include/gromacs/fileio/trrio.h b/src/include/gromacs/fileio/trrio.h
new file mode 100644 (file)
index 0000000..6a551c0
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_TRRIO_H
+#define GMX_FILEIO_TRRIO_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+/**************************************************************
+ *
+ * These routines handle trr (trajectory) I/O, they read and
+ * write trr files. The routines should be able to read single
+ * and double precision files without the user noting it.
+ * The files are backward compatible, therefore the header holds
+ * some unused variables.
+ *
+ * The routines in the corresponding c-file trrio.cpp
+ * are based on the lower level routines in gmxfio.cpp
+ * The file handle returned from gmx_trr_open()
+ * can also be used with the routines in gmxfio.h
+ *
+ * Note that TRR was designed to represent a step number as a default
+ * integer, which depends on the implementation, but is typically and
+ * 32 bit. We didn't design the format to be extensible, so we can't
+ * fix the fact that after 2^31 frames, step numbers will wrap to be
+ * negative. Fortunately, this tends not to cause serious problems,
+ * and we've fixed it in TNG. Meanwhile, the implementation pretends
+ * to the rest of GROMACS that it functions with int64_t like all
+ * other step numbers, but the actual range in practice depends on the
+ * defaults of the implementation in use now (or when the file was
+ * written).
+ *
+ **************************************************************/
+
+struct t_fileio;
+
+/* This struct describes the order and the  */
+/* sizes of the structs in a trr file, sizes are given in bytes. */
+typedef struct gmx_trr_header_t
+{
+    gmx_bool bDouble;   /* Double precision?                   */
+    int      ir_size;   /* Backward compatibility              */
+    int      e_size;    /* Backward compatibility              */
+    int      box_size;  /* Non zero if a box is present        */
+    int      vir_size;  /* Backward compatibility              */
+    int      pres_size; /* Backward compatibility              */
+    int      top_size;  /* Backward compatibility              */
+    int      sym_size;  /* Backward compatibility              */
+    int      x_size;    /* Non zero if coordinates are present */
+    int      v_size;    /* Non zero if velocities are present  */
+    int      f_size;    /* Non zero if forces are present      */
+
+    int     natoms;    /* The total number of atoms           */
+    int64_t step;      /* Current step number                 */
+    int     nre;       /* Backward compatibility              */
+    real    t;         /* Current time                        */
+    real    lambda;    /* Current value of lambda             */
+    int     fep_state; /* Current value of alchemical state   */
+} gmx_trr_header_t;
+
+struct t_fileio* gmx_trr_open(const char* fn, const char* mode);
+/* Open a trr file */
+
+void gmx_trr_close(struct t_fileio* fio);
+/* Close it */
+
+gmx_bool gmx_trr_read_frame_header(struct t_fileio* fio, gmx_trr_header_t* header, gmx_bool* bOK);
+/* Read the header of a trr file. Return FALSE if there is no frame.
+ * bOK will be FALSE when the header is incomplete.
+ */
+
+gmx_bool gmx_trr_read_frame_data(struct t_fileio* fio, gmx_trr_header_t* sh, rvec* box, rvec* x, rvec* v, rvec* f);
+/* Extern read a frame except the header (that should be pre-read,
+ * using routine gmx_trr_read_frame_header(), see above) from a trr file.
+ * Return FALSE on error
+ */
+
+gmx_bool gmx_trr_read_frame(struct t_fileio* fio,
+                            int64_t*         step,
+                            real*            t,
+                            real*            lambda,
+                            rvec*            box,
+                            int*             natoms,
+                            rvec*            x,
+                            rvec*            v,
+                            rvec*            f);
+/* Read a trr frame, including the header from fp. box, x, v, f may
+ * be NULL, in which case the data will be skipped over.
+ * return FALSE on error
+ */
+
+void gmx_trr_write_frame(struct t_fileio* fio,
+                         int64_t          step,
+                         real             t,
+                         real             lambda,
+                         const rvec*      box,
+                         int              natoms,
+                         const rvec*      x,
+                         const rvec*      v,
+                         const rvec*      f);
+/* Write a trr frame to file fp, box, x, v, f may be NULL */
+
+void gmx_trr_read_single_header(const char* fn, gmx_trr_header_t* header);
+/* Read the header of a trr file from fn, and close the file afterwards.
+ */
+
+void gmx_trr_read_single_frame(const char* fn,
+                               int64_t*    step,
+                               real*       t,
+                               real*       lambda,
+                               rvec*       box,
+                               int*        natoms,
+                               rvec*       x,
+                               rvec*       v,
+                               rvec*       f);
+/* Read a single trr frame from file fn, which is closed afterwards
+ */
+
+void gmx_trr_write_single_frame(const char* fn,
+                                int64_t     step,
+                                real        t,
+                                real        lambda,
+                                const rvec* box,
+                                int         natoms,
+                                const rvec* x,
+                                const rvec* v,
+                                const rvec* f);
+/* Write a single trr frame to file fn, which is closed afterwards */
+
+
+#endif
diff --git a/src/include/gromacs/fileio/vmdio.h b/src/include/gromacs/fileio/vmdio.h
new file mode 100644 (file)
index 0000000..f10712b
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2016,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_VMDIO_H
+#define GMX_FILEIO_VMDIO_H
+
+#include "external/vmd_molfile/molfile_plugin.h"
+
+#include "gromacs/utility/basedefinitions.h"
+
+struct t_trxframe;
+
+struct gmx_vmdplugin_t
+{
+    molfile_plugin_t* api;
+    const char*       filetype;
+    void*             handle;
+    gmx_bool          bV;
+};
+
+int      read_first_vmd_frame(const char* fn, gmx_vmdplugin_t** vmdpluginp, t_trxframe* fr);
+gmx_bool read_next_vmd_frame(gmx_vmdplugin_t* vmdplugin, t_trxframe* fr);
+
+#endif
diff --git a/src/include/gromacs/fileio/warninp.h b/src/include/gromacs/fileio/warninp.h
new file mode 100644 (file)
index 0000000..15b71f0
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_WARNINP_H
+#define GMX_FILEIO_WARNINP_H
+
+#include <string>
+
+#include "gromacs/utility/basedefinitions.h"
+
+/* Abstract type for warning bookkeeping */
+typedef struct warninp* warninp_t;
+
+
+warninp_t init_warning(gmx_bool bAllowWarnings, int maxwarning);
+/* Initialize the warning data structure.
+ * If bAllowWarnings=FALSE, all warnings (calls to warning()) will be
+ * transformed into errors, calls to warning_note still produce notes.
+ * maxwarning determines the maximum number of warnings that are allowed
+ * for proceeding. When this number is exceeded check_warning_error
+ * and done_warning will generate a fatal error.
+ * bAllowWarnings=TRUE should only be used by programs that have
+ * a -maxwarn command line option.
+ */
+
+void set_warning_line(warninp_t wi, const char* fn, int line);
+/* Set filename and linenumber for the warning */
+
+int get_warning_line(warninp_t wi);
+/* Get linenumber for the warning */
+
+
+const char* get_warning_file(warninp_t wi);
+/* Get filename for the warning */
+
+void warning(warninp_t wi, const char* s);
+/* Issue a warning, with the string s. If s == NULL, then warn_buf
+ * will be printed instead. The file and line set by set_warning_line
+ * are printed, nwarn_warn (local) is incremented.
+ * A fatal error will be generated after processing the input
+ * when nwarn_warn is larger than maxwarning passed to init_warning.
+ * So warning should only be called for issues that should be resolved,
+ * otherwise warning_note should be called.
+ */
+//! Convenience wrapper.
+void warning(warninp_t wi, const std::string& s);
+
+void warning_note(warninp_t wi, const char* s);
+/* Issue a note, with the string s. If s == NULL, then warn_buf
+ * will be printed instead. The file and line set by set_warning_line
+ * are printed, nwarn_note (local) is incremented.
+ * This is for issues which could be a problem for some systems,
+ * but 100% ok for other systems.
+ */
+
+//! Convenience wrapper.
+void warning_note(warninp_t wi, const std::string& s);
+
+void warning_error(warninp_t wi, const char* s);
+/* Issue an error, with the string s. If s == NULL, then warn_buf
+ * will be printed instead. The file and line set by set_warning_line
+ * are printed, nwarn_error (local) is incremented.
+ */
+
+//! Convenience wrapper.
+void warning_error(warninp_t wi, const std::string& s);
+
+/*! \brief Issue an error with warning_error() and prevent further
+ * processing by calling check_warning_error().
+ *
+ * This is intended for use where there is no way to produce a data
+ * structure that would prevent execution from segfaulting. */
+[[noreturn]] void warning_error_and_exit(warninp_t wi, const char* s, int f_errno, const char* file, int line);
+//! \copydoc warning_error_and_exit(warninp_t,const char *,int,const char *,int)
+[[noreturn]] void
+warning_error_and_exit(warninp_t wi, const std::string& s, int f_errno, const char* file, int line);
+
+gmx_bool warning_errors_exist(warninp_t wi);
+/* Return whether any error-level warnings were issued to wi. */
+
+//! Resets the count for all kinds of warnings to zero.
+void warning_reset(warninp_t wi);
+
+void check_warning_error(warninp_t wi, int f_errno, const char* file, int line);
+/* When warning_error has been called at least once gmx_fatal is called,
+ * otherwise does nothing.
+ */
+
+void done_warning(warninp_t wi, int f_errno, const char* file, int line);
+/* Should be called when finished processing the input file.
+ * Prints the number of notes and warnings
+ * and generates a fatal error when errors were found or too many
+ * warnings were generatesd.
+ * Frees the data structure pointed to by wi.
+ */
+
+void free_warning(warninp_t wi);
+/* Frees the data structure pointed to by wi. */
+
+void too_few_function(warninp_t wi, const char* fn, int line);
+#define too_few(wi) too_few_function(wi, __FILE__, __LINE__)
+/* Issue a warning stating 'Too few parameters' */
+
+void incorrect_n_param_function(warninp_t wi, const char* fn, int line);
+#define incorrect_n_param(wi) incorrect_n_param_function(wi, __FILE__, __LINE__)
+/* Issue a warning stating 'Incorrect number of parameters' */
+
+#endif
diff --git a/src/include/gromacs/fileio/writeps.h b/src/include/gromacs/fileio/writeps.h
new file mode 100644 (file)
index 0000000..d0518b6
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_WRITEPS_H
+#define GMX_FILEIO_WRITEPS_H
+
+#include <cstdio>
+
+#include <string>
+#include <vector>
+
+#include "gromacs/fileio/rgb.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+/* TODO: These two enums are used also in xutil.h in src/programs/view/.
+ * The Y position enum doesn't seem to be actually used in this header...
+ */
+typedef enum
+{
+    eXCenter,
+    eXLeft,
+    eXRight
+} eXPos;
+
+typedef enum
+{
+    eYCenter,
+    eYTop,
+    eYBottom
+} eYPos;
+
+enum class Fonts : int
+{
+    Times,
+    TimesItalic,
+    TimesBold,
+    TimesBoldItalic,
+    Helvetica,
+    HelveticaItalic,
+    HelveticaBold,
+    HelveticaBoldItalic,
+    Courier,
+    CourierItalic,
+    CourierBold,
+    CourierBoldItalic,
+    Count
+};
+
+
+struct t_psdata
+{
+    FILE*              fp     = nullptr;
+    int                maxrgb = 0;
+    std::vector<t_rgb> rgb;
+    real               gen_ybox = 0;
+    int                ostack   = 0;
+};
+
+
+const char* enumValueToString(Fonts enumValue);
+
+t_psdata ps_open(const char* fn, real x1, real y1, real x2, real y2);
+
+void ps_linewidth(t_psdata* ps, int lw);
+void ps_color(t_psdata* ps, real r, real g, real b);
+void ps_rgb(t_psdata* ps, const t_rgb* rgb);
+
+void ps_rgb_box(t_psdata* ps, t_rgb* rgb);
+void ps_rgb_nbox(t_psdata* ps, t_rgb* rgb, real n);
+void ps_init_rgb_box(t_psdata* ps, real xbox, real ybox);
+void ps_init_rgb_nbox(t_psdata* ps, real xbox, real ybox);
+
+void ps_lineto(t_psdata* ps, real x, real y);
+void ps_linerel(t_psdata* ps, real dx, real dy);
+
+void ps_moveto(t_psdata* ps, real x, real y);
+void ps_moverel(t_psdata* ps, real dx, real dy);
+
+void ps_line(t_psdata* ps, real x1, real y1, real x2, real y2);
+
+void ps_box(t_psdata* ps, real x1, real y1, real x2, real y2);
+void ps_fillbox(t_psdata* ps, real x1, real y1, real x2, real y2);
+
+void ps_arc(t_psdata* ps, real x1, real y1, real rad, real a0, real a1);
+void ps_fillarc(t_psdata* ps, real x1, real y1, real rad, real a0, real a1);
+void ps_arcslice(t_psdata* ps, real xc, real yc, real rad1, real rad2, real a0, real a1);
+void ps_fillarcslice(t_psdata* ps, real xc, real yc, real rad1, real rad2, real a0, real a1);
+
+void ps_circle(t_psdata* ps, real x1, real y1, real rad);
+
+void ps_font(t_psdata* ps, Fonts font, real size);
+void ps_strfont(t_psdata* ps, char* font, real size);
+
+void ps_text(t_psdata* ps, real x1, real y1, const std::string& str);
+void ps_ctext(t_psdata* ps, real x1, real y1, const std::string& str, int expos);
+
+void ps_close(t_psdata* ps);
+
+void ps_flip(t_psdata* ps, gmx_bool bPlus);
+/* Rotate over 90 (bPlus) or -90 (!bPlus) degrees */
+
+void ps_rotate(t_psdata* ps, real angle);
+
+void ps_translate(t_psdata* ps, real x, real y);
+
+void ps_setorigin(t_psdata* ps);
+void ps_unsetorigin(t_psdata* ps);
+
+void ps_comment(t_psdata* ps, const char* s);
+
+#endif
diff --git a/src/include/gromacs/fileio/xdr_datatype.h b/src/include/gromacs/fileio/xdr_datatype.h
new file mode 100644 (file)
index 0000000..6b097e8
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_XDR_DATATYPE_H
+#define GMX_FILEIO_XDR_DATATYPE_H
+
+/* the xdr data types; note that there is no data type 'real' because
+   here we deal with the types as they are actually written to disk.  */
+enum class XdrDataType : int
+{
+    Int,
+    Float,
+    Double,
+    Int64,
+    Char,
+    String,
+    Count
+};
+
+/* names corresponding to the XdrDataType enum */
+const char* enumValueToString(XdrDataType enumValue);
+
+#endif
diff --git a/src/include/gromacs/fileio/xdrf.h b/src/include/gromacs/fileio/xdrf.h
new file mode 100644 (file)
index 0000000..fd83e5e
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_XDRF_H
+#define GMX_FILEIO_XDRF_H
+
+#include <stdio.h>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+#ifdef __PGI /*Portland group compiler*/
+#    define int64_t long long
+#endif
+
+#include "config.h"
+
+#if GMX_INTERNAL_XDR
+#    include "gromacs/fileio/gmx_internal_xdr.h"
+#else
+#    include <rpc/rpc.h>
+#    include <rpc/xdr.h>
+#endif
+
+/* Read or write reduced precision *float* coordinates */
+int xdr3dfcoord(XDR* xdrs, float* fp, int* size, float* precision);
+
+
+/* Read or write a *real* value (stored as float) */
+int xdr_real(XDR* xdrs, real* r);
+
+
+/* Read or write reduced precision *real* coordinates */
+int xdr3drcoord(XDR* xdrs, real* fp, int* size, real* precision);
+
+
+//! Read or write a int32_t value.
+int xdr_int32(XDR* xdrs, int32_t* i);
+
+//! Read or write a int64_t value.
+int xdr_int64(XDR* xdrs, int64_t* i);
+
+int xdr_xtc_seek_time(real time, FILE* fp, XDR* xdrs, int natoms, gmx_bool bSeekForwardOnly);
+
+
+int xdr_xtc_seek_frame(int frame, FILE* fp, XDR* xdrs, int natoms);
+
+
+float xdr_xtc_get_last_frame_time(FILE* fp, XDR* xdrs, int natoms, gmx_bool* bOK);
+
+
+int xdr_xtc_get_last_frame_number(FILE* fp, XDR* xdrs, int natoms, gmx_bool* bOK);
+
+#endif
diff --git a/src/include/gromacs/fileio/xtcio.h b/src/include/gromacs/fileio/xtcio.h
new file mode 100644 (file)
index 0000000..bb37a8d
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_FILEIO_XTCIO_H
+#define GMX_FILEIO_XTCIO_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct t_fileio;
+
+/* All functions return 1 if successful, 0 otherwise
+ * bOK tells if a frame is not corrupted
+ */
+
+/* Note that XTC was implemented to use xdr_int for the step number,
+ * which is defined by the standard to be signed and 32 bit. We didn't
+ * design the format to be extensible, so we can't fix the fact that
+ * after 2^31 frames, step numbers will wrap to be
+ * negative. Fortunately, this tends not to cause serious problems,
+ * and we've fixed it in TNG. */
+
+struct t_fileio* open_xtc(const char* filename, const char* mode);
+/* Open a file for xdr I/O */
+
+void close_xtc(struct t_fileio* fio);
+/* Close the file for xdr I/O */
+
+int read_first_xtc(struct t_fileio* fio,
+                   int*             natoms,
+                   int64_t*         step,
+                   real*            time,
+                   matrix           box,
+                   rvec**           x,
+                   real*            prec,
+                   gmx_bool*        bOK);
+/* Open xtc file, read xtc file first time, allocate memory for x */
+
+int read_next_xtc(struct t_fileio* fio, int natoms, int64_t* step, real* time, matrix box, rvec* x, real* prec, gmx_bool* bOK);
+/* Read subsequent frames */
+
+int write_xtc(struct t_fileio* fio, int natoms, int64_t step, real time, const rvec* box, const rvec* x, real prec);
+/* Write a frame to xtc file */
+
+#endif
diff --git a/src/include/gromacs/fileio/xvgr.h b/src/include/gromacs/fileio/xvgr.h
new file mode 100644 (file)
index 0000000..fc8683c
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_FILEIO_XVGR_H
+#define GMX_FILEIO_XVGR_H
+
+#include <stdio.h>
+
+#include <string>
+#include <vector>
+
+#include "gromacs/math/multidimarray.h"
+#include "gromacs/mdspan/extensions.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_output_env_t;
+
+/***************************************************
+ *            XVGR   DEFINITIONS
+ ***************************************************/
+enum
+{
+    elNone,
+    elSolid,
+    elDotted,
+    elDashed,
+    elLongDashed,
+    elDotDashed,
+    elNR
+};
+/* xvgr line-styles */
+
+enum
+{
+    ecWhite,
+    ecFrank,
+    ecBlack = ecFrank,
+    ecRed,
+    ecGreen,
+    ecBlue,
+    ecYellow,
+    ecBrown,
+    ecGray,
+    ecPurple,
+    ecLightBlue,
+    ecViolet,
+    ecHolland,
+    ecLila,
+    ecDarkGray,
+    ecAquamarine,
+    ecOlive,
+    ecNR
+};
+/* xvgr line-colors */
+
+enum
+{
+    eppNone,
+    eppColor,
+    eppPattern,
+    eppNR
+};
+/* xvgr pattern type */
+
+enum
+{
+    evView,
+    evWorld,
+    evNR
+};
+/* view type */
+
+/***************************************************
+ *            XVGR   ROUTINES
+ ***************************************************/
+
+/* Strings such as titles, lables and legends can contain escape sequences
+ * for formatting. Currently supported are:
+ * \s : start subscript
+ * \S : start superscript
+ * \N : end sub/superscript
+ * \symbol : where symbol is the full name of a greek letter
+ *           (see the xvgrstr function in xvgr.c for the full list)
+ *           when starting with a capital, a capital symbol will be printed,
+ *           note that symbol does not need to be followed by a space
+ * \8 : (deprecated) start symbol font
+ * \4 : (deprecated) end symbol font
+ */
+
+gmx_bool output_env_get_print_xvgr_codes(const struct gmx_output_env_t* oenv);
+/* Returns if we should print xmgrace or xmgr codes */
+
+enum
+{
+    exvggtNONE,
+    exvggtXNY,
+    exvggtXYDY,
+    exvggtXYDYDY,
+    exvggtNR
+};
+
+void xvgr_header(FILE*                          fp,
+                 const char*                    title,
+                 const std::string&             xaxis,
+                 const std::string&             yaxis,
+                 int                            exvg_graph_type,
+                 const struct gmx_output_env_t* oenv);
+/* In most cases you want to use xvgropen_type, which does the same thing
+ * but takes a filename and opens it.
+ */
+
+FILE* xvgropen_type(const char*                    fn,
+                    const char*                    title,
+                    const std::string&             xaxis,
+                    const std::string&             yaxis,
+                    int                            exvg_graph_type,
+                    const struct gmx_output_env_t* oenv);
+/* Open a file, and write a title, and axis-labels in Xvgr format
+ * or write nothing when oenv specifies so.
+ * The xvgr graph type enum is defined above.
+ */
+
+FILE* xvgropen(const char*                    fn,
+               const char*                    title,
+               const std::string&             xaxis,
+               const std::string&             yaxis,
+               const struct gmx_output_env_t* oenv);
+/* Calls xvgropen_type with graph type xvggtXNY. */
+
+/* Close xvgr file, and clean up internal file buffers correctly */
+void xvgrclose(FILE* fp);
+
+void xvgr_subtitle(FILE* out, const char* subtitle, const struct gmx_output_env_t* oenv);
+/* Set the subtitle in xvgr */
+
+void xvgr_view(FILE* out, real xmin, real ymin, real xmax, real ymax, const struct gmx_output_env_t* oenv);
+/* Set the view in xvgr */
+
+void xvgr_world(FILE* out, real xmin, real ymin, real xmax, real ymax, const struct gmx_output_env_t* oenv);
+/* Set the world in xvgr */
+
+void xvgrLegend(FILE* out, const std::vector<std::string>& setNames, const struct gmx_output_env_t* oenv);
+/* Make a legend box, and also modifies the view to make room for the legend */
+
+void xvgr_legend(FILE* out, int nsets, const char* const* setnames, const struct gmx_output_env_t* oenv);
+/* Make a legend box, and also modifies the view to make room for the legend */
+
+void xvgr_new_dataset(FILE* out, int nr_first, int nsets, const char** setnames, const struct gmx_output_env_t* oenv);
+/* End the previous data set(s) and start new one(s).
+    nr_first = the global set number of the first new set (or 0 if no legend)
+    nsets = the number of sets (or 0 if no legends)
+    setnames = the set names (or NULL if no legends)
+ */
+
+void xvgr_line_props(FILE* out, int NrSet, int LineStyle, int LineColor, const struct gmx_output_env_t* oenv);
+/* Set xvgr line styles and colors */
+
+void xvgr_box(FILE*                          out,
+              int                            LocType,
+              real                           xmin,
+              real                           ymin,
+              real                           xmax,
+              real                           ymax,
+              int                            LineStyle,
+              int                            LineWidth,
+              int                            LineColor,
+              int                            BoxFill,
+              int                            BoxColor,
+              int                            BoxPattern,
+              const struct gmx_output_env_t* oenv);
+/* Make a box */
+
+int read_xvg_legend(const char* fn, double*** y, int* ny, char** subtitle, char*** legend);
+/* Read an xvg file for post processing. The number of rows is returned
+ * fn is the filename, y is a pointer to a 2D array (to be allocated by
+ * the routine) ny is the number of columns (including X if appropriate).
+ * If subtitle!=NULL, read the subtitle (when present),
+ * the subtitle string will be NULL when not present.
+ * If legend!=NULL, read the legends for the sets (when present),
+ * 0 is the first y legend, the legend string will be NULL when not present.
+ */
+
+/* \brief Read only the data from an xvg file for post processing.
+ *
+ * Note: this function is deprecated in favor of readXvg, which is
+ *       used under the hood in this function.
+ *
+ * \param[out]    nx Number of rows.
+ * \param[in]     fn Xvg file to read.
+ * \param[in/out] y  Pointer to 2D array (allocated by the routine).
+ * \param[in/out] ny Number of columns.
+ *
+ * Todo: Port all read_xvg calls to use readXvgData
+ */
+int read_xvg(const char* fn, double*** y, int* ny);
+
+/* \brief Read only the data from an xvg file for post processing.
+ *
+ * \param[out] XvgData Data in row major.
+ * \param[in]  fn      Xvg file to read.
+ */
+gmx::MultiDimArray<std::vector<double>, gmx::dynamicExtents2D> readXvgData(const std::string& fn);
+
+
+void write_xvg(const char*                    fn,
+               const char*                    title,
+               int                            nx,
+               int                            ny,
+               real**                         y,
+               const char**                   leg,
+               const struct gmx_output_env_t* oenv);
+/* Write a two D array (y) of dimensions nx rows times
+ * ny columns to a file. If leg != NULL it will be written too.
+ */
+
+
+/* This function reads ascii (xvg) files and extracts the data sets to a
+ * two dimensional array which is returned.
+ */
+real** read_xvg_time(const char* fn,
+                     gmx_bool    bHaveT,
+                     gmx_bool    bTB,
+                     real        tb,
+                     gmx_bool    bTE,
+                     real        te,
+                     int         nsets_in,
+                     int*        nset,
+                     int*        nval,
+                     real*       dt,
+                     real**      t);
+#endif
diff --git a/src/include/gromacs/gmxana/angle_correction.h b/src/include/gromacs/gmxana/angle_correction.h
new file mode 100644 (file)
index 0000000..feed719
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXANA_ANGLE_CORRECTION_H
+#define GMX_GMXANA_ANGLE_CORRECTION_H
+
+#include "gromacs/utility/real.h"
+
+/*! \brief
+ * Return the angle in radians after correcting to be within range of -PI < \p angle < PI.
+ *
+ * \param[in] angle The angle in radians.
+ * \returns Angle within range.
+ */
+real correctRadianAngleRange(real angle);
+
+#endif
diff --git a/src/include/gromacs/gmxana/binsearch.h b/src/include/gromacs/gmxana/binsearch.h
new file mode 100644 (file)
index 0000000..49ffb36
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXANA_BINSEARCH_H
+#define GMX_GMXANA_BINSEARCH_H
+
+#include "gromacs/utility/real.h"
+
+void rangeArray(int* ar, int size);
+
+void insertionSort(real* ar, int* perm, int start, int end, int direction);
+
+int BinarySearch(const real* ar, int start, int end, real key, int direction);
+
+int start_binsearch(real* array, int* perm, int low, int high, real key, int direction);
+
+int LinearSearch(const double* array, int startindx, int stopindx, double key, int* count, int direction);
+
+#endif
diff --git a/src/include/gromacs/gmxana/cluster_methods.h b/src/include/gromacs/gmxana/cluster_methods.h
new file mode 100644 (file)
index 0000000..d512f90
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXANA_CLUSTER_METHODS_H
+#define GMX_GMXANA_CLUSTER_METHODS_H
+
+#include <stdio.h>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_output_env_t;
+struct t_mat;
+
+struct t_clusters
+{
+    int  ncl;
+    int* cl;
+};
+
+struct t_nnb
+{
+    int  nr;
+    int* nb;
+};
+
+
+void mc_optimize(FILE*             log,
+                 t_mat*            m,
+                 real*             time,
+                 int               maxiter,
+                 int               nrandom,
+                 int               seed,
+                 real              kT,
+                 const char*       conv,
+                 gmx_output_env_t* oenv);
+
+void gather(t_mat* m, real cutoff, t_clusters* clust);
+
+void jarvis_patrick(int n1, real** mat, int M, int P, real rmsdcut, t_clusters* clust);
+
+void gromos(int n1, real** mat, real rmsdcut, t_clusters* clust);
+
+#endif
diff --git a/src/include/gromacs/gmxana/cmat.h b/src/include/gromacs/gmxana/cmat.h
new file mode 100644 (file)
index 0000000..714eec3
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXANA_CMAT_H
+#define GMX_GMXANA_CMAT_H
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_output_env_t;
+
+typedef struct
+{
+    int  i, j;
+    real dist;
+} t_dist;
+
+typedef struct
+{
+    int conf, clust;
+} t_clustid;
+
+struct t_mat
+{
+    int      n1, nn;
+    int*     m_ind;
+    gmx_bool b1D;
+    real     minrms, maxrms, sumrms;
+    real*    erow;
+    real**   mat;
+};
+
+/* The matrix is indexed using the matrix index */
+#define EROW(m, i) m->erow[i]
+
+extern t_mat* init_mat(int n1, gmx_bool b1D);
+
+extern void copy_t_mat(t_mat* dst, t_mat* src);
+
+extern void enlarge_mat(t_mat* m, int deltan);
+
+extern void reset_index(t_mat* m);
+
+extern void swap_rows(t_mat* m, int iswap, int jswap);
+
+extern void set_mat_entry(t_mat* m, int i, int j, real val);
+
+extern void done_mat(t_mat** m);
+
+extern real mat_energy(t_mat* mat);
+
+extern void swap_mat(t_mat* m);
+
+extern void low_rmsd_dist(const char* fn, real maxrms, int nn, real** mat, const gmx_output_env_t* oenv);
+
+extern void rmsd_distribution(const char* fn, t_mat* m, const gmx_output_env_t* oenv);
+
+extern t_clustid* new_clustid(int n1);
+
+#endif
diff --git a/src/include/gromacs/gmxana/dens_filter.h b/src/include/gromacs/gmxana/dens_filter.h
new file mode 100644 (file)
index 0000000..4a8f43a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2011,2013,2014,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXANA_DENS_FILTER_H
+#define GMX_GMXANA_DENS_FILTER_H
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+extern gmx_bool convolution(int dataSize, real* in, int kernelSize, const real* kernel);
+extern gmx_bool periodic_convolution(int dsize, real* in, int ksize, const real* kernel);
+extern void     gausskernel(real* out, int size, real var);
+
+#endif
diff --git a/src/include/gromacs/gmxana/eigio.h b/src/include/gromacs/gmxana/eigio.h
new file mode 100644 (file)
index 0000000..9413f3f
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXANA_EIGIO_H
+#define GMX_GMXANA_EIGIO_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+enum
+{
+    eWXR_NO,
+    eWXR_YES,
+    eWXR_NOFIT
+};
+
+extern void read_eigenvectors(const char* file,
+                              int*        natoms,
+                              gmx_bool*   bFit,
+                              rvec**      xref,
+                              gmx_bool*   bDMR,
+                              rvec**      xav,
+                              gmx_bool*   bDMA,
+                              int*        nvec,
+                              int**       eignr,
+                              rvec***     eigvec,
+                              real**      eigval);
+/* Read eigenvectors from file into eigvec, the eigenvector numbers   */
+/* are stored in eignr.                                               */
+/* When bFit=FALSE no fitting was used in the covariance analysis.    */
+/* xref is the reference structure, can be NULL if not present.       */
+/* bDMR indicates mass weighted fit.                                  */
+/* xav is the average/minimum structure is written (t=0).             */
+/* bDMA indicates mass weighted analysis/eigenvectors.                */
+
+extern void write_eigenvectors(const char* trrname,
+                               int         natoms,
+                               const real  mat[],
+                               gmx_bool    bReverse,
+                               int         begin,
+                               int         end,
+                               int         WriteXref,
+                               const rvec* xref,
+                               gmx_bool    bDMR,
+                               const rvec  xav[],
+                               gmx_bool    bDMA,
+                               const real* eigval);
+/* Write eigenvectors in mat to a TRR file.                           */
+/* The reference structure is written (t=-1) when WriteXref=eWXR_YES. */
+/* When WriteXref==eWXR_NOFIT a zero frame is written (t=-1),         */
+/* with lambda=-1.                                                    */
+/* bDMR indicates mass weighted fit.                                  */
+/* The average/minimum structure is written (t=0).                    */
+/* bDMA indicates mass weighted analysis/eigenvectors.                */
+/* eigenvectors with begin <= num <= end are written (num is base-1), */
+/* the timestamp of eigenvector num is num.                           */
+/* If bReverse==TRUE, num=1 is the last vector in mat.                */
+
+
+/* Read up to nmax eigenvalues from file fn, store the values in eigval[],
+ * and the corresponding indices (start counting on 0) in eigvalnr[].
+ * Returns the number of values read.
+ */
+int read_eigval(const char* fn, int nmax, int eigvalnr[], real eigval[]);
+
+#endif
diff --git a/src/include/gromacs/gmxana/fitahx.h b/src/include/gromacs/gmxana/fitahx.h
new file mode 100644 (file)
index 0000000..5fa57da
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXANA_FITAHX_H
+#define GMX_GMXANA_FITAHX_H
+
+
+#include "gromacs/gmxana/hxprops.h"
+
+real fit_ahx(int nres, t_bb bb[], int natoms, int nall, int allindex[], rvec x[], int nca, int caindex[], gmx_bool bFit);
+
+#endif
diff --git a/src/include/gromacs/gmxana/gmx_ana.h b/src/include/gromacs/gmxana/gmx_ana.h
new file mode 100644 (file)
index 0000000..881e012
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXANA_GMX_ANA_H
+#    define GMX_GMXANA_GMX_ANA_H
+
+int gmx_analyze(int argc, char* argv[]);
+
+int gmx_anaeig(int argc, char* argv[]);
+
+int gmx_awh(int argc, char* argv[]);
+
+int gmx_g_angle(int argc, char* argv[]);
+
+int gmx_bar(int argc, char* argv[]);
+
+int gmx_bundle(int argc, char* argv[]);
+
+int gmx_chi(int argc, char* argv[]);
+
+int gmx_cluster(int argc, char* argv[]);
+
+int gmx_confrms(int argc, char* argv[]);
+
+int gmx_covar(int argc, char* argv[]);
+
+int gmx_current(int argc, char* argv[]);
+
+int gmx_density(int argc, char* argv[]);
+
+int gmx_densmap(int argc, char* argv[]);
+
+int gmx_densorder(int argc, char* argv[]);
+
+int gmx_dielectric(int argc, char* argv[]);
+
+int gmx_dipoles(int argc, char* argv[]);
+
+int gmx_disre(int argc, char* argv[]);
+
+int gmx_do_dssp(int argc, char* argv[]);
+
+int gmx_dos(int argc, char* argv[]);
+
+int gmx_dyecoupl(int argc, char* argv[]);
+
+int gmx_enemat(int argc, char* argv[]);
+
+int gmx_energy(int argc, char* argv[]);
+
+int gmx_lie(int argc, char* argv[]);
+
+int gmx_filter(int argc, char* argv[]);
+
+int gmx_gyrate(int argc, char* argv[]);
+
+int gmx_h2order(int argc, char* argv[]);
+
+int gmx_hbond(int argc, char* argv[]);
+
+int gmx_helix(int argc, char* argv[]);
+
+int gmx_helixorient(int argc, char* argv[]);
+
+int gmx_hydorder(int argc, char* argv[]);
+
+int gmx_make_edi(int argc, char* argv[]);
+
+int gmx_mindist(int argc, char* argv[]);
+
+int gmx_nmeig(int argc, char* argv[]);
+
+int gmx_nmens(int argc, char* argv[]);
+
+int gmx_nmr(int argc, char* argv[]);
+
+int gmx_nmtraj(int argc, char* argv[]);
+
+int gmx_order(int argc, char* argv[]);
+
+int gmx_polystat(int argc, char* argv[]);
+
+int gmx_potential(int argc, char* argv[]);
+
+int gmx_principal(int argc, char* argv[]);
+
+int gmx_rama(int argc, char* argv[]);
+
+int gmx_rotmat(int argc, char* argv[]);
+
+int gmx_rms(int argc, char* argv[]);
+
+int gmx_rmsdist(int argc, char* argv[]);
+
+int gmx_rmsf(int argc, char* argv[]);
+
+int gmx_rotacf(int argc, char* argv[]);
+
+int gmx_saltbr(int argc, char* argv[]);
+
+int gmx_sham(int argc, char* argv[]);
+
+int gmx_sigeps(int argc, char* argv[]);
+
+int gmx_sorient(int argc, char* argv[]);
+
+int gmx_spol(int argc, char* argv[]);
+
+int gmx_spatial(int argc, char* argv[]);
+
+int gmx_tcaf(int argc, char* argv[]);
+
+int gmx_traj(int argc, char* argv[]);
+
+int gmx_trjorder(int argc, char* argv[]);
+
+int gmx_velacc(int argc, char* argv[]);
+
+int gmx_clustsize(int argc, char* argv[]);
+
+int gmx_mdmat(int argc, char* argv[]);
+
+int gmx_vanhove(int argc, char* argv[]);
+
+int gmx_wham(int argc, char* argv[]);
+
+int gmx_wheel(int argc, char* argv[]);
+
+int gmx_xpm2ps(int argc, char* argv[]);
+
+int gmx_membed(int argc, char* argv[]);
+
+int gmx_sans(int argc, char* argv[]);
+
+int gmx_saxs(int argc, char* argv[]);
+
+#endif
+/* _gmx_ana_h */
diff --git a/src/include/gromacs/gmxana/gstat.h b/src/include/gromacs/gmxana/gstat.h
new file mode 100644 (file)
index 0000000..ec13293
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXANA_GSTAT_H
+#define GMX_GMXANA_GSTAT_H
+
+#include <string>
+
+#include "gromacs/commandline/pargs.h"
+#include "gromacs/topology/index.h"
+
+struct gmx_output_env_t;
+
+/* must correspond with 'leg' g_chi.c:727 */
+enum
+{
+    edPhi = 0,
+    edPsi,
+    edOmega,
+    edChi1,
+    edChi2,
+    edChi3,
+    edChi4,
+    edChi5,
+    edChi6,
+    edMax
+};
+
+enum
+{
+    edPrintST = 0,
+    edPrintRO
+};
+
+#define NHISTO 360
+#define NONCHI 3
+#define MAXCHI (edMax - NONCHI)
+#define NROT 4 /* number of rotamers: 1=g(-), 2=t, 3=g(+), 0=other */
+
+typedef struct
+{
+    int minCalpha, minC, H, N, C, O, Cn[MAXCHI + 3];
+} t_dihatms; /* Cn[0]=N, Cn[1]=Ca, Cn[2]=Cb etc. */
+
+struct t_dlist
+{
+    char        name[12];
+    int         resnr;
+    std::string residueName;
+    int         j0[edMax]; /* Index in dih array (phi angle is first...) */
+    t_dihatms   atm;
+    int         b[edMax];
+    int         ntr[edMax];
+    real        S2[edMax];
+    real        rot_occ[edMax][NROT];
+};
+
+typedef struct
+{
+    const char* name;    /* Description of the J coupling constant */
+    real        A, B, C; /* Karplus coefficients */
+    real        offset;  /* Offset for dihedral angle in histogram (e.g. -M_PI/3) */
+    real        Jc;      /* Resulting Jcoupling */
+    real        Jcsig;   /* Standard deviation in Jc */
+} t_karplus;
+
+void calc_distribution_props(int nh, const int histo[], real start, int nkkk, t_karplus kkk[], real* S2);
+/* This routine takes a dihedral distribution and calculates
+ * coupling constants and dihedral order parameters of it.
+ *
+ * nh      is the number of points
+ * histo   is the array of datapoints which is assumed to span
+ *         2 M_PI radians
+ * start   is the starting angle of the histogram, this can be either 0
+ *         or -M_PI
+ * nkkk    is the number of karplus sets (multiple coupling constants may be
+ *         derived from a single angle)
+ * kkk     are the constants for calculating J coupling constants using a
+ *         Karplus equation according to
+ *
+ *                  2
+ *         J = A cos theta + B cos theta + C
+ *
+ *         where theta is phi - offset (phi is the angle in the histogram)
+ * offset  is subtracted from phi before substitution in the Karplus
+ *         equation
+ * S2      is the resulting dihedral order parameter
+ *
+ */
+
+void ana_dih_trans(const char*             fn_trans,
+                   const char*             fn_histo,
+                   real**                  dih,
+                   int                     nframes,
+                   int                     nangles,
+                   const char*             grpname,
+                   real*                   time,
+                   gmx_bool                bRb,
+                   const gmx_output_env_t* oenv);
+/*
+ * Analyse dihedral transitions, by counting transitions per dihedral
+ * and per frame. The total number of transitions is printed to
+ * stderr, as well as the average time between transitions.
+ *
+ * is wrapper to low_ana_dih_trans, which also passes in and out the
+     number of transitions per dihedral per residue. that uses struc dlist
+     which is not external, so pp2shift.h must be included.
+
+ * Dihedrals are supposed to be in either of three minima,
+ * (trans, gauche+, gauche-)
+ *
+ * fn_trans  output file name for #transitions per timeframe
+ * fn_histo  output file name for transition time histogram
+ * dih       the actual dihedral angles
+ * nframes   number of times frames
+ * nangles   number of angles
+ * grpname   a string for the header of plots
+ * time      array (size nframes) of times of trajectory frames
+ * bRb       determines whether the polymer convention is used
+ *           (trans = 0)
+ */
+
+void low_ana_dih_trans(gmx_bool                bTrans,
+                       const char*             fn_trans,
+                       gmx_bool                bHisto,
+                       const char*             fn_histo,
+                       int                     maxchi,
+                       real**                  dih,
+                       gmx::ArrayRef<t_dlist>  dlist,
+                       int                     nframes,
+                       int                     nangles,
+                       const char*             grpname,
+                       int                     multiplicity[],
+                       real*                   time,
+                       gmx_bool                bRb,
+                       real                    core_frac,
+                       const gmx_output_env_t* oenv);
+/* as above but passes dlist so can copy occupancies into it, and multiplicity[]
+ *  (1..nangles, corresp to dih[this][], so can have non-3 multiplicity of
+ * rotamers. Also production of xvg output files is conditional
+ * and the fractional width of each rotamer can be set ie for a 3 fold
+ * dihedral with core_frac = 0.5 only the central 60 degrees is assigned
+ * to each rotamer, the rest goes to rotamer zero */
+
+
+void read_ang_dih(const char*             trj_fn,
+                  gmx_bool                bAngles,
+                  gmx_bool                bSaveAll,
+                  gmx_bool                bRb,
+                  gmx_bool                bPBC,
+                  int                     maxangstat,
+                  int                     angstat[],
+                  int*                    nframes,
+                  real**                  time,
+                  int                     isize,
+                  int                     index[],
+                  real**                  trans_frac,
+                  real**                  aver_angle,
+                  real*                   dih[],
+                  const gmx_output_env_t* oenv);
+/*
+ * Read a trajectory and calculate angles and dihedrals.
+ *
+ * trj_fn      file name of trajectory
+ * bAngles     do we have to read angles or dihedrals
+ * bSaveAll    do we have to store all in the dih array
+ * bRb         do we have Ryckaert-Bellemans dihedrals (trans = 0)
+ * bPBC        compute angles module 2 Pi
+ * maxangstat  number of entries in distribution array
+ * angstat     angle distribution
+ * *nframes    number of frames read
+ * time        simulation time at each time frame
+ * isize       number of entries in the index, when angles 3*number of angles
+ *             else 4*number of angles
+ * index       atom numbers that define the angles or dihedrals
+ *             (i,j,k) resp (i,j,k,l)
+ * trans_frac  number of dihedrals in trans
+ * aver_angle  average angle at each time frame
+ * dih         all angles at each time frame
+ */
+
+void make_histo(FILE* log, int ndata, real data[], int npoints, int histo[], real minx, real maxx);
+/*
+ * Make a histogram from data. The min and max of the data array can
+ * be determined (if minx == 0 and maxx == 0)
+ * and the index in the histogram is computed from
+ * ind = npoints/(max(data) - min(data))
+ *
+ * log       write error output to this file
+ * ndata     number of points in data
+ * data      data points
+ * npoints   number of points in histogram
+ * histo     histogram array. This is NOT set to zero, to allow you
+ *           to add multiple histograms
+ * minx      start of the histogram
+ * maxx      end of the histogram
+ *           if both are 0, these values are computed by the routine itself
+ */
+
+void normalize_histo(gmx::ArrayRef<const int> histo, real dx, gmx::ArrayRef<real> normhisto);
+/*
+ * Normalize a histogram so that the integral over the histo is 1
+ *
+ * histo      input histogram
+ * dx         distance between points on the X-axis
+ * normhisto  normalized output histogram
+ */
+
+/* Routines from pp2shift (anadih.c etc.) */
+
+void do_pp2shifts(FILE* fp, int nframes, gmx::ArrayRef<const t_dlist> dlist, real** dih);
+
+gmx_bool has_dihedral(int Dih, const t_dlist& dlist);
+
+/*! \brief Describe the dihedrals in the residues of the \c atoms
+ * structure
+ *
+ * Return a vector with a t_dlist entry for each residue in \c
+ * atoms. The entry for a residue contains its name, its index within
+ * the residues, and a mapping from chemical peptide atom names to
+ * atom indices based on the atom names. Many fields of t_dlist are
+ * not yet filled. */
+std::vector<t_dlist> mk_dlist(FILE*          log,
+                              const t_atoms* atoms,
+                              gmx_bool       bPhi,
+                              gmx_bool       bPsi,
+                              gmx_bool       bChi,
+                              gmx_bool       bHChi,
+                              int            maxchi,
+                              int            r0);
+
+void pr_dlist(FILE*                        fp,
+              gmx::ArrayRef<const t_dlist> dlist,
+              real                         dt,
+              int                          printtype,
+              gmx_bool                     bPhi,
+              gmx_bool                     bPsi,
+              gmx_bool                     bChi,
+              gmx_bool                     bOmega,
+              int                          maxchi);
+
+void mk_chi_lookup(int** lookup, int maxchi, gmx::ArrayRef<const t_dlist> dlist);
+
+void mk_multiplicity_lookup(int* multiplicity, int maxchi, gmx::ArrayRef<const t_dlist> dlist, int nangle);
+
+void get_chi_product_traj(real**                       dih,
+                          int                          nframes,
+                          int                          maxchi,
+                          gmx::ArrayRef<const t_dlist> dlist,
+                          real                         time[],
+                          int**                        lookup,
+                          int*                         multiplicity,
+                          gmx_bool                     bRb,
+                          gmx_bool                     bNormalize,
+                          real                         core_frac,
+                          gmx_bool                     bAll,
+                          const char*                  fnall,
+                          const gmx_output_env_t*      oenv);
+
+void print_one(const gmx_output_env_t* oenv,
+               const char*             base,
+               const char*             name,
+               const char*             title,
+               const char*             ylabel,
+               int                     nf,
+               real                    time[],
+               real                    data[]);
+
+/* Routines from g_hbond */
+void analyse_corr(int  n,
+                  real t[],
+                  real ct[],
+                  real nt[],
+                  real kt[],
+                  real sigma_ct[],
+                  real sigma_nt[],
+                  real sigma_kt[],
+                  real fit_start,
+                  real temp);
+
+void compute_derivative(int nn, const real x[], const real y[], real dydx[]);
+
+#endif
diff --git a/src/include/gromacs/gmxana/hxprops.h b/src/include/gromacs/gmxana/hxprops.h
new file mode 100644 (file)
index 0000000..8dadfad
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXANA_HXPROPS_H
+#define GMX_GMXANA_HXPROPS_H
+
+#include <stdio.h>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct t_atom;
+struct t_resinfo;
+
+#define PHI_AHX (-55.0)
+#define PSI_AHX (-45.0)
+/* Canonical values of the helix phi/psi angles */
+
+
+/*! \internal \brief Struct containing properties of a residue in a protein backbone. */
+struct t_bb
+{
+    //! Protein backbone phi angle.
+    real phi;
+    //! Protein backbone psi angle.
+    real psi;
+    //! RMS distance of phi and psi angles from ideal helix
+    real pprms2;
+    //! Estimated J-coupling value
+    real jcaha;
+    //! Value of 3 turn helix?
+    real d3;
+    //! Value of 4 turn helix?
+    real d4;
+    //! Value of 5 turn?
+    real d5;
+    //! Average of RMS for analysis.
+    real rmsa;
+    //! If the structure is helical.
+    gmx_bool bHelix;
+    //! Number of elliptical elements
+    int nhx;
+    //! Average RMS Deviation when atoms of this residue are fitted to ideal helix
+    int nrms;
+    //! Residue index for output, relative to gmx_helix -r0 value
+    int resno;
+    //! Index for previous carbon.
+    int Cprev;
+    //! Index for backbone nitrogen.
+    int N;
+    //! Index for backbone NH hydrogen.
+    int H;
+    //! Index for alpha carbon.
+    int CA;
+    //! Index for carbonyl carbon.
+    int C;
+    //! Index for carbonyl oxygen.
+    int O;
+    //! Index for next backbone nitrogen.
+    int Nnext;
+    //! Name for this residue.
+    char label[32];
+};
+
+enum
+{
+    efhRAD,
+    efhTWIST,
+    efhRISE,
+    efhLEN,
+    efhDIP,
+    efhRMS,
+    efhRMSA,
+    efhCD222,
+    efhPPRMS,
+    efhCPHI,
+    efhPHI,
+    efhPSI,
+    efhHB3,
+    efhHB4,
+    efhHB5,
+    efhJCA,
+    efhAHX,
+    efhNR
+};
+
+extern real ahx_len(int gnx, const int index[], rvec x[]);
+/* Assume we have a list of Calpha atoms only! */
+
+extern real ellipticity(int nres, t_bb bb[]);
+
+extern real radius(FILE* fp, int nca, const int ca_index[], rvec x[]);
+/* Assume we have calphas */
+
+extern real twist(int nca, const int caindex[], rvec x[]);
+/* Calculate the twist of the helix */
+
+extern real pprms(FILE* fp, int nbb, t_bb bb[]);
+/* Calculate the average RMS from canonical phi/psi values
+ * and the distance per residue
+ */
+
+extern real ca_phi(int gnx, const int index[], rvec x[]);
+/* Assume we have a list of Calpha atoms only! */
+
+extern real dip(int nbb, const int bbind[], const rvec x[], const t_atom atom[]);
+
+extern real rise(int gnx, const int index[], rvec x[]);
+/* Assume we have a list of Calpha atoms only! */
+
+extern void
+av_hblen(FILE* fp3, FILE* fp3a, FILE* fp4, FILE* fp4a, FILE* fp5, FILE* fp5a, real t, int nres, t_bb bb[]);
+
+extern void av_phipsi(FILE* fphi, FILE* fpsi, FILE* fphi2, FILE* fpsi2, real t, int nres, t_bb bb[]);
+
+/*! \brief Allocate and fill an array of information about residues in a protein backbone.
+ *
+ * The user is propted for an index group of protein residues (little
+ * error checking occurs). For the number of residues found in the
+ * selected group, nbb entries are made in the returned array.  Each
+ * entry contains the atom indices of the N, H, CA, C and O atoms (for
+ * PRO, H means CD), as well as the C of the previous residue and the
+ * N of the next (-1 if not found).
+ *
+ * In the output array, the first residue will be numbered starting
+ * from res0. */
+extern t_bb* mkbbind(const char* fn,
+                     int*        nres,
+                     int*        nbb,
+                     int         res0,
+                     int*        nall,
+                     int**       index,
+                     char***     atomname,
+                     t_atom      atom[],
+                     t_resinfo*  resinfo);
+
+extern void do_start_end(int      nres,
+                         t_bb     bb[],
+                         int*     nbb,
+                         int      bbindex[],
+                         int*     nca,
+                         int      caindex[],
+                         gmx_bool bRange,
+                         int      rStart,
+                         int      rEnd);
+
+extern void calc_hxprops(int nres, t_bb bb[], const rvec x[]);
+
+extern void pr_bb(FILE* fp, int nres, t_bb bb[]);
+
+#endif
diff --git a/src/include/gromacs/gmxana/interf.h b/src/include/gromacs/gmxana/interf.h
new file mode 100644 (file)
index 0000000..e1f15aa
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXANA_INTERF_H
+#define GMX_GMXANA_INTERF_H
+
+#include "gromacs/utility/real.h"
+
+typedef struct
+{
+    real Z; /* Interface height-coordinate */
+    real t; /* Interface thickness */
+} t_interf;
+
+static inline void init_interf(t_interf* surf)
+{
+    surf->Z = 0;
+    surf->t = 0;
+}
+
+#endif
diff --git a/src/include/gromacs/gmxana/nrama.h b/src/include/gromacs/gmxana/nrama.h
new file mode 100644 (file)
index 0000000..71c00fd
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXANA_NRAMA_H
+#define GMX_GMXANA_NRAMA_H
+
+#include "gromacs/fileio/trxio.h"
+#include "gromacs/topology/topology.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_output_env_t;
+
+typedef struct
+{
+    gmx_bool bShow;
+    char*    label;
+    int      iphi, ipsi; /* point in the dih array of xr... */
+} t_phipsi;
+
+typedef struct
+{
+    int  ai[4];
+    int  mult;
+    real phi0;
+    real ang;
+} t_dih;
+
+typedef struct
+{
+    int               ndih;
+    t_dih*            dih;
+    int               npp;
+    t_phipsi*         pp;
+    t_trxstatus*      traj;
+    int               natoms;
+    int               amin, amax;
+    real              t;
+    rvec*             x;
+    matrix            box;
+    t_idef*           idef;
+    PbcType           pbcType;
+    gmx_output_env_t* oenv;
+} t_xrama;
+
+t_topology* init_rama(gmx_output_env_t* oenv, const char* infile, const char* topfile, t_xrama* xr, int mult);
+
+gmx_bool new_data(t_xrama* xr);
+
+#endif /* GMX_GMXANA_NRAMA_H */
diff --git a/src/include/gromacs/gmxana/nsfactor.h b/src/include/gromacs/gmxana/nsfactor.h
new file mode 100644 (file)
index 0000000..f22010a
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXANA_NSFACTOR_H
+#define GMX_GMXANA_NSFACTOR_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct t_topology;
+
+typedef struct gmx_neutron_atomic_structurefactors_t
+{
+    int     nratoms;
+    int*    p;       /* proton number */
+    int*    n;       /* neuton number */
+    double* slength; /* scattering length in fm */
+    char**  atomnm;  /* atom symbol */
+} gmx_neutron_atomic_structurefactors_t;
+
+typedef struct gmx_sans_t
+{
+    const t_topology* top;     /* topology */
+    double*           slength; /* scattering length for this topology */
+} gmx_sans_t;
+
+typedef struct gmx_radial_distribution_histogram_t
+{
+    int     grn;      /* number of bins */
+    double  binwidth; /* bin size */
+    double* r;        /* Distances */
+    double* gr;       /* Probability */
+} gmx_radial_distribution_histogram_t;
+
+typedef struct gmx_static_structurefactor_t
+{
+    int     qn;    /* number of items */
+    double* s;     /* scattering */
+    double* q;     /* q vectors */
+    double  qstep; /* q increment */
+} gmx_static_structurefactor_t;
+
+void check_binwidth(real binwidth);
+
+void check_mcover(real mcover);
+
+void normalize_probability(int n, double* a);
+
+gmx_neutron_atomic_structurefactors_t* gmx_neutronstructurefactors_init(const char* datfn);
+
+gmx_sans_t* gmx_sans_init(const t_topology* top, gmx_neutron_atomic_structurefactors_t* gnsf);
+
+gmx_radial_distribution_histogram_t* calc_radial_distribution_histogram(gmx_sans_t*  gsans,
+                                                                        rvec*        x,
+                                                                        matrix       box,
+                                                                        const int*   index,
+                                                                        int          isize,
+                                                                        double       binwidth,
+                                                                        gmx_bool     bMC,
+                                                                        gmx_bool     bNORM,
+                                                                        real         mcover,
+                                                                        unsigned int seed);
+
+gmx_static_structurefactor_t* convert_histogram_to_intensity_curve(gmx_radial_distribution_histogram_t* pr,
+                                                                   double start_q,
+                                                                   double end_q,
+                                                                   double q_step);
+
+
+#endif
diff --git a/src/include/gromacs/gmxana/powerspect.h b/src/include/gromacs/gmxana/powerspect.h
new file mode 100644 (file)
index 0000000..e8b83fe
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXANA_POWERSPECT_H
+#define GMX_GMXANA_POWERSPECT_H
+
+#include <string>
+#include <vector>
+
+#include "gromacs/gmxana/interf.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+extern void powerspectavg(real*** interface, int t, int xbins, int ybins, gmx::ArrayRef<const std::string> outfiles);
+
+extern void powerspectavg_intf(t_interf***                      if1,
+                               t_interf***                      if2,
+                               int                              t,
+                               int                              xbins,
+                               int                              ybins,
+                               gmx::ArrayRef<const std::string> outfiles);
+
+#endif
diff --git a/src/include/gromacs/gmxana/princ.h b/src/include/gromacs/gmxana/princ.h
new file mode 100644 (file)
index 0000000..bf24db3
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXANA_PRINC_H
+#define GMX_GMXANA_PRINC_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct t_atom;
+struct t_atoms;
+
+void rotate_atoms(int gnx, const int index[], rvec x[], matrix trans);
+/* Rotate all atoms in index using matrix trans */
+
+void principal_comp(int n, const int index[], t_atom atom[], rvec x[], matrix trans, rvec d);
+/* Calculate the principal components of atoms in index. Atoms are
+ * mass weighted. It is assumed that the center of mass is in the origin!
+ */
+
+void orient_princ(const t_atoms* atoms, int isize, const int* index, int natoms, rvec x[], rvec* v, rvec d);
+/* rotates molecule to align principal axes with coordinate axes */
+
+real calc_xcm(const rvec x[], int gnx, const int* index, const t_atom* atom, rvec xcm, gmx_bool bQ);
+/* Calculate the center of mass of the atoms in index. if bQ then the atoms
+ * will be charge weighted rather than mass weighted.
+ * Returns the total mass/charge.
+ */
+
+real sub_xcm(rvec x[], int gnx, const int* index, const t_atom atom[], rvec xcm, gmx_bool bQ);
+/* Calc. the center of mass and subtract it from all coordinates.
+ * Returns the original center of mass in xcm
+ * Returns the total mass
+ */
+
+void add_xcm(rvec x[], int gnx, const int* index, rvec xcm);
+/* Increment all atoms in index with xcm */
+
+#endif
diff --git a/src/include/gromacs/gmxana/sfactor.h b/src/include/gromacs/gmxana/sfactor.h
new file mode 100644 (file)
index 0000000..97b1ce1
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXANA_SFACTOR_H
+#define GMX_GMXANA_SFACTOR_H
+
+#include "gromacs/math/gmxcomplex.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_output_env_t;
+struct t_topology;
+struct t_trxframe;
+
+typedef struct gmx_structurefactors gmx_structurefactors_t;
+
+typedef struct structure_factor structure_factor_t;
+
+typedef struct reduced_atom reduced_atom_t;
+
+int* create_indexed_atom_type(reduced_atom_t* atm, int size);
+
+void compute_structure_factor(structure_factor_t* sft,
+                              matrix              box,
+                              reduced_atom_t*     red,
+                              int                 isize,
+                              real                start_q,
+                              real                end_q,
+                              int                 group,
+                              real**              sf_table);
+
+gmx_structurefactors_t* gmx_structurefactors_init(const char* datfn);
+
+void gmx_structurefactors_done(gmx_structurefactors_t* gsf);
+
+int gmx_structurefactors_get_sf(gmx_structurefactors_t* gsf, int elem, real a[4], real b[4], real* c);
+
+real** gmx_structurefactors_table(gmx_structurefactors_t* gsf, real momentum, real ref_k, real lambda, int n_angles);
+
+void save_data(structure_factor_t* sft, const char* file, int ngrps, real start_q, real end_q, const gmx_output_env_t* oenv);
+
+double CMSF(gmx_structurefactors_t* gsf, int type, int nh, double lambda, double sin_theta);
+
+int return_atom_type(const char* name, gmx_structurefactors_t* gsf);
+
+void rearrange_atoms(reduced_atom_t*         positions,
+                     struct t_trxframe*      fr,
+                     const int*              index,
+                     int                     isize,
+                     const t_topology*       top,
+                     gmx_bool                flag,
+                     gmx_structurefactors_t* gsf);
+
+int do_scattering_intensity(const char*             fnTPS,
+                            const char*             fnNDX,
+                            const char*             fnXVG,
+                            const char*             fnTRX,
+                            const char*             fnDAT,
+                            real                    start_q,
+                            real                    end_q,
+                            real                    energy,
+                            int                     ng,
+                            const gmx_output_env_t* oenv);
+
+t_complex*** rc_tensor_allocation(int x, int y, int z);
+
+real** compute_scattering_factor_table(gmx_structurefactors_t* gsf, structure_factor_t* sft);
+
+#endif
diff --git a/src/include/gromacs/gmxana/thermochemistry.h b/src/include/gromacs/gmxana/thermochemistry.h
new file mode 100644 (file)
index 0000000..efc50dd
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Code for computing entropy and heat capacity from eigenvalues
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ */
+#ifndef GMXANA_THERMOCHEMISTRY_H
+#define GMXANA_THERMOCHEMISTRY_H
+
+#include "gromacs/math/units.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/*! \brief Compute zero point energy from an array of eigenvalues.
+ *
+ * This routine first converts the eigenvalues from a normal mode
+ * analysis to frequencies and then computes the zero point energy.
+ *
+ * \param[in] eigval       The eigenvalues
+ * \param[in] scale_factor Factor to scale frequencies by before computing cv
+ * \return The zero point energy (kJ/mol)
+ */
+double calcZeroPointEnergy(gmx::ArrayRef<const real> eigval, real scale_factor);
+
+/*! \brief Compute heat capacity due to vibrational motion
+ *
+ * \param[in] eigval       The eigenvalues
+ * \param[in] temperature  Temperature (K)
+ * \param[in] linear       TRUE if this is a linear molecule
+ * \param[in] scale_factor Factor to scale frequencies by before computing cv
+ * \return The heat capacity at constant volume (J/mol K)
+ */
+double calcVibrationalHeatCapacity(gmx::ArrayRef<const real> eigval,
+                                   real                      temperature,
+                                   gmx_bool                  linear,
+                                   real                      scale_factor);
+
+/*! \brief Compute entropy due to translational motion
+ *
+ * Following the equations in J. W. Ochterski,
+ * Thermochemistry in Gaussian, Gaussian, Inc., 2000
+ * Pitssburg PA
+ *
+ * \param[in] mass         Molecular mass (Dalton)
+ * \param[in] temperature  Temperature (K)
+ * \param[in] pressure     Pressure (bar) at which to compute
+ * \returns The translational entropy (J/mol K)
+ */
+double calcTranslationalEntropy(real mass, real temperature, real pressure);
+
+/*! \brief Compute entropy due to rotational motion
+ *
+ * Following the equations in J. W. Ochterski,
+ * Thermochemistry in Gaussian, Gaussian, Inc., 2000
+ * Pitssburg PA
+ *
+ * \param[in] temperature  Temperature (K)
+ * \param[in] natom        Number of atoms
+ * \param[in] linear       TRUE if this is a linear molecule
+ * \param[in] theta        The principal moments of inertia (unit of Energy)
+ * \param[in] sigma_r      Symmetry factor, should be >= 1
+ * \returns The rotational entropy (J/mol K)
+ */
+double calcRotationalEntropy(real temperature, int natom, gmx_bool linear, const rvec theta, real sigma_r);
+
+/*! \brief Compute internal energy due to vibrational motion
+ *
+ * \param[in] eigval       The eigenvalues
+ * \param[in] temperature  Temperature (K)
+ * \param[in] linear       TRUE if this is a linear molecule
+ * \param[in] scale_factor Factor to scale frequencies by before computing E
+ * \return The internal energy (J/mol K)
+ */
+double calcVibrationalInternalEnergy(gmx::ArrayRef<const real> eigval,
+                                     real                      temperature,
+                                     gmx_bool                  linear,
+                                     real                      scale_factor);
+
+/*! \brief Compute entropy using Schlitter formula
+ *
+ * Computes entropy for a molecule / molecular system using the
+ * algorithm due to Schlitter (Chem. Phys. Lett. 215 (1993)
+ * 617-621).
+ * The input should be eigenvalues from a covariance analysis,
+ * the units of the eigenvalues are those of energy.
+ *
+ * \param[in] eigval       The eigenvalues
+ * \param[in] temperature  Temperature (K)
+ * \param[in] linear       True if this is a linear molecule (typically a diatomic molecule).
+ * \return the entropy (J/mol K)
+ */
+double calcSchlitterEntropy(gmx::ArrayRef<const real> eigval, real temperature, gmx_bool linear);
+
+/*! \brief Compute entropy using Quasi-Harmonic formula
+ *
+ * Computes entropy for a molecule / molecular system using the
+ * Quasi-harmonic algorithm (Macromolecules 1984, 17, 1370).
+ * The input should be eigenvalues from a normal mode analysis.
+ * In both cases the units of the eigenvalues are those of energy.
+ *
+ * \param[in] eigval       The eigenvalues
+ * \param[in] temperature  Temperature (K)
+ * \param[in] linear       True if this is a linear molecule (typically a diatomic molecule).
+ * \param[in] scale_factor Factor to scale frequencies by before computing S0
+ * \return the entropy (J/mol K)
+ */
+double calcQuasiHarmonicEntropy(gmx::ArrayRef<const real> eigval, real temperature, gmx_bool linear, real scale_factor);
+
+#endif
diff --git a/src/include/gromacs/gmxlib/conformation_utilities.h b/src/include/gromacs/gmxlib/conformation_utilities.h
new file mode 100644 (file)
index 0000000..b8c0ec8
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_CONFORMATION_UTILITIES_H
+#define GMX_CONFORMATION_UTILITIES_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+void rotate_conf(int natom, rvec* x, rvec* v, real alfa, real beta, real gamma);
+/*rotate() rotates a configuration alfa degrees around the x_axis and beta degrees around the y_axis, *v can be NULL */
+
+void make_new_box(int natoms, rvec* x, matrix box, const rvec box_space, gmx_bool bCenter);
+/* Generates a box around a configuration, box_space is optional extra
+ * space around it. If bCenter then coordinates will be centered in
+ * the generated box
+ */
+
+#endif
diff --git a/src/include/gromacs/gmxlib/network.h b/src/include/gromacs/gmxlib/network.h
new file mode 100644 (file)
index 0000000..3bc41df
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXLIB_NETWORK_H
+#define GMX_GMXLIB_NETWORK_H
+
+/*
+ * This module defines the interface of the actual communication routines.
+ */
+
+#include <stdio.h>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxmpi.h"
+#include "gromacs/utility/stringutil.h"
+#include "gromacs/utility/unique_cptr.h"
+
+struct t_commrec;
+struct t_filenm;
+
+//! Free memory associated with the commrec.
+void done_commrec(t_commrec* cr);
+
+//! Convenience alias.
+using CommrecHandle = gmx::unique_cptr<t_commrec, done_commrec>;
+
+//! Allocate, initialize and return the commrec.
+CommrecHandle init_commrec(MPI_Comm communicator);
+
+struct t_commrec* reinitialize_commrec_for_this_thread(const t_commrec* cro);
+
+/* Initialize communication records for thread-parallel simulations.
+   Must be called on all threads before any communication takes place by
+   the individual threads. Copies the original commrec to
+   thread-local versions (a small memory leak results because we don't
+   deallocate the old shared version).  */
+
+void gmx_fill_commrec_from_mpi(t_commrec* cr);
+/* Continues t_commrec construction */
+
+void gmx_setup_nodecomm(FILE* fplog, struct t_commrec* cr);
+/* Sets up fast global communication for clusters with multi-core nodes */
+
+//! Wait until all processes in communicator have reached the barrier
+void gmx_barrier(MPI_Comm communicator);
+
+//! Broadcast nbytes bytes from the master to communicator
+void gmx_bcast(int nbytes, void* b, MPI_Comm communicator);
+
+void gmx_sumi(int nr, int r[], const struct t_commrec* cr);
+/* Calculate the global sum of an array of ints */
+
+void gmx_sumli(int nr, int64_t r[], const struct t_commrec* cr);
+/* Calculate the global sum of an array of large ints */
+
+void gmx_sumf(int nr, float r[], const struct t_commrec* cr);
+/* Calculate the global sum of an array of floats */
+
+void gmx_sumd(int nr, double r[], const struct t_commrec* cr);
+/* Calculate the global sum of an array of doubles */
+
+#if GMX_DOUBLE
+#    define gmx_sum gmx_sumd
+#else
+#    define gmx_sum gmx_sumf
+#endif
+
+const char* opt2fn_master(const char* opt, int nfile, const t_filenm fnm[], t_commrec* cr);
+/* Return the filename belonging to cmd-line option opt, or NULL when
+ * no such option or not running on master */
+
+[[noreturn]] void gmx_fatal_collective(int                    f_errno,
+                                       const char*            file,
+                                       int                    line,
+                                       MPI_Comm               comm,
+                                       gmx_bool               bMaster,
+                                       gmx_fmtstr const char* fmt,
+                                       ...) gmx_format(printf, 6, 7);
+/* As gmx_fatal declared in utility/fatalerror.h,
+ * but only the master process prints the error message.
+ * This should only be called one of the following two situations:
+ * 1) On all nodes in cr->mpi_comm_mysim, with cr!=NULL,dd==NULL.
+ * 2) On all nodes in dd->mpi_comm_all,   with cr==NULL,dd!=NULL.
+ * This will call MPI_Finalize instead of MPI_Abort when possible,
+ * This is useful for handling errors in code that is executed identically
+ * for all processes.
+ */
+
+#endif
diff --git a/src/include/gromacs/gmxlib/nonbonded/nb_free_energy.h b/src/include/gromacs/gmxlib/nonbonded/nb_free_energy.h
new file mode 100644 (file)
index 0000000..fb97c67
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXLIB_NONBONDED_NB_FREE_ENERGY_H
+#define GMX_GMXLIB_NONBONDED_NB_FREE_ENERGY_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct t_forcerec;
+struct t_nrnb;
+struct t_nblist;
+struct interaction_const_t;
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+template<typename>
+class ArrayRefWithPadding;
+} // namespace gmx
+
+void gmx_nb_free_energy_kernel(const t_nblist&                                  nlist,
+                               const gmx::ArrayRefWithPadding<const gmx::RVec>& coords,
+                               bool                                             useSimd,
+                               int                                              ntype,
+                               real                                             rlist,
+                               const interaction_const_t&                       ic,
+                               gmx::ArrayRef<const gmx::RVec>                   shiftvec,
+                               gmx::ArrayRef<const real>                        nbfp,
+                               gmx::ArrayRef<const real>                        nbfp_grid,
+                               gmx::ArrayRef<const real>                        chargeA,
+                               gmx::ArrayRef<const real>                        chargeB,
+                               gmx::ArrayRef<const int>                         typeA,
+                               gmx::ArrayRef<const int>                         typeB,
+                               int                                              flags,
+                               gmx::ArrayRef<const real>                        lambda,
+                               t_nrnb* gmx_restrict                             nrnb,
+                               gmx::ArrayRefWithPadding<gmx::RVec>              threadForceBuffer,
+                               rvec*               threadForceShiftBuffer,
+                               gmx::ArrayRef<real> threadVc,
+                               gmx::ArrayRef<real> threadVv,
+                               gmx::ArrayRef<real> threadDvdl);
+
+#endif
diff --git a/src/include/gromacs/gmxlib/nonbonded/nb_softcore.h b/src/include/gromacs/gmxlib/nonbonded/nb_softcore.h
new file mode 100644 (file)
index 0000000..5db070f
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXLIB_NONBONDED_SOFTCORE_H
+#define GMX_GMXLIB_NONBONDED_SOFTCORE_H
+
+#include "config.h"
+
+#include "gromacs/math/functions.h"
+#include "gromacs/simd/simd.h"
+#include "gromacs/simd/simd_math.h"
+
+/* linearized electrostatics */
+template<bool computeForces, class RealType, class BoolType>
+static inline void quadraticApproximationCoulomb(const RealType qq,
+                                                 const RealType rInvQ,
+                                                 const RealType r,
+                                                 const real     lambdaFac,
+                                                 const real     dLambdaFac,
+                                                 RealType gmx_unused* force,
+                                                 RealType*            potential,
+                                                 RealType*            dvdl,
+                                                 BoolType             dvdlMask)
+{
+    RealType constFac = qq * rInvQ;
+    RealType linFac   = constFac * r * rInvQ;
+    RealType quadrFac = linFac * r * rInvQ;
+
+    /* Computing Coulomb force and potential energy */
+    if constexpr (computeForces)
+    {
+        *force = -2 * quadrFac + 3 * linFac;
+    }
+
+    *potential = quadrFac - 3 * (linFac - constFac);
+
+    RealType lambdaFacRevInv = gmx::maskzInv(1 - lambdaFac, dvdlMask);
+    *dvdl = dLambdaFac * 0.5_real * (lambdaFac * lambdaFacRevInv) * (quadrFac - 2 * linFac + constFac);
+}
+
+/* reaction-field linearized electrostatics */
+template<bool computeForces, class RealType, class BoolType>
+static inline void reactionFieldQuadraticPotential(const RealType qq,
+                                                   const real     facel,
+                                                   const RealType r,
+                                                   const real     rCutoff,
+                                                   const real     lambdaFac,
+                                                   const real     dLambdaFac,
+                                                   const RealType alphaEff,
+                                                   const real     krf,
+                                                   const real     potentialShift,
+                                                   RealType gmx_unused* force,
+                                                   RealType*            potential,
+                                                   RealType*            dvdl,
+                                                   BoolType             mask)
+{
+    /* check if we have to use the hardcore values */
+    BoolType computeValues = mask && (lambdaFac < 1 && 0 < alphaEff && facel != 0);
+    if (gmx::anyTrue(computeValues))
+    {
+        RealType lambdaFacRev = gmx::selectByMask(1 - lambdaFac, computeValues);
+
+        RealType rQ = gmx::cbrt(lambdaFacRev);
+        rQ          = gmx::sqrt(rQ) * (1 + gmx::abs(qq / facel));
+        rQ          = rQ * alphaEff;
+
+        // ensure that the linearization point doesn't go beyond rCutoff
+        BoolType beyondCutoff = rCutoff < rQ;
+        BoolType withinCutoff = rQ <= rCutoff;
+        if (gmx::anyTrue(beyondCutoff))
+        {
+            rQ = gmx::blend(rQ, rCutoff, beyondCutoff);
+        }
+
+        computeValues = computeValues && (r < rQ);
+        if (gmx::anyTrue(computeValues))
+        {
+            RealType rInvQ = gmx::maskzInv(rQ, computeValues);
+
+            // Compute quadratic force, potential and dvdl
+            RealType forceQuad(0);
+            RealType potentialQuad(0);
+            RealType dvdlQuad(0);
+            quadraticApproximationCoulomb<computeForces>(
+                    qq, rInvQ, r, lambdaFac, dLambdaFac, &forceQuad, &potentialQuad, &dvdlQuad, computeValues);
+
+            // rf modification
+            forceQuad     = forceQuad - qq * 2 * krf * r * r;
+            potentialQuad = potentialQuad + qq * (krf * r * r - potentialShift);
+
+            // update
+            if constexpr (computeForces)
+            {
+                *force = gmx::blend(*force, forceQuad, computeValues);
+            }
+            *potential = gmx::blend(*potential, potentialQuad, computeValues);
+            *dvdl      = *dvdl + gmx::selectByMask(dvdlQuad, computeValues && withinCutoff);
+        }
+    }
+}
+
+/* ewald linearized electrostatics */
+template<bool computeForces, class RealType, class BoolType>
+static inline void ewaldQuadraticPotential(const RealType qq,
+                                           const real     facel,
+                                           const RealType r,
+                                           const real     rCutoff,
+                                           const real     lambdaFac,
+                                           const real     dLambdaFac,
+                                           const RealType alphaEff,
+                                           const real     potentialShift,
+                                           RealType gmx_unused* force,
+                                           RealType*            potential,
+                                           RealType*            dvdl,
+                                           BoolType             mask)
+{
+    /* check if we have to use the hardcore values */
+    BoolType computeValues = mask && (lambdaFac < 1 && 0 < alphaEff && facel != 0);
+    if (gmx::anyTrue(computeValues))
+    {
+        RealType lambdaFacRev = gmx::selectByMask(1 - lambdaFac, computeValues);
+
+        RealType rQ = gmx::cbrt(lambdaFacRev);
+        rQ          = gmx::sqrt(rQ) * (1 + gmx::abs(qq / facel));
+        rQ          = rQ * alphaEff;
+
+        // ensure that the linearization point doesn't go beyond rCutoff
+        BoolType beyondCutoff = rCutoff < rQ;
+        BoolType withinCutoff = rQ <= rCutoff;
+        if (gmx::anyTrue(beyondCutoff))
+        {
+            rQ = gmx::blend(rQ, rCutoff, beyondCutoff);
+        }
+
+        computeValues = computeValues && (r < rQ);
+        if (gmx::anyTrue(computeValues))
+        {
+            RealType rInvQ = gmx::maskzInv(rQ, computeValues);
+
+            // Compute quadratic force, potential and dvdl
+            RealType forceQuad(0);
+            RealType potentialQuad(0);
+            RealType dvdlQuad(0);
+            quadraticApproximationCoulomb<computeForces>(
+                    qq, rInvQ, r, lambdaFac, dLambdaFac, &forceQuad, &potentialQuad, &dvdlQuad, computeValues);
+
+            // ewald modification
+            potentialQuad = potentialQuad - qq * potentialShift;
+
+            // update
+            if constexpr (computeForces)
+            {
+                *force = gmx::blend(*force, forceQuad, computeValues);
+            }
+            *potential = gmx::blend(*potential, potentialQuad, computeValues);
+            *dvdl      = *dvdl + gmx::selectByMask(dvdlQuad, computeValues && withinCutoff);
+        }
+    }
+}
+
+/* cutoff LJ with quadratic appximation of lj-potential */
+template<bool computeForces, class RealType, class BoolType>
+static inline void lennardJonesQuadraticPotential(const RealType c6,
+                                                  const RealType c12,
+                                                  const RealType r,
+                                                  const RealType rsq,
+                                                  const real     lambdaFac,
+                                                  const real     dLambdaFac,
+                                                  const RealType sigma6,
+                                                  const RealType alphaEff,
+                                                  const real     repulsionShift,
+                                                  const real     dispersionShift,
+                                                  RealType gmx_unused* force,
+                                                  RealType*            potential,
+                                                  RealType*            dvdl,
+                                                  BoolType             mask)
+{
+    constexpr real c_twentySixSeventh = 26.0_real / 7.0_real;
+    constexpr real c_oneSixth         = 1.0_real / 6.0_real;
+    constexpr real c_oneTwelth        = 1.0_real / 12.0_real;
+    constexpr real c_half             = 1.0_real / 2.0_real;
+
+    /* check if we have to use the hardcore values */
+    BoolType computeValues = mask && (lambdaFac < 1 && 0 < alphaEff);
+    if (gmx::anyTrue(computeValues))
+    {
+        RealType lambdaFacRev    = gmx::selectByMask(1 - lambdaFac, computeValues);
+        RealType lambdaFacRevInv = gmx::maskzInv(1 - lambdaFac, computeValues);
+
+        RealType rQ = gmx::cbrt(c_twentySixSeventh * sigma6 * lambdaFacRev);
+        rQ          = gmx::sqrt(rQ);
+        rQ          = rQ * alphaEff;
+
+        computeValues = (computeValues && r < rQ);
+        if (gmx::anyTrue(computeValues))
+        {
+            /* scaled values for c6 and c12 */
+            RealType c6s, c12s;
+            c6s  = c_oneSixth * c6;
+            c12s = c_oneTwelth * c12;
+            /* Temporary variables for inverted values */
+            RealType rInvQ = gmx::maskzInv(rQ, computeValues);
+            RealType rInv14C, rInv13C, rInv12C;
+            RealType rInv8C, rInv7C, rInv6C;
+            rInv6C  = rInvQ * rInvQ * rInvQ;
+            rInv6C  = rInv6C * rInv6C;
+            rInv7C  = rInv6C * rInvQ;
+            rInv8C  = rInv7C * rInvQ;
+            rInv14C = c12s * rInv7C * rInv7C * rsq;
+            rInv13C = c12s * rInv7C * rInv6C * r;
+            rInv12C = c12s * rInv6C * rInv6C;
+            rInv8C  = rInv8C * c6s * rsq;
+            rInv7C  = rInv7C * c6s * r;
+            rInv6C  = rInv6C * c6s;
+
+            /* Temporary variables for A and B */
+            RealType quadrFac, linearFac, constFac;
+            quadrFac  = 156 * rInv14C - 42 * rInv8C;
+            linearFac = 168 * rInv13C - 48 * rInv7C;
+            constFac  = 91 * rInv12C - 28 * rInv6C;
+
+            /* Computing LJ force and potential energy */
+            RealType gmx_unused forceQuad     = -quadrFac + linearFac;
+            RealType            potentialQuad = c_half * quadrFac - linearFac + constFac;
+            RealType            dvdlQuad      = dLambdaFac * 28 * (lambdaFac * lambdaFacRevInv)
+                                * ((6.5_real * rInv14C - rInv8C) - (13 * rInv13C - 2 * rInv7C)
+                                   + (6.5_real * rInv12C - rInv6C));
+
+            potentialQuad = potentialQuad
+                            + gmx::selectByMask(((c12s * repulsionShift) - (c6s * dispersionShift)),
+                                                computeValues);
+            if constexpr (computeForces)
+            {
+                *force = gmx::blend(*force, forceQuad, computeValues);
+            }
+            *potential = gmx::blend(*potential, potentialQuad, computeValues);
+            *dvdl      = *dvdl + gmx::selectByMask(dvdlQuad, computeValues);
+        }
+    }
+}
+
+#endif
diff --git a/src/include/gromacs/gmxlib/nonbonded/nonbonded.h b/src/include/gromacs/gmxlib/nonbonded/nonbonded.h
new file mode 100644 (file)
index 0000000..9ab0997
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXLIB_NONBONDED_NONBONDED_H
+#define GMX_GMXLIB_NONBONDED_NONBONDED_H
+
+#define GMX_NONBONDED_DO_FORCE (1 << 1)
+#define GMX_NONBONDED_DO_SHIFTFORCE (1 << 2)
+#define GMX_NONBONDED_DO_FOREIGNLAMBDA (1 << 3)
+#define GMX_NONBONDED_DO_POTENTIAL (1 << 4)
+#define GMX_NONBONDED_DO_SR (1 << 5)
+
+#endif
diff --git a/src/include/gromacs/gmxlib/nrnb.h b/src/include/gromacs/gmxlib/nrnb.h
new file mode 100644 (file)
index 0000000..a21d13e
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXLIB_NRNB_H
+#define GMX_GMXLIB_NRNB_H
+
+#include <array>
+#include <cstdint>
+#include <cstdio>
+
+enum
+{
+    eNR_NBKERNEL_VDW_VF,
+    eNR_NBKERNEL_VDW_F,
+    eNR_NBKERNEL_ELEC_VF,
+    eNR_NBKERNEL_ELEC_F,
+    eNR_NBKERNEL_ELEC_W3_VF,
+    eNR_NBKERNEL_ELEC_W3_F,
+    eNR_NBKERNEL_ELEC_W3W3_VF,
+    eNR_NBKERNEL_ELEC_W3W3_F,
+    eNR_NBKERNEL_ELEC_W4_VF,
+    eNR_NBKERNEL_ELEC_W4_F,
+    eNR_NBKERNEL_ELEC_W4W4_VF,
+    eNR_NBKERNEL_ELEC_W4W4_F,
+    eNR_NBKERNEL_ELEC_VDW_VF,
+    eNR_NBKERNEL_ELEC_VDW_F,
+    eNR_NBKERNEL_ELEC_VDW_W3_VF,
+    eNR_NBKERNEL_ELEC_VDW_W3_F,
+    eNR_NBKERNEL_ELEC_VDW_W3W3_VF,
+    eNR_NBKERNEL_ELEC_VDW_W3W3_F,
+    eNR_NBKERNEL_ELEC_VDW_W4_VF,
+    eNR_NBKERNEL_ELEC_VDW_W4_F,
+    eNR_NBKERNEL_ELEC_VDW_W4W4_VF,
+    eNR_NBKERNEL_ELEC_VDW_W4W4_F,
+
+    eNR_NBKERNEL_NR, /* Total number of interaction-specific kernel entries */
+
+    eNR_NBKERNEL_GENERIC = eNR_NBKERNEL_NR, /* Reuse number; KERNEL_NR is not an entry itself */
+    eNR_NBKERNEL_GENERIC_CG,
+    eNR_NBKERNEL_FREE_ENERGY, /* Add other generic kernels _before_ the free energy one */
+
+    eNR_NBKERNEL_TOTAL_NR,
+
+    eNR_NBNXN_DIST2 = eNR_NBKERNEL_TOTAL_NR, // Reuse the symbolic constant that indicates the last kernel
+    eNR_NBNXN_LJ_RF,
+    eNR_NBNXN_LJ_RF_E,
+    eNR_NBNXN_LJ_TAB,
+    eNR_NBNXN_LJ_TAB_E,
+    eNR_NBNXN_LJ_EWALD,
+    eNR_NBNXN_LJ_EWALD_E,
+    eNR_NBNXN_LJ,
+    eNR_NBNXN_LJ_E,
+    eNR_NBNXN_RF,
+    eNR_NBNXN_RF_E,
+    eNR_NBNXN_TAB,
+    eNR_NBNXN_TAB_E,
+    eNR_NBNXN_EWALD,
+    eNR_NBNXN_EWALD_E,
+    eNR_NBNXN_ADD_LJ_FSW,
+    eNR_NBNXN_ADD_LJ_FSW_E,
+    eNR_NBNXN_ADD_LJ_PSW,
+    eNR_NBNXN_ADD_LJ_PSW_E,
+    eNR_NBNXN_ADD_LJ_EWALD,
+    eNR_NBNXN_ADD_LJ_EWALD_E,
+    eNR_NB14,
+    eNR_WEIGHTS,
+    eNR_SPREAD,
+    eNR_SPREADBSP,
+    eNR_GATHERF,
+    eNR_GATHERFBSP,
+    eNR_FFT,
+    eNR_CONV,
+    eNR_SOLVEPME,
+    eNR_NS,
+    eNR_RESETX,
+    eNR_SHIFTX,
+    eNR_CGCM,
+    eNR_FSUM,
+    eNR_BONDS,
+    eNR_G96BONDS,
+    eNR_FENEBONDS,
+    eNR_TABBONDS,
+    eNR_RESTRBONDS,
+    eNR_LINEAR_ANGLES,
+    eNR_ANGLES,
+    eNR_G96ANGLES,
+    eNR_QANGLES,
+    eNR_TABANGLES,
+    eNR_PROPER,
+    eNR_IMPROPER,
+    eNR_RB,
+    eNR_FOURDIH,
+    eNR_TABDIHS,
+    eNR_DISRES,
+    eNR_ORIRES,
+    eNR_DIHRES,
+    eNR_POSRES,
+    eNR_FBPOSRES,
+    eNR_ANGRES,
+    eNR_ANGRESZ,
+    eNR_MORSE,
+    eNR_CUBICBONDS,
+    eNR_WALLS,
+    eNR_POLARIZE,
+    eNR_ANHARM_POL,
+    eNR_WPOL,
+    eNR_THOLE,
+    eNR_VIRIAL,
+    eNR_UPDATE,
+    eNR_EXTUPDATE,
+    eNR_STOPCM,
+    eNR_PCOUPL,
+    eNR_EKIN,
+    eNR_LINCS,
+    eNR_LINCSMAT,
+    eNR_SHAKE,
+    eNR_CONSTR_V,
+    eNR_SHAKE_RIJ,
+    eNR_CONSTR_VIR,
+    eNR_SETTLE,
+    eNR_VSITE1,
+    eNR_VSITE2,
+    eNR_VSITE2FD,
+    eNR_VSITE3,
+    eNR_VSITE3FD,
+    eNR_VSITE3FAD,
+    eNR_VSITE3OUT,
+    eNR_VSITE4FD,
+    eNR_VSITE4FDN,
+    eNR_VSITEN,
+    eNR_CMAP,
+    eNR_UREY_BRADLEY,
+    eNR_CROSS_BOND_BOND,
+    eNR_CROSS_BOND_ANGLE,
+    eNRNB
+};
+
+
+struct t_nrnb
+{
+    std::array<double, eNRNB> n = { 0 };
+};
+
+void clear_nrnb(t_nrnb* nrnb);
+
+void print_nrnb(FILE* out, t_nrnb* nrnb);
+
+/*! \brief Increment the nonbonded kernel flop counters
+ *
+ * \param nrnb The nonbonded kernel flop counters.
+ * \param index Which counter to incrememnt.
+ * \param increment How much to increment the counter by.
+ */
+static inline void inc_nrnb(t_nrnb* nrnb, int index, int increment)
+{
+    nrnb->n[index] += increment;
+}
+
+/*! \brief Atomic increment of nonbonded kernel flop counters
+ *
+ * \param nrnb The nonbonded kernel flop counters.
+ * \param index Which counter to incrememnt.
+ * \param increment How much to increment the counter by.
+ *
+ * Same as inc_nrnb but includes omp atomic pragma
+ */
+void atomicNrnbIncrement(t_nrnb* nrnb, int index, int increment);
+
+void print_flop(FILE* out, t_nrnb* nrnb, double* nbfs, double* mflop);
+/* Calculates the non-bonded forces and flop count.
+ * When out!=NULL also prints the full count table.
+ */
+
+void print_perf(FILE* out, double nodetime, double realtime, int64_t nsteps, double delta_t, double nbfs, double mflop);
+/* Prints the performance, nbfs and mflop come from print_flop */
+
+int cost_nrnb(int enr);
+/* Cost in i860 cycles of this component of MD */
+
+const char* nrnb_str(int enr);
+/* Name of this component */
+
+#endif /* GMX_GMXLIB_NRNB_H */
diff --git a/src/include/gromacs/gmxpreprocess/add_par.h b/src/include/gromacs/gmxpreprocess/add_par.h
new file mode 100644 (file)
index 0000000..22d60b8
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_ADD_PAR_H
+#define GMX_GMXPREPROCESS_ADD_PAR_H
+
+#include "gromacs/utility/real.h"
+
+struct InteractionsOfType;
+struct PreprocessResidue;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+void add_param(InteractionsOfType* ps, int ai, int aj, gmx::ArrayRef<const real> c, const char* s);
+
+void add_cmap_param(InteractionsOfType* ps, int ai, int aj, int ak, int al, int am, const char* s);
+
+void add_vsite3_atoms(InteractionsOfType* ps, int ai, int aj, int ak, int al, bool bSwapParity);
+
+void add_vsite2_param(InteractionsOfType* ps, int ai, int aj, int ak, real c0);
+
+void add_vsite3_param(InteractionsOfType* ps, int ai, int aj, int ak, int al, real c0, real c1);
+
+void add_vsite4_atoms(InteractionsOfType* ps, int ai, int aj, int ak, int al, int am);
+
+int search_jtype(const PreprocessResidue& localPpResidue, const char* name, bool bFirstRes);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/calch.h b/src/include/gromacs/gmxpreprocess/calch.h
new file mode 100644 (file)
index 0000000..20a3c14
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_PREPROCESS_CALCH_H
+#define GMX_PREPROCESS_CALCH_H
+
+#include "gromacs/math/vectypes.h"
+
+void calc_h_pos(int nht, rvec xa[], rvec xh[], int* l);
+/*
+ *    w.f. van gunsteren, groningen, july 1981
+ *
+ *    translated to c d. van der spoel groningen jun 1993
+ *    added option 5 jan 95
+ *
+ *    subroutine genh (nht,nh,na,d,alfa,x)
+ *
+ *    genh generates cartesian coordinates for hydrogen atoms
+ *    using the coordinates of neighbour atoms.
+ *
+ *    nht      : type of hydrogen attachment (see manual)
+ *    xh(1.. ) : atomic positions of the hydrogen atoms that are to be
+ *               generated
+ *    xa(1..4) : atomic positions of the control atoms i, j and k and l
+ *    default bond lengths and angles are defined internally
+ *
+ *    l : dynamically changed index
+ */
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/convparm.h b/src/include/gromacs/gmxpreprocess/convparm.h
new file mode 100644 (file)
index 0000000..eac7e91
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_CONVPARM_H
+#define GMX_GMXPREPROCESS_CONVPARM_H
+
+#include "gromacs/utility/real.h"
+
+struct gmx_mtop_t;
+struct MoleculeInformation;
+struct InteractionsOfType;
+enum class CombinationRule : int;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+void convertInteractionsOfType(int                                      atnr,
+                               gmx::ArrayRef<const InteractionsOfType>  nbtypes,
+                               gmx::ArrayRef<const MoleculeInformation> mi,
+                               const MoleculeInformation*               intermolecular_interactions,
+                               CombinationRule                          comb,
+                               double                                   reppow,
+                               real                                     fudgeQQ,
+                               gmx_mtop_t*                              mtop);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/editconf.h b/src/include/gromacs/gmxpreprocess/editconf.h
new file mode 100644 (file)
index 0000000..a7c4e1d
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXPREPROCESS_EDITCONF_H
+#define GMX_GMXPREPROCESS_EDITCONF_H
+
+int gmx_editconf(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/fflibutil.h b/src/include/gromacs/gmxpreprocess/fflibutil.h
new file mode 100644 (file)
index 0000000..386a10b
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_FFLIBUTIL_H
+#define GMX_GMXPREPROCESS_FFLIBUTIL_H
+
+#include <cstdio>
+
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/datafilefinder.h"
+
+/*! \brief
+ * Enumerates forcefields in the data directories.
+ */
+std::vector<gmx::DataFileInfo> fflib_enumerate_forcefields();
+
+const char* fflib_forcefield_dir_ext();
+/* Returns the name of the force field directory extension */
+
+const char* fflib_forcefield_itp();
+/* Returns the name of the main forcefield itp file */
+
+const char* fflib_forcefield_doc();
+/* Returns the name of the forcefield documentation file */
+
+void fflib_filename_base(const char* filename, char* filebase, int maxlen);
+/* Return the base file name of filename in base,
+ * i.e. remove path and extension, if present.
+ * base should be at least of size maxlen.
+ */
+
+std::vector<std::string> fflib_search_file_end(const char* ffdir, const char* file_end, bool bFatalError);
+/* Search for files ending on file_end in the force field directory fflib.
+ * fflib should be in the GROMACS lib.path.
+ * Return the number of files and the file names in filenames.
+ */
+
+bool fflib_fexist(const std::string& file);
+/* Check if a file exists in the force field library */
+
+FILE* fflib_open(const std::string& file);
+/* Open force field library file "file" for reading.
+ * "file" should contain the whole path to the force field library,
+ * either absolute or relative to the current dir.
+ */
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/gen_ad.h b/src/include/gromacs/gmxpreprocess/gen_ad.h
new file mode 100644 (file)
index 0000000..9ab5eea
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_GEN_AD_H
+#define GMX_GMXPREPROCESS_GEN_AD_H
+
+struct t_atoms;
+struct t_excls;
+struct MoleculePatchDatabase;
+struct InteractionsOfType;
+struct PreprocessResidue;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+void gen_pad(t_atoms*                               atoms,
+             gmx::ArrayRef<const PreprocessResidue> rtpFFDB,
+             gmx::ArrayRef<InteractionsOfType>      plist,
+             t_excls                                excls[],
+             gmx::ArrayRef<MoleculePatchDatabase>   globalPatches,
+             bool                                   bAllowMissing,
+             gmx::ArrayRef<const int>               circ_bonds);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/gen_maxwell_velocities.h b/src/include/gromacs/gmxpreprocess/gen_maxwell_velocities.h
new file mode 100644 (file)
index 0000000..5a31cb4
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_MAXWELL_VELOCITIES
+#define GMX_MAXWELL_VELOCITIES
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_mtop_t;
+
+namespace gmx
+{
+class MDLogger;
+}
+
+/*! \brief
+ * Generate Maxwellian velocities.
+ *
+ * \param[in] tempi Temperature to generate around
+ * \param[in] seed  Random number generator seed
+ * \param[in] mtop  Molecular Topology
+ * \param[out] v    Velocities
+ * \param[in] logger Handle to logging interface.
+ */
+void maxwell_speed(real tempi, unsigned int seed, gmx_mtop_t* mtop, rvec v[], const gmx::MDLogger& logger);
+
+/*! \brief
+ * Remove the center of mass motion in a set of coordinates.
+ *
+ * \param[in]  logger Handle to logging interface.
+ * \param[in]  natoms Number of atoms
+ * \param[in]  mass   Atomic masses
+ * \param[in]  x      Coordinates
+ * \param[out] v      Velocities
+ */
+void stop_cm(const gmx::MDLogger& logger, int natoms, real mass[], rvec x[], rvec v[]);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/gen_vsite.h b/src/include/gromacs/gmxpreprocess/gen_vsite.h
new file mode 100644 (file)
index 0000000..2cd20ce
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_GEN_VSITE_H
+#define GMX_GMXPREPROCESS_GEN_VSITE_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+class PreprocessingAtomTypes;
+struct t_atoms;
+struct InteractionsOfType;
+struct PreprocessResidue;
+struct t_symtab;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/* stuff for pdb2gmx */
+
+void do_vsites(gmx::ArrayRef<const PreprocessResidue> rtpFFDB,
+               PreprocessingAtomTypes*                atype,
+               t_atoms*                               at,
+               t_symtab*                              symtab,
+               std::vector<gmx::RVec>*                x,
+               gmx::ArrayRef<InteractionsOfType>      plist,
+               int*                                   dummy_type[],
+               int*                                   cgnr[],
+               real                                   mHmult,
+               bool                                   bVSiteAromatics,
+               const char*                            ffdir);
+
+void do_h_mass(InteractionsOfType* psb, int vsite_type[], t_atoms* at, real mHmult, bool bDeuterate);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/genconf.h b/src/include/gromacs/gmxpreprocess/genconf.h
new file mode 100644 (file)
index 0000000..395c6ca
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXPREPROCESS_GENCONF_H
+#define GMX_GMXPREPROCESS_GENCONF_H
+
+int gmx_genconf(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/genhydro.h b/src/include/gromacs/gmxpreprocess/genhydro.h
new file mode 100644 (file)
index 0000000..63a78fc
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_GENHYDRO_H
+#define GMX_GMXPREPROCESS_GENHYDRO_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+
+struct t_atoms;
+struct t_symtab;
+struct MoleculePatchDatabase;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/*! \brief
+ * Generate hydrogen atoms and N and C terminal patches.
+ *
+ * \param[inout] initialAtoms The input atoms data structure to be modified.
+ * \param[inout] localAtoms The extra atoms for reassigning the new entries.
+ * \param[inout] xptr Coordinates to be updated with those for new atoms.
+ * \param[in] globalPatches The atom modifications to use.
+ * \param[inout] symtab Global symbol table for atom names.
+ * \param[in] nterpairs Number of termini pairs in the molecule.
+ * \param[in] ntdb Entries for N-terminus in each chain, each entry can be valid or nullptr.
+ * \param[in] ctdb Entries for C-terminus in each cahin, each entry can be valid or nullptr.
+ * \param[in] rN Residue number of the N-terminus of each chain.
+ * \param[in] rC Residue number of the C-terminus of each chain.
+ * \param[in] bMissing If routine should continue if atoms are not found.
+ * \param[in] cyclicBondsIndex Index of cyclic bonds or empty.
+ * \returns New total number of atoms.
+ */
+int add_h(t_atoms**                                   initialAtoms,
+          t_atoms**                                   localAtoms,
+          std::vector<gmx::RVec>*                     xptr,
+          gmx::ArrayRef<const MoleculePatchDatabase>  globalPatches,
+          t_symtab*                                   symtab,
+          int                                         nterpairs,
+          gmx::ArrayRef<MoleculePatchDatabase* const> ntdb,
+          gmx::ArrayRef<MoleculePatchDatabase* const> ctdb,
+          gmx::ArrayRef<const int>                    rN,
+          gmx::ArrayRef<const int>                    rC,
+          bool                                        bMissing,
+          gmx::ArrayRef<const int>                    cyclicBondsIndex);
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/genion.h b/src/include/gromacs/gmxpreprocess/genion.h
new file mode 100644 (file)
index 0000000..bde3085
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXPREPROCESS_GENION_H
+#define GMX_GMXPREPROCESS_GENION_H
+
+int gmx_genion(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/genrestr.h b/src/include/gromacs/gmxpreprocess/genrestr.h
new file mode 100644 (file)
index 0000000..9281f96
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXPREPROCESS_GENPR_H
+#define GMX_GMXPREPROCESS_GENPR_H
+
+int gmx_genrestr(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/gmxcpp.h b/src/include/gromacs/gmxpreprocess/gmxcpp.h
new file mode 100644 (file)
index 0000000..a97a72b
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2012,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_GMXCPP_H
+#define GMX_GMXPREPROCESS_GMXCPP_H
+
+#include <string>
+
+typedef struct gmx_cpp* gmx_cpp_t;
+
+/* The possible return codes for these functions */
+enum
+{
+    eCPP_OK,
+    eCPP_FILE_NOT_FOUND,
+    eCPP_EOF,
+    eCPP_SYNTAX,
+    eCPP_INTERRUPT,
+    eCPP_INVALID_HANDLE,
+    eCPP_INVALID_INCLUDE_DELIMITER,
+    eCPP_FILE_NOT_OPEN,
+    eCPP_UNKNOWN,
+    eCPP_NR
+};
+
+/* Open the file to be processed. The handle variable holds internal
+   info for the cpp emulator. The cppopt variable (null terminated)
+   can hold cpp options like -IXXX and -DXXX. Return integer status.
+ */
+int cpp_open_file(const char* filenm, gmx_cpp_t* handlep, char** cppopts);
+
+/* Return one whole line from the file into buf which holds at most n
+   characters, for subsequent processing. Returns integer status.
+ */
+int cpp_read_line(gmx_cpp_t* handlep, int n, char buf[]);
+
+/* Return the file currently being read.
+ */
+const char* cpp_cur_file(const gmx_cpp_t* handlep);
+
+/* Return the current line number.
+ */
+int cpp_cur_linenr(const gmx_cpp_t* handlep);
+
+/* Close the file! Return integer status.
+ */
+int cpp_close_file(gmx_cpp_t* handlep);
+
+/* Return a pointer to the value of defineName, when present, nullptr othwerwise.
+ */
+const std::string* cpp_find_define(const gmx_cpp_t* handlep, const std::string& defineName);
+
+/* Clean up normal and file static data structures
+ */
+void cpp_done(gmx_cpp_t handle);
+
+/* Return a string containing the error message coresponding to status
+   variable.
+ */
+char* cpp_error(gmx_cpp_t* handlep, int status);
+
+/* Returns warning message if strings defined in mdp define section (e.g. -DFLEXIBLE)
+ * were not not found when processing the topology */
+std::string checkAndWarnForUnusedDefines(const gmx_cpp& handle);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/gpp_atomtype.h b/src/include/gromacs/gmxpreprocess/gpp_atomtype.h
new file mode 100644 (file)
index 0000000..6d28b0c
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2011,2014,2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares PreprocessingAtomType.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_preprocessing
+ */
+#ifndef GMX_GMXPREPROCESS_GPP_ATOMTYPE_H
+#define GMX_GMXPREPROCESS_GPP_ATOMTYPE_H
+
+#include <cstdio>
+
+#include <memory>
+#include <optional>
+#include <string>
+
+#include "gromacs/utility/real.h"
+
+struct gmx_mtop_t;
+struct t_atom;
+struct t_atomtypes;
+class InteractionOfType;
+struct InteractionsOfType;
+struct t_symtab;
+enum class ParticleType : int;
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/*! \libinternal \brief
+ * Storage of all atom types used during preprocessing of a simulation
+ * input.
+ */
+class PreprocessingAtomTypes
+{
+public:
+    PreprocessingAtomTypes();
+    //! Move constructor.
+    PreprocessingAtomTypes(PreprocessingAtomTypes&& old) noexcept;
+    //! Move assignment constructor.
+    PreprocessingAtomTypes& operator=(PreprocessingAtomTypes&& old) noexcept;
+
+    ~PreprocessingAtomTypes();
+
+    /*! \brief
+     *  Get atom type index for atom type name if present in the database, or empty optional.
+     *
+     *  \todo The code should be changed to instead use a gmx::compat version
+     *  of std::optional to return an iterator to the element being searched,
+     *  or an empty optional construct if the entry has not been found.
+     *
+     *  \param[in] str Input string to search type for.
+     *  \returns Optional atomtype as integer.
+     */
+    std::optional<int> atomTypeFromName(const std::string& str) const;
+
+    //! Get number of defined atom types.
+    size_t size() const;
+
+    /*! \brief
+     * Get name of atom from internal atom type number.
+     *
+     * \param[in] nt Internal number of atom type.
+     * \returns The optional type name.
+     */
+    std::optional<const char*> atomNameFromAtomType(int nt) const;
+
+    /*! \brief
+     * Get normal mass of atom from internal atom type number.
+     *
+     * \param[in] nt Internal number of atom type.
+     * \returns The optional mass for the atom.
+     */
+    std::optional<real> atomMassFromAtomType(int nt) const;
+
+    /*! \brief
+     * Get normal charge of atom from internal atom type number.
+     *
+     * \param[in] nt Internal number of atom type.
+     * \returns The optional charge for the atom.
+     */
+    std::optional<real> atomChargeFromAtomType(int nt) const;
+
+    /*! \brief
+     * Get particle type for atom type \p nt
+     *
+     * \param[in] nt Internal number of atom type.
+     * \returns The optional particle type.
+     */
+    std::optional<ParticleType> atomParticleTypeFromAtomType(int nt) const;
+
+    /*! \brief
+     * Get bond atom parameter of atom from internal atom type number.
+     *
+     * \param[in] nt Internal number of atom type.
+     * \returns The optional bond atom parameter.
+     */
+    std::optional<int> bondAtomTypeFromAtomType(int nt) const;
+
+    /*! \brief
+     * Get atomic number of atom from internal atom type number.
+     *
+     * \param[in] nt Internal number of atom type.
+     * \returns The optional atomic number type.
+     */
+    std::optional<int> atomNumberFromAtomType(int nt) const;
+
+    /*! \brief
+     * Get the value of \p param of type \p nt.
+     *
+     * \param[in] param The parameter value to find.
+     * \param[in] nt The number of the type.
+     * \returns The optional value of the parameter.
+     */
+    std::optional<real> atomNonBondedParamFromAtomType(int nt, int param) const;
+
+    /*! \brief
+     * If a value is within the range of the current types or not.
+     *
+     * \param[in] nt Value to check.
+     * \returns True if value is in range.
+     */
+    bool isSet(int nt) const;
+
+    /*! \brief
+     * Print data to file.
+     *
+     * \param[in] out File pointer.
+     */
+    void printTypes(FILE* out);
+
+    /*! \brief
+     * Set the values of an existing atom type \p nt.
+     *
+     * \param[in] nt Type that should be set.
+     * \param[in] tab Symbol table.
+     * \param[in] a Atom information.
+     * \param[in] name Atom name.
+     * \param[in] nb Nonbonded parameters.
+     * \param[in] bondAtomType What kind of bonded interactions are there.
+     * \param[in] atomNumber Atomic number of the entry.
+     * \returns Optional number of the type set.
+     */
+    std::optional<int> setType(int                      nt,
+                               t_symtab*                tab,
+                               const t_atom&            a,
+                               const std::string&       name,
+                               const InteractionOfType& nb,
+                               int                      bondAtomType,
+                               int                      atomNumber);
+
+    /*! \brief
+     * Add a unique type to the database.
+     *
+     * \param[in] tab Symbol table.
+     * \param[in] a Atom information.
+     * \param[in] name Atom name.
+     * \param[in] nb Nonbonded parameters.
+     * \param[in] bondAtomType What kind of bonded interactions are there.
+     * \param[in] atomNumber Atomic number of the entry.
+     * \returns Index to the type in the database. If the type shares
+     *          a name with an existing type, return the index of that type.
+     */
+    int addType(t_symtab*                tab,
+                const t_atom&            a,
+                const std::string&       name,
+                const InteractionOfType& nb,
+                int                      bondAtomType,
+                int                      atomNumber);
+
+    /*! \brief
+     * Renumber existing atom type entries.
+     *
+     * \param[in] plist List of parameters.
+     * \param[in] mtop Global topology.
+     * \param[inout] wallAtomType Atom types of wall atoms, which may also be renumbered
+     * \param[in] verbose If we want to print additional info.
+     */
+    void renumberTypes(gmx::ArrayRef<InteractionsOfType> plist, gmx_mtop_t* mtop, int* wallAtomType, bool verbose);
+
+    /*! \brief
+     * Copy information to other structure.
+     *
+     * \param[inout] atypes Other datastructure to copy to.
+     */
+    void copyTot_atomtypes(t_atomtypes* atypes) const;
+
+private:
+    class Impl;
+    //! Pimpl that holds the data.
+    std::unique_ptr<Impl> impl_;
+};
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/gpp_bond_atomtype.h b/src/include/gromacs/gmxpreprocess/gpp_bond_atomtype.h
new file mode 100644 (file)
index 0000000..7226e30
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2011,2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares PreprocessingBondAtomType.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_preprocessing
+ */
+#ifndef GMX_GMXPREPROCESS_GPP_BOND_ATOMTYPE_H
+#define GMX_GMXPREPROCESS_GPP_BOND_ATOMTYPE_H
+
+#include <cstdio>
+
+#include <memory>
+#include <optional>
+#include <string>
+
+struct t_symtab;
+
+/*! \libinternal \brief
+ * Storage for all bonded atomtypes during simulation preprocessing.
+ */
+class PreprocessingBondAtomType
+{
+public:
+    PreprocessingBondAtomType();
+    ~PreprocessingBondAtomType();
+
+    //! Get number of defined bond atom types.
+    size_t size() const;
+
+    /*! \brief
+     * Get name of atom from internal bond atom type number.
+     *
+     * \param[in] nt Internal number of atom type.
+     * \returns The optional type name.
+     */
+    std::optional<const char*> atomNameFromBondAtomType(int nt) const;
+
+    /*! \brief
+     *  Get bond atom type index for atom type name if present in the database, or NOTSET.
+     *
+     *  \todo The code should be changed to instead use a gmx::compat version
+     *  of std::optional to return a handle to the element being searched,
+     *  or an empty optional construct if the entry has not been found.
+     *
+     *  \param[in] str Input string to search type for.
+     *  \returns Optional atomtype as integer.
+     */
+    std::optional<int> bondAtomTypeFromName(const std::string& str) const;
+
+    /*! \brief
+     * Add a unique type to the database.
+     *
+     * \param[in] tab Symbol table.
+     * \param[in] name Atom name.
+     * \returns Index to the type in the database. If the type shares
+     *          a name with an existing type, return the index of that type.
+     */
+    int addBondAtomType(t_symtab* tab, const std::string& name);
+
+    /*! \brief
+     * If a value is within the range of the current types or not.
+     *
+     * \param[in] nt Value to check.
+     * \returns True if value is in range.
+     */
+    bool isSet(int nt) const;
+
+private:
+    class Impl;
+    //! Pimpl that holds the data.
+    std::unique_ptr<Impl> impl_;
+};
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/gpp_nextnb.h b/src/include/gromacs/gmxpreprocess/gpp_nextnb.h
new file mode 100644 (file)
index 0000000..2788f18
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_GPP_NEXTNB_H
+#define GMX_GMXPREPROCESS_GPP_NEXTNB_H
+
+struct InteractionsOfType;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+template<typename>
+class ListOfLists;
+} // namespace gmx
+
+struct t_nextnb
+{
+    int nr;   /* nr atoms (0 <= i < nr) (atoms->nr)            */
+    int nrex; /* with nrex lists of neighbours         */
+    /* respectively containing zeroth, first   */
+    /* second etc. neigbours (0 <= nre < nrex) */
+    int** nrexcl; /* with (0 <= nrx < nrexcl[i][nre]) neigbours    */
+    /* per list stored in one 2d array of lists        */
+    int*** a; /* like this: a[i][nre][nrx]                     */
+};
+
+void init_nnb(t_nextnb* nnb, int nr, int nrex);
+/* Initiate the arrays for nnb (see above) */
+
+void done_nnb(t_nextnb* nnb);
+/* Cleanup the nnb struct */
+
+#ifdef DEBUG_NNB
+#    define print_nnb(nnb, s) __print_nnb(nnb, s)
+void print_nnb(t_nextnb* nnb, char* s);
+/* Print the nnb struct */
+#else
+#    define print_nnb(nnb, s)
+#endif
+
+void gen_nnb(t_nextnb* nnb, gmx::ArrayRef<InteractionsOfType> plist);
+/* Generate a t_nextnb structure from bond information.
+ * With the structure you can either generate exclusions
+ * or generate angles and dihedrals. The structure must be
+ * initiated using init_nnb.
+ */
+
+void generate_excl(int nrexcl, int nratoms, gmx::ArrayRef<InteractionsOfType> plist, gmx::ListOfLists<int>* excls);
+/* Generate an exclusion block from bonds and constraints in
+ * plist.
+ */
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/grompp.h b/src/include/gromacs/gmxpreprocess/grompp.h
new file mode 100644 (file)
index 0000000..29a783f
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXPREPROCESS_GROMPP_H
+#define GMX_GMXPREPROCESS_GROMPP_H
+
+int gmx_grompp(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/grompp_impl.h b/src/include/gromacs/gmxpreprocess/grompp_impl.h
new file mode 100644 (file)
index 0000000..47ecb01
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_GROMPP_IMPL_H
+#define GMX_GMXPREPROCESS_GROMPP_IMPL_H
+
+#include <string>
+
+#include "gromacs/gmxpreprocess/notset.h"
+#include "gromacs/topology/atoms.h"
+#include "gromacs/topology/block.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/listoflists.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/*! \libinternal \brief
+ * Describes an interaction of a given type, plus its parameters.
+ */
+class InteractionOfType
+{
+public:
+    //! Constructor that initializes vectors.
+    InteractionOfType(gmx::ArrayRef<const int>  atoms,
+                      gmx::ArrayRef<const real> params,
+                      const std::string&        name = "");
+    /*!@{*/
+    //! Access the individual elements set for the parameter.
+    const int& ai() const;
+    const int& aj() const;
+    const int& ak() const;
+    const int& al() const;
+    const int& am() const;
+
+    const real& c0() const;
+    const real& c1() const;
+    const real& c2() const;
+
+    const std::string& interactionTypeName() const;
+    /*!@}*/
+
+    /*! \brief Renumbers atom Ids.
+     *
+     *  Enforces that ai() is less than the opposite terminal atom index,
+     *  with the number depending on the interaction type.
+     */
+    void sortAtomIds();
+
+    //! Set single force field parameter.
+    void setForceParameter(int pos, real value);
+
+    //! View on all atoms numbers that are actually set.
+    gmx::ArrayRef<int> atoms() { return atoms_; }
+    //! Const view on all atoms numbers that are actually set.
+    gmx::ArrayRef<const int> atoms() const { return atoms_; }
+    //! View on all of the force field parameters
+    gmx::ArrayRef<const real> forceParam() const { return forceParam_; }
+    //! View on all of the force field parameters
+    gmx::ArrayRef<real> forceParam() { return forceParam_; }
+
+private:
+    //! Return if we have a bond parameter, means two atoms right now.
+    bool isBond() const { return atoms_.size() == 2; }
+    //! Return if we have an angle parameter, means three atoms right now.
+    bool isAngle() const { return atoms_.size() == 3; }
+    //! Return if we have a dihedral parameter, means four atoms right now.
+    bool isDihedral() const { return atoms_.size() == 4; }
+    //! Return if we have a cmap parameter, means five atoms right now.
+    bool isCmap() const { return atoms_.size() == 5; }
+    //! Enforce that atom id ai() is less than aj().
+    void sortBondAtomIds();
+    //! Enforce that atom id ai() is less than ak(). Does not change aj().
+    void sortAngleAtomIds();
+    /*! \brief Enforce order of atoms in dihedral.
+     *
+     * Changes atom order if needed to enforce that ai() is less than al().
+     * If ai() and al() are swapped, aj() and ak() are swapped as well,
+     * independent of their previous order.
+     */
+    void sortDihedralAtomIds();
+    //! The atom list (eg. bonds: particle, i = atoms[0] (ai), j = atoms[1] (aj))
+    std::vector<int> atoms_;
+    //! Force parameters (eg. b0 = forceParam[0])
+    std::array<real, MAXFORCEPARAM> forceParam_;
+    //! Used with forcefields whose .rtp files name the interaction types (e.g. GROMOS), rather than look them up from the atom names.
+    std::string interactionTypeName_;
+};
+
+/*! \libinternal \brief
+ * A set of interactions of a given type
+ * (found in the enumeration in ifunc.h), complete with
+ * atom indices and force field function parameters.
+ *
+ * This is used for containing the data obtained from the
+ * lists of interactions of a given type in a [moleculetype]
+ * topology file definition.
+ */
+struct InteractionsOfType
+{ // NOLINT (clang-analyzer-optin.performance.Padding)
+    //! The different parameters in the system.
+    std::vector<InteractionOfType> interactionTypes;
+    //! CMAP grid spacing.
+    int cmakeGridSpacing = -1;
+    //! Number of cmap angles.
+    int cmapAngles = -1;
+    //! CMAP grid data.
+    std::vector<real> cmap;
+    //! The five atomtypes followed by a number that identifies the type.
+    std::vector<int> cmapAtomTypes;
+
+    //! Number of parameters.
+    size_t size() const { return interactionTypes.size(); }
+    //! Elements in cmap grid data.
+    int ncmap() const { return cmap.size(); }
+    //! Number of elements in cmapAtomTypes.
+    int nct() const { return cmapAtomTypes.size(); }
+};
+
+struct t_excls
+{
+    int  nr; /* The number of exclusions             */
+    int* e;  /* The excluded atoms                   */
+};
+
+
+/*! \libinternal \brief
+ * Holds the molecule information during preprocessing.
+ */
+struct MoleculeInformation
+{
+    //! Name of the molecule.
+    char** name = nullptr;
+    //! Number of exclusions per atom.
+    int nrexcl = 0;
+    //! Have exclusions been generated?.
+    bool excl_set = false;
+    //! Has the mol been processed.
+    bool bProcessed = false;
+    //! Atoms in the moelcule.
+    t_atoms atoms;
+    //! Molecules separated in datastructure.
+    t_block mols;
+    //! Exclusions in the molecule.
+    gmx::ListOfLists<int> excls;
+    //! Interactions of a defined type.
+    std::array<InteractionsOfType, F_NRE> interactions;
+
+    /*! \brief
+     * Initializer.
+     *
+     * This should be removed as soon as the underlying datastructures
+     * have been cleaned up to use proper initialization and can be copy
+     * constructed.
+     */
+    void initMolInfo();
+
+    /*! \brief
+     * Partial clean up function.
+     *
+     * Should be removed once this datastructure actually owns all its own memory and
+     * elements of it are not stolen by other structures and properly copy constructed
+     * or moved.
+     * Cleans up the mols and plist datastructures but not cgs and excls.
+     */
+    void partialCleanUp();
+
+    /*! \brief
+     * Full clean up function.
+     *
+     * Should be removed once the destructor can always do this.
+     */
+    void fullCleanUp();
+};
+
+struct t_mols
+{
+    std::string name;
+    int         nr;
+};
+
+bool is_int(double x);
+/* Returns TRUE when x is integer */
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/h_db.h b/src/include/gromacs/gmxpreprocess/h_db.h
new file mode 100644 (file)
index 0000000..6f68c7e
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2011,2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_H_DB_H
+#define GMX_GMXPREPROCESS_H_DB_H
+
+#include <cstdio>
+
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+
+struct MoleculePatch;
+struct MoleculePatchDatabase;
+
+/* functions for the h-database */
+
+void read_ab(char* line, const char* fn, MoleculePatch* ab);
+/* Read one add block */
+
+/*! \brief
+ * Read the databse from hdb file(s).
+ *
+ * \param[in] ffdir Directory for files.
+ * \param[inout] globalPatches The database for atom modifications to populate.
+ * \returns The number of modifications stored.
+ */
+int read_h_db(const char* ffdir, std::vector<MoleculePatchDatabase>* globalPatches);
+
+void print_ab(FILE* out, const MoleculePatch& ab, const char* nname);
+/* print one add block */
+
+/*! \brief
+ * Search for an entry.
+ *
+ * \param[in] globalPatches Database to search.
+ * \param[in] key Name to search for.
+ */
+gmx::ArrayRef<const MoleculePatchDatabase>::iterator
+search_h_db(gmx::ArrayRef<const MoleculePatchDatabase> globalPatches, const char* key);
+/* Search for an entry in the database */
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/hackblock.h b/src/include/gromacs/gmxpreprocess/hackblock.h
new file mode 100644 (file)
index 0000000..4469056
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \libinternal \brief
+ * Methods to modify atoms during preprocessing.
+ */
+#ifndef GMX_GMXPREPROCESS_HACKBLOCK_H
+#define GMX_GMXPREPROCESS_HACKBLOCK_H
+
+#include <cstdio>
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include "gromacs/gmxpreprocess/notset.h"
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+struct t_atom;
+struct t_symtab;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/*! \brief
+ * Used for reading .rtp/.tdb
+ * BondedTypes::Bonds must be the first, new types can be added to the end
+ * these *MUST* correspond to the arrays in hackblock.cpp
+ */
+enum class BondedTypes : int
+{
+    Bonds,
+    Angles,
+    ProperDihedrals,
+    ImproperDihedrals,
+    Exclusions,
+    Cmap,
+    Count
+};
+//! Names for interaction type entries
+const char* enumValueToString(BondedTypes enumValue);
+//! Numbers for atoms in the interactions.
+int enumValueToNumIAtoms(BondedTypes enumValue);
+
+/* if changing any of these structs, make sure that all of the
+   free/clear/copy/merge_t_* functions stay updated */
+
+/*! \libinternal \brief
+ * Information about single bonded interaction.
+ */
+struct BondedInteraction
+{
+    //! Atom names in the bond.
+    std::array<std::string, MAXATOMLIST> a;
+    /*! \brief
+     * Optional define string which gets copied from
+     * .rtp/.tdb to .top and will be parsed by cpp
+     * during grompp.
+     */
+    std::string s;
+    //! Has the entry been found?
+    bool match = false;
+    //! Get name of first atom in bonded interaction.
+    const std::string& ai() const { return a[0]; }
+    //! Get name of second atom in bonded interaction..
+    const std::string& aj() const { return a[1]; }
+    //! Get name of third atom in bonded interaction.
+    const std::string& ak() const { return a[2]; }
+    //! Get name of fourth atom in bonded interaction.
+    const std::string& al() const { return a[3]; }
+    //! Get name of fifth atom in bonded interaction.
+    const std::string& am() const { return a[4]; }
+};
+
+/*! \libinternal \brief
+ * Accumulation of different bonded types for preprocessing.
+ * \todo This should be merged with BondedInteraction.
+ */
+struct BondedInteractionList
+{
+    //! The type of bonded interaction.
+    int type = -1;
+    //! The actual bonded interactions.
+    std::vector<BondedInteraction> b;
+};
+
+/*! \libinternal \brief
+ * Information about preprocessing residues.
+ */
+struct PreprocessResidue
+{
+    //! Name of the residue.
+    std::string resname;
+    //! The base file name this rtp entry was read from.
+    std::string filebase;
+    //! Atom data.
+    std::vector<t_atom> atom;
+    //! Atom names.
+    std::vector<char**> atomname;
+    //! Charge group numbers.
+    std::vector<int> cgnr;
+    //! Delete autogenerated dihedrals or not.
+    bool bKeepAllGeneratedDihedrals = false;
+    //! Number of bonded exclusions.
+    int nrexcl = -1;
+    //! If Hydrogen only 1-4 interactions should be generated.
+    bool bGenerateHH14Interactions = false;
+    //! Delete dihedrals also defined by impropers.
+    bool bRemoveDihedralIfWithImproper = false;
+    //! List of bonded interactions to potentially add.
+    gmx::EnumerationArray<BondedTypes, BondedInteractionList> rb;
+    //! Get number of atoms in residue.
+    int natom() const { return atom.size(); }
+};
+
+//! Declare different types of hacks for later check.
+enum class MoleculePatchType
+{
+    //! Hack adds atom to structure/rtp.
+    Add,
+    //! Hack deletes atom.
+    Delete,
+    //! Hack replaces atom.
+    Replace
+};
+
+/*! \libinternal \brief
+ * Block to modify individual residues
+ */
+struct MoleculePatch
+{
+    //! Number of new are deleted atoms. NOT always equal to atom.size()!
+    int nr;
+    //! Old name for entry.
+    std::string oname;
+    //! New name for entry.
+    std::string nname;
+    //! New atom data.
+    std::vector<t_atom> atom;
+    //! Chargegroup number.
+    int cgnr = NOTSET;
+    //! Type of attachment.
+    int tp = 0;
+    //! Number of control atoms.
+    int nctl = 0;
+    //! Name of control atoms.
+    std::array<std::string, 4> a;
+    //! Is an atom to be hacked already present?
+    bool bAlreadyPresent = false;
+    //! Are coordinates for a new atom already set?
+    bool bXSet = false;
+    //! New position for hacked atom.
+    rvec newx = { NOTSET };
+    //! New atom index number after additions.
+    int newi = -1;
+
+    /*! \brief
+     * Get type of hack.
+     *
+     * This depends on the setting of oname and nname
+     * for legacy reasons. If oname is empty, we are adding,
+     * if oname is set and nname is empty, an atom is deleted,
+     * if both are set replacement is going on. If both are unset,
+     * an error is thrown.
+     */
+    MoleculePatchType type() const;
+
+    //! Control atom i name.
+    const std::string& ai() const { return a[0]; }
+    //! Control atom j name.
+    const std::string& aj() const { return a[1]; }
+    //! Control atom k name.
+    const std::string& ak() const { return a[2]; }
+    //! Control atom l name.
+    const std::string& al() const { return a[3]; }
+};
+/*! \libinternal \brief
+ * A set of modifications to apply to atoms.
+ */
+struct MoleculePatchDatabase
+{
+    //! Name of block
+    std::string name;
+    //! File that entry was read from.
+    std::string filebase;
+    //! List of changes to atoms.
+    std::vector<MoleculePatch> hack;
+    //! List of bonded interactions to potentially add.
+    gmx::EnumerationArray<BondedTypes, BondedInteractionList> rb;
+    //! Number of atoms to modify
+    int nhack() const { return hack.size(); }
+};
+
+/*!\brief
+ * Reset modification block.
+ *
+ * \param[inout] globalPatches Block to reset.
+ * \todo Remove once constructor/destructor takes care of all of this.
+ */
+void clearModificationBlock(MoleculePatchDatabase* globalPatches);
+
+/*!\brief
+ * Copy residue information.
+ *
+ * \param[in] s Source information.
+ * \param[in] d Destination to copy to.
+ * \param[inout] symtab Symbol table for names.
+ * \todo Remove once copy can be done directly.
+ */
+void copyPreprocessResidues(const PreprocessResidue& s, PreprocessResidue* d, t_symtab* symtab);
+
+/*! \brief
+ * Add bond information in \p s to \p d.
+ *
+ * \param[in] s Source information to copy.
+ * \param[inout] d Destination to copy to.
+ * \param[in] bMin don't copy bondeds with atoms starting with '-'.
+ * \param[in] bPlus don't copy bondeds with atoms starting with '+'.
+ * \returns if bonds were removed at the termini.
+ */
+bool mergeBondedInteractionList(gmx::ArrayRef<const BondedInteractionList> s,
+                                gmx::ArrayRef<BondedInteractionList>       d,
+                                bool                                       bMin,
+                                bool                                       bPlus);
+
+/*! \brief
+ * Copy all information from datastructure.
+ *
+ * \param[in] s Source information.
+ * \param[inout] d Destination to copy to.
+ */
+void copyModificationBlocks(const MoleculePatchDatabase& s, MoleculePatchDatabase* d);
+
+/*!\brief
+ * Add the individual modifications in \p s to \p d.
+ *
+ * \param[in] s Source information.
+ * \param[inout] d Destination to copy to.
+ */
+void mergeAtomModifications(const MoleculePatchDatabase& s, MoleculePatchDatabase* d);
+
+//! \copydoc mergeAtomModifications
+void mergeAtomAndBondModifications(const MoleculePatchDatabase& s, MoleculePatchDatabase* d);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/hizzie.h b/src/include/gromacs/gmxpreprocess/hizzie.h
new file mode 100644 (file)
index 0000000..2c71279
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_HIZZIE_H
+#define GMX_GMXPREPROCESS_HIZZIE_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+struct t_atoms;
+struct t_symtab;
+
+void set_histp(t_atoms* pdba, rvec* x, t_symtab* symtab, real angle, real distance);
+/* calculate HIStidine protonation state */
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/insert_molecules.h b/src/include/gromacs/gmxpreprocess/insert_molecules.h
new file mode 100644 (file)
index 0000000..24fd986
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXPREPROCESS_INSERT_MOLECULES_H
+#define GMX_GMXPREPROCESS_INSERT_MOLECULES_H
+
+#include "gromacs/commandline/cmdlineoptionsmodule.h"
+
+namespace gmx
+{
+
+class InsertMoleculesInfo
+{
+public:
+    static const char*                      name();
+    static const char*                      shortDescription();
+    static ICommandLineOptionsModulePointer create();
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/makeexclusiondistances.h b/src/include/gromacs/gmxpreprocess/makeexclusiondistances.h
new file mode 100644 (file)
index 0000000..57be811
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXPREPROCESS_READ_CONFORMATION_H
+#define GMX_GMXPREPROCESS_READ_CONFORMATION_H
+
+#include <vector>
+
+#include "gromacs/utility/real.h"
+
+class AtomProperties;
+struct t_atoms;
+
+/*! \brief Allocate and fill an array of inter-atomic half distances
+ *
+ * These are either scaled VDW radii taken from vdwradii.dat, or the
+ * default value. Used directly and indirectly by solvate and
+ * insert-molecules for deciding whether molecules clash. The return
+ * pointer should be freed by the caller. */
+std::vector<real> makeExclusionDistances(const t_atoms* a, AtomProperties* aps, real defaultDistance, real scaleFactor);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/nm2type.h b/src/include/gromacs/gmxpreprocess/nm2type.h
new file mode 100644 (file)
index 0000000..720f46e
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMX_NM2TYPE_H
+#define GMX_GMX_NM2TYPE_H
+
+#include <cstdio>
+
+class PreprocessingAtomTypes;
+struct t_atoms;
+struct InteractionsOfType;
+struct t_symtab;
+
+struct t_nm2type
+{
+    char *  elem, *type;
+    double  q, m;
+    int     nbonds;
+    char**  bond;
+    double* blen;
+};
+
+t_nm2type* rd_nm2type(const char* ffdir, int* nnm);
+/* Read the name 2 type database. nnm is the number of entries
+ * ff is the force field.
+ */
+
+void dump_nm2type(FILE* fp, int nnm, t_nm2type nm2t[]);
+/* Dump the database for debugging. Can be reread by the program */
+
+int nm2type(int                     nnm,
+            t_nm2type               nm2t[],
+            t_symtab*               tab,
+            t_atoms*                atoms,
+            PreprocessingAtomTypes* atype,
+            int*                    nbonds,
+            InteractionsOfType*     bond);
+/* Try to determine the atomtype (force field dependent) for the atoms
+ * with help of the bond list
+ */
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/notset.h b/src/include/gromacs/gmxpreprocess/notset.h
new file mode 100644 (file)
index 0000000..1dbf934
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXPREPROCESS_NOTSET_H
+#define GMX_GMXPREPROCESS_NOTSET_H
+
+// TODO: Remove this whole file and make a proper implementation of uninitialized vars.
+static const int NOTSET = -409203;
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/pdb2gmx.h b/src/include/gromacs/gmxpreprocess/pdb2gmx.h
new file mode 100644 (file)
index 0000000..60b72ad
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXPREPROCESS_PDB2GMX_H
+#define GMX_GMXPREPROCESS_PDB2GMX_H
+
+#include "gromacs/commandline/cmdlineoptionsmodule.h"
+
+namespace gmx
+{
+
+class pdb2gmxInfo
+{
+
+public:
+    static const char                       name[];
+    static const char                       shortDescription[];
+    static ICommandLineOptionsModulePointer create();
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/pdb2top.h b/src/include/gromacs/gmxpreprocess/pdb2top.h
new file mode 100644 (file)
index 0000000..c7c8ff2
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_PDB2TOP_H
+#define GMX_GMXPREPROCESS_PDB2TOP_H
+
+#include <cstdio>
+
+#include <string>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+class PreprocessingAtomTypes;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class MDLogger;
+} // namespace gmx
+
+struct t_atoms;
+struct t_excls;
+struct MoleculePatchDatabase;
+struct t_mols;
+struct InteractionsOfType;
+struct t_resinfo;
+struct PreprocessResidue;
+struct DisulfideBond;
+struct t_symtab;
+
+/* this *MUST* correspond to array in pdb2top.cpp */
+enum class HistidineStates : int
+{
+    A,
+    B,
+    H,
+    One,
+    Count
+};
+const char* enumValueToString(HistidineStates enumValue);
+
+void choose_ff(const char*          ffsel,
+               char*                forcefield,
+               int                  ff_maxlen,
+               char*                ffdir,
+               int                  ffdir_maxlen,
+               const gmx::MDLogger& logger);
+/* Find force fields in the current and libdirs and choose an ff.
+ * If ffsel!=NULL: search for ffsel.
+ * If ffsel==NULL: interactive selection.
+ */
+
+void choose_watermodel(const char* wmsel, const char* ffdir, char** watermodel, const gmx::MDLogger& logger);
+/* Choose, possibly interactively, which water model to include,
+ * based on the wmsel command line option choice and watermodels.dat
+ * in ffdir.
+ */
+
+void get_hackblocks_rtp(std::vector<MoleculePatchDatabase>*    globalPatches,
+                        std::vector<PreprocessResidue>*        usedPpResidues,
+                        gmx::ArrayRef<const PreprocessResidue> rtpFFDB,
+                        int                                    nres,
+                        t_resinfo*                             resinfo,
+                        int                                    nterpairs,
+                        t_symtab*                              symtab,
+                        gmx::ArrayRef<MoleculePatchDatabase*>  ntdb,
+                        gmx::ArrayRef<MoleculePatchDatabase*>  ctdb,
+                        gmx::ArrayRef<const int>               rn,
+                        gmx::ArrayRef<const int>               rc,
+                        bool                                   bAllowMissing,
+                        const gmx::MDLogger&                   logger);
+/* Get the database entries for the nres residues in resinfo
+ * and store them in restp and hb.
+ */
+
+void match_atomnames_with_rtp(gmx::ArrayRef<PreprocessResidue>     usedPpResidues,
+                              gmx::ArrayRef<MoleculePatchDatabase> globalPatches,
+                              t_atoms*                             pdba,
+                              t_symtab*                            symtab,
+                              gmx::ArrayRef<gmx::RVec>             x,
+                              bool                                 bVerbose,
+                              const gmx::MDLogger&                 logger);
+/* Check if atom in pdba need to be deleted of renamed due to tdb or hdb.
+ * If renaming involves atoms added wrt to the rtp database,
+ * add these atoms to restp.
+ */
+
+void print_top_comment(FILE* out, const char* filename, const char* ffdir, bool bITP);
+
+void print_top_header(FILE* out, const char* filename, bool bITP, const char* ffdir, real mHmult);
+
+void print_top_mols(FILE*                            out,
+                    const char*                      title,
+                    const char*                      ffdir,
+                    const char*                      water,
+                    gmx::ArrayRef<const std::string> incls,
+                    gmx::ArrayRef<const t_mols>      mols);
+
+void write_top(FILE*                                   out,
+               const char*                             pr,
+               const char*                             molname,
+               t_atoms*                                at,
+               bool                                    bRTPresname,
+               gmx::ArrayRef<const int>                bts,
+               gmx::ArrayRef<const InteractionsOfType> plist,
+               t_excls                                 excls[],
+               PreprocessingAtomTypes*                 atype,
+               int*                                    cgnr,
+               int                                     nrexcl);
+/* NOTE: nrexcl is not the size of *excl! */
+
+void pdb2top(FILE*                                  top_file,
+             const char*                            posre_fn,
+             const char*                            molname,
+             t_atoms*                               atoms,
+             std::vector<gmx::RVec>*                x,
+             PreprocessingAtomTypes*                atype,
+             t_symtab*                              tab,
+             gmx::ArrayRef<const PreprocessResidue> rtpFFDB,
+             gmx::ArrayRef<PreprocessResidue>       usedPpResidues,
+             gmx::ArrayRef<MoleculePatchDatabase>   globalPatches,
+             bool                                   bAllowMissing,
+             bool                                   bVsites,
+             bool                                   bVsiteAromatics,
+             const char*                            ffdir,
+             real                                   mHmult,
+             gmx::ArrayRef<const DisulfideBond>     ssbonds,
+             real                                   long_bond_dist,
+             real                                   short_bond_dist,
+             bool                                   bDeuterate,
+             bool                                   bChargeGroups,
+             bool                                   bCmap,
+             bool                                   bRenumRes,
+             bool                                   bRTPresname,
+             gmx::ArrayRef<const int>               cyclicBondsIndex,
+             const gmx::MDLogger&                   logger);
+/* Create a topology ! */
+
+void print_sums(const t_atoms* atoms, bool bSystem, const gmx::MDLogger& logger);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/pgutil.h b/src/include/gromacs/gmxpreprocess/pgutil.h
new file mode 100644 (file)
index 0000000..54842eb
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_PGUTIL_H
+#define GMX_GMXPREPROCESS_PGUTIL_H
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct t_atom;
+struct t_atoms;
+
+/*! \brief
+ * Search an atom in array of pointers to strings
+ *
+ * If \p type starts with '-', start from \p start.
+ * Will search backwards from that.
+ *
+ * \p bondtype is only used for printing the error/warning string,
+ * when \p bondtype ="check" no error/warning is issued.
+ * When \p bAllowMissing = FALSE an fatal error is issued, otherwise a warning.
+ *
+ * \param[in] type             Atom type string to parse
+ * \param[in] start            Possible position to begin search from.
+ * \param[in] atoms            Global topology atoms information.
+ * \param[in] bondtype         Information what kind of bond, used for error messages.
+ * \param[in] bAllowMissing    If true, missing bond types are allowed.
+ *                             AKA allow cartoon physics.
+ * \param[in] cyclicBondsIndex Information about bonds creating cyclic molecules,
+ *                             empty if no such bonds exist.
+ */
+int search_atom(const char*              type,
+                int                      start,
+                const t_atoms*           atoms,
+                const char*              bondtype,
+                bool                     bAllowMissing,
+                gmx::ArrayRef<const int> cyclicBondsIndex);
+
+/* Similar to search_atom, but this routine searches for the specified
+ * atom in residue resind.
+ */
+int search_res_atom(const char* type, int resind, const t_atoms* atoms, const char* bondtype, bool bAllowMissing);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/readir.h b/src/include/gromacs/gmxpreprocess/readir.h
new file mode 100644 (file)
index 0000000..9b7aa81
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_READIR_H
+#define GMX_GMXPREPROCESS_READIR_H
+
+#include <string>
+
+#include "gromacs/fileio/readinp.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/multipletimestepping.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+class MDModules;
+struct MDModulesNotifiers;
+} // namespace gmx
+
+struct gmx_mtop_t;
+struct gmx_output_env_t;
+struct pull_params_t;
+struct pull_t;
+struct t_blocka;
+struct t_grpopts;
+struct t_inpfile;
+struct t_inputrec;
+struct t_pull_group;
+struct t_pull_coord;
+struct t_rot;
+struct warninp;
+typedef warninp* warninp_t;
+
+enum
+{
+    eshNONE,
+    eshHBONDS,
+    eshALLBONDS,
+    eshHANGLES,
+    eshALLANGLES,
+    eshNR
+};
+
+enum
+{
+    ecouplamVDWQ,
+    ecouplamVDW,
+    ecouplamQ,
+    ecouplamNONE,
+    ecouplamNR
+};
+
+struct t_gromppopts
+{
+    int                warnings  = 0;
+    int                nshake    = 0;
+    char*              include   = nullptr;
+    char*              define    = nullptr;
+    bool               bGenVel   = false;
+    bool               bGenPairs = false;
+    real               tempi     = 0;
+    int                seed      = 0;
+    gmx::GromppMtsOpts mtsOpts;
+    bool               bOrire           = false;
+    bool               bMorse           = false;
+    char*              wall_atomtype[2] = { nullptr, nullptr };
+    char*              couple_moltype   = nullptr;
+    int                couple_lam0      = 0;
+    int                couple_lam1      = 0;
+    bool               bCoupleIntra     = false;
+};
+
+/*! \brief Initialise object to hold strings parsed from an .mdp file */
+void init_inputrec_strings();
+
+/*! \brief Clean up object that holds strings parsed from an .mdp file */
+void done_inputrec_strings();
+
+void check_ir(const char*                    mdparin,
+              const gmx::MDModulesNotifiers& mdModulesNotifiers,
+              t_inputrec*                    ir,
+              t_gromppopts*                  opts,
+              warninp_t                      wi);
+/* Validate inputrec data.
+ * Fatal errors will be added to nerror.
+ */
+int search_string(const char* s, int ng, char* gn[]);
+/* Returns the index of string s in the index groups */
+
+void double_check(t_inputrec* ir, matrix box, bool bHasNormalConstraints, bool bHasAnyConstraints, warninp_t wi);
+/* Do more checks */
+
+void triple_check(const char* mdparin, t_inputrec* ir, gmx_mtop_t* sys, warninp_t wi);
+/* Do even more checks */
+
+void get_ir(const char*     mdparin,
+            const char*     mdparout,
+            gmx::MDModules* mdModules,
+            t_inputrec*     ir,
+            t_gromppopts*   opts,
+            WriteMdpHeader  writeMdpHeader,
+            warninp_t       wi);
+/* Read the input file, and retrieve data for inputrec.
+ * More data are read, but the are only evaluated when the next
+ * function is called. Also prints the input file back to mdparout.
+ */
+
+void do_index(const char*                    mdparin,
+              const char*                    ndx,
+              gmx_mtop_t*                    mtop,
+              bool                           bVerbose,
+              const gmx::MDModulesNotifiers& mdModulesNotifiers,
+              t_inputrec*                    ir,
+              warninp_t                      wi);
+/* Read the index file and assign grp numbers to atoms.
+ */
+
+/* Routines In readpull.c */
+
+std::vector<std::string> read_pullparams(std::vector<t_inpfile>* inp, pull_params_t* pull, warninp_t wi);
+/* Reads the pull parameters, returns a list of the pull group names */
+void process_pull_groups(gmx::ArrayRef<t_pull_group>      pullGroups,
+                         gmx::ArrayRef<const std::string> pullGroupNames,
+                         const t_blocka*                  grps,
+                         char**                           gnames);
+/* Process the pull group parameters after reading the index groups */
+
+void checkPullCoords(gmx::ArrayRef<const t_pull_group> pullGroups,
+                     gmx::ArrayRef<const t_pull_coord> pullCoords);
+/* Process the pull coordinates after reading the pull groups */
+
+pull_t* set_pull_init(t_inputrec*                    ir,
+                      const gmx_mtop_t&              mtop,
+                      gmx::ArrayRef<const gmx::RVec> x,
+                      matrix                         box,
+                      real                           lambda,
+                      warninp_t                      wi);
+/* Prints the initial pull group distances in x.
+ * If requested, adds the current distance to the initial reference location.
+ * Returns the pull_t pull work struct. This should be passed to finish_pull()
+ * after all modules have registered their external potentials, if present.
+ */
+
+std::vector<std::string> read_rotparams(std::vector<t_inpfile>* inp, t_rot* rot, warninp_t wi);
+/* Reads enforced rotation parameters, returns a list of the rot group names */
+
+void make_rotation_groups(t_rot*                           rot,
+                          gmx::ArrayRef<const std::string> rotateGroupNames,
+                          t_blocka*                        grps,
+                          char**                           gnames);
+/* Process the rotation parameters after reading the index groups */
+
+void set_reference_positions(t_rot* rot, rvec* x, matrix box, const char* fn, bool bSet, warninp_t wi);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/resall.h b/src/include/gromacs/gmxpreprocess/resall.h
new file mode 100644 (file)
index 0000000..a4fb876
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \internal \brief
+ * Methods to get residue information during preprocessing.
+ */
+#ifndef GMX_GMXPREPROCESS_RESALL_H
+#define GMX_GMXPREPROCESS_RESALL_H
+
+#include <cstdio>
+
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+
+class PreprocessingAtomTypes;
+
+namespace gmx
+{
+class MDLogger;
+}
+struct PreprocessResidue;
+struct t_symtab;
+
+/*! \brief
+ * Search for an entry in the rtp database.
+ *
+ * A mismatch of one character is allowed,
+ * if there is only one nearly matching entry in the database,
+ * a warning will be generated.
+ *
+ * \param[in] key The atomname to search for.
+ * \param[in] rtpDBEntry Database with residue information.
+ * \param[in] logger Logging object.
+ * \returns The rtp residue name.
+ */
+std::string searchResidueDatabase(const std::string&                     key,
+                                  gmx::ArrayRef<const PreprocessResidue> rtpDBEntry,
+                                  const gmx::MDLogger&                   logger);
+
+/*! \brief
+ * Returns matching entry in database.
+ *
+ * \param[in] rtpname Name of the entry looked for.
+ * \param[in] rtpDBEntry Database to search.
+ * \throws If the name can not be found in the database.
+ */
+gmx::ArrayRef<const PreprocessResidue>::const_iterator
+getDatabaseEntry(const std::string& rtpname, gmx::ArrayRef<const PreprocessResidue> rtpDBEntry);
+
+/*! \brief
+ * Read atom types into database.
+ *
+ * \param[in] ffdir Force field directory.
+ * \param[in] tab Symbol table for names.
+ * \returns Atom type database.
+ */
+PreprocessingAtomTypes read_atype(const char* ffdir, t_symtab* tab);
+
+/*! \brief
+ * Read in database, append to exisiting.
+ *
+ * \param[in] resdb Name of database file.
+ * \param[inout] rtpDBEntry Database to populate.
+ * \param[inout] atype Atomtype information.
+ * \param[inout] tab Symbol table for names.
+ * \param[in] logger MDLogger interface.
+ * \param[in] bAllowOverrideRTP If entries can be overwritten in the database.
+ */
+void readResidueDatabase(const std::string&              resdb,
+                         std::vector<PreprocessResidue>* rtpDBEntry,
+                         PreprocessingAtomTypes*         atype,
+                         t_symtab*                       tab,
+                         const gmx::MDLogger&            logger,
+                         bool                            bAllowOverrideRTP);
+
+/*! \brief
+ * Print out database.
+ *
+ * \param[in] out File to write to.
+ * \param[in] rtpDBEntry Database to write out.
+ * \param[in] atype Atom type information.
+ */
+void print_resall(FILE*                                  out,
+                  gmx::ArrayRef<const PreprocessResidue> rtpDBEntry,
+                  const PreprocessingAtomTypes&          atype);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/solvate.h b/src/include/gromacs/gmxpreprocess/solvate.h
new file mode 100644 (file)
index 0000000..cd81b5c
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXPREPROCESS_SOLVATE_H
+#define GMX_GMXPREPROCESS_SOLVATE_H
+
+int gmx_solvate(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/specbond.h b/src/include/gromacs/gmxpreprocess/specbond.h
new file mode 100644 (file)
index 0000000..008be6c
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_SPECBOND_H
+#define GMX_GMXPREPROCESS_SPECBOND_H
+
+#include <string>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+
+struct t_atoms;
+struct SpecialBond;
+
+struct DisulfideBond
+{
+    int         firstResidue = -1, secondResidue = -1;
+    std::string firstAtom, secondAtom;
+};
+
+std::vector<DisulfideBond> makeDisulfideBonds(t_atoms* pdba, rvec x[], bool bInteractive, bool bVerbose);
+
+std::vector<SpecialBond> generateSpecialBonds();
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/ter_db.h b/src/include/gromacs/gmxpreprocess/ter_db.h
new file mode 100644 (file)
index 0000000..25288ce
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2011,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_TER_DB_H
+#define GMX_GMXPREPROCESS_TER_DB_H
+
+#include <vector>
+
+class PreprocessingAtomTypes;
+struct MoleculePatchDatabase;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/*! \brief
+ * Read database for N&C terminal modifications.
+ *
+ * \param[in] ffdir Directory for files.
+ * \param[in] ter Which terminal side to read.
+ * \param[inout] tbptr Database for terminii entry to populate.
+ * \param[in] atype Database for atomtype information.
+ * \returns Number of entries entered into database.
+ */
+int read_ter_db(const char*                         ffdir,
+                char                                ter,
+                std::vector<MoleculePatchDatabase>* tbptr,
+                PreprocessingAtomTypes*             atype);
+
+/*! \brief
+ * Return entries for modification blocks that match a residue name.
+ *
+ * \param[in] tb Complete modification database.
+ * \param[in] resname Residue name for terminus.
+ * \returns A list of pointers to entries that match, or of nullptr for no matching entry.
+ */
+std::vector<MoleculePatchDatabase*> filter_ter(gmx::ArrayRef<MoleculePatchDatabase> tb, const char* resname);
+
+/*! \brief
+ * Interactively select one terminus.
+ *
+ * \param[in] tb List of possible entries, with pointer to actual entry or nullptr.
+ * \param[in] title Name of entry.
+ * \returns The modification block selected.
+ */
+MoleculePatchDatabase* choose_ter(gmx::ArrayRef<MoleculePatchDatabase*> tb, const char* title);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/tomorse.h b/src/include/gromacs/gmxpreprocess/tomorse.h
new file mode 100644 (file)
index 0000000..e41adeb
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2015,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_TOMORSE_H
+#define GMX_GMXPREPROCESS_TOMORSE_H
+
+class PreprocessingAtomTypes;
+struct MoleculeInformation;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+void convert_harmonics(gmx::ArrayRef<MoleculeInformation> mols, PreprocessingAtomTypes* atype);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/topdirs.h b/src/include/gromacs/gmxpreprocess/topdirs.h
new file mode 100644 (file)
index 0000000..d904e6f
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_TOPDIRS_H
+#define GMX_GMXPREPROCESS_TOPDIRS_H
+
+/* Must correspond to strings in topdirs.cpp */
+enum class Directive : int
+{
+    d_defaults,
+    d_atomtypes,
+    d_bondtypes,
+    d_constrainttypes,
+    d_pairtypes,
+    d_angletypes,
+    d_dihedraltypes,
+    d_nonbond_params,
+    d_implicit_genborn_params,
+    d_implicit_surface_params,
+    d_cmaptypes,
+    d_moleculetype,
+    d_atoms,
+    d_vsites1,
+    d_vsites2,
+    d_vsites3,
+    d_vsites4,
+    d_vsitesn,
+    d_bonds,
+    d_exclusions,
+    d_pairs,
+    d_pairs_nb,
+    d_angles,
+    d_dihedrals,
+    d_constraints,
+    d_settles,
+    d_polarization,
+    d_water_polarization,
+    d_thole_polarization,
+    d_system,
+    d_molecules,
+    d_position_restraints,
+    d_angle_restraints,
+    d_angle_restraints_z,
+    d_distance_restraints,
+    d_orientation_restraints,
+    d_dihedral_restraints,
+    d_cmap,
+    d_intermolecular_interactions,
+    d_maxdir,
+    d_invalid,
+    d_none,
+    Count
+};
+
+const char* enumValueToString(Directive d);
+
+struct DirStack
+{
+    Directive d;
+    DirStack* prev;
+};
+
+int ifunc_index(Directive d, int type);
+
+Directive str2dir(char* dstr);
+
+void DS_Init(DirStack** DS);
+
+void DS_Done(DirStack** DS);
+
+void DS_Push(DirStack** DS, Directive d);
+
+int DS_Search(DirStack* DS, Directive d);
+
+int DS_Check_Order(DirStack* DS, Directive d);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/topio.h b/src/include/gromacs/gmxpreprocess/topio.h
new file mode 100644 (file)
index 0000000..96ee43e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_TOPIO_H
+#define GMX_GMXPREPROCESS_TOPIO_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/utility/real.h"
+
+struct gmx_molblock_t;
+struct gmx_mtop_t;
+class PreprocessingAtomTypes;
+struct t_gromppopts;
+struct t_inputrec;
+struct MoleculeInformation;
+struct InteractionsOfType;
+struct t_symtab;
+struct warninp;
+typedef warninp* warninp_t;
+enum class CombinationRule : int;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class MDLogger;
+} // namespace gmx
+
+double check_mol(const gmx_mtop_t* mtop, warninp_t wi);
+/* Check mass and charge */
+
+char** do_top(bool                                  bVerbose,
+              const char*                           topfile,
+              const char*                           topppfile,
+              t_gromppopts*                         opts,
+              bool                                  bZero,
+              t_symtab*                             symtab,
+              gmx::ArrayRef<InteractionsOfType>     plist,
+              CombinationRule*                      combination_rule,
+              double*                               repulsion_power,
+              real*                                 fudgeQQ,
+              PreprocessingAtomTypes*               atype,
+              std::vector<MoleculeInformation>*     molinfo,
+              std::unique_ptr<MoleculeInformation>* intermolecular_interactions,
+              const t_inputrec*                     ir,
+              std::vector<gmx_molblock_t>*          molblock,
+              bool*                                 ffParametrizedWithHBondConstraints,
+              warninp_t                             wi,
+              const gmx::MDLogger&                  logger);
+
+/* This routine expects sys->molt[m].ilist to be of size F_NRE and ordered. */
+void generate_qmexcl(gmx_mtop_t* sys, t_inputrec* ir, const gmx::MDLogger& logger);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/toppush.h b/src/include/gromacs/gmxpreprocess/toppush.h
new file mode 100644 (file)
index 0000000..302af13
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_TOPPUSH_H
+#define GMX_GMXPREPROCESS_TOPPUSH_H
+
+#include <vector>
+
+#include "gromacs/utility/real.h"
+
+enum class Directive : int;
+class PreprocessingAtomTypes;
+class PreprocessingBondAtomType;
+struct t_atoms;
+struct t_block;
+struct MoleculeInformation;
+struct t_nbparam;
+class InteractionOfType;
+struct InteractionsOfType;
+struct PreprocessResidue;
+struct warninp;
+enum class CombinationRule : int;
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+struct ExclusionBlock;
+} // namespace gmx
+
+void generate_nbparams(CombinationRule         comb,
+                       int                     funct,
+                       InteractionsOfType*     plist,
+                       PreprocessingAtomTypes* atype,
+                       warninp*                wi);
+
+void push_at(struct t_symtab*           symtab,
+             PreprocessingAtomTypes*    at,
+             PreprocessingBondAtomType* bat,
+             char*                      line,
+             int                        nb_funct,
+             t_nbparam***               nbparam,
+             t_nbparam***               pair,
+             warninp*                   wi);
+
+void push_bt(Directive                         d,
+             gmx::ArrayRef<InteractionsOfType> bt,
+             int                               nral,
+             PreprocessingAtomTypes*           at,
+             PreprocessingBondAtomType*        bat,
+             char*                             line,
+             warninp*                          wi);
+
+void push_dihedraltype(Directive                         d,
+                       gmx::ArrayRef<InteractionsOfType> bt,
+                       PreprocessingBondAtomType*        bat,
+                       char*                             line,
+                       warninp*                          wi);
+
+void push_cmaptype(Directive                         d,
+                   gmx::ArrayRef<InteractionsOfType> bt,
+                   int                               nral,
+                   PreprocessingAtomTypes*           at,
+                   PreprocessingBondAtomType*        bat,
+                   char*                             line,
+                   warninp*                          wi);
+
+void push_nbt(Directive d, t_nbparam** nbt, PreprocessingAtomTypes* atype, char* plines, int nb_funct, warninp* wi);
+
+void push_atom(struct t_symtab* symtab, t_atoms* at, PreprocessingAtomTypes* atype, char* line, warninp* wi);
+
+void push_bond(Directive                         d,
+               gmx::ArrayRef<InteractionsOfType> bondtype,
+               gmx::ArrayRef<InteractionsOfType> bond,
+               t_atoms*                          at,
+               PreprocessingAtomTypes*           atype,
+               char*                             line,
+               bool                              bBonded,
+               bool                              bGenPairs,
+               real                              fudgeQQ,
+               bool                              bZero,
+               bool*                             bWarn_copy_A_B,
+               warninp*                          wi);
+
+void push_cmap(Directive                         d,
+               gmx::ArrayRef<InteractionsOfType> bondtype,
+               gmx::ArrayRef<InteractionsOfType> bond,
+               t_atoms*                          at,
+               PreprocessingAtomTypes*           atype,
+               char*                             line,
+               warninp*                          wi);
+
+void push_vsitesn(Directive d, gmx::ArrayRef<InteractionsOfType> bond, t_atoms* at, char* line, warninp* wi);
+
+void push_mol(gmx::ArrayRef<MoleculeInformation> mols, char* pline, int* whichmol, int* nrcopies, warninp* wi);
+
+void push_molt(struct t_symtab* symtab, std::vector<MoleculeInformation>* mol, char* line, warninp* wi);
+
+void push_excl(char* line, gmx::ArrayRef<gmx::ExclusionBlock> b2, warninp* wi);
+
+int copy_nbparams(t_nbparam** param, int ftype, InteractionsOfType* plist, int nr);
+
+void free_nbparam(t_nbparam** param, int nr);
+
+int add_atomtype_decoupled(struct t_symtab*        symtab,
+                           PreprocessingAtomTypes* at,
+                           t_nbparam***            nbparam,
+                           t_nbparam***            pair);
+/* Add an atom type with all parameters set to zero (no interactions).
+ * Returns the atom type number.
+ */
+
+void convert_moltype_couple(MoleculeInformation* mol,
+                            int                  atomtype_decouple,
+                            real                 fudgeQQ,
+                            int                  couple_lam0,
+                            int                  couple_lam1,
+                            bool                 bCoupleIntra,
+                            int                  nb_funct,
+                            InteractionsOfType*  nbp,
+                            warninp*             wi);
+/* Setup mol such that the B-state has no interaction with the rest
+ * of the system, but full interaction with itself.
+ */
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/topshake.h b/src/include/gromacs/gmxpreprocess/topshake.h
new file mode 100644 (file)
index 0000000..93b7bd8
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_TOPSHAKE_H
+#define GMX_GMXPREPROCESS_TOPSHAKE_H
+
+struct t_atoms;
+struct InteractionsOfType;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class MDLogger;
+} // namespace gmx
+
+void make_shake(gmx::ArrayRef<InteractionsOfType> plist, t_atoms* atoms, int nshake, const gmx::MDLogger& logger);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/toputil.h b/src/include/gromacs/gmxpreprocess/toputil.h
new file mode 100644 (file)
index 0000000..ef1fea1
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_TOPUTIL_H
+#define GMX_GMXPREPROCESS_TOPUTIL_H
+
+#include <cstdio>
+
+enum class Directive : int;
+class PreprocessingAtomTypes;
+struct t_atoms;
+struct t_blocka;
+struct t_excls;
+struct MoleculeInformation;
+class InteractionOfType;
+struct InteractionsOfType;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/* UTILITIES */
+
+void add_param_to_list(InteractionsOfType* list, const InteractionOfType& b);
+
+/* PRINTING */
+
+void print_atoms(FILE* out, PreprocessingAtomTypes* atype, t_atoms* at, int* cgnr, bool bRTPresname);
+
+void print_bondeds(FILE*                                   out,
+                   int                                     natoms,
+                   Directive                               d,
+                   int                                     ftype,
+                   int                                     fsubtype,
+                   gmx::ArrayRef<const InteractionsOfType> plist);
+
+void print_excl(FILE* out, int natoms, t_excls excls[]);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/vsite_parm.h b/src/include/gromacs/gmxpreprocess/vsite_parm.h
new file mode 100644 (file)
index 0000000..98d6615
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_VSITE_PARM_H
+#define GMX_GMXPREPROCESS_VSITE_PARM_H
+
+class PreprocessingAtomTypes;
+struct gmx_moltype_t;
+struct t_atoms;
+struct InteractionsOfType;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class MDLogger;
+} // namespace gmx
+
+int set_vsites(bool                              bVerbose,
+               t_atoms*                          atoms,
+               PreprocessingAtomTypes*           atype,
+               gmx::ArrayRef<InteractionsOfType> plist,
+               const gmx::MDLogger&              logger);
+/* set parameters for virtual sites, return number of virtual sites */
+
+void set_vsites_ptype(bool bVerbose, gmx_moltype_t* molt, const gmx::MDLogger& logger);
+/* set ptype to VSite for virtual sites */
+
+/*! \brief Clean up the bonded interactions
+ *
+ * Throw away all obsolete bonds, angles and dihedrals.
+ * Throw away all constraints. */
+void clean_vsite_bondeds(gmx::ArrayRef<InteractionsOfType> ps,
+                         int                               natoms,
+                         bool                              bRmVSiteBds,
+                         const gmx::MDLogger&              logger);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/x2top.h b/src/include/gromacs/gmxpreprocess/x2top.h
new file mode 100644 (file)
index 0000000..39794c9
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GMXPREPROCESS_X2TOP_H
+#define GMX_GMXPREPROCESS_X2TOP_H
+
+int gmx_x2top(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/gmxpreprocess/xlate.h b/src/include/gromacs/gmxpreprocess/xlate.h
new file mode 100644 (file)
index 0000000..1a34376
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXPREPROCESS_XLATE_H
+#define GMX_GMXPREPROCESS_XLATE_H
+
+#include "gromacs/topology/residuetypes.h"
+
+struct t_atoms;
+struct PreprocessResidue;
+struct t_symtab;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/* If bResname is true renames atoms based on residue names,
+ * otherwise renames atoms based on rtp entry names.
+ */
+void rename_atoms(const char*                            xlfile,
+                  const char*                            ffdir,
+                  t_atoms*                               atoms,
+                  t_symtab*                              symtab,
+                  gmx::ArrayRef<const PreprocessResidue> restp,
+                  bool                                   bResname,
+                  const ResidueTypeMap&                  rt,
+                  bool                                   bReorderNum,
+                  bool                                   bVerbose);
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/clfftinitializer.h b/src/include/gromacs/gpu_utils/clfftinitializer.h
new file mode 100644 (file)
index 0000000..25847bd
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares ClfftInitializer class, which initializes and
+ * tears down the clFFT library resources in OpenCL builds,
+ * and does nothing in other builds, and a factory function for it.
+ *
+ * clFFT itself is used in the OpenCL implementation of PME
+ * for 3D R2C/C2R transforms. It is know to work with NVidia
+ * OpenCL, AMD fglrx and AMDGPU-PRO drivers, and to not work with
+ * AMD Rocm dev preview as of May 2018 (#2515).
+ * TODO: find out compatibility with Intel once the rest of PME
+ * gets there (#2516), or by building clFFT own tests.
+ *
+ * \inlibraryapi
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ */
+#ifndef GMX_GPU_UTILS_CLFFTINITIALIZER_H
+#define GMX_GPU_UTILS_CLFFTINITIALIZER_H
+
+#include <memory>
+
+#include "gromacs/utility/classhelpers.h"
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief Handle clFFT library init and tear down in RAII style also
+ * with mutual exclusion.
+ *
+ * Only one thread per process needs to attempt to set up and tear
+ * down the clFFT library, but this wrapper object ensures that it is
+ * safe to do so more than once, or from any thread. It is the
+ * responsibility of the caller to ensure that no use of the clFFT
+ * library is made before making an object of this type, or after the
+ * first such object is destroyed. If no more objects remain to be
+ * destroyed, then it is safe to create another and resume clFFT work.
+ *
+ * \todo Consider making a composite object that also handles
+ * on-demand compilation, managing lifetime of PME FFT kernel programs
+ * to avoid exhausting resources and/or recompiling kernels previously
+ * used.  See Issue #2535.
+ *
+ * \todo Consider implementing an appropriate flavor of the nifty
+ * counter idiom so that both static initialization and
+ * deinitialization can work in a fast, leak-free, and thread-safe way
+ * without imposing constraints on the calling code.
+ * See Issue #2535. */
+class ClfftInitializer
+{
+public:
+    /*! \brief Constructor
+     *
+     * This initializers the clFFT library if there is a
+     * possibility of an FFT task on the device, and preserves it
+     * until destruction. Any time required for this
+     * initialization or tear down should not be accrued to
+     * per-MD-step counters. */
+    ClfftInitializer();
+    //! Destructor
+    ~ClfftInitializer();
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(ClfftInitializer);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/cuda_arch_utils.cuh b/src/include/gromacs/gpu_utils/cuda_arch_utils.cuh
new file mode 100644 (file)
index 0000000..9a2a31d
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef CUDA_ARCH_UTILS_CUH_
+#define CUDA_ARCH_UTILS_CUH_
+
+#include "gromacs/utility/basedefinitions.h"
+
+/*! \file
+ *  \brief CUDA arch dependent definitions.
+ *
+ *  \author Szilard Pall <pall.szilard@gmail.com>
+ */
+
+/* GMX_PTX_ARCH is set to the virtual arch (PTX) version targeted by
+ * the current compiler pass or zero for the host pass and it is
+ * intended to be used instead of __CUDA_ARCH__.
+ */
+#ifndef __CUDA_ARCH__
+#    define GMX_PTX_ARCH 0
+#else
+#    define GMX_PTX_ARCH __CUDA_ARCH__
+#endif
+
+/* Until CC 5.2 and likely for the near future all NVIDIA architectures
+   have a warp size of 32, but this could change later. If it does, the
+   following constants should depend on the value of GMX_PTX_ARCH.
+ */
+static const int warp_size      = 32;
+static const int warp_size_log2 = 5;
+/*! \brief Bitmask corresponding to all threads active in a warp.
+ *  NOTE that here too we assume 32-wide warps.
+ */
+static const unsigned int c_fullWarpMask = 0xffffffff;
+
+/*! \brief Allow disabling CUDA textures using the GMX_DISABLE_CUDA_TEXTURES macro.
+ *
+ *  Only texture objects supported.
+ *  Disable texture support missing in clang (all versions up to <=5.0-dev as of writing).
+ *  Disable texture support on CC 7.0 and 8.0 for performance reasons (Issue #3845).
+ *
+ *  This option will not influence functionality. All features using textures ought
+ *  to have fallback for texture-less reads (direct/LDG loads), all new code needs
+ *  to provide fallback code.
+ */
+#if defined(GMX_DISABLE_CUDA_TEXTURES) || (defined(__clang__) && defined(__CUDA__)) \
+        || (GMX_PTX_ARCH == 700) || (GMX_PTX_ARCH == 800)
+#    define DISABLE_CUDA_TEXTURES 1
+#else
+#    define DISABLE_CUDA_TEXTURES 0
+#endif
+
+/*! \brief True if the use of texture fetch in the CUDA kernels is disabled. */
+static const bool c_disableCudaTextures = DISABLE_CUDA_TEXTURES;
+
+
+/* CUDA architecture technical characteristics. Needs macros because it is used
+ * in the __launch_bounds__ function qualifiers and might need it in preprocessor
+ * conditionals.
+ *
+ */
+#if GMX_PTX_ARCH > 0
+#    if GMX_PTX_ARCH <= 370 // CC 3.x
+#        define GMX_CUDA_MAX_BLOCKS_PER_MP 16
+#        define GMX_CUDA_MAX_THREADS_PER_MP 2048
+#    elif GMX_PTX_ARCH == 750 // CC 7.5, lower limits compared to 7.0
+#        define GMX_CUDA_MAX_BLOCKS_PER_MP 16
+#        define GMX_CUDA_MAX_THREADS_PER_MP 1024
+#    elif GMX_PTX_ARCH == 860 // CC 8.6, lower limits compared to 8.0
+#        define GMX_CUDA_MAX_BLOCKS_PER_MP 16
+#        define GMX_CUDA_MAX_THREADS_PER_MP 1536
+#    else // CC 5.x, 6.x, 7.0, 8.0
+/* Note that this final branch covers all future architectures (current gen
+ * is 8.x as of writing), hence assuming that these *currently defined* upper
+ * limits will not be lowered.
+ */
+#        define GMX_CUDA_MAX_BLOCKS_PER_MP 32
+#        define GMX_CUDA_MAX_THREADS_PER_MP 2048
+#    endif
+#else
+#    define GMX_CUDA_MAX_BLOCKS_PER_MP 0
+#    define GMX_CUDA_MAX_THREADS_PER_MP 0
+#endif
+
+// Macro defined for clang CUDA device compilation in the presence of debug symbols
+// used to work around codegen bug that breaks some kernels when assertions are on
+// at -O1 and higher (tested with clang 6-8).
+#if defined(__clang__) && defined(__CUDA__) && defined(__CUDA_ARCH__) && !defined(NDEBUG)
+#    define CLANG_DISABLE_OPTIMIZATION_ATTRIBUTE __attribute__((optnone))
+#else
+#    define CLANG_DISABLE_OPTIMIZATION_ATTRIBUTE
+#endif
+
+
+#endif /* CUDA_ARCH_UTILS_CUH_ */
diff --git a/src/include/gromacs/gpu_utils/cuda_kernel_utils.cuh b/src/include/gromacs/gpu_utils/cuda_kernel_utils.cuh
new file mode 100644 (file)
index 0000000..b8497c6
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_CUDA_KERNEL_UTILS_CUH
+#define GMX_GPU_UTILS_CUDA_KERNEL_UTILS_CUH
+
+/*! \file
+ *  \brief CUDA device util functions (callable from GPU kernels).
+ *
+ *  \author Szilard Pall <pall.szilard@gmail.com>
+ */
+
+#include "gromacs/gpu_utils/cuda_arch_utils.cuh"
+
+/*! Load directly or using __ldg() when supported. */
+template<typename T>
+__device__ __forceinline__ T LDG(const T* ptr)
+{
+#if GMX_PTX_ARCH >= 350
+    /* CC >=3.5 supports constant loads through texture or L1 */
+    return __ldg(ptr);
+#else
+    /* Device does not have LDG support, fall back to direct load. */
+    return *ptr;
+#endif
+}
+
+/*! \brief Fetch the value by \p index from the texture object.
+ *
+ * \tparam T            Raw data type
+ * \param[in] texObj    Table texture object
+ * \param[in] index     Non-negative element index
+ * \returns             The value from the table at \p index
+ */
+template<typename T>
+static __forceinline__ __device__ T fetchFromTexture(const cudaTextureObject_t texObj, int index)
+{
+    assert(index >= 0);
+    // NOLINTNEXTLINE(misc-static-assert)
+    assert(!c_disableCudaTextures);
+    return tex1Dfetch<T>(texObj, index);
+}
+
+/*! \brief Fetch the value by \p index from the parameter lookup table.
+ *
+ *  Depending on what is supported, it fetches parameters either
+ *  using direct load or texture objects.
+ *
+ * \tparam T            Raw data type
+ * \param[in] d_ptr     Device pointer to the raw table memory
+ * \param[in] texObj    Table texture object
+ * \param[in] index     Non-negative element index
+ * \returns             The value from the table at \p index
+ */
+template<typename T>
+static __forceinline__ __device__ T fetchFromParamLookupTable(const T*                  d_ptr,
+                                                              const cudaTextureObject_t texObj,
+                                                              int                       index)
+{
+    assert(index >= 0);
+    T result;
+#if DISABLE_CUDA_TEXTURES
+    GMX_UNUSED_VALUE(texObj);
+    result = LDG(d_ptr + index);
+#else
+    GMX_UNUSED_VALUE(d_ptr);
+    result = fetchFromTexture<T>(texObj, index);
+#endif
+    return result;
+}
+
+
+#endif /* GMX_GPU_UTILS_CUDA_KERNEL_UTILS_CUH */
diff --git a/src/include/gromacs/gpu_utils/cudautils.cuh b/src/include/gromacs/gpu_utils/cudautils.cuh
new file mode 100644 (file)
index 0000000..0c4507d
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_CUDAUTILS_CUH
+#define GMX_GPU_UTILS_CUDAUTILS_CUH
+
+#include <stdio.h>
+
+#include <array>
+#include <string>
+#include <type_traits>
+
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/gputraits.cuh"
+#include "gromacs/math/vec.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+namespace
+{
+
+/*! \brief Add the API information on the specific error to the error message.
+ *
+ * \param[in]  deviceError  The error to assert cudaSuccess on.
+ *
+ * \returns A description of the API error. Returns '(CUDA error #0 (cudaSuccess): no error)' in case deviceError is cudaSuccess.
+ */
+inline std::string getDeviceErrorString(const cudaError_t deviceError)
+{
+    return formatString("CUDA error #%d (%s): %s.",
+                        deviceError,
+                        cudaGetErrorName(deviceError),
+                        cudaGetErrorString(deviceError));
+}
+
+/*! \brief Check if API returned an error and throw an exception with information on it.
+ *
+ * \param[in]  deviceError  The error to assert cudaSuccess on.
+ * \param[in]  errorMessage  Undecorated error message.
+ *
+ *  \throws InternalError if deviceError is not a success.
+ */
+inline void checkDeviceError(const cudaError_t deviceError, const std::string& errorMessage)
+{
+    if (deviceError != cudaSuccess)
+    {
+        GMX_THROW(gmx::InternalError(errorMessage + " " + getDeviceErrorString(deviceError)));
+    }
+}
+
+/*! \brief Helper function to ensure no pending error silently
+ * disrupts error handling.
+ *
+ * Asserts in a debug build if an unhandled error is present. Issues a
+ * warning at run time otherwise.
+ *
+ * \param[in]  errorMessage  Undecorated error message.
+ */
+inline void ensureNoPendingDeviceError(const std::string& errorMessage)
+{
+    // Ensure there is no pending error that would otherwise affect
+    // the behaviour of future error handling.
+    cudaError_t deviceError = cudaGetLastError();
+    if (deviceError == cudaSuccess)
+    {
+        return;
+    }
+
+    // If we would find an error in a release build, we do not know
+    // what is appropriate to do about it, so assert only for debug
+    // builds.
+    const std::string fullErrorMessage =
+            errorMessage + " An unhandled error from a previous CUDA operation was detected. "
+            + gmx::getDeviceErrorString(deviceError);
+    GMX_ASSERT(deviceError == cudaSuccess, fullErrorMessage.c_str());
+    // TODO When we evolve a better logging framework, use that
+    // for release-build error reporting.
+    gmx_warning("%s", fullErrorMessage.c_str());
+}
+
+} // namespace
+} // namespace gmx
+
+enum class GpuApiCallBehavior;
+
+/* TODO error checking needs to be rewritten. We have 2 types of error checks needed
+   based on where they occur in the code:
+   - non performance-critical: these errors are unsafe to be ignored and must be
+     _always_ checked for, e.g. initializations
+   - performance critical: handling errors might hurt performance so care need to be taken
+     when/if we should check for them at all, e.g. in cu_upload_X. However, we should be
+     able to turn the check for these errors on!
+
+   Probably we'll need two sets of the macros below...
+
+ */
+#define CHECK_CUDA_ERRORS
+
+#ifdef CHECK_CUDA_ERRORS
+
+/*! Check for CUDA error on the return status of a CUDA RT API call. */
+#    define CU_RET_ERR(deviceError, msg)                                                            \
+        do                                                                                          \
+        {                                                                                           \
+            if ((deviceError) != cudaSuccess)                                                       \
+            {                                                                                       \
+                gmx_fatal(FARGS, "%s\n", ((msg) + gmx::getDeviceErrorString(deviceError)).c_str()); \
+            }                                                                                       \
+        } while (0)
+
+#else /* CHECK_CUDA_ERRORS */
+
+#    define CU_RET_ERR(status, msg) \
+        do                          \
+        {                           \
+        } while (0)
+
+#endif /* CHECK_CUDA_ERRORS */
+
+// TODO: the 2 functions below are pretty much a constructor/destructor of a simple
+// GPU table object. There is also almost self-contained fetchFromParamLookupTable()
+// in cuda_kernel_utils.cuh. They could all live in a separate class/struct file.
+
+/*! \brief Add a triplets stored in a float3 to an rvec variable.
+ *
+ * \param[out]  a Rvec to increment
+ * \param[in]   b Float triplet to increment with.
+ */
+static inline void rvec_inc(rvec a, const float3 b)
+{
+    rvec tmp = { b.x, b.y, b.z };
+    rvec_inc(a, tmp);
+}
+
+/*! \brief  Returns true if all tasks in \p s have completed.
+ *
+ *  \param[in] deviceStream CUDA stream to check.
+ *
+ *  \returns True if all tasks enqueued in the stream \p deviceStream (at the time of this call) have completed.
+ */
+static inline bool haveStreamTasksCompleted(const DeviceStream& deviceStream)
+{
+    cudaError_t stat = cudaStreamQuery(deviceStream.stream());
+
+    if (stat == cudaErrorNotReady)
+    {
+        // work is still in progress in the stream
+        return false;
+    }
+
+    GMX_ASSERT(stat != cudaErrorInvalidResourceHandle,
+               ("Stream identifier not valid. " + gmx::getDeviceErrorString(stat)).c_str());
+
+    // cudaSuccess and cudaErrorNotReady are the expected return values
+    CU_RET_ERR(stat, "Unexpected cudaStreamQuery failure. ");
+
+    GMX_ASSERT(stat == cudaSuccess,
+               ("Values other than cudaSuccess should have been explicitly handled. "
+                + gmx::getDeviceErrorString(stat))
+                       .c_str());
+
+    return true;
+}
+
+/* Kernel launch helpers */
+
+/*! \brief
+ * A function for setting up a single CUDA kernel argument.
+ * This is the tail of the compile-time recursive function below.
+ * It has to be seen by the compiler first.
+ *
+ * \tparam        totalArgsCount  Number of the kernel arguments
+ * \tparam        KernelPtr       Kernel function handle type
+ * \param[in]     argIndex        Index of the current argument
+ */
+template<size_t totalArgsCount, typename KernelPtr>
+void prepareGpuKernelArgument(KernelPtr /*kernel*/,
+                              std::array<void*, totalArgsCount>* /* kernelArgsPtr */,
+                              size_t gmx_used_in_debug argIndex)
+{
+    GMX_ASSERT(argIndex == totalArgsCount, "Tail expansion");
+}
+
+/*! \brief
+ * Compile-time recursive function for setting up a single CUDA kernel argument.
+ * This function copies a kernel argument pointer \p argPtr into \p kernelArgsPtr,
+ * and calls itself on the next argument, eventually calling the tail function above.
+ *
+ * \tparam        CurrentArg      Type of the current argument
+ * \tparam        RemainingArgs   Types of remaining arguments after the current one
+ * \tparam        totalArgsCount  Number of the kernel arguments
+ * \tparam        KernelPtr       Kernel function handle type
+ * \param[in]     kernel          Kernel function handle
+ * \param[in,out] kernelArgsPtr   Pointer to the argument array to be filled in
+ * \param[in]     argIndex        Index of the current argument
+ * \param[in]     argPtr          Pointer to the current argument
+ * \param[in]     otherArgsPtrs   Pack of pointers to arguments remaining to process after the current one
+ */
+template<typename CurrentArg, typename... RemainingArgs, size_t totalArgsCount, typename KernelPtr>
+void prepareGpuKernelArgument(KernelPtr                          kernel,
+                              std::array<void*, totalArgsCount>* kernelArgsPtr,
+                              size_t                             argIndex,
+                              const CurrentArg*                  argPtr,
+                              const RemainingArgs*... otherArgsPtrs)
+{
+    (*kernelArgsPtr)[argIndex] = const_cast<void*>(static_cast<const void*>(argPtr));
+    prepareGpuKernelArgument(kernel, kernelArgsPtr, argIndex + 1, otherArgsPtrs...);
+}
+
+/*! \brief
+ * A wrapper function for setting up all the CUDA kernel arguments.
+ * Calls the recursive functions above.
+ *
+ * \tparam    KernelPtr       Kernel function handle type
+ * \tparam    Args            Types of all the kernel arguments
+ * \param[in] kernel          Kernel function handle
+ * \param[in] argsPtrs        Pointers to all the kernel arguments
+ * \returns A prepared parameter pack to be used with launchGpuKernel() as the last argument.
+ */
+template<typename KernelPtr, typename... Args>
+std::array<void*, sizeof...(Args)> prepareGpuKernelArguments(KernelPtr kernel,
+                                                             const KernelLaunchConfig& /*config */,
+                                                             const Args*... argsPtrs)
+{
+    std::array<void*, sizeof...(Args)> kernelArgs;
+    prepareGpuKernelArgument(kernel, &kernelArgs, 0, argsPtrs...);
+    return kernelArgs;
+}
+
+/*! \brief Launches the CUDA kernel and handles the errors.
+ *
+ * \tparam    Args            Types of all the kernel arguments
+ * \param[in] kernel          Kernel function handle
+ * \param[in] config          Kernel configuration for launching
+ * \param[in] deviceStream    GPU stream to launch kernel in
+ * \param[in] kernelName      Human readable kernel description, for error handling only
+ * \param[in] kernelArgs      Array of the pointers to the kernel arguments, prepared by
+ * prepareGpuKernelArguments() \throws gmx::InternalError on kernel launch failure
+ */
+template<typename... Args>
+void launchGpuKernel(void (*kernel)(Args...),
+                     const KernelLaunchConfig& config,
+                     const DeviceStream&       deviceStream,
+                     CommandEvent* /*timingEvent */,
+                     const char*                               kernelName,
+                     const std::array<void*, sizeof...(Args)>& kernelArgs)
+{
+    dim3 blockSize(config.blockSize[0], config.blockSize[1], config.blockSize[2]);
+    dim3 gridSize(config.gridSize[0], config.gridSize[1], config.gridSize[2]);
+    cudaLaunchKernel(reinterpret_cast<void*>(kernel),
+                     gridSize,
+                     blockSize,
+                     const_cast<void**>(kernelArgs.data()),
+                     config.sharedMemorySize,
+                     deviceStream.stream());
+
+    gmx::ensureNoPendingDeviceError("GPU kernel (" + std::string(kernelName)
+                                    + ") failed to launch.");
+}
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/device_context.h b/src/include/gromacs/gpu_utils/device_context.h
new file mode 100644 (file)
index 0000000..ee201f4
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_DEVICE_CONTEXT_H
+#define GMX_GPU_UTILS_DEVICE_CONTEXT_H
+
+/*! \libinternal \file
+ *
+ * \brief Declarations for DeviceContext class.
+ *
+ * Only needed for OpenCL builds. Other platforms will be given a stub class.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_gpu_utils
+ * \inlibraryapi
+ */
+
+#include "config.h"
+
+#include <memory>
+
+#if GMX_GPU_OPENCL
+#    include "gromacs/gpu_utils/gmxopencl.h"
+#endif
+#if GMX_GPU_SYCL
+#    include "gromacs/gpu_utils/gmxsycl.h"
+#endif
+
+#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/hardware/device_management.h"
+#include "gromacs/utility/classhelpers.h"
+
+struct DeviceInformation;
+
+// Stub for device context
+class DeviceContext
+{
+public:
+    //! Constructor.
+    DeviceContext(const DeviceInformation& deviceInfo);
+    //! Destructor
+    // NOLINTNEXTLINE(performance-trivially-destructible)
+    ~DeviceContext();
+
+    //! Get the associated device information
+    const DeviceInformation& deviceInfo() const { return deviceInfo_; }
+
+    void activate() { setActiveDevice(deviceInfo_); }
+
+private:
+    //! A reference to the device information used upon context creation
+    const DeviceInformation& deviceInfo_;
+
+#if GMX_GPU_OPENCL
+public:
+    //! Getter
+    cl_context context() const;
+
+private:
+    //! OpenCL context object
+    cl_context context_ = nullptr;
+#endif
+
+#if GMX_GPU_SYCL
+public:
+    //! Const getter
+    const cl::sycl::context& context() const { return context_; }
+    //! Getter
+    cl::sycl::context& context() { return context_; }
+
+private:
+    //! SYCL context object
+    cl::sycl::context context_;
+#endif
+
+    GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(DeviceContext);
+};
+
+#endif // GMX_GPU_UTILS_DEVICE_CONTEXT_H
diff --git a/src/include/gromacs/gpu_utils/device_event.cuh b/src/include/gromacs/gpu_utils/device_event.cuh
new file mode 100644 (file)
index 0000000..fee0237
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Implements a DeviceEvent class for CUDA.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ *  \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_DEVICE_EVENT_CUH
+#define GMX_GPU_UTILS_DEVICE_EVENT_CUH
+
+#include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/gputraits.cuh"
+#include "gromacs/utility/gmxassert.h"
+
+#ifndef DOXYGEN
+
+class DeviceEvent
+{
+public:
+    DeviceEvent() : isMarked_(false)
+    {
+        cudaError_t stat = cudaEventCreateWithFlags(&event_, cudaEventDisableTiming);
+        if (stat != cudaSuccess)
+        {
+            GMX_THROW(gmx::InternalError("cudaEventCreate failed: " + gmx::getDeviceErrorString(stat)));
+        }
+    }
+    ~DeviceEvent() { cudaEventDestroy(event_); }
+    // Disable copy, move, and assignment. Move can be allowed, but not needed yet.
+    DeviceEvent& operator=(const DeviceEvent&) = delete;
+    DeviceEvent(const DeviceEvent&)            = delete;
+    DeviceEvent& operator=(DeviceEvent&&) = delete;
+    DeviceEvent(DeviceEvent&&)            = delete;
+
+    /*! \brief Marks the synchronization point in the \p stream.
+     * Should be followed by waitForEvent().
+     */
+    inline void mark(const DeviceStream& deviceStream)
+    {
+        cudaError_t stat = cudaEventRecord(event_, deviceStream.stream());
+        if (stat != cudaSuccess)
+        {
+            GMX_THROW(gmx::InternalError("cudaEventRecord failed: " + gmx::getDeviceErrorString(stat)));
+        }
+        isMarked_ = true;
+    }
+    //! Synchronizes the host thread on the marked event.
+    inline void wait()
+    {
+        cudaError_t gmx_used_in_debug stat = cudaEventSynchronize(event_);
+        if (stat != cudaSuccess)
+        {
+            GMX_THROW(gmx::InternalError("cudaEventSynchronize failed: " + gmx::getDeviceErrorString(stat)));
+        }
+    }
+    //! Checks the completion of the underlying event.
+    inline bool isReady()
+    {
+        cudaError_t stat = cudaEventQuery(event_);
+        if (stat != cudaSuccess && stat != cudaErrorNotReady)
+        {
+            GMX_THROW(gmx::InternalError("cudaEventQuery failed: " + gmx::getDeviceErrorString(stat)));
+        }
+        return (stat == cudaSuccess);
+    }
+    //! Check if this event was marked
+    inline bool isMarked() const { return isMarked_; }
+    //! Enqueues a wait for the recorded event in stream \p stream
+    inline void enqueueWait(const DeviceStream& deviceStream)
+    {
+        cudaError_t stat = cudaStreamWaitEvent(deviceStream.stream(), event_, 0);
+        if (stat != cudaSuccess)
+        {
+            GMX_THROW(gmx::InternalError("cudaStreamWaitEvent failed: " + gmx::getDeviceErrorString(stat)));
+        }
+    }
+    //! Reset the event
+    inline void reset() { isMarked_ = false; }
+
+private:
+    cudaEvent_t event_;
+    bool        isMarked_;
+};
+
+#endif
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/device_event.h b/src/include/gromacs/gpu_utils/device_event.h
new file mode 100644 (file)
index 0000000..1082b91
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declares DeviceEvent for all build configuraitons
+ *
+ *  This header may be included from any build configuration and
+ *  defers valid GPU declarations to headers valid only in such
+ *  build configurations.
+ *
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_DEVICE_EVENT_H
+#define GMX_GPU_UTILS_DEVICE_EVENT_H
+
+#include "config.h"
+
+#include "gromacs/utility/exceptions.h"
+
+#if !GMX_GPU || defined(DOXYGEN)
+
+class DeviceStream;
+
+// [[noreturn]] attributes must be added to the methods, so it's
+// easier to silence the warning here and avoid them appearing in
+// the Doxygen
+#    ifdef __clang__
+#        pragma clang diagnostic push
+#        pragma clang diagnostic ignored "-Wmissing-noreturn"
+#    endif
+
+class DeviceEvent
+{
+public:
+    DeviceEvent() = default;
+    // Disable copy, move, and assignment. Move can be allowed, but not needed yet.
+    DeviceEvent& operator=(const DeviceEvent&) = delete;
+    DeviceEvent(const DeviceEvent&)            = delete;
+    DeviceEvent& operator=(DeviceEvent&&) = delete;
+    DeviceEvent(DeviceEvent&&)            = delete;
+
+    /*! \brief Marks the synchronization point in the \p stream.
+     * Should be followed by waitForEvent().
+     */
+    inline void mark(const DeviceStream& /*deviceStream*/) // NOLINT readability-convert-member-functions-to-static
+    {
+        GMX_THROW(gmx::NotImplementedError("Not implemented for non-GPU build"));
+    }
+    //! Synchronizes the host thread on the marked event.
+    inline void wait() // NOLINT readability-convert-member-functions-to-static
+    {
+        GMX_THROW(gmx::NotImplementedError("Not implemented for non-GPU build"));
+    }
+    //! Checks the completion of the underlying event.
+    inline bool isReady() // NOLINT readability-convert-member-functions-to-static
+    {
+        GMX_THROW(gmx::NotImplementedError("Not implemented for non-GPU build"));
+    }
+    //! Enqueues a wait for the recorded event in stream \p stream
+    // NOLINTNEXTLINE readability-convert-member-functions-to-static
+    inline void enqueueWait(const DeviceStream& /*deviceStream*/)
+    {
+        GMX_THROW(gmx::NotImplementedError("Not implemented for non-GPU build"));
+    }
+    //! Checks whether the underlying event was marked.
+    inline bool isMarked() const // NOLINT readability-convert-member-functions-to-static
+    {
+        GMX_THROW(gmx::NotImplementedError("Not implemented for non-GPU build"));
+    }
+
+    //! Reset the event (not needed in CUDA)
+    // NOLINTNEXTLINE readability-convert-member-functions-to-static
+    inline void reset() // NOLINT readability-convert-member-functions-to-static
+    {
+        GMX_THROW(gmx::NotImplementedError("Not implemented for non-GPU build"));
+    }
+};
+
+#    ifdef __clang__
+#        pragma clang diagnostic pop
+#    endif
+
+#elif GMX_GPU_CUDA
+#    include "device_event.cuh"
+#elif GMX_GPU_OPENCL
+#    include "device_event_ocl.h"
+#elif GMX_GPU_SYCL
+#    include "device_event_sycl.h"
+#endif
+
+#endif // GMX_GPU_UTILS_DEVICE_EVENT_H
diff --git a/src/include/gromacs/gpu_utils/device_event_ocl.h b/src/include/gromacs/gpu_utils/device_event_ocl.h
new file mode 100644 (file)
index 0000000..2942c1f
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Implements a DeviceEvent class for OpenCL.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ * \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_DEVICE_EVENT_OCL_H
+#define GMX_GPU_UTILS_DEVICE_EVENT_OCL_H
+
+#include "gromacs/gpu_utils/gputraits_ocl.h"
+#include "gromacs/gpu_utils/oclutils.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+
+#ifndef DOXYGEN
+
+class DeviceEvent
+{
+public:
+    //! A constructor
+    DeviceEvent() : event_(sc_nullEvent) {}
+    DeviceEvent(cl_event event) : event_(event) {}
+    //! A destructor
+    ~DeviceEvent()
+    {
+        if (isMarked())
+        {
+            // Can not throw in destructor, so not checking for any error
+            clReleaseEvent(event_);
+        }
+    }
+    // Disable copy, move, and assignment. Move can be allowed, but not needed yet.
+    DeviceEvent& operator=(const DeviceEvent&) = delete;
+    DeviceEvent(const DeviceEvent&)            = delete;
+    DeviceEvent& operator=(DeviceEvent&&) = delete;
+    DeviceEvent(DeviceEvent&&)            = delete;
+
+    /*! \brief Marks the synchronization point in the \p stream.
+     * Should be called first and then followed by wait().
+     */
+    inline void mark(const DeviceStream& deviceStream)
+    {
+        reset();
+        cl_int clError = clEnqueueMarkerWithWaitList(deviceStream.stream(), 0, nullptr, &event_);
+        if (CL_SUCCESS != clError)
+        {
+            GMX_THROW(gmx::InternalError("Failed to enqueue the GPU synchronization event: "
+                                         + ocl_get_error_string(clError)));
+        }
+    }
+
+    /*! \brief Synchronizes the host thread on the marked event. */
+    inline void wait()
+    {
+        GMX_RELEASE_ASSERT(isMarked(), "Can not wait for an unmarked event");
+        cl_int clError = clWaitForEvents(1, &event_);
+        if (CL_SUCCESS != clError)
+        {
+            GMX_THROW(gmx::InternalError("Failed to synchronize on the GPU event: "
+                                         + ocl_get_error_string(clError)));
+        }
+    }
+
+    /*! \brief Enqueues a wait for the recorded event in stream \p stream. */
+    inline void enqueueWait(const DeviceStream& deviceStream)
+    {
+        GMX_RELEASE_ASSERT(isMarked(), "Can not enqueue an unmarked event");
+        cl_int clError = clEnqueueBarrierWithWaitList(deviceStream.stream(), 1, &event_, nullptr);
+        if (CL_SUCCESS != clError)
+        {
+            GMX_THROW(gmx::InternalError("Failed to enqueue device barrier for the GPU event: "
+                                         + ocl_get_error_string(clError)));
+        }
+    }
+
+    //!  Checks the completion of the underlying event.
+    inline bool isReady()
+    {
+        GMX_RELEASE_ASSERT(isMarked(), "Can not check the status of unmarked event");
+        cl_int result;
+        cl_int clError = clGetEventInfo(
+                event_, CL_EVENT_COMMAND_EXECUTION_STATUS, sizeof(cl_int), &result, nullptr);
+        if (CL_SUCCESS != clError)
+        {
+            GMX_THROW(gmx::InternalError("Failed to retrieve event info: " + ocl_get_error_string(clError)));
+        }
+        return (result == CL_COMPLETE);
+    }
+
+    //! Checks whether this object encapsulates an underlying event.
+    inline bool isMarked() const { return event_ != sc_nullEvent; }
+
+    //! Reset (release) the event to unmarked state.
+    inline void reset()
+    {
+        if (isMarked())
+        {
+            cl_int clError = clReleaseEvent(event_);
+            if (CL_SUCCESS != clError)
+            {
+                GMX_THROW(gmx::InternalError("Failed to release the GPU event: "
+                                             + ocl_get_error_string(clError)));
+            }
+        }
+        event_ = sc_nullEvent;
+    }
+
+private:
+    cl_event event_;
+
+    //! Magic value to indicate uninitialized state.
+    static constexpr cl_event sc_nullEvent = nullptr;
+};
+
+#endif
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/device_event_sycl.h b/src/include/gromacs/gpu_utils/device_event_sycl.h
new file mode 100644 (file)
index 0000000..3404aa5
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Implements a GpuEventSynchronizer class for SYCL.
+ *
+ *  This implementation relies on SYCL_INTEL_enqueue_barrier proposal,
+ *  https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/EnqueueBarrier/enqueue_barrier.asciidoc
+ *
+ *  Using event-based synchronization is not recommended for SYCL.
+ *  SYCL queues are out-of-order and rely on data dependencies, allowing only to wait
+ *  for a specific kernel (by capturing the \c event returned from \c queue.submit) or for all
+ *  the tasks in the queue (\c queue.wait).
+ *
+ *  \author Erik Lindahl <erik.lindahl@gmail.com>
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ * \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_DEVICE_EVENT_SYCL_H
+#define GMX_GPU_UTILS_DEVICE_EVENT_SYCL_H
+
+#include <algorithm>
+#include <vector>
+
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/gmxsycl.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+
+#ifndef DOXYGEN
+
+class DeviceEvent
+{
+public:
+    //! A constructor.
+    DeviceEvent()
+    {
+        doNotSynchronizeBetweenStreams_ = (std::getenv("GMX_GPU_SYCL_NO_SYNCHRONIZE") != nullptr);
+        events_.reserve(1);
+    }
+    //! A constructor from an existing event.
+    DeviceEvent(const cl::sycl::event& event) : events_{ event } {}
+    //! A destructor.
+    ~DeviceEvent() = default;
+    // Disable copy, move, and assignment. They all can be allowed, but not needed yet.
+    DeviceEvent& operator=(const DeviceEvent&) = delete;
+    DeviceEvent(const DeviceEvent&)            = delete;
+    DeviceEvent& operator=(DeviceEvent&&) = delete;
+    DeviceEvent(DeviceEvent&&)            = delete;
+
+    /*! \brief Marks the synchronization point in the \p deviceStream.
+     * Should be called first and then followed by wait() or enqueueWait().
+     */
+    inline void mark(const DeviceStream& deviceStream)
+    {
+#    if GMX_SYCL_HIPSYCL
+        // Relies on HIPSYCL_EXT_QUEUE_WAIT_LIST extension
+        events_   = deviceStream.stream().get_wait_list();
+        isMarked_ = true;
+#    else
+        // Relies on SYCL_INTEL_enqueue_barrier
+        events_ = { deviceStream.stream().submit_barrier() };
+#    endif
+    }
+
+    //! Synchronizes the host thread on the marked event.
+    inline void wait()
+    {
+#    if GMX_SYCL_DPCPP
+        // Note: this is not to prevent use-before-marking, but for checking the DPC++ vs hipSYCL consistency
+        GMX_ASSERT(events_.size() <= 1, "One event expected in DPC++, but we have several!");
+#    endif
+        for (auto& event : events_)
+        {
+            event.wait_and_throw();
+        }
+    }
+
+    inline void enqueueWait(const DeviceStream& deviceStream)
+    {
+        if (!doNotSynchronizeBetweenStreams_)
+        {
+#    if GMX_SYCL_HIPSYCL
+            // Submit an empty kernel that depends on all the events recorded.
+            deviceStream.stream().single_task(events_, [=]() {});
+#    else
+            GMX_ASSERT(events_.size() <= 1, "One event expected in DPC++, but we have several!");
+            // Relies on SYCL_INTEL_enqueue_barrier extensions
+            deviceStream.stream().submit_barrier(events_);
+#    endif
+        }
+    }
+
+    //! Checks the completion of the underlying event.
+    inline bool isReady()
+    {
+        bool allReady = std::all_of(events_.begin(), events_.end(), [](cl::sycl::event& event) {
+            auto info       = event.get_info<cl::sycl::info::event::command_execution_status>();
+            bool isComplete = (info == cl::sycl::info::event_command_status::complete);
+            return isComplete;
+        });
+        return allReady;
+    }
+
+    //! Checks whether this object encapsulates an underlying event.
+    inline bool isMarked() const
+    {
+#    if GMX_SYCL_HIPSYCL
+        return isMarked_;
+#    else
+        return !events_.empty();
+#    endif
+    }
+
+    //! Reset the event to unmarked state.
+    inline void reset()
+    {
+        events_.clear();
+#    if GMX_SYCL_HIPSYCL
+        isMarked_ = false;
+#    endif
+    }
+
+private:
+    std::vector<cl::sycl::event> events_;
+#    if GMX_SYCL_HIPSYCL
+    /*! \brief Flag to track event marking in hipSYCL.
+     *
+     * In hipSYCL, we can have empty \ref events_ after marking if there were no pending tasks in
+     * the queue. So, we use an explicit flag to check the event state. */
+    bool isMarked_ = false;
+#    endif
+    /*! \brief Dev. setting to no-op enqueueWait
+     *
+     * In SYCL, dependencies between the GPU tasks are managed by the runtime, so manual
+     * synchronization between GPU streams should be redundant, but we keep it on by default.
+     *
+     * Setting this to \c true via \c GMX_GPU_SYCL_NO_SYNCHRONIZE environment variable will
+     * immediately return from \ref enqueueWaitEvent, without placing a barrier into the stream.
+     */
+    bool doNotSynchronizeBetweenStreams_;
+};
+
+#endif // DOXYGEN
+
+#endif // GMX_GPU_UTILS_DEVICE_EVENT_SYCL_H
diff --git a/src/include/gromacs/gpu_utils/device_stream.h b/src/include/gromacs/gpu_utils/device_stream.h
new file mode 100644 (file)
index 0000000..e9794e0
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_DEVICE_STREAM_H
+#define GMX_GPU_UTILS_DEVICE_STREAM_H
+
+/*! \libinternal \file
+ *
+ * \brief Declarations for DeviceStream class.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ *
+ * \ingroup module_gpu_utils
+ * \inlibraryapi
+ */
+
+#include "config.h"
+
+#include <memory>
+
+#if GMX_GPU_CUDA
+#    include <cuda_runtime.h>
+
+#elif GMX_GPU_OPENCL
+#    include "gromacs/gpu_utils/gmxopencl.h"
+#elif GMX_GPU_SYCL
+#    include "gromacs/gpu_utils/gmxsycl.h"
+#endif
+
+#include "gromacs/utility/classhelpers.h"
+
+struct DeviceInformation;
+class DeviceContext;
+
+//! Enumeration describing the priority with which a stream operates.
+enum class DeviceStreamPriority : int
+{
+    //! High-priority stream
+    High,
+    //! Normal-priority stream
+    Normal,
+    //! Conventional termination of the enumeration
+    Count
+};
+
+/*! \libinternal \brief Declaration of platform-agnostic device stream/queue.
+ *
+ * The command stream (or command queue) is a sequence of operations that are executed
+ * in they order they were issued. Several streams may co-exist to represent concurrency.
+ * This class declares the interfaces, that are exposed to platform-agnostic code and
+ * it should be implemented for each compute architecture (e.g. CUDA and OpenCL).
+ *
+ * Destruction of the \p DeviceStream calls the destructor of the underlying low-level
+ * stream/queue, hence should only be called when the stream is no longer needed. To
+ * prevent accidental stream destruction, while copying or moving a \p DeviceStream
+ * object, copy and move constructors and copy and move assignments are not allowed
+ * and the \p DeviceStream object should be passed as a pointer or constant reference.
+ *
+ */
+class DeviceStream
+{
+public:
+    /*! \brief Construct and init.
+     *
+     * \param[in] deviceContext  Device context (only used in OpenCL and SYCL).
+     * \param[in] priority       Stream priority: high or normal (only used in CUDA).
+     * \param[in] useTiming      If the timing should be enabled (only used in OpenCL and SYCL).
+     */
+    DeviceStream(const DeviceContext& deviceContext, DeviceStreamPriority priority, bool useTiming);
+
+    //! Destructor
+    // NOLINTNEXTLINE(performance-trivially-destructible)
+    ~DeviceStream();
+
+    /*! \brief Check if the underlying stream is valid.
+     *
+     *  \returns Whether the stream is valid (false in CPU-only builds).
+     */
+    bool isValid() const;
+
+    //! Synchronize the stream
+    void synchronize() const;
+
+#if GMX_GPU_CUDA
+
+    //! Getter
+    cudaStream_t stream() const;
+
+private:
+    cudaStream_t stream_ = nullptr;
+#elif GMX_GPU_SYCL
+
+    /*! \brief
+     * Getter for the underlying \c cl::sycl:queue object.
+     *
+     * Returns a copy instead of const-reference, because it's impossible to submit to or wait
+     * on a \c const cl::sycl::queue. SYCL standard guarantees that operating on copy is
+     * equivalent to operating on the original queue.
+     *
+     * \throws std::bad_optional_access if the stream is not valid.
+     *
+     * \returns A copy of the internal \c cl::sycl:queue.
+     */
+    cl::sycl::queue stream() const { return cl::sycl::queue(stream_); }
+    //! Getter. Can throw std::bad_optional_access if the stream is not valid.
+    cl::sycl::queue& stream() { return stream_; }
+    //! Synchronize the stream. Non-const version of \c ::synchronize() for SYCL that does not do unnecessary copying.
+    void synchronize();
+
+private:
+    cl::sycl::queue stream_;
+#elif GMX_GPU_OPENCL || defined DOXYGEN
+
+    //! Getter
+    cl_command_queue stream() const;
+
+private:
+    cl_command_queue stream_ = nullptr;
+
+#endif
+
+    GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(DeviceStream);
+};
+
+#endif // GMX_GPU_UTILS_DEVICE_STREAM_H
diff --git a/src/include/gromacs/gpu_utils/device_stream_manager.h b/src/include/gromacs/gpu_utils/device_stream_manager.h
new file mode 100644 (file)
index 0000000..036822d
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file declares a manager of GPU context and streams needed for
+ * running workloads on GPUs.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_gpu_utils
+ */
+#ifndef GMX_GPU_UTILS_GPUSTREAMMANAGER_H
+#define GMX_GPU_UTILS_GPUSTREAMMANAGER_H
+
+#include <memory>
+#include <string>
+
+class DeviceContext;
+struct DeviceInformation;
+class DeviceStream;
+
+namespace gmx
+{
+
+class SimulationWorkload;
+
+/*! \brief Class enum to describe the different logical streams used
+ * for GPU work.
+ *
+ * Whether the actual streams differ is an implementation detail of
+ * the manager class.
+ */
+enum class DeviceStreamType : int
+{
+    //! Stream primarily for short-ranged local nonbonded work.
+    NonBondedLocal,
+    //! Stream primarily for short-ranged nonlocal nonbonded work.
+    NonBondedNonLocal,
+    //! Stream primarily for PME work.
+    Pme,
+    //! Stream primarily for data exchange between PME and PP ranks.
+    PmePpTransfer,
+    //! Stream primarily for update and constraints.
+    UpdateAndConstraints,
+    //! Conventional termination of the enumeration.
+    Count
+};
+
+/*! \libinternal
+ * \brief Device stream and context manager.
+ *
+ * Manages the lifetime of the GPU streams and their association
+ * with context and device information that is needed to use them.
+ *
+ * If supported by the GPU API, the available runtime and the
+ * indicated device, some streams will be configured at high
+ * priority. Otherwise, all streams will share the default priority
+ * appropriate to the situation.
+ */
+class DeviceStreamManager
+{
+public:
+    /*! \brief Constructor.
+     *
+     * \throws InternalError  If any of the required resources could not be initialized.
+     */
+    DeviceStreamManager(const DeviceInformation& deviceInfo,
+                        bool                     havePpDomainDecomposition,
+                        SimulationWorkload       simulationWork,
+                        bool                     useTiming);
+    ~DeviceStreamManager();
+
+    /*! \brief Get the device information object of the associated device.
+     *
+     * \returns reference to device info.
+     */
+    const DeviceInformation& deviceInfo() const;
+
+    /*! \brief Returns a handle to the GPU context.
+     *
+     * \todo This relies on the fact that only one unique device
+     * is described by nonbondedDeviceInfo and pmeDeviceInfo.
+     */
+    const DeviceContext& context() const;
+
+    /*! \brief Returns a handle to the requested GPU stream.
+     *
+     * \param[in] streamToGet Which stream to get.
+     */
+    const DeviceStream& stream(DeviceStreamType streamToGet) const;
+
+    /*! \brief Returns a handle to the GPU stream to compute bonded forces in.
+     *
+     * \param[in] hasPPDomainDecomposition Whether there is a particle-particle domain decomposition.
+     */
+    const DeviceStream& bondedStream(bool hasPPDomainDecomposition) const;
+
+    /*! \brief Return whether the requested GPU stream is valid for use.
+     *
+     * \param[in] streamToCheck Which stream to check.
+     *
+     * \returns Whether the stream was initialized.
+     */
+    bool streamIsValid(DeviceStreamType streamToCheck) const;
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/device_utils.clh b/src/include/gromacs/gpu_utils/device_utils.clh
new file mode 100644 (file)
index 0000000..30fe06e
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief OpenCL device-side utilities.
+ *
+ *  Implements device-side macros and inline functions to be used in OpenCL kernels.
+ *
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \ingroup module_gpu_utils
+ */
+
+#ifndef GMX_GPU_UTILS_DEVICE_UTILS_CLH
+#define GMX_GPU_UTILS_DEVICE_UTILS_CLH
+
+///! As far as we know this should be enough to convince OpenCL C compilers
+//   to inline, but if needed, customization should be done here.
+#define gmx_opencl_inline static inline
+
+/* \brief Atomically increment the float value at address addr (in local memory) with value val.
+ *
+ * The adrress addr should be a local memory pointer.
+ *
+ * \param[in]   addr    The address where the data to be added to resides
+ * \param[in]   val     The value to increment with
+ */
+gmx_opencl_inline void atomicAdd_l_f(volatile __local float* addr, float val)
+{
+    union
+    {
+        unsigned int u32;
+        float        f32;
+    } next, expected, current;
+    current.f32 = *addr;
+    do
+    {
+        expected.f32 = current.f32;
+        next.f32     = expected.f32 + val;
+        current.u32  = atomic_cmpxchg((volatile __local unsigned int*)addr, expected.u32, next.u32);
+    } while (current.u32 != expected.u32);
+}
+
+/* \brief Atomically increment the float3 value at address addr (in local memory) with value val.
+ *
+ * Note that the thee components of the float3 addr are incremented sequentially.
+ *
+ * The adrress should be a local memory pointer.
+ *
+ * \param[in]   addr    The address where the data to be added to resides
+ * \param[in]   val     The value to increment with
+ */
+gmx_opencl_inline void atomicAdd_l_f3(__local float3* addr, float3 val)
+{
+    atomicAdd_l_f(((__local float*)(addr)), val.x);
+    atomicAdd_l_f(((__local float*)(addr)) + 1, val.y);
+    atomicAdd_l_f(((__local float*)(addr)) + 2, val.z);
+}
+
+/* \brief Atomically increment the float value at address addr (in global memory) with value val.
+ *
+ * The address addr should be a global memory pointer.
+ *
+ * \param[in]   addr    The address where the data to be added to resides
+ * \param[in]   val     The value to increment with
+ */
+gmx_opencl_inline void atomicAdd_g_f(volatile __global float* addr, float val)
+{
+    union
+    {
+        unsigned int u32;
+        float        f32;
+    } next, expected, current;
+    current.f32 = *addr;
+    do
+    {
+        expected.f32 = current.f32;
+        next.f32     = expected.f32 + val;
+        current.u32 = atomic_cmpxchg((volatile __global unsigned int*)addr, expected.u32, next.u32);
+    } while (current.u32 != expected.u32);
+}
+
+/* \brief Atomically increment the float3 value at address addr (in local memory) with value val.
+ *
+ * Note that the thee components of the float3 addr are incremented sequentially.
+ * On the host float3 is used, but on the device float1 because f3 translates to f4 and messes up memory indexing.
+ *
+ * The adrress addr should be a local memory pointer.
+ *
+ * \param[in]   addr    The address where the data to be added to resides
+ * \param[in]   val     The value to increment with
+ */
+gmx_opencl_inline void atomicAdd_g_f3(__global float* addr, const float3 val)
+{
+    atomicAdd_g_f(addr, val.x);
+    atomicAdd_g_f(addr + 1, val.y);
+    atomicAdd_g_f(addr + 2, val.z);
+}
+
+#endif /* GMX_GPU_UTILS_DEVICE_UTILS_CLH */
diff --git a/src/include/gromacs/gpu_utils/devicebuffer.cuh b/src/include/gromacs/gpu_utils/devicebuffer.cuh
new file mode 100644 (file)
index 0000000..9dd29c2
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_DEVICEBUFFER_CUH
+#define GMX_GPU_UTILS_DEVICEBUFFER_CUH
+
+/*! \libinternal \file
+ *  \brief Implements the DeviceBuffer type and routines for CUDA.
+ *  Should only be included directly by the main DeviceBuffer file devicebuffer.h.
+ *  TODO: the intent is for DeviceBuffer to become a class.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *
+ *  \inlibraryapi
+ */
+
+#include "gromacs/gpu_utils/cuda_arch_utils.cuh"
+#include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gpu_utils.h" //only for GpuApiCallBehavior
+#include "gromacs/gpu_utils/gputraits.cuh"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+/*! \brief
+ * Allocates a device-side buffer.
+ * It is currently a caller's responsibility to call it only on not-yet allocated buffers.
+ *
+ * \tparam        ValueType            Raw value type of the \p buffer.
+ * \param[in,out] buffer               Pointer to the device-side buffer.
+ * \param[in]     numValues            Number of values to accommodate.
+ * \param[in]     deviceContext        The buffer's dummy device  context - not managed explicitly in CUDA RT.
+ */
+template<typename ValueType>
+void allocateDeviceBuffer(DeviceBuffer<ValueType>* buffer, size_t numValues, const DeviceContext& /* deviceContext */)
+{
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    cudaError_t stat = cudaMalloc(buffer, numValues * sizeof(ValueType));
+    GMX_RELEASE_ASSERT(
+            stat == cudaSuccess,
+            ("Allocation of the device buffer failed. " + gmx::getDeviceErrorString(stat)).c_str());
+}
+
+/*! \brief
+ * Frees a device-side buffer.
+ * This does not reset separately stored size/capacity integers,
+ * as this is planned to be a destructor of DeviceBuffer as a proper class,
+ * and no calls on \p buffer should be made afterwards.
+ *
+ * \param[in] buffer  Pointer to the buffer to free.
+ */
+template<typename DeviceBuffer>
+void freeDeviceBuffer(DeviceBuffer* buffer)
+{
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    if (*buffer)
+    {
+        cudaError_t stat = cudaFree(*buffer);
+        GMX_RELEASE_ASSERT(
+                stat == cudaSuccess,
+                ("Freeing of the device buffer failed. " + gmx::getDeviceErrorString(stat)).c_str());
+    }
+}
+
+/*! \brief
+ * Performs the host-to-device data copy, synchronous or asynchronously on request.
+ *
+ * \tparam        ValueType            Raw value type of the \p buffer.
+ * \param[in,out] buffer               Pointer to the device-side buffer
+ * \param[in]     hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
+ * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy into.
+ * \param[in]     numValues            Number of values to copy.
+ * \param[in]     deviceStream         GPU stream to perform asynchronous copy in.
+ * \param[in]     transferKind         Copy type: synchronous or asynchronous.
+ * \param[out]    timingEvent          A dummy pointer to the H2D copy timing event to be filled in.
+ *                                     Not used in CUDA implementation.
+ */
+template<typename ValueType>
+void copyToDeviceBuffer(DeviceBuffer<ValueType>* buffer,
+                        const ValueType*         hostBuffer,
+                        size_t                   startingOffset,
+                        size_t                   numValues,
+                        const DeviceStream&      deviceStream,
+                        GpuApiCallBehavior       transferKind,
+                        CommandEvent* /*timingEvent*/)
+{
+    if (numValues == 0)
+    {
+        return;
+    }
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
+    cudaError_t  stat;
+    const size_t bytes = numValues * sizeof(ValueType);
+
+    switch (transferKind)
+    {
+        case GpuApiCallBehavior::Async:
+            GMX_ASSERT(isHostMemoryPinned(hostBuffer), "Source host buffer was not pinned for CUDA");
+            stat = cudaMemcpyAsync(*reinterpret_cast<ValueType**>(buffer) + startingOffset,
+                                   hostBuffer,
+                                   bytes,
+                                   cudaMemcpyHostToDevice,
+                                   deviceStream.stream());
+            GMX_RELEASE_ASSERT(
+                    stat == cudaSuccess,
+                    ("Asynchronous H2D copy failed. " + gmx::getDeviceErrorString(stat)).c_str());
+            break;
+
+        case GpuApiCallBehavior::Sync:
+            stat = cudaMemcpy(*reinterpret_cast<ValueType**>(buffer) + startingOffset,
+                              hostBuffer,
+                              bytes,
+                              cudaMemcpyHostToDevice);
+            GMX_RELEASE_ASSERT(
+                    stat == cudaSuccess,
+                    ("Synchronous H2D copy failed. " + gmx::getDeviceErrorString(stat)).c_str());
+            break;
+
+        default: throw;
+    }
+}
+
+/*! \brief
+ * Performs the device-to-host data copy, synchronous or asynchronously on request.
+ *
+ * \tparam        ValueType            Raw value type of the \p buffer.
+ * \param[in,out] hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
+ * \param[in]     buffer               Pointer to the device-side buffer
+ * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy from.
+ * \param[in]     numValues            Number of values to copy.
+ * \param[in]     deviceStream         GPU stream to perform asynchronous copy in.
+ * \param[in]     transferKind         Copy type: synchronous or asynchronous.
+ * \param[out]    timingEvent          A dummy pointer to the H2D copy timing event to be filled in.
+ *                                     Not used in CUDA implementation.
+ */
+template<typename ValueType>
+void copyFromDeviceBuffer(ValueType*               hostBuffer,
+                          DeviceBuffer<ValueType>* buffer,
+                          size_t                   startingOffset,
+                          size_t                   numValues,
+                          const DeviceStream&      deviceStream,
+                          GpuApiCallBehavior       transferKind,
+                          CommandEvent* /*timingEvent*/)
+{
+    if (numValues == 0)
+    {
+        return;
+    }
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
+
+    cudaError_t  stat;
+    const size_t bytes = numValues * sizeof(ValueType);
+    switch (transferKind)
+    {
+        case GpuApiCallBehavior::Async:
+            GMX_ASSERT(isHostMemoryPinned(hostBuffer),
+                       "Destination host buffer was not pinned for CUDA");
+            stat = cudaMemcpyAsync(hostBuffer,
+                                   *reinterpret_cast<ValueType**>(buffer) + startingOffset,
+                                   bytes,
+                                   cudaMemcpyDeviceToHost,
+                                   deviceStream.stream());
+            GMX_RELEASE_ASSERT(
+                    stat == cudaSuccess,
+                    ("Asynchronous D2H copy failed. " + gmx::getDeviceErrorString(stat)).c_str());
+            break;
+
+        case GpuApiCallBehavior::Sync:
+            stat = cudaMemcpy(hostBuffer,
+                              *reinterpret_cast<ValueType**>(buffer) + startingOffset,
+                              bytes,
+                              cudaMemcpyDeviceToHost);
+            GMX_RELEASE_ASSERT(
+                    stat == cudaSuccess,
+                    ("Synchronous D2H copy failed. " + gmx::getDeviceErrorString(stat)).c_str());
+            break;
+
+        default: throw;
+    }
+}
+
+/*! \brief
+ * Performs the device-to-device data copy, synchronous or asynchronously on request.
+ *
+ * \tparam        ValueType                Raw value type of the \p buffer.
+ * \param[in,out] destinationDeviceBuffer  Device-side buffer to copy to
+ * \param[in]     sourceDeviceBuffer       Device-side buffer to copy from
+ * \param[in]     numValues                Number of values to copy.
+ * \param[in]     deviceStream             GPU stream to perform asynchronous copy in.
+ * \param[in]     transferKind             Copy type: synchronous or asynchronous.
+ * \param[out]    timingEvent              A dummy pointer to the D2D copy timing event to be filled
+ * in. Not used in CUDA implementation.
+ */
+template<typename ValueType>
+void copyBetweenDeviceBuffers(DeviceBuffer<ValueType>* destinationDeviceBuffer,
+                              DeviceBuffer<ValueType>* sourceDeviceBuffer,
+                              size_t                   numValues,
+                              const DeviceStream&      deviceStream,
+                              GpuApiCallBehavior       transferKind,
+                              CommandEvent* /*timingEvent*/)
+{
+    if (numValues == 0)
+    {
+        return;
+    }
+    GMX_ASSERT(destinationDeviceBuffer, "needs a destination buffer pointer");
+    GMX_ASSERT(sourceDeviceBuffer, "needs a source buffer pointer");
+
+    cudaError_t  stat;
+    const size_t bytes = numValues * sizeof(ValueType);
+    switch (transferKind)
+    {
+        case GpuApiCallBehavior::Async:
+            stat = cudaMemcpyAsync(*destinationDeviceBuffer,
+                                   *sourceDeviceBuffer,
+                                   bytes,
+                                   cudaMemcpyDeviceToDevice,
+                                   deviceStream.stream());
+            GMX_RELEASE_ASSERT(
+                    stat == cudaSuccess,
+                    ("Asynchronous D2D copy failed. " + gmx::getDeviceErrorString(stat)).c_str());
+            break;
+
+        case GpuApiCallBehavior::Sync:
+            stat = cudaMemcpy(*destinationDeviceBuffer, *sourceDeviceBuffer, bytes, cudaMemcpyDeviceToDevice);
+            GMX_RELEASE_ASSERT(
+                    stat == cudaSuccess,
+                    ("Synchronous D2D copy failed. " + gmx::getDeviceErrorString(stat)).c_str());
+            break;
+
+        default: throw;
+    }
+}
+
+/*! \brief
+ * Clears the device buffer asynchronously.
+ *
+ * \tparam        ValueType       Raw value type of the \p buffer.
+ * \param[in,out] buffer          Pointer to the device-side buffer
+ * \param[in]     startingOffset  Offset (in values) at the device-side buffer to start clearing at.
+ * \param[in]     numValues       Number of values to clear.
+ * \param[in]     deviceStream    GPU stream.
+ */
+template<typename ValueType>
+void clearDeviceBufferAsync(DeviceBuffer<ValueType>* buffer,
+                            size_t                   startingOffset,
+                            size_t                   numValues,
+                            const DeviceStream&      deviceStream)
+{
+    if (numValues == 0)
+    {
+        return;
+    }
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    const size_t bytes   = numValues * sizeof(ValueType);
+    const char   pattern = 0;
+
+    cudaError_t stat = cudaMemsetAsync(
+            *reinterpret_cast<ValueType**>(buffer) + startingOffset, pattern, bytes, deviceStream.stream());
+    GMX_RELEASE_ASSERT(stat == cudaSuccess,
+                       ("Couldn't clear the device buffer. " + gmx::getDeviceErrorString(stat)).c_str());
+}
+
+/*! \brief Check the validity of the device buffer.
+ *
+ * Checks if the buffer is not nullptr.
+ *
+ * \todo Add checks on the buffer size when it will be possible.
+ *
+ * \param[in] buffer        Device buffer to be checked.
+ * \param[in] requiredSize  Number of elements that the buffer will have to accommodate.
+ *
+ * \returns Whether the device buffer can be set.
+ */
+template<typename T>
+gmx_unused static bool checkDeviceBuffer(DeviceBuffer<T> buffer, gmx_unused int requiredSize)
+{
+    GMX_ASSERT(buffer != nullptr, "The device pointer is nullptr");
+    return buffer != nullptr;
+}
+
+//! Device texture wrapper.
+using DeviceTexture = cudaTextureObject_t;
+
+/*! \brief Create a texture object for an array of type ValueType.
+ *
+ * Creates the device buffer, copies data and binds texture object for an array of type ValueType.
+ *
+ * \todo Test if using textures is still relevant on modern hardware.
+ *
+ * \tparam      ValueType      Raw data type.
+ *
+ * \param[out]  deviceBuffer   Device buffer to store data in.
+ * \param[out]  deviceTexture  Device texture object to initialize.
+ * \param[in]   hostBuffer     Host buffer to get date from
+ * \param[in]   numValues      Number of elements in the buffer.
+ * \param[in]   deviceContext  GPU device context.
+ */
+template<typename ValueType>
+void initParamLookupTable(DeviceBuffer<ValueType>* deviceBuffer,
+                          DeviceTexture*           deviceTexture,
+                          const ValueType*         hostBuffer,
+                          int                      numValues,
+                          const DeviceContext&     deviceContext)
+{
+    if (numValues == 0)
+    {
+        return;
+    }
+    GMX_ASSERT(hostBuffer, "Host buffer should be specified.");
+
+    allocateDeviceBuffer(deviceBuffer, numValues, deviceContext);
+
+    const size_t sizeInBytes = numValues * sizeof(ValueType);
+
+    cudaError_t stat = cudaMemcpy(
+            *reinterpret_cast<ValueType**>(deviceBuffer), hostBuffer, sizeInBytes, cudaMemcpyHostToDevice);
+
+    GMX_RELEASE_ASSERT(stat == cudaSuccess,
+                       ("Synchronous H2D copy failed. " + gmx::getDeviceErrorString(stat)).c_str());
+
+    if (!c_disableCudaTextures)
+    {
+        cudaResourceDesc rd;
+        cudaTextureDesc  td;
+
+        memset(&rd, 0, sizeof(rd));
+        rd.resType                = cudaResourceTypeLinear;
+        rd.res.linear.devPtr      = *deviceBuffer;
+        rd.res.linear.desc        = cudaCreateChannelDesc<ValueType>();
+        rd.res.linear.sizeInBytes = sizeInBytes;
+
+        memset(&td, 0, sizeof(td));
+        td.readMode = cudaReadModeElementType;
+        stat        = cudaCreateTextureObject(deviceTexture, &rd, &td, nullptr);
+        GMX_RELEASE_ASSERT(
+                stat == cudaSuccess,
+                ("Binding of the texture object failed. " + gmx::getDeviceErrorString(stat)).c_str());
+    }
+}
+
+/*! \brief Unbind the texture and release the CUDA texture object.
+ *
+ * \tparam         ValueType      Raw data type
+ *
+ * \param[in,out]  deviceBuffer   Device buffer to store data in.
+ * \param[in,out]  deviceTexture  Device texture object to unbind.
+ */
+template<typename ValueType>
+void destroyParamLookupTable(DeviceBuffer<ValueType>* deviceBuffer, const DeviceTexture* deviceTexture)
+{
+    if (!c_disableCudaTextures && deviceTexture && deviceBuffer)
+    {
+        cudaError_t stat = cudaDestroyTextureObject(*deviceTexture);
+        GMX_RELEASE_ASSERT(
+                stat == cudaSuccess,
+                ("Destruction of the texture object failed. " + gmx::getDeviceErrorString(stat)).c_str());
+    }
+    freeDeviceBuffer(deviceBuffer);
+}
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/devicebuffer.h b/src/include/gromacs/gpu_utils/devicebuffer.h
new file mode 100644 (file)
index 0000000..d3fc1c9
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_DEVICEBUFFER_H
+#define GMX_GPU_UTILS_DEVICEBUFFER_H
+
+/*! \libinternal \file
+ *  \brief Implements the logic for handling of DeviceBuffer types in OpenCL, CUDA and SYCL.
+ *
+ *  Can only be included on GPU build paths.
+ *
+ *  Note that most of the buffer operations have an early return, if the requested operation
+ *  size is zero. This allows for calling these functions with zero operation size even when
+ *  the underlying buffers were not properly intialized.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *
+ *  \inlibraryapi
+ */
+
+#include "config.h"
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/smalloc.h" // TODO: this is only for over_alloc_large
+
+#if GMX_GPU_CUDA
+#    include "gromacs/gpu_utils/devicebuffer.cuh"
+#elif GMX_GPU_OPENCL
+#    include "gromacs/gpu_utils/devicebuffer_ocl.h"
+#elif GMX_GPU_SYCL
+#    include "gromacs/gpu_utils/devicebuffer_sycl.h"
+#else
+#    error "devicebuffer.h included on non-GPU build!"
+#endif
+
+/*! \brief
+ *  Reallocates the device-side buffer.
+ *
+ *  Reallocates the device-side memory pointed by \p buffer.
+ *  Allocation is buffered and therefore freeing is only needed
+ *  if the previously allocated space is not enough.
+ *  \p currentNumValues and \p currentMaxNumValues are updated.
+ *  TODO: \p currentNumValues, \p currentMaxNumValues, \p deviceContext
+ *  should all be encapsulated in a host-side class together with the buffer.
+ *
+ *  \tparam        ValueType            Raw value type of the \p buffer.
+ *  \param[in,out] buffer               Pointer to the device-side buffer
+ *  \param[in]     numValues            Number of values to accommodate.
+ *  \param[in,out] currentNumValues     The pointer to the buffer's number of values.
+ *  \param[in,out] currentMaxNumValues  The pointer to the buffer's capacity.
+ *  \param[in]     deviceContext        The buffer's device context.
+ */
+template<typename ValueType>
+void reallocateDeviceBuffer(DeviceBuffer<ValueType>* buffer,
+                            size_t                   numValues,
+                            int*                     currentNumValues,
+                            int*                     currentMaxNumValues,
+                            const DeviceContext&     deviceContext)
+{
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    GMX_ASSERT(currentNumValues, "needs a size pointer");
+    GMX_ASSERT(currentMaxNumValues, "needs a capacity pointer");
+
+    /* reallocate only if the data does not fit */
+    if (static_cast<int>(numValues) > *currentMaxNumValues)
+    {
+        if (*currentMaxNumValues >= 0)
+        {
+            freeDeviceBuffer(buffer);
+        }
+
+        *currentMaxNumValues = over_alloc_large(numValues);
+        allocateDeviceBuffer(buffer, *currentMaxNumValues, deviceContext);
+    }
+    /* size could have changed without actual reallocation */
+    *currentNumValues = numValues;
+}
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/devicebuffer_datatype.h b/src/include/gromacs/gpu_utils/devicebuffer_datatype.h
new file mode 100644 (file)
index 0000000..d1c27bd
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_DEVICEBUFFER_DATATYPE_H
+#define GMX_GPU_UTILS_DEVICEBUFFER_DATATYPE_H
+
+/*! \libinternal \file
+ *  \brief Declares the DeviceBuffer data type.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ *  \inlibraryapi
+ */
+
+#include "config.h"
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+
+#if GMX_GPU_CUDA
+
+//! \brief A device-side buffer of ValueTypes
+template<typename ValueType>
+using DeviceBuffer = ValueType*;
+
+#elif GMX_GPU_OPENCL
+
+#    include "gromacs/gpu_utils/gputraits_ocl.h"
+
+/*! \libinternal \brief
+ * A minimal cl_mem wrapper that remembers its allocation type.
+ * The only point is making template type deduction possible.
+ */
+template<typename ValueType>
+class TypedClMemory
+{
+private:
+    /*! \brief Underlying data
+     *
+     *  \todo Make it nullptr when there are no snew()'s around
+     */
+    cl_mem data_;
+
+public:
+    //! \brief An assignment operator - the purpose is to make allocation/zeroing work
+    TypedClMemory& operator=(cl_mem data)
+    {
+        data_ = data;
+        return *this;
+    }
+    //! \brief Returns underlying cl_mem transparently
+    operator cl_mem() { return data_; }
+};
+
+//! \libinternal \brief A device-side buffer of ValueTypes
+template<typename ValueType>
+using DeviceBuffer = TypedClMemory<ValueType>;
+
+#elif GMX_GPU_SYCL
+
+/*! \libinternal \brief
+ * A minimal wrapper around \c cl::sycl::buffer to hide it away and simplify compilation.
+ */
+template<typename ValueType>
+struct DeviceBuffer
+{
+    class ClSyclBufferWrapper;
+    std::unique_ptr<ClSyclBufferWrapper> buffer_;
+
+    DeviceBuffer();
+    ~DeviceBuffer();
+    DeviceBuffer(DeviceBuffer<ValueType> const& src);
+    DeviceBuffer(DeviceBuffer<ValueType>&& src) noexcept;
+    DeviceBuffer& operator=(DeviceBuffer<ValueType> const& src);
+    DeviceBuffer& operator=(DeviceBuffer<ValueType>&& src) noexcept;
+
+    //! Helper function to get the size in bytes of a single element
+    static constexpr size_t elementSize() { return sizeof(ValueType); }
+
+    // Both explicit and implicit casts to void* are used in MPI+CUDA code, this stub is necessary for compilation.
+    operator void*() const { throw; }
+
+    //! Allow implicit conversion to bool to check buffer status for compatibility with other implementations.
+    operator bool() const { return buffer_.get() != nullptr; }
+
+    //! An assignment operator to allow resetting buffer by assigning nullptr to it. Necessary for compilation.
+    DeviceBuffer& operator=(std::nullptr_t nullPtr);
+};
+
+// Must explicitly instantiate for some types.
+extern template struct DeviceBuffer<gmx::RVec>;
+
+#else
+
+//! \brief A device-side buffer of ValueTypes
+template<typename ValueType>
+using DeviceBuffer = void*;
+
+#endif
+
+#endif // GMX_GPU_UTILS_DEVICEBUFFER_DATATYPE_H
diff --git a/src/include/gromacs/gpu_utils/devicebuffer_ocl.h b/src/include/gromacs/gpu_utils/devicebuffer_ocl.h
new file mode 100644 (file)
index 0000000..a6d9e68
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_DEVICEBUFFER_OCL_H
+#define GMX_GPU_UTILS_DEVICEBUFFER_OCL_H
+
+/*! \libinternal \file
+ *  \brief Implements the DeviceBuffer type and routines for OpenCL.
+ *  Should only be included directly by the main DeviceBuffer file devicebuffer.h.
+ *  TODO: the intent is for DeviceBuffer to become a class.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *
+ *  \inlibraryapi
+ */
+
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gpu_utils.h" //only for GpuApiCallBehavior
+#include "gromacs/gpu_utils/gputraits_ocl.h"
+#include "gromacs/gpu_utils/oclutils.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+/*! \libinternal \brief
+ * Allocates a device-side buffer.
+ * It is currently a caller's responsibility to call it only on not-yet allocated buffers.
+ *
+ * \tparam        ValueType            Raw value type of the \p buffer.
+ * \param[in,out] buffer               Pointer to the device-side buffer.
+ * \param[in]     numValues            Number of values to accommodate.
+ * \param[in]     deviceContext        The buffer's device context-to-be.
+ */
+template<typename ValueType>
+void allocateDeviceBuffer(DeviceBuffer<ValueType>* buffer, size_t numValues, const DeviceContext& deviceContext)
+{
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    void*  hostPtr = nullptr;
+    cl_int clError;
+    *buffer = clCreateBuffer(
+            deviceContext.context(), CL_MEM_READ_WRITE, numValues * sizeof(ValueType), hostPtr, &clError);
+    GMX_RELEASE_ASSERT(clError == CL_SUCCESS,
+                       gmx::formatString("clCreateBuffer failure (OpenCL error %d: %s)",
+                                         clError,
+                                         ocl_get_error_string(clError).c_str())
+                               .c_str());
+}
+
+/*! \brief
+ * Frees a device-side buffer.
+ * This does not reset separately stored size/capacity integers,
+ * as this is planned to be a destructor of DeviceBuffer as a proper class,
+ * and no calls on \p buffer should be made afterwards.
+ *
+ * \param[in] buffer  Pointer to the buffer to free.
+ */
+template<typename DeviceBuffer>
+void freeDeviceBuffer(DeviceBuffer* buffer)
+{
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    if (*buffer)
+    {
+        cl_int clError = clReleaseMemObject(*buffer);
+        GMX_RELEASE_ASSERT(clError == CL_SUCCESS,
+                           gmx::formatString("clReleaseMemObject failed (OpenCL error %d: %s)",
+                                             clError,
+                                             ocl_get_error_string(clError).c_str())
+                                   .c_str());
+    }
+}
+
+/*! \brief
+ * Performs the host-to-device data copy, synchronous or asynchronously on request.
+ *
+ * Note that synchronous copy will not synchronize the stream in case of zero \p numValues
+ * because of the early return.
+ *
+ * \tparam        ValueType            Raw value type of the \p buffer.
+ * \param[in,out] buffer               Pointer to the device-side buffer
+ * \param[in]     hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
+ * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy into.
+ * \param[in]     numValues            Number of values to copy.
+ * \param[in]     deviceStream         GPU stream to perform asynchronous copy in.
+ * \param[in]     transferKind         Copy type: synchronous or asynchronous.
+ * \param[out]    timingEvent          A pointer to the H2D copy timing event to be filled in.
+ *                                     If the pointer is not null, the event can further be used
+ *                                     to queue a wait for this operation or to query profiling information.
+ */
+template<typename ValueType>
+void copyToDeviceBuffer(DeviceBuffer<ValueType>* buffer,
+                        const ValueType*         hostBuffer,
+                        size_t                   startingOffset,
+                        size_t                   numValues,
+                        const DeviceStream&      deviceStream,
+                        GpuApiCallBehavior       transferKind,
+                        CommandEvent*            timingEvent)
+{
+    if (numValues == 0)
+    {
+        return; // such calls are actually made with empty domains
+    }
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
+    cl_int       clError;
+    const size_t offset = startingOffset * sizeof(ValueType);
+    const size_t bytes  = numValues * sizeof(ValueType);
+    switch (transferKind)
+    {
+        case GpuApiCallBehavior::Async:
+            clError = clEnqueueWriteBuffer(
+                    deviceStream.stream(), *buffer, CL_FALSE, offset, bytes, hostBuffer, 0, nullptr, timingEvent);
+            GMX_RELEASE_ASSERT(
+                    clError == CL_SUCCESS,
+                    gmx::formatString("Asynchronous H2D copy failed (OpenCL error %d: %s)",
+                                      clError,
+                                      ocl_get_error_string(clError).c_str())
+                            .c_str());
+            break;
+
+        case GpuApiCallBehavior::Sync:
+            clError = clEnqueueWriteBuffer(
+                    deviceStream.stream(), *buffer, CL_TRUE, offset, bytes, hostBuffer, 0, nullptr, timingEvent);
+            GMX_RELEASE_ASSERT(
+                    clError == CL_SUCCESS,
+                    gmx::formatString("Synchronous H2D copy failed (OpenCL error %d: %s)",
+                                      clError,
+                                      ocl_get_error_string(clError).c_str())
+                            .c_str());
+            break;
+
+        default: throw;
+    }
+}
+
+/*! \brief
+ * Performs the device-to-host data copy, synchronous or asynchronously on request.
+ *
+ * Note that synchronous copy will not synchronize the stream in case of zero \p numValues
+ * because of the early return.
+ *
+ * \tparam        ValueType            Raw value type of the \p buffer.
+ * \param[in,out] hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
+ * \param[in]     buffer               Pointer to the device-side buffer
+ * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy from.
+ * \param[in]     numValues            Number of values to copy.
+ * \param[in]     deviceStream         GPU stream to perform asynchronous copy in.
+ * \param[in]     transferKind         Copy type: synchronous or asynchronous.
+ * \param[out]    timingEvent          A pointer to the H2D copy timing event to be filled in.
+ *                                     If the pointer is not null, the event can further be used
+ *                                     to queue a wait for this operation or to query profiling information.
+ */
+template<typename ValueType>
+void copyFromDeviceBuffer(ValueType*               hostBuffer,
+                          DeviceBuffer<ValueType>* buffer,
+                          size_t                   startingOffset,
+                          size_t                   numValues,
+                          const DeviceStream&      deviceStream,
+                          GpuApiCallBehavior       transferKind,
+                          CommandEvent*            timingEvent)
+{
+    if (numValues == 0)
+    {
+        return;
+    }
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
+    cl_int       clError;
+    const size_t offset = startingOffset * sizeof(ValueType);
+    const size_t bytes  = numValues * sizeof(ValueType);
+    switch (transferKind)
+    {
+        case GpuApiCallBehavior::Async:
+            clError = clEnqueueReadBuffer(
+                    deviceStream.stream(), *buffer, CL_FALSE, offset, bytes, hostBuffer, 0, nullptr, timingEvent);
+            GMX_RELEASE_ASSERT(
+                    clError == CL_SUCCESS,
+                    gmx::formatString("Asynchronous D2H copy failed (OpenCL error %d: %s)",
+                                      clError,
+                                      ocl_get_error_string(clError).c_str())
+                            .c_str());
+            break;
+
+        case GpuApiCallBehavior::Sync:
+            clError = clEnqueueReadBuffer(
+                    deviceStream.stream(), *buffer, CL_TRUE, offset, bytes, hostBuffer, 0, nullptr, timingEvent);
+            GMX_RELEASE_ASSERT(
+                    clError == CL_SUCCESS,
+                    gmx::formatString("Synchronous D2H copy failed (OpenCL error %d: %s)",
+                                      clError,
+                                      ocl_get_error_string(clError).c_str())
+                            .c_str());
+            break;
+
+        default: throw;
+    }
+}
+
+/*! \brief
+ * Performs the device-to-device data copy, synchronous or asynchronously on request.
+ *
+ * \tparam        ValueType                Raw value type of the \p buffer.
+ */
+template<typename ValueType>
+void copyBetweenDeviceBuffers(DeviceBuffer<ValueType>* /* destinationDeviceBuffer */,
+                              DeviceBuffer<ValueType>* /* sourceDeviceBuffer */,
+                              size_t /* numValues */,
+                              const DeviceStream& /* deviceStream */,
+                              GpuApiCallBehavior /* transferKind */,
+                              CommandEvent* /*timingEvent*/)
+{
+    // OpenCL-TODO
+    gmx_fatal(FARGS, "D2D copy stub was called. Not yet implemented in OpenCL.");
+}
+
+/*! \brief
+ * Clears the device buffer asynchronously.
+ *
+ * \tparam        ValueType       Raw value type of the \p buffer.
+ * \param[in,out] buffer          Pointer to the device-side buffer
+ * \param[in]     startingOffset  Offset (in values) at the device-side buffer to start clearing at.
+ * \param[in]     numValues       Number of values to clear.
+ * \param[in]     deviceStream    GPU stream.
+ */
+template<typename ValueType>
+void clearDeviceBufferAsync(DeviceBuffer<ValueType>* buffer,
+                            size_t                   startingOffset,
+                            size_t                   numValues,
+                            const DeviceStream&      deviceStream)
+{
+    if (numValues == 0)
+    {
+        return;
+    }
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    const size_t    offset        = startingOffset * sizeof(ValueType);
+    const size_t    bytes         = numValues * sizeof(ValueType);
+    const int       pattern       = 0;
+    const cl_uint   numWaitEvents = 0;
+    const cl_event* waitEvents    = nullptr;
+    cl_event        commandEvent;
+    cl_int          clError = clEnqueueFillBuffer(
+            deviceStream.stream(), *buffer, &pattern, sizeof(pattern), offset, bytes, numWaitEvents, waitEvents, &commandEvent);
+    GMX_RELEASE_ASSERT(clError == CL_SUCCESS,
+                       gmx::formatString("Couldn't clear the device buffer (OpenCL error %d: %s)",
+                                         clError,
+                                         ocl_get_error_string(clError).c_str())
+                               .c_str());
+}
+
+#if defined(__clang__)
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wunused-template"
+#endif
+
+/*! \brief Check the validity of the device buffer.
+ *
+ * Checks if the buffer is not nullptr and if its allocation is big enough.
+ *
+ * \param[in] buffer        Device buffer to be checked.
+ * \param[in] requiredSize  Number of elements that the buffer will have to accommodate.
+ *
+ * \returns Whether the device buffer can be set.
+ */
+template<typename T>
+static bool checkDeviceBuffer(DeviceBuffer<T> buffer, int requiredSize)
+{
+    const size_t requiredSizeBytes = requiredSize * sizeof(T);
+    size_t       sizeBytes;
+    cl_int retval = clGetMemObjectInfo(buffer, CL_MEM_SIZE, sizeof(sizeBytes), &sizeBytes, nullptr);
+    GMX_ASSERT(retval == CL_SUCCESS,
+               gmx::formatString("clGetMemObjectInfo failed with error code #%d", retval).c_str());
+    GMX_ASSERT(sizeBytes >= requiredSizeBytes,
+               "Number of atoms in device buffer is smaller then required size.");
+    return retval == CL_SUCCESS && sizeBytes >= requiredSizeBytes;
+}
+
+//! Device texture wrapper.
+using DeviceTexture = void*;
+
+/*! \brief Create a texture object for an array of type ValueType.
+ *
+ * Creates the device buffer and copies read-only data for an array of type ValueType.
+ *
+ * \todo Decide if using image2d is most efficient.
+ *
+ * \tparam      ValueType      Raw data type.
+ *
+ * \param[out]  deviceBuffer   Device buffer to store data in.
+ * \param[in]   hostBuffer     Host buffer to get date from.
+ * \param[in]   numValues      Number of elements in the buffer.
+ * \param[in]   deviceContext  GPU device context.
+ */
+template<typename ValueType>
+void initParamLookupTable(DeviceBuffer<ValueType>* deviceBuffer,
+                          DeviceTexture* /* deviceTexture */,
+                          const ValueType*     hostBuffer,
+                          int                  numValues,
+                          const DeviceContext& deviceContext)
+{
+    GMX_ASSERT(hostBuffer, "Host buffer pointer can not be null");
+    const size_t bytes = numValues * sizeof(ValueType);
+    cl_int       clError;
+    *deviceBuffer = clCreateBuffer(deviceContext.context(),
+                                   CL_MEM_READ_ONLY | CL_MEM_HOST_WRITE_ONLY | CL_MEM_COPY_HOST_PTR,
+                                   bytes,
+                                   const_cast<ValueType*>(hostBuffer),
+                                   &clError);
+
+    GMX_RELEASE_ASSERT(clError == CL_SUCCESS,
+                       gmx::formatString("Constant memory allocation failed (OpenCL error %d: %s)",
+                                         clError,
+                                         ocl_get_error_string(clError).c_str())
+                               .c_str());
+}
+
+/*! \brief Release the OpenCL device buffer.
+ *
+ * \tparam        ValueType     Raw data type.
+ *
+ * \param[in,out] deviceBuffer  Device buffer to store data in.
+ */
+template<typename ValueType>
+void destroyParamLookupTable(DeviceBuffer<ValueType>* deviceBuffer, const DeviceTexture& /* deviceTexture*/)
+{
+    freeDeviceBuffer(deviceBuffer);
+}
+#if defined(__clang__)
+#    pragma clang diagnostic pop
+#endif
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/devicebuffer_sycl.h b/src/include/gromacs/gpu_utils/devicebuffer_sycl.h
new file mode 100644 (file)
index 0000000..cb3277b
--- /dev/null
@@ -0,0 +1,547 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_DEVICEBUFFER_SYCL_H
+#define GMX_GPU_UTILS_DEVICEBUFFER_SYCL_H
+
+/*! \libinternal \file
+ *  \brief Implements the DeviceBuffer type and routines for SYCL.
+ *  Should only be included directly by the main DeviceBuffer file devicebuffer.h.
+ *  TODO: the intent is for DeviceBuffer to become a class.
+ *
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *  \author Erik Lindahl <erik.lindahl@gmail.com>
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ *
+ *  \inlibraryapi
+ */
+
+#include <utility>
+
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gmxsycl.h"
+#include "gromacs/gpu_utils/gpu_utils.h" //only for GpuApiCallBehavior
+#include "gromacs/gpu_utils/gputraits_sycl.h"
+#include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+#ifndef DOXYGEN
+template<typename T>
+class DeviceBuffer<T>::ClSyclBufferWrapper : public cl::sycl::buffer<T, 1>
+{
+    using cl::sycl::buffer<T, 1>::buffer; // Get all the constructors
+};
+
+template<typename T>
+using ClSyclBufferWrapper = typename DeviceBuffer<T>::ClSyclBufferWrapper;
+
+//! Constructor.
+template<typename T>
+DeviceBuffer<T>::DeviceBuffer() : buffer_(nullptr)
+{
+}
+
+//! Destructor.
+template<typename T>
+DeviceBuffer<T>::~DeviceBuffer() = default;
+
+//! Copy constructor (references the same underlying SYCL buffer).
+template<typename T>
+DeviceBuffer<T>::DeviceBuffer(DeviceBuffer<T> const& src)
+{
+    if (src.buffer_)
+    {
+        buffer_ = std::make_unique<ClSyclBufferWrapper>(*src.buffer_);
+    }
+    else
+    {
+        buffer_ = nullptr;
+    }
+}
+
+//! Move constructor.
+template<typename T>
+DeviceBuffer<T>::DeviceBuffer(DeviceBuffer<T>&& src) noexcept = default;
+
+//! Copy assignment (references the same underlying SYCL buffer).
+template<typename T>
+DeviceBuffer<T>& DeviceBuffer<T>::operator=(DeviceBuffer<T> const& src)
+{
+    if (src.buffer_)
+    {
+        buffer_ = std::make_unique<ClSyclBufferWrapper>(*src.buffer_);
+    }
+    else
+    {
+        buffer_.reset(nullptr);
+    }
+    return *this;
+}
+
+//! Move assignment.
+template<typename T>
+DeviceBuffer<T>& DeviceBuffer<T>::operator=(DeviceBuffer<T>&& src) noexcept = default;
+
+/*! \brief Dummy assignment operator to allow compilation of some cross-platform code.
+ *
+ * A hacky way to make SYCL implementation of DeviceBuffer compatible with details of CUDA and
+ * OpenCL implementations.
+ *
+ * \todo Should be removed after DeviceBuffer refactoring.
+ *
+ * \tparam T Type of buffer content.
+ * \param nullPtr \c std::nullptr. Not possible to assign any other pointers.
+ */
+template<typename T>
+DeviceBuffer<T>& DeviceBuffer<T>::operator=(std::nullptr_t nullPtr)
+{
+    buffer_.reset(nullPtr);
+    return *this;
+}
+
+
+namespace gmx::internal
+{
+//! Shorthand alias to create a placeholder SYCL accessor with chosen data type and access mode.
+template<class T, cl::sycl::access::mode mode>
+using PlaceholderAccessor =
+        cl::sycl::accessor<T, 1, mode, cl::sycl::access::target::global_buffer, cl::sycl::access::placeholder::true_t>;
+} // namespace gmx::internal
+
+/** \brief
+ * Thin wrapper around placeholder accessor that allows implicit construction from \c DeviceBuffer.
+ *
+ * "Placeholder accessor" is an indicator of the intent to create an accessor for certain buffer
+ * of a certain type, that is not yet bound to a specific command group handler (device). Such
+ * accessors can be created outside SYCL kernels, which is helpful if we want to pass them as
+ * function arguments.
+ *
+ * \tparam T Type of buffer content.
+ * \tparam mode Access mode.
+ */
+template<class T, cl::sycl::access::mode mode>
+class DeviceAccessor : public gmx::internal::PlaceholderAccessor<T, mode>
+{
+public:
+    // Inherit all the constructors
+    using gmx::internal::PlaceholderAccessor<T, mode>::PlaceholderAccessor;
+    //! Construct Accessor from DeviceBuffer (must be initialized)
+    DeviceAccessor(DeviceBuffer<T>& buffer) :
+        gmx::internal::PlaceholderAccessor<T, mode>(getSyclBuffer(buffer))
+    {
+    }
+    //! Construct read-only Accessor from a const DeviceBuffer (must be initialized)
+    DeviceAccessor(const DeviceBuffer<T>& buffer) :
+        gmx::internal::PlaceholderAccessor<T, mode>(getSyclBuffer(const_cast<DeviceBuffer<T>&>(buffer)))
+    {
+        /* There were some discussions about making it possible to create read-only sycl::accessor
+         * from a const sycl::buffer (https://github.com/KhronosGroup/SYCL-Docs/issues/10), but
+         * it did not make it into the SYCL2020 standard. So, we have to use const_cast above */
+        /* Using static_assert to ensure that only mode::read accessors can be created from a
+         * const DeviceBuffer. static_assert provides better error messages than std::enable_if. */
+        static_assert(mode == cl::sycl::access::mode::read,
+                      "Can not create non-read-only accessor from a const DeviceBuffer");
+    }
+
+private:
+    //! Helper function to get sycl:buffer object from DeviceBuffer wrapper, with a sanity check.
+    static inline cl::sycl::buffer<T, 1>& getSyclBuffer(DeviceBuffer<T>& buffer)
+    {
+        GMX_ASSERT(bool(buffer), "Trying to construct accessor from an uninitialized buffer");
+        return *buffer.buffer_;
+    }
+};
+
+namespace gmx::internal
+{
+//! A "blackhole" class to be used when we want to ignore an argument to a function.
+struct EmptyClassThatIgnoresConstructorArguments
+{
+    template<class... Args>
+    [[maybe_unused]] EmptyClassThatIgnoresConstructorArguments(Args&&... /*args*/)
+    {
+    }
+    //! Allow casting to nullptr
+    constexpr operator std::nullptr_t() const { return nullptr; }
+};
+} // namespace gmx::internal
+
+/** \brief
+ * Helper class to be used as function argument. Will either correspond to a device accessor, or an empty class.
+ *
+ * Example usage:
+ * \code
+    template <bool doFoo>
+    void getBarKernel(handler& cgh, OptionalAccessor<float, mode::read, doFoo> a_fooPrms)
+    {
+        if constexpr (doFoo)
+            cgh.require(a_fooPrms);
+        // Can only use a_fooPrms if doFoo == true
+    }
+
+    template <bool doFoo>
+    void callBar(DeviceBuffer<float> b_fooPrms)
+    {
+        // If doFoo is false, b_fooPrms will be ignored (can be not initialized).
+        // Otherwise, an accessor will be built (b_fooPrms must be a valid buffer).
+        auto kernel = getBarKernel<doFoo>(b_fooPrms);
+        // If the accessor in not enabled, anything can be passed as its ctor argument.
+        auto kernel2 = getBarKernel<false>(nullptr_t);
+    }
+ * \endcode
+ *
+ * \tparam T Data type of the underlying buffer
+ * \tparam mode Access mode of the accessor
+ * \tparam enabled Compile-time flag indicating whether we want to actually create an accessor.
+ */
+template<class T, cl::sycl::access::mode mode, bool enabled>
+using OptionalAccessor =
+        std::conditional_t<enabled, DeviceAccessor<T, mode>, gmx::internal::EmptyClassThatIgnoresConstructorArguments>;
+
+#endif // #ifndef DOXYGEN
+
+/*! \brief Check the validity of the device buffer.
+ *
+ * Checks if the buffer is valid and if its allocation is big enough.
+ *
+ * \param[in] buffer        Device buffer to be checked.
+ * \param[in] requiredSize  Number of elements that the buffer will have to accommodate.
+ *
+ * \returns Whether the device buffer exists and has enough capacity.
+ */
+template<typename T>
+static gmx_unused bool checkDeviceBuffer(const DeviceBuffer<T>& buffer, int requiredSize)
+{
+    return buffer.buffer_ && (static_cast<int>(buffer.buffer_->get_count()) >= requiredSize);
+}
+
+/*! \libinternal \brief
+ * Allocates a device-side buffer.
+ * It is currently a caller's responsibility to call it only on not-yet allocated buffers.
+ *
+ * \tparam        ValueType            Raw value type of the \p buffer.
+ * \param[in,out] buffer               Pointer to the device-side buffer.
+ * \param[in]     numValues            Number of values to accommodate.
+ * \param[in]     deviceContext        The buffer's device context-to-be.
+ */
+template<typename ValueType>
+void allocateDeviceBuffer(DeviceBuffer<ValueType>* buffer, size_t numValues, const DeviceContext& deviceContext)
+{
+    /* SYCL does not require binding buffer to a specific context or device. The ::context_bound
+     * property only enforces the use of only given context, and possibly offers some optimizations */
+    const cl::sycl::property_list bufferProperties{ cl::sycl::property::buffer::context_bound(
+            deviceContext.context()) };
+    buffer->buffer_.reset(
+            new ClSyclBufferWrapper<ValueType>(cl::sycl::range<1>(numValues), bufferProperties));
+}
+
+/*! \brief
+ * Frees a device-side buffer.
+ * This does not reset separately stored size/capacity integers,
+ * as this is planned to be a destructor of DeviceBuffer as a proper class,
+ * and no calls on \p buffer should be made afterwards.
+ *
+ * \param[in] buffer  Pointer to the buffer to free.
+ */
+template<typename ValueType>
+void freeDeviceBuffer(DeviceBuffer<ValueType>* buffer)
+{
+    buffer->buffer_.reset(nullptr);
+}
+
+/*! \brief
+ * Performs the host-to-device data copy, synchronous or asynchronously on request.
+ *
+ * Unlike in CUDA and OpenCL, synchronous call does not guarantee that all previously
+ * submitted operations are complete, only the ones that are required for \p buffer consistency.
+ *
+ * \tparam        ValueType            Raw value type of the \p buffer.
+ * \param[in,out] buffer               Pointer to the device-side buffer.
+ * \param[in]     hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType.
+ * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy into.
+ * \param[in]     numValues            Number of values to copy.
+ * \param[in]     deviceStream         GPU stream to perform asynchronous copy in.
+ * \param[in]     transferKind         Copy type: synchronous or asynchronous.
+ * \param[out]    timingEvent          A pointer to the H2D copy timing event to be filled in.
+ *                                     Ignored in SYCL.
+ */
+template<typename ValueType>
+void copyToDeviceBuffer(DeviceBuffer<ValueType>* buffer,
+                        const ValueType*         hostBuffer,
+                        size_t                   startingOffset,
+                        size_t                   numValues,
+                        const DeviceStream&      deviceStream,
+                        GpuApiCallBehavior       transferKind,
+                        CommandEvent* gmx_unused timingEvent)
+{
+    if (numValues == 0)
+    {
+        return; // such calls are actually made with empty domains
+    }
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
+
+    GMX_ASSERT(checkDeviceBuffer(*buffer, startingOffset + numValues),
+               "buffer too small or not initialized");
+
+    cl::sycl::buffer<ValueType>& syclBuffer = *buffer->buffer_;
+
+    cl::sycl::event ev = deviceStream.stream().submit([&](cl::sycl::handler& cgh) {
+        /* Here and elsewhere in this file, accessor constructor is user instead of a more common
+         * buffer::get_access, since the compiler (icpx 2021.1-beta09) occasionally gets confused
+         * by all the overloads */
+        auto d_bufferAccessor = cl::sycl::accessor<ValueType, 1, cl::sycl::access::mode::discard_write>{
+            syclBuffer, cgh, cl::sycl::range(numValues), cl::sycl::id(startingOffset)
+        };
+        cgh.copy(hostBuffer, d_bufferAccessor);
+    });
+    if (transferKind == GpuApiCallBehavior::Sync)
+    {
+        ev.wait_and_throw();
+    }
+}
+
+/*! \brief
+ * Performs the device-to-host data copy, synchronous or asynchronously on request.
+ *
+ * Unlike in CUDA and OpenCL, synchronous call does not guarantee that all previously
+ * submitted operations are complete, only the ones that are required for \p buffer consistency.
+ *
+ * \tparam        ValueType            Raw value type of the \p buffer.
+ * \param[in,out] hostBuffer           Pointer to the raw host-side memory, also typed \p ValueType
+ * \param[in]     buffer               Pointer to the device-side buffer.
+ * \param[in]     startingOffset       Offset (in values) at the device-side buffer to copy from.
+ * \param[in]     numValues            Number of values to copy.
+ * \param[in]     deviceStream         GPU stream to perform asynchronous copy in.
+ * \param[in]     transferKind         Copy type: synchronous or asynchronous.
+ * \param[out]    timingEvent          A pointer to the H2D copy timing event to be filled in.
+ *                                     Ignored in SYCL.
+ */
+template<typename ValueType>
+void copyFromDeviceBuffer(ValueType*               hostBuffer,
+                          DeviceBuffer<ValueType>* buffer,
+                          size_t                   startingOffset,
+                          size_t                   numValues,
+                          const DeviceStream&      deviceStream,
+                          GpuApiCallBehavior       transferKind,
+                          CommandEvent* gmx_unused timingEvent)
+{
+    if (numValues == 0)
+    {
+        return; // such calls are actually made with empty domains
+    }
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+    GMX_ASSERT(hostBuffer, "needs a host buffer pointer");
+
+    GMX_ASSERT(checkDeviceBuffer(*buffer, startingOffset + numValues),
+               "buffer too small or not initialized");
+
+    cl::sycl::buffer<ValueType>& syclBuffer = *buffer->buffer_;
+
+    cl::sycl::event ev = deviceStream.stream().submit([&](cl::sycl::handler& cgh) {
+        const auto d_bufferAccessor = cl::sycl::accessor<ValueType, 1, cl::sycl::access::mode::read>{
+            syclBuffer, cgh, cl::sycl::range(numValues), cl::sycl::id(startingOffset)
+        };
+        cgh.copy(d_bufferAccessor, hostBuffer);
+    });
+    if (transferKind == GpuApiCallBehavior::Sync)
+    {
+        ev.wait_and_throw();
+    }
+}
+
+/*! \brief
+ * Performs the device-to-device data copy, synchronous or asynchronously on request.
+ *
+ * \tparam        ValueType                Raw value type of the \p buffer.
+ */
+template<typename ValueType>
+void copyBetweenDeviceBuffers(DeviceBuffer<ValueType>* /* destinationDeviceBuffer */,
+                              DeviceBuffer<ValueType>* /* sourceDeviceBuffer */,
+                              size_t /* numValues */,
+                              const DeviceStream& /* deviceStream */,
+                              GpuApiCallBehavior /* transferKind */,
+                              CommandEvent* /*timingEvent*/)
+{
+    // SYCL-TODO
+    gmx_fatal(FARGS, "D2D copy stub was called. Not yet implemented in SYCL.");
+}
+
+
+namespace gmx::internal
+{
+/*! \brief Helper function to clear device buffer.
+ *
+ * Not applicable to GROMACS's Float3 (a.k.a. gmx::RVec) and other custom types.
+ * From SYCL specs: "T must be a scalar value or a SYCL vector type."
+ */
+template<typename ValueType>
+cl::sycl::event fillSyclBufferWithNull(cl::sycl::buffer<ValueType, 1>& buffer,
+                                       size_t                          startingOffset,
+                                       size_t                          numValues,
+                                       cl::sycl::queue                 queue)
+{
+    using cl::sycl::access::mode;
+    const cl::sycl::range<1> range(numValues);
+    const cl::sycl::id<1>    offset(startingOffset);
+    const ValueType pattern = ValueType(0); // SYCL vectors support initialization by scalar
+
+    return queue.submit([&](cl::sycl::handler& cgh) {
+        auto d_bufferAccessor =
+                cl::sycl::accessor<ValueType, 1, mode::discard_write>{ buffer, cgh, range, offset };
+        cgh.fill(d_bufferAccessor, pattern);
+    });
+}
+
+//! \brief Helper function to clear device buffer of type Float3.
+template<>
+inline cl::sycl::event fillSyclBufferWithNull(cl::sycl::buffer<Float3, 1>& buffer,
+                                              size_t                       startingOffset,
+                                              size_t                       numValues,
+                                              cl::sycl::queue              queue)
+{
+    constexpr bool usingHipSycl =
+#ifdef __HIPSYCL__
+            true;
+#else
+            false;
+#endif
+
+
+    if constexpr (usingHipSycl)
+    {
+        // hipSYCL does not support reinterpret but allows using Float3 directly.
+        using cl::sycl::access::mode;
+        const cl::sycl::range<1> range(numValues);
+        const cl::sycl::id<1>    offset(startingOffset);
+        const Float3             pattern{ 0, 0, 0 };
+
+        return queue.submit([&](cl::sycl::handler& cgh) {
+            auto d_bufferAccessor =
+                    cl::sycl::accessor<Float3, 1, mode::discard_write>{ buffer, cgh, range, offset };
+            cgh.fill(d_bufferAccessor, pattern);
+        });
+    }
+    else // When not using hipSYCL, reinterpret as a flat float array
+    {
+#ifndef __HIPSYCL__
+        cl::sycl::buffer<float, 1> bufferAsFloat = buffer.reinterpret<float, 1>(buffer.get_count() * DIM);
+        return fillSyclBufferWithNull<float>(
+                bufferAsFloat, startingOffset * DIM, numValues * DIM, std::move(queue));
+#endif
+    }
+}
+
+} // namespace gmx::internal
+
+/*! \brief
+ * Clears the device buffer asynchronously.
+ *
+ * \tparam        ValueType       Raw value type of the \p buffer.
+ * \param[in,out] buffer          Pointer to the device-side buffer.
+ * \param[in]     startingOffset  Offset (in values) at the device-side buffer to start clearing at.
+ * \param[in]     numValues       Number of values to clear.
+ * \param[in]     deviceStream    GPU stream.
+ */
+template<typename ValueType>
+void clearDeviceBufferAsync(DeviceBuffer<ValueType>* buffer,
+                            size_t                   startingOffset,
+                            size_t                   numValues,
+                            const DeviceStream&      deviceStream)
+{
+    if (numValues == 0)
+    {
+        return;
+    }
+    GMX_ASSERT(buffer, "needs a buffer pointer");
+
+    GMX_ASSERT(checkDeviceBuffer(*buffer, startingOffset + numValues),
+               "buffer too small or not initialized");
+
+    cl::sycl::buffer<ValueType>& syclBuffer = *(buffer->buffer_);
+
+    gmx::internal::fillSyclBufferWithNull<ValueType>(
+            syclBuffer, startingOffset, numValues, deviceStream.stream());
+}
+
+/*! \brief Create a texture object for an array of type ValueType.
+ *
+ * Creates the device buffer and copies read-only data for an array of type ValueType.
+ * Like OpenCL, does not really do anything with textures, simply creates a buffer
+ * and initializes it.
+ *
+ * \tparam      ValueType      Raw data type.
+ *
+ * \param[out]  deviceBuffer   Device buffer to store data in.
+ * \param[in]   hostBuffer     Host buffer to get date from.
+ * \param[in]   numValues      Number of elements in the buffer.
+ * \param[in]   deviceContext  GPU device context.
+ */
+template<typename ValueType>
+void initParamLookupTable(DeviceBuffer<ValueType>* deviceBuffer,
+                          DeviceTexture* /* deviceTexture */,
+                          const ValueType*     hostBuffer,
+                          int                  numValues,
+                          const DeviceContext& deviceContext)
+{
+    GMX_ASSERT(hostBuffer, "Host buffer should be specified.");
+    GMX_ASSERT(deviceBuffer, "Device buffer should be specified.");
+
+    /* Constructing buffer with cl::sycl::buffer(T* data, size_t size) will take ownership
+     * of this memory region making it unusable, which might lead to side-effects.
+     * On the other hand, cl::sycl::buffer(InputIterator<T> begin, InputIterator<T> end) will
+     * initialize the buffer without affecting ownership of the memory, although
+     * it will consume extra memory on host. */
+    const cl::sycl::property_list bufferProperties{ cl::sycl::property::buffer::context_bound(
+            deviceContext.context()) };
+    deviceBuffer->buffer_.reset(new ClSyclBufferWrapper<ValueType>(
+            hostBuffer, hostBuffer + numValues, bufferProperties));
+}
+
+/*! \brief Release the OpenCL device buffer.
+ *
+ * \tparam        ValueType     Raw data type.
+ *
+ * \param[in,out] deviceBuffer  Device buffer to store data in.
+ */
+template<typename ValueType>
+void destroyParamLookupTable(DeviceBuffer<ValueType>* deviceBuffer, DeviceTexture* /* deviceTexture */)
+{
+    deviceBuffer->buffer_.reset(nullptr);
+}
+
+#endif // GMX_GPU_UTILS_DEVICEBUFFER_SYCL_H
diff --git a/src/include/gromacs/gpu_utils/gmxopencl.h b/src/include/gromacs/gpu_utils/gmxopencl.h
new file mode 100644 (file)
index 0000000..15a00fd
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Wraps the complexity of including OpenCL in Gromacs.
+ *
+ * Because OpenCL 2.0 is not officially supported widely, \Gromacs
+ * uses earlier interfaces. Some of those have been deprecated in 2.0,
+ * and generate warnings, which we need to suppress.
+ *
+ * Additionally, this code wraps they way that things work differently
+ * on Apple platforms.
+ *
+ * \inlibraryapi
+ */
+
+#ifndef GMX_GPU_UTILS_GMXOPENCL_H
+#define GMX_GPU_UTILS_GMXOPENCL_H
+
+/*! \brief Declare to OpenCL SDKs that we intend to use OpenCL API
+   features that were deprecated in 1.2 or 2.0, so that they don't
+   warn about it. */
+///@{
+#define CL_USE_DEPRECATED_OPENCL_1_2_APIS
+#define CL_USE_DEPRECATED_OPENCL_2_0_APIS
+///@}
+#ifdef __APPLE__
+#    include <OpenCL/opencl.h>
+#else
+#    include <CL/opencl.h>
+#endif
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/gmxsycl.h b/src/include/gromacs/gpu_utils/gmxsycl.h
new file mode 100644 (file)
index 0000000..2b07b92
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Wraps the complexity of including SYCL in GROMACS.
+ *
+ * The __SYCL_COMPILER_VERSION macro is used to identify Intel DPCPP compiler.
+ * See https://github.com/intel/llvm/pull/2998 for better proposal.
+ *
+ * Intel SYCL headers use symbol DIM as a template parameter, which gets broken by macro DIM defined
+ * in gromacs/math/vectypes.h. Here, we include the SYCL header while temporary undefining this macro.
+ * See https://github.com/intel/llvm/issues/2981.
+ *
+ * Different compilers, at the time of writing, have different names for some of the proposed features
+ * of the SYCL2020 standard. For uniformity, they are all aliased in our custom sycl_2020 namespace.
+ *
+ * \inlibraryapi
+ */
+
+#ifndef GMX_GPU_UTILS_GMXSYCL_H
+#define GMX_GPU_UTILS_GMXSYCL_H
+
+#include "config.h"
+
+// For hipSYCL, we need to activate floating-point atomics
+#if GMX_SYCL_HIPSYCL
+#    define HIPSYCL_EXT_FP_ATOMICS
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wunused-variable"
+#    pragma clang diagnostic ignored "-Wunused-parameter"
+#    pragma clang diagnostic ignored "-Wmissing-noreturn"
+#    pragma clang diagnostic ignored "-Wshadow-field"
+#    pragma clang diagnostic ignored "-Wctad-maybe-unsupported"
+#    pragma clang diagnostic ignored "-Wdeprecated-copy-dtor"
+#    pragma clang diagnostic ignored "-Winconsistent-missing-destructor-override"
+#    pragma clang diagnostic ignored "-Wunused-template"
+#    pragma clang diagnostic ignored "-Wsign-compare"
+#    pragma clang diagnostic ignored "-Wundefined-reinterpret-cast"
+#    pragma clang diagnostic ignored "-Wdeprecated-copy"
+#    pragma clang diagnostic ignored "-Wnewline-eof"
+#    pragma clang diagnostic ignored "-Wextra-semi"
+#    pragma clang diagnostic ignored "-Wsuggest-override"
+#    pragma clang diagnostic ignored "-Wsuggest-destructor-override"
+#    pragma clang diagnostic ignored "-Wgcc-compat"
+#endif
+
+
+#ifdef DIM
+#    if DIM != 3
+#        error "The workaround here assumes we use DIM=3."
+#    else
+#        undef DIM
+#        include <CL/sycl.hpp>
+#        define DIM 3
+#    endif
+#else
+#    include <CL/sycl.hpp>
+#endif
+
+#if GMX_SYCL_HIPSYCL
+#    pragma clang diagnostic pop
+#endif
+
+/* Exposing Intel-specific extensions in a manner compatible with SYCL2020 provisional spec.
+ * Despite ICPX (up to 2021.3.0 at the least) having SYCL_LANGUAGE_VERSION=202001,
+ * some parts of the spec are still in custom sycl::ONEAPI namespace (sycl::ext::oneapi in beta versions),
+ * and some functions have different names. To make things easier to upgrade
+ * in the future, this thin layer is added.
+ * */
+namespace sycl_2020
+{
+namespace detail
+{
+#if GMX_SYCL_DPCPP
+// Confirmed to work for 2021.1-beta10 (20201005) to 2021.3.0 (20210619).
+// Deprecated in favor of sycl::ext::oneapi on 20210717 in https://github.com/intel/llvm/commit/d703f578.
+namespace origin = cl::sycl::ONEAPI;
+#elif GMX_SYCL_HIPSYCL
+namespace origin = cl::sycl;
+#else
+#    error "Unsupported version of SYCL compiler"
+#endif
+} // namespace detail
+
+using detail::origin::memory_order;
+using detail::origin::memory_scope;
+using detail::origin::plus;
+using detail::origin::sub_group;
+
+#if GMX_SYCL_DPCPP
+using detail::origin::atomic_ref;
+template<typename... Args>
+bool group_any_of(Args&&... args)
+{
+    return detail::origin::any_of(std::forward<Args>(args)...);
+}
+template<typename... Args>
+auto group_reduce(Args&&... args) -> decltype(detail::origin::reduce(std::forward<Args>(args)...))
+{
+    return detail::origin::reduce(std::forward<Args>(args)...);
+}
+#elif GMX_SYCL_HIPSYCL
+using detail::origin::atomic_ref;
+using detail::origin::group_any_of;
+using detail::origin::group_reduce;
+#else
+#    error "Unsupported SYCL compiler"
+#endif
+
+} // namespace sycl_2020
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/gpu_macros.h b/src/include/gromacs/gpu_utils/gpu_macros.h
new file mode 100644 (file)
index 0000000..bf57d57
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team.
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_MACROS_H
+#define GMX_GPU_UTILS_MACROS_H
+
+#include "config.h"
+
+#include "gromacs/utility/basedefinitions.h" // for gmx_unused
+
+/* These macros that let us define inlineable null implementations so
+   that non-GPU Gromacs can run with no overhead without conditionality
+   everywhere a GPU function is called. */
+#define REAL_FUNC_QUALIFIER
+#define REAL_FUNC_ARGUMENT(arg) arg
+#define REAL_FUNC_TERM
+#define REAL_FUNC_TERM_WITH_RETURN(arg)
+
+#define NULL_FUNC_QUALIFIER gmx_unused static
+#define NULL_FUNC_ARGUMENT(arg) arg gmx_unused
+#define NULL_FUNC_TERM \
+    {                  \
+    }
+#define NULL_FUNC_TERM_WITH_RETURN(arg) \
+    {                                   \
+        return (arg);                   \
+    }
+
+#ifdef DOXYGEN
+
+/* Doxygen build appreciates always having argument names, and doesn't
+ * care about duplicate function definitions. */
+#    define GPU_FUNC_QUALIFIER REAL_FUNC_QUALIFIER
+#    define GPU_FUNC_ARGUMENT REAL_FUNC_ARGUMENT
+#    define GPU_FUNC_TERM REAL_FUNC_TERM
+#    define GPU_FUNC_TERM_WITH_RETURN(arg) REAL_FUNC_TERM_WITH_RETURN(arg)
+#    define CUDA_FUNC_QUALIFIER REAL_FUNC_QUALIFIER
+#    define CUDA_FUNC_ARGUMENT REAL_FUNC_ARGUMENT
+#    define CUDA_FUNC_TERM REAL_FUNC_TERM
+#    define CUDA_FUNC_TERM_WITH_RETURN(arg) REAL_FUNC_TERM_WITH_RETURN(arg)
+#    define OPENCL_FUNC_QUALIFIER REAL_FUNC_QUALIFIER
+#    define OPENCL_FUNC_ARGUMENT REAL_FUNC_ARGUMENT
+#    define OPENCL_FUNC_TERM REAL_FUNC_TERM
+#    define OPENCL_FUNC_TERM_WITH_RETURN(arg) REAL_FUNC_TERM_WITH_RETURN(arg)
+#    define SYCL_FUNC_QUALIFIER REAL_FUNC_QUALIFIER
+#    define SYCL_FUNC_ARGUMENT REAL_FUNC_ARGUMENT
+#    define SYCL_FUNC_TERM REAL_FUNC_TERM
+#    define SYCL_FUNC_TERM_WITH_RETURN(arg) REAL_FUNC_TERM_WITH_RETURN(arg)
+
+#else // Not DOXYGEN
+
+/* GPU support is enabled, so these functions will have real code defined somewhere */
+#    if GMX_GPU
+#        define GPU_FUNC_QUALIFIER REAL_FUNC_QUALIFIER
+#        define GPU_FUNC_ARGUMENT REAL_FUNC_ARGUMENT
+#        define GPU_FUNC_TERM REAL_FUNC_TERM
+#        define GPU_FUNC_TERM_WITH_RETURN(arg) REAL_FUNC_TERM_WITH_RETURN(arg)
+#    else
+#        define GPU_FUNC_QUALIFIER NULL_FUNC_QUALIFIER
+#        define GPU_FUNC_ARGUMENT NULL_FUNC_ARGUMENT
+#        define GPU_FUNC_TERM NULL_FUNC_TERM
+#        define GPU_FUNC_TERM_WITH_RETURN(arg) NULL_FUNC_TERM_WITH_RETURN(arg)
+#    endif
+
+/* Enable and disable platform-specific function implementations */
+#    if GMX_GPU_OPENCL
+#        define OPENCL_FUNC_QUALIFIER REAL_FUNC_QUALIFIER
+#        define OPENCL_FUNC_ARGUMENT REAL_FUNC_ARGUMENT
+#        define OPENCL_FUNC_TERM REAL_FUNC_TERM
+#        define OPENCL_FUNC_TERM_WITH_RETURN(arg) REAL_FUNC_TERM_WITH_RETURN(arg)
+#    else
+#        define OPENCL_FUNC_QUALIFIER NULL_FUNC_QUALIFIER
+#        define OPENCL_FUNC_ARGUMENT NULL_FUNC_ARGUMENT
+#        define OPENCL_FUNC_TERM NULL_FUNC_TERM
+#        define OPENCL_FUNC_TERM_WITH_RETURN(arg) NULL_FUNC_TERM_WITH_RETURN(arg)
+#    endif
+
+#    if GMX_GPU_CUDA
+#        define CUDA_FUNC_QUALIFIER REAL_FUNC_QUALIFIER
+#        define CUDA_FUNC_ARGUMENT REAL_FUNC_ARGUMENT
+#        define CUDA_FUNC_TERM REAL_FUNC_TERM
+#        define CUDA_FUNC_TERM_WITH_RETURN(arg) REAL_FUNC_TERM_WITH_RETURN(arg)
+#    else
+#        define CUDA_FUNC_QUALIFIER NULL_FUNC_QUALIFIER
+#        define CUDA_FUNC_ARGUMENT NULL_FUNC_ARGUMENT
+#        define CUDA_FUNC_TERM NULL_FUNC_TERM
+#        define CUDA_FUNC_TERM_WITH_RETURN(arg) NULL_FUNC_TERM_WITH_RETURN(arg)
+#    endif
+
+#    if GMX_GPU_SYCL
+#        define SYCL_FUNC_QUALIFIER REAL_FUNC_QUALIFIER
+#        define SYCL_FUNC_ARGUMENT REAL_FUNC_ARGUMENT
+#        define SYCL_FUNC_TERM REAL_FUNC_TERM
+#        define SYCL_FUNC_TERM_WITH_RETURN(arg) REAL_FUNC_TERM_WITH_RETURN(arg)
+#    else
+#        define SYCL_FUNC_QUALIFIER NULL_FUNC_QUALIFIER
+#        define SYCL_FUNC_ARGUMENT NULL_FUNC_ARGUMENT
+#        define SYCL_FUNC_TERM NULL_FUNC_TERM
+#        define SYCL_FUNC_TERM_WITH_RETURN(arg) NULL_FUNC_TERM_WITH_RETURN(arg)
+#    endif
+
+#endif // ifdef DOXYGEN
+
+#endif // GMX_GPU_UTILS_MACROS_H
diff --git a/src/include/gromacs/gpu_utils/gpu_utils.h b/src/include/gromacs/gpu_utils/gpu_utils.h
new file mode 100644 (file)
index 0000000..f9c5353
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2010, The GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declare functions for detection and initialization for GPU devices.
+ *
+ *  \author Szilard Pall <pall.szilard@gmail.com>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *
+ *  \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_GPU_UTILS_H
+#define GMX_GPU_UTILS_GPU_UTILS_H
+
+#include <cstdio>
+
+#include <string>
+#include <vector>
+
+#include "gromacs/gpu_utils/gpu_macros.h"
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+class MDLogger;
+}
+
+//! Enum which is only used to describe transfer calls at the moment
+enum class GpuApiCallBehavior : int
+{
+    //! Synchronous
+    Sync,
+    //! Asynchronous
+    Async,
+    //! Size of the enumeration
+    Count
+};
+
+//! String corresponding to GPU API call behavior
+const char* enumValueToString(GpuApiCallBehavior enumValue);
+
+//! Types of actions associated to waiting or checking the completion of GPU tasks
+enum class GpuTaskCompletion
+{
+    Wait, /*<< Issue a blocking wait for the task */
+    Check /*<< Only check whether the task has completed */
+};
+
+/*! \brief Starts the GPU profiler if mdrun is being profiled.
+ *
+ *  When a profiler run is in progress (based on the presence of the NVPROF_ID
+ *  env. var.), the profiler is started to begin collecting data during the
+ *  rest of the run (or until stopGpuProfiler is called).
+ *
+ *  Note that this is implemented only for the CUDA API.
+ */
+CUDA_FUNC_QUALIFIER
+void startGpuProfiler() CUDA_FUNC_TERM;
+
+
+/*! \brief Resets the GPU profiler if mdrun is being profiled.
+ *
+ * When a profiler run is in progress (based on the presence of the NVPROF_ID
+ * env. var.), the profiler data is restet in order to eliminate the data collected
+ * from the preceding part fo the run.
+ *
+ * This function should typically be called at the mdrun counter reset time.
+ *
+ * Note that this is implemented only for the CUDA API.
+ */
+CUDA_FUNC_QUALIFIER
+void resetGpuProfiler() CUDA_FUNC_TERM;
+
+
+/*! \brief Stops the CUDA profiler if mdrun is being profiled.
+ *
+ *  This function can be called at cleanup when skipping recording
+ *  recording subsequent API calls from being traces/profiled is desired,
+ *  e.g. before uninitialization.
+ *
+ *  Note that this is implemented only for the CUDA API.
+ */
+CUDA_FUNC_QUALIFIER
+void stopGpuProfiler() CUDA_FUNC_TERM;
+
+//! Tells whether the host buffer was pinned for non-blocking transfers. Only implemented for CUDA.
+CUDA_FUNC_QUALIFIER
+bool isHostMemoryPinned(const void* CUDA_FUNC_ARGUMENT(h_ptr)) CUDA_FUNC_TERM_WITH_RETURN(false);
+
+/*! \brief Enable peer access between GPUs where supported
+ * \param[in] gpuIdsToUse   List of GPU IDs in use
+ * \param[in] mdlog         Logger object
+ */
+CUDA_FUNC_QUALIFIER
+void setupGpuDevicePeerAccess(const std::vector<int>& CUDA_FUNC_ARGUMENT(gpuIdsToUse),
+                              const gmx::MDLogger&    CUDA_FUNC_ARGUMENT(mdlog)) CUDA_FUNC_TERM;
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/gpueventsynchronizer.h b/src/include/gromacs/gpu_utils/gpueventsynchronizer.h
new file mode 100644 (file)
index 0000000..9feabf5
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Implements a GpuEventSynchronizer class.
+ *
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_GPUEVENTSYNCHRONIZER_H
+#define GMX_GPU_UTILS_GPUEVENTSYNCHRONIZER_H
+
+#include "config.h"
+
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "device_event.h"
+
+/*! \libinternal \brief
+ * A class which allows for CPU thread to mark and wait for certain GPU stream execution point.
+ *
+ * The event can be put into the stream with \ref markEvent and then later waited on with \ref
+ * waitForEvent or \ref enqueueWaitEvent.
+ *
+ * Additionally, this class offers facilities for runtime checking of correctness by counting
+ * how many times each marked event is used as a synchronization point.
+ *
+ * - When the class is constructed, a required minimal (\c minConsumptionCount) and maximal (\c maxConsumptionCount) number of
+ * consumptions can be specified. By default, both are set to 1.
+ * - The event is considered <em>fully consumed</em> if its current number of consumptions \c c equals
+ * \c maxConsumptionCount.
+ * - The event is considered <em>sufficiently consumed</em> if <tt>minConsumptionCount <= c <= maxConsumptionCount</tt>.
+ * - The class is initialized in the <em>fully consumed</em> state, so it can not be consumed right away.
+ * - Consuming the event is only possible if it is not <em>fully consumed</em> (<tt>c < maxConsumptionCount</tt>).
+ * Consuming the event increments \c c by 1. Trying to consume <em>fully consumed</em> event
+ * throws \ref gmx::InternalError.
+ * - \ref reset returns object into the initial <em>fully consumed</em> state.
+ * This function is intended to manually override the consumption limits.
+ * - \ref consume \em consumes the event, without doing anything else.
+ * This function is intended to manually override the consumption limits.
+ * - \ref markEvent enqueues new event into the provided stream, and sets \c to 0. Marking is only
+ * possible if the event is <em>sufficiently consumed</em>, otherwise \ref gmx::InternalError
+ * is thrown.
+ * - \ref waitForEvent \em consumes the event and blocks the host thread until the event
+ * is ready (complete).
+ * - \ref enqueueWaitEvent \em consumes the event and blocks the inserts a blocking barrier
+ * into the provided stream which blocks the execution of all tasks later submitted to this
+ * stream until the event is ready (completes).
+ *
+ * Default <tt>minConsumptionCount=maxConsumptionCount=1</tt> limits mean that each call to \ref markEvent must be followed
+ * by exactly one \ref enqueueWaitEvent or \ref enqueueWaitEvent. This is the recommended pattern
+ * for most use cases. By providing other constructor arguments, this requirement can be relaxed
+ * as needed.
+ */
+class GpuEventSynchronizer
+{
+public:
+    //! A constructor
+    GpuEventSynchronizer(int minConsumptionCount, int maxConsumptionCount) :
+        minConsumptionCount_(minConsumptionCount), maxConsumptionCount_(maxConsumptionCount)
+    {
+        reset();
+    }
+    GpuEventSynchronizer() : GpuEventSynchronizer(1, 1) {}
+    //! A destructor
+    ~GpuEventSynchronizer() = default;
+    //! Remove copy assignment, because we can not copy the underlying event object.
+    GpuEventSynchronizer& operator=(const GpuEventSynchronizer&) = delete;
+    //! Remove copy constructor, because we can not copy the underlying event object.
+    GpuEventSynchronizer(const GpuEventSynchronizer&) = delete;
+    //! Remove move assignment, because we don't allow moving the underlying event object.
+    GpuEventSynchronizer& operator=(GpuEventSynchronizer&&) = delete;
+    //! Remove move constructor, because we don't allow moving the underlying event object.
+    GpuEventSynchronizer(GpuEventSynchronizer&&) = delete;
+
+    /*! \brief Marks the synchronization point in the \p stream and reset the consumption counter.
+     *
+     * Should be called before implicitly consuming actions (\ref waitForEvent() or \ref enqueueWaitEvent()) are executed or explicit \ref consume() calls are made.
+     *
+     * If the event has been marked before and not fully consumed, throws \ref gmx::InternalError.
+     */
+    inline void markEvent(const DeviceStream& deviceStream)
+    {
+#if !GMX_GPU_CUDA // For now, we have relaxed conditions for CUDA
+        if (consumptionCount_ < minConsumptionCount_)
+        {
+            GMX_THROW(gmx::InternalError("Trying to mark event before fully consuming it"));
+        }
+#endif
+        event_.mark(deviceStream);
+        consumptionCount_ = 0;
+    }
+    /*! \brief Synchronizes the host thread on the marked event.
+     *
+     * Consumes the event if able, otherwise throws \ref gmx::InternalError.
+     */
+    inline void waitForEvent()
+    {
+        consume();
+        event_.wait();
+        resetIfFullyConsumed();
+    }
+    //! Checks the completion of the underlying event and consumes the event if it is ready.
+    inline bool isReady()
+    {
+        bool isReady = event_.isReady();
+        if (isReady)
+        {
+            consume();
+            resetIfFullyConsumed();
+        }
+        return isReady;
+    }
+    //! Checks whether the event was marked (and was not reset since then).
+    inline bool isMarked() const { return event_.isMarked(); }
+    /*! \brief Manually consume the event without waiting for it.
+     *
+     * If the event is already fully consumed, throws \ref gmx::InternalError.
+     */
+    inline void consume()
+    {
+#if !GMX_GPU_CUDA // For now, we have relaxed conditions for CUDA
+        if (consumptionCount_ >= maxConsumptionCount_)
+        {
+            GMX_THROW(gmx::InternalError(
+                    "Trying to consume an event before marking it or after fully consuming it"));
+        }
+#endif
+        consumptionCount_++;
+    }
+    //! Helper function to reset the event when it is fully consumed.
+    inline void resetIfFullyConsumed()
+    {
+        if (consumptionCount_ == maxConsumptionCount_)
+        {
+            event_.reset();
+        }
+    }
+    /*! \brief Enqueues a wait for the recorded event in stream \p deviceStream.
+     *
+     * Consumes the event if able, otherwise throws \ref gmx::InternalError.
+     */
+    inline void enqueueWaitEvent(const DeviceStream& deviceStream)
+    {
+        consume();
+        event_.enqueueWait(deviceStream);
+        resetIfFullyConsumed();
+    }
+
+    //! Resets the event to unmarked state, releasing the underlying event object if needed.
+    inline void reset()
+    {
+        // Set such that we can mark new event without triggering an exception, but can not consume.
+        consumptionCount_ = maxConsumptionCount_;
+        event_.reset();
+    }
+
+private:
+    DeviceEvent event_;
+    int         consumptionCount_;
+#if defined(__clang__) && GMX_GPU_CUDA
+    [[maybe_unused]]
+#endif
+    int minConsumptionCount_; // Unused in CUDA builds, yet
+    int maxConsumptionCount_;
+};
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/gpuregiontimer.cuh b/src/include/gromacs/gpu_utils/gpuregiontimer.cuh
new file mode 100644 (file)
index 0000000..5979309
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *  \brief Implements the GPU region timer for CUDA.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *
+ *  \inlibraryapi
+ */
+
+#ifndef GMX_GPU_UTILS_GPUREGIONTIMER_CUH
+#define GMX_GPU_UTILS_GPUREGIONTIMER_CUH
+
+#include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/gputraits.cuh"
+
+#include "gpuregiontimer.h"
+
+/*! \libinternal \brief
+ * This is a GPU region timing implementation for CUDA.
+ * It provides methods for measuring the last timespan.
+ * Copying/assignment is disabled since the underlying timing events are owned by this.
+ */
+class GpuRegionTimerImpl
+{
+    //! The underlying timing event pair - the beginning and the end of the timespan
+    cudaEvent_t eventStart_, eventStop_;
+
+public:
+    GpuRegionTimerImpl()
+    {
+        const int eventFlags = cudaEventDefault;
+        CU_RET_ERR(cudaEventCreate(&eventStart_, eventFlags), "GPU timing creation failure");
+        CU_RET_ERR(cudaEventCreate(&eventStop_, eventFlags), "GPU timing creation failure");
+    }
+    ~GpuRegionTimerImpl()
+    {
+        CU_RET_ERR(cudaEventDestroy(eventStart_), "GPU timing destruction failure");
+        CU_RET_ERR(cudaEventDestroy(eventStop_), "GPU timing destruction failure");
+    }
+    //! No copying
+    GpuRegionTimerImpl(const GpuRegionTimerImpl&) = delete;
+    //! No assignment
+    GpuRegionTimerImpl& operator=(GpuRegionTimerImpl&&) = delete;
+    //! Moving is disabled but can be considered in the future if needed
+    GpuRegionTimerImpl(GpuRegionTimerImpl&&) = delete;
+
+    /*! \brief Will be called before the region start. */
+    inline void openTimingRegion(const DeviceStream& deviceStream)
+    {
+        CU_RET_ERR(cudaEventRecord(eventStart_, deviceStream.stream()),
+                   "GPU timing recording failure");
+    }
+
+    /*! \brief Will be called after the region end. */
+    inline void closeTimingRegion(const DeviceStream& deviceStream)
+    {
+        CU_RET_ERR(cudaEventRecord(eventStop_, deviceStream.stream()),
+                   "GPU timing recording failure");
+    }
+
+    /*! \brief Returns the last measured region timespan (in milliseconds) and calls reset() */
+    inline double getLastRangeTime()
+    {
+        float milliseconds = 0.0;
+        CU_RET_ERR(cudaEventElapsedTime(&milliseconds, eventStart_, eventStop_),
+                   "GPU timing update failure");
+        reset();
+        return milliseconds;
+    }
+
+    /*! \brief Resets internal state */
+    inline void reset() {}
+
+    /*! \brief Returns a new raw timing event
+     * for passing into individual GPU API calls.
+     * This is just a dummy in CUDA.
+     */
+    static inline CommandEvent* fetchNextEvent() { return nullptr; }
+};
+
+//! Short-hand for external use
+using GpuRegionTimer = GpuRegionTimerWrapper<GpuRegionTimerImpl>;
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/gpuregiontimer.h b/src/include/gromacs/gpu_utils/gpuregiontimer.h
new file mode 100644 (file)
index 0000000..f0860b1
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *  \brief Defines the GPU region timer implementation/wrapper classes.
+ *  The implementations live in gpuregiontimer.cuh for CUDA and gpuregiontimer_ocl.h for OpenCL.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ */
+
+#ifndef GMX_GPU_UTILS_GPUREGIONTIMER_H
+#define GMX_GPU_UTILS_GPUREGIONTIMER_H
+
+#include <string>
+
+#include "gromacs/utility/gmxassert.h"
+
+//! Debug GPU timers in debug builds only
+#if defined(NDEBUG)
+static const bool c_debugTimerState = false;
+#else
+static const bool c_debugTimerState = true;
+#endif
+
+/*! \libinternal \brief
+ * This is a GPU region timing wrapper class.
+ * It allows for host-side tracking of the accumulated execution timespans in GPU code
+ * (measuring kernel or transfers duration).
+ * It also partially tracks the correctness of the timer state transitions,
+ * as far as current implementation allows (see TODO in getLastRangeTime() for a disabled check).
+ * Internally it uses GpuRegionTimerImpl for measuring regions.
+ */
+template<typename GpuRegionTimerImpl>
+class GpuRegionTimerWrapper
+{
+    //! The timer state used for debug-only assertions
+    enum class TimerState
+    {
+        Idle,
+        Recording,
+        Stopped
+    } debugState_ = TimerState::Idle;
+
+    //! The number of times the timespan has been measured
+    unsigned int callCount_ = 0;
+    //! The accumulated duration of the timespans measured (milliseconds)
+    double totalMilliseconds_ = 0.0;
+    //! The underlying region timer implementation
+    GpuRegionTimerImpl impl_;
+
+public:
+    /*! \brief
+     * To be called before the region start.
+     *
+     * \param[in] deviceStream   The GPU command stream where the event being measured takes place.
+     */
+    void openTimingRegion(const DeviceStream& deviceStream)
+    {
+        if (c_debugTimerState)
+        {
+            std::string error = "GPU timer should be idle, but is "
+                                + std::string((debugState_ == TimerState::Stopped) ? "stopped" : "recording")
+                                + ".";
+            GMX_ASSERT(debugState_ == TimerState::Idle, error.c_str());
+            debugState_ = TimerState::Recording;
+        }
+        impl_.openTimingRegion(deviceStream);
+    }
+    /*! \brief
+     * To be called after the region end.
+     *
+     * \param[in] deviceStream   The GPU command stream where the event being measured takes place.
+     */
+    void closeTimingRegion(const DeviceStream& deviceStream)
+    {
+        if (c_debugTimerState)
+        {
+            std::string error = "GPU timer should be recording, but is "
+                                + std::string((debugState_ == TimerState::Idle) ? "idle" : "stopped")
+                                + ".";
+            GMX_ASSERT(debugState_ == TimerState::Recording, error.c_str());
+            debugState_ = TimerState::Stopped;
+        }
+        callCount_++;
+        impl_.closeTimingRegion(deviceStream);
+    }
+    /*! \brief
+     * Accumulates the last timespan of all the events used into the total duration,
+     * and resets the internal timer state.
+     * To be called after closeTimingRegion() and the command stream of the event having been
+     * synchronized. \returns The last timespan (in milliseconds).
+     */
+    double getLastRangeTime()
+    {
+        if (c_debugTimerState)
+        {
+            /* The assertion below is commented because it is currently triggered on a special case:
+             * the early return before the local non-bonded kernel launch if there is nothing to do.
+             * This can be reproduced in OpenCL build by running
+             * mdrun-mpi-test -ntmpi 2 --gtest_filter=*Empty*
+             * Similarly, the GpuRegionTimerImpl suffers from non-nullptr
+             * cl_event conditionals which ideally should only be asserts.
+             * TODO: improve internal task scheduling, re-enable the assert, turn conditionals into asserts
+             */
+            /*
+               std::string error = "GPU timer should be stopped, but is " + std::string((debugState_ == TimerState::Idle) ? "idle" : "recording") + ".";
+               GMX_ASSERT(debugState_ == TimerState::Stopped, error.c_str());
+             */
+            debugState_ = TimerState::Idle;
+        }
+        double milliseconds = impl_.getLastRangeTime();
+        totalMilliseconds_ += milliseconds;
+        return milliseconds;
+    }
+    /*! \brief Resets the implementation and total time/call count to zeroes. */
+    void reset()
+    {
+        if (c_debugTimerState)
+        {
+            debugState_ = TimerState::Idle;
+        }
+        totalMilliseconds_ = 0.0;
+        callCount_         = 0;
+        impl_.reset();
+    }
+    /*! \brief Gets total time recorded (in milliseconds). */
+    double getTotalTime() const { return totalMilliseconds_; }
+    /*! \brief Gets total call count recorded. */
+    unsigned int getCallCount() const { return callCount_; }
+    /*! \brief
+     * Gets a pointer to a new timing event for passing into individual GPU API calls
+     * within the region if they require it (e.g. on OpenCL).
+     * \returns The pointer to the underlying single command timing event.
+     */
+    CommandEvent* fetchNextEvent()
+    {
+        if (c_debugTimerState)
+        {
+            std::string error = "GPU timer should be recording, but is "
+                                + std::string((debugState_ == TimerState::Idle) ? "idle" : "stopped")
+                                + ".";
+            GMX_ASSERT(debugState_ == TimerState::Recording, error.c_str());
+        }
+        return impl_.fetchNextEvent();
+    }
+};
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/gpuregiontimer_ocl.h b/src/include/gromacs/gpu_utils/gpuregiontimer_ocl.h
new file mode 100644 (file)
index 0000000..32505f7
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *  \brief Implements the GPU region timer for OpenCL.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *
+ *  \inlibraryapi
+ */
+
+#ifndef GMX_GPU_UTILS_GPUREGIONTIMER_OCL_H
+#define GMX_GPU_UTILS_GPUREGIONTIMER_OCL_H
+
+#include <array>
+
+#include "gromacs/gpu_utils/gputraits_ocl.h"
+#include "gromacs/gpu_utils/oclutils.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "gpuregiontimer.h"
+
+/*! \libinternal \brief
+ * The OpenCL implementation of the GPU code region timing.
+ * With OpenCL, one has to use cl_event handle for each API call that has to be timed, and
+ * accumulate the timing afterwards. As we would like to avoid overhead on API calls,
+ * we only query and accumulate cl_event timing at the end of time steps, not after the API calls.
+ * Thus, this implementation does not reuse a single cl_event for multiple calls, but instead
+ * maintains an array of cl_events to be used within any single code region.
+ * The array size is fixed at a small but sufficiently large value for the number of cl_events
+ * that might contribute to a timer region, currently 10.
+ */
+class GpuRegionTimerImpl
+{
+    /*! \brief The underlying individual timing events array.
+     * The maximum size is chosen arbitrarily to work with current code, and can be changed.
+     * There is simply no need for run-time resizing, and it's unlikely we'll ever need more than 10.
+     */
+    std::array<cl_event, 10> events_ = { { nullptr } };
+    //! Index of the active event
+    size_t currentEvent_ = 0;
+
+public:
+    GpuRegionTimerImpl()  = default;
+    ~GpuRegionTimerImpl() = default;
+    //! No copying
+    GpuRegionTimerImpl(const GpuRegionTimerImpl&) = delete;
+    //! No assignment
+    GpuRegionTimerImpl& operator=(GpuRegionTimerImpl&&) = delete;
+    //! Moving is disabled but can be considered in the future if needed
+    GpuRegionTimerImpl(GpuRegionTimerImpl&&) = delete;
+
+    /*! \brief Should be called before the region start. */
+    inline void openTimingRegion(const DeviceStream& /*unused*/) {}
+    /*! \brief Should be called after the region end. */
+    inline void closeTimingRegion(const DeviceStream& /*unused*/) {}
+    /*! \brief Returns the last measured region timespan (in milliseconds) and calls reset(). */
+    inline double getLastRangeTime()
+    {
+        double milliseconds = 0.0;
+        for (size_t i = 0; i < currentEvent_; i++)
+        {
+            if (events_[i]) // This conditional is ugly, but is required to make some tests (e.g. empty domain) pass
+            {
+                cl_ulong          start_ns, end_ns;
+                cl_int gmx_unused cl_error;
+
+                cl_error = clGetEventProfilingInfo(
+                        events_[i], CL_PROFILING_COMMAND_START, sizeof(cl_ulong), &start_ns, nullptr);
+                GMX_ASSERT(CL_SUCCESS == cl_error,
+                           gmx::formatString("GPU timing update failure (OpenCL error %d: %s).",
+                                             cl_error,
+                                             ocl_get_error_string(cl_error).c_str())
+                                   .c_str());
+                cl_error = clGetEventProfilingInfo(
+                        events_[i], CL_PROFILING_COMMAND_END, sizeof(cl_ulong), &end_ns, nullptr);
+                GMX_ASSERT(CL_SUCCESS == cl_error,
+                           gmx::formatString("GPU timing update failure (OpenCL error %d: %s).",
+                                             cl_error,
+                                             ocl_get_error_string(cl_error).c_str())
+                                   .c_str());
+                milliseconds += (end_ns - start_ns) / 1000000.0;
+            }
+        }
+        reset();
+        return milliseconds;
+    }
+    /*! \brief Resets the internal state, releasing the used cl_events. */
+    inline void reset()
+    {
+        for (size_t i = 0; i < currentEvent_; i++)
+        {
+            if (events_[i]) // This conditional is ugly, but is required to make some tests (e.g. empty domain) pass
+            {
+                cl_int gmx_unused cl_error = clReleaseEvent(events_[i]);
+                GMX_ASSERT(CL_SUCCESS == cl_error, "OpenCL event release failure");
+            }
+        }
+        currentEvent_ = 0;
+        // As long as we're doing nullptr checks, we might want to be extra cautious.
+        events_.fill(nullptr);
+    }
+    /*! \brief Returns a new raw timing event
+     * for passing into individual GPU API calls
+     * within the region if the API requires it (e.g. on OpenCL).
+     */
+    inline CommandEvent* fetchNextEvent()
+    {
+        GMX_ASSERT(currentEvent_ < events_.size(), "Increase c_maxEventNumber_ if needed");
+        cl_event* result = &events_[currentEvent_];
+        currentEvent_++;
+        return result;
+    }
+};
+
+//! Short-hand for external use
+using GpuRegionTimer = GpuRegionTimerWrapper<GpuRegionTimerImpl>;
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/gpuregiontimer_sycl.h b/src/include/gromacs/gpu_utils/gpuregiontimer_sycl.h
new file mode 100644 (file)
index 0000000..b734960
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *  \brief Implements the GPU region timer for SYCL.
+ *
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ *
+ *  \inlibraryapi
+ */
+
+#ifndef GMX_GPU_UTILS_GPUREGIONTIMER_SYCL_H
+#define GMX_GPU_UTILS_GPUREGIONTIMER_SYCL_H
+
+#include "gromacs/gpu_utils/devicebuffer_sycl.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "gpuregiontimer.h"
+
+// Disabling Doxygen to avoid it having GpuRegionTimerImpl from both OpenCL and SYCL
+#ifndef DOXYGEN
+
+/*! \libinternal \brief
+ * The stub of SYCL implementation of the GPU code region timing.
+ *
+ * Does not do anything.
+ *
+ * \todo Implement
+ */
+class GpuRegionTimerImpl
+{
+public:
+    GpuRegionTimerImpl()  = default;
+    ~GpuRegionTimerImpl() = default;
+    //! No copying
+    GpuRegionTimerImpl(const GpuRegionTimerImpl&) = delete;
+    //! No assignment
+    GpuRegionTimerImpl& operator=(GpuRegionTimerImpl&&) = delete;
+    //! Moving is disabled but can be considered in the future if needed
+    GpuRegionTimerImpl(GpuRegionTimerImpl&&) = delete;
+
+    /*! \brief Should be called before the region start. */
+    inline void openTimingRegion(const DeviceStream& /*unused*/) {}
+    /*! \brief Should be called after the region end. */
+    inline void closeTimingRegion(const DeviceStream& /*unused*/) {}
+    /*! \brief Returns the last measured region timespan (in milliseconds) and calls \c reset(). */
+    // NOLINTNEXTLINE readability-convert-member-functions-to-static
+    inline double getLastRangeTime() { return 0; }
+    /*! \brief Resets the internal state, releasing the used handles, if any. */
+    inline void reset() {}
+    /*! \brief Returns a new raw timing event
+     * for passing into individual GPU API calls
+     * within the region if the API requires it (e.g. on OpenCL).
+     */
+    inline CommandEvent* fetchNextEvent() { return nullptr; }
+};
+
+//! Short-hand for external use
+using GpuRegionTimer = GpuRegionTimerWrapper<GpuRegionTimerImpl>;
+
+#endif // !DOXYGEN
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/gputraits.cuh b/src/include/gromacs/gpu_utils/gputraits.cuh
new file mode 100644 (file)
index 0000000..656e8a2
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_GPUTRAITS_CUH
+#define GMX_GPU_UTILS_GPUTRAITS_CUH
+
+/*! \libinternal \file
+ *  \brief Declares the CUDA type traits.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_gpu_utils
+ */
+#include <cuda_runtime.h>
+#include "gromacs/math/vectypes.h"
+
+//! Device texture for fast read-only data fetching
+using DeviceTexture = cudaTextureObject_t;
+
+//! \brief Single GPU call timing event - meaningless in CUDA
+using CommandEvent = void;
+
+//! Convenience alias for 2-wide float
+using Float2 = float2;
+
+//! Convenience alias for 3-wide float
+using Float3 = gmx::RVec;
+
+//! Convenience alias for 4-wide float.
+using Float4 = float4;
+
+/*! \internal \brief
+ * GPU kernels scheduling description. This is same in OpenCL/CUDA.
+ * Provides reasonable defaults, one typically only needs to set the GPU stream
+ * and non-1 work sizes.
+ */
+struct KernelLaunchConfig
+{
+    //! Block counts
+    size_t gridSize[3] = { 1, 1, 1 };
+    //! Per-block thread counts
+    size_t blockSize[3] = { 1, 1, 1 };
+    //! Shared memory size in bytes
+    size_t sharedMemorySize = 0;
+};
+
+//! Sets whether device code can use arrays that are embedded in structs.
+#define c_canEmbedBuffers true
+// TODO this should be constexpr bool
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/gputraits.h b/src/include/gromacs/gpu_utils/gputraits.h
new file mode 100644 (file)
index 0000000..3fb58eb
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_GPUTRAITS_H
+#define GMX_GPU_UTILS_GPUTRAITS_H
+
+/*! \libinternal \file
+ *  \brief Declares the GPU type traits for non-GPU builds.
+ *
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_gpu_utils
+ */
+
+#include "config.h"
+
+#if GMX_GPU_CUDA
+
+#    include "gromacs/gpu_utils/gputraits.cuh"
+
+#elif GMX_GPU_OPENCL
+
+#    include "gromacs/gpu_utils/gputraits_ocl.h"
+
+#elif GMX_GPU_SYCL
+
+#    include "gromacs/gpu_utils/gputraits_sycl.h"
+
+#else
+
+using DeviceTexture = void*;
+
+//! \brief Single GPU call timing event
+using CommandEvent = void*;
+
+// Stubs for CPU-only build. Might be changed in #3312.
+struct Float2
+{
+};
+struct Float3
+{
+};
+struct Float4
+{
+};
+
+#endif // GMX_GPU
+
+namespace gmx
+{
+//! Reinterpret-cast any pointer \p in to \c Float3, checking the type compatibility.
+template<typename T>
+static inline Float3* asGenericFloat3Pointer(T* in)
+{
+    static_assert(sizeof(T) == sizeof(Float3),
+                  "Size of the host-side data-type is different from the size of the generic "
+                  "device-side counterpart.");
+    return reinterpret_cast<Float3*>(in);
+}
+
+//! Reinterpret-cast any const pointer \p in to \c Float3, checking the type compatibility.
+template<typename T>
+static inline const Float3* asGenericFloat3Pointer(const T* in)
+{
+    static_assert(sizeof(T) == sizeof(Float3),
+                  "Size of the host-side data-type is different from the size of the generic "
+                  "device-side counterpart.");
+    return reinterpret_cast<const Float3*>(in);
+}
+
+//! Reinterpret-cast any container \p in to \c Float3, checking the type compatibility.
+template<typename C>
+static inline Float3* asGenericFloat3Pointer(C& in)
+{
+    static_assert(sizeof(*in.data()) == sizeof(Float3),
+                  "Size of the host-side data-type is different from the size of the device-side "
+                  "counterpart.");
+    return reinterpret_cast<Float3*>(in.data());
+}
+
+//! Reinterpret-cast any const container \p in to \c Float3, checking the type compatibility.
+template<typename C>
+static inline const Float3* asGenericFloat3Pointer(const C& in)
+{
+    static_assert(sizeof(*in.data()) == sizeof(Float3),
+                  "Size of the host-side data-type is different from the size of the device-side "
+                  "counterpart.");
+    return reinterpret_cast<const Float3*>(in.data());
+}
+} // namespace gmx
+
+#endif // GMX_GPU_UTILS_GPUTRAITS_H
diff --git a/src/include/gromacs/gpu_utils/gputraits_ocl.h b/src/include/gromacs/gpu_utils/gputraits_ocl.h
new file mode 100644 (file)
index 0000000..a8a3c26
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_GPUTRAITS_OCL_H
+#define GMX_GPU_UTILS_GPUTRAITS_OCL_H
+
+/*! \libinternal \file
+ *  \brief Declares the OpenCL type traits.
+ *
+ *  \author Aleksei Iupinov <a.yupinov@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_gpu_utils
+ */
+
+#include "gromacs/gpu_utils/gmxopencl.h"
+#include "gromacs/math/vectypes.h"
+
+using DeviceTexture = void*;
+
+//! \brief Single GPU call timing event
+using CommandEvent = cl_event;
+
+//! Convenience alias for 2-wide float
+using Float2 = cl_float2;
+
+//! Convenience alias for 3-wide float. Not using cl_float3 due to alignment issues.
+using Float3 = gmx::RVec;
+
+//! Convenience alias for 4-wide float.
+using Float4 = cl_float4;
+
+/*! \internal \brief
+ * GPU kernels scheduling description. This is same in OpenCL/CUDA.
+ * Provides reasonable defaults, one typically only needs to set the GPU stream
+ * and non-1 work sizes.
+ */
+struct KernelLaunchConfig
+{
+    //! Work groups (CUDA blocks) counts
+    size_t gridSize[3] = { 1, 1, 1 };
+    //! Per work group (CUDA block) thread counts
+    size_t blockSize[3] = { 1, 1, 1 };
+    //! Shared memory size in bytes
+    size_t sharedMemorySize = 0;
+};
+
+/*! \brief Sets whether device code can use arrays that are embedded in structs.
+ * Note that OpenCL 2.x might be able to do this, but we use 1.2.
+ */
+#define c_canEmbedBuffers false
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/gputraits_sycl.h b/src/include/gromacs/gpu_utils/gputraits_sycl.h
new file mode 100644 (file)
index 0000000..65734c7
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_GPU_UTILS_GPUTRAITS_SYCL_H
+#define GMX_GPU_UTILS_GPUTRAITS_SYCL_H
+
+/*! \libinternal \file
+ *  \brief Declares the SYCL type traits.
+ *
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_gpu_utils
+ */
+
+#include <cstddef>
+
+#include "gromacs/gpu_utils/gmxsycl.h"
+#include "gromacs/math/vectypes.h"
+
+//! Type of device texture object. In SYCL, that would be \c sycl::image, but it's not used.
+using DeviceTexture = void*;
+
+//! \brief Single GPU call timing event, not used with SYCL
+using CommandEvent = void*;
+
+// TODO: Issue #3312
+//! Convenience alias.
+using Float4 = cl::sycl::float4;
+//! Convenience alias. Not using cl::sycl::float3 due to alignment issues.
+using Float3 = gmx::RVec;
+//! Convenience alias for cl::sycl::float2
+using Float2 = cl::sycl::float2;
+
+/*! \internal \brief
+ * GPU kernels scheduling description.
+ * One typically only needs to set non-1 work sizes.
+ *
+ * \note This struct uses CUDA/OpenCL layout, with the first dimension being contiguous.
+ *       It is different from the SYCL standard, where the last dimension is contiguous.
+ *       The transpose is to be performed internally in ISyclKernelFunctor::launch.
+ * \note \c sharedMemorySize is ignored in SYCL.
+ */
+struct KernelLaunchConfig
+{
+    //! Work groups (CUDA blocks) counts
+    size_t gridSize[3] = { 1, 1, 1 };
+    //! Per work group (CUDA block) thread counts
+    size_t blockSize[3] = { 1, 1, 1 };
+    //! Shared memory size in bytes
+    size_t sharedMemorySize = 0;
+};
+
+/*! \brief Sets whether device code can use arrays that are embedded in structs.
+ *
+ * That is not technically true for SYCL: the device code needs dedicated
+ * \c sycl::buffer/accessor objects.
+ * But our \c prepareGpuKernelArguments and \c launchGpuKernel functions deal
+ * with that, so we can pass embedded buffers to them, which is what this
+ * constant actually controls.
+ */
+#define c_canEmbedBuffers true
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/hostallocator.h b/src/include/gromacs/gpu_utils/hostallocator.h
new file mode 100644 (file)
index 0000000..f483f0c
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief Declares gmx::HostAllocationPolicy, gmx::HostAllocator,
+ * gmx::HostVector and gmx::PaddedHostVector, which are used to make/be
+ * standard library containers that can allocate memory suitable for transfers.
+ * Currently the only supported transfers using pinned memory are
+ * to CUDA GPUs, but other possibilities exist in future.
+ *
+ * \todo This should not be in the public API, but it needs to be
+ * for the moment because state.h is in that API.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inpublicapi
+ */
+#ifndef GMX_GPU_UTILS_HOSTALLOCATOR_H
+#define GMX_GPU_UTILS_HOSTALLOCATOR_H
+
+#include <cstddef>
+
+#include <memory>
+
+#include "gromacs/math/paddedvector.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/exceptions.h"
+
+namespace gmx
+{
+
+/*! \brief Helper enum for pinning policy of the allocation of
+ * HostAllocationPolicy.
+ *
+ * For an efficient non-blocking transfer (e.g. to a GPU), the memory
+ * pages for a buffer need to be pinned to a physical page. Aligning
+ * such buffers to a physical page should miminize the number of pages
+ * that need to be pinned. However, some buffers that may be used for
+ * such transfers may also be used in either GROMACS builds or run
+ * paths that cannot use such a device, so the policy can be
+ * configured so that the resource consumption is no higher than
+ * required for correct, efficient operation in all cases. */
+enum class PinningPolicy : int
+{
+    CannotBePinned,    // Memory is not known to be suitable for pinning.
+    PinnedIfSupported, // Memory is suitable for efficient pinning, e.g. because it is
+                       // allocated to be page aligned, and will be pinned when supported.
+};
+
+//! Forward declaration of host allocation policy class.
+class HostAllocationPolicy;
+
+/*! \brief Memory allocator that uses HostAllocationPolicy.
+ *
+ *  \tparam T          Type of objects to allocate
+ *
+ * This convenience partial specialization can be used for the
+ * optional allocator template parameter in standard library
+ * containers whose memory may be used for e.g. GPU transfers. The
+ * memory will always be allocated according to the behavior of
+ * HostAllocationPolicy.
+ */
+template<class T>
+using HostAllocator = Allocator<T, HostAllocationPolicy>;
+
+//! Convenience alias for std::vector that uses HostAllocator.
+template<class T>
+using HostVector = std::vector<T, HostAllocator<T>>;
+
+//! Convenience alias for PaddedVector that uses HostAllocator.
+template<class T>
+using PaddedHostVector = PaddedVector<T, HostAllocator<T>>;
+
+/*! \libinternal
+ * \brief Policy class for configuring gmx::Allocator, to manage
+ * allocations of memory that may be needed for e.g. GPU transfers.
+ *
+ * This allocator has state, so is most useful in cases where it is
+ * not known at compile time whether the allocated memory will be
+ * transferred to some device. It will increase the size of containers
+ * that use it. If the GROMACS build is configured with CUDA support,
+ * then memory will be allocated with PageAlignedAllocator, and that
+ * page pinned to physical memory if the pinning mode has been
+ * activated. If pinning mode is deactivated, or the GROMACS build
+ * does not support CUDA, then the memory will be allocated with
+ * AlignedAllocator. The pin() and unpin() methods work with the CUDA
+ * build, and silently do nothing otherwise. In future, we may modify
+ * or generalize this to work differently in other cases.
+ *
+ * The intended use is to configure gmx::Allocator with this class as
+ * its policy class, and then to use e.g.
+ * std::vector::get_allocator().getPolicy() to control whether the
+ * allocation policy should activate its pinning mode. The policy
+ * object can also be used to explicitly pin() and unpin() the buffer
+ * when it is using PinningPolicy::PinnedIfSupported. The policy object is
+ * returned by value (as required by the C++ standard for
+ * get_allocator(), which copies a std::shared_ptr, so the policy
+ * object should be retrieved sparingly, e.g. only upon resize of the
+ * allocation. (Normal operation of the vector, e.g. during resize,
+ * incurs only the cost of the pointer indirection needed to consult
+ * the current state of the allocation policy.)
+ *
+ * \todo As a minor optimization, consider also having a stateless
+ * version of this policy, which might be slightly faster or more
+ * convenient to use in the cases where it is known at compile time
+ * that the allocation will be used to transfer to a GPU.
+ */
+class HostAllocationPolicy
+{
+public:
+    //! Constructor
+    HostAllocationPolicy(PinningPolicy policy = PinningPolicy::CannotBePinned);
+    /*! \brief Return the alignment size currently used by the active pinning policy. */
+    std::size_t alignment() const noexcept;
+    /*! \brief Allocate and perhaps pin page-aligned memory suitable for
+     * e.g. GPU transfers.
+     *
+     * Before attempting to allocate, unpin() is called. After a
+     * successful allocation, pin() is called. (Whether these do
+     * things depends on the PinningPolicy that is in effect.)
+     *
+     *  \param bytes Amount of memory (bytes) to allocate. It is valid to ask for
+     *               0 bytes, which will return a non-null pointer that is properly
+     *               aligned and padded (but that you should not use).
+     *
+     *  \return Valid pointer if the allocation+optional pinning worked, otherwise nullptr.
+     *
+     *  \note Memory allocated with this routine must be released
+     *        with gmx::HostAllocationPolicy::free(), and
+     *        absolutely not the system free().
+     *
+     * Does not throw.
+     */
+    void* malloc(std::size_t bytes) const noexcept;
+    /*! \brief Free the memory, after unpinning (if appropriate).
+     *
+     *  \param buffer  Memory pointer previously returned from gmx::HostAllocationPolicy::malloc()
+     *
+     *  \note This routine should only be called with pointers
+     *        obtained from gmx:HostAllocationPolicy::malloc(),
+     *        and absolutely not any pointers obtained the system
+     *        malloc().
+     *
+     * Does not throw.
+     */
+    void free(void* buffer) const noexcept;
+    /*! \brief Return the active pinning policy.
+     *
+     * Does not throw.
+     */
+    PinningPolicy pinningPolicy() const { return pinningPolicy_; }
+    //! Don't propagate for copy
+    using propagate_on_container_copy_assignment = std::false_type;
+    //! Propagate for move
+    using propagate_on_container_move_assignment = std::true_type;
+    //! Propagate for move
+    using propagate_on_container_swap = std::true_type;
+    //! Use default allocator for copy (same as construct+copy)
+    // NOLINTNEXTLINE readability-convert-member-functions-to-static
+    HostAllocationPolicy select_on_container_copy_construction() const { return {}; }
+
+private:
+    //! Pinning policy
+    PinningPolicy pinningPolicy_;
+};
+
+/*! \brief Return true if two allocators are identical
+ *
+ * True if pinning policy is the same.
+ */
+template<class T1, class T2>
+bool operator==(const Allocator<T1, HostAllocationPolicy>& a, const Allocator<T2, HostAllocationPolicy>& b)
+{
+    return a.pinningPolicy() == b.pinningPolicy();
+}
+
+/*! \brief Helper function for changing the pinning policy of a pinnable vector.
+ *
+ * If the vector has contents, then a full reallocation and buffer
+ * copy are needed if the policy change requires tighter restrictions,
+ * and desirable even if the policy change requires looser
+ * restrictions. That cost is OK, because GROMACS will do this
+ * operation very rarely (e.g. when auto-tuning and deciding to switch
+ * whether a task will run on a GPU, or not). */
+template<typename PinnableVector>
+void changePinningPolicy(PinnableVector* v, PinningPolicy pinningPolicy)
+{
+    // Force reallocation by element-wise move (because policy is
+    // different container is forced to realloc). Does nothing if
+    // policy is the same.
+    *v = PinnableVector(std::move(*v), { pinningPolicy });
+}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/ocl_caching.h b/src/include/gromacs/gpu_utils/ocl_caching.h
new file mode 100644 (file)
index 0000000..82b4e45
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *  \brief Declare infrastructure for managing caching of OpenCL
+ *  JIT-ted binaries
+ *
+ *  This functionality is currently disabled in compileProgram()
+ *
+ *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
+ *  \author Anca Hamuraru <anca@streamcomputing.eu>
+ *  \author Teemu Virolainen <teemu@streamcomputing.eu>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ */
+#ifndef GMX_GPU_UTILS_OCL_CACHING_H
+#define GMX_GPU_UTILS_OCL_CACHING_H
+
+#include <string>
+
+#include "gromacs/gpu_utils/oclutils.h"
+
+namespace gmx
+{
+namespace ocl
+{
+
+/*! \brief Construct the name for the binary cache file
+ *
+ * \param[in]  kernelFilename  Name of the kernel from which the binary will be compiled.
+ * \param[in]  deviceId        ID of the device upon which the binary is used.
+ *
+ * \todo The set of preprocessor options should also form part of the
+ * identification of the cached binary. Also perhaps compiler, runtime
+ * and device version info?
+ *
+ * \todo Mutual exclusion of ranks and nodes should also be implemented
+ * if/when caching is re-enabled.
+ *
+ * \returns The name of the cache file.
+ */
+std::string makeBinaryCacheFilename(const std::string& kernelFilename, cl_device_id deviceId);
+
+/*! \brief Check if there's a valid cache available, and return it if so
+ *
+ * \param[in]  filename   Name of valid file containing the binary cache
+ * \param[in]  context    The OpenCL context
+ * \param[in]  deviceId   The ID of the device on which to use the program
+ *
+ * \returns The OpenCL program read from the cache
+ *
+ * \throws InternalError  if an OpenCL error was encountered
+ *         FileIOError    if the file could not be opened
+ */
+cl_program makeProgramFromCache(const std::string& filename, cl_context context, cl_device_id deviceId);
+
+/*! \brief Implement caching of OpenCL binaries
+ *
+ * \param[in] program     Index of program to cache
+ * \param[in] filename    Name of file to use for the cache
+ *
+ * \throws InternalError  if an OpenCL error was encountered
+ *         FileIOError    if the file could not be opened
+ */
+void writeBinaryToCache(cl_program program, const std::string& filename);
+
+} // namespace ocl
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/ocl_compiler.h b/src/include/gromacs/gpu_utils/ocl_compiler.h
new file mode 100644 (file)
index 0000000..00baec0
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declare infrastructure for OpenCL JIT compilation
+ *
+ *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
+ *  \author Anca Hamuraru <anca@streamcomputing.eu>
+ *  \author Teemu Virolainen <teemu@streamcomputing.eu>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_OCL_COMPILER_H
+#define GMX_GPU_UTILS_OCL_COMPILER_H
+
+#include <string>
+
+#include "gromacs/gpu_utils/oclutils.h"
+#include "gromacs/hardware/device_information.h"
+
+namespace gmx
+{
+namespace ocl
+{
+
+/*! \brief Get the device-specific warp size
+ *
+ *  This is platform implementation dependent and seems to only work on the Nvidia and AMD
+ * platforms! Nvidia reports 32, AMD for GPU 64. Intel seems to report 16, but that is not correct,
+ *  as it execution width can be between 8-32 and it's picked per-kernel at compile-time.
+ *  Therefore, for Intel it should actually be queried separately for each kernel (Issue #2520).
+ *
+ *  \param  context   Current OpenCL context
+ *  \param  deviceId OpenCL device with the context
+ *  \return cl_int value of the warp size
+ *
+ * \throws InternalError if an OpenCL error was encountered
+ */
+size_t getDeviceWarpSize(cl_context context, cl_device_id deviceId);
+
+
+/*! \brief Get the kernel-specific warp size
+ *
+ *  \param  kernel   THe OpenCL kernel object
+ *  \param  deviceId OpenCL device for which the kernel warp size is queried
+ *  \return cl_int value of the warp size
+ *
+ * \throws InternalError if an OpenCL error was encountered
+ */
+size_t getKernelWarpSize(cl_kernel kernel, cl_device_id deviceId);
+
+/*! \brief Compile the specified kernel for the context and device.
+ *
+ * \param[out] fplog                 Open file pointer for log output
+ * \param[in]  kernelRelativePath    Relative path to the kernel in the source tree,
+ *                                   e.g. "src/gromacs/mdlib/nbnxn_ocl" for NB kernels.
+ * \param[in]  kernelBaseFilename    The name of the kernel source file to compile, e.g.
+ *                                   "nbnxn_ocl_kernels.cl"
+ * \param[in]  extraDefines          Preprocessor defines required by the calling code,
+ *                                   e.g. for configuring the kernels
+ * \param[in]  context               OpenCL context on the device to compile for
+ * \param[in]  deviceId              OpenCL device id of the device to compile for
+ * \param[in]  deviceVendor          Enumerator of the device vendor to compile for
+ *
+ * \returns The compiled OpenCL program
+ *
+ * \todo Consider whether we can parallelize the compilation of all
+ * the kernels by compiling them in separate programs - but since the
+ * resulting programs can't refer to each other, that might lead to
+ * bloat of util code?
+ *
+ * \throws std::bad_alloc  if out of memory.
+ *         FileIOError     if a file I/O error prevents returning a valid compiled program.
+ *         InternalError   if an OpenCL API error prevents returning a valid compiled program. */
+cl_program compileProgram(FILE*              fplog,
+                          const std::string& kernelRelativePath,
+                          const std::string& kernelBaseFilename,
+                          const std::string& extraDefines,
+                          cl_context         context,
+                          cl_device_id       deviceId,
+                          DeviceVendor       deviceVendor);
+
+} // namespace ocl
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/oclraii.h b/src/include/gromacs/gpu_utils/oclraii.h
new file mode 100644 (file)
index 0000000..02aeae0
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declare RAII helpers for OpenCL types, along with
+ * supporting type traits.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_OCLRAII_H
+#define GMX_GPU_UTILS_OCLRAII_H
+
+#include "gromacs/gpu_utils/gmxopencl.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief Stub for OpenCL type traits */
+template<typename cl_type>
+struct OpenClTraits;
+
+/*! \libinternal \brief Implements common trait infrastructure for OpenCL types. */
+template<typename cl_type>
+struct OpenClTraitsBase
+{
+    //! Type of the function that will release a handle of this type.
+    using ReleaserType = cl_int (*)(cl_type);
+};
+
+/*! \libinternal \brief Implements traits for cl_context. */
+template<>
+struct OpenClTraits<cl_context> : public OpenClTraitsBase<cl_context>
+{
+    //! Function that will release a handle of this type.
+    static constexpr ReleaserType releaser = clReleaseContext;
+};
+
+/*! \libinternal \brief Implements traits for cl_command_queue. */
+template<>
+struct OpenClTraits<cl_command_queue> : public OpenClTraitsBase<cl_command_queue>
+{
+    //! Function that will release a handle of this type.
+    static constexpr ReleaserType releaser = clReleaseCommandQueue;
+};
+
+/*! \libinternal \brief Implements traits for cl_program. */
+template<>
+struct OpenClTraits<cl_program> : public OpenClTraitsBase<cl_program>
+{
+    //! Function that will release a handle of this type.
+    static constexpr ReleaserType releaser = clReleaseProgram;
+};
+
+/*! \libinternal \brief Implements traits for cl_kernel. */
+template<>
+struct OpenClTraits<cl_kernel> : public OpenClTraitsBase<cl_kernel>
+{
+    //! Function that will release a handle of this type.
+    static constexpr ReleaserType releaser = clReleaseKernel;
+};
+
+/*! \libinternal \brief Wrapper of OpenCL type \c cl_type to implement RAII.
+ *
+ * Works by calling the releaser function associated with cl_type
+ * by OpenClTraits.
+ *
+ * Simple copying and assignment are not supported, because there's no
+ * need for that, and would require OpenCL API calls for deep copies
+ * if they were needed. Move and move assignment are fine, however. */
+template<typename cl_type>
+class ClHandle
+{
+public:
+    //! Constructor that takes an already created handle.
+    explicit ClHandle(cl_type handle) : handle_(handle) {}
+    //! Destructor that calls the releaser associated with cl_type.
+    ~ClHandle() { OpenClTraits<cl_type>::releaser(handle_); }
+    //! Deleted default constructor.
+    ClHandle() = delete;
+    //! Deleted assignment operator.
+    ClHandle& operator=(const ClHandle&) = delete;
+    //! Deleted copy constructor.
+    ClHandle(const ClHandle&) = delete;
+    //! Default move assignment operator.
+    ClHandle& operator=(ClHandle&&) noexcept = default;
+    //! Default copy constructor.
+    ClHandle(ClHandle&&) noexcept = default;
+    /*! \brief Convenience conversion operator so the wrapper type
+     * can simply convert to the wrapped type. */
+    operator cl_type() const { return handle_; }
+
+private:
+    //! The wrapped object.
+    cl_type handle_;
+};
+
+//! Convenience declarations.
+/*! @{ */
+using ClContext      = ClHandle<cl_context>;
+using ClCommandQueue = ClHandle<cl_command_queue>;
+using ClProgram      = ClHandle<cl_program>;
+using ClKernel       = ClHandle<cl_kernel>;
+/*! @} */
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/oclutils.h b/src/include/gromacs/gpu_utils/oclutils.h
new file mode 100644 (file)
index 0000000..5f881c3
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declare utility routines for OpenCL
+ *
+ *  \author Anca Hamuraru <anca@streamcomputing.eu>
+ *  \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_OCLUTILS_H
+#define GMX_GPU_UTILS_OCLUTILS_H
+
+#include <string>
+
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/gmxopencl.h"
+#include "gromacs/gpu_utils/gputraits_ocl.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+enum class GpuApiCallBehavior;
+
+/*! \internal
+ * \brief OpenCL GPU runtime data
+ *
+ * The device runtime data is meant to hold objects associated with a GROMACS rank's
+ * (thread or process) use of a single device (multiple devices per rank is not
+ * implemented). These objects should be constructed at ther point where a device
+ * dets assigned to a rank and released at when this assignment is no longer valid
+ * (i.e. at cleanup in the current implementation).
+ *
+ */
+struct gmx_device_runtime_data_t
+{
+    //! OpenCL program
+    cl_program program;
+};
+
+/*! \brief Convert error code to diagnostic string */
+std::string ocl_get_error_string(cl_int error);
+
+/*! \brief Pretend to synchronize an OpenCL stream (dummy implementation).
+ *
+ *  \returns  Not implemented in OpenCL.
+ */
+static inline bool haveStreamTasksCompleted(const DeviceStream& /* deviceStream */)
+{
+    GMX_RELEASE_ASSERT(false, "haveStreamTasksCompleted is not implemented for OpenCL");
+    return false;
+}
+
+/* Kernel launch helpers */
+
+/*! \brief
+ * A function for setting up a single OpenCL kernel argument.
+ * This is the tail of the compile-time recursive function below.
+ * It has to be seen by the compiler first.
+ * As NB kernels might be using dynamic local memory as the last argument,
+ * this function also manages that, using sharedMemorySize from \p config.
+ *
+ * \param[in]     kernel          Kernel function handle
+ * \param[in]     config          Kernel configuration for launching
+ * \param[in]     argIndex        Index of the current argument
+ */
+void inline prepareGpuKernelArgument(cl_kernel kernel, const KernelLaunchConfig& config, size_t argIndex)
+{
+    if (config.sharedMemorySize > 0)
+    {
+        cl_int gmx_used_in_debug clError =
+                clSetKernelArg(kernel, argIndex, config.sharedMemorySize, nullptr);
+        GMX_ASSERT(CL_SUCCESS == clError, ocl_get_error_string(clError).c_str());
+    }
+}
+
+/*! \brief
+ * Compile-time recursive function for setting up a single OpenCL kernel argument.
+ * This function uses one kernel argument pointer \p argPtr to call clSetKernelArg(),
+ * and calls itself on the next argument, eventually calling the tail function above.
+ *
+ * \tparam        CurrentArg      Type of the current argument
+ * \tparam        RemainingArgs   Types of remaining arguments after the current one
+ * \param[in]     kernel          Kernel function handle
+ * \param[in]     config          Kernel configuration for launching
+ * \param[in]     argIndex        Index of the current argument
+ * \param[in]     argPtr          Pointer to the current argument
+ * \param[in]     otherArgsPtrs   Pack of pointers to arguments remaining to process after the current one
+ */
+template<typename CurrentArg, typename... RemainingArgs>
+void prepareGpuKernelArgument(cl_kernel                 kernel,
+                              const KernelLaunchConfig& config,
+                              size_t                    argIndex,
+                              const CurrentArg*         argPtr,
+                              const RemainingArgs*... otherArgsPtrs)
+{
+    cl_int gmx_used_in_debug clError = clSetKernelArg(kernel, argIndex, sizeof(CurrentArg), argPtr);
+    GMX_ASSERT(CL_SUCCESS == clError, ocl_get_error_string(clError).c_str());
+
+    // Assert on types not allowed to be passed to a kernel
+    // (as per section 6.9 of the OpenCL spec).
+    static_assert(
+            !std::is_same_v<CurrentArg,
+                            bool> && !std::is_same_v<CurrentArg, size_t> && !std::is_same_v<CurrentArg, ptrdiff_t> && !std::is_same_v<CurrentArg, intptr_t> && !std::is_same_v<CurrentArg, uintptr_t>,
+            "Invalid type passed to OpenCL kernel functions (see OpenCL spec section 6.9).");
+
+    prepareGpuKernelArgument(kernel, config, argIndex + 1, otherArgsPtrs...);
+}
+
+/*! \brief
+ * A wrapper function for setting up all the OpenCL kernel arguments.
+ * Calls the recursive functions above.
+ *
+ * \tparam    Args            Types of all the kernel arguments
+ * \param[in] kernel          Kernel function handle
+ * \param[in] config          Kernel configuration for launching
+ * \param[in] argsPtrs        Pointers to all the kernel arguments
+ * \returns A handle for the prepared parameter pack to be used with launchGpuKernel() as the last argument
+ * - currently always nullptr for OpenCL, as it manages kernel/arguments association by itself.
+ */
+template<typename... Args>
+void* prepareGpuKernelArguments(cl_kernel kernel, const KernelLaunchConfig& config, const Args*... argsPtrs)
+{
+    prepareGpuKernelArgument(kernel, config, 0, argsPtrs...);
+    return nullptr;
+}
+
+/*! \brief Launches the OpenCL kernel and handles the errors.
+ *
+ * \param[in] kernel          Kernel function handle
+ * \param[in] config          Kernel configuration for launching
+ * \param[in] deviceStream    GPU stream to launch kernel in
+ * \param[in] timingEvent     Timing event, fetched from GpuRegionTimer
+ * \param[in] kernelName      Human readable kernel description, for error handling only
+ * \throws gmx::InternalError on kernel launch failure
+ */
+inline void launchGpuKernel(cl_kernel                 kernel,
+                            const KernelLaunchConfig& config,
+                            const DeviceStream&       deviceStream,
+                            CommandEvent*             timingEvent,
+                            const char*               kernelName,
+                            const void* /*kernelArgs*/)
+{
+    const int       workDimensions   = 3;
+    const size_t*   globalWorkOffset = nullptr;
+    const size_t    waitListSize     = 0;
+    const cl_event* waitList         = nullptr;
+    size_t          globalWorkSize[3];
+    for (int i = 0; i < workDimensions; i++)
+    {
+        globalWorkSize[i] = config.gridSize[i] * config.blockSize[i];
+    }
+    cl_int clError = clEnqueueNDRangeKernel(deviceStream.stream(),
+                                            kernel,
+                                            workDimensions,
+                                            globalWorkOffset,
+                                            globalWorkSize,
+                                            config.blockSize,
+                                            waitListSize,
+                                            waitList,
+                                            timingEvent);
+    if (CL_SUCCESS != clError)
+    {
+        const std::string errorMessage = "GPU kernel (" + std::string(kernelName)
+                                         + ") failed to launch: " + ocl_get_error_string(clError);
+        GMX_THROW(gmx::InternalError(errorMessage));
+    }
+}
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/pinning.h b/src/include/gromacs/gpu_utils/pinning.h
new file mode 100644 (file)
index 0000000..c53e8e8
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares functions for pinning memory to be suitable for
+ * efficient GPU transfers on CUDA.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ */
+
+#include <cstddef>
+
+#include "gromacs/gpu_utils/gpu_macros.h"
+
+namespace gmx
+{
+
+/*! \brief Pin the allocation to physical memory.
+ *
+ * Requires that \c pointer is not nullptr.
+ *
+ * Does not throw.
+ */
+CUDA_FUNC_QUALIFIER void pinBuffer(void*       CUDA_FUNC_ARGUMENT(pointer),
+                                   std::size_t CUDA_FUNC_ARGUMENT(numBytes)) noexcept CUDA_FUNC_TERM;
+
+/*! \brief Unpin the allocation.
+ *
+ * Requries that \c pointer is not nullptr and was previously pinned
+ * with pinBuffer().
+ *
+ * Does not throw.
+ */
+CUDA_FUNC_QUALIFIER void unpinBuffer(void* CUDA_FUNC_ARGUMENT(pointer)) noexcept CUDA_FUNC_TERM;
+
+} // namespace gmx
diff --git a/src/include/gromacs/gpu_utils/pmalloc.h b/src/include/gromacs/gpu_utils/pmalloc.h
new file mode 100644 (file)
index 0000000..d6b2707
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declare functions for host-side memory handling.
+ *
+ *  \author Szilard Pall <pall.szilard@gmail.com>
+ *  \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_PMALLOC_H
+#define GMX_GPU_UTILS_PMALLOC_H
+
+#include <stdlib.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+///@cond INTERNAL
+
+/*! \brief Allocates nbytes of page-locked memory. */
+void pmalloc(void** h_ptr, size_t nbytes);
+
+/*! \brief Frees page locked memory allocated with pmalloc. */
+void pfree(void* h_ptr);
+
+///@endcond
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/sycl_kernel_utils.h b/src/include/gromacs/gpu_utils/sycl_kernel_utils.h
new file mode 100644 (file)
index 0000000..544c9e2
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GPU_UTILS_SYCL_KERNEL_UTILS_H
+#define GMX_GPU_UTILS_SYCL_KERNEL_UTILS_H
+
+#include "gmxsycl.h"
+
+/*! \file
+ *  \brief SYCL kernel helper functions.
+ *
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ */
+
+//! \brief Full warp active thread mask used in CUDA warp-level primitives.
+static constexpr unsigned int c_cudaFullWarpMask = 0xffffffff;
+
+/*! \brief Convenience wrapper to do atomic addition to a global buffer.
+ */
+template<typename T, sycl_2020::memory_scope MemoryScope = sycl_2020::memory_scope::device>
+static inline void atomicFetchAdd(T& val, const T delta)
+{
+    sycl_2020::atomic_ref<T, sycl_2020::memory_order::relaxed, MemoryScope, cl::sycl::access::address_space::global_space> ref(
+            val);
+    ref.fetch_add(delta);
+}
+
+/*! \brief Convenience wrapper to do atomic loads from a global buffer.
+ */
+template<typename T, sycl_2020::memory_scope MemoryScope = sycl_2020::memory_scope::device>
+static inline T atomicLoad(T& val)
+{
+    sycl_2020::atomic_ref<T, sycl_2020::memory_order::relaxed, MemoryScope, cl::sycl::access::address_space::global_space> ref(
+            val);
+    return ref.load();
+}
+
+
+/*! \brief Issue an intra sub-group barrier.
+ *
+ * Equivalent with CUDA's \c syncwarp(c_cudaFullWarpMask).
+ *
+ */
+template<int Dim>
+static inline void subGroupBarrier(const cl::sycl::nd_item<Dim> itemIdx)
+{
+#if GMX_SYCL_HIPSYCL
+    cl::sycl::group_barrier(itemIdx.get_sub_group(), cl::sycl::memory_scope::sub_group);
+#else
+    itemIdx.get_sub_group().barrier();
+#endif
+}
+
+namespace sycl_2020
+{
+#if GMX_SYCL_HIPSYCL
+__device__ __host__ static inline float shift_left(sycl_2020::sub_group,
+                                                   float                                var,
+                                                   sycl_2020::sub_group::linear_id_type delta)
+{
+    // No sycl::sub_group::shift_left / shuffle_down in hipSYCL yet
+#    ifdef SYCL_DEVICE_ONLY
+#        if defined(HIPSYCL_PLATFORM_CUDA) && defined(__HIPSYCL_ENABLE_CUDA_TARGET__)
+    return __shfl_down_sync(c_cudaFullWarpMask, var, delta);
+#        elif defined(HIPSYCL_PLATFORM_ROCM) && defined(__HIPSYCL_ENABLE_HIP_TARGET__)
+    // Do we need more ifdefs? https://github.com/ROCm-Developer-Tools/HIP/issues/1491
+    return __shfl_down(var, delta);
+#        else
+#            error "Unsupported hipSYCL target"
+#        endif
+#    else
+    // Should never be called
+    GMX_UNUSED_VALUE(var);
+    GMX_UNUSED_VALUE(delta);
+    assert(false);
+    return NAN;
+#    endif
+}
+#elif GMX_SYCL_DPCPP
+static inline float shift_left(sycl_2020::sub_group sg, float var, sycl_2020::sub_group::linear_id_type delta)
+{
+    return sg.shuffle_down(var, delta);
+}
+#endif
+
+#if GMX_SYCL_HIPSYCL
+__device__ __host__ static inline float shift_right(sycl_2020::sub_group,
+                                                    float                                var,
+                                                    sycl_2020::sub_group::linear_id_type delta)
+{
+    // No sycl::sub_group::shift_right / shuffle_up in hipSYCL yet
+#    ifdef SYCL_DEVICE_ONLY
+#        if defined(HIPSYCL_PLATFORM_CUDA) && defined(__HIPSYCL_ENABLE_CUDA_TARGET__)
+    return __shfl_up_sync(c_cudaFullWarpMask, var, delta);
+#        elif defined(HIPSYCL_PLATFORM_ROCM) && defined(__HIPSYCL_ENABLE_HIP_TARGET__)
+    // Do we need more ifdefs? https://github.com/ROCm-Developer-Tools/HIP/issues/1491
+    return __shfl_up(var, delta);
+#        else
+#            error "Unsupported hipSYCL target"
+#        endif
+#    else
+    // Should never be called
+    assert(false);
+    GMX_UNUSED_VALUE(var);
+    GMX_UNUSED_VALUE(delta);
+    return NAN;
+#    endif
+}
+#elif GMX_SYCL_DPCPP
+static inline float shift_right(sycl_2020::sub_group sg, float var, sycl_2020::sub_group::linear_id_type delta)
+{
+    return sg.shuffle_up(var, delta);
+}
+#endif
+} // namespace sycl_2020
+
+#endif /* GMX_GPU_UTILS_SYCL_KERNEL_UTILS_H */
diff --git a/src/include/gromacs/gpu_utils/syclutils.h b/src/include/gromacs/gpu_utils/syclutils.h
new file mode 100644 (file)
index 0000000..92a5127
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declare utility routines for SYCL
+ *
+ *  \author Andrey Alekseenko <al42and@gmail.com>
+ *  \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_SYCLUTILS_H
+#define GMX_GPU_UTILS_SYCLUTILS_H
+
+#include <string>
+
+#include "gromacs/gpu_utils/gmxsycl.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+class DeviceStream;
+enum class GpuApiCallBehavior;
+
+/*! \internal
+ * \brief SYCL GPU runtime data
+ *
+ * The device runtime data is meant to hold objects associated with a GROMACS rank's
+ * (thread or process) use of a single device (multiple devices per rank is not
+ * implemented). These objects should be constructed at the point where a device
+ * gets assigned to a rank and released at when this assignment is no longer valid
+ * (i.e. at cleanup in the current implementation).
+ */
+struct gmx_device_runtime_data_t
+{
+};
+
+#ifndef DOXYGEN
+
+//! \brief Interface for SYCL kernel function objects.
+class ISyclKernelFunctor
+{
+public:
+    //! \brief Virtual destructor.
+    virtual ~ISyclKernelFunctor() = default;
+    /*! \brief Set the kernel argument number \p argIndex to \p arg.
+     *
+     * \param argIndex Index of the argument. Maximum allowed value depends
+     *                 on the specific concrete class implementing this interface.
+     * \param arg      Pointer to the argument value.
+     *
+     * \note Valid values of \p argIndex and types of \p arg depend on the
+     *       specific concrete class implementing this interface. Passing
+     *       illegal values is undefined behavior.
+     * \note Similar to \c clSetKernelArg, it is not safe to call this
+     *       function on the same kernel object from multiple host threads.
+     */
+    virtual void setArg(size_t argIndex, void* arg) = 0;
+    /*! \brief Launch the kernel.
+     *
+     * \param config       Work-group configuration.
+     * \param deviceStream \c DeviceStream to use.
+     */
+    virtual cl::sycl::event launch(const KernelLaunchConfig& /*config*/,
+                                   const DeviceStream& /*deviceStream*/) = 0;
+};
+
+
+/*! \brief
+ * A function for setting up a single SYCL kernel argument.
+ * This is the tail of the compile-time recursive function below.
+ * It has to be seen by the compiler first.
+ *
+ * \param[in]     kernel          Kernel function handle
+ * \param[in]     argIndex        Index of the current argument
+ */
+void inline prepareGpuKernelArgument(ISyclKernelFunctor* /*kernel*/, size_t /*argIndex*/) {}
+
+/*! \brief
+ * Compile-time recursive function for setting up a single SYCL kernel argument.
+ * This function uses one kernel argument pointer \p argPtr to call
+ * \c ISyclKernelFunctor::setArg, and calls itself on the next argument, eventually
+ * calling the tail function above.
+ *
+ * \tparam        CurrentArg      Type of the current argument
+ * \tparam        RemainingArgs   Types of remaining arguments after the current one
+ * \param[in]     kernel          Kernel function handle
+ * \param[in]     argIndex        Index of the current argument
+ * \param[in]     argPtr          Pointer to the current argument
+ * \param[in]     otherArgsPtrs   Pack of pointers to arguments remaining to process after the current one
+ */
+template<typename CurrentArg, typename... RemainingArgs>
+void prepareGpuKernelArgument(ISyclKernelFunctor* kernel,
+                              size_t              argIndex,
+                              const CurrentArg*   argPtr,
+                              const RemainingArgs*... otherArgsPtrs)
+{
+    kernel->setArg(argIndex, const_cast<void*>(reinterpret_cast<const void*>(argPtr)));
+    prepareGpuKernelArgument(kernel, argIndex + 1, otherArgsPtrs...);
+}
+
+/*! \brief
+ * A wrapper function for setting up all the SYCL kernel arguments.
+ * Calls the recursive functions above.
+ *
+ * \tparam    Args            Types of all the kernel arguments
+ * \param[in] kernel          Kernel function handle
+ * \param[in] config          Kernel configuration for launching
+ * \param[in] argsPtrs        Pointers to all the kernel arguments
+ * \returns A dummy value to be used with launchGpuKernel() as the last argument.
+ */
+template<typename... Args>
+void* prepareGpuKernelArguments(void* kernel, const KernelLaunchConfig& /*config*/, const Args*... argsPtrs)
+{
+    auto* kernelFunctor = reinterpret_cast<ISyclKernelFunctor*>(kernel);
+    prepareGpuKernelArgument(kernelFunctor, 0, argsPtrs...);
+    return nullptr;
+}
+
+/*! \brief Launches the SYCL kernel and handles the errors.
+ *
+ * \param[in] kernel          Kernel function handle
+ * \param[in] config          Kernel configuration for launching
+ * \param[in] deviceStream    GPU stream to launch kernel in
+ * \param[in] timingEvent     Timing event, fetched from GpuRegionTimer. Unused.
+ * \param[in] kernelName      Human readable kernel description, for error handling only. Unused.
+ * \param[in] kernelArgs      Unused.
+ * \throws gmx::InternalError on kernel launch failure
+ */
+inline void launchGpuKernel(void*                     kernel,
+                            const KernelLaunchConfig& config,
+                            const DeviceStream&       deviceStream,
+                            CommandEvent* /*timingEvent*/,
+                            const char* /*kernelName*/,
+                            const void* /*kernelArgs*/)
+{
+    auto*           kernelFunctor = reinterpret_cast<ISyclKernelFunctor*>(kernel);
+    cl::sycl::event event         = kernelFunctor->launch(config, deviceStream);
+}
+
+/* To properly mark function as [[noreturn]], we must do it everywhere it is declared, which
+ * will pollute common headers.*/
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wmissing-noreturn"
+
+/*! \brief Pretend to check a SYCL stream for unfinished work (dummy implementation).
+ *
+ *  \returns  Not implemented in SYCL.
+ */
+static inline bool haveStreamTasksCompleted(const DeviceStream& /* deviceStream */)
+{
+    GMX_THROW(gmx::NotImplementedError("Not implemented on SYCL yet"));
+}
+
+#    pragma clang diagnostic pop
+
+#endif // !DOXYGEN
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/tests/devicetransfers.h b/src/include/gromacs/gpu_utils/tests/devicetransfers.h
new file mode 100644 (file)
index 0000000..1315741
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares helper functionality for device transfers for tests
+ * for GPU host allocator.
+ *
+ * Undefined symbols in Google Test, GROMACS use of -Wundef, and the
+ * implementation of FindCUDA.cmake and/or nvcc mean that no
+ * compilation unit should include a gtest header while being compiled
+ * by nvcc. None of -isystem, -Wno-undef, nor the pragma GCC
+ * diagnostic work.
+ *
+ * Thus, this header isolates CUDA-specific functionality to its own
+ * translation unit. The OpenCL and no-GPU implementations do not
+ * require this separation, but do so for consistency.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ */
+#ifndef GMX_GPU_UTILS_TESTS_DEVICETRANSFERS_H
+#define GMX_GPU_UTILS_TESTS_DEVICETRANSFERS_H
+
+struct DeviceInformation;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+
+/*! \brief Helper function for GPU test code to be platform agnostic.
+ *
+ * Transfers \c input to device 0, if present, and transfers it back
+ * into \c output. Both sizes must match. If no devices are present,
+ * do a simple host-side buffer copy instead.
+ *
+ * \throws InternalError  Upon any GPU API error condition. */
+void doDeviceTransfers(const DeviceInformation& deviceInfo, ArrayRef<const char> input, ArrayRef<char> output);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/gpu_utils/tests/typecasts_runner.h b/src/include/gromacs/gpu_utils/tests/typecasts_runner.h
new file mode 100644 (file)
index 0000000..f1c6051
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Header for runner for CUDA float3 type layout tests.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef GMX_GPU_UTILS_TESTS_TYPECASTS_RUNNER_H
+#define GMX_GPU_UTILS_TESTS_TYPECASTS_RUNNER_H
+
+#include "gmxpre.h"
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/math/vectypes.h"
+
+#include "testutils/test_device.h"
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+
+namespace test
+{
+
+/*! \brief Tests the compatibility of RVec and float3 using the conversion on host.
+ *
+ * \param[out] rVecOutput  Data in RVec format for the output.
+ * \param[in]  rVecInput   Data in RVec format with the input.
+ */
+void convertRVecToFloat3OnHost(ArrayRef<gmx::RVec> rVecOutput, ArrayRef<const gmx::RVec> rVecInput);
+
+/*! \brief Tests the compatibility of RVec and float3 using the conversion on device.
+ *
+ * \param[out] rVecOutput  Data in RVec format for the output.
+ * \param[in]  rVecInput   Data in RVec format with the input.
+ * \param[in]  testDevice  Test herdware environment to get DeviceContext and DeviceStream from.
+ */
+void convertRVecToFloat3OnDevice(ArrayRef<gmx::RVec>       rVecOutput,
+                                 ArrayRef<const gmx::RVec> rVecInput,
+                                 const TestDevice*         testDevice);
+
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_GPU_UTILS_TESTS_TYPECASTS_RUNNER_H
diff --git a/src/include/gromacs/gpu_utils/typecasts.cuh b/src/include/gromacs/gpu_utils/typecasts.cuh
new file mode 100644 (file)
index 0000000..d98c587
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declare functions to be used to cast CPU types to compatible GPU types.
+ *
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ *  \inlibraryapi
+ */
+#ifndef GMX_GPU_UTILS_TYPECASTS_CUH
+#define GMX_GPU_UTILS_TYPECASTS_CUH
+
+#include "gmxpre.h"
+
+#include "gromacs/math/vectypes.h"
+
+/*! \brief Cast RVec buffer to float3 buffer.
+ *
+ * \param[in] in The RVec buffer to cast.
+ *
+ * \returns Buffer, casted to float3*.
+ */
+static inline __host__ __device__ float3* asFloat3(gmx::RVec* in)
+{
+    static_assert(sizeof(in[0]) == sizeof(float3),
+                  "Size of the host-side data-type is different from the size of the device-side "
+                  "counterpart.");
+    return reinterpret_cast<float3*>(in);
+}
+
+/*! \brief Cast pointer RVec buffer to a pointer to float3 buffer.
+ *
+ * \param[in] in The Pointer to RVec buffer to cast.
+ *
+ * \returns Buffer pointer, casted to float3*.
+ */
+static inline __host__ __device__ float3** asFloat3Pointer(gmx::RVec** in)
+{
+    static_assert(sizeof((*in)[0]) == sizeof(float3),
+                  "Size of the host-side data-type is different from the size of the device-side "
+                  "counterpart.");
+    return reinterpret_cast<float3**>(in);
+}
+static inline __host__ __device__ const float3* const* asFloat3Pointer(const gmx::RVec* const* in)
+{
+    static_assert(sizeof((*in)[0]) == sizeof(float3),
+                  "Size of the host-side data-type is different from the size of the device-side "
+                  "counterpart.");
+    return reinterpret_cast<const float3* const*>(in);
+}
+
+#endif // GMX_GPU_UTILS_TYPECASTS_CUH
diff --git a/src/include/gromacs/gpu_utils/vectype_ops.clh b/src/include/gromacs/gpu_utils/vectype_ops.clh
new file mode 100644 (file)
index 0000000..b19cee1
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#include "device_utils.clh"
+
+#ifndef VECTYPE_OPS_CLH
+#    define VECTYPE_OPS_CLH
+
+/**** float3 ****/
+
+gmx_opencl_inline float norm_f3(float3 a)
+{
+    return sqrt(dot(a, a));
+}
+gmx_opencl_inline float norm_ref_f3(float3 a)
+{
+    return sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
+}
+gmx_opencl_inline float norm2(float3 a)
+{
+    return dot(a, a);
+}
+gmx_opencl_inline float norm2_ref(float3 a)
+{
+    return (a.x * a.x + a.y * a.y + a.z * a.z);
+}
+gmx_opencl_inline float dist3_f3(float3 a, float3 b)
+{
+    return distance(b, a);
+}
+gmx_opencl_inline float dist3_ref_f3(float3 a, float3 b)
+{
+    return norm_ref_f3(b - a);
+}
+
+/****************************************************************/
+
+/**** float4 ****/
+
+
+gmx_opencl_inline float norm_f4(float4 a)
+{
+    return sqrt(dot(a, a));
+}
+
+gmx_opencl_inline float norm_ref_f4(float4 a)
+{
+    return sqrt(a.x * a.x + a.y * a.y + a.z * a.z + a.w * a.w);
+}
+
+gmx_opencl_inline float dist3_f4(float4 a, float4 b)
+{
+    return norm_f4(b - a);
+}
+
+gmx_opencl_inline float dist3_ref_f4(float4 a, float4 b)
+{
+    return norm_ref_f4(b - a);
+}
+#endif /* VECTYPE_OPS_CLH */
diff --git a/src/include/gromacs/gpu_utils/vectype_ops.cuh b/src/include/gromacs/gpu_utils/vectype_ops.cuh
new file mode 100644 (file)
index 0000000..e358e0c
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2015,2016,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef VECTYPE_OPS_CUH
+#define VECTYPE_OPS_CUH
+
+/**** float3 ****/
+__forceinline__ __host__ __device__ float3 make_float3(float s)
+{
+    return make_float3(s, s, s);
+}
+__forceinline__ __host__ __device__ float3 make_float3(float4 a)
+{
+    return make_float3(a.x, a.y, a.z);
+}
+__forceinline__ __host__ __device__ float3 operator-(const float3& a)
+{
+    return make_float3(-a.x, -a.y, -a.z);
+}
+__forceinline__ __host__ __device__ float3 operator+(float3 a, float3 b)
+{
+    return make_float3(a.x + b.x, a.y + b.y, a.z + b.z);
+}
+__forceinline__ __host__ __device__ float3 operator-(float3 a, float3 b)
+{
+    return make_float3(a.x - b.x, a.y - b.y, a.z - b.z);
+}
+__forceinline__ __host__ __device__ float3 operator*(float3 a, float k)
+{
+    return make_float3(k * a.x, k * a.y, k * a.z);
+}
+__forceinline__ __host__ __device__ float3 operator*(float k, float3 a)
+{
+    return make_float3(k * a.x, k * a.y, k * a.z);
+}
+// NOLINTNEXTLINE(google-runtime-references)
+__forceinline__ __host__ __device__ void operator+=(float3& a, float3 b)
+{
+    a.x += b.x;
+    a.y += b.y;
+    a.z += b.z;
+}
+// NOLINTNEXTLINE(google-runtime-references)
+__forceinline__ __host__ __device__ void operator+=(float3& a, float4 b)
+{
+    a.x += b.x;
+    a.y += b.y;
+    a.z += b.z;
+}
+// NOLINTNEXTLINE(google-runtime-references)
+__forceinline__ __host__ __device__ void operator-=(float3& a, float3 b)
+{
+    a.x -= b.x;
+    a.y -= b.y;
+    a.z -= b.z;
+}
+__forceinline__ __host__ __device__ float norm(float3 a)
+{
+    return sqrt(a.x * a.x + a.y * a.y + a.z * a.z);
+}
+__forceinline__ __host__ __device__ float norm2(float3 a)
+{
+    return (a.x * a.x + a.y * a.y + a.z * a.z);
+}
+__forceinline__ __host__ __device__ float dist3(float3 a, float3 b)
+{
+    return norm(b - a);
+}
+__forceinline__ __host__ __device__ float3 operator*(float3 a, float3 b)
+{
+    return make_float3(a.x * b.x, a.y * b.y, a.z * b.z);
+}
+// NOLINTNEXTLINE(google-runtime-references)
+__forceinline__ __host__ __device__ void operator*=(float3& a, float3 b)
+{
+    a.x *= b.x;
+    a.y *= b.y;
+    a.z *= b.z;
+}
+// NOLINTNEXTLINE(google-runtime-references)
+__forceinline__ __host__ __device__ void operator*=(float3& a, float b)
+{
+    a.x *= b;
+    a.y *= b;
+    a.z *= b;
+}
+__forceinline__ __device__ void atomicAdd(float3* addr, float3 val)
+{
+    atomicAdd(&addr->x, val.x);
+    atomicAdd(&addr->y, val.y);
+    atomicAdd(&addr->z, val.z);
+}
+/****************************************************************/
+
+/**** float4 ****/
+__forceinline__ __host__ __device__ float4 make_float4(float s)
+{
+    return make_float4(s, s, s, s);
+}
+__forceinline__ __host__ __device__ float4 make_float4(float3 a)
+{
+    return make_float4(a.x, a.y, a.z, 0.0F);
+}
+__forceinline__ __host__ __device__ float4 operator+(float4 a, float4 b)
+{
+    return make_float4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
+}
+__forceinline__ __host__ __device__ float4 operator+(float4 a, float3 b)
+{
+    return make_float4(a.x + b.x, a.y + b.y, a.z + b.z, a.w);
+}
+__forceinline__ __host__ __device__ float4 operator-(float4 a, float4 b)
+{
+    return make_float4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
+}
+__forceinline__ __host__ __device__ float4 operator*(float4 a, float k)
+{
+    return make_float4(k * a.x, k * a.y, k * a.z, k * a.w);
+}
+__forceinline__ __host__ __device__ void operator+=(float4& a, float4 b)
+{
+    a.x += b.x;
+    a.y += b.y;
+    a.z += b.z;
+    a.w += b.w;
+}
+// NOLINTNEXTLINE(google-runtime-references)
+__forceinline__ __host__ __device__ void operator+=(float4& a, float3 b)
+{
+    a.x += b.x;
+    a.y += b.y;
+    a.z += b.z;
+}
+// NOLINTNEXTLINE(google-runtime-references)
+__forceinline__ __host__ __device__ void operator-=(float4& a, float3 b)
+{
+    a.x -= b.x;
+    a.y -= b.y;
+    a.z -= b.z;
+}
+
+__forceinline__ __host__ __device__ float norm(float4 a)
+{
+    return sqrt(a.x * a.x + a.y * a.y + a.z * a.z + a.w * a.w);
+}
+
+__forceinline__ __host__ __device__ float dist3(float4 a, float4 b)
+{
+    return norm(b - a);
+}
+
+/* \brief Compute the scalar product of two vectors.
+ *
+ * \param[in] a  First vector.
+ * \param[in] b  Second vector.
+ * \returns Scalar product.
+ */
+__forceinline__ __device__ float iprod(const float3 a, const float3 b)
+{
+    return a.x * b.x + a.y * b.y + a.z * b.z;
+}
+
+/* \brief Compute the vector product of two vectors.
+ *
+ * \param[in] a  First vector.
+ * \param[in] b  Second vector.
+ * \returns Vector product.
+ */
+__forceinline__ __device__ float3 cprod(const float3 a, const float3 b)
+{
+    float3 c;
+    c.x = a.y * b.z - a.z * b.y;
+    c.y = a.z * b.x - a.x * b.z;
+    c.z = a.x * b.y - a.y * b.x;
+    return c;
+}
+
+/* \brief Cosine of an angle between two vectors.
+ *
+ * Computes cosine using the following formula:
+ *
+ *                  ax*bx + ay*by + az*bz
+ * cos-vec (a,b) =  ---------------------
+ *                      ||a|| * ||b||
+ *
+ * This function also makes sure that the cosine does not leave the [-1, 1]
+ * interval, which can happen due to numerical errors.
+ *
+ * \param[in] a  First vector.
+ * \param[in] b  Second vector.
+ * \returns Cosine between a and b.
+ */
+__forceinline__ __device__ float cos_angle(const float3 a, const float3 b)
+{
+    float cosval;
+
+    float ipa  = norm2(a);
+    float ipb  = norm2(b);
+    float ip   = iprod(a, b);
+    float ipab = ipa * ipb;
+    if (ipab > 0.0F)
+    {
+        cosval = ip * rsqrt(ipab);
+    }
+    else
+    {
+        cosval = 1.0F;
+    }
+    if (cosval > 1.0F)
+    {
+        return 1.0F;
+    }
+    if (cosval < -1.0F)
+    {
+        return -1.0F;
+    }
+
+    return cosval;
+}
+
+/* \brief Compute the angle between two vectors.
+ *
+ * Uses atan( |axb| / a.b ) formula.
+ *
+ * \param[in] a  First vector.
+ * \param[in] b  Second vector.
+ * \returns Angle between vectors in radians.
+ */
+__forceinline__ __device__ float gmx_angle(const float3 a, const float3 b)
+{
+    float3 w = cprod(a, b);
+
+    float wlen = norm(w);
+    float s    = iprod(a, b);
+
+    return atan2f(wlen, s); // requires float
+}
+
+/* \brief Atomically add components of the vector.
+ *
+ * Executes atomicAdd one-by-one on all components of the float3 vector.
+ *
+ * \param[in] a  First vector.
+ * \param[in] b  Second vector.
+ */
+// NOLINTNEXTLINE(google-runtime-references)
+__forceinline__ __device__ void atomicAdd(float3& a, const float3 b)
+{
+    atomicAdd(&a.x, b.x);
+    atomicAdd(&a.y, b.y);
+    atomicAdd(&a.z, b.z);
+}
+
+#endif /* VECTYPE_OPS_CUH */
diff --git a/src/include/gromacs/hardware/architecture.h b/src/include/gromacs/hardware/architecture.h
new file mode 100644 (file)
index 0000000..4a82221
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares and defines architecture booleans to minimize preprocessed code
+ *
+ * \ingroup module_hardware
+ */
+#ifndef GMX_ARCHITECTURE_H
+#define GMX_ARCHITECTURE_H
+
+namespace gmx
+{
+
+//! Enum for GROMACS CPU hardware detection support
+enum class Architecture
+{
+    Unknown, //! Not one of the cases below
+    X86,     //! X86
+    Arm,     //! ARM
+    PowerPC  //! IBM PowerPC
+};
+
+//! Whether the compilation is targeting 32-bit x86.
+#if (defined __i386__ || defined __i386 || defined _X86_ || defined _M_IX86)
+#    define GMX_IS_X86_32 1
+#else
+#    define GMX_IS_X86_32 0
+#endif
+
+//! Whether the compilation is targeting 64-bit x86.
+#if (defined __x86_64__ || defined __x86_64 || defined __amd64__ || defined __amd64 \
+     || defined _M_X64 || defined _M_AMD64)
+#    define GMX_IS_X86_64 1
+#else
+#    define GMX_IS_X86_64 0
+#endif
+
+//! Constant that tells what the architecture is
+static constexpr Architecture c_architecture =
+#if GMX_IS_X86_32 || GMX_IS_X86_64
+        Architecture::X86;
+#elif defined __arm__ || defined __arm || defined _M_ARM || defined __aarch64__
+        Architecture::Arm;
+#elif defined __powerpc__ || defined __ppc__ || defined __PPC__
+        Architecture::PowerPC;
+#else
+        Architecture::Unknown;
+#endif
+
+} // namespace gmx
+
+#endif // GMX_ARCHITECTURE_H
diff --git a/src/include/gromacs/hardware/cpuinfo.h b/src/include/gromacs/hardware/cpuinfo.h
new file mode 100644 (file)
index 0000000..a6b9041
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::CpuInfo
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_hardware
+ */
+#ifndef GMX_HARDWARE_CPUINFO_H
+#define GMX_HARDWARE_CPUINFO_H
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+namespace gmx
+{
+
+/*! \libinternal \brief Detect CPU capabilities and basic logical processor info
+ *
+ *  This class provides a lot of information about x86 CPUs, and some very
+ *  limited information about other hardware. The logical processor information
+ *  is only available on x86, and is used as a fallback implementation in
+ *  the HardwareTopology class.
+ *  If you actually need information about the hardware topology, use the much
+ *  more general implementation in the HardwareTopology class instead, since
+ *  that will both be more portable and contain more information.
+ *
+ * \ingroup module_hardware
+ */
+class CpuInfo
+{
+
+public:
+    /*! \brief Amount of cpu information present (incremental) */
+    enum class SupportLevel
+    {
+        None,                //!< No cpu information whatsoever. Sorry.
+        Name,                //!< Only vendor and/or brand is set
+        Features,            //!< Some features are set
+        LogicalProcessorInfo //!< Everything includling logical processor information
+    };
+
+    /*! \brief Processor/system vendors */
+    enum class Vendor
+    {
+        Unknown, //!< Unidentified
+        Intel,   //!< GenuineIntel
+        Amd,     //!< AuthenticAMD
+        Fujitsu, //!< Only works on Linux (parsed from /proc/cpuinfo)
+        Ibm,     //!< Only works on Linux (parsed from /proc/cpuinfo)
+        Arm,     //!< Only works on Linux (parsed from /proc/cpuinfo)
+        Oracle,  //!< Cannot detect anything else yet (no /proc/cpuinfo available)
+        Hygon,   //!< HygonGenuine
+    };
+
+    /*! \brief List of CPU features
+     *
+     *  These values can be used as arguments to the feature() method
+     *  to check whether a specific feature was found on the CPU we are
+     *  running on.
+     */
+    enum class Feature
+    {
+        X86_Aes,             //!< x86 advanced encryption standard accel.
+        X86_Amd,             //!< This is an AMD x86 processor
+        X86_Apic,            //!< APIC support
+        X86_Avx,             //!< Advanced vector extensions
+        X86_Avx2,            //!< AVX2 including gather support (not used yet)
+        X86_Avx512F,         //!< Foundation AVX-512 instructions
+        X86_Avx512PF,        //!< Extended gather/scatter for AVX-512
+        X86_Avx512ER,        //!< AVX-512 exponential and reciprocal extensions
+        X86_Avx512CD,        //!< Memory conflict-detection for AVX-512
+        X86_Avx512BW,        //!< AVX-512 byte and word instructions
+        X86_Avx512VL,        //!< AVX-512 vector length extensions
+        X86_Avx512BF16,      //!< AVX-512 BFloat16 instructions
+        X86_Avx512secondFMA, //!< AVX-512 second FMA unit
+        X86_Clfsh,           //!< Supports CLFLUSH instruction
+        X86_Cmov,            //!< Conditional move insn support
+        X86_Cx8,             //!< Supports CMPXCHG8B (8-byte compare-exchange)
+        X86_Cx16,            //!< Supports CMPXCHG16B (16-byte compare-exchg)
+        X86_F16C,            //!< Supports 16-bit FP conversion instructions
+        X86_Fma,             //!< Fused-multiply add support (mainly for AVX)
+        X86_Fma4,            //!< 4-operand FMA, only on AMD for now
+        X86_Hle,             //!< Hardware lock elision
+        X86_Htt,   //!< Hyper-Threading enabled (NOTE: might not match the CPUID HTT support flag)
+        X86_Intel, //!< This is an Intel x86 processor
+        X86_Lahf,  //!< LAHF/SAHF support in 64 bits
+        X86_MisalignSse, //!< Support for misaligned SSE data instructions
+        X86_Mmx,         //!< MMX registers and instructions
+        X86_Msr,         //!< Supports Intel model-specific-registers
+        X86_NonstopTsc,  //!< Invariant TSC (constant rate in ACPI states)
+        X86_Pcid,        //!< Process context identifier support
+        X86_Pclmuldq,    //!< Carry-less 64-bit multiplication supported
+        X86_Pdcm,        //!< Perfmon and Debug Capability
+        X86_PDPE1GB,     //!< Support for 1GB pages
+        X86_Popcnt,      //!< Supports the POPCNT (population count) insn
+        X86_Pse,         //!< Supports 4MB-pages (page size extension)
+        X86_Rdrnd,       //!< RDRAND high-quality hardware random numbers
+        X86_Rdtscp,      //!< Serializing rdtscp instruction available
+        X86_Rtm,         //!< Restricted transactional memory
+        X86_Sha,         //!< Intel SHA extensions
+        X86_Sse2,        //!< SSE 2
+        X86_Sse3,        //!< SSE 3
+        X86_Sse4A,       //!< SSE 4A
+        X86_Sse4_1,      //!< SSE 4.1
+        X86_Sse4_2,      //!< SSE 4.2
+        X86_Ssse3,       //!< Supplemental SSE3
+        X86_Tdt,         //!< TSC deadline timer
+        X86_X2Apic,      //!< Extended xAPIC Support
+        X86_Xop,         //!< AMD extended instructions, only AMD for now
+        Arm_Neon,        //!< 32-bit ARM NEON
+        Arm_NeonAsimd,   //!< 64-bit ARM AArch64 Advanced SIMD
+        Arm_Sve,         //!< ARM Scalable Vector Extensions
+        Ibm_Qpx,         //!< IBM QPX SIMD (BlueGene/Q)
+        Ibm_Vmx,         //!< IBM VMX SIMD (Altivec on Power6 and later)
+        Ibm_Vsx,         //!< IBM VSX SIMD (Power7 and later)
+        Fujitsu_HpcAce,  //!< Fujitsu Sparc64 HPC-ACE
+        X86_Hygon        //!< This is a Hygon x86 processor
+    };
+
+    /*! \libinternal \brief Entry with basic information for a single logical processor */
+    struct LogicalProcessor
+    {
+        int socketRankInMachine; //!< Relative rank of the current socket in the system
+        int coreRankInSocket;    //!< Relative rank of the current core in its socket
+        int hwThreadRankInCore;  //!< Relative rank of logical processor in its core
+    };
+
+    /*! \brief Perform detection and construct a CpuInfo class from the results.
+     *
+     *  \note The detection should generally be performed again in different
+     *        contexts.  This might seem like overkill, but there
+     *        are systems (e.g. Arm) where processors can go completely offline
+     *        during deep sleep, so at least in theory it is good to have a
+     *        possibility of forcing re-detection if necessary.
+     */
+    static CpuInfo detect();
+
+    /*! \brief Check what cpu information is available
+     *
+     *  The amount of cpu information that can be detected depends on the
+     *  OS, compiler, and CPU, and on non-x86 platforms it can be fragile.
+     *  Before basing decisions on the output or warning the user about
+     *  optimizations, you want to check whether it was possible to detect
+     *  the information you need.
+     */
+    SupportLevel supportLevel() const { return supportLevel_; }
+
+    /*! \brief Enumerated value for vendor */
+    Vendor vendor() const { return vendor_; }
+
+    /*! \brief String description of vendor:
+     *
+     *  \throws std::out_of_range if the vendor is not present in the internal
+     *          map of vendor names. This can only happen if we extend the enum
+     *          type but forget to add the string with the vendor name.
+     */
+    const std::string& vendorString() const;
+
+    /*! \brief String description of processor */
+    const std::string& brandString() const { return brandString_; }
+
+    /*! \brief Major version/generation of the processor */
+    int family() const { return family_; }
+
+    /*! \brief Middle version of the processor */
+    int model() const { return model_; }
+
+    /*! \brief Minor version of the processor */
+    int stepping() const { return stepping_; }
+
+    /*! \brief Check for availability of specific feature
+     *
+     *  \param f  feature to query support for
+     *
+     *  \return True if the feature is available, otherwise false.
+     */
+    bool feature(Feature f) const
+    {
+        // If the entry is present in the set it is supported
+        return (features_.count(f) != 0);
+    }
+
+    /*! \brief String description of a specific feature
+     *
+     *  \throws std::out_of_range if the feature is not present in the internal
+     *          map of feature names. This can only happen if we extend the enum
+     *          type but forget to add the string with the feature name.
+     */
+    static const std::string& featureString(Feature f);
+
+    /*! \brief Set of all supported features on this processor
+     *
+     *  This is only intended for logfiles, debugging or similar output when we
+     *  need a full list of all the features available on the CPU.
+     */
+    const std::set<Feature>& featureSet() const { return features_; }
+
+    /*! \brief Reference to processing unit topology
+     *
+     *  Only a few systems (x86) provide logical processor information in cpuinfo.
+     *  This method returns a reference to a vector, whose length will either be
+     *  zero (if topology information is not available) or the number of enabled
+     *  processing units, as defined by the operating system. In the latter
+     *  case, each entry will contain information about the relative rank in the
+     *  core and socket of this hardware thread.
+     *
+     *  This is only meant to be use as a fallback implementation for our
+     *  HardwareTopology class; any user code that needs access to hardware
+     *  topology information should use that class instead.
+     *
+     *  \note For clarity, it is likely better to use the supportLevel()
+     *        method to check if this information is available rather than
+     *        relying on the length of the vector.
+     */
+    const std::vector<LogicalProcessor>& logicalProcessors() const { return logicalProcessors_; }
+
+private:
+    CpuInfo();
+
+    SupportLevel                  supportLevel_;      //!< Available cpuinfo information
+    Vendor                        vendor_;            //!<  Value of vendor for current cpu
+    std::string                   brandString_;       //!<  Text description of cpu
+    int                           family_;            //!<  Major version of current cpu
+    int                           model_;             //!<  Middle version of current cpu
+    int                           stepping_;          //!<  Minor version of current cpu
+    std::set<Feature>             features_;          //!< Set of features supported on this cpu
+    std::vector<LogicalProcessor> logicalProcessors_; //!< Simple logical processor topology
+};                                                    // class CpuInfo
+
+/*! \brief Return true if the CPU is an Intel x86 Nehalem
+ *
+ * \param cpuInfo  Object with cpu information
+ *
+ * \returns  True if running on Nehalem CPU
+ */
+bool cpuIsX86Nehalem(const CpuInfo& cpuInfo);
+
+/*! \brief Return true if the CPU is a first generation AMD Zen (produced by AMD or Hygon)
+ *
+ * \param cpuInfo  Object with cpu information
+ *
+ * \returns  True if running on a first generation AMD Zen
+ */
+bool cpuIsAmdZen1(const CpuInfo& cpuInfo);
+
+} // namespace gmx
+
+#endif // GMX_HARDWARE_CPUINFO_H
diff --git a/src/include/gromacs/hardware/detecthardware.h b/src/include/gromacs/hardware/detecthardware.h
new file mode 100644 (file)
index 0000000..84286c9
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_HARDWARE_DETECTHARDWARE_H
+#define GMX_HARDWARE_DETECTHARDWARE_H
+
+#include <memory>
+
+struct gmx_hw_info_t;
+
+namespace gmx
+{
+class HardwareTopology;
+class MDLogger;
+class PhysicalNodeCommunicator;
+
+/*! \brief Run detection and make correct and consistent
+ * hardware information available on all ranks.
+ *
+ * May do communication on MPI_COMM_WORLD when compiled with real MPI.
+ *
+ * This routine is designed to be called once on each process.  In a
+ * thread-MPI configuration, it may only be called before the threads
+ * are spawned. With real MPI, communication is needed to coordinate
+ * the results. In all cases, any thread within a process may use the
+ * returned handle.
+ *
+ * \todo Replace the use of MPI_COMM_WORLD e.g. by using a libraryCommWorld
+ * argument. See https://gitlab.com/gromacs/gromacs/-/issues/3650
+ */
+std::unique_ptr<gmx_hw_info_t> gmx_detect_hardware(const PhysicalNodeCommunicator& physicalNodeComm);
+
+/*! \brief Sanity check hardware topology and print some notes to log
+ *
+ *  \param mdlog            Logger.
+ *  \param hardwareTopology Reference to hardwareTopology object.
+ */
+void hardwareTopologyDoubleCheckDetection(const gmx::MDLogger&         mdlog,
+                                          const gmx::HardwareTopology& hardwareTopology);
+
+/*! \brief Issue warnings to mdlog that were decided during detection
+ *
+ * \param[in] mdlog                Logger
+ * \param[in] hardwareInformation  The hardwareInformation */
+void logHardwareDetectionWarnings(const gmx::MDLogger& mdlog, const gmx_hw_info_t& hardwareInformation);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/hardware/device_information.h b/src/include/gromacs/hardware/device_information.h
new file mode 100644 (file)
index 0000000..a03e915
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declares the GPU information structure and its helpers
+ *
+ *  \author Anca Hamuraru <anca@streamcomputing.eu>
+ *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
+ *  \author Teemu Virolainen <teemu@streamcomputing.eu>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ */
+#ifndef GMX_HARDWARE_DEVICE_INFORMATION_H
+#define GMX_HARDWARE_DEVICE_INFORMATION_H
+
+#include "config.h"
+
+#if GMX_GPU_CUDA
+#    include <cuda_runtime.h>
+#endif
+
+#if GMX_GPU_OPENCL
+#    include "gromacs/gpu_utils/gmxopencl.h"
+#endif
+
+#if GMX_GPU_SYCL
+#    include "gromacs/gpu_utils/gmxsycl.h"
+#endif
+
+#include "gromacs/utility/enumerationhelpers.h"
+
+//! Constant used to help minimize preprocessed code
+static constexpr bool c_binarySupportsGpus = (GMX_GPU != 0);
+//! Whether \ref DeviceInformation can be serialized for sending via MPI.
+static constexpr bool c_canSerializeDeviceInformation =
+        (!GMX_GPU_OPENCL && !GMX_GPU_SYCL); /*NOLINT(misc-redundant-expression)*/
+
+//! Possible results of the GPU detection/check.
+enum class DeviceStatus : int
+{
+    //! The device is compatible
+    Compatible = 0,
+    //! Device does not exist
+    Nonexistent = 1,
+    //! Device is not compatible
+    Incompatible = 2,
+    //! OpenCL device has incompatible cluster size for non-bonded kernels.
+    IncompatibleClusterSize = 3,
+    //! There are known issues with OpenCL on NVIDIA Volta and newer.
+    IncompatibleNvidiaVolta = 4,
+    /*! \brief The device originates from non-recommended SYCL backend.
+     * The device might work by itself, but to simplify device allocation, it is marked as incompatible.
+     * */
+    NotPreferredBackend = 5,
+    /*! \brief An error occurred during the functionality checks.
+     * That indicates malfunctioning of the device, driver, or incompatible driver/runtime.
+     */
+    NonFunctional = 6,
+    /*! \brief CUDA devices are busy or unavailable.
+     * typically due to use of \p cudaComputeModeExclusive, \p cudaComputeModeProhibited modes.
+     */
+    Unavailable = 7,
+    //! Enumeration size
+    Count = 8
+};
+
+/*! \brief Names of the GPU detection/check results
+ *
+ * Check-source wants to warn about the use of a symbol name that would
+ * require an inclusion of config.h. However the use is in a comment, so that
+ * is a false warning. So C-style string concatenation is used to fool the
+ * naive parser in check-source. That needs a clang-format suppression
+ * in order to look reasonable. Also clang-tidy wants to suggest that a comma is
+ * missing, so that is suppressed.
+ */
+static const gmx::EnumerationArray<DeviceStatus, const char*> c_deviceStateString = {
+    "compatible",
+    "nonexistent",
+    "incompatible",
+    // clang-format off
+    // NOLINTNEXTLINE(bugprone-suspicious-missing-comma)
+    "incompatible (please recompile with correct GMX" "_GPU_NB_CLUSTER_SIZE of 4)",
+    // clang-format on
+    "incompatible (please use CUDA build for NVIDIA Volta GPUs or newer)",
+    "not recommended (please use SYCL_DEVICE_FILTER to limit visibility to a single backend)",
+    "non-functional",
+    "unavailable"
+};
+
+//! Device vendors
+enum class DeviceVendor : int
+{
+    //! No data
+    Unknown = 0,
+    //! NVIDIA
+    Nvidia = 1,
+    //! Advanced Micro Devices
+    Amd = 2,
+    //! Intel
+    Intel = 3,
+    //! Enumeration size
+    Count = 4
+};
+
+
+/*! \libinternal \brief Platform-dependent device information.
+ *
+ * The device information is queried and set at detection and contains
+ * both information about the device/hardware returned by the runtime as well
+ * as additional data like support status.
+ */
+struct DeviceInformation
+{
+    //! Device status.
+    DeviceStatus status;
+    //! ID of the device.
+    int id;
+    //! Device vendor.
+    DeviceVendor deviceVendor;
+#if GMX_GPU_CUDA
+    //! CUDA device properties.
+    cudaDeviceProp prop;
+#elif GMX_GPU_OPENCL
+    cl_platform_id oclPlatformId;       //!< OpenCL Platform ID.
+    cl_device_id   oclDeviceId;         //!< OpenCL Device ID.
+    char           device_name[256];    //!< Device name.
+    char           device_version[256]; //!< Device version.
+    char           vendorName[256];     //!< Device vendor name.
+    int            compute_units;       //!< Number of compute units.
+    int            adress_bits;         //!< Number of address bits the device is capable of.
+    size_t         maxWorkItemSizes[3]; //!< Workgroup size limits (CL_DEVICE_MAX_WORK_ITEM_SIZES).
+    size_t         maxWorkGroupSize;    //!< Workgroup total size limit (CL_DEVICE_MAX_WORK_GROUP_SIZE).
+#elif GMX_GPU_SYCL
+    cl::sycl::device syclDevice;
+#endif
+};
+
+#endif // GMX_HARDWARE_DEVICE_INFORMATION_H
diff --git a/src/include/gromacs/hardware/device_management.h b/src/include/gromacs/hardware/device_management.h
new file mode 100644 (file)
index 0000000..138b6f7
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016, by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declares functions to manage GPU resources.
+ *
+ *  This has several implementations: one for each supported GPU platform,
+ *  and a stub implementation if the build does not support GPUs.
+ *
+ *  \author Anca Hamuraru <anca@streamcomputing.eu>
+ *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
+ *  \author Teemu Virolainen <teemu@streamcomputing.eu>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_hardware
+ */
+#ifndef GMX_HARDWARE_DEVICE_MANAGEMENT_H
+#define GMX_HARDWARE_DEVICE_MANAGEMENT_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/iserializer.h"
+
+struct DeviceInformation;
+enum class DeviceVendor : int;
+
+/*! \brief Return whether GPUs can be detected.
+ *
+ * Returns true when this is a build of GROMACS configured to support
+ * GPU usage, GPU detection is not disabled by \c GMX_DISABLE_GPU_DETECTION
+ * environment variable and a valid device driver, ICD, and/or runtime was
+ * detected. Does not throw.
+ *
+ * \param[out] errorMessage  When returning false on a build configured with
+ *                           GPU support and non-nullptr was passed,
+ *                           the string contains a descriptive message about
+ *                           why GPUs cannot be detected.
+ */
+bool canPerformDeviceDetection(std::string* errorMessage);
+
+/*! \brief Return whether GPU detection is enabled
+ *
+ * Returns true when this is a build of GROMACS configured to support
+ * GPU usage and GPU detection is not disabled by \c GMX_DISABLE_GPU_DETECTION
+ * environment variable.
+ *
+ * Does not throw.
+ */
+bool isDeviceDetectionEnabled();
+
+/*! \brief Return whether GPU detection is functioning correctly
+ *
+ * Returns true when this is a build of GROMACS configured to support
+ * GPU usage, and a valid device driver, ICD, and/or runtime was detected.
+ *
+ * This function is not intended to be called from build
+ * configurations that do not support GPUs, and there will be no
+ * descriptive message in that case.
+ *
+ * \param[out] errorMessage  When returning false on a build configured with
+ *                           GPU support and non-nullptr was passed,
+ *                           the string contains a descriptive message about
+ *                           why GPUs cannot be detected.
+ *
+ * Does not throw.
+ */
+bool isDeviceDetectionFunctional(std::string* errorMessage);
+
+/*! \brief Returns an DeviceVendor value corresponding to the input OpenCL vendor name.
+ *
+ *  \returns               DeviceVendor value for the input vendor name
+ */
+DeviceVendor getDeviceVendor(const char* vendorName);
+
+/*! \brief Find all GPUs in the system.
+ *
+ *  Will detect every GPU supported by the device driver in use.
+ *  Must only be called if \c canPerformDeviceDetection() has returned true.
+ *  This routine also checks for the compatibility of each device and fill the
+ *  deviceInfo array with the required information on each device: ID, device
+ *  properties, status.
+ *
+ *  Note that this function leaves the GPU runtime API error state clean;
+ *  this is implemented ATM in the CUDA flavor. This invalidates any existing
+ *  CUDA streams, allocated memory on GPU, etc.
+ *
+ *  \todo:  Check if errors do propagate in OpenCL as they do in CUDA and
+ *          whether there is a mechanism to "clear" them.
+ *
+ * \return  Standard vector with the list of devices found
+ *
+ *  \throws InternalError if a GPU API returns an unexpected failure (because
+ *          the call to canDetectGpus() should always prevent this occuring)
+ */
+std::vector<std::unique_ptr<DeviceInformation>> findDevices();
+
+/*! \brief Return a container of device-information handles that are compatible.
+ *
+ * This function filters the result of the detection for compatible
+ * GPUs, based on the previously run compatibility tests.
+ *
+ * \param[in] deviceInfoList An information on available devices.
+ *
+ * \return  Vector of DeviceInformations on GPUs recorded as compatible
+ */
+std::vector<std::reference_wrapper<DeviceInformation>>
+getCompatibleDevices(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList);
+
+/*! \brief Return a container of the IDs of the compatible GPU ids.
+ *
+ * This function filters the result of the detection for compatible
+ * GPUs, based on the previously run compatibility tests.
+ *
+ * \param[in] deviceInfoList An information on available devices.
+ *
+ * \return  Vector of compatible GPU ids.
+ */
+std::vector<int> getCompatibleDeviceIds(gmx::ArrayRef<const std::unique_ptr<DeviceInformation>> deviceInfoList);
+
+/*! \brief Return whether \p deviceId is found in \p deviceInfoList and is compatible
+ *
+ * This function filters the result of the detection for compatible
+ * GPUs, based on the previously run compatibility tests.
+ *
+ * \param[in] deviceInfoList An information on available devices.
+ * \param[in] deviceId       The device ID to find in the list.
+ *
+ * \throws RangeError If \p deviceId does not match the id of any device in \c deviceInfoList
+ *
+ * \return  Whether \c deviceId is compatible.
+ */
+bool deviceIdIsCompatible(gmx::ArrayRef<const std::unique_ptr<DeviceInformation>> deviceInfoList,
+                          int                                                     deviceId);
+
+/*! \brief Set the active GPU.
+ *
+ * This sets the device for which the device information is passed active. Essential in CUDA, where
+ * the device buffers and kernel launches are not connected to the device context. In OpenCL, checks
+ * the device vendor and makes vendor-specific performance adjustments.
+ *
+ * \param[in] deviceInfo Information on the device to be set.
+ *
+ * Issues a fatal error for any critical errors that occur during
+ * initialization.
+ */
+void setActiveDevice(const DeviceInformation& deviceInfo);
+
+/*! \brief Releases the GPU device used by the active context at the time of calling (CUDA only).
+ *
+ * If \c deviceInfo is nullptr, then it is understood that no device
+ * was selected so no context is active to be freed. Otherwise, the
+ * context is explicitly destroyed and therefore all data uploaded to
+ * the GPU is lost. This must only be called when none of this data is
+ * required anymore, because subsequent attempts to free memory
+ * associated with the context will otherwise fail.
+ *
+ * Calls \c gmx_warning upon errors.
+ *
+ * \todo This should go through all the devices, not only the one currently active.
+ *       Reseting only one device will not work, e.g. in CUDA tests.
+ *
+ * \param[in] deviceInfo Information on the device to be released.
+ */
+void releaseDevice(DeviceInformation* deviceInfo);
+
+/*! \brief Formats and returns a device information string for a given GPU.
+ *
+ * Given an index *directly* into the array of available GPUs, returns
+ * a formatted info string for the respective GPU which includes ID, name,
+ * compute capability, and detection status.
+ *
+ * \param[in] deviceInfo  An information on device that is to be set.
+ *
+ * \returns A string describing the device.
+ */
+std::string getDeviceInformationString(const DeviceInformation& deviceInfo);
+
+/*! \brief Return a string describing how compatible the GPU with given \c deviceId is.
+ *
+ * \param[in] deviceInfoList An information on available devices.
+ * \param[in] deviceId       An index of the device to check
+ * \returns                  A string describing the compatibility status, useful for error messages.
+ */
+std::string getDeviceCompatibilityDescription(gmx::ArrayRef<const std::unique_ptr<DeviceInformation>> deviceInfoList,
+                                              int deviceId);
+
+/*! \brief Serialization of information on devices for MPI broadcasting.
+ *
+ * \param[in] deviceInfoList  The vector with device informations to serialize.
+ * \param[in] serializer      Serializing object.
+ */
+void serializeDeviceInformations(const std::vector<std::unique_ptr<DeviceInformation>>& deviceInfoList,
+                                 gmx::ISerializer*                                      serializer);
+
+/*! \brief Deserialization of information on devices after MPI broadcasting.
+ *
+ * \param[in] serializer Serializing object.
+ *
+ * \return deviceInfoList   Deserialized vector with device informations.
+ */
+std::vector<std::unique_ptr<DeviceInformation>> deserializeDeviceInformations(gmx::ISerializer* serializer);
+
+#endif // GMX_HARDWARE_DEVICE_MANAGEMENT_H
diff --git a/src/include/gromacs/hardware/hardwaretopology.h b/src/include/gromacs/hardware/hardwaretopology.h
new file mode 100644 (file)
index 0000000..8f8f229
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::HardwareTopology
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_hardware
+ */
+#ifndef GMX_HARDWARE_HARDWARETOPOLOGY_H
+#define GMX_HARDWARE_HARDWARETOPOLOGY_H
+
+#include <cstdint>
+
+#include <vector>
+
+namespace gmx
+{
+
+/*! \libinternal \brief Information about sockets, cores, threads, numa, caches
+ *
+ * This class is the main GROMACS interface to provide information about the
+ * hardware of the system we are running on. Internally, it uses either
+ * hwloc for full or almost-full information, or a fallback implementation
+ * that relies on CpuInfo on x86.
+ *
+ * You should always use this class (rather than CpuInfo directly) to query
+ * the hardware layout in user code. Note that you cannot rely on any
+ * information being present, but you must check with the supportLevel()
+ * method before trying to access any information.
+ */
+class HardwareTopology
+{
+public:
+    /*! \brief Amount of topology information present (incremental) */
+    enum class SupportLevel
+    {
+        None,                  //!< No hardware information whatsoever. Sorry.
+        LogicalProcessorCount, //!< Only machine().logicalProcessorCount is valid
+        Basic,                 //!< Socket, core and hardware thread info
+        Full,                  //!< Cache, memory and numa node info
+        FullWithDevices        //!< Information about devices on the PCI bus
+    };
+
+    /*! \libinternal \brief Information about a single cache level */
+    struct Cache
+    {
+        int         level;         //!< Level relative to core (starts at 1)
+        std::size_t size;          //!< size in bytes, 0 if unknown
+        int         linesize;      //!< size of each cache line in bytes, 0 if unknown
+        int         associativity; //!< associativity, -1 means fully associative
+        int         shared;        //!< Number of logical processors sharing this cache
+    };
+
+    /*! \libinternal \brief Information about a single hardware thread in a core
+     *
+     * The id of the thread typically increases continuously as you walk
+     * through sockets and cores in order of their ids. In general, this can
+     * be different from the logical processor id provided by the operating
+     * system. To achieve better load balancing when using SMT, Linux
+     * typically assigns logical processors in a round-robin fashion
+     * over all cores.
+     */
+    struct HWThread
+    {
+        int id;                 //!< Absolute id of this thread in hardware topology
+        int logicalProcessorId; //!< Id of the operating system logical processor
+    };
+
+    /*! \libinternal \brief Information about a single core in a socket */
+    struct Core
+    {
+        int                   id;         //!< Absolute id of this core in hardware topology
+        int                   numaNodeId; //!< id of the numa node of this core
+        std::vector<HWThread> hwThreads;  //!< All the hardware threads in this core
+    };
+
+    /*! \libinternal \brief Information about a single socket in the system */
+    struct Socket
+    {
+        int               id;    //!< Absolute id of this socket in hardware topology
+        std::vector<Core> cores; //!< All the cores in this socket
+    };
+
+    /*! \libinternal \brief Information about each numa node in system */
+    struct NumaNode
+    {
+        int              id;                 //!< Absolute id of numa node in hardware topology
+        std::size_t      memory;             //!< Total detected memory in bytes
+        std::vector<int> logicalProcessorId; //!< Vector of all the logical processors in this node
+    };
+
+    /*! \libinternal \brief Information about a single numa node */
+    struct Numa
+    {
+        std::vector<NumaNode>           nodes;       //!< Information about each numa node
+        float                           baseLatency; //!< Scale factor for relative latencies
+        std::vector<std::vector<float>> relativeLatency; //!< 2D matrix of relative latencies between nodes
+        float                           maxRelativeLatency; //!< Largest relative latency
+    };
+
+    /*! \libinternal \brief Information about a single PCI device.
+     *
+     *  \note On many systems the PCI bus is not directly connected to any numa node.
+     *        For these systems the numaNodeId will be -1, so you cannot rely on this
+     *        number reflecting a specific numa node.
+     */
+    struct Device
+    {
+        std::uint16_t vendorId;   //!< Vendor identification
+        std::uint16_t deviceId;   //!< Vendor-specific device identification
+        std::uint16_t classId;    //!< class (high 8 bits) and subclass (low 8 bits)
+        std::uint16_t domain;     //!< Domain, usually 0 for PCI bus
+        std::uint8_t  bus;        //!< Bus number in domain
+        std::uint8_t  dev;        //!< Device on bus
+        std::uint8_t  func;       //!< Function id for multi-function devices
+        int           numaNodeId; //!< Numa node, -1 if the bus is not located inside a node
+    };
+
+    /*! \libinternal \brief Information about socket, core and hwthread for a logical processor */
+    struct LogicalProcessor
+    {
+        int socketRankInMachine; //!< Index of socket in machine
+        int coreRankInSocket;    //!< Index of core in socket
+        int hwThreadRankInCore;  //!< Index of hardware thread in core
+        int numaNodeId;          //!< Index of numa node
+    };
+
+    /*! \libinternal \brief Hardware topology information about the entire machine
+     *
+     * The machine structure is a tree with top-down information about all
+     * sockets, cores, and hardware threads in the system. For example, an
+     * operating system logical processor index can be found as
+     * machine.socket[0].core[1].hwthread[2].logicalProcessorId.
+     * In some cases you might need the opposite lookup, i.e. the physical
+     * hardware data for a specific logical processor. This is present in the
+     * logicalProcessor vector for convenience.
+     *
+     * \note The logicalProcessor vector will only have non-zero length if the
+     *       support level is SupportLevel::Basic or higher. You cannot use the
+     *       size of this vector to query the number of logical processors on
+     *       lower support levels.
+     */
+    struct Machine
+    {
+        Machine();
+
+        int logicalProcessorCount;                       //!< Number of logical processors in system
+        std::vector<LogicalProcessor> logicalProcessors; //!< Map logical processors to socket/core
+        std::vector<Socket>           sockets;           //!< All the sockets in the system
+        std::vector<Cache>            caches;            //!< Caches in increasing level order
+        Numa                          numa;              //!< Structure with all numa information
+        std::vector<Device>           devices;           //!< Devices on PCI bus
+    };
+
+    /*! \brief Detects the hardware topology. */
+    static HardwareTopology detect();
+
+    /*! \brief Creates a topology with given number of logical cores.
+     *
+     * The support level will be either None or LogicalProcessorCount.
+     *
+     * Intended for testing of code that uses the hardware topology.
+     */
+    explicit HardwareTopology(int logicalProcessorCount);
+
+    /*! \brief Check what topology information that is available and valid
+     *
+     *  The amount of hardware topology information that can be detected depends
+     *  on both the hardware and whether GROMACS was linked with the external
+     *  hwloc library. You cannot assume that any information is present,
+     *  although we can almost always provide the number of logical processors.
+     *  On x86 we can usually get basic information about how sockets, cores
+     *  and hardware threads are ordered even without hwloc.
+     *  With the hwloc library we can usually also get information about cache,
+     *  memory and concepts such as core groups and ccNUMA nodes.
+     *  Finally, if hwloc was built with support for libpci we can also
+     *  detect how the PCI devices are connected.
+     */
+    SupportLevel supportLevel() const { return supportLevel_; }
+
+    /*! \brief Return true if we actually detected hardware.
+     *
+     *  \return This method will normally return true, when we actually ran
+     *          the hardware detection as part of this process to construct
+     *          the object. It will be false when the object was constructed
+     *          by reading a cached XML file, or possibly generated from
+     *          synthetic data.
+     */
+    bool isThisSystem() const { return isThisSystem_; }
+
+    /*! \brief Return the machine topology tree
+     *
+     *  You can always call this routine, but be aware that some or all contents
+     *  will not be valid unless supportLevel() returns a sufficient level.
+     *
+     *  - With SupportLevel::LogicalProcessorCount, only the field
+     *    machine.logicalProcessorCount is valid.
+     *  - With SupportLevel::Basic, you can access the vectors of sockets,
+     *    cores, and hardware threads, and query what logical processorId
+     *    each hardware thread corresponds to.
+     *  - SupportLevel::Full adds cache, memory and ccNUMA information.
+     *  - SupportLevel::FullWithDevices also adds the PCI express bus.
+     *
+     *  While data that is not valid has been initialized to special values,
+     *  you should not rely on those but query the supportLevel() method before
+     *  accessing it.
+     */
+    const Machine& machine() const { return machine_; }
+
+    /*! \brief Returns the number of cores.
+     *
+     * You can always call this routine, but if sufficient support is not
+     * available, it may return the logical processor count or zero instead
+     * of the physical core count.
+     */
+    int numberOfCores() const;
+
+private:
+    HardwareTopology();
+
+    SupportLevel supportLevel_; //!< Available topology information
+    Machine      machine_;      //!< The machine map
+    bool         isThisSystem_; //!< Machine map is real (vs. cached/synthetic)
+};
+
+} // namespace gmx
+
+#endif // GMX_HARDWARE_HARDWARETOPOLOGY_H
diff --git a/src/include/gromacs/hardware/hw_info.h b/src/include/gromacs/hardware/hw_info.h
new file mode 100644 (file)
index 0000000..044c8dd
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_HARDWARE_HWINFO_H
+#define GMX_HARDWARE_HWINFO_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/hardware/device_management.h"
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+class CpuInfo;
+class HardwareTopology;
+} // namespace gmx
+struct DeviceInformation;
+
+/* Hardware information structure with CPU and GPU information.
+ * It is initialized by gmx_detect_hardware().
+ * NOTE: this structure may only contain structures that are
+ *       valid over the whole process (i.e. must be able to
+ *       be shared among all threads) */
+struct gmx_hw_info_t
+{
+    gmx_hw_info_t(std::unique_ptr<gmx::CpuInfo>          cpuInfo,
+                  std::unique_ptr<gmx::HardwareTopology> hardwareTopology);
+    ~gmx_hw_info_t();
+
+    /* Data for our local physical node */
+
+    /*! \brief Number of hardware threads available.
+     *
+     * This number is based on the number of CPUs reported as
+     * available by the OS at the time of detection. */
+    int nthreads_hw_avail;
+
+
+    std::unique_ptr<gmx::CpuInfo>          cpuInfo; /* Information about CPU capabilities */
+    std::unique_ptr<gmx::HardwareTopology> hardwareTopology; /* Information about hardware topology */
+    std::vector<std::unique_ptr<DeviceInformation>> deviceInfoList; /* Information about GPUs detected on this physical node */
+
+
+    /* Data reduced through MPI over all physical nodes */
+    int nphysicalnode;       /* Number of physical nodes */
+    int ncore_tot;           /* Sum of #cores over all nodes, can be 0 */
+    int ncore_min;           /* Min #cores over all nodes */
+    int ncore_max;           /* Max #cores over all nodes */
+    int nhwthread_tot;       /* Sum of #hwthreads over all nodes */
+    int nhwthread_min;       /* Min #hwthreads over all nodes */
+    int nhwthread_max;       /* Max #hwthreads over all nodes */
+    int ngpu_compatible_tot; /* Sum of #GPUs over all nodes */
+    int ngpu_compatible_min; /* Min #GPUs over all nodes */
+    int ngpu_compatible_max; /* Max #GPUs over all nodes */
+
+    int simd_suggest_min; /* Highest SIMD instruction set supported by all ranks */
+    int simd_suggest_max; /* Highest SIMD instruction set supported by at least one rank */
+
+    gmx_bool bIdenticalGPUs; /* TRUE if all ranks have the same type(s) and order of GPUs */
+    bool     haveAmdZen1Cpu; /* TRUE when at least one CPU in any of the nodes is AMD Zen of the first generation */
+
+    //! Container of warning strings to log later when that is possible.
+    std::vector<std::string> hardwareDetectionWarnings_;
+};
+
+
+/* The options for the thread affinity setting, default: auto */
+enum class ThreadAffinity
+{
+    Select,
+    Auto,
+    On,
+    Off,
+    Count
+};
+
+/*! \internal \brief Threading and GPU options, can be set automatically or by the user
+ *
+ * \todo During mdrunner(), if the user has left any of these values
+ * at their defaults (which tends to mean "choose automatically"),
+ * then those values are over-written with the result of such
+ * automation. This creates problems for the subsequent code in
+ * knowing what was done, why, and reporting correctly to the
+ * user. Find a way to improve this.
+ */
+struct gmx_hw_opt_t
+{
+    //! Total number of threads requested (thread-MPI + OpenMP).
+    int nthreads_tot = 0;
+    //! Number of thread-MPI threads requested.
+    int nthreads_tmpi = 0;
+    //! Number of OpenMP threads requested.
+    int nthreads_omp = 0;
+    //! Number of OpenMP threads to use on PME_only ranks.
+    int nthreads_omp_pme = 0;
+    //! Thread affinity switch, see enum above.
+    ThreadAffinity threadAffinity = ThreadAffinity::Select;
+    //! Logical core pinning stride.
+    int core_pinning_stride = 0;
+    //! Logical core pinning offset.
+    int core_pinning_offset = 0;
+    //! Empty, or a string provided by the user declaring (unique) GPU IDs available for mdrun to use.
+    std::string devicesSelectedByUser;
+    //! Empty, or a string provided by the user mapping GPU tasks to devices.
+    std::string userGpuTaskAssignment;
+    //! Tells whether mdrun is free to choose the total number of threads (by choosing the number of OpenMP and/or thread-MPI threads).
+    bool totNumThreadsIsAuto;
+};
+
+#endif
diff --git a/src/include/gromacs/hardware/identifyavx512fmaunits.h b/src/include/gromacs/hardware/identifyavx512fmaunits.h
new file mode 100644 (file)
index 0000000..5e20d1e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Defines a routine to check the number of AVX512 fma units
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_hardware
+ */
+
+namespace gmx
+{
+
+/*! \brief Test whether machine has dual AVX512 FMA units
+ *
+ * \return 1 or 2 for the number of AVX512 FMA units if AVX512
+ *         support is present, 0 if we know the hardware does
+ *         not have AVX512 support, or -1 if the test cannot
+ *         run because the compiler lacked AVX512 support.
+ */
+int identifyAvx512FmaUnits();
+
+} // namespace gmx
diff --git a/src/include/gromacs/hardware/prepare_detection.h b/src/include/gromacs/hardware/prepare_detection.h
new file mode 100644 (file)
index 0000000..38c94b0
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal
+ * \file
+ * \brief Declares routine for activating potentially deactivated
+ * cores so they can be detected.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_hardware
+ */
+#ifndef GMX_HARDWARE_PREPAREDETECTION_H
+#define GMX_HARDWARE_PREPAREDETECTION_H
+
+namespace gmx
+{
+
+/*! \brief Prepare the system before hardware topology detection
+ *
+ * This routine should perform any actions we want to put the system in a state
+ * where we want it to be before detecting the hardware topology. For most
+ * processors there is nothing to do, but some architectures (in particular ARM)
+ * have support for taking configured cores offline, which will make them disappear
+ * from the online processor count.
+ *
+ * This routine checks if there is a mismatch between the number of cores
+ * configured and online, and in that case we issue a small workload that
+ * attempts to wake sleeping cores before doing the actual detection.
+ *
+ * This type of mismatch can also occur for x86 or PowerPC on Linux, if SMT has only
+ * been disabled in the kernel (rather than bios). Since those cores will never
+ * come online automatically, we currently skip this test for x86 & PowerPC to
+ * avoid wasting 2 seconds. We also skip the test if there is no thread support.
+ *
+ * \note Cores will sleep relatively quickly again, so it's important to issue
+ *       the real detection code directly after this routine.
+ */
+void hardwareTopologyPrepareDetection();
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/hardware/printhardware.h b/src/include/gromacs/hardware/printhardware.h
new file mode 100644 (file)
index 0000000..a8061dd
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_HARDWARE_PRINTHARDWARE_H
+#define GMX_HARDWARE_PRINTHARDWARE_H
+
+#include <cstdio>
+
+struct gmx_hw_info_t;
+
+namespace gmx
+{
+class MDLogger;
+}
+
+/* Print information about the detected hardware to fplog (if != NULL)
+ * and to stderr on the master rank of the master simulation.
+ */
+void gmx_print_detected_hardware(FILE*                fplog,
+                                 bool                 warnToStdErr,
+                                 const gmx::MDLogger& mdlog,
+                                 const gmx_hw_info_t* hwinfo);
+
+#endif
diff --git a/src/include/gromacs/imd/imd.h b/src/include/gromacs/imd/imd.h
new file mode 100644 (file)
index 0000000..1d5c4fa
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal
+ * \defgroup module_imd Interactive molecular dynamics (IMD)
+ * \ingroup group_mdrun
+ *
+ * \brief
+ * Allows mdrun to interface with VMD via the interactive molecular dynamics
+ * (IMD) protocol.
+ *
+ * \author Martin Hoefling, Carsten Kutzner <ckutzne@gwdg.de>
+ *
+ * \todo Rename the directory, source and test files to
+ * interactive_md, and prefer type names like
+ * InteractiveMDSession. Avoid ambiguity with IMDModule.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief This file declares the class that coordinates with VMD via
+ * the Interactive Molecular Dynamics protocol, along with supporting
+ * free functions.
+ *
+ * \author Martin Hoefling, Carsten Kutzner <ckutzne@gwdg.de>
+ *
+ * \inlibraryapi
+ * \ingroup module_imd
+ */
+
+#ifndef GMX_IMD_IMD_H
+#define GMX_IMD_IMD_H
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+
+struct gmx_domdec_t;
+struct gmx_enerdata_t;
+struct gmx_mtop_t;
+struct gmx_multisim_t;
+struct gmx_output_env_t;
+struct gmx_wallcycle;
+struct t_commrec;
+struct t_filenm;
+struct t_IMD;
+struct t_inputrec;
+class t_state;
+
+namespace gmx
+{
+enum class StartingBehavior;
+class IMDModule;
+class ImdSession;
+class InteractiveMolecularDynamics;
+class MDLogger;
+struct ImdOptions;
+struct MdrunOptions;
+template<typename>
+class ArrayRef;
+
+/*! \brief
+ * Creates a module for interactive molecular dynamics.
+ */
+std::unique_ptr<IMDModule> createInteractiveMolecularDynamicsModule();
+
+static const char IMDstr[] = "IMD:"; /**< Tag output from the IMD module with this string. */
+
+/*! \brief Writes out the group of atoms selected for interactive manipulation.
+ *
+ * Called by grompp.
+ * The resulting file has to be read in by VMD if one wants it to connect to mdrun.
+ *
+ * \param bIMD    Only springs into action if bIMD is TRUE. Otherwise returns directly.
+ * \param ir      Structure containing MD input parameters, among those
+ *                the IMD data structure.
+ * \param state   The current state of the MD system.
+ * \param sys     The global, complete system topology.
+ * \param nfile   Number of files.
+ * \param fnm     Filename struct.
+ */
+void write_IMDgroup_to_file(bool              bIMD,
+                            t_inputrec*       ir,
+                            const t_state*    state,
+                            const gmx_mtop_t& sys,
+                            int               nfile,
+                            const t_filenm    fnm[]);
+
+
+/*! \brief Makes and returns an initialized IMD session, which may be inactive.
+ *
+ * This function is called before the main MD loop over time steps.
+ *
+ * \param ir           The inputrec structure containing the MD input parameters
+ * \param cr           Information structure for MPI communication.
+ * \param wcycle       Count wallcycles of IMD routines for diagnostic output.
+ * \param enerd        Contains the GROMACS energies for the different interaction types.
+ * \param ms           Handler for multi-simulations.
+ * \param top_global   The topology of the whole system.
+ * \param mdlog        Logger
+ * \param coords       The starting positions of the atoms.
+ * \param nfile        Number of files.
+ * \param fnm          Struct containing file names etc.
+ * \param oenv         Output options.
+ * \param options      Options for interactive MD.
+ * \param startingBehavior  Describes whether this is a restart appending to output files
+ */
+std::unique_ptr<ImdSession> makeImdSession(const t_inputrec*              ir,
+                                           const t_commrec*               cr,
+                                           gmx_wallcycle*                 wcycle,
+                                           gmx_enerdata_t*                enerd,
+                                           const gmx_multisim_t*          ms,
+                                           const gmx_mtop_t&              top_global,
+                                           const MDLogger&                mdlog,
+                                           gmx::ArrayRef<const gmx::RVec> coords,
+                                           int                            nfile,
+                                           const t_filenm                 fnm[],
+                                           const gmx_output_env_t*        oenv,
+                                           const ImdOptions&              options,
+                                           StartingBehavior               startingBehavior);
+
+class ImdSession
+{
+private:
+    //! Private constructor, to force the use of makeImdSession()
+    ImdSession(const MDLogger& mdlog);
+
+public:
+    ~ImdSession();
+
+    /*! \brief Make a selection of the home atoms for the IMD group.
+     *
+     * Should be called at every domain decomposition.
+     * Each node checks which of the atoms from "ind" are local and puts its local
+     * atom numbers into the "ind_local" array. Furthermore, in "xa_ind" it is
+     * stored at which position each local atom belongs in the assembled/collective
+     * array, so that on the master node all positions can be merged into the
+     * assembled array correctly.
+     *
+     * \param dd          Structure containing domain decomposition data.
+     */
+    void dd_make_local_IMD_atoms(const gmx_domdec_t* dd);
+
+    /*! \brief Prepare to do IMD if required in this time step
+     * Also checks for new IMD connection and syncs the nodes.
+     *
+     * \param step         The time step.
+     * \param bNS          Is this a neighbor searching step?
+     * \param box          The simulation box.
+     * \param coords       The local atomic positions on this node.
+     * \param t            The time.
+     *
+     * \returns            Whether or not we have to do IMD communication at this step.
+     */
+    bool run(int64_t step, bool bNS, const matrix box, gmx::ArrayRef<const gmx::RVec> coords, double t);
+
+    /*! \brief Add external forces from a running interactive molecular dynamics session.
+     *
+     * \param force The forces.
+     */
+    void applyForces(gmx::ArrayRef<gmx::RVec> force);
+
+    /*! \brief Copy energies and convert to float from enerdata to the IMD energy record.
+     *
+     * We do no conversion, so units in client are SI!
+     *
+     * \param step             The time step.
+     * \param bHaveNewEnergies Only copy energies if we have done global summing of them before.
+     */
+    void fillEnergyRecord(int64_t step, bool bHaveNewEnergies);
+
+    /*! \brief Send positions and energies to the client. */
+    void sendPositionsAndEnergies();
+
+    /*! \brief Updates the energy record sent to VMD if needed, and sends positions and energies.
+     *
+     * \param bIMDstep         If true, transfer the positions. Otherwise just update the time step and potentially the energy record.
+     * \param step             The time step.
+     * \param bHaveNewEnergies Update the energy record if we have done global summing of the energies.
+     */
+    void updateEnergyRecordAndSendPositionsAndEnergies(bool bIMDstep, int64_t step, bool bHaveNewEnergies);
+
+private:
+    //! Implementation type.
+    class Impl;
+    //! Implementation object.
+    std::unique_ptr<Impl> impl_;
+
+public:
+    // Befriend the factory function.
+    friend std::unique_ptr<ImdSession> makeImdSession(const t_inputrec*              ir,
+                                                      const t_commrec*               cr,
+                                                      gmx_wallcycle*                 wcycle,
+                                                      gmx_enerdata_t*                enerd,
+                                                      const gmx_multisim_t*          ms,
+                                                      const gmx_mtop_t&              top_global,
+                                                      const MDLogger&                mdlog,
+                                                      gmx::ArrayRef<const gmx::RVec> coords,
+                                                      int                            nfile,
+                                                      const t_filenm                 fnm[],
+                                                      const gmx_output_env_t*        oenv,
+                                                      const ImdOptions&              options,
+                                                      StartingBehavior startingBehavior);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/imd/imdsocket.h b/src/include/gromacs/imd/imdsocket.h
new file mode 100644 (file)
index 0000000..fdf1b46
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * Implements the parts of the vmdsock.h interface needed for IMD communication.
+ *
+ * \author Martin Hoefling, Carsten Kutzner <ckutzne@gwdg.de>
+ *
+ * For more information, see https://www-s.ks.uiuc.edu/Research/vmd/imd/ for general
+ * IMD information and https://www-s.ks.uiuc.edu/Research/vmd/imd/code/imdapi.tar.gz
+ * for the IMD reference implementation and API.
+ *
+ * \inlibraryapi
+ * \ingroup module_imd
+ */
+
+#ifndef GMX_IMD_IMDSOCKET_H
+#define GMX_IMD_IMDSOCKET_H
+
+namespace gmx
+{
+
+struct IMDSocket;
+
+
+/*! \internal
+ *
+ * \brief Function to initialize winsock
+ *
+ * \returns 0 if successful.
+ */
+int imdsock_winsockinit();
+
+/*! \brief Create an IMD master socket.
+ *
+ * \returns  The IMD socket if successful. Otherwise prints an error message and returns NULL.
+ */
+IMDSocket* imdsock_create();
+
+//! Portability wrapper around sleep function
+void imd_sleep(unsigned int seconds);
+
+
+/*! \brief Bind the IMD socket to address and port.
+ *
+ * Prints out an error message if unsuccessful.
+ * If port == 0, bind() assigns a free port automatically.
+ *
+ *
+ * \param sock      The IMD socket.
+ * \param port      The port to bind to.
+ *
+ * \returns 0 if successful.
+ */
+int imdsock_bind(IMDSocket* sock, int port);
+
+
+/*! \brief Set socket to listening state.
+ *
+ * Prints out an error message if unsuccessful.
+ *
+ * \param sock      The IMD socket.
+ *
+ * \returns 0 if successful.
+ *
+ */
+int imd_sock_listen(IMDSocket* sock);
+
+
+/*! \brief Accept incoming connection and redirect to client socket.
+ *
+ * Prints out an error message if unsuccessful.
+ *
+ * \param sock      The IMD socket.
+ *
+ * \returns IMD socket if successful, NULL otherwise.
+ */
+IMDSocket* imdsock_accept(IMDSocket* sock);
+
+
+/*! \brief Get the port number used for IMD connection.
+ *
+ * Prints out an error message if unsuccessful.
+ *
+ * \param sock      The IMD socket.
+ * \param port      The assigned port number.
+ *
+ * \returns 0 if successful, an error code otherwise.
+ */
+int imdsock_getport(IMDSocket* sock, int* port);
+
+//! Portability wrapper around system htonl function.
+int imd_htonl(int src);
+
+//! Portability wrapper around system ntohl function.
+int imd_ntohl(int src);
+
+
+/*! \brief  Write to socket.
+ *
+ *
+ * \param sock      The IMD socket.
+ * \param buffer    The data to write.
+ * \param length    Number of bytes to write.
+ *
+ * \returns The number of bytes written, or -1.
+ */
+int imdsock_write(IMDSocket* sock, const char* buffer, int length);
+
+
+/*! \brief  Read from socket.
+ *
+ * \param sock      The IMD socket.
+ * \param buffer    Buffer to put the read data.
+ * \param length    Number of bytes to read.
+ *
+ * \returns The number of bytes read, or -1 for errors.
+ */
+int imdsock_read(IMDSocket* sock, char* buffer, int length);
+
+
+/*! \brief Shutdown the socket.
+ *
+ * \param sock      The IMD socket.
+ *
+ */
+void imdsock_shutdown(IMDSocket* sock);
+
+
+/*! \brief Close the socket and free the sock struct memory.
+ *
+ * Writes an error message if unsuccessful.
+ *
+ * \param sock      The IMD socket.
+ *
+ * \returns 1 on success, or 0 if unsuccessful.
+ */
+int imdsock_destroy(IMDSocket* sock);
+
+
+/*! \brief Try to read from the socket.
+ *
+ * Time out after waiting the interval specified.
+ * Print an error message if unsuccessful.
+ *
+ * \param sock         The IMD socket.
+ * \param timeoutsec   Time out seconds
+ * \param timeoutusec  Time out microseconds
+ *
+ */
+int imdsock_tryread(IMDSocket* sock, int timeoutsec, int timeoutusec);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/linearalgebra/eigensolver.h b/src/include/gromacs/linearalgebra/eigensolver.h
new file mode 100644 (file)
index 0000000..e8c54e1
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_LINEARALGEBRA_EIGENSOLVER_H
+#define GMX_LINEARALGEBRA_EIGENSOLVER_H
+
+#include "gromacs/linearalgebra/sparsematrix.h"
+#include "gromacs/utility/real.h"
+
+/** Calculate eigenvalues/vectors a matrix stored in linear memory (not sparse).
+ *
+ *  This routine uses lapack to diagonalize a matrix efficiently, and
+ *  the eigenvalues/vectors will be sorted in ascending order on output.
+ *  Gromacs comes with a built-in portable BLAS/LAPACK, but if performance
+ *  matters it is advisable to link with an optimized vendor-provided library.
+ *
+ *  \param a            Pointer to matrix data, total size n*n
+ *                      The input data in the matrix will be destroyed/changed.
+ *  \param n            Side of the matrix to calculate eigenvalues for.
+ *  \param index_lower  Index of first eigenvector to determine.
+ *  \param index_upper  Last eigenvector determined is index_upper-1.
+ *  \param eigenvalues  Array of the eigenvalues on return. The length
+ *                      of this array _must_ be n, even if not all
+ *                      eigenvectors are calculated, since all eigenvalues
+ *                      might be needed as an intermediate step.
+ *  \param eigenvec     If this pointer is non-NULL, the eigenvectors
+ *                      specified by the indices are returned as rows of
+ *                      a matrix, i.e. eigenvector j starts at offset j*n, and
+ *                      is of length n.
+ */
+void eigensolver(real* a, int n, int index_lower, int index_upper, real* eigenvalues, real* eigenvec);
+
+
+/*! \brief Sparse matrix eigensolver.
+ *
+ *  This routine is intended for large matrices that might not fit in memory.
+ *
+ *  It will determine the neig lowest eigenvalues, and if the eigenvectors pointer
+ *  is non-NULL also the corresponding eigenvectors.
+ *
+ *  maxiter=100000 should suffice in most cases!
+ */
+void sparse_eigensolver(gmx_sparsematrix_t* A, int neig, real* eigenvalues, real* eigenvectors, int maxiter);
+
+#endif
diff --git a/src/include/gromacs/linearalgebra/gmx_arpack.h b/src/include/gromacs/linearalgebra/gmx_arpack.h
new file mode 100644 (file)
index 0000000..e7b9511
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2004 David van der Spoel, Erik Lindahl, University of Groningen.
+ * Copyright (c) 2012,2013,2014,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Selected routines from ARPACK
+ *
+ * This file contains a subset of ARPACK functions to perform
+ * diagonalization and SVD for sparse matrices in Gromacs.
+ *
+ * Consult the main ARPACK site for detailed documentation:
+ * http://www.caam.rice.edu/software/ARPACK/
+ *
+ * Below, we just list the options and any specific differences
+ * from ARPACK. The code is essentially the same, but the routines
+ * have been made thread-safe by using extra workspace arrays.
+ */
+#ifndef GMX_ARPACK_H
+#define GMX_ARPACK_H
+
+#include "config.h"
+/*! \brief Implicitly Restarted Arnoldi Iteration, double precision.
+ *
+ *  Reverse communication interface for the Implicitly Restarted Arnoldi
+ *  Iteration.  For symmetric problems this reduces to a variant of the
+ *  Lanczos method. See the ARPACK site for details.
+ *
+ *  \param ido     Reverse communication flag. Set to 0 first time.
+ *                 Upon return with ido=-1 or ido=1 you should calculate
+ *                 Y=A*X and recall the routine. Return with ido=2 means
+ *                 Y=B*X should be calculated. ipntr[0] is the pointer in
+ *                 workd for X, ipntr[1] is the index for Y.
+ *                 Return with ido=99 means it finished.
+ *  \param bmat    'I' for standard eigenproblem, 'G' for generalized.
+ *  \param n       Order of eigenproblem.
+ *  \param which   Which eigenvalues to calculate. 'LA' for largest
+ *                 algebraic, 'SA' for smallest algebraic, 'LM' for largest
+ *                 magnitude, 'SM' for smallest magnitude, and finally
+ *                 'BE' (both ends) to calculate half from each end of
+ *                 the spectrum.
+ *  \param nev     Number of eigenvalues to calculate. 0<nev<n.
+ *  \param tol     Tolerance. Machine precision of it is 0.
+ *  \param resid   Optional starting residual vector at input if info=1,
+ *                 otherwise a random one is used. Final residual vector on
+ *                 return.
+ *  \param ncv     Number of columns in matrix v.
+ *  \param v       N*NCV matrix. V contain the Lanczos basis vectors.
+ *  \param ldv     Leading dimension of v.
+ *  \param iparam  Integer array, size 11. Same contents as arpack.
+ *  \param ipntr   Integer array, size 11. Points to starting locations
+ *                 in the workd/workl arrays. Same contents as arpack.
+ *  \param workd   Double precision work array, length 3*n+4.
+ *                 Provide the same array for all calls, and don't touch it.
+ *                 IMPORTANT: This is 4 units larger than standard ARPACK!
+ *  \param iwork   Integer work array, size 80.
+ *                 Provide the same array for all calls, and don't touch it.
+ *                 IMPORTANT: New argument compared to standard ARPACK!
+ *  \param workl   Double precision work array, length lwork.
+ *  \param lworkl  Length of the work array workl. Must be at least ncv*(ncv+8)
+ *  \param info    Set info to 0 to use random initial residual vector,
+ *                 or to 1 if you provide a one. On output, info=0 means
+ *                 normal exit, 1 that max number of iterations was reached,
+ *                 and 3 that no shifts could be applied. Negative numbers
+ *                 correspond to errors in the arguments provided.
+ */
+void F77_FUNC(dsaupd, DSAUPD)(int*        ido,
+                              const char* bmat,
+                              int*        n,
+                              const char* which,
+                              int*        nev,
+                              double*     tol,
+                              double*     resid,
+                              int*        ncv,
+                              double*     v,
+                              int*        ldv,
+                              int*        iparam,
+                              int*        ipntr,
+                              double*     workd,
+                              int*        iwork,
+                              double*     workl,
+                              int*        lworkl,
+                              int*        info);
+
+
+/*! \brief Get eigenvalues/vectors after Arnoldi iteration, double prec.
+ *
+ *  See the ARPACK site for details. You must have finished the interative
+ *  part with dsaupd() before calling this function.
+ *
+ *  \param rvec    1 if you want eigenvectors, 0 if not.
+ *  \param howmny  'A' if you want all nvec vectors, 'S' if you
+ *                 provide a subset selection in select[].
+ *  \param select  Integer array, dimension nev. Indices of the
+ *                 eigenvectors to calculate. Fortran code means we
+ *                 start counting on 1. This array must be given even in
+ *                 howmny is 'A'. (Arpack documentation is wrong on this).
+ *  \param d       Double precision array, length nev. Eigenvalues.
+ *  \param z       Double precision array, n*nev. Eigenvectors.
+ *  \param ldz     Leading dimension of z. Normally n.
+ *  \param sigma   Shift if iparam[6] is 3,4, or 5. Ignored otherwise.
+ *  \param bmat    Provide the same argument as you did to dsaupd()
+ *  \param n       Provide the same argument as you did to dsaupd()
+ *  \param which   Provide the same argument as you did to dsaupd()
+ *  \param nev     Provide the same argument as you did to dsaupd()
+ *  \param tol     Provide the same argument as you did to dsaupd()
+ *  \param resid   Provide the same argument as you did to dsaupd()
+ *                 The array must not be touched between the two function calls!
+ *  \param ncv     Provide the same argument as you did to dsaupd()
+ *  \param v       Provide the same argument as you did to dsaupd()
+ *                 The array must not be touched between the two function calls!
+ *  \param ldv     Provide the same argument as you did to dsaupd()
+ *  \param iparam  Provide the same argument as you did to dsaupd()
+ *                 The array must not be touched between the two function calls!
+ *  \param ipntr   Provide the same argument as you did to dsaupd()
+ *                 The array must not be touched between the two function calls!
+ *  \param workd   Provide the same argument as you did to dsaupd()
+ *                 The array must not be touched between the two function calls!
+ *  \param workl   Double precision work array, length lwork.
+ *                 The array must not be touched between the two function calls!
+ *  \param lworkl  Provide the same argument as you did to dsaupd()
+ *  \param info    Provide the same argument as you did to dsaupd()
+ */
+void F77_FUNC(dseupd, DSEUPD)(int*        rvec,
+                              const char* howmny,
+                              int*        select,
+                              double*     d,
+                              double*     z,
+                              int*        ldz,
+                              double*     sigma,
+                              const char* bmat,
+                              int*        n,
+                              const char* which,
+                              int*        nev,
+                              double*     tol,
+                              double*     resid,
+                              int*        ncv,
+                              double*     v,
+                              int*        ldv,
+                              int*        iparam,
+                              int*        ipntr,
+                              double*     workd,
+                              double*     workl,
+                              int*        lworkl,
+                              int*        info);
+
+
+/*! \brief Implicitly Restarted Arnoldi Iteration, single precision.
+ *
+ *  Reverse communication interface for the Implicitly Restarted Arnoldi
+ *  Iteration.  For symmetric problems this reduces to a variant of the
+ *  Lanczos method. See the ARPACK site for details.
+ *
+ *  \param ido     Reverse communication flag. Set to 0 first time.
+ *                 Upon return with ido=-1 or ido=1 you should calculate
+ *                 Y=A*X and recall the routine. Return with ido=2 means
+ *                 Y=B*X should be calculated. ipntr[0] is the pointer in
+ *                 workd for X, ipntr[1] is the index for Y.
+ *                 Return with ido=99 means it finished.
+ *  \param bmat    'I' for standard eigenproblem, 'G' for generalized.
+ *  \param n       Order of eigenproblem.
+ *  \param which   Which eigenvalues to calculate. 'LA' for largest
+ *                 algebraic, 'SA' for smallest algebraic, 'LM' for largest
+ *                 magnitude, 'SM' for smallest magnitude, and finally
+ *                 'BE' (both ends) to calculate half from each end of
+ *                 the spectrum.
+ *  \param nev     Number of eigenvalues to calculate. 0<nev<n.
+ *  \param tol     Tolerance. Machine precision of it is 0.
+ *  \param resid   Optional starting residual vector at input if info=1,
+ *                 otherwise a random one is used. Final residual vector on
+ *                 return.
+ *  \param ncv     Number of columns in matrix v.
+ *  \param v       N*NCV matrix. V contain the Lanczos basis vectors.
+ *  \param ldv     Leading dimension of v.
+ *  \param iparam  Integer array, size 11. Same contents as arpack.
+ *  \param ipntr   Integer array, size 11. Points to starting locations
+ *                 in the workd/workl arrays. Same contents as arpack.
+ *  \param workd   Single precision work array, length 3*n+4.
+ *                 Provide the same array for all calls, and don't touch it.
+ *                 IMPORTANT: This is 4 units larger than standard ARPACK!
+ *  \param iwork   Integer work array, size 80.
+ *                 Provide the same array for all calls, and don't touch it.
+ *                 IMPORTANT: New argument compared to standard ARPACK!
+ *  \param workl   Single precision work array, length lwork.
+ *  \param lworkl  Length of the work array workl. Must be at least ncv*(ncv+8)
+ *  \param info    Set info to 0 to use random initial residual vector,
+ *                 or to 1 if you provide a one. On output, info=0 means
+ *                 normal exit, 1 that max number of iterations was reached,
+ *                 and 3 that no shifts could be applied. Negative numbers
+ *                 correspond to errors in the arguments provided.
+ */
+void F77_FUNC(ssaupd, SSAUPD)(int*        ido,
+                              const char* bmat,
+                              int*        n,
+                              const char* which,
+                              int*        nev,
+                              float*      tol,
+                              float*      resid,
+                              int*        ncv,
+                              float*      v,
+                              int*        ldv,
+                              int*        iparam,
+                              int*        ipntr,
+                              float*      workd,
+                              int*        iwork,
+                              float*      workl,
+                              int*        lworkl,
+                              int*        info);
+
+
+/*! \brief Get eigenvalues/vectors after Arnoldi iteration, single prec.
+ *
+ *  See the ARPACK site for details. You must have finished the interative
+ *  part with ssaupd() before calling this function.
+ *
+ *  \param rvec    1 if you want eigenvectors, 0 if not.
+ *  \param howmny  'A' if you want all nvec vectors, 'S' if you
+ *                 provide a subset selection in select[].
+ *  \param select  Integer array, dimension nev. Indices of the
+ *                 eigenvectors to calculate. Fortran code means we
+ *                 start counting on 1. This array must be given even in
+ *                 howmny is 'A'. (Arpack documentation is wrong on this).
+ *  \param d       Single precision array, length nev. Eigenvalues.
+ *  \param z       Single precision array, n*nev. Eigenvectors.
+ *  \param ldz     Leading dimension of z. Normally n.
+ *  \param sigma   Shift if iparam[6] is 3,4, or 5. Ignored otherwise.
+ *  \param bmat    Provide the same argument as you did to ssaupd()
+ *  \param n       Provide the same argument as you did to ssaupd()
+ *  \param which   Provide the same argument as you did to ssaupd()
+ *  \param nev     Provide the same argument as you did to ssaupd()
+ *  \param tol     Provide the same argument as you did to ssaupd()
+ *  \param resid   Provide the same argument as you did to ssaupd()
+ *                 The array must not be touched between the two function calls!
+ *  \param ncv     Provide the same argument as you did to ssaupd()
+ *  \param v       Provide the same argument as you did to ssaupd()
+ *                 The array must not be touched between the two function calls!
+ *  \param ldv     Provide the same argument as you did to ssaupd()
+ *  \param iparam  Provide the same argument as you did to ssaupd()
+ *                 The array must not be touched between the two function calls!
+ *  \param ipntr   Provide the same argument as you did to ssaupd()
+ *                 The array must not be touched between the two function calls!
+ *  \param workd   Provide the same argument as you did to ssaupd()
+ *                 The array must not be touched between the two function calls!
+ *  \param workl   Single precision work array, length lwork.
+ *                 The array must not be touched between the two function calls!
+ *  \param lworkl  Provide the same argument as you did to ssaupd()
+ *  \param info    Provide the same argument as you did to ssaupd()
+ */
+void F77_FUNC(sseupd, SSEUPD)(int*        rvec,
+                              const char* howmny,
+                              int*        select,
+                              float*      d,
+                              float*      z,
+                              int*        ldz,
+                              float*      sigma,
+                              const char* bmat,
+                              int*        n,
+                              const char* which,
+                              int*        nev,
+                              float*      tol,
+                              float*      resid,
+                              int*        ncv,
+                              float*      v,
+                              int*        ldv,
+                              int*        iparam,
+                              int*        ipntr,
+                              float*      workd,
+                              float*      workl,
+                              int*        lworkl,
+                              int*        info);
+
+#endif
diff --git a/src/include/gromacs/linearalgebra/gmx_blas.h b/src/include/gromacs/linearalgebra/gmx_blas.h
new file mode 100644 (file)
index 0000000..4e8e4d7
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Header definitions for the standard BLAS library.
+ *
+ * This is the subset of BLAS routines used for the
+ * linear algebra operations in Gromacs.
+ * Do NOT use this for other purposes - we only provide this as a
+ * simple fallback/reference implementation when no optimized BLAS
+ * is present. If you need an implementation for your own code
+ * there are several much faster versions out there.
+ *
+ * All routines are compatible with the BLAS reference implementation,
+ * meaning they assume fortran-style matrix row/column organization.
+ *
+ * There is plenty of documentation for these routines available
+ * at http://www.netlib.org/blas , so there is no point in repeating
+ * it here.
+ */
+#ifndef GMX_BLAS_H
+#define GMX_BLAS_H
+
+/*! \cond */
+
+#include "config.h"
+
+/* These are not required by this file, but by the internal BLAS
+ * implementation.  In principle, they could be included in each file
+ * that requires them, but this is simpler.  Since the header is internal
+ * to the linearyalgebra/ module, the added complexity may not be worth it. */
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#if 0
+}
+#endif
+
+    /* Double precision versions */
+    double F77_FUNC(dasum, DASUM)(int* n, double* dx, int* incx);
+
+    void F77_FUNC(daxpy, DAXPY)(int* n, double* da, double* dx, int* incx, double* dy, int* incy);
+
+    void F77_FUNC(dcopy, DCOPY)(int* n, double* dx, int* incx, double* dy, int* incy);
+
+    double F77_FUNC(ddot, DDOT)(int* n, double* dx, int* incx, double* dy, int* incy);
+
+    void F77_FUNC(dgemm, DGEMM)(const char* transa,
+                                const char* transb,
+                                int*        m,
+                                int*        n,
+                                int*        k,
+                                double*     alpha,
+                                double*     a,
+                                int*        lda,
+                                double*     b,
+                                int*        ldb,
+                                double*     beta,
+                                double*     c,
+                                int*        ldc);
+
+    void F77_FUNC(dgemv, DGEMV)(const char* trans,
+                                int*        m,
+                                int*        n,
+                                double*     alpha,
+                                double*     a,
+                                int*        lda,
+                                double*     x,
+                                int*        incx,
+                                double*     beta,
+                                double*     y,
+                                int*        incy);
+
+    void F77_FUNC(dger,
+                  DGER)(int* m, int* n, double* alpha, double* x, int* incx, double* y, int* incy, double* a, int* lda);
+
+    double F77_FUNC(dnrm2, DNRM2)(int* n, double* x, int* incx);
+
+    void F77_FUNC(drot, DROT)(int* n, double* dx, int* incx, double* dy, int* incy, double* c, double* s);
+
+    void F77_FUNC(dscal, DSCAL)(int* n, double* fact, double* dx, int* incx);
+
+    void F77_FUNC(dswap, DSWAP)(int* n, double* dx, int* incx, double* dy, int* incy);
+
+    void F77_FUNC(dsymv, DSYMV)(const char* uplo,
+                                int*        n,
+                                double*     alpha,
+                                double*     a,
+                                int*        lda,
+                                double*     x,
+                                int*        incx,
+                                double*     beta,
+                                double*     y,
+                                int*        incy);
+
+    void F77_FUNC(dsyr2, DSYR2)(const char* uplo,
+                                int*        n,
+                                double*     alpha,
+                                double*     x,
+                                int*        incx,
+                                double*     y,
+                                int*        incy,
+                                double*     a,
+                                int*        lda);
+
+    void F77_FUNC(dsyr2k, DSYR2K)(const char* uplo,
+                                  const char* trans,
+                                  int*        n,
+                                  int*        k,
+                                  double*     alpha,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     b,
+                                  int*        ldb,
+                                  double*     beta,
+                                  double*     c,
+                                  int*        ldc);
+
+    void F77_FUNC(dtrmm, DTRMM)(const char* side,
+                                const char* uplo,
+                                const char* transa,
+                                const char* diag,
+                                int*        m,
+                                int*        n,
+                                double*     alpha,
+                                double*     a,
+                                int*        lda,
+                                double*     b,
+                                int*        ldb);
+
+    void F77_FUNC(dtrmv, DTRMV)(const char* uplo,
+                                const char* trans,
+                                const char* diag,
+                                int*        n,
+                                double*     a,
+                                int*        lda,
+                                double*     x,
+                                int*        incx);
+
+    void F77_FUNC(dtrsm, DTRSM)(const char* side,
+                                const char* uplo,
+                                const char* transa,
+                                const char* diag,
+                                int*        m,
+                                int*        n,
+                                double*     alpha,
+                                double*     a,
+                                int*        lda,
+                                double*     b,
+                                int*        ldb);
+
+    int F77_FUNC(idamax, IDAMAX)(int* n, double* dx, int* incx);
+
+
+    /* Single precision versions */
+    float F77_FUNC(sasum, SASUM)(int* n, float* dx, int* incx);
+
+    void F77_FUNC(saxpy, SAXPY)(int* n, float* da, float* dx, int* incx, float* dy, int* incy);
+
+    void F77_FUNC(scopy, SCOPY)(int* n, float* dx, int* incx, float* dy, int* incy);
+
+    float F77_FUNC(sdot, SDOT)(int* n, float* dx, int* incx, float* dy, int* incy);
+
+    void F77_FUNC(sgemm, SGEMM)(const char* transa,
+                                const char* transb,
+                                int*        m,
+                                int*        n,
+                                int*        k,
+                                float*      alpha,
+                                float*      a,
+                                int*        lda,
+                                float*      b,
+                                int*        ldb,
+                                float*      beta,
+                                float*      c,
+                                int*        ldc);
+
+    void F77_FUNC(sgemv, SGEMV)(const char* trans,
+                                int*        m,
+                                int*        n,
+                                float*      alpha,
+                                float*      a,
+                                int*        lda,
+                                float*      x,
+                                int*        incx,
+                                float*      beta,
+                                float*      y,
+                                int*        incy);
+
+    void F77_FUNC(sger,
+                  SGER)(int* m, int* n, float* alpha, float* x, int* incx, float* y, int* incy, float* a, int* lda);
+
+    float F77_FUNC(snrm2, SNRM2)(int* n, float* x, int* incx);
+
+    void F77_FUNC(srot, SROT)(int* n, float* dx, int* incx, float* dy, int* incy, float* c, float* s);
+
+    void F77_FUNC(sscal, SSCAL)(int* n, float* fact, float* dx, int* incx);
+
+    void F77_FUNC(sswap, SSWAP)(int* n, float* dx, int* incx, float* dy, int* incy);
+
+    void F77_FUNC(ssymv, SSYMV)(const char* uplo,
+                                int*        n,
+                                float*      alpha,
+                                float*      a,
+                                int*        lda,
+                                float*      x,
+                                int*        incx,
+                                float*      beta,
+                                float*      y,
+                                int*        incy);
+
+    void F77_FUNC(ssyr2, SSYR2)(const char* uplo,
+                                int*        n,
+                                float*      alpha,
+                                float*      x,
+                                int*        incx,
+                                float*      y,
+                                int*        incy,
+                                float*      a,
+                                int*        lda);
+
+    void F77_FUNC(ssyr2k, SSYR2K)(const char* uplo,
+                                  const char* trans,
+                                  int*        n,
+                                  int*        k,
+                                  float*      alpha,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      b,
+                                  int*        ldb,
+                                  float*      beta,
+                                  float*      c,
+                                  int*        ldc);
+
+    void F77_FUNC(strmm, STRMM)(const char* side,
+                                const char* uplo,
+                                const char* transa,
+                                const char* diag,
+                                int*        m,
+                                int*        n,
+                                float*      alpha,
+                                float*      a,
+                                int*        lda,
+                                float*      b,
+                                int*        ldb);
+
+    void F77_FUNC(strmv, STRMV)(const char* uplo,
+                                const char* trans,
+                                const char* diag,
+                                int*        n,
+                                float*      a,
+                                int*        lda,
+                                float*      x,
+                                int*        incx);
+
+    void F77_FUNC(strsm, STRSM)(const char* side,
+                                const char* uplo,
+                                const char* transa,
+                                const char* diag,
+                                int*        m,
+                                int*        n,
+                                float*      alpha,
+                                float*      a,
+                                int*        lda,
+                                float*      b,
+                                int*        ldb);
+
+    int F77_FUNC(isamax, ISAMAX)(int* n, float* dx, int* incx);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/*! \endcond */
+
+#endif /* GMX_BLAS_H */
diff --git a/src/include/gromacs/linearalgebra/gmx_lapack.h b/src/include/gromacs/linearalgebra/gmx_lapack.h
new file mode 100644 (file)
index 0000000..3d9fb90
--- /dev/null
@@ -0,0 +1,1808 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Header definitions for the standard LAPACK library.
+ *
+ * This is the subset of LAPACK routines used for the
+ * linear algebra operations in Gromacs. Most of the execution time
+ * will be spent in the BLAS routines, which you hopefully have an
+ * optimized version of. Gromacs includes reference implementations
+ * of both BLAS and LAPACK so it compiles everywhere, but you should
+ * really try to find a vendor or otherwise optimized version at least
+ * of BLAS for better performance.
+ *
+ * Do NOT use this code for other purposes - we only provide this as a
+ * simple fallback/reference implementation when no optimized BLAS
+ * is present. If you need an implementation for your own code
+ * there are several much faster versions out there.
+ *
+ * All routines are compatible with the LAPACK/BLAS reference implementations,
+ * meaning they assume fortran-style matrix row/column organization.
+ *
+ * There is plenty of documentation for these routines available
+ * at http://www.netlib.org/lapack , so there is no point in repeating
+ * it here.
+ */
+#ifndef GMX_LAPACK_H
+#define GMX_LAPACK_H
+
+/*! \cond */
+
+#include "config.h"
+
+/* These are not required by this file, but by the internal LAPACK
+ * implementation.  In principle, they could be included in each file
+ * that requires them, but this is simpler.  Since the header is internal
+ * to the linearyalgebra/ module, the added complexity may not be worth it. */
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+#if 0
+}
+#endif
+    /* Double precision */
+
+    void F77_FUNC(dbdsdc, DBDSDC)(const char* uplo,
+                                  const char* compq,
+                                  int*        n,
+                                  double*     d,
+                                  double*     e,
+                                  double*     u,
+                                  int*        ldu,
+                                  double*     vt,
+                                  int*        ldvt,
+                                  double*     q,
+                                  int*        iq,
+                                  double*     work,
+                                  int*        iwork,
+                                  int*        info);
+
+    void F77_FUNC(dgetf2, DGETF2)(int* m, int* n, double* a, int* lda, int* ipiv, int* info);
+
+    void F77_FUNC(dlamrg, DLAMRG)(int* n1, int* n2, double* a, int* dtrd1, int* dtrd2, int* index);
+
+    void F77_FUNC(dlarnv, DLARNV)(int* idist, int* iseed, int* n, double* x);
+
+    void F77_FUNC(dlasd0, DLASD0)(int*    n,
+                                  int*    sqre,
+                                  double* d,
+                                  double* e,
+                                  double* u,
+                                  int*    ldu,
+                                  double* vt,
+                                  int*    ldvt,
+                                  int*    smlsiz,
+                                  int*    iwork,
+                                  double* work,
+                                  int*    info);
+
+    void F77_FUNC(dlasda, DLASDA)(int*    icompq,
+                                  int*    smlsiz,
+                                  int*    n,
+                                  int*    sqre,
+                                  double* d,
+                                  double* e,
+                                  double* u,
+                                  int*    ldu,
+                                  double* vt,
+                                  int*    k,
+                                  double* difl,
+                                  double* difr,
+                                  double* z,
+                                  double* poles,
+                                  int*    givptr,
+                                  int*    givcol,
+                                  int*    ldgcol,
+                                  int*    perm,
+                                  double* givnum,
+                                  double* c,
+                                  double* s,
+                                  double* work,
+                                  int*    iwork,
+                                  int*    info);
+
+    void F77_FUNC(dlasq6, DLASQ6)(int*    i0,
+                                  int*    n0,
+                                  double* z,
+                                  int*    pp,
+                                  double* dmin,
+                                  double* dmin1,
+                                  double* dmin2,
+                                  double* dn,
+                                  double* dnm1,
+                                  double* dnm2);
+
+    void F77_FUNC(dorgl2,
+                  DORGL2)(int* m, int* n, int* k, double* a, int* lda, double* tau, double* work, int* info);
+
+    void F77_FUNC(dbdsqr, DBDSQR)(const char* uplo,
+                                  int*        n,
+                                  int*        ncvt,
+                                  int*        nru,
+                                  int*        ncc,
+                                  double*     d,
+                                  double*     e,
+                                  double*     vt,
+                                  int*        ldvt,
+                                  double*     u,
+                                  int*        ldu,
+                                  double*     c,
+                                  int*        ldc,
+                                  double*     work,
+                                  int*        info);
+
+    void F77_FUNC(dgetrf, DGETRF)(int* m, int* n, double* a, int* lda, int* ipiv, int* info);
+
+    void F77_FUNC(dgetri,
+                  DGETRI)(int* n, double* a, int* lda, int* ipiv, double* work, int* lwork, int* info);
+
+    void F77_FUNC(dgetrs, DGETRS)(const char* trans,
+                                  int*        n,
+                                  int*        nrhs,
+                                  double*     a,
+                                  int*        lda,
+                                  int*        ipiv,
+                                  double*     b,
+                                  int*        ldb,
+                                  int*        info);
+
+    void F77_FUNC(dtrtri,
+                  DTRTRI)(const char* uplo, const char* diag, int* n, double* a, int* lda, int* info);
+
+    void F77_FUNC(dtrti2,
+                  DTRTI2)(const char* uplo, const char* diag, int* n, double* a, int* lda, int* info);
+
+    double F77_FUNC(dlange, DLANGE)(const char* norm, int* m, int* n, double* a, int* lda, double* work);
+
+    void F77_FUNC(dlarrbx, DLARRBX)(int*    n,
+                                    double* d,
+                                    double* l,
+                                    double* ld,
+                                    double* lld,
+                                    int*    ifirst,
+                                    int*    ilast,
+                                    double* rtol1,
+                                    double* rtol2,
+                                    int*    offset,
+                                    double* w,
+                                    double* wgap,
+                                    double* werr,
+                                    double* work,
+                                    int*    iwork,
+                                    int*    info);
+
+    void F77_FUNC(dlasd1, DLASD1)(int*    nl,
+                                  int*    nr,
+                                  int*    sqre,
+                                  double* d,
+                                  double* alpha,
+                                  double* beta,
+                                  double* u,
+                                  int*    ldu,
+                                  double* vt,
+                                  int*    ldvt,
+                                  int*    idxq,
+                                  int*    iwork,
+                                  double* work,
+                                  int*    info);
+
+    void F77_FUNC(dlasdq, DLASDQ)(const char* uplo,
+                                  int*        sqre,
+                                  int*        n,
+                                  int*        ncvt,
+                                  int*        nru,
+                                  int*        ncc,
+                                  double*     d,
+                                  double*     e,
+                                  double*     vt,
+                                  int*        ldvt,
+                                  double*     u,
+                                  int*        ldu,
+                                  double*     c,
+                                  int*        ldc,
+                                  double*     work,
+                                  int*        info);
+
+    void F77_FUNC(dlasr, DLASR)(const char* side,
+                                const char* pivot,
+                                const char* direct,
+                                int*        m,
+                                int*        n,
+                                double*     c,
+                                double*     s,
+                                double*     a,
+                                int*        lda);
+
+    void F77_FUNC(dorglq,
+                  DORGLQ)(int* m, int* n, int* k, double* a, int* lda, double* tau, double* work, int* lwork, int* info);
+
+    void F77_FUNC(dormtr, DORMTR)(const char* side,
+                                  const char* uplo,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     tau,
+                                  double*     c,
+                                  int*        ldc,
+                                  double*     work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(dgebd2, DGEBD2)(int*    m,
+                                  int*    n,
+                                  double* a,
+                                  int*    lda,
+                                  double* d,
+                                  double* e,
+                                  double* tauq,
+                                  double* taup,
+                                  double* work,
+                                  int*    info);
+
+    void F77_FUNC(dlabrd, DLABRD)(int*    m,
+                                  int*    n,
+                                  int*    nb,
+                                  double* a,
+                                  int*    lda,
+                                  double* d,
+                                  double* e,
+                                  double* tauq,
+                                  double* taup,
+                                  double* x,
+                                  int*    ldx,
+                                  double* y,
+                                  int*    ldy);
+
+    double F77_FUNC(dlanst, DLANST)(const char* norm, int* n, double* d, double* e);
+
+    double F77_FUNC(dlansy,
+                    DLANSY)(const char* norm, const char* uplo, int* n, double* a, int* lda, double* work);
+
+    void F77_FUNC(dlarrex, DLARREX)(const char* range,
+                                    int*        n,
+                                    double*     vl,
+                                    double*     vu,
+                                    int*        il,
+                                    int*        iu,
+                                    double*     d,
+                                    double*     e,
+                                    double*     tol,
+                                    int*        nsplit,
+                                    int*        isplit,
+                                    int*        m,
+                                    double*     w,
+                                    int*        iblock,
+                                    int*        indexw,
+                                    double*     gersch,
+                                    double*     work,
+                                    int*        iwork,
+                                    int*        info);
+
+    void F77_FUNC(dlasd2, DLASD2)(int*    nl,
+                                  int*    nr,
+                                  int*    sqre,
+                                  int*    k,
+                                  double* d,
+                                  double* z,
+                                  double* alpha,
+                                  double* beta,
+                                  double* u,
+                                  int*    ldu,
+                                  double* vt,
+                                  int*    ldvt,
+                                  double* dsigma,
+                                  double* u2,
+                                  int*    ldu2,
+                                  double* vt2,
+                                  int*    ldvt2,
+                                  int*    idxp,
+                                  int*    idx,
+                                  int*    idxc,
+                                  int*    idxq,
+                                  int*    coltyp,
+                                  int*    info);
+
+    void F77_FUNC(dlasdt, DLASDT)(int* n, int* lvl, int* nd, int* inode, int* ndiml, int* ndimr, int* msub);
+
+    void F77_FUNC(dlasrt, DLASRT)(const char* id, int* n, double* d, int* info);
+
+    void F77_FUNC(dlasrt2, DLASRT2)(const char* id, int* n, double* d, int* key, int* info);
+
+    void F77_FUNC(ilasrt2, ILASRT2)(const char* id, int* n, int* d, int* key, int* info);
+
+    void F77_FUNC(dorgqr,
+                  DORGQR)(int* m, int* n, int* k, double* a, int* lda, double* tau, double* work, int* lwork, int* info);
+
+    void F77_FUNC(dstebz, DSTEBZ)(const char* range,
+                                  const char* order,
+                                  int*        n,
+                                  double*     vl,
+                                  double*     vu,
+                                  int*        il,
+                                  int*        iu,
+                                  double*     abstol,
+                                  double*     d,
+                                  double*     e,
+                                  int*        m,
+                                  int*        nsplit,
+                                  double*     w,
+                                  int*        iblock,
+                                  int*        isplit,
+                                  double*     work,
+                                  int*        iwork,
+                                  int*        info);
+
+    void F77_FUNC(dsteqr, DSTEQR)(const char* compz,
+                                  int*        n,
+                                  double*     d__,
+                                  double*     e,
+                                  double*     z__,
+                                  int*        ldz,
+                                  double*     work,
+                                  int*        info);
+
+    void F77_FUNC(dgebrd, DGEBRD)(int*    m,
+                                  int*    n,
+                                  double* a,
+                                  int*    lda,
+                                  double* d,
+                                  double* e,
+                                  double* tauq,
+                                  double* taup,
+                                  double* work,
+                                  int*    lwork,
+                                  int*    info);
+
+    void F77_FUNC(dlacpy,
+                  DLACPY)(const char* uplo, int* m, int* n, double* a, int* lda, double* b, int* ldb);
+
+    double F77_FUNC(dlapy2, DLAPY2)(double* x, double* y);
+
+
+    void F77_FUNC(dlarrfx, DLARRFX)(int*    n,
+                                    double* d,
+                                    double* l,
+                                    double* ld,
+                                    double* lld,
+                                    int*    ifirst,
+                                    int*    ilast,
+                                    double* w,
+                                    double* sigma,
+                                    double* dplus,
+                                    double* lplus,
+                                    double* work,
+                                    int*    info);
+
+    void F77_FUNC(dlasd3, DLASD3)(int*    nl,
+                                  int*    nr,
+                                  int*    sqre,
+                                  int*    k,
+                                  double* d,
+                                  double* q,
+                                  int*    ldq,
+                                  double* dsigma,
+                                  double* u,
+                                  int*    ldu,
+                                  double* u2,
+                                  int*    ldu2,
+                                  double* vt,
+                                  int*    ldvt,
+                                  double* vt2,
+                                  int*    ldvt2,
+                                  int*    idxc,
+                                  int*    ctot,
+                                  double* z,
+                                  int*    info);
+
+    void F77_FUNC(dlaset,
+                  DLASET)(const char* uplo, int* m, int* n, double* alpha, double* beta, double* a, int* lda);
+
+    void F77_FUNC(dlassq, DLASSQ)(int* n, double* x, int* incx, double* scale, double* sumsq);
+
+    void F77_FUNC(dorm2l, DORM2L)(const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     tau,
+                                  double*     c,
+                                  int*        ldc,
+                                  double*     work,
+                                  int*        info);
+
+    void F77_FUNC(dstegr, DSTEGR)(const char* jobz,
+                                  const char* range,
+                                  int*        n,
+                                  double*     d,
+                                  double*     e,
+                                  double*     vl,
+                                  double*     vu,
+                                  int*        il,
+                                  int*        iu,
+                                  double*     abstol,
+                                  int*        m,
+                                  double*     w,
+                                  double*     z,
+                                  int*        ldz,
+                                  int*        isuppz,
+                                  double*     work,
+                                  int*        lwork,
+                                  int*        iwork,
+                                  int*        liwork,
+                                  int*        info);
+
+    void F77_FUNC(ssteqr,
+                  SSTEQR)(const char* compz, int* n, float* d__, float* e, float* z__, int* ldz, float* work, int* info);
+
+    void F77_FUNC(dgelq2, DGELQ2)(int* m, int* n, double* a, int* lda, double* tau, double* work, int* info);
+
+    void F77_FUNC(dlae2, DLAE2)(double* a, double* b, double* c, double* rt1, double* rt2);
+
+    void F77_FUNC(dlaev2,
+                  DLAEV2)(double* a, double* b, double* c, double* rt1, double* rt2, double* cs1, double* cs2);
+
+    void F77_FUNC(dlar1vx, DLAR1VX)(int*    n,
+                                    int*    b1,
+                                    int*    bn,
+                                    double* sigma,
+                                    double* d,
+                                    double* l,
+                                    double* ld,
+                                    double* lld,
+                                    double* eval,
+                                    double* gersch,
+                                    double* z,
+                                    double* ztz,
+                                    double* mingma,
+                                    int*    r,
+                                    int*    isuppz,
+                                    double* work);
+
+    void F77_FUNC(dlarrvx, DLARRVX)(int*    n,
+                                    double* d,
+                                    double* l,
+                                    int*    isplit,
+                                    int*    m,
+                                    double* w,
+                                    int*    iblock,
+                                    int*    indexw,
+                                    double* gersch,
+                                    double* tol,
+                                    double* z,
+                                    int*    ldz,
+                                    int*    isuppz,
+                                    double* work,
+                                    int*    iwork,
+                                    int*    info);
+
+    void F77_FUNC(dlasd4, DLASD4)(int*    n,
+                                  int*    i,
+                                  double* d,
+                                  double* z,
+                                  double* delta,
+                                  double* rho,
+                                  double* sigma,
+                                  double* work,
+                                  int*    info);
+
+    void F77_FUNC(dlasq1, DLASQ1)(int* n, double* d, double* e, double* work, int* info);
+
+
+    void F77_FUNC(dlasv2, DLASV2)(double* f,
+                                  double* g,
+                                  double* h,
+                                  double* ssmin,
+                                  double* ssmax,
+                                  double* snr,
+                                  double* csr,
+                                  double* snl,
+                                  double* csl);
+
+    void F77_FUNC(dorm2r, DORM2R)(const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     tau,
+                                  double*     c,
+                                  int*        ldc,
+                                  double*     work,
+                                  int*        info);
+
+    void F77_FUNC(dstein, DSTEIN)(int*    n,
+                                  double* d,
+                                  double* e,
+                                  int*    m,
+                                  double* w,
+                                  int*    iblock,
+                                  int*    isplit,
+                                  double* z,
+                                  int*    ldz,
+                                  double* work,
+                                  int*    iwork,
+                                  int*    ifail,
+                                  int*    info);
+
+    void F77_FUNC(dgelqf,
+                  DGELQF)(int* m, int* n, double* a, int* lda, double* tau, double* work, int* lwork, int* info);
+
+    void F77_FUNC(dlaebz, DLAEBZ)(int*    ijob,
+                                  int*    nitmax,
+                                  int*    n,
+                                  int*    mmax,
+                                  int*    minp,
+                                  int*    nbmin,
+                                  double* abstol,
+                                  double* reltol,
+                                  double* pivmin,
+                                  double* d,
+                                  double* e,
+                                  double* e2,
+                                  int*    nval,
+                                  double* ab,
+                                  double* c,
+                                  int*    mout,
+                                  int*    nab,
+                                  double* work,
+                                  int*    iwork,
+                                  int*    info);
+
+    void F77_FUNC(dlarf, DLARF)(const char* side,
+                                int*        m,
+                                int*        n,
+                                double*     v,
+                                int*        incv,
+                                double*     tau,
+                                double*     c,
+                                int*        ldc,
+                                double*     work);
+
+    void F77_FUNC(dlartg, DLARTG)(double* f, double* g, double* cs, double* sn, double* r);
+
+    void F77_FUNC(dlasd5,
+                  DLASD5)(int* i, double* d, double* z, double* delta, double* rho, double* dsigma, double* work);
+
+    void F77_FUNC(dlasq2, DLASQ2)(int* n, double* z, int* info);
+
+    void F77_FUNC(dlasq3, DLASQ3)(int*    i0,
+                                  int*    n0,
+                                  double* z,
+                                  int*    pp,
+                                  double* dmin,
+                                  double* sigma,
+                                  double* desig,
+                                  double* qmax,
+                                  int*    nfail,
+                                  int*    iter,
+                                  int*    ndiv,
+                                  int*    ieee);
+
+    void F77_FUNC(dlaswp, DLASWP)(int* n, double* a, int* lda, int* k1, int* k2, int* ipiv, int* incx);
+
+    void F77_FUNC(dormbr, DORMBR)(const char* vect,
+                                  const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     tau,
+                                  double*     c,
+                                  int*        ldc,
+                                  double*     work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(dsterf, DSTERF)(int* n, double* d, double* e, int* info);
+
+    void F77_FUNC(dgeqr2, DGEQR2)(int* m, int* n, double* a, int* lda, double* tau, double* work, int* info);
+
+    void F77_FUNC(dlaed6, DLAED6)(int*    kniter,
+                                  int*    orgati,
+                                  double* rho,
+                                  double* d,
+                                  double* z,
+                                  double* finit,
+                                  double* tau,
+                                  int*    info);
+
+    void F77_FUNC(dlarfb, DLARFB)(const char* side,
+                                  const char* trans,
+                                  const char* direct,
+                                  const char* storev,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  double*     v,
+                                  int*        ldv,
+                                  double*     t,
+                                  int*        ldt,
+                                  double*     c,
+                                  int*        ldc,
+                                  double*     work,
+                                  int*        ldwork);
+
+    void F77_FUNC(dlaruv, DLARUV)(int* iseed, int* n, double* x);
+
+    void F77_FUNC(dlasd6, DLASD6)(int*    icompq,
+                                  int*    nl,
+                                  int*    nr,
+                                  int*    sqre,
+                                  double* d,
+                                  double* vf,
+                                  double* vl,
+                                  double* alpha,
+                                  double* beta,
+                                  int*    idxq,
+                                  int*    perm,
+                                  int*    givptr,
+                                  int*    givcol,
+                                  int*    ldgcol,
+                                  double* givnum,
+                                  int*    ldgnum,
+                                  double* poles,
+                                  double* difl,
+                                  double* difr,
+                                  double* z,
+                                  int*    k,
+                                  double* c,
+                                  double* s,
+                                  double* work,
+                                  int*    iwork,
+                                  int*    info);
+
+    void F77_FUNC(dlatrd, DLATRD)(const char* uplo,
+                                  int*        n,
+                                  int*        nb,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     e,
+                                  double*     tau,
+                                  double*     w,
+                                  int*        ldw);
+
+    void F77_FUNC(dorml2, DORML2)(const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     tau,
+                                  double*     c,
+                                  int*        ldc,
+                                  double*     work,
+                                  int*        info);
+
+    void F77_FUNC(dstevr, DSTEVR)(const char* jobz,
+                                  const char* range,
+                                  int*        n,
+                                  double*     d,
+                                  double*     e,
+                                  double*     vl,
+                                  double*     vu,
+                                  int*        il,
+                                  int*        iu,
+                                  double*     abstol,
+                                  int*        m,
+                                  double*     w,
+                                  double*     z,
+                                  int*        ldz,
+                                  int*        isuppz,
+                                  double*     work,
+                                  int*        lwork,
+                                  int*        iwork,
+                                  int*        liwork,
+                                  int*        info);
+
+    void F77_FUNC(dsytrd, DSYTRD)(const char* uplo,
+                                  int*        n,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     d,
+                                  double*     e,
+                                  double*     tau,
+                                  double*     work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(dsyevr, DSYEVR)(const char* jobz,
+                                  const char* range,
+                                  const char* uplo,
+                                  int*        n,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     vl,
+                                  double*     vu,
+                                  int*        il,
+                                  int*        iu,
+                                  double*     abstol,
+                                  int*        m,
+                                  double*     w,
+                                  double*     z__,
+                                  int*        ldz,
+                                  int*        isuppz,
+                                  double*     work,
+                                  int*        lwork,
+                                  int*        iwork,
+                                  int*        liwork,
+                                  int*        info);
+
+    void F77_FUNC(dormql, DORMQL)(const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     tau,
+                                  double*     c,
+                                  int*        ldc,
+                                  double*     work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(dormqr, DORMQR)(const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     tau,
+                                  double*     c,
+                                  int*        ldc,
+                                  double*     work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(dorgbr, DORGBR)(const char* vect,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     tau,
+                                  double*     work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(dlasq5, DLASQ5)(int*    i0,
+                                  int*    n0,
+                                  double* z,
+                                  int*    pp,
+                                  double* tau,
+                                  double* dmin,
+                                  double* dmin1,
+                                  double* dmin2,
+                                  double* dn,
+                                  double* dnm1,
+                                  double* dnm2,
+                                  int*    ieee);
+
+    void F77_FUNC(dlasd8, DLASD8)(int*    icompq,
+                                  int*    k,
+                                  double* d,
+                                  double* z,
+                                  double* vf,
+                                  double* vl,
+                                  double* difl,
+                                  double* difr,
+                                  int*    lddifr,
+                                  double* dsigma,
+                                  double* work,
+                                  int*    info);
+
+    void F77_FUNC(dlascl, DLASCL)(const char* type,
+                                  int*        kl,
+                                  int*        ku,
+                                  double*     cfrom,
+                                  double*     cto,
+                                  int*        m,
+                                  int*        n,
+                                  double*     a,
+                                  int*        lda,
+                                  int*        info);
+
+    void F77_FUNC(dlarft, DLARFT)(const char* direct,
+                                  const char* storev,
+                                  int*        n,
+                                  int*        k,
+                                  double*     v,
+                                  int*        ldv,
+                                  double*     tau,
+                                  double*     t,
+                                  int*        ldt);
+
+    void F77_FUNC(dlagts, DLAGTS)(int*    job,
+                                  int*    n,
+                                  double* a,
+                                  double* b,
+                                  double* c,
+                                  double* d,
+                                  int*    in,
+                                  double* y,
+                                  double* tol,
+                                  int*    info);
+
+    void F77_FUNC(dgesdd, DGESDD)(const char* jobz,
+                                  int*        m,
+                                  int*        n,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     s,
+                                  double*     u,
+                                  int*        ldu,
+                                  double*     vt,
+                                  int*        ldvt,
+                                  double*     work,
+                                  int*        lwork,
+                                  int*        iwork,
+                                  int*        info);
+
+    void F77_FUNC(dsytd2,
+                  DSYTD2)(const char* uplo, int* n, double* a, int* lda, double* d, double* e, double* tau, int* info);
+
+    void F77_FUNC(dormlq, DORMLQ)(const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  double*     a,
+                                  int*        lda,
+                                  double*     tau,
+                                  double*     c,
+                                  int*        ldc,
+                                  double*     work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(dorg2r,
+                  DORG2R)(int* m, int* n, int* k, double* a, int* lda, double* tau, double* work, int* info);
+
+    void F77_FUNC(dlasq4, DLASQ4)(int*    i0,
+                                  int*    n0,
+                                  double* z,
+                                  int*    pp,
+                                  int*    n0in,
+                                  double* dmin,
+                                  double* dmin1,
+                                  double* dmin2,
+                                  double* dn,
+                                  double* dn1,
+                                  double* dn2,
+                                  double* tau,
+                                  int*    ttype);
+
+    void F77_FUNC(dlasd7, DLASD7)(int*    icompq,
+                                  int*    nl,
+                                  int*    nr,
+                                  int*    sqre,
+                                  int*    k,
+                                  double* d,
+                                  double* z,
+                                  double* zw,
+                                  double* vf,
+                                  double* vfw,
+                                  double* vl,
+                                  double* vlw,
+                                  double* alpha,
+                                  double* beta,
+                                  double* dsigma,
+                                  int*    idx,
+                                  int*    idxp,
+                                  int*    idxq,
+                                  int*    perm,
+                                  int*    givptr,
+                                  int*    givcol,
+                                  int*    ldgcol,
+                                  double* givnum,
+                                  int*    ldgnum,
+                                  double* c,
+                                  double* s,
+                                  int*    info);
+
+    void F77_FUNC(dlas2, DLAS2)(double* f, double* g, double* h, double* ssmin, double* ssmax);
+
+    void F77_FUNC(dlarfg, DLARFG)(int* n, double* alpha, double* x, int* incx, double* tau);
+
+    void F77_FUNC(dlagtf, DLAGTF)(int*    n,
+                                  double* a,
+                                  double* lambda,
+                                  double* b,
+                                  double* c,
+                                  double* tol,
+                                  double* d,
+                                  int*    in,
+                                  int*    info);
+
+    void F77_FUNC(dgeqrf,
+                  DGEQRF)(int* m, int* n, double* a, int* lda, double* tau, double* work, int* lwork, int* info);
+
+
+    /* Single precision */
+
+    void F77_FUNC(sbdsdc, SBDSDC)(const char* uplo,
+                                  const char* compq,
+                                  int*        n,
+                                  float*      d,
+                                  float*      e,
+                                  float*      u,
+                                  int*        ldu,
+                                  float*      vt,
+                                  int*        ldvt,
+                                  float*      q,
+                                  int*        iq,
+                                  float*      work,
+                                  int*        iwork,
+                                  int*        info);
+
+    void F77_FUNC(sgetf2, SGETF2)(int* m, int* n, float* a, int* lda, int* ipiv, int* info);
+
+    void F77_FUNC(slamrg, SLAMRG)(int* n1, int* n2, float* a, int* dtrd1, int* dtrd2, int* index);
+
+    void F77_FUNC(slarnv, SLARNV)(int* idist, int* iseed, int* n, float* x);
+
+    void F77_FUNC(slasd0, SLASD0)(int*   n,
+                                  int*   sqre,
+                                  float* d,
+                                  float* e,
+                                  float* u,
+                                  int*   ldu,
+                                  float* vt,
+                                  int*   ldvt,
+                                  int*   smlsiz,
+                                  int*   iwork,
+                                  float* work,
+                                  int*   info);
+
+    void F77_FUNC(slasda, SLASDA)(int*   icompq,
+                                  int*   smlsiz,
+                                  int*   n,
+                                  int*   sqre,
+                                  float* d,
+                                  float* e,
+                                  float* u,
+                                  int*   ldu,
+                                  float* vt,
+                                  int*   k,
+                                  float* difl,
+                                  float* difr,
+                                  float* z,
+                                  float* poles,
+                                  int*   givptr,
+                                  int*   givcol,
+                                  int*   ldgcol,
+                                  int*   perm,
+                                  float* givnum,
+                                  float* c,
+                                  float* s,
+                                  float* work,
+                                  int*   iwork,
+                                  int*   info);
+
+    void F77_FUNC(slasq6, SLASQ6)(int*   i0,
+                                  int*   n0,
+                                  float* z,
+                                  int*   pp,
+                                  float* dmin,
+                                  float* dmin1,
+                                  float* dmin2,
+                                  float* dn,
+                                  float* dnm1,
+                                  float* dnm2);
+
+    void F77_FUNC(sorgl2,
+                  SORGL2)(int* m, int* n, int* k, float* a, int* lda, float* tau, float* work, int* info);
+
+    void F77_FUNC(sbdsqr, SBDSQR)(const char* uplo,
+                                  int*        n,
+                                  int*        ncvt,
+                                  int*        nru,
+                                  int*        ncc,
+                                  float*      d,
+                                  float*      e,
+                                  float*      vt,
+                                  int*        ldvt,
+                                  float*      u,
+                                  int*        ldu,
+                                  float*      c,
+                                  int*        ldc,
+                                  float*      work,
+                                  int*        info);
+
+    void F77_FUNC(sgetrf, SGETRF)(int* m, int* n, float* a, int* lda, int* ipiv, int* info);
+
+    void F77_FUNC(sgetri, SGETRI)(int* n, float* a, int* lda, int* ipiv, float* work, int* lwork, int* info);
+
+    void F77_FUNC(sgetrs, SGETRS)(const char* trans,
+                                  int*        n,
+                                  int*        nrhs,
+                                  float*      a,
+                                  int*        lda,
+                                  int*        ipiv,
+                                  float*      b,
+                                  int*        ldb,
+                                  int*        info);
+
+    void F77_FUNC(strtri, STRTRI)(const char* uplo, const char* diag, int* n, float* a, int* lda, int* info);
+
+    void F77_FUNC(strti2, STRTI2)(const char* uplo, const char* diag, int* n, float* a, int* lda, int* info);
+
+    float F77_FUNC(slange, SLANGE)(const char* norm, int* m, int* n, float* a, int* lda, float* work);
+
+    void F77_FUNC(slarrbx, SLARRBX)(int*   n,
+                                    float* d,
+                                    float* l,
+                                    float* ld,
+                                    float* lld,
+                                    int*   ifirst,
+                                    int*   ilast,
+                                    float* rtol1,
+                                    float* rtol2,
+                                    int*   offset,
+                                    float* w,
+                                    float* wgap,
+                                    float* werr,
+                                    float* work,
+                                    int*   iwork,
+                                    int*   info);
+
+    void F77_FUNC(slasd1, SLASD1)(int*   nl,
+                                  int*   nr,
+                                  int*   sqre,
+                                  float* d,
+                                  float* alpha,
+                                  float* beta,
+                                  float* u,
+                                  int*   ldu,
+                                  float* vt,
+                                  int*   ldvt,
+                                  int*   idxq,
+                                  int*   iwork,
+                                  float* work,
+                                  int*   info);
+
+    void F77_FUNC(slasdq, SLASDQ)(const char* uplo,
+                                  int*        sqre,
+                                  int*        n,
+                                  int*        ncvt,
+                                  int*        nru,
+                                  int*        ncc,
+                                  float*      d,
+                                  float*      e,
+                                  float*      vt,
+                                  int*        ldvt,
+                                  float*      u,
+                                  int*        ldu,
+                                  float*      c,
+                                  int*        ldc,
+                                  float*      work,
+                                  int*        info);
+
+    void F77_FUNC(slasr, SLASR)(const char* side,
+                                const char* pivot,
+                                const char* direct,
+                                int*        m,
+                                int*        n,
+                                float*      c,
+                                float*      s,
+                                float*      a,
+                                int*        lda);
+
+    void F77_FUNC(sorglq,
+                  SORGLQ)(int* m, int* n, int* k, float* a, int* lda, float* tau, float* work, int* lwork, int* info);
+
+    void F77_FUNC(sormtr, SORMTR)(const char* side,
+                                  const char* uplo,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      tau,
+                                  float*      c,
+                                  int*        ldc,
+                                  float*      work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(sgebd2, SGEBD2)(int*   m,
+                                  int*   n,
+                                  float* a,
+                                  int*   lda,
+                                  float* d,
+                                  float* e,
+                                  float* tauq,
+                                  float* taup,
+                                  float* work,
+                                  int*   info);
+
+    void F77_FUNC(slabrd, SLABRD)(int*   m,
+                                  int*   n,
+                                  int*   nb,
+                                  float* a,
+                                  int*   lda,
+                                  float* d,
+                                  float* e,
+                                  float* tauq,
+                                  float* taup,
+                                  float* x,
+                                  int*   ldx,
+                                  float* y,
+                                  int*   ldy);
+
+    float F77_FUNC(slanst, SLANST)(const char* norm, int* n, float* d, float* e);
+
+    float F77_FUNC(slansy,
+                   SLANSY)(const char* norm, const char* uplo, int* n, float* a, int* lda, float* work);
+
+    void F77_FUNC(slarrex, SLARREX)(const char* range,
+                                    int*        n,
+                                    float*      vl,
+                                    float*      vu,
+                                    int*        il,
+                                    int*        iu,
+                                    float*      d,
+                                    float*      e,
+                                    float*      tol,
+                                    int*        nsplit,
+                                    int*        isplit,
+                                    int*        m,
+                                    float*      w,
+                                    int*        iblock,
+                                    int*        indexw,
+                                    float*      gersch,
+                                    float*      work,
+                                    int*        iwork,
+                                    int*        info);
+
+    void F77_FUNC(slasd2, SLASD2)(int*   nl,
+                                  int*   nr,
+                                  int*   sqre,
+                                  int*   k,
+                                  float* d,
+                                  float* z,
+                                  float* alpha,
+                                  float* beta,
+                                  float* u,
+                                  int*   ldu,
+                                  float* vt,
+                                  int*   ldvt,
+                                  float* dsigma,
+                                  float* u2,
+                                  int*   ldu2,
+                                  float* vt2,
+                                  int*   ldvt2,
+                                  int*   idxp,
+                                  int*   idx,
+                                  int*   idxc,
+                                  int*   idxq,
+                                  int*   coltyp,
+                                  int*   info);
+
+    void F77_FUNC(slasdt, SLASDT)(int* n, int* lvl, int* nd, int* inode, int* ndiml, int* ndimr, int* msub);
+
+    void F77_FUNC(slasrt, SLASRT)(const char* id, int* n, float* d, int* info);
+
+    void F77_FUNC(slasrt2, SLASRT2)(const char* id, int* n, float* d, int* key, int* info);
+
+    void F77_FUNC(sorgqr,
+                  SORGQR)(int* m, int* n, int* k, float* a, int* lda, float* tau, float* work, int* lwork, int* info);
+
+    void F77_FUNC(sstebz, SSTEBZ)(const char* range,
+                                  const char* order,
+                                  int*        n,
+                                  float*      vl,
+                                  float*      vu,
+                                  int*        il,
+                                  int*        iu,
+                                  float*      abstol,
+                                  float*      d,
+                                  float*      e,
+                                  int*        m,
+                                  int*        nsplit,
+                                  float*      w,
+                                  int*        iblock,
+                                  int*        isplit,
+                                  float*      work,
+                                  int*        iwork,
+                                  int*        info);
+
+    void F77_FUNC(sgebrd, SGEBRD)(int*   m,
+                                  int*   n,
+                                  float* a,
+                                  int*   lda,
+                                  float* d,
+                                  float* e,
+                                  float* tauq,
+                                  float* taup,
+                                  float* work,
+                                  int*   lwork,
+                                  int*   info);
+
+    void F77_FUNC(slacpy, SLACPY)(const char* uplo, int* m, int* n, float* a, int* lda, float* b, int* ldb);
+
+    float F77_FUNC(slapy2, SLAPY2)(float* x, float* y);
+
+    void F77_FUNC(slarrfx, SLARRFX)(int*   n,
+                                    float* d,
+                                    float* l,
+                                    float* ld,
+                                    float* lld,
+                                    int*   ifirst,
+                                    int*   ilast,
+                                    float* w,
+                                    float* sigma,
+                                    float* dplus,
+                                    float* lplus,
+                                    float* work,
+                                    int*   info);
+
+    void F77_FUNC(slasd3, SLASD3)(int*   nl,
+                                  int*   nr,
+                                  int*   sqre,
+                                  int*   k,
+                                  float* d,
+                                  float* q,
+                                  int*   ldq,
+                                  float* dsigma,
+                                  float* u,
+                                  int*   ldu,
+                                  float* u2,
+                                  int*   ldu2,
+                                  float* vt,
+                                  int*   ldvt,
+                                  float* vt2,
+                                  int*   ldvt2,
+                                  int*   idxc,
+                                  int*   ctot,
+                                  float* z,
+                                  int*   info);
+
+    void F77_FUNC(slaset,
+                  SLASET)(const char* uplo, int* m, int* n, float* alpha, float* beta, float* a, int* lda);
+
+    void F77_FUNC(slassq, SLASSQ)(int* n, float* x, int* incx, float* scale, float* sumsq);
+
+    void F77_FUNC(sorm2l, SORM2L)(const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      tau,
+                                  float*      c,
+                                  int*        ldc,
+                                  float*      work,
+                                  int*        info);
+
+    void F77_FUNC(sstegr, SSTEGR)(const char* jobz,
+                                  const char* range,
+                                  int*        n,
+                                  float*      d,
+                                  float*      e,
+                                  float*      vl,
+                                  float*      vu,
+                                  int*        il,
+                                  int*        iu,
+                                  float*      abstol,
+                                  int*        m,
+                                  float*      w,
+                                  float*      z,
+                                  int*        ldz,
+                                  int*        isuppz,
+                                  float*      work,
+                                  int*        lwork,
+                                  int*        iwork,
+                                  int*        liwork,
+                                  int*        info);
+
+    void F77_FUNC(sgelq2, SGELQ2)(int* m, int* n, float* a, int* lda, float* tau, float* work, int* info);
+
+    void F77_FUNC(slae2, SLAE2)(float* a, float* b, float* c, float* rt1, float* rt2);
+
+    void F77_FUNC(slaev2,
+                  SLAEV2)(float* a, float* b, float* c, float* rt1, float* rt2, float* cs1, float* cs2);
+
+    void F77_FUNC(slar1vx, SLAR1VX)(int*   n,
+                                    int*   b1,
+                                    int*   bn,
+                                    float* sigma,
+                                    float* d,
+                                    float* l,
+                                    float* ld,
+                                    float* lld,
+                                    float* eval,
+                                    float* gersch,
+                                    float* z,
+                                    float* ztz,
+                                    float* mingma,
+                                    int*   r,
+                                    int*   isuppz,
+                                    float* work);
+
+    void F77_FUNC(slarrvx, SLARRVX)(int*   n,
+                                    float* d,
+                                    float* l,
+                                    int*   isplit,
+                                    int*   m,
+                                    float* w,
+                                    int*   iblock,
+                                    int*   indexw,
+                                    float* gersch,
+                                    float* tol,
+                                    float* z,
+                                    int*   ldz,
+                                    int*   isuppz,
+                                    float* work,
+                                    int*   iwork,
+                                    int*   info);
+
+    void F77_FUNC(slasd4, SLASD4)(int*   n,
+                                  int*   i,
+                                  float* d,
+                                  float* z,
+                                  float* delta,
+                                  float* rho,
+                                  float* sigma,
+                                  float* work,
+                                  int*   info);
+
+    void F77_FUNC(slasq1, SLASQ1)(int* n, float* d, float* e, float* work, int* info);
+
+
+    void F77_FUNC(slasv2, SLASV2)(float* f,
+                                  float* g,
+                                  float* h,
+                                  float* ssmin,
+                                  float* ssmax,
+                                  float* snr,
+                                  float* csr,
+                                  float* snl,
+                                  float* csl);
+
+    void F77_FUNC(sorm2r, SORM2R)(const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      tau,
+                                  float*      c,
+                                  int*        ldc,
+                                  float*      work,
+                                  int*        info);
+
+    void F77_FUNC(sstein, SSTEIN)(int*   n,
+                                  float* d,
+                                  float* e,
+                                  int*   m,
+                                  float* w,
+                                  int*   iblock,
+                                  int*   isplit,
+                                  float* z,
+                                  int*   ldz,
+                                  float* work,
+                                  int*   iwork,
+                                  int*   ifail,
+                                  int*   info);
+
+    void F77_FUNC(sgelqf,
+                  SGELQF)(int* m, int* n, float* a, int* lda, float* tau, float* work, int* lwork, int* info);
+
+    void F77_FUNC(slaebz, SLAEBZ)(int*   ijob,
+                                  int*   nitmax,
+                                  int*   n,
+                                  int*   mmax,
+                                  int*   minp,
+                                  int*   nbmin,
+                                  float* abstol,
+                                  float* reltol,
+                                  float* pivmin,
+                                  float* d,
+                                  float* e,
+                                  float* e2,
+                                  int*   nval,
+                                  float* ab,
+                                  float* c,
+                                  int*   mout,
+                                  int*   nab,
+                                  float* work,
+                                  int*   iwork,
+                                  int*   info);
+
+    void F77_FUNC(slarf,
+                  SLARF)(const char* side, int* m, int* n, float* v, int* incv, float* tau, float* c, int* ldc, float* work);
+
+    void F77_FUNC(slartg, SLARTG)(float* f, float* g, float* cs, float* sn, float* r);
+
+    void F77_FUNC(slasd5,
+                  SLASD5)(int* i, float* d, float* z, float* delta, float* rho, float* dsigma, float* work);
+
+    void F77_FUNC(slasq2, SLASQ2)(int* n, float* z, int* info);
+
+    void F77_FUNC(slasq3, SLASQ3)(int*   i0,
+                                  int*   n0,
+                                  float* z,
+                                  int*   pp,
+                                  float* dmin,
+                                  float* sigma,
+                                  float* desig,
+                                  float* qmax,
+                                  int*   nfail,
+                                  int*   iter,
+                                  int*   ndiv,
+                                  int*   ieee);
+
+    void F77_FUNC(slaswp, SLASWP)(int* n, float* a, int* lda, int* k1, int* k2, int* ipiv, int* incx);
+
+    void F77_FUNC(sormbr, SORMBR)(const char* vect,
+                                  const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      tau,
+                                  float*      c,
+                                  int*        ldc,
+                                  float*      work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(ssterf, SSTERF)(int* n, float* d, float* e, int* info);
+
+    void F77_FUNC(sgeqr2, SGEQR2)(int* m, int* n, float* a, int* lda, float* tau, float* work, int* info);
+
+    void F77_FUNC(slaed6,
+                  SLAED6)(int* kniter, int* orgati, float* rho, float* d, float* z, float* finit, float* tau, int* info);
+
+    void F77_FUNC(slarfb, SLARFB)(const char* side,
+                                  const char* trans,
+                                  const char* direct,
+                                  const char* storev,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  float*      v,
+                                  int*        ldv,
+                                  float*      t,
+                                  int*        ldt,
+                                  float*      c,
+                                  int*        ldc,
+                                  float*      work,
+                                  int*        ldwork);
+
+    void F77_FUNC(slaruv, SLARUV)(int* iseed, int* n, float* x);
+
+    void F77_FUNC(slasd6, SLASD6)(int*   icompq,
+                                  int*   nl,
+                                  int*   nr,
+                                  int*   sqre,
+                                  float* d,
+                                  float* vf,
+                                  float* vl,
+                                  float* alpha,
+                                  float* beta,
+                                  int*   idxq,
+                                  int*   perm,
+                                  int*   givptr,
+                                  int*   givcol,
+                                  int*   ldgcol,
+                                  float* givnum,
+                                  int*   ldgnum,
+                                  float* poles,
+                                  float* difl,
+                                  float* difr,
+                                  float* z,
+                                  int*   k,
+                                  float* c,
+                                  float* s,
+                                  float* work,
+                                  int*   iwork,
+                                  int*   info);
+
+    void F77_FUNC(slatrd,
+                  SLATRD)(const char* uplo, int* n, int* nb, float* a, int* lda, float* e, float* tau, float* w, int* ldw);
+
+    void F77_FUNC(sorml2, SORML2)(const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      tau,
+                                  float*      c,
+                                  int*        ldc,
+                                  float*      work,
+                                  int*        info);
+
+    void F77_FUNC(sstevr, SSTEVR)(const char* jobz,
+                                  const char* range,
+                                  int*        n,
+                                  float*      d,
+                                  float*      e,
+                                  float*      vl,
+                                  float*      vu,
+                                  int*        il,
+                                  int*        iu,
+                                  float*      abstol,
+                                  int*        m,
+                                  float*      w,
+                                  float*      z,
+                                  int*        ldz,
+                                  int*        isuppz,
+                                  float*      work,
+                                  int*        lwork,
+                                  int*        iwork,
+                                  int*        liwork,
+                                  int*        info);
+
+    void F77_FUNC(ssytrd, SSYTRD)(const char* uplo,
+                                  int*        n,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      d,
+                                  float*      e,
+                                  float*      tau,
+                                  float*      work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(ssyevr, SSYEVR)(const char* jobz,
+                                  const char* range,
+                                  const char* uplo,
+                                  int*        n,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      vl,
+                                  float*      vu,
+                                  int*        il,
+                                  int*        iu,
+                                  float*      abstol,
+                                  int*        m,
+                                  float*      w,
+                                  float*      z__,
+                                  int*        ldz,
+                                  int*        isuppz,
+                                  float*      work,
+                                  int*        lwork,
+                                  int*        iwork,
+                                  int*        liwork,
+                                  int*        info);
+
+    void F77_FUNC(sormql, SORMQL)(const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      tau,
+                                  float*      c,
+                                  int*        ldc,
+                                  float*      work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(sormqr, SORMQR)(const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      tau,
+                                  float*      c,
+                                  int*        ldc,
+                                  float*      work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(sorgbr, SORGBR)(const char* vect,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      tau,
+                                  float*      work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(slasq5, SLASQ5)(int*   i0,
+                                  int*   n0,
+                                  float* z,
+                                  int*   pp,
+                                  float* tau,
+                                  float* dmin,
+                                  float* dmin1,
+                                  float* dmin2,
+                                  float* dn,
+                                  float* dnm1,
+                                  float* dnm2,
+                                  int*   ieee);
+
+    void F77_FUNC(slasd8, SLASD8)(int*   icompq,
+                                  int*   k,
+                                  float* d,
+                                  float* z,
+                                  float* vf,
+                                  float* vl,
+                                  float* difl,
+                                  float* difr,
+                                  int*   lddifr,
+                                  float* dsigma,
+                                  float* work,
+                                  int*   info);
+
+    void F77_FUNC(slascl, SLASCL)(const char* type,
+                                  int*        kl,
+                                  int*        ku,
+                                  float*      cfrom,
+                                  float*      cto,
+                                  int*        m,
+                                  int*        n,
+                                  float*      a,
+                                  int*        lda,
+                                  int*        info);
+
+    void F77_FUNC(slarft, SLARFT)(const char* direct,
+                                  const char* storev,
+                                  int*        n,
+                                  int*        k,
+                                  float*      v,
+                                  int*        ldv,
+                                  float*      tau,
+                                  float*      t,
+                                  int*        ldt);
+
+    void F77_FUNC(slagts,
+                  SLAGTS)(int* job, int* n, float* a, float* b, float* c, float* d, int* in, float* y, float* tol, int* info);
+
+    void F77_FUNC(sgesdd, SGESDD)(const char* jobz,
+                                  int*        m,
+                                  int*        n,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      s,
+                                  float*      u,
+                                  int*        ldu,
+                                  float*      vt,
+                                  int*        ldvt,
+                                  float*      work,
+                                  int*        lwork,
+                                  int*        iwork,
+                                  int*        info);
+
+    void F77_FUNC(ssytd2,
+                  SSYTD2)(const char* uplo, int* n, float* a, int* lda, float* d, float* e, float* tau, int* info);
+
+    void F77_FUNC(sormlq, SORMLQ)(const char* side,
+                                  const char* trans,
+                                  int*        m,
+                                  int*        n,
+                                  int*        k,
+                                  float*      a,
+                                  int*        lda,
+                                  float*      tau,
+                                  float*      c,
+                                  int*        ldc,
+                                  float*      work,
+                                  int*        lwork,
+                                  int*        info);
+
+    void F77_FUNC(sorg2r,
+                  SORG2R)(int* m, int* n, int* k, float* a, int* lda, float* tau, float* work, int* info);
+
+    void F77_FUNC(slasq4, SLASQ4)(int*   i0,
+                                  int*   n0,
+                                  float* z,
+                                  int*   pp,
+                                  int*   n0in,
+                                  float* dmin,
+                                  float* dmin1,
+                                  float* dmin2,
+                                  float* dn,
+                                  float* dn1,
+                                  float* dn2,
+                                  float* tau,
+                                  int*   ttype);
+
+    void F77_FUNC(slasd7, SLASD7)(int*   icompq,
+                                  int*   nl,
+                                  int*   nr,
+                                  int*   sqre,
+                                  int*   k,
+                                  float* d,
+                                  float* z,
+                                  float* zw,
+                                  float* vf,
+                                  float* vfw,
+                                  float* vl,
+                                  float* vlw,
+                                  float* alpha,
+                                  float* beta,
+                                  float* dsigma,
+                                  int*   idx,
+                                  int*   idxp,
+                                  int*   idxq,
+                                  int*   perm,
+                                  int*   givptr,
+                                  int*   givcol,
+                                  int*   ldgcol,
+                                  float* givnum,
+                                  int*   ldgnum,
+                                  float* c,
+                                  float* s,
+                                  int*   info);
+
+    void F77_FUNC(slas2, SLAS2)(float* f, float* g, float* h, float* ssmin, float* ssmax);
+
+    void F77_FUNC(slarfg, SLARFG)(int* n, float* alpha, float* x, int* incx, float* tau);
+
+    void F77_FUNC(slagtf,
+                  SLAGTF)(int* n, float* a, float* lambda, float* b, float* c, float* tol, float* d, int* in, int* info);
+
+    void F77_FUNC(sgeqrf,
+                  SGEQRF)(int* m, int* n, float* a, int* lda, float* tau, float* work, int* lwork, int* info);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+/*! \endcond */
+
+#endif /* GMX_LAPACK_H */
diff --git a/src/include/gromacs/linearalgebra/gmx_lapack/lapack_limits.h b/src/include/gromacs/linearalgebra/gmx_lapack/lapack_limits.h
new file mode 100644 (file)
index 0000000..5e34a24
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef _LAPACK_LIMITS_H_
+#define _LAPACK_LIMITS_H_
+
+#define DSTEBZ_BLOCKSIZE  1
+
+#define DORGBR_BLOCKSIZE    32
+#define DORGBR_MINBLOCKSIZE 2
+#define DORGBR_CROSSOVER    128
+
+#define DGEQRF_BLOCKSIZE    32
+#define DGEQRF_MINBLOCKSIZE 2  
+#define DGEQRF_CROSSOVER    128
+
+#define DORGQR_BLOCKSIZE    32
+#define DORGQR_MINBLOCKSIZE 2
+#define DORGQR_CROSSOVER    128
+
+#define DORMLQ_BLOCKSIZE    32
+#define DORMLQ_MINBLOCKSIZE 2
+#define DORMLQ_CROSSOVER    128
+
+#define DORMQL_BLOCKSIZE    32
+#define DORMQL_MINBLOCKSIZE 2
+#define DORMQL_CROSSOVER    128
+
+#define DSYTRD_BLOCKSIZE    32
+#define DSYTRD_MINBLOCKSIZE 2
+#define DSYTRD_CROSSOVER    128
+
+#define DGEBRD_BLOCKSIZE    32
+#define DGEBRD_MINBLOCKSIZE 2
+#define DGEBRD_CROSSOVER    128
+
+#define DORMQR_BLOCKSIZE    32
+#define DORMQR_MINBLOCKSIZE 2
+#define DORMQR_CROSSOVER    128
+
+#define DGELQF_BLOCKSIZE    32
+#define DGELQF_MINBLOCKSIZE 2  
+#define DGELQF_CROSSOVER    128
+
+#define DGETRF_BLOCKSIZE    64
+#define DGETRF_MINBLOCKSIZE 2
+
+#define DGETRI_BLOCKSIZE    64
+#define DGETRI_MINBLOCKSIZE 2
+
+#define DTRTRI_BLOCKSIZE    64
+
+#define DBDSDC_SMALLSIZE 25
+
+#define DBDSQR_MAXITR 6
+
+
+
+#endif /* _LAPACK_LIMITS_H_ */
diff --git a/src/include/gromacs/linearalgebra/matrix.h b/src/include/gromacs/linearalgebra/matrix.h
new file mode 100644 (file)
index 0000000..43b37ca
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2012,2013,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_LINEARALGEBRA_MATRIX_H
+#define GMX_LINEARALGEBRA_MATRIX_H
+
+#include <stdio.h>
+
+double** alloc_matrix(int n, int m);
+
+void free_matrix(double** a);
+
+void matrix_multiply(FILE* fp, int n, int m, double** x, double** y, double** z);
+
+/* Return 0 if OK or row number where inversion failed otherwise. */
+int matrix_invert(FILE* fp, int n, double** a);
+
+double multi_regression(FILE* fp, int ny, double* y, int nx, double** xx, double* a0);
+/* Perform a regression analysis to fit
+ * y' = a0[0] xx[0] + a0[1] xx[1] ... + a0[nx-1] xx[nx-1]
+ * with ny data points in each vector.
+ * The coefficients are returned in vector a0.
+ * The return value of the function is the chi2 value:
+ * sum_{j=0}^{ny-1} (y[j] - y'[j])^2
+ * If fp is not NULL debug information will be written to it.
+ */
+
+#endif
diff --git a/src/include/gromacs/linearalgebra/nrjac.h b/src/include/gromacs/linearalgebra/nrjac.h
new file mode 100644 (file)
index 0000000..82bbb51
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2017,2018,2019 by the GROMACS development team.
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_LINEARALGEBRA_NRJAC_H
+#define GMX_LINEARALGEBRA_NRJAC_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+/* Diagonalizes a symmetric matrix
+ *
+ * \param[in,out] a           Input matrix a[0..n-1][0..n-1] must be symmetric, gets modified
+ * \param[in]  numDimensions  Number of rows and columns
+ * \param[out] eigenvalues    eigenvalues[0]..eigenvalues[n-1] are the eigenvalues of a
+ * \param[out] eigenvectors   v[0..n-1][0..n-1] the eigenvectors: v[i][j] is component i of vector j
+ * \param[out] numRotations   The number of jacobi rotations, can be nullptr
+ */
+void jacobi(double** a, int numDimensions, double* eigenvalues, double** eigenvectors, int* numRotations);
+
+/* Like jacobi above, but specialized for n=3
+ *
+ * \param[in,out] a  The symmetric matrix to diagonalize, size 3, note that the contents gets modified
+ * \param[out] eigenvalues  The eigenvalues, size 3
+ * \param[out] eigenvectors The eigenvectors, size 3
+
+ * Returns the number of jacobi rotations.
+ */
+int jacobi(gmx::ArrayRef<gmx::DVec> a, gmx::ArrayRef<double> eigenvalues, gmx::ArrayRef<gmx::DVec> eigenvectors);
+
+int m_inv_gen(real* m, int n, real* minv);
+/* Produces minv, a generalized inverse of m, both stored as linear arrays.
+ * Inversion is done via diagonalization,
+ * eigenvalues smaller than 1e-6 times the average diagonal element
+ * are assumed to be zero.
+ * For zero eigenvalues 1/eigenvalue is set to zero for the inverse matrix.
+ * Returns the number of zero eigenvalues.
+ */
+
+#endif
diff --git a/src/include/gromacs/linearalgebra/sparsematrix.h b/src/include/gromacs/linearalgebra/sparsematrix.h
new file mode 100644 (file)
index 0000000..15cec63
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_LINEARALGEBRA_SPARSEMATRIX_H
+#define GMX_LINEARALGEBRA_SPARSEMATRIX_H
+
+#include <stdio.h>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+typedef struct gmx_sparsematrix_entry
+{
+    int  col;
+    real value;
+} gmx_sparsematrix_entry_t;
+
+/*! \brief Sparse matrix storage format
+ *
+ *  This structure specifies a storage format for a sparse matrix.
+ *  The memory requirements are only proportional to the number
+ *  of nonzero elements, and it provides a reasonably fast way to
+ *  perform matrix-vector multiplications.
+ *
+ *  The data format is very similar to a neighborlist. It is optimized
+ *  for fast access, but it is difficult to add entries. If you are
+ *  constructing a matrix you should either do it in exactly the order
+ *  specified here, or use some other more flexible intermediate structure.
+ *
+ *  The index array is of size nrow+1. All non-zero matrix elements
+ *  on row i are stored in positions index[i] through index[i+1]-1 in
+ *  the arrays column and value. The column array contains the column
+ *  index for each entry, in ascending order, and the corresponding
+ *  position in the value array contains the floating point matrix element.
+ *
+ *  index[nrow] should be equal to the total number of elements stored.
+ *
+ *  Thus, to find the value of matrix element [5,4] you should loop
+ *  over positions index[5] to index[6]-1 in column until you either find
+ *  the value 4, or a higher value (meaning the element was zero).
+ *
+ *  It is fairly easy to construct the matrix on-the-fly if you can do
+ *  it row-by-row.
+ *
+ *  IMPORTANT:
+ *  If compressed_symmetric is set to TRUE, you should only store EITHER the upper OR
+ *  lower triangle (and the diagonal), and the other half is assumed to be
+ *  symmetric. Otherwise, if compressed_symmetric==FALSE, no symmetry is implied and all
+ *  elements should be stored.
+ *
+ *  The symmetry compression saves us a factor 2 both in storage and
+ *  matrix multiplication CPU-time, which can be very useful for huge eigenproblems.
+ *
+ *  If you are unsure, just set compressed_symmetric to FALSE and list all elements. If
+ *  you enable it but still list all elements (both upper and lower triangle) you will be sorry...
+ *
+ *  Internally, the sparse data is stored as a separate list for each row, where the list
+ *  element is a structure with a column and (floating-point) data value. This makes it
+ *  possible, although not completely transparent, to update values in random access order.
+ *  The drawback is that the structure will allocate nrow memory regions.
+ *  The matrix data could be stored in a single contiguous array with indices for each row,
+ *  but then we could only insert elements at the end without copying the entire matrix.
+ *
+ *  After you have
+ *
+ *  In other words: Not perfect, but it works.
+ */
+typedef struct gmx_sparsematrix
+{
+    gmx_bool compressed_symmetric;   /**< Store half elements and assume symmetry. */
+    int      nrow;                   /**< Number of rows in matrix                 */
+    int*     ndata;                  /**< Number of entries on each row (list)     */
+    int*     nalloc;                 /**< Allocated entry list length for each row */
+    gmx_sparsematrix_entry_t** data; /**< data[i] is a list with entries on row i  */
+} gmx_sparsematrix_t;
+
+
+/*! \brief Allocate a new sparse matrix structure
+ *
+ *  The number of rows is used to allocate the index array entry. Obviously you
+ *  can reallocate these later yourself if necessary - this is a
+ *  convenience routine.
+ *
+ *  By default, the compressed_symmetric flag in the structure will
+ *  be FALSE. Set it to TRUE manually if you are only storing either the
+ *  upper or lower half of the matrix.
+ */
+gmx_sparsematrix_t* gmx_sparsematrix_init(int nrow);
+
+
+/*! \brief Release all resources used by a sparse matrix structure
+ *
+ *  All arrays in the structure will be freed, and the structure itself.
+ */
+void gmx_sparsematrix_destroy(gmx_sparsematrix_t* A);
+
+
+/*! \brief Print sparse matrix to a stream.
+ *
+ *  Mainly used for debugging. Be warned that the real sparse matrices used
+ *  in Gromacs runs can be HUGE (think 100,000 rows).
+ */
+void gmx_sparsematrix_print(FILE* stream, gmx_sparsematrix_t* A);
+
+/* Adds value at row,col. If the value did not exist
+ * previously it is added, otherwise it is incremented with difference.
+ *
+ * The column sort order might change, so you need to run fix_sparsematrix
+ * once you are done changing the matrix.
+ */
+real gmx_sparsematrix_value(gmx_sparsematrix_t* A, int row, int col);
+
+
+/* Adds value at row,col. If the value did not exist
+ * previously it is added, otherwise it is incremented with difference.
+ *
+ * The column sort order might change, so you need to run fix_sparsematrix
+ * once you are done changing the matrix.
+ */
+void gmx_sparsematrix_increment_value(gmx_sparsematrix_t* A, int row, int col, real difference);
+
+
+/*! \brief Sort elements in each column and remove zeros.
+ *
+ *  Sparse matrix access is faster when the elements are stored in
+ *  increasing column order in each row. In some cases previously non-zero
+ *  elements will be zero after adding more data, and this routine also removes
+ *  those entries to reduce the storage requirements.
+ *
+ *  It never hurts to run this routine if you have been updating the matrix...
+ */
+void gmx_sparsematrix_compress(gmx_sparsematrix_t* A);
+
+
+/*! \brief Sparse matrix vector multiplication
+ *
+ * Calculate y = A * x for a sparse matrix A.
+ */
+void gmx_sparsematrix_vector_multiply(gmx_sparsematrix_t* A, real* x, real* y);
+
+
+#endif
diff --git a/src/include/gromacs/listed_forces/bonded.h b/src/include/gromacs/listed_forces/bonded.h
new file mode 100644 (file)
index 0000000..6347431
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file contains declarations necessary for low-level
+ * functions for computing energies and forces for bonded interactions.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_listed_forces
+ */
+
+#ifndef GMX_LISTED_FORCES_BONDED_H
+#define GMX_LISTED_FORCES_BONDED_H
+
+#include <string>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_cmap_t;
+struct t_fcdata;
+struct t_nrnb;
+struct t_pbc;
+struct t_disresdata;
+struct t_oriresdata;
+
+namespace gmx
+{
+template<typename EnumType, typename DataType, EnumType ArraySize>
+struct EnumerationArray;
+template<typename>
+class ArrayRef;
+} // namespace gmx
+
+/*! \brief Calculate bond-angle. No PBC is taken into account (use mol-shift) */
+real bond_angle(const rvec          xi,
+                const rvec          xj,
+                const rvec          xk,
+                const struct t_pbc* pbc,
+                rvec                r_ij,
+                rvec                r_kj,
+                real*               costh,
+                int*                t1,
+                int*                t2); /* out */
+
+/*! \brief Calculate dihedral-angle. No PBC is taken into account (use mol-shift) */
+real dih_angle(const rvec          xi,
+               const rvec          xj,
+               const rvec          xk,
+               const rvec          xl,
+               const struct t_pbc* pbc,
+               rvec                r_ij,
+               rvec                r_kj,
+               rvec                r_kl,
+               rvec                m,
+               rvec                n, /* out */
+               int*                t1,
+               int*                t2,
+               int*                t3);
+
+/*! \brief Do an update of the forces for dihedral potentials */
+void do_dih_fup(int                 i,
+                int                 j,
+                int                 k,
+                int                 l,
+                real                ddphi,
+                rvec                r_ij,
+                rvec                r_kj,
+                rvec                r_kl,
+                rvec                m,
+                rvec                n,
+                rvec4               f[],
+                rvec                fshift[],
+                const struct t_pbc* pbc,
+                const rvec*         x,
+                int                 t1,
+                int                 t2,
+                int                 t3);
+
+/*! \brief Make a dihedral fall in the range (-pi,pi) */
+void make_dp_periodic(real* dp);
+
+/*! \brief Compute CMAP dihedral energies and forces */
+real cmap_dihs(int                 nbonds,
+               const t_iatom       forceatoms[],
+               const t_iparams     forceparams[],
+               const gmx_cmap_t*   cmap_grid,
+               const rvec          x[],
+               rvec4               f[],
+               rvec                fshift[],
+               const struct t_pbc* pbc,
+               real gmx_unused     lambda,
+               real gmx_unused* dvdlambda,
+               gmx::ArrayRef<const real> /*charge*/,
+               t_fcdata gmx_unused* fcd,
+               t_disresdata gmx_unused* disresdata,
+               t_oriresdata gmx_unused* oriresdata,
+               int gmx_unused* global_atom_index);
+
+/*! \brief For selecting which flavor of bonded kernel is used for simple bonded types */
+enum class BondedKernelFlavor
+{
+    ForcesSimdWhenAvailable, //!< Compute only forces, use SIMD when available; should not be used with perturbed parameters
+    ForcesNoSimd,             //!< Compute only forces, do not use SIMD
+    ForcesAndVirialAndEnergy, //!< Compute forces, virial and energy (no SIMD)
+    ForcesAndEnergy,          //!< Compute forces and energy (no SIMD)
+    Count                     //!< The number of flavors
+};
+
+//! Helper strings for human-readable messages
+extern const gmx::EnumerationArray<BondedKernelFlavor, std::string, BondedKernelFlavor::Count> c_bondedKernelFlavorStrings;
+
+/*! \brief Returns whether the energy should be computed */
+static constexpr inline bool computeEnergy(const BondedKernelFlavor flavor)
+{
+    return (flavor == BondedKernelFlavor::ForcesAndVirialAndEnergy
+            || flavor == BondedKernelFlavor::ForcesAndEnergy);
+}
+
+/*! \brief Returns whether the virial should be computed */
+static constexpr inline bool computeVirial(const BondedKernelFlavor flavor)
+{
+    return (flavor == BondedKernelFlavor::ForcesAndVirialAndEnergy);
+}
+
+/*! \brief Returns whether the energy and/or virial should be computed */
+static constexpr inline bool computeEnergyOrVirial(const BondedKernelFlavor flavor)
+{
+    return (flavor == BondedKernelFlavor::ForcesAndVirialAndEnergy
+            || flavor == BondedKernelFlavor::ForcesAndEnergy);
+}
+
+/*! \brief Calculates bonded interactions for simple bonded types
+ *
+ * Exits with an error when the bonded type is not simple
+ * All pointers should be non-null, except for pbc and g which can be nullptr.
+ * \returns the energy or 0 when \p bondedKernelFlavor did not request the energy.
+ */
+real calculateSimpleBond(int                       ftype,
+                         int                       numForceatoms,
+                         const t_iatom             forceatoms[],
+                         const t_iparams           forceparams[],
+                         const rvec                x[],
+                         rvec4                     f[],
+                         rvec                      fshift[],
+                         const struct t_pbc*       pbc,
+                         real                      lambda,
+                         real*                     dvdlambda,
+                         gmx::ArrayRef<const real> charge,
+                         t_fcdata*                 fcd,
+                         t_disresdata*             disresdata,
+                         t_oriresdata*             oriresdata,
+                         int gmx_unused*    global_atom_index,
+                         BondedKernelFlavor bondedKernelFlavor);
+
+//! Getter for finding the flop count for an \c ftype interaction.
+int nrnbIndex(int ftype);
+
+#endif
diff --git a/src/include/gromacs/listed_forces/disre.h b/src/include/gromacs/listed_forces/disre.h
new file mode 100644 (file)
index 0000000..34aaba0
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares functions for handling distance restraints.
+ *
+ * \inlibraryapi
+ * \ingroup module_listed_forces
+ */
+#ifndef GMX_LISTED_FORCES_DISRE_H
+#define GMX_LISTED_FORCES_DISRE_H
+
+#include <cstdio>
+
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxmpi.h"
+
+struct gmx_mtop_t;
+struct gmx_multisim_t;
+class history_t;
+struct t_commrec;
+struct t_disresdata;
+struct t_oriresdata;
+struct t_fcdata;
+struct t_inputrec;
+struct t_pbc;
+class t_state;
+enum class DDRole;
+enum class NumRanks;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+} // namespace gmx
+
+//! Whether distance restraints are called from mdrun or from an analysis tool
+enum class DisResRunMode
+{
+    MDRun,
+    AnalysisTool
+};
+
+/*! \brief
+ * Initiates *disresdata.
+ *
+ * Must be called once, nbonds is the number
+ * of iatoms in the ilist of the idef struct.
+ * When time averaging is used, the history is initialized in state,
+ * unless it was read before from a checkpoint file.
+ * The implementation of distance restraints with -multidir
+ * must differ according to whether REMD is active.
+ */
+void init_disres(FILE*                 fplog,
+                 const gmx_mtop_t&     mtop,
+                 t_inputrec*           ir,
+                 DisResRunMode         disResRunMode,
+                 DDRole                ddRole,
+                 NumRanks              numRanks,
+                 MPI_Comm              communicator,
+                 const gmx_multisim_t* ms,
+                 t_disresdata*         disresdata,
+                 t_state*              state,
+                 gmx_bool              bIsREMD);
+
+/*! \brief
+ * Calculates r and r^-3 (inst. and time averaged) for all pairs
+ * and the ensemble averaged r^-6 (inst. and time averaged) for all restraints
+ */
+void calc_disres_R_6(const t_commrec*      cr,
+                     const gmx_multisim_t* ms,
+                     int                   nfa,
+                     const t_iatom*        fa,
+                     const rvec*           x,
+                     const t_pbc*          pbc,
+                     t_disresdata*         disresdata,
+                     const history_t*      hist);
+
+//! Calculates the distance restraint forces, return the potential.
+real ta_disres(int                       nfa,
+               const t_iatom*            forceatoms,
+               const t_iparams*          ip,
+               const rvec*               x,
+               rvec4*                    f,
+               rvec*                     fshift,
+               const t_pbc*              pbc,
+               real                      lambda,
+               real*                     dvdlambda,
+               gmx::ArrayRef<const real> charge,
+               t_fcdata gmx_unused* fcd,
+               t_disresdata*        disresdata,
+               t_oriresdata gmx_unused* oriresdata,
+               int*                     global_atom_index);
+
+//! Copies the new time averages that have been calculated in calc_disres_R_6.
+void update_disres_history(const t_disresdata& disresdata, history_t* hist);
+
+#endif
diff --git a/src/include/gromacs/listed_forces/listed_forces.h b/src/include/gromacs/listed_forces/listed_forces.h
new file mode 100644 (file)
index 0000000..6d531ea
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \defgroup module_listed_forces Interactions between lists of particles
+ * \ingroup group_mdrun
+ *
+ * \brief Handles computing energies and forces for listed
+ * interactions.
+ *
+ * Located here is the code for
+ * - computing energies and forces for interactions between a small
+     number of particles, e.g bonds, position restraints and listed
+     non-bonded interactions (e.g. 1-4).
+ * - high-level functions used by mdrun for computing a set of such
+     quantities
+ * - managing thread-wise decomposition, thread-local buffer output,
+     and reduction of output data across threads.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Berk Hess <hess@kth.se>
+ *
+ */
+/*! \libinternal \file
+ *
+ * \brief This file contains declarations of high-level functions used
+ * by mdrun to compute energies and forces for listed interactions.
+ *
+ * Clients of libgromacs that want to evaluate listed interactions
+ * should call functions declared here.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Berk Hess <hess@kth.se>
+ *
+ * \inlibraryapi
+ * \ingroup module_listed_forces
+ */
+#ifndef GMX_LISTED_FORCES_LISTED_FORCES_H
+#define GMX_LISTED_FORCES_LISTED_FORCES_H
+
+#include <memory>
+#include <vector>
+
+#include <bitset>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/utility/classhelpers.h"
+
+struct bonded_threading_t;
+struct gmx_enerdata_t;
+struct gmx_ffparams_t;
+struct gmx_grppairener_t;
+struct gmx_localtop_t;
+struct gmx_multisim_t;
+class history_t;
+struct t_commrec;
+struct t_fcdata;
+struct t_forcerec;
+struct t_lambda;
+struct t_mdatoms;
+struct t_nrnb;
+class t_state;
+struct t_disresdata;
+struct t_oriresdata;
+
+namespace gmx
+{
+class ForceOutputs;
+class StepWorkload;
+template<typename>
+class ArrayRef;
+template<typename>
+class ArrayRefWithPadding;
+} // namespace gmx
+
+//! Type of CPU function to compute a bonded interaction.
+using BondedFunction = real (*)(int                 nbonds,
+                                const t_iatom       iatoms[],
+                                const t_iparams     iparams[],
+                                const rvec          x[],
+                                rvec4               f[],
+                                rvec                fshift[],
+                                const t_pbc*        pbc,
+                                real                lambda,
+                                gmx::ArrayRef<real> dvdlambda,
+                                const t_mdatoms*    md,
+                                t_fcdata*           fcd,
+                                t_disresdata*       disresdata,
+                                t_oriresdata*       oriresdata,
+                                int*                ddgatindex);
+
+//! Getter for finding a callable CPU function to compute an \c ftype interaction.
+BondedFunction bondedFunction(int ftype);
+
+/*! \libinternal
+ * \brief Class for calculating listed interactions, uses OpenMP parallelization
+ *
+ * Listed interactions can be divided over multiple instances of ListedForces
+ * using the selection flags passed to the constructor.
+ */
+class ListedForces
+{
+public:
+    //! Enum for selecting groups of listed interaction types
+    enum class InteractionGroup : int
+    {
+        Pairs,     //!< Pair interactions
+        Dihedrals, //!< Dihedrals, including cmap
+        Angles,    //!< Angles
+        Rest,      //!< All listed interactions that are not any of the above
+        Count      //!< The number of items above
+    };
+
+    //! Type for specifying selections of groups of interaction types
+    using InteractionSelection = std::bitset<static_cast<int>(InteractionGroup::Count)>;
+
+    //! Returns a selection with all listed interaction types selected
+    static InteractionSelection interactionSelectionAll()
+    {
+        InteractionSelection is;
+        return is.flip();
+    }
+
+    /*! \brief Constructor
+     *
+     * \param[in] ffparams         The force field parameters
+     * \param[in] numEnergyGroups  The number of energy groups, used for storage of pair energies
+     * \param[in] numThreads       The number of threads used for computed listed interactions
+     * \param[in] interactionSelection  Select of interaction groups through bits set
+     * \param[in] fplog            Log file for printing env.var. override, can be nullptr
+     */
+    ListedForces(const gmx_ffparams_t& ffparams,
+                 int                   numEnergyGroups,
+                 int                   numThreads,
+                 InteractionSelection  interactionSelection,
+                 FILE*                 fplog);
+
+    //! Move constructor, default, but in the source file to hide implementation classes
+    ListedForces(ListedForces&& o) noexcept;
+
+    //! Destructor which is actually default but in the source file to hide implementation classes
+    ~ListedForces();
+
+    /*! \brief Copy the listed interactions from \p idef and set up the thread parallelization
+     *
+     * \param[in] domainIdef     Interaction definitions for all listed interactions to be computed on this domain/rank
+     * \param[in] numAtomsForce  Force are, potentially, computed for atoms 0 to \p numAtomsForce
+     * \param[in] useGpu         Whether a GPU is used to compute (part of) the listed interactions
+     */
+    void setup(const InteractionDefinitions& domainIdef, int numAtomsForce, bool useGpu);
+
+    /*! \brief Do all aspects of energy and force calculations for mdrun
+     * on the set of listed interactions
+     *
+     * xWholeMolecules only needs to contain whole molecules when orientation
+     * restraints need to be computed and can be empty otherwise.
+     */
+    void calculate(struct gmx_wallcycle*                     wcycle,
+                   const matrix                              box,
+                   const t_lambda*                           fepvals,
+                   const t_commrec*                          cr,
+                   const gmx_multisim_t*                     ms,
+                   gmx::ArrayRefWithPadding<const gmx::RVec> coordinates,
+                   gmx::ArrayRef<const gmx::RVec>            xWholeMolecules,
+                   t_fcdata*                                 fcdata,
+                   const history_t*                          hist,
+                   gmx::ForceOutputs*                        forceOutputs,
+                   const t_forcerec*                         fr,
+                   const struct t_pbc*                       pbc,
+                   gmx_enerdata_t*                           enerd,
+                   t_nrnb*                                   nrnb,
+                   gmx::ArrayRef<const real>                 lambda,
+                   const t_mdatoms*                          md,
+                   int*                                      global_atom_index,
+                   const gmx::StepWorkload&                  stepWork);
+
+    //! Returns whether bonded interactions are assigned to the CPU
+    bool haveCpuBondeds() const;
+
+    /*! \brief Returns whether listed forces are computed on the CPU
+     *
+     * NOTE: the current implementation returns true if there are position restraints
+     * or any bonded interactions computed on the CPU.
+     */
+    bool haveCpuListedForces(const t_fcdata& fcdata) const;
+
+    //! Returns true if there are position, distance or orientation restraints
+    bool haveRestraints(const t_fcdata& fcdata) const;
+
+private:
+    //! Pointer to the interaction definitions
+    InteractionDefinitions const* idef_ = nullptr;
+    //! Interaction defintions used for storing selections
+    InteractionDefinitions idefSelection_;
+    //! Thread parallelization setup, unique_ptr to avoid declaring bonded_threading_t
+    std::unique_ptr<bonded_threading_t> threading_;
+    //! Tells which interactions to select for computation
+    const InteractionSelection interactionSelection_;
+    //! Force buffer for free-energy forces
+    std::vector<real> forceBufferLambda_;
+    //! Shift force buffer for free-energy forces
+    std::vector<gmx::RVec> shiftForceBufferLambda_;
+    //! Temporary array for storing foreign lambda group pair energies
+    std::unique_ptr<gmx_grppairener_t> foreignEnergyGroups_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(ListedForces);
+};
+
+#endif
diff --git a/src/include/gromacs/listed_forces/listed_forces_gpu.h b/src/include/gromacs/listed_forces/listed_forces_gpu.h
new file mode 100644 (file)
index 0000000..5cdd8c7
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file contains declarations of high-level functions used
+ * by mdrun to compute energies and forces for listed interactions.
+ *
+ * Clients of libgromacs that want to evaluate listed interactions
+ * should call functions declared here.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_listed_forces
+ */
+#ifndef GMX_LISTED_FORCES_LISTED_FORCES_GPU_H
+#define GMX_LISTED_FORCES_LISTED_FORCES_GPU_H
+
+#include <memory>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/topology/idef.h"
+
+class DeviceContext;
+class DeviceStream;
+
+struct gmx_enerdata_t;
+struct gmx_ffparams_t;
+struct gmx_mtop_t;
+struct t_inputrec;
+struct gmx_wallcycle;
+
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+class StepWorkload;
+
+/*! \brief The number on bonded function types supported on GPUs */
+static constexpr int numFTypesOnGpu = 8;
+
+/*! \brief List of all bonded function types supported on GPUs
+ *
+ * \note This list should be in sync with the actual GPU code.
+ * \note Perturbed interactions are not supported on GPUs.
+ * \note The function types in the list are ordered on increasing value.
+ * \note Currently bonded are only supported with CUDA, not with OpenCL.
+ */
+constexpr std::array<int, numFTypesOnGpu> fTypesOnGpu = { F_BONDS,  F_ANGLES, F_UREY_BRADLEY,
+                                                          F_PDIHS,  F_RBDIHS, F_IDIHS,
+                                                          F_PIDIHS, F_LJ14 };
+
+/*! \brief Checks whether the GROMACS build allows to compute bonded interactions on a GPU.
+ *
+ * \param[out] error  If non-null, the diagnostic message when bondeds cannot run on a GPU.
+ *
+ * \returns true when this build can run bonded interactions on a GPU, false otherwise.
+ *
+ * \throws std::bad_alloc when out of memory.
+ */
+bool buildSupportsListedForcesGpu(std::string* error);
+
+/*! \brief Checks whether the input system allows to compute bonded interactions on a GPU.
+ *
+ * \param[in]  ir     Input system.
+ * \param[in]  mtop   Complete system topology to search for supported interactions.
+ * \param[out] error  If non-null, the error message if the input is not supported on GPU.
+ *
+ * \returns true if PME can run on GPU with this input, false otherwise.
+ */
+bool inputSupportsListedForcesGpu(const t_inputrec& ir, const gmx_mtop_t& mtop, std::string* error);
+
+class ListedForcesGpu
+{
+public:
+    /*! \brief Construct the manager with constant data and the stream to use.
+     *
+     * \param[in] ffparams                   Force-field parameters.
+     * \param[in] electrostaticsScaleFactor  Scaling factor for the electrostatic potential
+     *                                       (Coulomb constant, multiplied by the Fudge factor).
+     * \param[in] deviceContext              GPU device context (not used in CUDA).
+     * \param[in] deviceStream               GPU device stream.
+     * \param[in] wcycle                     The wallclock counter.
+     *
+     */
+    ListedForcesGpu(const gmx_ffparams_t& ffparams,
+                    float                 electrostaticsScaleFactor,
+                    const DeviceContext&  deviceContext,
+                    const DeviceStream&   deviceStream,
+                    gmx_wallcycle*        wcycle);
+    //! Destructor
+    ~ListedForcesGpu();
+
+    /*! \brief Update lists of interactions from idef suitable for the GPU,
+     * using the data structures prepared for PP work.
+     *
+     * Intended to be called after each neighbour search
+     * stage. Copies the bonded interactions assigned to the GPU
+     * to device data structures, and updates device buffers that
+     * may have been updated after search.
+     *
+     * \param[in]     nbnxnAtomOrder  Mapping between rvec and NBNXM formats.
+     * \param[in]     idef            List of interactions to compute.
+     * \param[in]     xqDevice        Device buffer with coordinates and charge in xyzq-format.
+     * \param[in,out] forceDevice     Device buffer with forces.
+     * \param[in,out] fshiftDevice    Device buffer with shift forces.
+     */
+    void updateInteractionListsAndDeviceBuffers(ArrayRef<const int>           nbnxnAtomOrder,
+                                                const InteractionDefinitions& idef,
+                                                void*                         xqDevice,
+                                                DeviceBuffer<RVec>            forceDevice,
+                                                DeviceBuffer<RVec>            fshiftDevice);
+    /*! \brief
+     * Update PBC data.
+     *
+     * Converts PBC data from t_pbc into the PbcAiuc format and stores the latter.
+     *
+     * \param[in] pbcType The type of the periodic boundary.
+     * \param[in] box     The periodic boundary box matrix.
+     * \param[in] canMoleculeSpanPbc  Whether one molecule can have atoms in different PBC cells.
+     */
+    void setPbc(PbcType pbcType, const matrix box, bool canMoleculeSpanPbc);
+
+    /*! \brief Returns whether there are bonded interactions
+     * assigned to the GPU
+     *
+     * \returns If the list of interaction has elements.
+     */
+    bool haveInteractions() const;
+
+    /*! \brief Launches bonded kernel on a GPU
+     *
+     * \param[in]  stepWork  Simulation step work to determine if energy/virial are to be computed on this step.
+     */
+    void launchKernel(const gmx::StepWorkload& stepWork);
+
+    /*! \brief Sets the PBC and launches bonded kernel on a GPU
+     *
+     * \param[in] pbcType The type of the periodic boundary.
+     * \param[in] box     The periodic boundary box matrix.
+     * \param[in] canMoleculeSpanPbc  Whether one molecule can have atoms in different PBC cells.
+     * \param[in] stepWork  Simulation step work to determine if energy/virial are to be computed on this step.
+     */
+    void setPbcAndlaunchKernel(PbcType                  pbcType,
+                               const matrix             box,
+                               bool                     canMoleculeSpanPbc,
+                               const gmx::StepWorkload& stepWork);
+
+    /*! \brief Launches the transfer of computed bonded energies.
+     */
+    void launchEnergyTransfer();
+
+    /*! \brief Waits on the energy transfer, and accumulates bonded energies to \c enerd.
+     *
+     * \param[in,out] enerd The energy data object to add energy terms to.
+     */
+    void waitAccumulateEnergyTerms(gmx_enerdata_t* enerd);
+
+    /*! \brief Clears the device side energy buffer
+     */
+    void clearEnergies();
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif // GMX_LISTED_FORCES_LISTED_FORCES_GPU_H
diff --git a/src/include/gromacs/listed_forces/listed_forces_gpu_impl.h b/src/include/gromacs/listed_forces/listed_forces_gpu_impl.h
new file mode 100644 (file)
index 0000000..ea9e8b8
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares GPU implementation class for CUDA bonded
+ * interactions.
+ *
+ * This header file is needed to include from both the device-side
+ * kernels file, and the host-side management code.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Szilárd Páll <pall.szilard@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ *
+ * \ingroup module_listed_forces
+ */
+#ifndef GMX_LISTED_FORCES_LISTED_FORCES_GPU_IMPL_H
+#define GMX_LISTED_FORCES_LISTED_FORCES_GPU_IMPL_H
+
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/gputraits.cuh"
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/listed_forces/listed_forces_gpu.h"
+#include "gromacs/pbcutil/pbc_aiuc.h"
+
+struct gmx_ffparams_t;
+struct t_forcerec;
+
+namespace gmx
+{
+
+/*! \internal \brief Version of InteractionList that supports pinning */
+struct HostInteractionList
+{
+    /*! \brief Returns the total number of elements in iatoms */
+    int size() const { return iatoms.size(); }
+
+    //! List of interactions, see \c HostInteractionLists
+    HostVector<int> iatoms = { {}, gmx::HostAllocationPolicy(gmx::PinningPolicy::PinnedIfSupported) };
+};
+
+/* \brief Bonded parameters and GPU pointers
+ *
+ * This is used to accumulate all the parameters and pointers so they can be passed
+ * to the GPU as a single structure.
+ *
+ */
+struct BondedCudaKernelParameters
+{
+    //! Periodic boundary data
+    PbcAiuc pbcAiuc;
+    //! Scale factor
+    float electrostaticsScaleFactor;
+    //! The bonded types on GPU
+    int fTypesOnGpu[numFTypesOnGpu];
+    //! The number of interaction atom (iatom) elements for every function type
+    int numFTypeIAtoms[numFTypesOnGpu];
+    //! The number of bonds for every function type
+    int numFTypeBonds[numFTypesOnGpu];
+    //! The start index in the range of each interaction type
+    int fTypeRangeStart[numFTypesOnGpu];
+    //! The end index in the range of each interaction type
+    int fTypeRangeEnd[numFTypesOnGpu];
+
+    //! Force parameters (on GPU)
+    t_iparams* d_forceParams;
+    //! Total Energy (on GPU)
+    float* d_vTot;
+    //! Interaction list atoms (on GPU)
+    t_iatom* d_iatoms[numFTypesOnGpu];
+
+    BondedCudaKernelParameters()
+    {
+        matrix boxDummy = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } };
+
+        setPbcAiuc(0, boxDummy, &pbcAiuc);
+
+        electrostaticsScaleFactor = 1.0;
+        d_forceParams             = nullptr;
+        d_vTot                    = nullptr;
+    }
+};
+
+/*! \internal \brief Implements GPU bondeds */
+class ListedForcesGpu::Impl
+{
+public:
+    //! Constructor
+    Impl(const gmx_ffparams_t& ffparams,
+         float                 electrostaticsScaleFactor,
+         const DeviceContext&  deviceContext,
+         const DeviceStream&   deviceStream,
+         gmx_wallcycle*        wcycle);
+    /*! \brief Destructor, non-default needed for freeing
+     * device-side buffers */
+    ~Impl();
+    /*! \brief Update lists of interactions from idef suitable for the GPU,
+     * using the data structures prepared for PP work.
+     *
+     * Intended to be called after each neighbour search
+     * stage. Copies the bonded interactions assigned to the GPU
+     * to device data structures, and updates device buffers that
+     * may have been updated after search. */
+    void updateInteractionListsAndDeviceBuffers(ArrayRef<const int>           nbnxnAtomOrder,
+                                                const InteractionDefinitions& idef,
+                                                void*                         xqDevice,
+                                                DeviceBuffer<RVec>            forceDevice,
+                                                DeviceBuffer<RVec>            fshiftDevice);
+    /*! \brief
+     * Update PBC data.
+     *
+     * Converts PBC data from t_pbc into the PbcAiuc format and stores the latter.
+     *
+     * \param[in] pbcType The type of the periodic boundary.
+     * \param[in] box     The periodic boundary box matrix.
+     * \param[in] canMoleculeSpanPbc  Whether one molecule can have atoms in different PBC cells.
+     */
+    void setPbc(PbcType pbcType, const matrix box, bool canMoleculeSpanPbc);
+
+    /*! \brief Launches bonded kernel on a GPU */
+    template<bool calcVir, bool calcEner>
+    void launchKernel();
+    /*! \brief Returns whether there are bonded interactions
+     * assigned to the GPU */
+    bool haveInteractions() const;
+    /*! \brief Launches the transfer of computed bonded energies. */
+    void launchEnergyTransfer();
+    /*! \brief Waits on the energy transfer, and accumulates bonded energies to \c enerd. */
+    void waitAccumulateEnergyTerms(gmx_enerdata_t* enerd);
+    /*! \brief Clears the device side energy buffer */
+    void clearEnergies();
+
+private:
+    /*! \brief The interaction lists
+     *
+     * \todo This is potentially several pinned allocations, which
+     * could contribute to exhausting such pages. */
+    std::array<HostInteractionList, F_NRE> iLists_;
+
+    //! Tells whether there are any interaction in iLists.
+    bool haveInteractions_;
+    //! Interaction lists on the device.
+    t_ilist d_iLists_[F_NRE] = {};
+    //! Bonded parameters for device-side use.
+    t_iparams* d_forceParams_ = nullptr;
+    //! Position-charge vector on the device.
+    const float4* d_xq_ = nullptr;
+    //! Force vector on the device.
+    float3* d_f_ = nullptr;
+    //! Shift force vector on the device.
+    float3* d_fShift_ = nullptr;
+    //! \brief Host-side virial buffer
+    HostVector<float> vTot_ = { {}, gmx::HostAllocationPolicy(gmx::PinningPolicy::PinnedIfSupported) };
+    //! \brief Device-side total virial
+    float* d_vTot_ = nullptr;
+
+    //! GPU context object
+    const DeviceContext& deviceContext_;
+    //! \brief Bonded GPU stream, not owned by this module
+    const DeviceStream& deviceStream_;
+
+    //! Parameters and pointers, passed to the CUDA kernel
+    BondedCudaKernelParameters kernelParams_;
+
+    //! GPU kernel launch configuration
+    KernelLaunchConfig kernelLaunchConfig_;
+
+    //! \brief Pointer to wallcycle structure.
+    gmx_wallcycle* wcycle_;
+};
+
+} // namespace gmx
+
+#endif // GMX_LISTED_FORCES_LISTED_FORCES_GPU_IMPL_H
diff --git a/src/include/gromacs/listed_forces/listed_internal.h b/src/include/gromacs/listed_forces/listed_internal.h
new file mode 100644 (file)
index 0000000..cc3e8c3
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file contains declarations for functions needed
+ * internally by the module.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_listed_forces
+ */
+#ifndef GMX_LISTED_FORCES_LISTED_INTERNAL_H
+#define GMX_LISTED_FORCES_LISTED_INTERNAL_H
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/enerdata.h"
+#include "gromacs/mdtypes/threaded_force_buffer.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/bitmask.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+/*! \internal \brief The division of bonded interactions of the threads */
+class WorkDivision
+{
+public:
+    //! Constructor
+    WorkDivision(int numThreads) : stride_(numThreads + 1), packedBounds_(F_NRE * stride_) {}
+
+    //! Sets the bound between threads \p boundIndex-1 and \p boundIndex to \p count
+    void setBound(int functionType, int boundIndex, int count)
+    {
+        packedBounds_[functionType * stride_ + boundIndex] = count;
+    }
+
+    //! Returns the bound between threads \p boundIndex-1 and \p boundIndex
+    int bound(int functionType, int boundIndex) const
+    {
+        return packedBounds_[functionType * stride_ + boundIndex];
+    }
+
+    //! Returns the last bound
+    int end(int ftype) const { return bound(ftype, stride_ - 1); }
+
+private:
+    //! The stride_ between and size of the entries for a function type
+    int stride_;
+    //! The bounds stored as a flat array for fast access
+    std::vector<int> packedBounds_;
+};
+
+/*! \internal \brief struct contain all data for bonded force threading */
+struct bonded_threading_t
+{
+    //! Constructor
+    bonded_threading_t(int numThreads, int numEnergyGroups, FILE* fplog);
+
+    //! Number of threads to be used for bondeds
+    int nthreads = 0;
+    //! The thread parallel force and energy buffers
+    gmx::ThreadedForceBuffer<rvec4> threadedForceBuffer;
+    //! true if we have and thus need to reduce bonded forces
+    bool haveBondeds = false;
+
+    /* There are two different ways to distribute the bonded force calculation
+     * over the threads. We decide which to use based on the number of threads.
+     */
+    //! Maximum thread count for uniform distribution of bondeds over threads
+    int max_nthread_uniform = 0;
+
+    //! The division of work in the t_list over threads.
+    WorkDivision workDivision;
+
+    //! Work division for free-energy foreign lambda calculations, always uses 1 thread
+    WorkDivision foreignLambdaWorkDivision;
+
+    GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(bonded_threading_t);
+};
+
+
+/*! \brief Returns the global topology atom number belonging to local
+ * atom index i.
+ *
+ * This function is intended for writing ascii output and returns atom
+ * numbers starting at 1.  When global_atom_index=NULL returns i+1.
+ */
+int glatnr(const int* global_atom_index, int i);
+
+#endif
diff --git a/src/include/gromacs/listed_forces/manage_threading.h b/src/include/gromacs/listed_forces/manage_threading.h
new file mode 100644 (file)
index 0000000..9f369fb
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares functions for managing threading of listed forces
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_listed_forces
+ */
+#ifndef GMX_LISTED_FORCES_MANAGE_THREADING_H
+#define GMX_LISTED_FORCES_MANAGE_THREADING_H
+
+#include <cstdio>
+
+struct bonded_threading_t;
+class InteractionDefinitions;
+
+/*! \brief Divide the listed interactions over the threads and GPU
+ *
+ * Uses fr->nthreads for the number of threads, and sets up the
+ * thread-force buffer reduction.
+ * This should be called each time the bonded setup changes;
+ * i.e. at start-up without domain decomposition and at DD.
+ */
+void setup_bonded_threading(bonded_threading_t*           bt,
+                            int                           numAtomsForce,
+                            bool                          useGpuForBondeds,
+                            const InteractionDefinitions& idef);
+
+#endif
diff --git a/src/include/gromacs/listed_forces/orires.h b/src/include/gromacs/listed_forces/orires.h
new file mode 100644 (file)
index 0000000..3fcb15d
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares functions for handling orientation restraints.
+ *
+ * \inlibraryapi
+ * \ingroup module_listed_forces
+ */
+#ifndef GMX_LISTED_FORCES_ORIRES_H
+#define GMX_LISTED_FORCES_ORIRES_H
+
+#include <cstdio>
+
+#include "gromacs/topology/ifunc.h"
+
+struct gmx_mtop_t;
+struct gmx_multisim_t;
+class history_t;
+struct t_inputrec;
+struct t_pbc;
+struct t_commrec;
+struct t_oriresdata;
+struct t_disresdata;
+struct t_fcdata;
+class t_state;
+struct t_mdatoms;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+} // namespace gmx
+
+/*! \brief Extends \p globalState with orientation restraint history
+ * when there are restraints and time averaging is used.
+ */
+void extendStateWithOriresHistory(const gmx_mtop_t& mtop, const t_inputrec& ir, t_state* globalState);
+
+/*! \brief
+ * Calculates the time averaged D matrices, the S matrix for each experiment.
+ *
+ * Returns the weighted RMS deviation of the orientation restraints.
+ */
+real calc_orires_dev(const gmx_multisim_t*          ms,
+                     int                            nfa,
+                     const t_iatom                  fa[],
+                     const t_iparams                ip[],
+                     gmx::ArrayRef<const gmx::RVec> xWholeMolecules,
+                     const rvec                     x[],
+                     const t_pbc*                   pbc,
+                     t_oriresdata*                  oriresdata);
+
+/*! \brief
+ * Diagonalizes the order tensor(s) of the orienation restraints.
+ *
+ * For each experiment eig containts first 3 eigenvalues and then
+ * the 3 eigenvectors. The eigenvalues are ordered on magnitude.
+ */
+void diagonalize_orires_tensors(t_oriresdata* od);
+
+//! Prints order parameter, eigenvalues and eigenvectors to the log file.
+void print_orires_log(FILE* log, t_oriresdata* od);
+
+//! Calculates the orientation restraint forces.
+real orires(int                       nfa,
+            const t_iatom             forceatoms[],
+            const t_iparams           ip[],
+            const rvec                x[],
+            rvec4                     f[],
+            rvec                      fshift[],
+            const t_pbc*              pbc,
+            real                      lambda,
+            real*                     dvdlambda,
+            gmx::ArrayRef<const real> charge,
+            t_fcdata*                 fcd,
+            t_disresdata*             disresdata,
+            t_oriresdata*             oriresdata,
+            int*                      global_atom_index);
+
+//! Copies the new time averages that have been calculated in calc_orires_dev.
+void update_orires_history(const t_oriresdata& oriresdata, history_t* hist);
+
+#endif
diff --git a/src/include/gromacs/listed_forces/pairs.h b/src/include/gromacs/listed_forces/pairs.h
new file mode 100644 (file)
index 0000000..57c0c42
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file declares functions for "pair" interactions
+ * (i.e. listed non-bonded interactions, e.g. 1-4 interactions)
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_listed_forces
+ */
+#ifndef GMX_LISTED_FORCES_PAIRS_H
+#define GMX_LISTED_FORCES_PAIRS_H
+
+#include "gromacs/math/vec.h"
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_grppairener_t;
+struct t_forcerec;
+struct t_pbc;
+
+namespace gmx
+{
+class StepWorkload;
+template<typename>
+class ArrayRef;
+} // namespace gmx
+
+/*! \brief Calculate VdW/charge listed pair interactions (usually 1-4
+ * interactions).
+ *
+ * global_atom_index is only passed for printing error messages.
+ */
+void do_pairs(int                           ftype,
+              int                           nbonds,
+              const t_iatom                 iatoms[],
+              const t_iparams               iparams[],
+              const rvec                    x[],
+              rvec4                         f[],
+              rvec                          fshift[],
+              const struct t_pbc*           pbc,
+              const real*                   lambda,
+              real*                         dvdl,
+              gmx::ArrayRef<real>           chargeA,
+              gmx::ArrayRef<real>           chargeB,
+              gmx::ArrayRef<bool>           atomIsPerturbed,
+              gmx::ArrayRef<unsigned short> cENER,
+              int                           numEnergyGroups,
+              const t_forcerec*             fr,
+              bool                          havePerturbedPairs,
+              const gmx::StepWorkload&      stepWork,
+              gmx_grppairener_t*            grppener,
+              int*                          global_atom_index);
+
+#endif
diff --git a/src/include/gromacs/listed_forces/position_restraints.h b/src/include/gromacs/listed_forces/position_restraints.h
new file mode 100644 (file)
index 0000000..3fc8d00
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file contains declarations necessary for low-level
+ * functions for computing energies and forces for position
+ * restraints.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_listed_forces
+ */
+
+#ifndef GMX_LISTED_FORCES_POSITION_RESTRAINTS_H
+#define GMX_LISTED_FORCES_POSITION_RESTRAINTS_H
+
+#include <stdio.h>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_enerdata_t;
+struct gmx_wallcycle;
+struct t_forcerec;
+class InteractionDefinitions;
+struct t_lambda;
+struct t_nrnb;
+struct t_pbc;
+
+namespace gmx
+{
+class ForceWithVirial;
+}
+
+/*! \brief Helper function that wraps calls to posres */
+void posres_wrapper(t_nrnb*                       nrnb,
+                    const InteractionDefinitions& idef,
+                    const struct t_pbc*           pbc,
+                    const rvec*                   x,
+                    gmx_enerdata_t*               enerd,
+                    gmx::ArrayRef<const real>     lambda,
+                    const t_forcerec*             fr,
+                    gmx::ForceWithVirial*         forceWithVirial);
+
+/*! \brief Helper function that wraps calls to posres for free-energy
+    pertubation */
+void posres_wrapper_lambda(struct gmx_wallcycle*         wcycle,
+                           const t_lambda*               fepvals,
+                           const InteractionDefinitions& idef,
+                           const struct t_pbc*           pbc,
+                           const rvec                    x[],
+                           gmx_enerdata_t*               enerd,
+                           gmx::ArrayRef<const real>     lambda,
+                           const t_forcerec*             fr);
+
+/*! \brief Helper function that wraps calls to fbposres for
+    free-energy perturbation */
+void fbposres_wrapper(t_nrnb*                       nrnb,
+                      const InteractionDefinitions& idef,
+                      const struct t_pbc*           pbc,
+                      const rvec*                   x,
+                      gmx_enerdata_t*               enerd,
+                      const t_forcerec*             fr,
+                      gmx::ForceWithVirial*         forceWithVirial);
+
+#endif
diff --git a/src/include/gromacs/listed_forces/restcbt.h b/src/include/gromacs/listed_forces/restcbt.h
new file mode 100644 (file)
index 0000000..20dc9ab
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ *
+ * \brief
+ * This file contains function declarations necessary
+   for computations of forces due to restricted angle, restricted dihedral and
+   combined bending-torsion potentials.
+ *
+ * \author Nicolae Goga
+ *
+ * \ingroup module_listed_forces
+ */
+
+#ifndef GMX_LISTED_FORCES_RESTCBT_H
+#define GMX_LISTED_FORCES_RESTCBT_H
+
+#include "gromacs/math/vec.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/utility/real.h"
+
+/*! \brief This function computes factors needed for restricted angle potentials.
+ *
+ * The restricted angle potential is used in coarse-grained simulations to avoid singularities
+ * when three particles align and the dihedral angle and dihedral potential cannot be calculated.
+ * This potential is calculated using the formula:
+ * \f[V_{\rm ReB}(\theta_i) = \frac{1}{2} k_{\theta} \frac{(\cos\theta_i - \cos\theta_0)^2}{\sin^2\theta_i}\f]
+ * (see section "Restricted Bending Potential" from the manual).
+ * The derivative of the restricted angle potential is calculated as:
+ * \f[\frac{\partial V_{\rm ReB}(\theta_i)} {\partial \vec r_{k}} = \frac{dV_{\rm ReB}(\theta_i)}{dcos\theta_i} \frac{\partial cos\theta_i}{\partial \vec r_{k}}\f]
+ * where all the derivatives of the bending angle with respect to Cartesian coordinates are calculated as in Allen & Tildesley (pp. 330-332)
+ *
+ *  \param[in]  type           type of force parameters
+ *  \param[in]  forceparams    array of parameters for force computations
+ *  \param[in]  delta_ante     distance between the first two particles
+ *  \param[in]  delta_post     distance between the last two particles
+ *  \param[out] prefactor      common term that comes in front of each force
+ *  \param[out] ratio_ante     ratio of scalar products of delta_ante with delta_post
+                              and delta_ante with delta_ante
+ *  \param[out] ratio_post    ratio of scalar products of delta_ante with delta_post
+                              and delta_post with delta_ante
+ *  \param[out] v              contribution to energy   (see formula above)
+ */
+
+
+void compute_factors_restangles(int             type,
+                                const t_iparams forceparams[],
+                                rvec            delta_ante,
+                                rvec            delta_post,
+                                double*         prefactor,
+                                double*         ratio_ante,
+                                double*         ratio_post,
+                                real*           v);
+
+
+/*! \brief Compute factors for restricted dihedral potentials.
+ *
+ * The restricted dihedral potential is the equivalent of the restricted bending potential
+ * for the dihedral angle. It imposes the dihedral angle to have only one equilibrium value.
+ * This potential is calculated using the formula:
+ * \f[V_{\rm ReT}(\phi_i) = \frac{1}{2} k_{\phi} \frac{(\cos\phi_i - \cos\phi_0)^2}{\sin^2\phi_i}\f]
+ * (see section "Proper dihedrals: Restricted torsion potential" from the manual).
+ * The derivative of the restricted dihedral potential is calculated as:
+ * \f[\frac{\partial V_{\rm ReT}(\phi_i)} {\partial \vec r_{k}} = \frac{dV_{\rm ReT}(\phi_i)}{dcos\phi_i} \frac{\partial cos\phi_i}{\partial \vec r_{k}}\f]
+ * where all the derivatives of the dihedral angle with respect to Cartesian coordinates
+ * are calculated as in Allen & Tildesley (pp. 330-332). Factors factor_phi_*  are coming from the
+ * derivatives of the torsion angle (phi) with respect to the beads ai, aj, ak, al, (four) coordinates
+ * and they are multiplied in the force computations with the particle distance
+ * stored in parameters delta_ante, delta_crnt, delta_post.
+ *
+ *  \param[in]  type                             type of force parameters
+ *  \param[in]  forceparams                      array of parameters for force computations
+ *  \param[in]  delta_ante                       distance between the first two particles
+ *  \param[in]  delta_crnt                       distance between the middle pair of particles
+ *  \param[in]  delta_post                       distance between the last two particles
+ *  \param[out] factor_phi_ai_ante               force factor for particle ai multiplied with delta_ante
+ *  \param[out] factor_phi_ai_crnt               force factor for particle ai multiplied with delta_crnt
+ *  \param[out] factor_phi_ai_post               force factor for particle ai multiplied with delta_post
+ *  \param[out] factor_phi_aj_ante               force factor for particle aj multiplied with delta_ante
+ *  \param[out] factor_phi_aj_crnt               force factor for particle aj multiplied with delta_crnt
+ *  \param[out] factor_phi_aj_post               force factor for particle aj multiplied with delta_post
+ *  \param[out] factor_phi_ak_ante               force factor for particle ak multiplied with delta_ante
+ *  \param[out] factor_phi_ak_crnt               force factor for particle ak multiplied with delta_crnt
+ *  \param[out] factor_phi_ak_post               force factor for particle ak multiplied with delta_post
+ *  \param[out] factor_phi_al_ante               force factor for particle al multiplied with delta_ante
+ *  \param[out] factor_phi_al_crnt               force factor for particle al multiplied with delta_crnt
+ *  \param[out] factor_phi_al_post               force factor for particle al multiplied with delta_post
+ *  \param[out] prefactor_phi                    multiplication constant of the torsion force
+ *  \param[out] v                                contribution to energy  (see formula above)
+ */
+
+void compute_factors_restrdihs(int             type,
+                               const t_iparams forceparams[],
+                               rvec            delta_ante,
+                               rvec            delta_crnt,
+                               rvec            delta_post,
+                               real*           factor_phi_ai_ante,
+                               real*           factor_phi_ai_crnt,
+                               real*           factor_phi_ai_post,
+                               real*           factor_phi_aj_ante,
+                               real*           factor_phi_aj_crnt,
+                               real*           factor_phi_aj_post,
+                               real*           factor_phi_ak_ante,
+                               real*           factor_phi_ak_crnt,
+                               real*           factor_phi_ak_post,
+                               real*           factor_phi_al_ante,
+                               real*           factor_phi_al_crnt,
+                               real*           factor_phi_al_post,
+                               real*           prefactor_phi,
+                               real*           v);
+
+/*! \brief Compute factors for combined bending-torsion (CBT) potentials.
+ *
+ * The combined bending-torsion potential goes to zero in a very smooth manner, eliminating the numerical
+ * instabilities, when three coarse-grained particles align and the dihedral angle and standard
+ * dihedral potentials cannot be calculated. The CBT potential is calculated using the formula:
+ * \f[V_{\rm CBT}(\theta_{i-1}, \theta_i, \phi_i) = k_{\phi} \sin^3\theta_{i-1} \sin^3\theta_{i}
+ * \sum_{n=0}^4 { a_n \cos^n\phi_i}\f] (see section "Proper dihedrals: Combined bending-torsion potential" from the manual).
+ * It contains in its expression not only the dihedral angle \f$\phi\f$
+ * but also \f$\theta_{i-1}\f$ (denoted as theta_ante below) and \f$\theta_{i}\f$ (denoted as theta_post below)
+ * --- the adjacent bending angles. The derivative of the CBT potential is calculated as:
+ * \f[\frac{\partial V_{\rm CBT}(\theta_{i-1},\theta_i,\phi_i)} {\partial \vec r_{l}} = \frac{\partial V_
+ * {\rm CBT}}{\partial \theta_{i-1}} \frac{\partial \theta_{i-1}}{\partial \vec r_{l}} +
+ * \frac{\partial V_{\rm CBT}}{\partial \phi_{i    }} \frac{\partial \phi_{i    }}{\partial \vec r_{l}}\f]
+ * where all the derivatives of the angles with respect to Cartesian coordinates are calculated as
+ * in Allen & Tildesley (pp. 330-332). Factors f_phi_* come from  the derivatives of the torsion angle
+ * with respect to the beads ai, aj, ak, al (four) coordinates; f_theta_ante_* come from the derivatives of
+ * the bending angle theta_ante (theta_{i-1} in formula above) with respect to the beads ai, aj, ak (three
+ * particles) coordinates and f_theta_post_* come from the derivatives of  the bending angle theta_post
+ * (theta_{i} in formula above) with respect to the beads aj, ak, al (three particles) coordinates.
+ *
+ *  \param[in]  type                             type of force parameters
+ *  \param[in]  forceparams                      array of parameters for force computations
+ *  \param[in]  delta_ante                       distance between the first two particles
+ *  \param[in]  delta_crnt                       distance between the middle pair of particles
+ *  \param[in]  delta_post                       distance between the last two particles
+ *  \param[out]  f_phi_ai                        force for particle ai due to derivative in phi angle
+ *  \param[out]  f_phi_aj                        force for particle aj due to derivative in phi angle
+ *  \param[out]  f_phi_ak                        force for particle ak due to derivative in phi angle
+ *  \param[out]  f_phi_al                        force for particle al due to derivative in phi angle
+ *  \param[out]  f_theta_ante_ai                 force for particle ai due to derivative in theta_ante angle
+ *  \param[out]  f_theta_ante_aj                 force for particle aj due to derivative in theta_ante angle
+ *  \param[out]  f_theta_ante_ak                 force for particle ak due to derivative in theta_ante angle
+ *  \param[out]  f_theta_post_aj                 force for particle aj due to derivative in theta_post angle
+ *  \param[out]  f_theta_post_ak                 force for particle ak due to derivative in theta_post angle
+ *  \param[out]  f_theta_post_al                 force for particle al due to derivative in theta_psot angle
+ *  \param[out] v                                contribution to energy (see formula above)
+ */
+
+void compute_factors_cbtdihs(int             type,
+                             const t_iparams forceparams[],
+                             rvec            delta_ante,
+                             rvec            delta_crnt,
+                             rvec            delta_post,
+                             rvec            f_phi_ai,
+                             rvec            f_phi_aj,
+                             rvec            f_phi_ak,
+                             rvec            f_phi_al,
+                             rvec            f_theta_ante_ai,
+                             rvec            f_theta_ante_aj,
+                             rvec            f_theta_ante_ak,
+                             rvec            f_theta_post_aj,
+                             rvec            f_theta_post_ak,
+                             rvec            f_theta_post_al,
+                             real*           v);
+
+#endif
diff --git a/src/include/gromacs/listed_forces/utilities.h b/src/include/gromacs/listed_forces/utilities.h
new file mode 100644 (file)
index 0000000..170bef2
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal
+ * \file
+ *
+ * \brief This file declares inline utility functionality.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ *
+ * \ingroup module_listed_forces
+ */
+#ifndef GMX_LISTED_FORCES_UTILITIES_H
+#define GMX_LISTED_FORCES_UTILITIES_H
+
+#include "gromacs/topology/ifunc.h"
+
+/*! \brief Return whether this is an interaction that actually
+ * calculates a potential and works on multiple atoms (not e.g. a
+ * connection or a position restraint).
+ *
+ * \todo This function could go away when idef is not a big bucket of
+ * everything. */
+static bool ftype_is_bonded_potential(int ftype)
+{
+    return ((interaction_function[ftype].flags & IF_BOND) != 0U)
+           && !(ftype == F_CONNBONDS || ftype == F_POSRES || ftype == F_FBPOSRES);
+}
+
+#endif
diff --git a/src/include/gromacs/math/3dtransforms.h b/src/include/gromacs/math/3dtransforms.h
new file mode 100644 (file)
index 0000000..3b146b3
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2016,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MATH_3DTRANSFORMS_H
+#define GMX_MATH_3DTRANSFORMS_H
+
+#include <stdio.h>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+/** Index for the fourth dimension for `vec4`. */
+#define WW 3
+
+/*! \brief
+ * 4D vector type used in 3D transformations.
+ *
+ * In \Gromacs, only a limited set of 3D transformations are used, and all of
+ * them operate on coordinates, so the fourth element is assumed to be one and
+ * ignored in all contexts.
+ */
+typedef real vec4[4];
+
+/*! \brief
+ * 4D matrix type used in 3D transformations.
+ */
+typedef real mat4[4][4];
+
+void gmx_mat4_copy(mat4 a, mat4 b);
+
+void gmx_mat4_transform_point(mat4 m, const rvec x, vec4 v);
+
+/*! \brief
+ * Computes the product of two `mat4` matrices as A = B * C.
+ *
+ * Note that the order of operands is different from mmul() in vec.h!
+ */
+void gmx_mat4_mmul(mat4 A, mat4 B, mat4 C);
+
+void gmx_mat4_init_unity(mat4 m);
+
+void gmx_mat4_init_rotation(int axis, real angle, mat4 A);
+
+void gmx_mat4_init_translation(real tx, real ty, real tz, mat4 A);
+
+void gmx_mat4_print(FILE* fp, const char* s, mat4 A);
+
+void gmx_vec4_print(FILE* fp, const char* s, vec4 a);
+
+#endif
diff --git a/src/include/gromacs/math/arrayrefwithpadding.h b/src/include/gromacs/math/arrayrefwithpadding.h
new file mode 100644 (file)
index 0000000..ee60701
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief Declares gmx::ArrayRefWithPadding that refers to memory whose
+ * size includes padding for SIMD operations.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ *
+ * \inpublicapi
+ * \ingroup module_math
+ */
+#ifndef GMX_MATH_ARRAYREFWITHPADDING_H
+#define GMX_MATH_ARRAYREFWITHPADDING_H
+
+#include <cstddef>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+/*! \brief Interface to a C array of T (or part of a std container of
+ * T), that includes padding that is suitable for the kinds of SIMD
+ * operations GROMACS uses.
+ *
+ * High level code e.g. in the force calculation routines need to hold
+ * a non-owning view of memory and be able to create ArrayRef objects
+ * that view padded or unpadded memory, suitable for the various
+ * component routines. This class implements that non-owning view,
+ * and the only available functionality refers to its size, and the
+ * methods to create such ArrayRef objects.
+ *
+ * \copydoc ArrayRef
+ * \inlibraryapi
+ * \ingroup module_math
+ */
+template<typename T>
+class ArrayRefWithPadding
+{
+public:
+    //! Type of values stored in the reference.
+    using value_type = T;
+    //! Type for representing size of the reference.
+    using size_type = index;
+    //! Const pointer to an element.
+    using const_pointer = const T*;
+    //! Const iterator type to an element.
+    using const_iterator = const T*;
+    //! Pointer to an element.
+    using pointer = T*;
+    //! Iterator type to an element.
+    using iterator = T*;
+
+    /*! \brief
+     * Constructs an empty reference.
+     */
+    ArrayRefWithPadding() : begin_(nullptr), end_(nullptr), paddedEnd_(nullptr) {}
+    /*! \brief
+     * Constructs a reference to a particular range.
+     *
+     * \param[in] begin        Pointer to the beginning of a range.
+     * \param[in] end          Pointer to the end of a range without padding.
+     * \param[in] paddedEnd    Pointer to the end of a range including padding.
+     *
+     * Passed pointers must remain valid for the lifetime of this object.
+     */
+    ArrayRefWithPadding(pointer begin, pointer end, pointer paddedEnd) :
+        begin_(begin), end_(end), paddedEnd_(paddedEnd)
+    {
+        GMX_ASSERT(end >= begin, "Invalid range");
+        GMX_ASSERT(paddedEnd >= end, "Invalid range");
+    }
+    //! Copy constructor
+    ArrayRefWithPadding(const ArrayRefWithPadding& o) :
+        begin_(o.begin_), end_(o.end_), paddedEnd_(o.paddedEnd_)
+    {
+    }
+    //! Move constructor
+    ArrayRefWithPadding(ArrayRefWithPadding&& o) noexcept :
+        begin_(std::move(o.begin_)), end_(std::move(o.end_)), paddedEnd_(std::move(o.paddedEnd_))
+    {
+    }
+    /*! \brief Convenience overload constructor to make an ArrayRefWithPadding<const T> from a non-const one.
+     *
+     * \todo Use std::is_same_v when CUDA 11 is a requirement.
+     */
+    template<typename U, typename = std::enable_if_t<std::is_same<value_type, const typename std::remove_reference_t<U>::value_type>::value>>
+    ArrayRefWithPadding(U&& o)
+    {
+        auto constArrayRefWithPadding = o.constArrayRefWithPadding();
+        this->swap(constArrayRefWithPadding);
+    }
+    //! Copy assignment operator
+    ArrayRefWithPadding& operator=(ArrayRefWithPadding const& o)
+    {
+        if (&o != this)
+        {
+            begin_     = o.begin_;
+            end_       = o.end_;
+            paddedEnd_ = o.paddedEnd_;
+        }
+        return *this;
+    }
+    //! Move assignment operator
+    ArrayRefWithPadding& operator=(ArrayRefWithPadding&& o) noexcept
+    {
+        if (&o != this)
+        {
+            begin_     = std::move(o.begin_);
+            end_       = std::move(o.end_);
+            paddedEnd_ = std::move(o.paddedEnd_);
+        }
+        return *this;
+    }
+
+    //! Return the size of the view, i.e with the padding.
+    size_type size() const { return paddedEnd_ - begin_; }
+    //! Whether the reference refers to no memory.
+    bool empty() const { return begin_ == end_; }
+
+    //! Returns an ArrayRef of elements that does not include the padding region.
+    ArrayRef<T> unpaddedArrayRef() { return { begin_, end_ }; }
+    //! Returns an ArrayRef of const elements that does not include the padding region.
+    ArrayRef<const T> unpaddedConstArrayRef() const { return { begin_, end_ }; }
+    //! Returns an ArrayRef of elements that does include the padding region.
+    ArrayRef<T> paddedArrayRef() { return { begin_, paddedEnd_ }; }
+    //! Returns an ArrayRef of const elements that does include the padding region.
+    ArrayRef<const T> paddedConstArrayRef() const { return { begin_, paddedEnd_ }; }
+    //! Returns an identical ArrayRefWithPadding that refers to const elements.
+    ArrayRefWithPadding<const T> constArrayRefWithPadding() const
+    {
+        return { begin_, end_, paddedEnd_ };
+    }
+    /*! \brief
+     * Swaps referenced memory with the other object.
+     *
+     * The actual memory areas are not modified, only the references are
+     * swapped.
+     */
+    void swap(ArrayRefWithPadding<T>& other)
+    {
+        std::swap(begin_, other.begin_);
+        std::swap(end_, other.end_);
+        std::swap(paddedEnd_, other.paddedEnd_);
+    }
+
+private:
+    pointer begin_;
+    pointer end_;
+    pointer paddedEnd_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/math/coordinatetransformation.h b/src/include/gromacs/math/coordinatetransformation.h
new file mode 100644 (file)
index 0000000..0312878
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares coordinate transformation routines.
+ *
+ * \author Christian Blau <blau@kth.se>
+ *
+ * \inlibraryapi
+ * \ingroup module_math
+ */
+#ifndef GMX_MATH_COORDINATETRANSFORMATION_H
+#define GMX_MATH_COORDINATETRANSFORMATION_H
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+
+#include "matrix.h"
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+class ScaleCoordinates
+{
+public:
+    //! Set up coordinate scaling with the scaling factor in each dimension
+    explicit ScaleCoordinates(const RVec& scale);
+    ~ScaleCoordinates();
+
+    //! Copy constructor
+    ScaleCoordinates(const ScaleCoordinates& other);
+    //! Copy assignment
+    ScaleCoordinates& operator=(const ScaleCoordinates& other);
+    //! Move constructor
+    ScaleCoordinates(ScaleCoordinates&& other) noexcept;
+    //! Move assignment
+    ScaleCoordinates& operator=(ScaleCoordinates&& other) noexcept;
+
+    /*! \brief Perform a coordinate transformation on input coordinates.
+     * \param[in] coordinates to be transformed
+     */
+    void operator()(ArrayRef<RVec> coordinates) const;
+
+    /*! \brief Perform a coordinate transformation on an input coordinate.
+     * \param[in] coordinate to be transformed
+     */
+    void operator()(RVec* coordinate) const;
+
+    /*! \brief Apply the inverse scale to coordinates, ignoring dimensions for which scale is zero.
+     * \param[in] coordinates to be transformed
+     */
+    void inverseIgnoringZeroScale(ArrayRef<RVec> coordinates) const;
+
+    /*! \brief Apply the inverse scale to a coordinate, ignoring dimensions for which scale is zero.
+     * \param[in] coordinate to be transformed
+     */
+    void inverseIgnoringZeroScale(RVec* coordinate) const;
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \libinternal \brief Transform coordinates in three dimensions by first
+ * translating, then scaling them.
+ */
+class TranslateAndScale
+{
+public:
+    /*! \brief Construct a three-dimensional coordinate transformation.
+     * Coordinates are first translated, then scaled.
+     * \param[in] translation to be performed on the coordinates
+     * \param[in] scale to be applied to the coordinates
+     */
+    TranslateAndScale(const RVec& scale, const RVec& translation);
+
+    ~TranslateAndScale();
+
+    //! Copy constructor
+    TranslateAndScale(const TranslateAndScale& other);
+    //! Copy assignment
+    TranslateAndScale& operator=(const TranslateAndScale& other);
+    //! Move constructor
+    TranslateAndScale(TranslateAndScale&& other) noexcept;
+    //! Move assignment
+    TranslateAndScale& operator=(TranslateAndScale&& other) noexcept;
+
+    /*! \brief Perform a coordinate transformation on input coordinates.
+     * \param[in] coordinates to be transformed
+     */
+    void operator()(ArrayRef<RVec> coordinates) const;
+
+    /*! \brief Perform a coordinate transformation on a coordinate.
+     * \param[in] coordinate to be transformed
+     */
+    void operator()(RVec* coordinate) const;
+
+    /*! \brief Returns the scaling operation, discarding the translation.
+     */
+    ScaleCoordinates scaleOperationOnly() const;
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \libinternal
+ * \brief Affine transformation of three-dimensional coordinates.
+ *
+ * Perfoms in-place coordinate transformations.
+ *
+ * Coordinates are first multiplied by a matrix, then translated.
+ */
+class AffineTransformation
+{
+public:
+    /*! \brief Construct a three-dimensional affine transformation.
+     * \param[in] matrix to be applied to the vectors
+     * \param[in] translation to be performed on the vectors
+     */
+    AffineTransformation(Matrix3x3ConstSpan matrix, const RVec& translation);
+
+    /*! \brief Perform an affine transformation on input vectors.
+     * \param[in,out] vectors to be transformed in-place
+     */
+    void operator()(ArrayRef<RVec> vectors) const;
+
+    /*! \brief Perform an affine transformation on a vector.
+     * \param[in,out] vector to be transformed in-place
+     */
+    void operator()(RVec* vector) const;
+
+private:
+    //! The matrix describing the affine transformation A(x) = matrix_ * x + translation_
+    Matrix3x3 matrix_;
+    //! The translation vector describing the affine transformation A(x) = matrix * x + translation
+    RVec translation_;
+};
+
+} // namespace gmx
+#endif // CoordinateTransformation
diff --git a/src/include/gromacs/math/densityfit.h b/src/include/gromacs/math/densityfit.h
new file mode 100644 (file)
index 0000000..0f33d62
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares density similarity measures and their derivatives.
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \inlibraryapi
+ * \ingroup module_math
+ */
+#ifndef GMX_MATH_DENSITYFIT_H
+#define GMX_MATH_DENSITYFIT_H
+
+#include <memory>
+
+#include "gromacs/mdspan/extensions.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+/*! \brief
+ * The methods that determine how two densities are compared to one another.
+ */
+enum class DensitySimilarityMeasureMethod : int
+{
+    /*! \brief Measure similarity between densities as normalized inner product of their
+     * voxel values.
+     *
+     * \f[
+     *      \mathrm{Similarity}(\rho_{\mathrm{r}},\rho_{\mathrm{c}}) =
+     *           \frac{1}{N_\mathrm{voxel}}/\sum_{v=1}^{N_\mathrm{voxel}} \rho^v_{\mathrm{r}}
+     * \rho^v_{\mathrm{c}}
+     * \f]
+     */
+    innerProduct,
+
+    /*! \brief Measure similarity between densities by negative relative entropy.
+     * \note Voxels with negative values are ignored.
+     *
+     * \f[
+     *      \mathrm{Similarity}(\rho_{\mathrm{r}},\rho_{\mathrm{c}}) =
+     *           \sum_{v=1}^{N_\mathrm{voxel}}
+     *              \rho^v_{\mathrm{r}} (\log(\rho^v_{\mathrm{c}}) - \log(\rho^v_{\mathrm{r}}))
+     * \f]
+     */
+    relativeEntropy,
+
+    /*! \brief Measure similarity between densities by cross correlation.
+     *
+     * \f[
+     *      \mathrm{Similarity}(\rho_{\mathrm{r}},\rho_{\mathrm{c}}) =
+     *           \frac{\sum_{v}\left((\rho_{\mathrm{r}} - \bar{\rho}_{\mathrm{r}})(\rho_{\mathrm{c}} - \bar{\rho}_{\mathrm{c}})\right)}
+     *           {\sqrt{\sum_v(\rho_{\mathrm{r}} - \bar{\rho}_{\mathrm{r}})^2 \sum_v (\rho_{\mathrm{c}} - \bar{\rho}_{\mathrm{c}})^2}}
+     * \f]
+     */
+    crossCorrelation,
+    Count,
+};
+
+/* Forward declaration of implementation class outside class to allow
+ * choose implementation class during construction of the DensitySimilarityMeasure*/
+class DensitySimilarityMeasureImpl;
+
+/*! \libinternal \brief
+ *  Measure similarity and gradient between densities.
+ */
+class DensitySimilarityMeasure
+{
+public:
+    //! a three-dimensional const view into density data
+    using density = basic_mdspan<const float, dynamicExtents3D>;
+    /*! \brief Chose comparison method and set reference density.
+     * \param[in] method defines how densities are compared to one another
+     * \param[in] referenceDensity
+     * \throws NotImplementedError if method is not known
+     */
+    DensitySimilarityMeasure(DensitySimilarityMeasureMethod method, density referenceDensity);
+    ~DensitySimilarityMeasure();
+    //! Copy constructor
+    DensitySimilarityMeasure(const DensitySimilarityMeasure& other);
+    //! Copy assignment
+    DensitySimilarityMeasure& operator=(const DensitySimilarityMeasure& other);
+    //! Move constructor
+    DensitySimilarityMeasure(DensitySimilarityMeasure&& other) noexcept;
+    //! Move assignment
+    DensitySimilarityMeasure& operator=(DensitySimilarityMeasure&& other) noexcept;
+
+    /*! \brief Derivative of the density similarity measure at all voxels.
+     * \param[in] comparedDensity the variable density
+     * \returns density similarity measure derivative
+     */
+    density gradient(density comparedDensity);
+    /*! \brief Similarity between reference and compared density.
+     * \param[in] comparedDensity the variable density
+     * \returns density similarity
+     */
+    real similarity(density comparedDensity);
+
+private:
+    std::unique_ptr<DensitySimilarityMeasureImpl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/math/densityfittingforce.h b/src/include/gromacs/math/densityfittingforce.h
new file mode 100644 (file)
index 0000000..904ddea
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares classes to calculate density fitting forces.
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \inlibraryapi
+ * \ingroup module_math
+ */
+
+#include <memory>
+
+#include "gromacs/math/gausstransform.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdspan/extensions.h"
+#include "gromacs/mdspan/mdspan.h"
+
+namespace gmx
+{
+
+
+/*! \libinternal
+ * \brief
+ * Manages evaluation of density-fitting forces for particles that were spread
+ * with a kernel.
+ *
+ * The density fitting force is the inner product of the derivative of the
+ * density-density goodness-of-fit function at all density values and the
+ * gradient of the spreading kernel used to simulate the density.
+ *
+ * For a discrete Gauss transform spreading kernel and density derivative \f$D\f$
+ * it is the Gauss transform of a single coordinate, weighted by a distance vector
+ * and density derivative at each lattice point
+ * \f[
+ *  \sum_v D_v \frac{\vec{x_v} - \vec{x}}{\sigma^2}
+ *  \frac{1}{\sigma^3 \sqrt(2^3*\pi^3)} * \exp(-\frac{(x_v-x)^2}{2 \sigma^2})
+ * \f]
+ *
+ */
+class DensityFittingForce
+{
+public:
+    /*! \brief Construct density fitting force evaluator.
+     * \param[in] kernelShapeParameters the global parameters of the density spreading kernel
+     */
+    DensityFittingForce(const GaussianSpreadKernelParameters::Shape& kernelShapeParameters);
+
+    ~DensityFittingForce();
+    //! Copy constructor
+    DensityFittingForce(const DensityFittingForce& other);
+    //! Copy assignment
+    DensityFittingForce& operator=(const DensityFittingForce& other);
+    //! Move constructor
+    DensityFittingForce(DensityFittingForce&& other) noexcept;
+    //! Move assignment
+    DensityFittingForce& operator=(DensityFittingForce&& other) noexcept;
+    /*! \brief
+     * Evaluate density-fitting force between a reference density and the
+     * density generated by a kernel.
+     *
+     * Implements the equation in the class description.
+     *
+     * \param[in] localParameters   local parameters of the spreading kernel
+     * \param[in] densityDerivative the spatial derivative of the similarity
+     *                              measure between the reference density and
+     *                              a density that was generated using the
+     *                              spreading kernel
+     *
+     * \returns the force that increases the measure of the goodness of fit used to calculate the density derivative
+     */
+    RVec evaluateForce(const GaussianSpreadKernelParameters::PositionAndAmplitude& localParameters,
+                       basic_mdspan<const float, dynamicExtents3D> densityDerivative);
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
diff --git a/src/include/gromacs/math/exponentialmovingaverage.h b/src/include/gromacs/math/exponentialmovingaverage.h
new file mode 100644 (file)
index 0000000..27a664b
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares an exponential moving average class.
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \inlibraryapi
+ * \ingroup module_math
+ */
+#ifndef GMX_MATH_EXPONENTIALMOVINGAVERAGE_H
+#define GMX_MATH_EXPONENTIALMOVINGAVERAGE_H
+
+#include "gromacs/utility/keyvaluetreebuilder.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief Store the state of exponential moving averages.
+ */
+struct ExponentialMovingAverageState
+{
+    //! The weighted sum
+    real weightedSum_ = 0;
+
+    //! The weighted count, used for bias correction
+    real weightedCount_ = 0;
+
+    //! Remember if adding the latest data point increased the average
+    bool increasing_ = false;
+};
+
+//! Convert the exponential moving average state as key-value-tree object
+void exponentialMovingAverageStateAsKeyValueTree(KeyValueTreeObjectBuilder            builder,
+                                                 const ExponentialMovingAverageState& state);
+
+//! Sets the expoential moving average state from a key-value-tree object
+ExponentialMovingAverageState exponentialMovingAverageStateFromKeyValueTree(const KeyValueTreeObject& object);
+
+/*! \libinternal
+ * \brief Evaluate the exponential moving average with bias correction.
+ *
+ * The exponential moving average at the 0th data point \f$Y_0\f$ is
+ * \f$ S_0 = Y_0 \f$ and at the n-th data point \f$Y_n\f$ with n>0 it is
+ * \f$ S_n = \alpha Y_n + (1-\alpha) S_{n-1} \f$, where the smoothing factor
+ * \f$\alpha=1/t\f$ is determined via a time constant \f$t\f$.
+ *
+ * To avoid large impact of the first data point in a "burn-in" phase, the
+ * weight of points are unbiased by substituting for \f$S_{n-1}\f$ above,
+ * \f$\hat{S}_{n-1} = S_{n-1} / (1-\alpha^{n})\f$.
+ */
+class ExponentialMovingAverage
+{
+public:
+    /*! \brief Construct by setting the time constant and state.
+     * Allows reinitiating with data from memory.
+     * \param[in] timeConstant time in number of data points
+     * \param[in] state of the exponential moving average
+     * \throws InconsistentInputError if timeConstant < 1
+     */
+    ExponentialMovingAverage(real timeConstant, const ExponentialMovingAverageState& state = {});
+
+    //! Update the moving average with a data point
+    void updateWithDataPoint(real dataPoint);
+
+    //! The exponential weighted average with bias correction
+    real biasCorrectedAverage() const;
+
+    //! Returns true if last added data point increased the average
+    bool increasing() const;
+
+    //! Return the current state of the exponential moving average
+    const ExponentialMovingAverageState& state() const;
+
+    //! The inverse time constant for the exponential moving average
+    real inverseTimeConstant() const;
+
+private:
+    //! The current state of the exponential moving average
+    ExponentialMovingAverageState state_;
+
+    //! The inverse time constant for the exponential moving average
+    real inverseTimeConstant_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/math/gausstransform.h b/src/include/gromacs/math/gausstransform.h
new file mode 100644 (file)
index 0000000..f00e43f
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares Gaussian function evaluations on lattices and related functionality
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \inlibraryapi
+ * \ingroup module_math
+ */
+#ifndef GMX_MATH_GAUSSTRANSFORM_H
+#define GMX_MATH_GAUSSTRANSFORM_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/math/multidimarray.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdspan/extensions.h"
+#include "gromacs/mdspan/mdspan.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+/*! \internal
+ * \brief Provide result of Gaussian function evaluation on a one-dimensional lattice.
+ *
+ * This class owns the result of the operation and provides a view on it.
+ *
+ * The distance between lattice points is one. Unit length is normalized by the
+ * lattice spacing, thus spreading width and range are given as multiples of
+ * lattice points distances.
+ *
+ * This works well as approximation to piece-wise integration of a Gaussian on a
+ * lattice, when \f$\sigma > 1\f$. The maximal relative error to evaluating erf
+ * differences for \f$\sigma = 1\f$ is 0.01602.
+ *
+ */
+class GaussianOn1DLattice
+{
+public:
+    /*! \brief Construct Gaussian spreader with spreading range and Gaussian width.
+     *
+     * Spread weights are distributed over a non-periodic lattice of length
+     * 2*numGridPointsForSpreadingHalfWidth+1. The lattice comprises a center point and
+     * spreadDistance points to the left and to the right.
+     *
+     * \note There is a maximum spreading width
+     *
+     * \param[in] numGridPointsForSpreadingHalfWidth maximum distance in number of gridpoints from 0
+     * \param[in] sigma Gaussian width.
+     */
+    GaussianOn1DLattice(int numGridPointsForSpreadingHalfWidth, real sigma);
+    ~GaussianOn1DLattice();
+    //! Copy constructor
+    GaussianOn1DLattice(const GaussianOn1DLattice& other);
+    //! Copy assignment
+    GaussianOn1DLattice& operator=(const GaussianOn1DLattice& other);
+    //! Move constructor
+    GaussianOn1DLattice(GaussianOn1DLattice&& other) noexcept;
+    //! Move assignment
+    GaussianOn1DLattice& operator=(GaussianOn1DLattice&& other) noexcept;
+    /*! \brief Spreads weight onto grid points in one dimension.
+     *
+     *
+     *            .            :            |            :            .
+     *            o            o            o            o            o
+     *                                  O---|
+     *                          latticeOffset
+     * O - atom position
+     * o - lattice positions
+     * . : | spreading value at grid points.
+     *
+     * \note Highest numerical accuracy is achieved when the spreading
+     *       with offset to the nearest lattice coordinated < 0.5
+     *
+     * Spreading on lattice coordinate \f$x_i\f$
+     * \f[
+     *      f(x_i) = \frac{\mathrm{amplitude}}{\sigma\sqrt{2\pi}}\exp(-\frac{\mathrm{offset}-x_i^2}{2 \sigma ^2})
+     * \f]
+     * \param[in] amplitude of the Gaussian spread.
+     * \param[in] latticeOffset The distance to the nearest grid point in lattice coordinates.
+     */
+    void spread(double amplitude, real latticeOffset);
+    /*! \brief Returns view on spread result. */
+    ArrayRef<const float> view();
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \libinternal \brief Parameters for density spreading kernels.
+ */
+struct GaussianSpreadKernelParameters
+{
+    /*! \libinternal \brief Shape parameters for Gaussian spreading kernels describe
+     * the kernel shape.
+     */
+    struct Shape
+    {
+        //! The width of the Gaussian function in lattice spacings
+        DVec sigma_;
+        //! The range of the spreading function in multiples of sigma
+        double spreadWidthMultiplesOfSigma_;
+        //! The spread range in lattice coordinates
+        IVec latticeSpreadRange() const;
+    };
+    /*! \libinternal \brief Parameters that describe the kernel position and amplitude.
+     */
+    struct PositionAndAmplitude
+    {
+        //! position of the kernel to be spread onto the lattice
+        const RVec& coordinate_;
+        //! amplitude of the spread kernel
+        real amplitude_;
+    };
+};
+
+/*! \libinternal \brief Sums Gaussian values at three dimensional lattice coordinates.
+ * The Gaussian is defined as \f$A \frac{1}{\sigma^3 \sqrt(2^3\pi^3)} * \exp(-\frac{(x-x0)^2}{2
+ \sigma^2})\f$ \verbatim x0:              X           x
+               /   \        / \
+             --     --    --   --
+   lattice: |    |    |    |    |    |    |
+   \endverbatim
+ * The lattice has spacing 1, all coordinates are given with respect to the lattice
+ * coordinates.
+ */
+class GaussTransform3D
+{
+public:
+    /*! \brief Construct a three-dimensional Gauss transform.
+     *
+     * Transform lattice values will be zero-initialized.
+     *
+     * \param[in] extent of the spread lattice
+     * \param[in] globalParameters of the spreading kernel
+     */
+    GaussTransform3D(const dynamicExtents3D&                      extent,
+                     const GaussianSpreadKernelParameters::Shape& globalParameters);
+
+    ~GaussTransform3D();
+
+    //! Copy constructor
+    GaussTransform3D(const GaussTransform3D& other);
+
+    //! Copy assignment
+    GaussTransform3D& operator=(const GaussTransform3D& other);
+
+    //! Move constructor
+    GaussTransform3D(GaussTransform3D&& other) noexcept;
+
+    //! Move assignment
+    GaussTransform3D& operator=(GaussTransform3D&& other) noexcept;
+
+    /*! \brief Add a three dimensional Gaussian with given amplitude at a coordinate.
+     * \param[in] localParameters of the spreading kernel
+     */
+    void add(const GaussianSpreadKernelParameters::PositionAndAmplitude& localParameters);
+
+    //! \brief Set all values on the lattice to zero.
+    void setZero();
+
+    //! Return a view on the spread lattice.
+    basic_mdspan<float, dynamicExtents3D> view();
+
+    //! Return a const view on the spread lattice.
+    basic_mdspan<const float, dynamicExtents3D> constView() const;
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \internal \brief A 3-orthotope over integer intervals.
+ */
+class IntegerBox
+{
+public:
+    //! Construct from begin and end
+    IntegerBox(const IVec& begin, const IVec& end);
+    //! Begin indices of the box
+    const IVec& begin() const;
+    //! End indices of the box
+    const IVec& end() const;
+    //! Empty if for any dimension, end <= begin;
+    bool empty() const;
+
+private:
+    const IVec begin_; //< interger indices denoting begin of box
+    const IVec end_;   //< integer indices denoting one-past end of box in any dimension
+};
+
+/*! \brief Construct a box that holds all indices that are not more than a given range remote from
+ * center coordinates and still within a given lattice extent.
+ *
+ * \param[in] center the coordinates of the center of the spread range
+ * \param[in] extent the end of the lattice, number of lattice points in each dimension
+ * \param[in] range the distance from the center
+ * \returns box describing the range of indices
+ */
+IntegerBox spreadRangeWithinLattice(const IVec& center, dynamicExtents3D extent, IVec range);
+
+/*! \internal \brief Evaluate the outer product of two number ranges.
+ * Keeps the memory for the outer product allocated.
+ */
+class OuterProductEvaluator
+{
+public:
+    //! Evaluate the outer product of two float number ranges.
+    mdspan<const float, dynamic_extent, dynamic_extent> operator()(ArrayRef<const float> x,
+                                                                   ArrayRef<const float> y);
+
+private:
+    MultiDimArray<std::vector<float>, extents<dynamic_extent, dynamic_extent>> data_;
+};
+
+} // namespace gmx
+
+#endif /* end of include guard: GMX_MATH_GAUSSTRANSFORM_H */
diff --git a/src/include/gromacs/math/gmxcomplex.h b/src/include/gromacs/math/gmxcomplex.h
new file mode 100644 (file)
index 0000000..87069cf
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MATH_GMXCOMPLEX_H
+#define GMX_MATH_GMXCOMPLEX_H
+
+#include <cmath>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+struct t_complex
+{
+    real re, im;
+};
+
+static t_complex rcmul(real r, t_complex c)
+{
+    t_complex d;
+
+    d.re = r * c.re;
+    d.im = r * c.im;
+
+    return d;
+}
+
+static inline t_complex rcexp(real r)
+{
+    t_complex c;
+
+    c.re = cos(r);
+    c.im = sin(r);
+
+    return c;
+}
+
+
+static inline t_complex cadd(t_complex a, t_complex b)
+{
+    t_complex c;
+
+    c.re = a.re + b.re;
+    c.im = a.im + b.im;
+
+    return c;
+}
+
+static inline t_complex csub(t_complex a, t_complex b)
+{
+    t_complex c;
+
+    c.re = a.re - b.re;
+    c.im = a.im - b.im;
+
+    return c;
+}
+
+static t_complex cmul(t_complex a, t_complex b)
+{
+    t_complex c;
+
+    c.re = a.re * b.re - a.im * b.im;
+    c.im = a.re * b.im + a.im * b.re;
+
+    return c;
+}
+
+static t_complex conjugate(t_complex c)
+{
+    t_complex d;
+
+    d.re = c.re;
+    d.im = -c.im;
+
+    return d;
+}
+
+static inline real cabs2(t_complex c)
+{
+    real abs2;
+    abs2 = (c.re * c.re) + (c.im * c.im);
+
+    return abs2;
+}
+
+static inline t_complex cdiv(t_complex teller, t_complex noemer)
+{
+    t_complex res, anoemer;
+
+    anoemer = cmul(conjugate(noemer), noemer);
+    res     = cmul(teller, conjugate(noemer));
+
+    return rcmul(1.0 / anoemer.re, res);
+}
+
+inline bool operator==(const t_complex& lhs, const t_complex& rhs)
+{
+    return (lhs.re == rhs.re) && (lhs.im == rhs.im);
+}
+inline bool operator!=(const t_complex& lhs, const t_complex& rhs)
+{
+    return !(lhs == rhs);
+}
+
+#endif
diff --git a/src/include/gromacs/math/invertmatrix.h b/src/include/gromacs/math/invertmatrix.h
new file mode 100644 (file)
index 0000000..6bd31fe
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares routines to invert 3x3 matrices
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_math
+ * \inlibraryapi
+ */
+#ifndef GMX_MATH_INVERTMATRIX_H
+#define GMX_MATH_INVERTMATRIX_H
+
+#include "gromacs/math/vec.h"
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+
+/*! \brief Invert a simulation-box matrix in \c src, return in \c dest
+ *
+ * This routine assumes that src is a simulation-box matrix, i.e. has
+ * zeroes in the upper-right triangle. A fatal error occurs if the
+ * product of the leading diagonal is too small. The inversion can be
+ * done "in place", i.e \c src and \c dest can be the same matrix.
+ */
+void invertBoxMatrix(const matrix src, matrix dest);
+
+/*! \brief Invert a general 3x3 matrix in \c src, return in \c dest
+ *
+ * A fatal error occurs if the determinant is too small. \c src and
+ * \c dest cannot be the same matrix.
+ */
+void invertMatrix(const matrix src, matrix dest);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/math/matrix.h b/src/include/gromacs/math/matrix.h
new file mode 100644 (file)
index 0000000..b7b03fa
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal
+ * \file
+ * \brief Declares special case of 3x3 matrix frequently used, and associated functions.
+ *
+ * \author Christian Blau <cblau@gwdg.de>
+ * \ingroup module_math
+ */
+
+#ifndef GMX_MATH_MATRIX_H_
+#define GMX_MATH_MATRIX_H_
+
+#include <array>
+
+#include "gromacs/math/multidimarray.h"
+#include "gromacs/math/utilities.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*! \brief Three-by-three matrix of ElementType.
+ * \tparam ElementType type of element to be stored in matrix
+ */
+template<class ElementType>
+using BasicMatrix3x3 = MultiDimArray<std::array<ElementType, 3 * 3>, extents<3, 3>>;
+
+/*! \brief Three-by-three real number matrix.
+ * \note will replace the C-style real[3][3] "matrix"
+ */
+using Matrix3x3 = BasicMatrix3x3<real>;
+//! Convenience alias for a matrix view
+using Matrix3x3Span = Matrix3x3::view_type;
+//! Convenience alias for a const matrix view
+using Matrix3x3ConstSpan = Matrix3x3::const_view_type;
+
+//! Determinant of a 3x3 matrix
+constexpr real determinant(Matrix3x3ConstSpan matrix)
+{
+    return (matrix(0, 0) * (matrix(1, 1) * matrix(2, 2) - matrix(2, 1) * matrix(1, 2))
+            - matrix(1, 0) * (matrix(0, 1) * matrix(2, 2) - matrix(2, 1) * matrix(0, 2))
+            + matrix(2, 0) * (matrix(0, 1) * matrix(1, 2) - matrix(1, 1) * matrix(0, 2)));
+}
+
+//! Calculates the trace of a 3x3 matrix view
+constexpr real trace(Matrix3x3ConstSpan matrixView)
+{
+    return matrixView(0, 0) + matrixView(1, 1) + matrixView(2, 2);
+}
+
+/*! \brief Create an identity matrix of ElementType with N * M elements.
+ *
+ * \tparam ElementType type of matrix elements
+ * \tparam N number of rows
+ * \tparam M number of columns, defaults to number of rows if not set
+ *
+ * \returns a matrix with values one where row equals column index and null
+ *          where row does not equal column index
+ */
+template<typename ElementType, int N, int M = N>
+MultiDimArray<std::array<ElementType, N * M>, extents<N, M>> identityMatrix()
+{
+    std::array<ElementType, N * M>                               matrixEntries{};
+    MultiDimArray<std::array<ElementType, N * M>, extents<N, M>> idMatrix(matrixEntries);
+    for (int i = 0; i < std::min(N, M); i++)
+    {
+        idMatrix(i, i) = 1;
+    }
+    return idMatrix;
+}
+
+//! Calculate the transpose of a 3x3 matrix, from its view
+Matrix3x3 transpose(Matrix3x3ConstSpan matrixView);
+
+//! Multiply matrix with vector.
+void matrixVectorMultiply(Matrix3x3ConstSpan matrix, RVec* v);
+
+//! Create new matrix type from legacy type.
+static inline Matrix3x3 createMatrix3x3FromLegacyMatrix(const matrix legacyMatrix)
+{
+    GMX_RELEASE_ASSERT(legacyMatrix, "Need valid legacy matrix");
+    Matrix3x3 newMatrix;
+    for (int i = 0; i < DIM; i++)
+    {
+        for (int j = 0; j < DIM; j++)
+        {
+            newMatrix(i, j) = legacyMatrix[i][j];
+        }
+    }
+    return newMatrix;
+}
+
+//! Fill legacy matrix from new matrix type.
+static inline void fillLegacyMatrix(Matrix3x3ConstSpan newMatrix, matrix legacyMatrix)
+{
+    GMX_RELEASE_ASSERT(legacyMatrix, "Need valid legacy matrix");
+    for (int i = 0; i < DIM; i++)
+    {
+        for (int j = 0; j < DIM; j++)
+        {
+            legacyMatrix[i][j] = newMatrix(i, j);
+        }
+    }
+}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/math/multidimarray.h b/src/include/gromacs/math/multidimarray.h
new file mode 100644 (file)
index 0000000..867ac72
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal
+ * \file
+ * \brief Declares MultiDimArray.
+ *
+ * \author Christian Blau <cblau@gwdg.de>
+ * \ingroup module_math
+ * \ingroup module_mdspan
+ * \inlibraryapi
+ */
+
+#ifndef GMX_MATH_MULTIDIMARRAY_H_
+#define GMX_MATH_MULTIDIMARRAY_H_
+
+#include "gromacs/mdspan/mdspan.h"
+#include "gromacs/utility/arrayref.h"
+
+namespace gmx
+{
+
+namespace detail
+{
+//! Same as std::void_t from C++17
+template<class...>
+using void_t = void;
+
+template<typename T, typename = void>
+struct is_resizable : std::false_type
+{
+};
+
+template<typename T>
+struct is_resizable<T, void_t<decltype(std::declval<T>().resize(size_t()))>> : std::true_type
+{
+};
+
+//! Type has a resize member function callable with size_t argument
+template<typename T>
+// NOLINTNEXTLINE misc-definitions-in-headers
+constexpr bool is_resizable_v = is_resizable<T>::value;
+} // namespace detail
+
+/*! \libinternal \brief
+ * Multidimensional array that manages its own memory.
+ *
+ * \note No bounds checking when accessing memory
+ *
+ * \note That the view holds a valid pointer to the data is a class invariant.
+ *
+ * The Container type that stores the data may be resizable (std::vector or similar)
+ * or static (std::array or similar). Copy and move assignment routines as well as
+ * swapping are designed to yield good performances in both cases, notably
+ * foregoing the copy-swap idiom due to the bad performance in swapping std::array.
+ *
+ * This class avoids throwing exeptions, apart from the ones that might be thrown
+ * from the containers during resizing an allocation. (bad_malloc from std::vector
+ * is a likely candidate)
+ *
+ *
+ * Holds as many elements as required by a multidimensional view.
+ * \tparam TContainer   Data type container for the data to be stored
+ *                      as MultiDimArray with random element access and
+ *                      value_type, refence and const_reference exposed
+ * \tparam Extents      An extents class describing the array dimensions
+ *                      as used in module_mdspan
+ * \tparam LayoutPolicy The data layout as in module_mdspan describes
+ *                      translation of indices to memory offset.
+ *                      Defaults to right aligned, so that the right-most index
+ *                      is contiguous in memory.
+ */
+template<class TContainer, class Extents, class LayoutPolicy = layout_right>
+class MultiDimArray
+{
+public:
+    //! the type of values that are stored
+    using value_type = typename TContainer::value_type;
+    //! reference type to the stored values
+    using reference = typename TContainer::reference;
+    //! const reference type to the stored values
+    using const_reference = typename TContainer::const_reference;
+    //! the view used to access the data
+    using view_type = basic_mdspan<value_type, Extents, LayoutPolicy>;
+    //! const view on the data
+    using const_view_type = basic_mdspan<const value_type, Extents, LayoutPolicy>;
+    /*! \brief Iterator type for contiguous iteration over the stored data.
+     * Used, e.g., in free begin and end functions
+     */
+    using iterator = typename ArrayRef<value_type>::iterator;
+    /*! \brief Const iterator type for contiguous iteration over the stored data.
+     *  used, e.g., in free begin and end functions
+     */
+    using const_iterator = const typename ArrayRef<const value_type>::const_iterator;
+
+    static_assert(detail::is_resizable_v<TContainer> == (Extents::rank_dynamic() > 0),
+                  "Resizable container (e.g. std::vector) requires at least one dynamic rank. "
+                  "Non-resizable container (e.g. std::array) requires zero dynamic ranks.");
+
+    /*! \brief
+     * Allocate dynamic array data and set view with the dynamic extents.
+     *
+     * \param[in] dynamicExtent A parameter pack that describes the dynamic
+     *                          size of the array. Empty if purely static.
+     *
+     * \tparam IndexType        Parameter pack type holding the dynamic
+     *                          extents of the multidimensional array
+     */
+    template<class... IndexType, typename T = TContainer, typename = typename std::enable_if_t<detail::is_resizable_v<T>>>
+    MultiDimArray(IndexType... dynamicExtent)
+    {
+        resize(dynamicExtent...);
+    }
+    /*! \brief
+     * Construction from fixed sized arrays if the array size is static and
+     * layout policy allows compile time determination of the container size.
+     *
+     * Enables the expected initialization
+     * MultiDimArray<std::array<float, 9>, extents<3,3>> arr = {{1,2...}}
+     * \tparam T Template parameter for activation via SFINAE.
+     */
+    // SFINAE required because std::vector::size isn't constexpr and is_constexpr doesn't exist.
+    template<typename T = TContainer, typename = typename std::enable_if_t<!detail::is_resizable_v<T>>>
+    constexpr MultiDimArray(const TContainer& data = {}) noexcept : data_(data), view_(data_.data())
+    {
+        static_assert(TContainer().size() == typename view_type::mapping_type().required_span_size(),
+                      "Non-resizable container type size must match static MultiDimArray size.");
+    }
+    //! Copy constructor
+    constexpr MultiDimArray(const MultiDimArray& o) :
+        data_(o.data_), view_(data_.data(), o.view_.extents())
+    {
+    }
+    //! Move constructor
+    MultiDimArray(MultiDimArray&& o) noexcept :
+        data_(std::move(o.data_)), view_(data_.data(), o.view_.extents())
+    {
+    }
+    //! Copy assignment
+    MultiDimArray& operator=(const MultiDimArray& o)
+    {
+        data_ = o.data_;
+        view_ = view_type(data_.data(), o.view_.extents());
+        return *this;
+    }
+    //! Move assignment
+    MultiDimArray& operator=(MultiDimArray&& o) noexcept
+    {
+        data_ = std::move(o.data_);
+        view_ = view_type(data_.data(), o.view_.extents());
+        return *this;
+    }
+    //! Swaps content with other
+    void swap(MultiDimArray& o)
+    {
+        using std::swap;
+        swap(data_, o.data_);
+        // swap(view_, o.view_) also swaps the pointer to the data and thus does not work
+        // instead, restore the view as class invariant after the data swapping operation
+        o.view_ = view_type(o.data_.data(), view_.extents());
+        view_   = view_type(data_.data(), o.view_.extents());
+    }
+    /*! \brief
+     * Resize the dynamic extents of the array if any and set container size
+     * accordingly.
+     *
+     * Invalidates data and views of this array.
+     *
+     * \param[in] dynamicExtent A parameter pack that describes the dynamic
+     *                          size of the array. Empty if purely static.
+     * \tparam IndexType        Parameter pack type holding the dynamic
+     *                          extents of the multidimensional array
+     */
+    template<class... IndexType>
+    void resize(IndexType... dynamicExtent)
+    {
+        // use a mapping object to determine the required span size;
+        layout_right::mapping<Extents> map{ Extents{ dynamicExtent... } };
+        data_.resize(map.required_span_size());
+        // to construct a valid view on the data, the container has to be resized before
+        // the assignment, so that data_.data() is valid
+        view_ = view_type(data_.data(), dynamicExtent...);
+    }
+    /*! \brief Data access via multidimensional indices.
+     * This allows referencing rank R array elements as array(x_0,x_1,x_2, .., x_R)
+     *
+     * \param[in] index multidimensional indices as parameter pack
+     *                  the number of parameters must match the rank of the array.
+     *
+     * \returns reference to array element
+     */
+    template<class... IndexType>
+    reference operator()(IndexType... index) noexcept
+    {
+        return view_(index...);
+    }
+    /*! \brief Const data access via multidimensional indices.
+     * This allows referencing rank R array elements as array(x_0,x_1,x_2, .., x_R)
+     *
+     * \param[in] index multidimensional indices as parameter pack
+     *                  the number of parameters must match the rank of the array.
+     *
+     * \returns const reference to array element
+     */
+    template<class... IndexType>
+    constexpr const_reference operator()(IndexType... index) const noexcept
+    {
+        return view_(index...);
+    }
+    /*! \brief Contiguous access to the data.
+     * \returns ArrayRef to stored data.
+     */
+    ArrayRef<value_type> toArrayRef() { return { data_.data(), data_.data() + data_.size() }; }
+    /*! \brief Contiguous const access to the data.
+     * \returns ArrayRef to stored data.
+     */
+    constexpr ArrayRef<const value_type> toArrayRef() const
+    {
+        return { data_.data(), data_.data() + data_.size() };
+    }
+    /*! \brief Return the extent.
+     * \param[in] k dimension to query for extent
+     * \returns extent along specified dimension
+     */
+    constexpr typename view_type::index_type extent(int k) const noexcept
+    {
+        return view_.extent(k);
+    }
+    //! Conversion to multidimensional view on the data
+    constexpr view_type asView() noexcept { return view_; }
+    //! Conversion to const multidimensional view on the data
+    constexpr const_view_type asConstView() const noexcept
+    {
+        return { data_.data(), view_.mapping() };
+    }
+
+private:
+    //! The contiguous data that is equipped with multidimensional indexing in this class
+    TContainer data_;
+    //! Multidimensional view into data_.
+    view_type view_;
+};
+
+//! Free MultiDimArray begin function addressing its contiguous memory.
+template<class TContainer, class Extents>
+constexpr typename MultiDimArray<TContainer, Extents>::const_iterator
+begin(const MultiDimArray<TContainer, Extents>& multiDimArray)
+{
+    return multiDimArray.toArrayRef().begin();
+}
+
+//! Free MultiDimArray begin function addressing its contiguous memory.
+template<class TContainer, class Extents>
+constexpr typename MultiDimArray<TContainer, Extents>::iterator begin(MultiDimArray<TContainer, Extents>& multiDimArray)
+{
+    return multiDimArray.toArrayRef().begin();
+}
+
+//! Free MultiDimArray end function addressing its contiguous memory.
+template<class TContainer, class Extents>
+constexpr typename MultiDimArray<TContainer, Extents>::const_iterator
+end(const MultiDimArray<TContainer, Extents>& multiDimArray)
+{
+    return multiDimArray.toArrayRef().end();
+}
+
+//! Free MultiDimArray end function addressing its contiguous memory.
+template<class TContainer, class Extents>
+constexpr typename MultiDimArray<TContainer, Extents>::iterator end(MultiDimArray<TContainer, Extents>& multiDimArray)
+{
+    return multiDimArray.toArrayRef().end();
+}
+
+//! Swap function
+template<class TContainer, class Extents>
+void swap(MultiDimArray<TContainer, Extents>& a, MultiDimArray<TContainer, Extents>& b) noexcept
+{
+    a.swap(b);
+}
+
+} // namespace gmx
+
+#endif // GMX_MATH_MULTIDIMARRAY_H_
diff --git a/src/include/gromacs/math/neldermead.h b/src/include/gromacs/math/neldermead.h
new file mode 100644 (file)
index 0000000..6036ebf
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declare classes to aid Nelder-Mead downhill simplex optimisation.
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_math
+ */
+
+#ifndef GMX_MATH_NELDERMEAD_H
+#define GMX_MATH_NELDERMEAD_H
+
+#include <functional>
+#include <list>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief Tie together coordinate and function value at this coordinate.
+ */
+struct RealFunctionvalueAtCoordinate
+{
+    //! Vertex coordinate
+    std::vector<real> coordinate_;
+    //! Function value at this coordinate
+    real value_;
+};
+
+/*! \internal
+ * \brief The simplex for the Nelder-Mead algorithm.
+ *
+ * Contains the N+1 simplex N-dimensional coordinates and its function values.
+ * Allows for simplex manipulations as needed for the Nelder-Mead algorithm.
+ *
+ * \note Keeps the simplex sorted according to function values with the simplex
+ *       at the lowest function value first.
+ */
+class NelderMeadSimplex
+{
+public:
+    /*! \brief Set up Nelder-Mead simplex from an initial guess.
+     *
+     * \note Triggers N+1 function evaluations at all simplex points.
+     *
+     * \param[in] f the function to be evaluated
+     * \param[in] initalGuess initial guess of the coordinates.
+     *
+     */
+    NelderMeadSimplex(const std::function<real(ArrayRef<const real>)>& f, ArrayRef<const real> initalGuess);
+
+    //! Return the vertex with the lowest function value at any of the simplex vertices.
+    const RealFunctionvalueAtCoordinate& bestVertex() const;
+
+    //! Return the vertex of the simplex with the highest (worst) function value.
+    const RealFunctionvalueAtCoordinate& worstVertex() const;
+
+    //! Return the second largest function value at any of the simplex vertices.
+    real secondWorstValue() const;
+
+    //! Return the reflection point and the evaluated function value at this point.
+    RealFunctionvalueAtCoordinate
+    evaluateReflectionPoint(const std::function<real(ArrayRef<const real>)>& f) const;
+
+    //! Evaluate and return the expansion point and function value.
+    RealFunctionvalueAtCoordinate evaluateExpansionPoint(const std::function<real(ArrayRef<const real>)>& f) const;
+
+    //! Evaluate and return the contraction point and function value.
+    RealFunctionvalueAtCoordinate
+    evaluateContractionPoint(const std::function<real(ArrayRef<const real>)>& f) const;
+
+    /*! \brief Replace the simplex vertex with the largest function value.
+     *
+     * \param[in] newVertex to replace the worst vertex with
+     * \note keeps the simplex list sorted and reevaluates the reflection point
+     */
+    void swapOutWorst(const RealFunctionvalueAtCoordinate& newVertex);
+
+    /*! \brief Shrink the simplex.
+     *
+     * All points move closer to the best point by a factor \f$\sigma\f$.
+     *
+     * Replace all point coordinates, except the best, with
+     * \f$x_i = x_{\mathrm{best}} + \sigma (x_i - x_{\mathrm{best}})\f$
+     */
+    void shrinkSimplexPointsExceptBest(const std::function<real(ArrayRef<const real>)>& f);
+
+    /*! \brief The oriented length of the vertex.
+     *
+     * The oriented length of the simplex is defined as the largest distance
+     * between the first simplex vertex coordinate (with the lowest, best function
+     * value) and any other simplex coordinate.
+     *
+     * The oriented length is used as a computationally fast and simple
+     * convergence criterion because it is proven that
+     * orientedLegnth < simplex_diameter < 2 * orientedLength
+     *
+     */
+    real orientedLength() const;
+
+private:
+    /*! \brief Update centroid and reflection point.
+     *
+     * The arithmetic mean of all vertex coordinates expect the one at the
+     * highest (worst) function value.
+     *
+     */
+    void updateCentroidAndReflectionPoint();
+
+    /*! \brief The points of the simplex with the function values.
+     * \note This list stays sorted according to function value during the
+     *       life-time of this object.
+     */
+    std::list<RealFunctionvalueAtCoordinate> simplex_;
+
+    //! The centroid of the simplex, skipping the worst point is updated once the simplex changes
+    std::vector<real> centroidWithoutWorstPoint_;
+
+    //! The reflection point and its function value is updated once the simplex changes
+    std::vector<real> reflectionPointCoordinates_;
+};
+
+
+} // namespace gmx
+
+#endif // GMX_MATH_NELDERMEAD_H
diff --git a/src/include/gromacs/math/optimization.h b/src/include/gromacs/math/optimization.h
new file mode 100644 (file)
index 0000000..412a27f
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declare function optimization routines.
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_math
+ */
+
+#ifndef GMX_MATH_OPTIMIZATION_H
+#define GMX_MATH_OPTIMIZATION_H
+
+#include <functional>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*! \internal
+ *  \brief Compiles results of an a function optimisation.
+ */
+struct OptimisationResult
+{
+    //! The coordinates at which the optimal function value has been found
+    std::vector<real> coordinates_;
+    //! The value of the function at the optimal coordinates
+    real functionValue_;
+};
+
+/*! \brief Derivative-free downhill simplex optimisation.
+ *
+ * Find a local minimum of an N-dimensional mapping
+ * \f$\mathbb{R}^N\to\mathbb{R}\f$ using the downhill simplex algorithm by
+ * Nelder and Mead as described in
+ *
+ *       Saša Singer and John Nelder (2009), Scholarpedia, 4(7):2928.
+ *       doi:10.4249/scholarpedia.2928
+ *
+ * Stops when the oriented simplex length is less than a constant factor times the
+ * initial lengths or when a maximum step size is reached.
+ *
+ * For best performance pre-condition problem magnitudes to 1.
+ *
+ * The following algorithm is implemented in this function
+ *  1 Define the N+1 vertices of the initial simplex
+ *      The inital simplex is constructed from the initial guess and N
+ *      additional vertices by adding 0.05 to the initial guess (or 0.0025 if
+ *      the initial guess is the null vector) from the initial vertex (in line
+ *      with usual implementations).
+ *
+ *  1a Sort vertices according to function value with the lowest function value
+ *    first in order to minimize the function.
+ *
+ *  2 Calculate the centroid of the simplex as arithmetic mean of all vertices
+ *    except the worst, \f$ x_c = \frac1n \sum_{i=1}{N} x_i\f$.
+ *
+ *  3 Reflect the worst simplex vertex (the one with the highest function value)
+ *    at the centroid to obtain a reflection point
+ *    \f$ x_r = x_c + \alpha (x_c - x_{N+1}) \f$ which lies outside the vertex.
+ *
+ *  3a Replace worst point with reflection point if reflection point function
+ *    value is better than second worst point, but not better than best and go
+ *    to 1a.
+ *
+ *  4 If the reflection point is better than all other points so far, attempt
+ *    an expansion by calculating the expansion point at
+ *    \f$ x_e = x_c + \gamma (x_r - x_c) \f$. Swap out the worst point in the
+ *    vertex with the expansion point if better than reflection point, otherwise
+ *    use the reflection point and go to 1a.
+ *
+ *  5 Attempt contraction, because reflection was not successful;
+ *    \f$ x_t = x_c + \rho (x_{N+1} - x_c) \f$. If the contraction point is
+ *    better than the worst point, swap out worst point with contracted point
+ *    and go to 1a.
+ *
+ *  6 Shrink the vertex. Replace all points except the best one with
+ *    \f$ x_i = x_1 + \sigma (x_i - x_1) \f$ and go to 1a.
+ *
+ * \param[in] functionToMinimize function to be minimized
+ * \param[in] initialGuess of coordinates
+ * \param[in] minimumRelativeSimplexLength minimal oriented simplex length with
+ *                                         respect to initial simplex
+ * \param[in] maxSteps to run algorithm for
+ *
+ * \returns the lowest found function value and corresponding coordinates.
+ */
+OptimisationResult nelderMead(const std::function<real(ArrayRef<const real>)>& functionToMinimize,
+                              ArrayRef<const real>                             initialGuess,
+                              real minimumRelativeSimplexLength = 1e-8,
+                              int  maxSteps                     = 10'000);
+
+} // namespace gmx
+#endif // GMX_MATH_OPTIMIZATION_H
diff --git a/src/include/gromacs/math/paddedvector.h b/src/include/gromacs/math/paddedvector.h
new file mode 100644 (file)
index 0000000..3664c73
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::PaddedRVecVector
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inpublicapi
+ * \ingroup module_math
+ */
+#ifndef GMX_MATH_PADDEDVECTOR_H
+#define GMX_MATH_PADDEDVECTOR_H
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+#include "gromacs/math/arrayrefwithpadding.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/allocator.h"
+#include "gromacs/utility/arrayref.h"
+
+namespace gmx
+{
+
+namespace detail
+{
+
+/*! \brief Traits classes for handling padding for types used with PaddedVector
+ *
+ * Only the base types of the SIMD module are supported for
+ * PaddedVector, because the purpose of the padding is to permit
+ * SIMD-width operations from the SIMD module.
+ *
+ * \todo Consider explicitly tying these types to the SimdTrait
+ * types. That would require depending on the SIMD module, or
+ * extracting the traits from it. This would also permit
+ * maxSimdWidthOfBaseType to be set more efficiently, e.g. as a
+ * metaprogramming max over the maximum width from different
+ * implementations.
+ */
+template<typename T>
+struct PaddingTraits
+{
+};
+
+template<>
+struct PaddingTraits<int32_t>
+{
+    using SimdBaseType                          = int32_t;
+    static constexpr int maxSimdWidthOfBaseType = 16;
+};
+
+template<>
+struct PaddingTraits<float>
+{
+    using SimdBaseType                          = float;
+    static constexpr int maxSimdWidthOfBaseType = GMX_FLOAT_MAX_SIMD_WIDTH;
+};
+
+template<>
+struct PaddingTraits<double>
+{
+    using SimdBaseType                          = double;
+    static constexpr int maxSimdWidthOfBaseType = GMX_DOUBLE_MAX_SIMD_WIDTH;
+};
+
+template<>
+struct PaddingTraits<BasicVector<float>>
+{
+    using SimdBaseType                          = float;
+    static constexpr int maxSimdWidthOfBaseType = GMX_FLOAT_MAX_SIMD_WIDTH;
+};
+
+template<>
+struct PaddingTraits<BasicVector<double>>
+{
+    using SimdBaseType                          = double;
+    static constexpr int maxSimdWidthOfBaseType = GMX_DOUBLE_MAX_SIMD_WIDTH;
+};
+
+/*! \brief Returns the allocation size for PaddedVector that contains
+ * \c numElements elements plus padding for SIMD operations.
+ *
+ * \param[in] numElements  The number of T elements for which data will be stored.
+ * \returns                The number of T elements that must be allocated
+ *                         (ie >= numElements).
+ */
+template<typename T>
+index computePaddedSize(index numElements)
+{
+    // We don't need padding if there is no access.
+    if (numElements == 0)
+    {
+        return 0;
+    }
+
+    // We sometimes load a whole extra element when doing 4-wide SIMD
+    // operations (which might e.g. be an RVec) so we need to pad for
+    // that.
+    index simdScatterAccessSize = numElements + 1;
+
+    // For SIMD updates based on RVec, we might load starting from
+    // the last RVec element, so that sets the minimum extent of the
+    // padding. That extent must take the initialized allocation up to
+    // the SIMD width of the base type multiplied by the width of T in
+    // that base type. But since storage_ contains RVec, we only have
+    // to tell it the number of elements, which means to round up to
+    // the next SIMD width.
+    //
+    // We don't want a dependence on the SIMD module for the actual
+    // SIMD width of the base type, so we use maximum for the base
+    // type via the traits. A little extra padding won't really hurt.
+    constexpr int maxSimdWidth = PaddingTraits<T>::maxSimdWidthOfBaseType;
+    index simdFlatAccessSize   = (numElements + (maxSimdWidth - 1)) / maxSimdWidth * maxSimdWidth;
+
+    return std::max(simdScatterAccessSize, simdFlatAccessSize);
+}
+
+//! Helper function to insert padding elements for most T.
+template<typename T, typename AllocatorType>
+inline void insertPaddingElements(std::vector<T, AllocatorType>* v, index newPaddedSize)
+{
+    // Ensure the padding region is initialized to zero. There is no
+    // way to insert a number of default-initialized elements. So we
+    // have to provide a value for those elements, which anyway suits
+    // this use case.
+    v->insert(v->end(), newPaddedSize - v->size(), 0);
+}
+
+//! Specialization of helper function to insert padding elements, used for BasicVector<T>.
+template<typename T, typename AllocatorType>
+inline void insertPaddingElements(std::vector<BasicVector<T>, AllocatorType>* v, index newPaddedSize)
+{
+    // Ensure the padding region is initialized to zero.
+    v->insert(v->end(), newPaddedSize - v->size(), BasicVector<T>(0, 0, 0));
+}
+
+} // namespace detail
+
+/*! \brief PaddedVector is a container of elements in contiguous
+ * storage that allocates extra memory for safe SIMD-style loads for
+ * operations used in GROMACS.
+ *
+ * \tparam T the type of objects within the container
+ * \tparam Allocator the allocator used. Can be any standard-compliant
+ * allocator, such gmx::Allocator used for alignment and/or pinning.
+ *
+ * The interface resembles std::vector. However, access
+ * intended to include padded elements must be via ArrayRef objects
+ * explicitly created to view those elements. Most other aspects of
+ * this vector refer to the unpadded view, e.g. iterators, data(),
+ * size().
+ *
+ * The underlying storage is allocated with extra elements, properly
+ * initialized, that ensure that any operations accessing the any
+ * non-additional element that operate on memory equivalent to a full
+ * SIMD lane do so on allocated memory that has been initialized, so
+ * that memory traps will not occur, and arithmetic operations will
+ * not cause e.g. floating-point exceptions so long as the values in
+ * the padded elements are properly managed.
+ *
+ * Proper initialization is tricker than it would first appear, since
+ * we intend this container to be used with scalar and class types
+ * (e.g. RVec). Resize and construction operations use "default
+ * insertion" which leads to zero initialization for the former, and
+ * calling the default constructor for the latter. BasicVector has a
+ * default constructor that leaves the elements uninitialized, which
+ * is particularly risky for elements only present as padding. Thus
+ * the implementation specifically initializes the padded elements to
+ * zero, which makes no difference to the scalar template
+ * instantiations, and makes the BasicVector ones safer to use.
+ *
+ * Because the allocator can be configured, the memory allocation can
+ * have other attributes such as SIMD alignment or being pinned to
+ * physical memory for efficient transfers. The default allocator
+ * ensures alignment, but std::allocator also works.
+ */
+template<typename T, typename Allocator = Allocator<T, AlignedAllocationPolicy>>
+class PaddedVector
+{
+public:
+    //! Standard helper types
+    //! \{
+    using value_type      = T;
+    using allocator_type  = Allocator;
+    using size_type       = index;
+    using reference       = value_type&;
+    using const_reference = const value_type&;
+    using storage_type    = std::vector<T, allocator_type>;
+    using pointer         = typename storage_type::pointer;
+    using const_pointer   = typename storage_type::const_pointer;
+    using iterator        = typename storage_type::iterator;
+    using const_iterator  = typename storage_type::const_iterator;
+    using difference_type = typename storage_type::iterator::difference_type;
+    //! \}
+
+    PaddedVector() : storage_(), unpaddedEnd_(begin()) {}
+    /*! \brief Constructor that specifies the initial size. */
+    explicit PaddedVector(size_type count, const allocator_type& allocator = Allocator()) :
+        storage_(count, allocator), unpaddedEnd_(begin() + count)
+    {
+        // The count elements have been default inserted, and now
+        // the padding elements are added
+        resizeWithPadding(count);
+    }
+    /*! \brief Constructor that specifies the initial size and an element to copy. */
+    explicit PaddedVector(size_type count, value_type const& v, const allocator_type& allocator = Allocator()) :
+        storage_(count, v, allocator), unpaddedEnd_(begin() + count)
+    {
+        // The count elements have been default inserted, and now
+        // the padding elements are added
+        resizeWithPadding(count);
+    }
+    //! Default constructor with allocator
+    explicit PaddedVector(allocator_type const& allocator) :
+        storage_(allocator), unpaddedEnd_(begin())
+    {
+    }
+    //! Copy constructor
+    PaddedVector(PaddedVector const& o) : storage_(o.storage_), unpaddedEnd_(begin() + o.size()) {}
+    /*! \brief Move constructor
+     *
+     * Leaves \c o in a valid state (ie the destructor can be
+     * called). */
+    PaddedVector(PaddedVector&& o) noexcept :
+        storage_(std::exchange(o.storage_, {})), unpaddedEnd_(o.unpaddedEnd_)
+    {
+    }
+    /*! \brief Move constructor using \c alloc for the new vector.
+     *
+     * Note that \c alloc is another instance of the same allocator
+     * type as used for \c PaddedVector. This makes sense e.g. for
+     * stateful allocators such as HostAllocator used in
+     * PaddedHostVector.
+     *
+     * Leaves \c o in a valid state (ie. the destructor can be
+     * called). */
+    PaddedVector(PaddedVector&& o, const Allocator& alloc) noexcept :
+        storage_(alloc), unpaddedEnd_(begin())
+    {
+        if (alloc == o.storage_.get_allocator())
+        {
+            std::swap(storage_, o.storage_);
+            unpaddedEnd_ = o.unpaddedEnd_;
+        }
+        else
+        {
+            // If the allocator compares differently, we must
+            // reallocate and copy.
+            auto unpaddedSize = o.size();
+            resizeWithPadding(unpaddedSize);
+            std::copy(o.begin(), o.end(), storage_.begin());
+            unpaddedEnd_ = begin() + unpaddedSize;
+        }
+    }
+    //! Construct from an initializer list
+    PaddedVector(std::initializer_list<value_type> const& il) :
+        storage_(il), unpaddedEnd_(storage_.end())
+    {
+        // We can't choose the padding until we know the size of
+        // the normal vector, so we have to make the storage_ and
+        // then resize it.
+        resizeWithPadding(storage_.size());
+    }
+    //! Reserve storage for the container to contain newExtent elements, plus the required padding.
+    void reserveWithPadding(const size_type newExtent)
+    {
+        auto unpaddedSize = end() - begin();
+        /* v.reserve(13) should allocate enough memory so that
+           v.resize(13) does not reallocate. This means that the
+           new extent should be large enough for the padded
+           storage for a vector whose size is newExtent. */
+        auto newPaddedExtent = detail::computePaddedSize<T>(newExtent);
+        storage_.reserve(newPaddedExtent);
+        unpaddedEnd_ = begin() + unpaddedSize;
+    }
+    //! Resize the container to contain newSize elements, plus the required padding.
+    void resizeWithPadding(const size_type newSize)
+    {
+        // When the contained type is e.g. a scalar, then the
+        // default initialization behaviour is to zero all
+        // elements, which is OK, but we have to make sure that it
+        // happens for the elements in the padded region when the
+        // vector is shrinking.
+        auto newPaddedSize = detail::computePaddedSize<T>(newSize);
+        // Make sure there is room for padding if we need to grow.
+        storage_.reserve(newPaddedSize);
+        // Make the unpadded size correct, with any additional
+        // elements initialized by the default constructor. It is
+        // particularly important to destruct former elements when
+        // newSize is smaller than the old size.
+        storage_.resize(newSize);
+        // Ensure the padding region is zeroed if required.
+        detail::insertPaddingElements(&storage_, newPaddedSize);
+        unpaddedEnd_ = begin() + newSize;
+    }
+    //! Return the size of the view without the padding.
+    size_type size() const { return end() - begin(); }
+    //! Return the container size including the padding.
+    size_type paddedSize() const { return storage_.size(); }
+    //! Return whether the storage is empty.
+    bool empty() const { return storage_.empty(); }
+    //! Swap two PaddedVectors
+    void swap(PaddedVector& x)
+    {
+        std::swap(storage_, x.storage_);
+        std::swap(unpaddedEnd_, x.unpaddedEnd_);
+    }
+    //! Clear the vector, ie. set size to zero and remove padding.
+    void clear()
+    {
+        storage_.clear();
+        unpaddedEnd_ = begin();
+    }
+    //! Iterator getters refer to a view without padding.
+    //! \{
+    pointer       data() noexcept { return storage_.data(); }
+    const_pointer data() const noexcept { return storage_.data(); }
+
+    iterator begin() { return storage_.begin(); }
+    iterator end() { return iterator(unpaddedEnd_); }
+
+    const_iterator cbegin() { return const_iterator(begin()); }
+    const_iterator cend() { return const_iterator(unpaddedEnd_); }
+
+    const_iterator begin() const { return storage_.begin(); }
+    const_iterator end() const { return const_iterator(unpaddedEnd_); }
+
+    const_iterator cbegin() const { return const_iterator(begin()); }
+    const_iterator cend() const { return const_iterator(unpaddedEnd_); }
+    //! \}
+    // TODO should these do bounds checking for the unpadded range? In debug mode?
+    //! Indexing operator.
+    reference operator[](int i) { return storage_[i]; }
+    //! Indexing operator as const.
+    const_reference operator[](int i) const { return storage_[i]; }
+    //! Returns an ArrayRef of elements that includes the padding region, e.g. for use in SIMD code.
+    ArrayRefWithPadding<T> arrayRefWithPadding()
+    {
+        return ArrayRefWithPadding<T>(data(), data() + size(), data() + paddedSize());
+    }
+    //! Returns an ArrayRef of const elements that includes the padding region, e.g. for use in SIMD code.
+    ArrayRefWithPadding<const T> constArrayRefWithPadding() const
+    {
+        return ArrayRefWithPadding<const T>(data(), data() + size(), data() + paddedSize());
+    }
+    /*! \brief Returns an rvec * pointer for containers of RVec, for use with legacy code.
+     *
+     * \todo Use std::is_same_v when CUDA 11 is a requirement.
+     */
+    template<typename AlsoT = T, typename = typename std::enable_if<std::is_same<AlsoT, RVec>::value>>
+    rvec* rvec_array()
+    {
+        return as_rvec_array(data());
+    }
+    /*! \brief Returns a const rvec * pointer for containers of RVec, for use with legacy code.
+     *
+     * \todo Use std::is_same_v when CUDA 11 is a requirement.
+     */
+    template<typename AlsoT = T, typename = typename std::enable_if<std::is_same<AlsoT, RVec>::value>>
+    const rvec* rvec_array() const
+    {
+        return as_rvec_array(data());
+    }
+    //! Copy assignment operator
+    PaddedVector& operator=(PaddedVector const& o)
+    {
+        if (&o != this)
+        {
+            storage_     = o.storage_;
+            unpaddedEnd_ = begin() + o.size();
+        }
+        return *this;
+    }
+    //! Move assignment operator
+    PaddedVector& operator=(PaddedVector&& o) noexcept
+    {
+        if (&o != this)
+        {
+            auto oSize     = o.size();
+            storage_       = std::move(o.storage_);
+            unpaddedEnd_   = begin() + oSize;
+            o.unpaddedEnd_ = o.begin();
+        }
+        return *this;
+    }
+    //! Getter for the allocator
+    allocator_type get_allocator() const { return storage_.get_allocator(); }
+
+private:
+    storage_type storage_;
+    iterator     unpaddedEnd_;
+};
+
+} // namespace gmx
+
+// TODO These are hacks to avoid littering gmx:: all over code that is
+// almost all destined to move into the gmx namespace at some point.
+// An alternative would be about 20 files with using statements.
+using gmx::PaddedVector; //NOLINT(google-global-names-in-headers)
+
+#endif
diff --git a/src/include/gromacs/math/tests/testarrayrefs.h b/src/include/gromacs/math/tests/testarrayrefs.h
new file mode 100644 (file)
index 0000000..f6aa58b
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares functions for comparing views of vector-like data.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_math
+ * \inlibraryapi
+ */
+#ifndef GMX_MATH_TESTARRAYREFS_H
+#define GMX_MATH_TESTARRAYREFS_H
+
+#include <gtest/gtest.h>
+
+#include "gromacs/math/vectypes.h"
+
+namespace gmx
+{
+namespace test
+{
+
+//! Initialization overload for non-BasicVector
+template<typename T>
+void fillInputContents(ArrayRef<T> inputRef, int scaleFactorForElements)
+{
+    for (size_t i = 0; i < inputRef.size(); i++)
+    {
+        inputRef[i] = T((i + 1) * scaleFactorForElements);
+    }
+}
+
+//! Initialization overload for BasicVector
+template<typename T>
+void fillInputContents(ArrayRef<BasicVector<T>> inputRef, int scaleFactorForElements)
+{
+    for (size_t i = 0; i < inputRef.size(); i++)
+    {
+        for (size_t j = 0; j < DIM; j++)
+        {
+            inputRef[i][j] = T((DIM * i + j + 1) * scaleFactorForElements);
+        }
+    }
+}
+
+//! Dispatcher function for filling.
+template<typename PaddedVectorOfT>
+void resizeAndFillInput(PaddedVectorOfT* input, int newSize, int scaleFactorForElements)
+{
+    input->resizeWithPadding(newSize);
+    fillInputContents(makeArrayRef(*input), scaleFactorForElements);
+    EXPECT_LE(newSize, input->paddedSize());
+}
+
+//! Comparison overload for non-BasicVector
+template<typename T>
+void compareViews(ArrayRef<T> input, ArrayRef<T> output)
+{
+    ASSERT_EQ(input.size(), output.size());
+    for (index i = 0; i != input.ssize(); ++i)
+    {
+        EXPECT_EQ(input[i], output[i]) << "for index " << i;
+    }
+}
+
+//! Comparison for non-BasicVector ignoring const qualifiers
+template<typename T, typename U>
+typename std::enable_if_t<std::is_same_v<std::remove_const_t<T>, std::remove_const_t<U>>, void>
+compareViewsIgnoreConst(ArrayRef<T> input, ArrayRef<U> output)
+{
+    ASSERT_EQ(input.size(), output.size());
+    for (index i = 0; i != input.ssize(); ++i)
+    {
+        EXPECT_EQ(input[i], output[i]) << "for index " << i;
+    }
+}
+
+//! Comparison overload for BasicVector<T>
+template<typename T>
+void compareViews(ArrayRef<BasicVector<T>> input, ArrayRef<BasicVector<T>> output)
+{
+    ASSERT_EQ(input.size(), output.size());
+    for (index i = 0; i != input.ssize(); ++i)
+    {
+        EXPECT_EQ(input[i][XX], output[i][XX]) << "for index " << i;
+        EXPECT_EQ(input[i][YY], output[i][YY]) << "for index " << i;
+        EXPECT_EQ(input[i][ZZ], output[i][ZZ]) << "for index " << i;
+    }
+}
+
+} // namespace test
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/math/veccompare.h b/src/include/gromacs/math/veccompare.h
new file mode 100644 (file)
index 0000000..475fe6a
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MATH_VECCOMPARE_H
+#define GMX_MATH_VECCOMPARE_H
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+void cmp_rvec(FILE* fp, const char* s, int index, const rvec i1, const rvec i2, real ftol, real abstol);
+
+void cmp_ivec(FILE* fp, const char* s, int index, const ivec i1, const ivec i2);
+
+void cmp_rvecs(FILE* fp, const char* title, int n, const rvec x1[], const rvec x2[], gmx_bool bRMSD, real ftol, real abstol);
+
+#endif
diff --git a/src/include/gromacs/math/vecdump.h b/src/include/gromacs/math/vecdump.h
new file mode 100644 (file)
index 0000000..d94b028
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MATH_VECDUMP_H
+#define GMX_MATH_VECDUMP_H
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+void pr_ivec(FILE* fp, int indent, const char* title, const int vec[], int n, gmx_bool bShowNumbers);
+void pr_ivec_block(FILE* fp, int indent, const char* title, const int vec[], int n, gmx_bool bShowNumbers);
+void pr_ivecs(FILE* fp, int indent, const char* title, const ivec vec[], int n, gmx_bool bShowNumbers);
+void pr_bvec(FILE* fp, int indent, const char* title, const gmx_bool vec[], int n, gmx_bool bShowNnumbers);
+void pr_rvec(FILE* fp, int indent, const char* title, const real vec[], int n, gmx_bool bShowNumbers);
+void pr_rvecs_of_dim(FILE* fp, int indent, const char* title, const rvec vec[], int n, int dim);
+void pr_fvec(FILE* fp, int indent, const char* title, const float vec[], int n, gmx_bool bShowNumbers);
+void pr_dvec(FILE* fp, int indent, const char* title, const double vec[], int n, gmx_bool bShowNumbers);
+void pr_rvecs(FILE* fp, int indent, const char* title, const rvec vec[], int n);
+void pr_rvecs_len(FILE* fp, int indent, const char* title, const rvec vec[], int n);
+
+#endif
diff --git a/src/include/gromacs/mdlib/boxdeformation.h b/src/include/gromacs/mdlib/boxdeformation.h
new file mode 100644 (file)
index 0000000..0eea78a
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares interface to box deformation code.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+
+#ifndef GMX_MDRUN_BOXDEFORMATION_H
+#define GMX_MDRUN_BOXDEFORMATION_H
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/gmxmpi.h"
+
+struct t_inputrec;
+enum class DDRole;
+enum class NumRanks;
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+class BoxDeformation
+{
+public:
+    //! Trivial constructor.
+    BoxDeformation(double timeStep, int64_t initialStep, const tensor& deformationTensor, const matrix& referenceBox);
+
+    //! Deform \c x and \c box at this \c step;
+    void apply(ArrayRef<RVec> x, matrix box, int64_t step);
+
+private:
+    //! The integrator time step.
+    double timeStep_;
+    //! The initial step number (from the .tpr, which permits checkpointing to work correctly).
+    int64_t initialStep_;
+    //! Non-zero elements provide a scaling factor for deformation in that box dimension.
+    tensor deformationTensor_;
+    //! The initial box, ie from the .tpr file.
+    matrix referenceBox_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(BoxDeformation);
+};
+
+/*! \brief Factory function for box deformation module.
+ *
+ * If the \c inputrec specifies the use of box deformation during the
+ * update phase, communicates the \c initialBox from SIMMASTER to
+ * other ranks, and constructs and returns an object to manage that
+ * update.
+ *
+ * \throws NotImplementedError if the \c inputrec specifies an
+ * unsupported combination.
+ */
+std::unique_ptr<BoxDeformation> prepareBoxDeformation(const matrix&     initialBox,
+                                                      DDRole            ddRole,
+                                                      NumRanks          numRanks,
+                                                      MPI_Comm          communicator,
+                                                      const t_inputrec& inputrec);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/broadcaststructs.h b/src/include/gromacs/mdlib/broadcaststructs.h
new file mode 100644 (file)
index 0000000..f8416ed
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief Convenience wrappers for broadcasting structs.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_BROADCASTSTRUCTS_H
+#define GMX_MDLIB_BROADCASTSTRUCTS_H
+
+#include <vector>
+
+#include "gromacs/gmxlib/network.h"
+#include "gromacs/mdtypes/commrec.h"
+#include "gromacs/utility/smalloc.h"
+
+struct gmx_mtop_t;
+struct t_inputrec;
+struct PartialDeserializedTprFile;
+class t_state;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+//! Convenience wrapper for gmx_bcast to communicator of a single value.
+template<typename T>
+void block_bc(MPI_Comm communicator, T& data)
+{
+    gmx_bcast(sizeof(T), static_cast<void*>(&data), communicator);
+}
+//! Convenience wrapper for gmx_bcast to communicator of a C-style array.
+template<typename T>
+void nblock_bc(MPI_Comm communicator, int numElements, T* data)
+{
+    gmx_bcast(numElements * sizeof(T), static_cast<void*>(data), communicator);
+}
+//! Convenience wrapper for gmx_bcast to communicator of an ArrayRef<T>
+template<typename T>
+void nblock_bc(MPI_Comm communicator, gmx::ArrayRef<T> data)
+{
+    gmx_bcast(data.size() * sizeof(T), static_cast<void*>(data.data()), communicator);
+}
+//! Convenience wrapper for allocation with snew of vectors that need allocation on non-master ranks.
+template<typename T>
+void snew_bc(bool isMasterRank, T*& data, int numElements)
+{
+    if (!isMasterRank)
+    {
+        snew(data, numElements);
+    }
+}
+//! Convenience wrapper for gmx_bcast of a C-style array which needs allocation on non-master ranks.
+template<typename T>
+void nblock_abc(bool isMasterRank, MPI_Comm communicator, int numElements, T** v)
+{
+    snew_bc(isMasterRank, v, numElements);
+    nblock_bc(communicator, numElements, *v);
+}
+//! Convenience wrapper for gmx_bcast of a std::vector which needs resizing on non-master ranks.
+template<typename T>
+void nblock_abc(bool isMasterRank, MPI_Comm communicator, int numElements, std::vector<T>* v)
+{
+    if (!isMasterRank)
+    {
+        v->resize(numElements);
+    }
+    gmx_bcast(numElements * sizeof(T), v->data(), communicator);
+}
+
+//! \brief Broadcasts the, non-dynamic, state from the master to all ranks in cr->mpi_comm_mygroup
+//
+// This is intended to be used with MPI parallelization without
+// domain decompostion (currently with NM and TPI).
+void broadcastStateWithoutDynamics(MPI_Comm communicator,
+                                   bool     useDomainDecomposition,
+                                   bool     isParallelRun,
+                                   t_state* state);
+
+//! \brief Broadcast inputrec and mtop and allocate node-specific settings
+void init_parallel(MPI_Comm                    communicator,
+                   bool                        isMasterRank,
+                   t_inputrec*                 inputrec,
+                   gmx_mtop_t*                 mtop,
+                   PartialDeserializedTprFile* partialDeserializedTpr);
+
+#endif
diff --git a/src/include/gromacs/mdlib/calc_verletbuf.h b/src/include/gromacs/mdlib/calc_verletbuf.h
new file mode 100644 (file)
index 0000000..a013cf2
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_MDLIB_CALC_VERLETBUF_H
+#define GMX_MDLIB_CALC_VERLETBUF_H
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_mtop_t;
+struct t_inputrec;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class RangePartitioning;
+} // namespace gmx
+
+namespace Nbnxm
+{
+enum class KernelType;
+} // namespace Nbnxm
+
+
+struct VerletbufListSetup
+{
+    int cluster_size_i; /* Cluster pair-list i-cluster size atom count */
+    int cluster_size_j; /* Cluster pair-list j-cluster size atom count */
+};
+
+
+/* Add a 5% and 10% rlist buffer for simulations without dynamics (EM, NM, ...)
+ * and NVE simulations with zero initial temperature, respectively.
+ * 10% should be enough for any NVE simulation with PME and nstlist=10,
+ * for other settings it might not be enough, but then it's difficult
+ * to come up with any reasonable (not crazily expensive) value
+ * and grompp will notify the user when using the 10% buffer.
+ */
+static const real verlet_buffer_ratio_nodynamics = 0.05;
+static const real verlet_buffer_ratio_NVE_T0     = 0.10;
+
+
+/* Returns the pair-list setup for the given nbnxn kernel type.
+ */
+VerletbufListSetup verletbufGetListSetup(Nbnxm::KernelType nbnxnKernelType);
+
+/* Enum for choosing the list type for verletbufGetSafeListSetup() */
+enum class ListSetupType
+{
+    CpuNoSimd,            /* CPU Plain-C 4x4 list */
+    CpuSimdWhenSupported, /* CPU 4xN list, where N=4 when the binary doesn't support SIMD or the smallest N supported by SIMD in this binary */
+    Gpu                   /* GPU (8x2x)8x4 list */
+};
+
+/* Returns the pair-list setup assumed for the current Gromacs configuration.
+ * The setup with smallest cluster sizes is returned, such that the Verlet
+ * buffer size estimated with this setup will be conservative.
+ */
+VerletbufListSetup verletbufGetSafeListSetup(ListSetupType listType);
+
+/* Returns the non-bonded pair-list radius including computed buffer
+ *
+ * Calculate the non-bonded pair-list buffer size for the Verlet list
+ * based on the particle masses, temperature, LJ types, charges
+ * and constraints as well as the non-bonded force behavior at the cut-off.
+ * The pair list update frequency and the list lifetime, which is nstlist-1
+ * for normal pair-list buffering, are passed separately, as in some cases
+ * we want an estimate for different values than the ones set in the inputrec.
+ * If referenceTemperature < 0, the maximum coupling temperature will be used.
+ * The target is a maximum average energy jump per atom of
+ * inputrec.verletbuf_tol*nstlist*inputrec.delta_t over the lifetime of the list.
+ *
+ * \note For non-linear virtual sites it can be problematic to determine their
+ *       contribution to the drift exaclty, so we approximate.
+ *
+ * \param[in] mtop          The system topology
+ * \param[in] boxVolume     The volume of the unit cell
+ * \param[in] inputrec      The input record
+ * \param[in] nstlist       The pair list update frequency in steps (is not taken from \p inputrec)
+ * \param[in] listLifetime  The lifetime of the pair-list, usually nstlist-1, but could be different
+ * for dynamic pruning \param[in] referenceTemperature  The reference temperature for the ensemble
+ * \param[in] listSetup     The pair-list setup
+ * \returns The computed pair-list radius including buffer
+ */
+real calcVerletBufferSize(const gmx_mtop_t&         mtop,
+                          real                      boxVolume,
+                          const t_inputrec&         inputrec,
+                          int                       nstlist,
+                          int                       listLifetime,
+                          real                      referenceTemperature,
+                          const VerletbufListSetup& listSetup);
+
+/* Convenience type */
+using PartitioningPerMoltype = gmx::ArrayRef<const gmx::RangePartitioning>;
+
+/* Determines the mininum cell size based on atom displacement
+ *
+ * The value returned is the minimum size for which the chance that
+ * an atom or update group crosses to non nearest-neighbor cells
+ * is <= chanceRequested within ir.nstlist steps.
+ * Update groups are used when !updateGrouping.empty().
+ * Without T-coupling, SD or BD, we can not estimate atom displacements
+ * and fall back to the, crude, estimate of using the pairlist buffer size.
+ *
+ * Note: Like the Verlet buffer estimate, this estimate is based on
+ *       non-interacting atoms and constrained atom-pairs. Therefore for
+ *       any system that is not an ideal gas, this will be an overestimate.
+ *
+ * Note: This size increases (very slowly) with system size.
+ */
+real minCellSizeForAtomDisplacement(const gmx_mtop_t&      mtop,
+                                    const t_inputrec&      ir,
+                                    PartitioningPerMoltype updateGrouping,
+                                    real                   chanceRequested);
+
+/* Struct for unique atom type for calculating the energy drift.
+ * The atom displacement depends on mass and constraints.
+ * The energy jump for given distance depend on LJ type and q.
+ */
+struct atom_nonbonded_kinetic_prop_t
+{
+    real mass     = 0;     /* mass */
+    int  type     = 0;     /* type (used for LJ parameters) */
+    real q        = 0;     /* charge */
+    bool bConstr  = false; /* constrained, if TRUE, use #DOF=2 iso 3 */
+    real con_mass = 0;     /* mass of heaviest atom connected by constraints */
+    real con_len  = 0;     /* constraint length to the heaviest atom */
+};
+
+/* This function computes two components of the estimate of the variance
+ * in the displacement of one atom in a system of two constrained atoms.
+ * Returns in sigma2_2d the variance due to rotation of the constrained
+ * atom around the atom to which it constrained.
+ * Returns in sigma2_3d the variance due to displacement of the COM
+ * of the whole system of the two constrained atoms.
+ *
+ * Only exposed here for testing purposes.
+ */
+void constrained_atom_sigma2(real kT_fac, const atom_nonbonded_kinetic_prop_t* prop, real* sigma2_2d, real* sigma2_3d);
+
+#endif
diff --git a/src/include/gromacs/mdlib/calcmu.h b/src/include/gromacs/mdlib/calcmu.h
new file mode 100644 (file)
index 0000000..23a4fd2
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_CALCMU_H
+#define GMX_MDLIB_CALCMU_H
+
+#include <stdio.h>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+void calc_mu(int                            start,
+             int                            homenr,
+             gmx::ArrayRef<const gmx::RVec> x,
+             gmx::ArrayRef<const real>      q,
+             gmx::ArrayRef<const real>      qB,
+             bool                           havePerturbedCharges,
+             dvec                           mu,
+             dvec                           mu_B);
+
+gmx_bool read_mu(FILE* fp, rvec mu, real* vol);
+/* Return true on succes */
+
+#endif
diff --git a/src/include/gromacs/mdlib/calcvir.h b/src/include/gromacs/mdlib/calcvir.h
new file mode 100644 (file)
index 0000000..1569c5b
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_CALCVIR_H
+#define GMX_MDLIB_CALCVIR_H
+
+#include "gromacs/math/vectypes.h"
+
+struct t_pbc;
+
+void calc_vir(int nxf, const rvec x[], const rvec f[], tensor vir, bool bScrewPBC, const matrix box);
+/* Calculate virial for nxf atoms, and add it to vir */
+
+void f_calc_vir(int i0, int i1, const rvec x[], const rvec f[], tensor vir, const rvec shift_vec[]);
+/* Calculate virial taking periodicity into account */
+
+#endif
diff --git a/src/include/gromacs/mdlib/checkpointhandler.h b/src/include/gromacs/mdlib/checkpointhandler.h
new file mode 100644 (file)
index 0000000..6725b48
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares the checkpoint handler class.
+ *
+ * This class sets the signal to checkpoint based on the elapsed simulation time,
+ * and handles the signal if it is received. When handling the signal (via
+ * decideIfCheckpointingThisStep()), it is deciding whether a checkpoint should be saved
+ * at the current step. This can be due to a received signal, or if the current simulation
+ * step is the last. This information can be queried via the isCheckpointingStep() function.
+ *
+ * The setting and handling is implemented in private functions. They are only called
+ * if a respective boolean is true. For the trivial case of no checkpointing (or no checkpoint
+ * signal setting on any other rank than master), the translation unit of the calling
+ * function is therefore never left. In the future, this will be achieved by adding
+ * (or not adding) handlers / setters to the task graph.
+ *
+ * \author Pascal Merz <pascal.merz@colorado.edu>
+ * \inlibraryapi
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_CHECKPOINTHANDLER_H
+#define GMX_MDLIB_CHECKPOINTHANDLER_H
+
+#include <memory>
+
+#include "gromacs/compat/pointers.h"
+#include "gromacs/mdlib/simulationsignal.h"
+
+struct gmx_walltime_accounting;
+
+namespace gmx
+{
+/*! \brief Checkpoint signals
+ *
+ * Signals set and read by CheckpointHandler. Possible signals include
+ *   * nothing to signal
+ *   * do checkpoint (at next NS step)
+ */
+enum class CheckpointSignal
+{
+    noSignal     = 0,
+    doCheckpoint = 1
+};
+
+/*! \libinternal
+ * \brief Class handling the checkpoint signal
+ *
+ * Master rank sets the checkpointing signal periodically
+ * All ranks receive checkpointing signal and set the respective flag
+ */
+class CheckpointHandler final
+{
+public:
+    /*! \brief CheckpointHandler constructor
+     *
+     * Needs a non-null pointer to the signal which is reduced by compute_globals, and
+     * (const) references to data it needs to determine whether a signal needs to be
+     * set or handled.
+     */
+    CheckpointHandler(compat::not_null<SimulationSignal*> signal,
+                      bool                                simulationsShareState,
+                      bool                                neverUpdateNeighborList,
+                      bool                                isMaster,
+                      bool                                writeFinalCheckpoint,
+                      real                                checkpointingPeriod);
+
+    /*! \brief Decides whether a checkpointing signal needs to be set
+     *
+     * Checkpointing signal is set based on the elapsed run time and the checkpointing
+     * interval.
+     */
+    void setSignal(gmx_walltime_accounting* walltime_accounting) const
+    {
+        if (rankCanSetSignal_)
+        {
+            setSignalImpl(walltime_accounting);
+        }
+    }
+
+    /*! \brief Decides whether a checkpoint shall be written at this step
+     *
+     * Checkpointing is done if this is not the initial step, and
+     *   * a signal has been set and the current step is a neighborlist creation
+     *     step, or
+     *   * the current step is the last step and a the simulation is writing
+     *     configurations.
+     *
+     * \todo Change these bools to enums to make calls more self-explanatory
+     */
+    void decideIfCheckpointingThisStep(bool bNS, bool bFirstStep, bool bLastStep)
+    {
+        if (checkpointingIsActive_)
+        {
+            decideIfCheckpointingThisStepImpl(bNS, bFirstStep, bLastStep);
+        }
+    }
+
+    //! Query decision in decideIfCheckpointingThisStep()
+    bool isCheckpointingStep() const { return checkpointThisStep_; }
+
+private:
+    void setSignalImpl(gmx_walltime_accounting* walltime_accounting) const;
+
+    void decideIfCheckpointingThisStepImpl(bool bNS, bool bFirstStep, bool bLastStep);
+
+    SimulationSignal& signal_;
+    bool              checkpointThisStep_;
+    int               numberOfNextCheckpoint_;
+
+    const bool rankCanSetSignal_;
+    const bool checkpointingIsActive_;
+    const bool writeFinalCheckpoint_;
+    const bool neverUpdateNeighborlist_;
+    const real checkpointingPeriod_;
+};
+} // namespace gmx
+
+#endif // GMX_MDLIB_CHECKPOINTHANDLER_H
diff --git a/src/include/gromacs/mdlib/compute_io.h b/src/include/gromacs/mdlib/compute_io.h
new file mode 100644 (file)
index 0000000..0542282
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2011,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_MDLIB_COMPUTE_IO_H
+#define GMX_MDLIB_COMPUTE_IO_H
+
+struct SimulationGroups;
+struct t_inputrec;
+
+double compute_io(const t_inputrec* ir, int natoms, const SimulationGroups& groups, int nrener, int nrepl);
+/* Return total output to be written from this simulation. */
+
+#endif
diff --git a/src/include/gromacs/mdlib/constr.h b/src/include/gromacs/mdlib/constr.h
new file mode 100644 (file)
index 0000000..ad8ff74
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares interface to constraint code.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+
+#ifndef GMX_MDLIB_CONSTR_H
+#define GMX_MDLIB_CONSTR_H
+
+#include <cstdio>
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_edsam;
+struct gmx_localtop_t;
+struct gmx_moltype_t;
+struct gmx_mtop_t;
+struct gmx_multisim_t;
+struct gmx_wallcycle;
+struct pull_t;
+struct t_commrec;
+struct t_ilist;
+struct t_inputrec;
+struct t_nrnb;
+struct t_pbc;
+class t_state;
+enum class ConstraintAlgorithm : int;
+namespace gmx
+{
+template<typename T>
+class ArrayRefWithPadding;
+template<typename>
+class ListOfLists;
+class ObservablesReducerBuilder;
+
+//! Describes supported flavours of constrained updates.
+enum class ConstraintVariable : int
+{
+    Positions,     /* Constrain positions (mass weighted)             */
+    Velocities,    /* Constrain velocities (mass weighted)            */
+    Derivative,    /* Constrain a derivative (mass weighted),         *
+                    * for instance velocity or acceleration,          *
+                    * constraint virial can not be calculated.        */
+    Deriv_FlexCon, /* As Derivative, but only output flex. con.       */
+    Force,         /* Constrain forces (non mass-weighted)            */
+    // TODO What does this do? Improve the comment.
+    ForceDispl /* Like Force, but free particles will have mass
+                * 1 and frozen particles mass 0                   */
+};
+
+/*! \libinternal
+ * \brief Handles constraints */
+class Constraints
+{
+private:
+    /*! \brief Constructor
+     *
+     * Private to enforce use of makeConstraints() factory
+     * function. */
+    Constraints(const gmx_mtop_t&          mtop,
+                const t_inputrec&          ir,
+                pull_t*                    pull_work,
+                FILE*                      log,
+                const t_commrec*           cr,
+                bool                       useUpdateGroups,
+                const gmx_multisim_t*      ms,
+                t_nrnb*                    nrnb,
+                gmx_wallcycle*             wcycle,
+                bool                       pbcHandlingRequired,
+                ObservablesReducerBuilder* observablesReducerBuilder,
+                int                        numConstraints,
+                int                        numSettles);
+
+public:
+    /*! \brief This member type helps implement a factory
+     * function, because its objects can access the private
+     * constructor. */
+    struct CreationHelper;
+
+    ~Constraints();
+
+    /*! \brief Returns the total number of flexible constraints in the system. */
+    int numFlexibleConstraints() const;
+
+    /*! \brief Returns whether the system contains perturbed constraints */
+    bool havePerturbedConstraints() const;
+
+    /*! \brief Set up all the local constraints for the domain.
+     *
+     * \todo Make this a callback that is called automatically
+     * once a new domain has been made. */
+    void setConstraints(gmx_localtop_t*                     top,
+                        int                                 numAtoms,
+                        int                                 numHomeAtoms,
+                        gmx::ArrayRef<const real>           masses,
+                        gmx::ArrayRef<const real>           inverseMasses,
+                        bool                                hasMassPerturbedAtoms,
+                        real                                lambda,
+                        gmx::ArrayRef<const unsigned short> cFREEZE);
+
+    /*! \brief Applies constraints to coordinates.
+     *
+     * When econq=ConstraintVariable::Positions constrains
+     * coordinates xprime using the directions in x, min_proj is
+     * not used.
+     *
+     * When econq=ConstraintVariable::Derivative, calculates the
+     * components xprime in the constraint directions and
+     * subtracts these components from min_proj.  So when
+     * min_proj=xprime, the constraint components are projected
+     * out.
+     *
+     * When econq=ConstraintVariable::Deriv_FlexCon, the same is
+     * done as with ConstraintVariable::Derivative, but only the
+     * components of the flexible constraints are stored.
+     *
+     * delta_step is used for determining the constraint reference lengths
+     * when lenA != lenB or will the pull code with a pulling rate.
+     * step + delta_step is the step at which the final configuration
+     * is meant to be; for update delta_step = 1.
+     *
+     * step_scaling can be used to update coordinates based on the time
+     * step multiplied by this factor. Thus, normally 1.0 is passed. The
+     * SD1 integrator uses 0.5 in one of its calls, to correct positions
+     * for half a step of changed velocities.
+     *
+     * If v!=NULL also constrain v by adding the constraint corrections / dt.
+     *
+     * If computeVirial is true, calculate the constraint virial.
+     *
+     * Return whether the application of constraints succeeded without error.
+     *
+     * /note x is non-const, because non-local atoms need to be communicated.
+     */
+    bool apply(bool                      bLog,
+               bool                      bEner,
+               int64_t                   step,
+               int                       delta_step,
+               real                      step_scaling,
+               ArrayRefWithPadding<RVec> x,
+               ArrayRefWithPadding<RVec> xprime,
+               ArrayRef<RVec>            min_proj,
+               const matrix              box,
+               real                      lambda,
+               real*                     dvdlambda,
+               ArrayRefWithPadding<RVec> v,
+               bool                      computeVirial,
+               tensor                    constraintsVirial,
+               ConstraintVariable        econq);
+    //! Links the essentialdynamics and constraint code.
+    void saveEdsamPointer(gmx_edsam* ed);
+    //! Getter for use by domain decomposition.
+    ArrayRef<const ListOfLists<int>> atom2constraints_moltype() const;
+    //! Getter for use by domain decomposition.
+    ArrayRef<const std::vector<int>> atom2settle_moltype() const;
+
+    /*! \brief Return the RMSD of the constraints when available. */
+    real rmsd() const;
+
+    /*! \brief Get the total number of constraints.
+     *
+     * \returns Total number of constraints, including SETTLE and LINCS/SHAKE constraints.
+     */
+    int numConstraintsTotal();
+
+private:
+    //! Implementation type.
+    class Impl;
+    //! Implementation object.
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \brief Generate a fatal error because of too many LINCS/SETTLE warnings. */
+[[noreturn]] void too_many_constraint_warnings(ConstraintAlgorithm eConstrAlg, int warncount);
+
+/*! \brief Returns whether constraint with parameter \p iparamsIndex is a flexible constraint */
+static inline bool isConstraintFlexible(ArrayRef<const t_iparams> iparams, int iparamsIndex)
+{
+    return (iparams[iparamsIndex].constr.dA == 0 && iparams[iparamsIndex].constr.dB == 0);
+};
+
+/* The at2con t_blocka struct returned by the routines below
+ * contains a list of constraints per atom.
+ * The F_CONSTRNC constraints in this structure number consecutively
+ * after the F_CONSTR constraints.
+ */
+
+/*! \brief Tells make_at2con how to treat flexible constraints */
+enum class FlexibleConstraintTreatment
+{
+    Include, //!< Include all flexible constraints
+    Exclude  //!< Exclude all flexible constraints
+};
+
+/*! \brief Returns the flexible constraint treatment depending on whether the integrator is dynamic */
+FlexibleConstraintTreatment flexibleConstraintTreatment(bool haveDynamicsIntegrator);
+
+/*! \brief Returns a ListOfLists object to go from atoms to constraints
+ *
+ * The object will contain constraint indices with lower indices
+ * directly matching the order in F_CONSTR and higher indices matching
+ * the order in F_CONSTRNC offset by the number of constraints in F_CONSTR.
+ *
+ * \param[in]  moltype   The molecule data
+ * \param[in]  iparams   Interaction parameters, can be null when
+ *                       \p flexibleConstraintTreatment==Include
+ * \param[in]  flexibleConstraintTreatment  The flexible constraint treatment,
+ *                                          see enum above
+ *
+ * \returns a ListOfLists object with all constraints for each atom
+ */
+ListOfLists<int> make_at2con(const gmx_moltype_t&           moltype,
+                             gmx::ArrayRef<const t_iparams> iparams,
+                             FlexibleConstraintTreatment    flexibleConstraintTreatment);
+
+/*! \brief Returns a ListOfLists object to go from atoms to constraints
+ *
+ * The object will contain constraint indices with lower indices
+ * directly matching the order in F_CONSTR and higher indices matching
+ * the order in F_CONSTRNC offset by the number of constraints in F_CONSTR.
+ *
+ * \param[in]  numAtoms  The number of atoms to construct the list for
+ * \param[in]  ilist     Interaction list, size F_NRE
+ * \param[in]  iparams   Interaction parameters, can be null when
+ *                       \p flexibleConstraintTreatment==Include
+ * \param[in]  flexibleConstraintTreatment  The flexible constraint treatment,
+ *                                          see enum above
+ *
+ * \returns a ListOfLists object with all constraints for each atom
+ */
+ListOfLists<int> make_at2con(int                             numAtoms,
+                             ArrayRef<const InteractionList> ilist,
+                             ArrayRef<const t_iparams>       iparams,
+                             FlexibleConstraintTreatment     flexibleConstraintTreatment);
+
+//! Return the number of flexible constraints in the \c ilist and \c iparams.
+int countFlexibleConstraints(ArrayRef<const InteractionList> ilist, ArrayRef<const t_iparams> iparams);
+
+/*! \brief Returns the constraint iatoms for a constraint number con
+ * which comes from a list where F_CONSTR and F_CONSTRNC constraints
+ * are concatenated. */
+inline const int* constr_iatomptr(gmx::ArrayRef<const int> iatom_constr,
+                                  gmx::ArrayRef<const int> iatom_constrnc,
+                                  int                      con)
+{
+    if (con * 3 < iatom_constr.ssize())
+    {
+        return iatom_constr.data() + con * 3;
+    }
+    else
+    {
+        return iatom_constrnc.data() + con * 3 - iatom_constr.size();
+    }
+};
+
+/*! \brief Returns whether there are inter charge group constraints */
+bool inter_charge_group_constraints(const gmx_mtop_t& mtop);
+
+/*! \brief Returns whether there are inter charge group settles */
+bool inter_charge_group_settles(const gmx_mtop_t& mtop);
+
+/*! \brief Constrain the initial coordinates and velocities */
+void do_constrain_first(FILE*                     log,
+                        gmx::Constraints*         constr,
+                        const t_inputrec*         inputrec,
+                        int                       numAtoms,
+                        int                       numHomeAtoms,
+                        ArrayRefWithPadding<RVec> x,
+                        ArrayRefWithPadding<RVec> v,
+                        const matrix              box,
+                        real                      lambda);
+
+/*! \brief Constrain velocities only.
+ *
+ * The dvdlambda contribution has to be added to the bonded interactions
+ */
+void constrain_velocities(gmx::Constraints* constr,
+                          bool              do_log,
+                          bool              do_ene,
+                          int64_t           step,
+                          t_state*          state,
+                          real*             dvdlambda,
+                          bool              computeVirial,
+                          tensor            constraintsVirial);
+
+/*! \brief Constraint coordinates.
+ *
+ * The dvdlambda contribution has to be added to the bonded interactions
+ */
+void constrain_coordinates(gmx::Constraints*         constr,
+                           bool                      do_log,
+                           bool                      do_ene,
+                           int64_t                   step,
+                           t_state*                  state,
+                           ArrayRefWithPadding<RVec> xp,
+                           real*                     dvdlambda,
+                           bool                      computeVirial,
+                           tensor                    constraintsVirial);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/constraintrange.h b/src/include/gromacs/mdlib/constraintrange.h
new file mode 100644 (file)
index 0000000..2d5b2a5
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_MDLIB_CONSTRAINTRANGE_H
+#define GMX_MDLIB_CONSTRAINTRANGE_H
+
+#include "gromacs/utility/real.h"
+
+struct gmx_mtop_t;
+struct t_inputrec;
+
+namespace gmx
+{
+
+class MDLogger;
+
+/*! \brief Returns an estimate of the maximum distance between atoms
+ * required for LINCS. */
+real constr_r_max(const MDLogger& mdlog, const gmx_mtop_t* mtop, const t_inputrec* ir);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/coupling.h b/src/include/gromacs/mdlib/coupling.h
new file mode 100644 (file)
index 0000000..4e8857c
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_COUPLING_H
+#define GMX_MDLIB_COUPLING_H
+
+#include <cstdio>
+
+#include <array>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/real.h"
+
+class gmx_ekindata_t;
+struct gmx_enerdata_t;
+struct t_commrec;
+struct t_extmass;
+struct t_grpopts;
+struct t_inputrec;
+struct t_nrnb;
+class t_state;
+
+enum class PbcType;
+
+namespace gmx
+{
+class BoxDeformation;
+class Constraints;
+class Update;
+template<typename>
+class ArrayRef;
+}; // namespace gmx
+
+/* Update the size of per-atom arrays (e.g. after DD re-partitioning,
+   which might increase the number of home atoms). */
+
+void update_tcouple(int64_t                             step,
+                    const t_inputrec*                   inputrec,
+                    t_state*                            state,
+                    gmx_ekindata_t*                     ekind,
+                    const t_extmass*                    MassQ,
+                    int                                 homenr,
+                    gmx::ArrayRef<const unsigned short> cTC);
+
+/* Update Parrinello-Rahman, to be called before the coordinate update */
+void update_pcouple_before_coordinates(FILE*             fplog,
+                                       int64_t           step,
+                                       const t_inputrec* inputrec,
+                                       t_state*          state,
+                                       matrix            parrinellorahmanMu,
+                                       matrix            M,
+                                       bool              bInitStep);
+
+/* Update the box, to be called after the coordinate update.
+ * For Berendsen P-coupling, also calculates the scaling factor
+ * and scales the coordinates.
+ * When the deform option is used, scales coordinates and box here.
+ */
+void update_pcouple_after_coordinates(FILE*                               fplog,
+                                      int64_t                             step,
+                                      const t_inputrec*                   inputrec,
+                                      int                                 homenr,
+                                      gmx::ArrayRef<const unsigned short> cFREEZE,
+                                      const matrix                        pressure,
+                                      const matrix                        forceVirial,
+                                      const matrix                        constraintVirial,
+                                      matrix                              pressureCouplingMu,
+                                      t_state*                            state,
+                                      t_nrnb*                             nrnb,
+                                      gmx::BoxDeformation*                boxDeformation,
+                                      bool                                scaleCoordinates);
+
+/* Return TRUE if OK, FALSE in case of Shake Error */
+
+extern bool update_randomize_velocities(const t_inputrec*                   ir,
+                                        int64_t                             step,
+                                        const t_commrec*                    cr,
+                                        int                                 homenr,
+                                        gmx::ArrayRef<const unsigned short> cTC,
+                                        gmx::ArrayRef<const real>           invMass,
+                                        gmx::ArrayRef<gmx::RVec>            v,
+                                        const gmx::Update*                  upd,
+                                        const gmx::Constraints*             constr);
+
+void berendsen_tcoupl(const t_inputrec*    ir,
+                      gmx_ekindata_t*      ekind,
+                      real                 dt,
+                      std::vector<double>& therm_integral); //NOLINT(google-runtime-references)
+
+void andersen_tcoupl(const t_inputrec*                   ir,
+                     int64_t                             step,
+                     const t_commrec*                    cr,
+                     int                                 homenr,
+                     gmx::ArrayRef<const unsigned short> cTC,
+                     gmx::ArrayRef<const real>           invMass,
+                     gmx::ArrayRef<gmx::RVec>            v,
+                     real                                rate,
+                     const std::vector<bool>&            randomize,
+                     gmx::ArrayRef<const real>           boltzfac);
+
+void nosehoover_tcoupl(const t_grpopts*      opts,
+                       const gmx_ekindata_t* ekind,
+                       real                  dt,
+                       gmx::ArrayRef<double> xi,
+                       gmx::ArrayRef<double> vxi,
+                       const t_extmass*      MassQ);
+
+void trotter_update(const t_inputrec*                   ir,
+                    int64_t                             step,
+                    gmx_ekindata_t*                     ekind,
+                    const gmx_enerdata_t*               enerd,
+                    t_state*                            state,
+                    const tensor                        vir,
+                    int                                 homenr,
+                    gmx::ArrayRef<const unsigned short> cTC,
+                    gmx::ArrayRef<const real>           invMass,
+                    const t_extmass*                    MassQ,
+                    gmx::ArrayRef<std::vector<int>>     trotter_seqlist,
+                    TrotterSequence                     trotter_seqno);
+
+gmx::EnumerationArray<TrotterSequence, std::vector<int>>
+init_npt_vars(const t_inputrec* ir, t_state* state, t_extmass* Mass, bool bTrotter);
+
+real NPT_energy(const t_inputrec* ir, const t_state* state, const t_extmass* MassQ);
+/* computes all the pressure/tempertature control energy terms to get a conserved energy */
+
+void vrescale_tcoupl(const t_inputrec*     ir,
+                     int64_t               step,
+                     gmx_ekindata_t*       ekind,
+                     real                  dt,
+                     gmx::ArrayRef<double> therm_integral);
+/* Compute temperature scaling. For V-rescale it is done in update. */
+
+void rescale_velocities(const gmx_ekindata_t*               ekind,
+                        gmx::ArrayRef<const unsigned short> cTC,
+                        int                                 start,
+                        int                                 end,
+                        gmx::ArrayRef<gmx::RVec>            v);
+/* Rescale the velocities with the scaling factor in ekind */
+
+/*!
+ * \brief Compute the new annealing temperature for a temperature group
+ *
+ * \param inputrec          The input record
+ * \param temperatureGroup  The temperature group
+ * \param time              The current time
+ * \return  The new reference temperature for the group
+ */
+real computeAnnealingTargetTemperature(const t_inputrec& inputrec, int temperatureGroup, real time);
+
+//! Check whether we do simulated annealing.
+bool doSimulatedAnnealing(const t_inputrec* ir);
+
+//! Initialize simulated annealing.
+bool initSimulatedAnnealing(t_inputrec* ir, gmx::Update* upd);
+
+// TODO: This is the only function in update.h altering the inputrec
+void update_annealing_target_temp(t_inputrec* ir, real t, gmx::Update* upd);
+/* Set reference temp for simulated annealing at time t*/
+
+real calc_temp(real ekin, real nrdf);
+/* Calculate the temperature */
+
+real calc_pres(PbcType pbcType, int nwall, const matrix box, const tensor ekin, const tensor vir, tensor pres);
+/* Calculate the pressure tensor, returns the scalar pressure.
+ * The unit of pressure is bar.
+ */
+
+void parrinellorahman_pcoupl(FILE*             fplog,
+                             int64_t           step,
+                             const t_inputrec* ir,
+                             real              dt,
+                             const tensor      pres,
+                             const tensor      box,
+                             tensor            box_rel,
+                             tensor            boxv,
+                             tensor            M,
+                             matrix            mu,
+                             bool              bFirstStep);
+
+/*! \brief Calculate the pressure coupling scaling matrix
+ *
+ * Used by Berendsen and C-Rescale pressure coupling, this function
+ * computes the current value of the scaling matrix. The template
+ * parameter determines the pressure coupling algorithm.
+ */
+template<PressureCoupling pressureCouplingType>
+void pressureCouplingCalculateScalingMatrix(FILE*             fplog,
+                                            int64_t           step,
+                                            const t_inputrec* ir,
+                                            real              dt,
+                                            const tensor      pres,
+                                            const matrix      box,
+                                            const matrix      force_vir,
+                                            const matrix      constraint_vir,
+                                            matrix            mu,
+                                            double*           baros_integral);
+
+/*! \brief Scale the box and coordinates
+ *
+ * Used by Berendsen and C-Rescale pressure coupling, this function scales
+ * the box, the positions, and the velocities (C-Rescale only) according to
+ * the scaling matrix mu. The template parameter determines the pressure
+ * coupling algorithm.
+ */
+template<PressureCoupling pressureCouplingType>
+void pressureCouplingScaleBoxAndCoordinates(const t_inputrec*                   ir,
+                                            const matrix                        mu,
+                                            matrix                              box,
+                                            matrix                              box_rel,
+                                            int                                 start,
+                                            int                                 nr_atoms,
+                                            gmx::ArrayRef<gmx::RVec>            x,
+                                            gmx::ArrayRef<gmx::RVec>            v,
+                                            gmx::ArrayRef<const unsigned short> cFREEZE,
+                                            t_nrnb*                             nrnb,
+                                            bool                                scaleCoordinates);
+
+void pleaseCiteCouplingAlgorithms(FILE* fplog, const t_inputrec& ir);
+
+/*! \brief Generate a new kinetic energy for the v-rescale thermostat
+ *
+ * Generates a new value for the kinetic energy, according to
+ * Bussi et al JCP (2007), Eq. (A7)
+ *
+ * This is used by update_tcoupl(), and by the VRescaleThermostat of the modular
+ * simulator.
+ * \todo Move this to the VRescaleThermostat once the modular simulator becomes
+ *       the default code path.
+ *
+ * \param[in] kk     present value of the kinetic energy of the atoms to be thermalized (in
+ * arbitrary units) \param[in] sigma  target average value of the kinetic energy (ndeg k_b T/2)  (in
+ * the same units as kk) \param[in] ndeg   number of degrees of freedom of the atoms to be
+ * thermalized \param[in] taut   relaxation time of the thermostat, in units of 'how often this
+ * routine is called' \param[in] step   the time step this routine is called on \param[in] seed the
+ * random number generator seed \return  the new kinetic energy
+ */
+real vrescale_resamplekin(real kk, real sigma, real ndeg, real taut, int64_t step, int64_t seed);
+
+#endif // GMX_MDLIB_COUPLING_H
diff --git a/src/include/gromacs/mdlib/dispersioncorrection.h b/src/include/gromacs/mdlib/dispersioncorrection.h
new file mode 100644 (file)
index 0000000..62e7cbc
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_DISPERSIONCORRECTION_H
+#define GMX_MDLIB_DISPERSIONCORRECTION_H
+
+#include <cstdio>
+
+#include <array>
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+
+struct gmx_mtop_t;
+struct interaction_const_t;
+struct t_forcerec;
+struct t_forcetable;
+struct t_inputrec;
+enum class DispersionCorrectionType : int;
+enum class VanDerWaalsType : int;
+enum class FreeEnergyPerturbationType : int;
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class MDLogger;
+} // namespace gmx
+
+class DispersionCorrection
+{
+public:
+    /*! \brief Constructor
+     *
+     * \param[in] mtop           The global topology
+     * \param[in] inputrec       The input record
+     * \param[in] useBuckingham  True when Buckingham is used instead of LJ
+     * \param[in] numAtomTypes   The number of non-bonded atom types
+     * \param[in] nonbondedForceParameters  The LJ or Bham parameter matrix stored as a flat list
+     * \param[in] ic             The nonbonded interaction parameters
+     * \param[in] tableFileName  Table file name, should != nullptr (checked)
+     */
+    DispersionCorrection(const gmx_mtop_t&          mtop,
+                         const t_inputrec&          inputrec,
+                         bool                       useBuckingham,
+                         int                        numAtomTypes,
+                         gmx::ArrayRef<const real>  nonbondedForceParameters,
+                         const interaction_const_t& ic,
+                         const char*                tableFileName);
+
+    /*! \brief Print dispersion correction information to the log file
+     *
+     * \param[in] mdlog  The MD logger
+     */
+    void print(const gmx::MDLogger& mdlog) const;
+
+    /*! \brief Computes and sets energy and virial correction parameters
+     *
+     * Sets all parameters that are affected by the cut-off and/or the
+     * LJ-Ewald coefficient. Should be called before calling calculate()
+     * and whenever interaction settings change, e.g. PME tuning.
+     *
+     * \param[in] ic  The nonbonded interaction parameters
+     */
+    void setParameters(const interaction_const_t& ic);
+
+    /*! \internal
+     * \brief Struct for returning all dispersion correction quantities
+     */
+    struct Correction
+    {
+        /*! \brief Correct the virial tensor for the missing dispersion
+         *
+         * \param[in,out] virialTensor  The virial tensor to correct
+         */
+        void correctVirial(tensor virialTensor) const
+        {
+            for (int m = 0; m < DIM; m++)
+            {
+                virialTensor[m][m] += virial;
+            }
+        }
+
+        real virial   = 0; //!< Scalar correction to the virial
+        real pressure = 0; //!< Scalar correction to the pressure
+        real energy   = 0; //!< Correction to the energy
+        real dvdl     = 0; //!< Correction to dH/dlambda
+    };
+
+    /*! \brief Computes and returns the dispersion correction for the pressure and energy
+     *
+     * \param[in]  box       The simulation unit cell
+     * \param[in]  lambda    The free-energy coupling parameter
+     */
+    Correction calculate(const matrix box, real lambda) const;
+
+private:
+    /*! \internal \brief Parameters that depend on the topology only
+     */
+    class TopologyParams
+    {
+    public:
+        TopologyParams(const gmx_mtop_t&         mtop,
+                       const t_inputrec&         inputrec,
+                       bool                      useBuckingham,
+                       int                       numAtomTypes,
+                       gmx::ArrayRef<const real> nonbondedForceParameters);
+
+        //! The number of atoms for computing the atom density
+        int numAtomsForDensity_;
+        //! The number of interactions to correct for, usually num. atoms/2
+        real numCorrections_;
+        //! Average C6 coefficient for for topology A/B ([0]/[1])
+        std::array<real, 2> avcsix_;
+        //! Average C12 coefficient for for topology A/B ([0]/[1])
+        std::array<real, 2> avctwelve_;
+    };
+
+    /*! \internal \brief Parameters that depend on the interaction functions and topology
+     */
+    struct InteractionParams
+    {
+    public:
+        ~InteractionParams();
+
+        //! Table used for correcting modified LJ interactions
+        std::unique_ptr<t_forcetable> dispersionCorrectionTable_;
+
+        //! Dispersion energy shift constant
+        real enershiftsix_ = 0;
+        //! Repulsion energy shift constant
+        real enershifttwelve_ = 0;
+        //! Dispersion energy difference per atom per unit of volume
+        real enerdiffsix_ = 0;
+        //! Repulsion energy difference per atom per unit of volume
+        real enerdifftwelve_ = 0;
+        //! Dispersion virial difference per atom per unit of volume
+        real virdiffsix_ = 0;
+        //! Repulsion virial difference per atom per unit of volume
+        real virdifftwelve_ = 0;
+    };
+
+    //! Sets the interaction parameters
+    static void setInteractionParameters(DispersionCorrection::InteractionParams* iParams,
+                                         const interaction_const_t&               ic,
+                                         const char*                              tableFileName);
+
+    //! Returns whether we correct both dispersion and repulsion
+    bool correctFullInteraction() const;
+
+    //! Type of dispersion correction
+    DispersionCorrectionType eDispCorr_;
+    //! Type of Van der Waals interaction
+    VanDerWaalsType vdwType_;
+    //! Free-energy perturbation
+    FreeEnergyPerturbationType eFep_;
+    //! Topology parameters
+    TopologyParams topParams_;
+    //! Interaction parameters
+    InteractionParams iParams_;
+};
+
+#endif
diff --git a/src/include/gromacs/mdlib/ebin.h b/src/include/gromacs/mdlib/ebin.h
new file mode 100644 (file)
index 0000000..ac76a2a
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/* \internal \file
+ * \brief
+ * Declares structures and interfaces to store, compute and print current and average values for thermodynamics properties.
+ *
+ * The word 'energy' is used here in wide scope and refer to any thermodynamic quantity that can benefit from
+ * averaging (e.g. temperature, pressure).
+ *
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_EBIN_H
+#define GMX_MDLIB_EBIN_H
+
+#include <stdio.h>
+
+#include "gromacs/fileio/enxio.h"
+#include "gromacs/trajectory/energyframe.h"
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/* \brief Running averaging structure ('energy bin') to store thermodynamic values.
+ *
+ * Collects the data on thermodynamic parameters (energy terms, temperature,
+ * pressure etc.) during the run, including their current and average values.
+ *
+ * \todo Clean this structure from unused values.
+ */
+struct t_ebin
+{
+    //! Number of thermodynamic terms
+    int nener;
+    //! Name and units for each term
+    gmx_enxnm_t* enm;
+    //! Number of steps used for sum (for energy history)
+    int64_t nsteps;
+    //! Number of values added to the sum so far
+    int64_t nsum;
+    //! Term values: each structure stores current, running average and sum.
+    t_energy* e;
+    //! Total number of steps saved (for energy history)
+    int64_t nsteps_sim;
+    //! Total number of values added to sum (used when printing average values at the end of the run)
+    int64_t nsum_sim;
+    //! Energy values throughout the entire simulation: structure stores current, average and sum, but only sum value is used to compute averages
+    t_energy* e_sim;
+};
+
+/* \brief Type of expected output: normal or average.
+ */
+enum
+{
+    eprNORMAL,
+    eprAVER,
+    eprNR
+};
+
+/*! \brief Create the structure to store thermodynamic properties*/
+t_ebin* mk_ebin();
+
+/*! \brief Destroy the \c eb structure.
+ *
+ * \param[in,out] eb  Pointer to the structure to destroy.
+ */
+void done_ebin(t_ebin* eb);
+
+/*! \brief Create space for the extra thermodynamic term(s) and register its(their) name(s).
+ *
+ * The enm array must be static, because the contents are not copied, only the pointers.
+ *
+ * \param[in] eb     Srtucture in which the space for the termodynamic terms shall be created..
+ * \param[in] nener  Number of thermodyamic terms to allocate memory for.
+ * \param[in] enm    Names of the terms.
+ * \param[in] unit   Units.
+ *
+ * \returns          A serial number (index) for the newly allocated terms.
+ */
+int get_ebin_space(t_ebin* eb, int nener, const char* const enm[], const char* unit);
+
+/*! \brief Add current value of the thermodynamic term(s) to the bin(s).
+ *
+ * Add nener reals (eg. energies, box-lengths, pressures) to the at position specified by \c
+ * entryIndex. If bSum is TRUE then the reals are also added to the sum and sum of squares.
+ *
+ * \param[in] eb          Structure that stores the thermodynamic values.
+ * \param[in] entryIndex  Internal index of the term(s) to add.
+ * \param[in] nener       Number of the terms to add.
+ * \param[in] ener        Value(s) of thermodynamic term(s) (nener ptc.)
+ * \param[in] bSum        If the average value should be accumulated for this term(s).
+ */
+void add_ebin(t_ebin* eb, int entryIndex, int nener, const real ener[], gmx_bool bSum);
+
+
+/*! \brief Add values from array to the bins if the matching entry in \c shouldUse is true.
+ *
+ * Caller must ensure that \c shouldUse and \c ener to have the same
+ * size, and that \c eb has enough room for the number of true
+ * entries in \c shouldUse.
+ *
+ * \param[in] eb          Structure that stores the thermodynamic values.
+ * \param[in] entryIndex  Internal index of the term(s).
+ * \param[in] shouldUse   Array of booleans that indicate which terms should be used.
+ * \param[in] ener        Values of thermodinamic terms to add.
+ * \param[in] bSum        If the average value should be accumulated for these terms.
+ */
+void add_ebin_indexed(t_ebin*                   eb,
+                      int                       entryIndex,
+                      gmx::ArrayRef<bool>       shouldUse,
+                      gmx::ArrayRef<const real> ener,
+                      gmx_bool                  bSum);
+
+/*! \brief Increase the counters for the sums.
+ *
+ * This routine should be called after all add_ebin calls for this step.
+ *
+ * \param[in] increment   How much counts should be increased
+ * \param[in] eb          Structure that stores the thermodynamic values.
+ * \param[in] bSum        If the sums counters should be increased as well.
+ */
+void ebin_increase_count(int increment, t_ebin* eb, gmx_bool bSum);
+
+
+/*! \brief Reset the average and fluctuation sums.
+ *
+ * \param[in] eb          Structure that stores the thermodynamic values.
+ */
+void reset_ebin_sums(t_ebin* eb);
+
+
+/*! \brief Print the contents of some energy bins.
+ *
+ * We will print \c nperline entries on a text line (advisory <=
+ * 5). \c prmode may be any of the above listed enum values. \c tsteps is
+ * used only when \c eprAVER is set. If \c bPrHead than the
+ * header is printed.
+ *
+ * \c entryIndex and \c nener must be in [0,\c eb->nener), except that \c
+ * nener -1 is interpreted as \c eb->nener.
+ *
+ * \todo Callers should be refactored to pass \c eb->nener, rather than
+ *       us implement and rely on this special behavior of -1.
+ *
+ * \param[in] fp          I/O pointer to print to.
+ * \param[in] eb          Structure that stores the thermodynamic values.
+ * \param[in] entryIndex  Internal index of the term(s).
+ * \param[in] nener       Number of the terms to print.
+ * \param[in] nperline    Number of values per line.
+ * \param[in] prmode      Print current (eprNORMAL) or average (eprAVER) values.
+ * \param[in] bPrHead     If the header should be printed.
+ */
+void pr_ebin(FILE* fp, t_ebin* eb, int entryIndex, int nener, int nperline, int prmode, gmx_bool bPrHead);
+
+#endif
diff --git a/src/include/gromacs/mdlib/enerdata_utils.h b/src/include/gromacs/mdlib/enerdata_utils.h
new file mode 100644 (file)
index 0000000..a36ee63
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_MDLIB_ENERDATA_UTILS_H
+#define GMX_MDLIB_ENERDATA_UTILS_H
+
+#include "gromacs/math/arrayrefwithpadding.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+
+struct gmx_enerdata_t;
+struct gmx_grppairener_t;
+struct t_fepvals;
+struct t_lambda;
+
+void reset_dvdl_enerdata(gmx_enerdata_t* enerd);
+/* Resets only the dvdl energy data */
+
+void reset_enerdata(gmx_enerdata_t* enerd);
+/* Resets the energy data */
+
+/*! \brief Sums energy group pair contributions into epot */
+void sum_epot(const gmx_grppairener_t& grpp, real* epot);
+
+/*! \brief Accumulates potential energy contributions to obtain final potential energies
+ *
+ * Accumulates energy group pair contributions into the output energy components
+ * and sums all potential energies into the total potential energy term.
+ * With free-energy also computes the foreign lambda potential energy differences.
+ *
+ * \param[in,out] enerd    Energy data with components to sum and to accumulate into
+ * \param[in]     lambda   Vector of free-energy lambdas
+ * \param[in]     fepvals  Foreign lambda energy differences, only summed with !=nullptr
+ */
+void accumulatePotentialEnergies(gmx_enerdata_t*           enerd,
+                                 gmx::ArrayRef<const real> lambda,
+                                 const t_lambda*           fepvals);
+
+/*! \brief Accumulates kinetic and constraint contributions to dH/dlambda and foreign energies */
+void accumulateKineticLambdaComponents(gmx_enerdata_t*           enerd,
+                                       gmx::ArrayRef<const real> lambda,
+                                       const t_lambda&           fepvals);
+
+#endif
diff --git a/src/include/gromacs/mdlib/energydrifttracker.h b/src/include/gromacs/mdlib/energydrifttracker.h
new file mode 100644 (file)
index 0000000..f189b66
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares and defines the EnergyDriftTracker class.
+ *
+ * \author Berk Hess <hess@kth.se>
+ *
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_ENERGYDRIFTTRACKER_H
+#define GMX_MDLIB_ENERGYDRIFTTRACKER_H
+
+#include <string>
+
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief Class for tracking and printing the drift in the conserved energy quantity
+ */
+class EnergyDriftTracker
+{
+public:
+    /*! \brief Constructor
+     *
+     * \param[in] numAtoms  The total number of atoms in the system
+     */
+    EnergyDriftTracker(int numAtoms) : numAtoms_(numAtoms) {}
+
+    //! Add a point to the conserved energy tracking
+    void addPoint(double time, double energy);
+
+    //! Returns the time of the last point minus the time of the first point
+    double timeInterval() const { return lastTime_ - firstTime_; }
+
+    //! Returns the energy drift over the measured interval
+    double energyDrift() const;
+
+    /*! \brief Returns two-line string with the time interval and drift over the interval
+     *
+     * \param[in] partName  A descriptive name for the period over which the tracking occured
+     */
+    std::string energyDriftString(const std::string& partName) const;
+
+private:
+    //! Whether we stored the first point
+    bool storedFirst_ = false;
+    //! The first time stored
+    double firstTime_ = 0;
+    //! The energy for the first time point
+    double firstEnergy_ = 0;
+    //! The last time stored
+    double lastTime_ = 0;
+    //! The energy for the last time point
+    double lastEnergy_ = 0;
+    //! The number of atoms in the system
+    int numAtoms_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/energyoutput.h b/src/include/gromacs/mdlib/energyoutput.h
new file mode 100644 (file)
index 0000000..6c58055
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Header for the code that writes energy-like quantities.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+#ifndef GMX_MDLIB_ENERGYOUTPUT_H
+#define GMX_MDLIB_ENERGYOUTPUT_H
+
+#include <cstdio>
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/enerdata.h"
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+class energyhistory_t;
+struct ener_file;
+class gmx_ekindata_t;
+struct gmx_enerdata_t;
+struct SimulationGroups;
+struct gmx_mtop_t;
+struct gmx_output_env_t;
+struct pull_t;
+struct t_ebin;
+struct t_expanded;
+struct t_fcdata;
+struct t_grpopts;
+struct t_inputrec;
+struct t_lambda;
+class t_state;
+
+struct t_mde_delta_h_coll;
+
+namespace gmx
+{
+class Awh;
+class Constraints;
+struct MDModulesNotifiers;
+enum class StartingBehavior;
+} // namespace gmx
+
+//! \brief Printed names for intergroup energies
+const char* enumValueToString(NonBondedEnergyTerms enumValue);
+
+/* \brief delta_h block type enum: the kinds of energies written out. */
+enum
+{
+    //! Delta H BAR energy difference
+    dhbtDH = 0,
+    //! dH/dlambda derivative
+    dhbtDHDL = 1,
+    //! System energy
+    dhbtEN,
+    //! pV term
+    dhbtPV,
+    //! Expanded ensemble statistics
+    dhbtEXPANDED,
+    //! Total number of energy types in this enum
+    dhbtNR
+};
+
+namespace gmx
+{
+class EnergyDriftTracker;
+
+/*! \internal
+ * \brief Arrays connected to Pressure and Temperature coupling
+ */
+struct PTCouplingArrays
+{
+    //! Box velocities for Parrinello-Rahman P-coupling.
+    const rvec* boxv;
+    //! Nose-Hoover coordinates.
+    ArrayRef<const double> nosehoover_xi;
+    //! Nose-Hoover velocities.
+    ArrayRef<const double> nosehoover_vxi;
+    //! Pressure Nose-Hoover coordinates.
+    ArrayRef<const double> nhpres_xi;
+    //! Pressure Nose-Hoover velocities.
+    ArrayRef<const double> nhpres_vxi;
+};
+
+/* Energy output class
+ *
+ * This is the collection of energy averages collected during mdrun, and to
+ * be written out to the .edr file.
+ *
+ * \todo Use more std containers.
+ * \todo Write free-energy output also to energy file (after adding more tests)
+ */
+class EnergyOutput
+{
+public:
+    /*! \brief Initiate MD energy bin
+     *
+     * \param[in] fp_ene     Energy output file.
+     * \param[in] mtop       Topology.
+     * \param[in] inputrec   Input parameters.
+     * \param[in] pull_work  Pulling simulations data
+     * \param[in] fp_dhdl    FEP file.
+     * \param[in] isRerun    Is this is a rerun instead of the simulations.
+     * \param[in] startingBehavior  Run starting behavior.
+     * \param[in] simulationsShareState  Tells whether the physical state is shared over simulations
+     * \param[in] mdModulesNotifiers Notifications to MD modules.
+     */
+    EnergyOutput(ener_file*                fp_ene,
+                 const gmx_mtop_t&         mtop,
+                 const t_inputrec&         inputrec,
+                 const pull_t*             pull_work,
+                 FILE*                     fp_dhdl,
+                 bool                      isRerun,
+                 StartingBehavior          startingBehavior,
+                 bool                      simulationsShareState,
+                 const MDModulesNotifiers& mdModulesNotifiers);
+
+    ~EnergyOutput();
+
+    /*! \brief Update the averaging structures.
+     *
+     * Called every step on which the thermodynamic values are evaluated.
+     *
+     * \param[in] bDoDHDL           Whether the FEP is enabled.
+     * \param[in] bSum              If this stepshould be recorded to compute sums and averages.
+     * \param[in] time              Current simulation time.
+     * \param[in] tmass             Total mass
+     * \param[in] enerd             Energy data object.
+     * \param[in] fep               FEP data.
+     * \param[in] expand            Expanded ensemble (for FEP).
+     * \param[in] lastbox           PBC data.
+     * \param[in] ptCouplingArrays  Arrays connected to pressure and temperature coupling.
+     * \param[in] fep_state         The current alchemical state we are in.
+     * \param[in] vir               Total virial.
+     * \param[in] pres              Pressure.
+     * \param[in] ekind             Kinetic energy data.
+     * \param[in] mu_tot            Total dipole.
+     * \param[in] constr            Constraints object to get RMSD from (for LINCS only).
+     */
+    void addDataAtEnergyStep(bool                    bDoDHDL,
+                             bool                    bSum,
+                             double                  time,
+                             real                    tmass,
+                             const gmx_enerdata_t*   enerd,
+                             const t_lambda*         fep,
+                             const t_expanded*       expand,
+                             const matrix            lastbox,
+                             PTCouplingArrays        ptCouplingArrays,
+                             int                     fep_state,
+                             const tensor            vir,
+                             const tensor            pres,
+                             const gmx_ekindata_t*   ekind,
+                             const rvec              mu_tot,
+                             const gmx::Constraints* constr);
+
+    /*! \brief Update the data averaging structure counts.
+     *
+     * Updates the number of steps, the values have not being computed.
+     */
+    void recordNonEnergyStep();
+
+    /*! \brief Writes current quantites to log and energy files.
+     *
+     * Prints current values of energies, pressure, temperature, restraint
+     * data, etc. to energy output file and to the log file (if not nullptr).
+     *
+     * This function only does something useful when bEne || bDR || bOR || log.
+     *
+     * \todo Perhaps this responsibility should involve some other
+     *       object visiting all the contributing objects.
+     *
+     * \param[in] fp_ene   Energy file for the output.
+     * \param[in] bEne     If it is a step for energy output or last step.
+     * \param[in] bDR      If it is a step of writing distance restraints.
+     * \param[in] bOR      If it is a step of writing orientation restraints.
+     * \param[in] log      Pointer to the log file.
+     * \param[in] step     Current step.
+     * \param[in] time     Current simulation time.
+     * \param[in] fcd      Bonded force computation data,
+     *                     including orientation and distance restraints.
+     * \param[in] awh      AWH data.
+     */
+    void printStepToEnergyFile(ener_file* fp_ene,
+                               bool       bEne,
+                               bool       bDR,
+                               bool       bOR,
+                               FILE*      log,
+                               int64_t    step,
+                               double     time,
+                               t_fcdata*  fcd,
+                               gmx::Awh*  awh);
+
+    /*! \brief Print reference temperatures for annealing groups.
+     *
+     * Nothing is done if log is nullptr.
+     *
+     * \param[in] log     Log file to print to.
+     * \param[in] groups  Information on atom groups.
+     * \param[in] opts    Atom temperature coupling groups options
+     *                    (annealing is done by groups).
+     */
+    static void printAnnealingTemperatures(FILE* log, const SimulationGroups* groups, const t_grpopts* opts);
+
+    /*! \brief Prints average values to log file.
+     *
+     * This is called at the end of the simulation run to print accumulated average values.
+     * Nothing it done if log is nullptr.
+     *
+     * \param[in]   log      Where to print.
+     * \param[in]   groups   Atom groups.
+     */
+    void printAverages(FILE* log, const SimulationGroups* groups);
+
+    /*! \brief Get the number of thermodynamic terms recorded.
+     *
+     * \todo Refactor this to return the expected output size,
+     *       rather than exposing the implementation details about
+     *       thermodynamic terms.
+     *
+     * \returns Number of thermodynamic terms.
+     */
+    int numEnergyTerms() const;
+
+    /*! \brief Fill the energyhistory_t data.
+     *
+     * Between .edr writes, the averages are history dependent,
+     * and that history needs to be retained in checkpoints.
+     * These functions set/read the energyhistory_t class
+     * that is written to checkpoints.
+     *
+     * \param[out] enerhist  Energy history data structure.
+     */
+    void fillEnergyHistory(energyhistory_t* enerhist) const;
+
+    /*! \brief Restore from energyhistory_t data.
+     *
+     * \param[in] enerhist  Energy history data structure.
+     */
+    void restoreFromEnergyHistory(const energyhistory_t& enerhist);
+
+    //! Print an output header to the log file.
+    static void printHeader(FILE* log, int64_t steps, double time);
+
+    /*! \brief Print conserved energy drift message to \p fplog
+     *
+     * Note that this is only over the current run (times are printed),
+     * this is not from the original start time for runs with continuation.
+     * This has the advantage that one can find if conservation issues are
+     * from the current run with the current settings on the current hardware.
+     */
+    void printEnergyConservation(FILE* fplog, int simulationPart, bool usingMdIntegrator) const;
+
+private:
+    //! Timestep
+    double delta_t_ = 0;
+
+    //! Structure to store energy components and their running averages
+    t_ebin* ebin_ = nullptr;
+
+    //! Is the periodic box triclinic
+    bool bTricl_ = false;
+    //! NHC trotter is used
+    bool bNHC_trotter_ = false;
+    //! If Nose-Hoover chains should be printed
+    bool bPrintNHChains_ = false;
+    //! If MTTK integrator was used
+    bool bMTTK_ = false;
+
+    //! Temperature control scheme
+    TemperatureCoupling etc_ = TemperatureCoupling::No;
+
+    //! Which of the main energy terms should be printed
+    bool bEner_[F_NRE] = { false };
+    //! Index for main energy terms
+    int ie_ = 0;
+    //! Number of energy terms from F_NRE list to be saved (i.e. number of 'true' in bEner)
+    int f_nre_ = 0;
+
+    //! Index for constraints RMSD
+    int iconrmsd_ = 0;
+    /* !\brief How many constraints RMSD terms there are.
+     * Usually 1 if LINCS is used and 0 otherwise)
+     * nCrmsd > 0 indicates when constraints RMSD is saves, hence no boolean
+     */
+    int nCrmsd_ = 0;
+
+    //! Is the periodic box dynamic
+    bool bDynBox_ = false;
+    //! Index for box dimensions
+    int ib_ = 0;
+    //! Index for box volume
+    int ivol_ = 0;
+    //! Index for density
+    int idens_ = 0;
+    //! Triclinic box and not a rerun
+    bool bDiagPres_ = false;
+    //! Reference pressure, averaged over dimensions
+    real ref_p_ = 0.0;
+    //! Index for thermodynamic work (pV)
+    int ipv_ = 0;
+    //! Index for entalpy (pV + total energy)
+    int ienthalpy_ = 0;
+
+    //! If we have pressure computed
+    bool bPres_ = false;
+    //! Index for total virial
+    int ivir_ = 0;
+    //! Index for pressure
+    int ipres_ = 0;
+    /*! \brief Index for surface tension
+     * [(pres[ZZ][ZZ]-(pres[XX][XX]+pres[YY][YY])*0.5)*box[ZZ][ZZ]]*/
+    int isurft_ = 0;
+
+    //! Pressure control scheme
+    PressureCoupling epc_ = PressureCoupling::No;
+    //! Index for velocity of the box borders
+    int ipc_ = 0;
+
+    //! If dipole was calculated
+    bool bMu_ = false;
+    //! Index for the dipole
+    int imu_ = 0;
+
+    //! Index for coseine acceleration used for viscocity calculation
+    int ivcos_ = 0;
+    //! Index for viscocity
+    int ivisc_ = 0;
+
+    //! Which energy terms from NonBondedEnergyTerms list should be printed in group-to-group block
+    gmx::EnumerationArray<NonBondedEnergyTerms, bool> bEInd_;
+    //! Number of energy terms to be printed (these, for which bEInd[] == true)
+    int nEc_ = 0;
+    //! Number of energy output groups
+    int nEg_ = 0;
+    //! Number of intergroup energy sets to be printed for each energy term (nE = (nEg*(nEg+1))/2)
+    int nE_ = 0;
+    //! Indexes for integroup energy sets (each set with nEc energies)
+    std::vector<int> igrp_;
+
+    //! Number of temperature coupling groups
+    int nTC_ = 0;
+    //! Index for temperature
+    int itemp_ = 0;
+
+    //! Number of Nose-Hoover chains
+    int nNHC_ = 0;
+    //! Number of temperature coupling coefficients in case of NH Chains
+    int mde_n_ = 0;
+    //! Index for temperature coupling coefficient in case of NH chains
+    int itc_ = 0;
+
+    //! Number of temperature coupling terms if the temperature control is dealt by barostat (MTTK case)
+    int nTCP_ = 0;
+    //! Scalling factors for temperaturs control in MTTK
+    int mdeb_n_ = 0;
+    //! Index for scalling factor of MTTK
+    int itcb_ = 0;
+
+    //! Array to accumulate values during update
+    std::vector<real> tmp_r_;
+
+    //! The dhdl.xvg output file
+    FILE* fp_dhdl_ = nullptr;
+    //! Energy components for dhdl.xvg output
+    std::vector<double> dE_;
+    //! The delta U components (raw data + histogram)
+    std::unique_ptr<t_mde_delta_h_coll> dhc_;
+    //! Temperatures for simulated tempering groups
+    std::vector<real> temperatures_;
+
+    //! For tracking the conserved or total energy
+    std::unique_ptr<EnergyDriftTracker> conservedEnergyTracker_;
+};
+
+} // namespace gmx
+
+//! Open the dhdl file for output
+FILE* open_dhdl(const char* filename, const t_inputrec* ir, const gmx_output_env_t* oenv);
+
+#endif
diff --git a/src/include/gromacs/mdlib/expanded.h b/src/include/gromacs/mdlib/expanded.h
new file mode 100644 (file)
index 0000000..8e4ea1f
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_EXPANDED_H
+#define GMX_MDLIB_EXPANDED_H
+
+#include <stdio.h>
+
+#include "gromacs/math/vectypes.h"
+
+struct df_history_t;
+struct gmx_enerdata_t;
+struct t_expanded;
+struct t_extmass;
+struct t_inputrec;
+struct t_lambda;
+struct t_simtemp;
+class t_state;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+void init_npt_masses(const t_inputrec* ir, t_state* state, t_extmass* MassQ, bool bInit);
+
+void init_expanded_ensemble(bool bStateFromCP, const t_inputrec* ir, df_history_t* dfhist);
+
+int ExpandedEnsembleDynamics(FILE*                               log,
+                             t_inputrec*                         ir,
+                             const gmx_enerdata_t*               enerd,
+                             t_state*                            state,
+                             t_extmass*                          MassQ,
+                             int                                 fep_state,
+                             df_history_t*                       dfhist,
+                             int64_t                             step,
+                             rvec*                               v,
+                             int                                 homenr,
+                             gmx::ArrayRef<const unsigned short> cTC);
+
+/*!
+ * \brief Return a new lambda state using expanded ensemble
+ *
+ * \param log  File pointer to the log file
+ * \param ir  The input record
+ * \param enerd  Data for energy output.
+ * \param fep_state  The current lambda state
+ * \param dfhist  Free energy sampling history struct
+ * \param step  The current simulation step
+ * \return  The new lambda state
+ */
+int expandedEnsembleUpdateLambdaState(FILE*                 log,
+                                      const t_inputrec*     ir,
+                                      const gmx_enerdata_t* enerd,
+                                      int                   fep_state,
+                                      df_history_t*         dfhist,
+                                      int64_t               step);
+
+void PrintFreeEnergyInfoToFile(FILE*               outfile,
+                               const t_lambda*     fep,
+                               const t_expanded*   expand,
+                               const t_simtemp*    simtemp,
+                               const df_history_t* dfhist,
+                               int                 fep_state,
+                               int                 frequency,
+                               int64_t             step);
+
+#endif
diff --git a/src/include/gromacs/mdlib/expanded_internal.h b/src/include/gromacs/mdlib/expanded_internal.h
new file mode 100644 (file)
index 0000000..f2f70b7
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares internal functionality for expanded ensemble
+ *
+ * This file is only used by expanded.cpp and tests/expanded.cpp.
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \author Michael Shirts <michael.shirts@colorado.edu>
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_EXPANDEDINTERNAL_H
+#define GMX_MDLIB_EXPANDEDINTERNAL_H
+
+#include "gromacs/utility/real.h"
+
+enum class LambdaWeightCalculation : int;
+
+namespace gmx
+{
+/*! \brief Calculates the acceptance weight for a lambda state transition
+ *
+ * \param[in] calculationMode  How the lambda weights are calculated
+ * \param[in] lambdaEnergyDifference  The difference in energy between the two states
+ * \return  The acceptance weight
+ */
+real calculateAcceptanceWeight(LambdaWeightCalculation calculationMode, real lambdaEnergyDifference);
+} // namespace gmx
+
+#endif // GMX_MDLIB_EXPANDEDINTERNAL_H
diff --git a/src/include/gromacs/mdlib/force.h b/src/include/gromacs/mdlib/force.h
new file mode 100644 (file)
index 0000000..f9ff390
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_FORCE_H
+#define GMX_MDLIB_FORCE_H
+
+#include <cstdio>
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+class DDBalanceRegionHandler;
+struct gmx_edsam;
+struct gmx_enerdata_t;
+struct gmx_enfrot;
+struct SimulationGroups;
+struct gmx_localtop_t;
+struct gmx_multisim_t;
+struct gmx_wallcycle;
+struct gmx_pme_t;
+class history_t;
+class InteractionDefinitions;
+struct pull_t;
+struct t_commrec;
+struct t_forcerec;
+struct t_inputrec;
+struct t_lambda;
+struct t_mdatoms;
+struct t_nrnb;
+struct gmx_ewald_tab_t;
+class CpuPpLongRangeNonbondeds;
+
+namespace gmx
+{
+template<typename>
+class ArrayRefWithPadding;
+class Awh;
+class ForceBuffersView;
+class ForceWithVirial;
+class ImdSession;
+class MdrunScheduleWorkload;
+class MDLogger;
+class StepWorkload;
+class VirtualSitesHandler;
+} // namespace gmx
+
+struct ewald_corr_thread_t
+{
+    real                                                            Vcorr_q;
+    real                                                            Vcorr_lj;
+    gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, real> dvdl;
+    tensor                                                          vir_q;
+    tensor                                                          vir_lj;
+};
+
+/* Perform the force and, if requested, energy computation
+ *
+ * Without multiple time stepping the force is returned in force->force().
+ *
+ * With multiple time stepping the behavior depends on the integration step.
+ * At fast steps (step % mtsFactor != 0), the fast force is returned in
+ * force->force(). The force->forceMtsCombined() buffer is unused.
+ * At slow steps, the normal force is returned in force->force(),
+ * unless the \p GMX_FORCE_DO_NOT_NEED_NORMAL_FORCE is set in \p legacyFlags.
+ * A MTS-combined force, F_fast + mtsFactor*F_slow, is always returned in
+ * force->forceMtsCombined(). This forceMts can be used directly in a standard
+ * leap-frog integrator to do multiple time stepping.
+ */
+void do_force(FILE*                               log,
+              const t_commrec*                    cr,
+              const gmx_multisim_t*               ms,
+              const t_inputrec&                   inputrec,
+              gmx::Awh*                           awh,
+              gmx_enfrot*                         enforcedRotation,
+              gmx::ImdSession*                    imdSession,
+              pull_t*                             pull_work,
+              int64_t                             step,
+              t_nrnb*                             nrnb,
+              gmx_wallcycle*                      wcycle,
+              const gmx_localtop_t*               top,
+              const matrix                        box,
+              gmx::ArrayRefWithPadding<gmx::RVec> coordinates,
+              const history_t*                    hist,
+              gmx::ForceBuffersView*              force,
+              tensor                              vir_force,
+              const t_mdatoms*                    mdatoms,
+              gmx_enerdata_t*                     enerd,
+              gmx::ArrayRef<const real>           lambda,
+              t_forcerec*                         fr,
+              gmx::MdrunScheduleWorkload*         runScheduleWork,
+              gmx::VirtualSitesHandler*           vsite,
+              rvec                                mu_tot,
+              double                              t,
+              gmx_edsam*                          ed,
+              CpuPpLongRangeNonbondeds*           longRangeNonbondeds,
+              int                                 legacyFlags,
+              const DDBalanceRegionHandler&       ddBalanceRegionHandler);
+
+/* Communicate coordinates (if parallel).
+ * Do neighbor searching (if necessary).
+ * Calculate forces.
+ * Communicate forces (if parallel).
+ * Spread forces for vsites (if present).
+ *
+ * f is always required.
+ */
+
+
+class CpuPpLongRangeNonbondeds
+{
+public:
+    /* \brief Constructor
+     *
+     * Should be called after init_forcerec if params come from a populated forcerec
+     */
+    CpuPpLongRangeNonbondeds(int                         numberOfTestPaticles,
+                             real                        ewaldCoeffQ,
+                             real                        epsilonR,
+                             gmx::ArrayRef<const double> chargeC6Sum,
+                             CoulombInteractionType      eeltype,
+                             VanDerWaalsType             vdwtype,
+                             const t_inputrec&           inputrec,
+                             t_nrnb*                     nrnb,
+                             gmx_wallcycle*              wcycle,
+                             FILE*                       fplog);
+
+    ~CpuPpLongRangeNonbondeds();
+
+    void updateAfterPartition(const t_mdatoms& md);
+
+    /* Calculate CPU Ewald or PME-mesh forces when done on this rank and Ewald corrections, when used
+     *
+     * Note that Ewald dipole and net charge corrections are always computed here, independently
+     * of whether the PME-mesh contribution is computed on a separate PME rank or on a GPU.
+     */
+    void calculate(gmx_pme_t*                     pmedata,
+                   const t_commrec*               commrec,
+                   gmx::ArrayRef<const gmx::RVec> coordinates,
+                   gmx::ForceWithVirial*          forceWithVirial,
+                   gmx_enerdata_t*                enerd,
+                   const matrix                   box,
+                   gmx::ArrayRef<const real>      lambda,
+                   gmx::ArrayRef<const gmx::RVec> mu_tot,
+                   const gmx::StepWorkload&       stepWork,
+                   const DDBalanceRegionHandler&  ddBalanceRegionHandler);
+
+private:
+    //! Number of particles for test particle insertion
+    int numTpiAtoms_;
+    //! Ewald charge coefficient
+    real ewaldCoeffQ_;
+    //! Dielectric constant
+    real epsilonR_;
+    //! [0]: sum of charges; [1]: sum of C6's
+    gmx::ArrayRef<const double> chargeC6Sum_;
+    //! Cut-off treatment for Coulomb
+    CoulombInteractionType coulombInteractionType_;
+    //! Van der Waals interaction treatment
+    VanDerWaalsType vanDerWaalsType_;
+    //! Ewald geometry
+    EwaldGeometry ewaldGeometry_;
+    //! Epsilon for PME dipole correction
+    real epsilonSurface_;
+    //! Whether a long range correction is used
+    bool haveEwaldSurfaceTerm_;
+    //! Scaling factor for the box for Ewald
+    real wallEwaldZfac_;
+    //! Whether the simulation is 2D periodic with two walls
+    bool havePbcXY2Walls_;
+    //! Free energy perturbation type
+    FreeEnergyPerturbationType freeEnergyPerturbationType_;
+    //! Number of atoms on this node
+    int homenr_;
+    //! Whether there are perturbed interactions
+    bool havePerturbed_;
+    //! State A charge
+    gmx::ArrayRef<const real> chargeA_;
+    //! State B charge
+    gmx::ArrayRef<const real> chargeB_;
+    //! State A LJ c6
+    gmx::ArrayRef<const real> sqrt_c6A_;
+    //! State B LJ c6
+    gmx::ArrayRef<const real> sqrt_c6B_;
+    //! State A LJ sigma
+    gmx::ArrayRef<const real> sigmaA_;
+    //! State B LJ sigma
+    gmx::ArrayRef<const real> sigmaB_;
+    //! Ewald correction thread local virial and energy data
+    std::vector<ewald_corr_thread_t> outputPerThread_;
+    //! Ewald table
+    std::unique_ptr<gmx_ewald_tab_t> ewaldTable_;
+    //! Non bonded kernel flop counters
+    t_nrnb* nrnb_;
+    //! Wall cycle counters
+    gmx_wallcycle* wcycle_;
+};
+
+#endif
diff --git a/src/include/gromacs/mdlib/force_flags.h b/src/include/gromacs/mdlib/force_flags.h
new file mode 100644 (file)
index 0000000..dd13874
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2012, The GROMACS development team.
+ * Copyright (c) 2012,2014,2015,2017,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_FORCE_FLAGS_H
+#define GMX_MDLIB_FORCE_FLAGS_H
+
+/* Flags to tell the force calculation routines what (not) to do */
+
+/* The state has changed, always set unless TPI is used. */
+#define GMX_FORCE_STATECHANGED (1u << 0u)
+/* The box might have changed */
+#define GMX_FORCE_DYNAMICBOX (1u << 1u)
+/* Do neighbor searching */
+#define GMX_FORCE_NS (1u << 2u)
+/* Calculate listed energies/forces (e.g. bonds, restraints, 1-4, FEP non-bonded) */
+#define GMX_FORCE_LISTED (1u << 4u)
+/* Calculate non-bonded energies/forces */
+#define GMX_FORCE_NONBONDED (1u << 6u)
+/* Calculate forces (not only energies) */
+#define GMX_FORCE_FORCES (1u << 7u)
+/* Calculate the virial */
+#define GMX_FORCE_VIRIAL (1u << 8u)
+/* Calculate energies */
+#define GMX_FORCE_ENERGY (1u << 9u)
+/* Calculate dHdl */
+#define GMX_FORCE_DHDL (1u << 10u)
+/* Tells whether only the MTS combined force buffer is needed and not the normal force buffer */
+#define GMX_FORCE_DO_NOT_NEED_NORMAL_FORCE (1u << 11u)
+
+/* Normally one want all energy terms and forces */
+#define GMX_FORCE_ALLFORCES (GMX_FORCE_LISTED | GMX_FORCE_NONBONDED | GMX_FORCE_FORCES)
+
+#endif
diff --git a/src/include/gromacs/mdlib/forcerec.h b/src/include/gromacs/mdlib/forcerec.h
new file mode 100644 (file)
index 0000000..11d0e42
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_FORCEREC_H
+#define GMX_MDLIB_FORCEREC_H
+
+#include "gromacs/math/vec.h"
+#include "gromacs/timing/wallcycle.h"
+#include "gromacs/utility/arrayref.h"
+
+struct gmx_hw_info_t;
+struct t_commrec;
+struct t_forcerec;
+struct t_filenm;
+struct t_inputrec;
+struct gmx_localtop_t;
+struct gmx_mtop_t;
+struct gmx_wallcycle;
+struct interaction_const_t;
+union t_iparams;
+enum class LongRangeVdW : int;
+
+namespace gmx
+{
+class MDLogger;
+class PhysicalNodeCommunicator;
+class SimulationWorkload;
+} // namespace gmx
+
+/*! \brief Create nonbonded parameter lists
+ *
+ * \param[in] numAtomTypes           The number of atom types
+ * \param[in] iparams                The LJ parameters
+ * \param[in] useBuckinghamPotential Use Buckingham potential
+ */
+std::vector<real> makeNonBondedParameterLists(int                            numAtomTypes,
+                                              gmx::ArrayRef<const t_iparams> iparams,
+                                              bool useBuckinghamPotential);
+
+/*! \brief Calculate c6 parameters for grid correction
+ *
+ * \param[in] numAtomTypes           The number of atom types
+ * \param[in] iparams                The LJ parameters
+ * \param[in] ljpme_combination_rule How long range LJ is treated
+ */
+std::vector<real> makeLJPmeC6GridCorrectionParameters(int                            numAtomTypes,
+                                                      gmx::ArrayRef<const t_iparams> iparams,
+                                                      LongRangeVdW ljpme_combination_rule);
+
+/*! \brief Set the number of charge groups and atoms.
+ *
+ * The force calculation needs information on which atoms it
+ * should do work.
+ * \param[inout] fr                  The forcerec
+ * \param[in]    natoms_force        Number of atoms to compute force on
+ * \param[in]    natoms_force_constr Number of atoms involved in constraints
+ * \param[in]    natoms_f_novirsum   Number of atoms for which
+ *                                   force is to be compute but no virial
+ */
+void forcerec_set_ranges(t_forcerec* fr, int natoms_force, int natoms_force_constr, int natoms_f_novirsum);
+
+/*! \brief Initiate table constants
+ *
+ * Initializes the tables in the interaction constant data structure.
+ * \param[in] fp                     File for debugging output
+ * \param[in] ic                     Structure holding the table constant
+ * \param[in] rlist                  Length of the neighbour list
+ * \param[in] tableExtensionLength   Length by which to extend the tables. Taken from the input record.
+ */
+void init_interaction_const_tables(FILE* fp, interaction_const_t* ic, real rlist, real tableExtensionLength);
+
+/*! \brief Initialize forcerec structure.
+ *
+ * \param[in]  fplog              File for printing
+ * \param[in]  mdlog              File for printing
+ * \param[out] forcerec                 The forcerec
+ * \param[in]  simulationWork           Simulation workload flags
+ * \param[in]  inputrec                 Inputrec structure
+ * \param[in]  mtop               Molecular topology
+ * \param[in]  commrec                 Communication structures
+ * \param[in]  box                Simulation box
+ * \param[in]  tabfn              Table potential file for non-bonded interactions
+ * \param[in]  tabpfn             Table potential file for pair interactions
+ * \param[in]  tabbfnm            Table potential files for bonded interactions
+ * \param[in]  print_force        Print forces for atoms with force >= print_force
+ */
+void init_forcerec(FILE*                            fplog,
+                   const gmx::MDLogger&             mdlog,
+                   const gmx::SimulationWorkload&   simulationWork,
+                   t_forcerec*                      forcerec,
+                   const t_inputrec&                inputrec,
+                   const gmx_mtop_t&                mtop,
+                   const t_commrec*                 commrec,
+                   matrix                           box,
+                   const char*                      tabfn,
+                   const char*                      tabpfn,
+                   gmx::ArrayRef<const std::string> tabbfnm,
+                   real                             print_force);
+
+#endif
diff --git a/src/include/gromacs/mdlib/freeenergyparameters.h b/src/include/gromacs/mdlib/freeenergyparameters.h
new file mode 100644 (file)
index 0000000..aaa8307
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Handling of free energy parameters
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \ingroup module_mdlib
+ */
+
+#ifndef GMX_MDLIB_FREEENERGYPARAMETERS_H
+#define GMX_MDLIB_FREEENERGYPARAMETERS_H
+
+#include <array>
+
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/real.h"
+
+struct t_lambda;
+
+namespace gmx
+{
+
+/*! \brief Evaluate the current lambdas
+ *
+ * \param[in] step the current simulation step
+ * \param[in] fepvals describing the lambda setup
+ * \param[in] currentLambdaState the lambda state to use to set the lambdas, -1 if not set
+ * \returns the current lambda-value array
+ */
+gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, real> currentLambdas(int64_t         step,
+                                                                               const t_lambda& fepvals,
+                                                                               int currentLambdaState);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/gmx_omp_nthreads.h b/src/include/gromacs/mdlib/gmx_omp_nthreads.h
new file mode 100644 (file)
index 0000000..1b87c11
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_OMP_NTHREADS_H
+#define GMX_OMP_NTHREADS_H
+
+#include <stdio.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+struct t_commrec;
+
+namespace gmx
+{
+class MDLogger;
+}
+
+/** Enum values corresponding to multithreaded algorithmic modules. */
+enum class ModuleMultiThread : int
+{
+    /* Default is meant to be used in OMP regions outside the named
+     * algorithmic modules listed below. */
+    Default,
+    Domdec,
+    Pairsearch,
+    Nonbonded,
+    Bonded,
+    Pme,
+    Update,
+    VirtualSite,
+    Lincs,
+    Settle,
+    Count
+};
+
+/*! \brief
+ * Initializes the per-module thread count.
+ *
+ * It is compatible with tMPI, thread-safety is ensured (for the features
+ * available with tMPI).
+ * This function should caled only once during the initialization of mdrun. */
+void gmx_omp_nthreads_init(const gmx::MDLogger& fplog,
+                           t_commrec*           cr,
+                           int                  nthreads_hw_avail,
+                           int                  numRanksOnThisNode,
+                           int                  omp_nthreads_req,
+                           int                  omp_nthreads_pme_req,
+                           gmx_bool             bCurrNodePMEOnly);
+
+/*! \brief
+ * Returns the number of threads to be used in the given module \p mod. */
+int gmx_omp_nthreads_get(ModuleMultiThread mod);
+
+/*! \brief
+ * Returns the number of threads to be used in the given module \p mod for simple rvec operations.
+ *
+ * When the, potentially, parallel task only consists of a loop of clear_rvec
+ * or rvec_inc for nrvec elements, the OpenMP overhead might be higher than
+ * the reduction in computional cost due to parallelization. This routine
+ * returns 1 when the overhead is expected to be higher than the gain.
+ */
+static inline int gmx_omp_nthreads_get_simple_rvec_task(ModuleMultiThread mod, int nrvec)
+{
+    /* There can be a relatively large overhead to an OpenMP parallel for loop.
+     * This overhead increases, slowly, with the numbe of threads used.
+     * The computational gain goes as 1/#threads. The two effects combined
+     * lead to a cross-over point for a (non-)parallel loop at loop count
+     * that is not strongly dependent on the thread count.
+     * Note that a (non-)parallel loop can have benefit later in the code
+     * due to generating more cache hits, depending on how the next lask
+     * that accesses the same data is (not) parallelized over threads.
+     *
+     * A value of 2000 is the switch-over point for Haswell without
+     * hyper-threading. With hyper-threading it is about a factor 1.5 higher.
+     */
+    const int nrvec_omp = 2000;
+
+    if (nrvec < nrvec_omp)
+    {
+        return 1;
+    }
+    else
+    {
+        return gmx_omp_nthreads_get(mod);
+    }
+}
+
+/*! \brief Sets the number of threads to be used in module.
+ *
+ * Intended for use in testing. */
+void gmx_omp_nthreads_set(ModuleMultiThread mod, int nthreads);
+
+/*! \brief
+ * Read the OMP_NUM_THREADS env. var. and check against the value set on the
+ * command line. */
+void gmx_omp_nthreads_read_env(const gmx::MDLogger& mdlog, int* nthreads_omp);
+
+#endif
diff --git a/src/include/gromacs/mdlib/gpuforcereduction.h b/src/include/gromacs/mdlib/gpuforcereduction.h
new file mode 100644 (file)
index 0000000..922b8fe
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares the GPU Force Reduction
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ *
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_GPUFORCEREDUCTION_H
+#define GMX_MDLIB_GPUFORCEREDUCTION_H
+
+#include <memory>
+
+#include "config.h"
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/timing/wallcycle.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/fixedcapacityvector.h"
+
+class GpuEventSynchronizer;
+class DeviceStream;
+class DeviceContext;
+
+namespace gmx
+{
+
+#define HAVE_GPU_FORCE_REDUCTION (GMX_GPU_CUDA || GMX_GPU_SYCL)
+
+/*! \internal
+ * \brief Manages the force reduction directly in GPU memory
+ *
+ * Manages the reduction of multiple GPU force buffers into a single
+ * GPU force buffer. The reduction involves at least one (input/output)
+ * Rvec-format buffer and one (input) Nbat-format buffer, where the
+ * Nbat->Rvec conversion is handled internally. One additional (input)
+ * Rvec-format buffer is supported as optional.
+ */
+class GpuForceReduction
+{
+
+public:
+    /*! \brief Creates GPU force reduction object
+     *
+     * \param [in] deviceContext GPU device context
+     * \param [in] deviceStream  Stream to use for reduction
+     * \param [in] wcycle        Wall-clock cycle counter
+     */
+    GpuForceReduction(const DeviceContext& deviceContext,
+                      const DeviceStream&  deviceStream,
+                      gmx_wallcycle*       wcycle);
+    ~GpuForceReduction();
+
+    /*! \brief Register a nbnxm-format force to be reduced
+     *
+     * \param [in] forcePtr  Pointer to force to be reduced
+     */
+    void registerNbnxmForce(DeviceBuffer<RVec> forcePtr);
+
+    /*! \brief Register a rvec-format force to be reduced
+     *
+     * \param [in] forcePtr  Pointer to force to be reduced
+     */
+    void registerRvecForce(DeviceBuffer<RVec> forcePtr);
+
+    /*! \brief Add a dependency for this force reduction
+     *
+     * \param [in] dependency   Dependency for this reduction
+     */
+    void addDependency(GpuEventSynchronizer* dependency);
+
+    /*! \brief Reinitialize the GPU force reduction
+     *
+     * \param [in] baseForcePtr     Pointer to force to be used as a base
+     * \param [in] numAtoms         The number of atoms
+     * \param [in] cell             Pointer to the cell array
+     * \param [in] atomStart        The start atom for the reduction
+     * \param [in] accumulate       Whether reduction should be accumulated
+     * \param [in] completionMarker Event to be marked when launch of reduction is complete
+     */
+    void reinit(DeviceBuffer<RVec>    baseForcePtr,
+                int                   numAtoms,
+                ArrayRef<const int>   cell,
+                int                   atomStart,
+                bool                  accumulate,
+                GpuEventSynchronizer* completionMarker = nullptr);
+
+    /*! \brief Execute the force reduction */
+    void execute();
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/gpuforcereduction_impl.h b/src/include/gromacs/mdlib/gpuforcereduction_impl.h
new file mode 100644 (file)
index 0000000..f4b5d05
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares the GPU Force Reduction
+ *
+ * \author Alan Gray <alang@nvidia.com>
+ *
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_GPUFORCEREDUCTION_IMPL_H
+#define GMX_MDLIB_GPUFORCEREDUCTION_IMPL_H
+
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/math/vectypes.h"
+
+#include "gpuforcereduction.h"
+
+namespace gmx
+{
+
+//! \internal
+//! \brief structure to hold cell information for any nbat-format forces
+struct cellInfo
+{
+    //! cell index mapping for any nbat-format forces
+    const int* cell = nullptr;
+    //! device copy of cell index mapping for any nbat-format forces
+    DeviceBuffer<int> d_cell;
+    //! number of atoms in cell array
+    int cellSize = -1;
+    //! number of atoms allocated in cell array
+    int cellSizeAlloc = -1;
+};
+
+class GpuForceReduction::Impl
+{
+
+public:
+    /*! \brief Creates GPU force reduction object
+     *
+     * \param [in] deviceStream  Stream to use for reduction
+     * \param [in] deviceContext GPU device context
+     * \param [in] wcycle        The wallclock counter
+     */
+    Impl(const DeviceContext& deviceContext, const DeviceStream& deviceStream, gmx_wallcycle* wcycle);
+
+    ~Impl() = default;
+    /*! \brief Register a nbnxm-format force to be reduced
+     *
+     * \param [in] forcePtr  Pointer to force to be reduced
+     */
+    void registerNbnxmForce(DeviceBuffer<Float3> forcePtr);
+
+    /*! \brief Register a rvec-format force to be reduced
+     *
+     * \param [in] forcePtr  Pointer to force to be reduced
+     */
+    void registerRvecForce(DeviceBuffer<Float3> forcePtr);
+
+    /*! \brief Add a dependency for this force reduction
+     *
+     * \param [in] dependency   Dependency for this reduction
+     */
+    void addDependency(GpuEventSynchronizer* dependency);
+
+    /*! \brief Reinitialize the GPU force reduction
+     *
+     * \param [in] baseForcePtr     Pointer to force to be used as a base
+     * \param [in] numAtoms         The number of atoms
+     * \param [in] cell             Pointer to the cell array
+     * \param [in] atomStart        The start atom for the reduction
+     * \param [in] accumulate       Whether reduction should be accumulated
+     * \param [in] completionMarker Event to be marked when launch of reduction is complete
+     */
+    void reinit(DeviceBuffer<Float3>  baseForcePtr,
+                int                   numAtoms,
+                ArrayRef<const int>   cell,
+                int                   atomStart,
+                bool                  accumulate,
+                GpuEventSynchronizer* completionMarker = nullptr);
+
+    /*! \brief Execute the force reduction */
+    void execute();
+
+private:
+    //! force to be used as a base for this reduction
+    DeviceBuffer<Float3> baseForce_;
+    //! starting atom
+    int atomStart_ = 0;
+    //! number of atoms
+    int numAtoms_ = 0;
+    //! whether reduction is accumulated into base force buffer
+    bool accumulate_ = true;
+    //! cell information for any nbat-format forces
+    struct cellInfo cellInfo_;
+    //! GPU context object
+    const DeviceContext& deviceContext_;
+    //! list of dependencies
+    gmx::FixedCapacityVector<GpuEventSynchronizer*, 3> dependencyList_;
+    //! stream to be used for this reduction
+    const DeviceStream& deviceStream_;
+    //! Nbnxm force to be added in this reduction
+    DeviceBuffer<RVec> nbnxmForceToAdd_;
+    //! Rvec-format force to be added in this reduction
+    DeviceBuffer<RVec> rvecForceToAdd_;
+    //! event to be marked when reduction launch has been completed
+    GpuEventSynchronizer* completionMarker_ = nullptr;
+    //! The wallclock counter
+    gmx_wallcycle* wcycle_ = nullptr;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/gpuforcereduction_impl_internal.h b/src/include/gromacs/mdlib/gpuforcereduction_impl_internal.h
new file mode 100644 (file)
index 0000000..23b87a2
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares vendor-specific function to launch force reduction kernel
+ *
+ * \author Andrey Alekseenko <al42and@gmail.com>
+ *
+ * \ingroup module_mdlib
+ */
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gputraits.h"
+
+class DeviceStream;
+
+namespace gmx
+{
+
+/*! \brief Backend-specific function to launch GPU Force Reduction kernel.
+ *
+ * In pseudocode:
+ *
+ * \code{.cpp}
+ * for (int i = 0; i < numAtoms; i++) {
+ *     totalForce = d_nbnxmForceToAdd[d_cell[i]]
+ *     if (accumulate)
+ *         totalForce += d_baseForce[atomStart + i]
+ *     if (addRvecForce)
+ *         totalForce += d_rvecForceToAdd[atomStart + i]
+ *     d_baseForce[atomStart + i] = totalForce[i]
+ * }
+ * \endcode
+ *
+ * \param numAtoms Number of atoms subject to reduction.
+ * \param atomStart First atom index (for \p d_rvecForceToAdd and \p d_baseForce).
+ * \param addRvecForce When \c false, \p d_rvecForceToAdd is ignored.
+ * \param accumulate When \c false, the previous values of \p d_baseForce are discarded.
+ * \param d_nbnxmForceToAdd Buffer containing Nbnxm forces in Nbnxm layout.
+ * \param d_rvecForceToAdd Optional buffer containing arbitrary forces in linear layout.
+ * \param d_baseForce Destination buffer for forces in linear layout.
+ * \param d_cell Atom index to Nbnxm cell index.
+ * \param deviceStream Device stream for kernel submission.
+ */
+void launchForceReductionKernel(int                  numAtoms,
+                                int                  atomStart,
+                                bool                 addRvecForce,
+                                bool                 accumulate,
+                                DeviceBuffer<Float3> d_nbnxmForceToAdd,
+                                DeviceBuffer<Float3> d_rvecForceToAdd,
+                                DeviceBuffer<Float3> d_baseForce,
+                                DeviceBuffer<int>    d_cell,
+                                const DeviceStream&  deviceStream);
+
+} // namespace gmx
diff --git a/src/include/gromacs/mdlib/groupcoord.h b/src/include/gromacs/mdlib/groupcoord.h
new file mode 100644 (file)
index 0000000..36fecea
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2012,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Assemble atomic positions of a (small) subset of atoms and distribute to all nodes.
+ *
+ *  This file contains functions to assemble the positions of a subset of the
+ *  atoms and to do operations on it like determining the center of mass, or
+ *  doing translations and rotations. These functions are useful when
+ *  a subset of the positions needs to be compared to some set of reference
+ *  positions, as e.g. done for essential dynamics.
+ *
+ * \inlibraryapi
+ */
+
+#include <stdio.h>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+class gmx_ga2la_t;
+struct t_commrec;
+
+/*! \brief Select local atoms of a group.
+ *
+ * Selects the indices of local atoms of a group and stores them in anrs_loc[0..nr_loc].
+ * If you need the positions of the group's atoms on all nodes, provide a coll_ind[0..nr]
+ * array and pass it on to communicate_group_positions. Thus the collective array
+ * will always have the same atom order (ascending indices).
+ *
+ *  \param[in]     ga2la      Global to local atom index conversion data.
+ *  \param[in]     nr         The total number of atoms that the group contains.
+ *  \param[in]     anrs       The global atom number of the group's atoms.
+ *  \param[out]    nr_loc     The number of group atoms present on the local node.
+ *  \param[out]    anrs_loc   The local atom numbers of the group.
+ *  \param[in,out] nalloc_loc Local allocation size of anrs_loc array.
+ *  \param[out]    coll_ind   If not NULL this array must be of size nr. It stores
+ *                            for each local atom where it belongs in the global
+ *                            (collective) array such that it can be gmx_summed
+ *                            in the communicate_group_positions routine.
+ */
+
+extern void dd_make_local_group_indices(const gmx_ga2la_t* ga2la,
+                                        int                nr,
+                                        int                anrs[],
+                                        int*               nr_loc,
+                                        int*               anrs_loc[],
+                                        int*               nalloc_loc,
+                                        int                coll_ind[]);
+
+
+/*! \brief Assemble local positions into a collective array present on all nodes.
+ *
+ * Communicate the positions of the group's atoms such that every node has all of
+ * them. Unless running on huge number of cores, this is not a big performance impact
+ * as long as the collective subset [0..nr] is kept small. The atom indices are
+ * retrieved from anrs_loc[0..nr_loc]. If you call the routine for the serial case,
+ * provide an array coll_ind[i] = i for i in 1..nr.
+ *
+ * If shifts != NULL, the PBC representation of each atom is chosen such that a
+ * continuous trajectory results. Therefore, if the group is whole at the start
+ * of the simulation, it will always stay whole.
+ * If shifts = NULL, the group positions are not made whole again, but assembled
+ * and distributed to all nodes. The variables marked "optional" are not used in
+ * that case.
+ *
+ * \param[in]     cr           Pointer to MPI communication data.
+ * \param[out]    xcoll        Collective array of positions, identical on all nodes
+ *                             after this routine has been called.
+ * \param[in,out] shifts       Collective array of shifts for xcoll, needed to make
+ *                             the group whole. This array remembers the shifts
+ *                             since the start of the simulation (where the group
+ *                             is whole) and must therefore not be changed outside
+ *                             of this routine! If NULL, the group will not be made
+ *                             whole and the optional variables are ignored.
+ * \param[out]    extra_shifts Extra shifts since last time step, only needed as
+ *                             buffer variable [0..nr] (optional).
+ * \param[in]     bNS          Neighbor searching / domain re-decomposition has been
+ *                             performed at the begin of this time step such that
+ *                             the shifts have changed and need to be updated
+ *                             (optional).
+ * \param[in]     x_loc        Pointer to the local atom positions this node has.
+ * \param[in]     nr           Total number of atoms in the group.
+ * \param[in]     nr_loc       Number of group atoms on the local node.
+ * \param[in]     anrs_loc     Array of the local atom indices.
+ * \param[in]     coll_ind     This array of size nr stores for each local atom where
+ *                             it belongs in the collective array so that the local
+ *                             contributions can be gmx_summed. It is provided by
+ *                             dd_make_local_group_indices.
+ * \param[in,out] xcoll_old    Positions from the last time step, used to make the
+ *                             group whole (optional).
+ * \param[in]     box          Simulation box matrix, needed to shift xcoll such that
+ *                             the group becomes whole (optional).
+ */
+extern void communicate_group_positions(const t_commrec* cr,
+                                        rvec*            xcoll,
+                                        ivec*            shifts,
+                                        ivec*            extra_shifts,
+                                        gmx_bool         bNS,
+                                        const rvec*      x_loc,
+                                        int              nr,
+                                        int              nr_loc,
+                                        const int*       anrs_loc,
+                                        const int*       coll_ind,
+                                        rvec*            xcoll_old,
+                                        const matrix     box);
+
+/*! \brief Calculates the center of the positions x locally.
+ *
+ * Calculates the center of mass (if masses are given in the weight array) or
+ * the geometrical center (if NULL is passed as weight).
+ *
+ * \param[in]   x            Positions.
+ * \param[in]   weight       Can be NULL or an array of weights. If masses are
+ *                           given as weights, the COM is calculated.
+ * \param[in]   nr           Number of positions and weights if present.
+ * \param[out]  center       The (weighted) center of the positions.
+ *
+ */
+extern void get_center(rvec x[], real weight[], int nr, rvec center);
+
+
+/*! \brief Calculates the sum of the positions x locally.
+ *
+ * Calculates the (weighted) sum of position vectors and returns the sum of
+ * weights, which is needed when local contributions shall be summed to a
+ * global weighted center.
+ *
+ * \param[in]   x            Array of positions.
+ * \param[in]   weight       Can be NULL or an array of weights.
+ * \param[in]   nr           Number of positions and weights if present.
+ * \param[out]  dsumvec      The (weighted) sum of the positions.
+ * \return Sum of weights.
+ *
+ */
+extern double get_sum_of_positions(rvec x[], real weight[], int nr, dvec dsumvec);
+
+
+/*! \brief Calculates the global center of all local arrays x.
+ *
+ * Get the center from local positions [0..nr_loc], this involves communication.
+ * Not that the positions must already have the correct PBC representation. Use
+ * this routine if no collective coordinates are assembled from which the center
+ * could be calculated without communication.
+ *
+ * \param[in]   cr           Pointer to MPI communication data.
+ * \param[in]   x_loc        Array of local positions [0..nr_loc].
+ * \param[in]   weight_loc   Array of local weights, these are the masses if the
+ *                           center of mass is to be calculated.
+ * \param[in]   nr_loc       The number of positions on the local node.
+ * \param[in]   nr_group     The number of positions in the whole group. Since
+ *                           this is known anyway, we do not need to communicate
+ *                           and sum nr_loc if we pass it over.
+ * \param[out]  center       The (weighted) center of all x_loc from all the
+ *                           nodes.
+ */
+extern void get_center_comm(const t_commrec* cr, rvec x_loc[], real weight_loc[], int nr_loc, int nr_group, rvec center);
+
+
+/*! \brief Translate positions.
+ *
+ * Add a translation vector to the positions x.
+ *
+ * \param[in,out] x          Array of positions.
+ * \param[in]     nr         Number of entries in the position array.
+ * \param[in]     transvec   Translation vector to be added to all positions.
+ *
+ */
+extern void translate_x(rvec x[], int nr, const rvec transvec);
+
+
+/*! \brief Rotate positions.
+ *
+ * Rotate the positions with the rotation matrix.
+ *
+ * \param[in,out] x          Array of positions.
+ * \param[in]     nr         Number of entries in the position array.
+ * \param[in]     rmat       Rotation matrix to operate on all positions.
+ *
+ */
+extern void rotate_x(rvec x[], int nr, matrix rmat);
diff --git a/src/include/gromacs/mdlib/leapfrog_gpu.h b/src/include/gromacs/mdlib/leapfrog_gpu.h
new file mode 100644 (file)
index 0000000..757aa4a
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declarations for GPU implementation of Leap-Frog.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+#ifndef GMX_MDLIB_LEAPFROG_GPU_H
+#define GMX_MDLIB_LEAPFROG_GPU_H
+
+#include "config.h"
+
+#if GMX_GPU_CUDA
+#    include "gromacs/gpu_utils/gputraits.cuh"
+#endif
+#if GMX_GPU_SYCL
+#    include "gromacs/gpu_utils/gputraits_sycl.h"
+#endif
+
+#include <memory>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/utility/arrayref.h"
+
+class DeviceContext;
+class DeviceStream;
+struct t_grp_tcstat;
+
+namespace gmx
+{
+
+
+/*! \brief Sets the number of different temperature coupling values
+ *
+ *  This is needed to template the kernel
+ *  \todo Unify with similar enum in CPU update module
+ */
+enum class NumTempScaleValues
+{
+    None     = 0, //!< No temperature coupling
+    Single   = 1, //!< Single T-scaling value (one group)
+    Multiple = 2, //!< Multiple T-scaling values, need to use T-group indices
+    Count    = 3  //!< Number of valid values
+};
+
+/*! \brief Different variants of the Parrinello-Rahman velocity scaling
+ *
+ *  This is needed to template the kernel
+ *  \todo Unify with similar enum in CPU update module
+ */
+enum class VelocityScalingType
+{
+    None     = 0, //!< Do not apply velocity scaling (not a PR-coupling run or step)
+    Diagonal = 1, //!< Apply velocity scaling using a diagonal matrix
+    Count    = 2  //!< Number of valid values
+};
+
+class LeapFrogGpu
+{
+
+public:
+    /*! \brief Constructor.
+     *
+     * \param[in] deviceContext       Device context (dummy in CUDA).
+     * \param[in] deviceStream        Device stream to use.
+     * \param[in] numTempScaleValues  Number of temperature scale groups.
+     */
+    LeapFrogGpu(const DeviceContext& deviceContext, const DeviceStream& deviceStream, int numTempScaleValues);
+    ~LeapFrogGpu();
+
+    /*! \brief Integrate
+     *
+     * Integrates the equation of motion using Leap-Frog algorithm.
+     * Updates coordinates and velocities on the GPU. The current coordinates are saved for constraints.
+     *
+     * \param[in,out] d_x                      Coordinates to update
+     * \param[out]    d_xp                     Place to save the values of initial coordinates coordinates to.
+     * \param[in,out] d_v                      Velocities (will be updated).
+     * \param[in]     d_f                      Forces.
+     * \param[in]     dt                       Timestep.
+     * \param[in]     doTemperatureScaling     If velocities should be scaled for temperature coupling.
+     * \param[in]     tcstat                   Temperature coupling data.
+     * \param[in]     doParrinelloRahman       If current step is a Parrinello-Rahman pressure coupling step.
+     * \param[in]     dtPressureCouple         Period between pressure coupling steps
+     * \param[in]     prVelocityScalingMatrix  Parrinello-Rahman velocity scaling matrix
+     */
+    void integrate(DeviceBuffer<Float3>              d_x,
+                   DeviceBuffer<Float3>              d_xp,
+                   DeviceBuffer<Float3>              d_v,
+                   DeviceBuffer<Float3>              d_f,
+                   float                             dt,
+                   bool                              doTemperatureScaling,
+                   gmx::ArrayRef<const t_grp_tcstat> tcstat,
+                   bool                              doParrinelloRahman,
+                   float                             dtPressureCouple,
+                   const matrix                      prVelocityScalingMatrix);
+
+    /*! \brief Set the integrator
+     *
+     * Allocates memory for inverse masses, and, if needed for temperature scaling factor(s)
+     * and temperature coupling groups. Copies inverse masses and temperature coupling groups
+     * to the GPU.
+     *
+     * \param[in] numAtoms        Number of atoms in the system.
+     * \param[in] inverseMasses   Inverse masses of atoms.
+     * \param[in] tempScaleGroups Maps the atom index to temperature scale value.
+     */
+    void set(int numAtoms, const real* inverseMasses, const unsigned short* tempScaleGroups);
+
+    /*! \brief Class with hardware-specific interfaces and implementations.*/
+    class Impl;
+
+private:
+    //! GPU context object
+    const DeviceContext& deviceContext_;
+    //! GPU stream
+    const DeviceStream& deviceStream_;
+
+    //! Number of atoms
+    int numAtoms_;
+
+    //! 1/mass for all atoms (GPU)
+    DeviceBuffer<float> d_inverseMasses_;
+    //! Current size of the reciprocal masses array
+    int numInverseMasses_ = -1;
+    //! Maximum size of the reciprocal masses array
+    int numInverseMassesAlloc_ = -1;
+
+    //! Number of temperature coupling groups (zero = no coupling)
+    int numTempScaleValues_ = 0;
+    /*! \brief Array with temperature scaling factors.
+     * This is temporary solution to remap data from t_grp_tcstat into plain array.
+     * \todo Replace with better solution.
+     */
+    gmx::HostVector<float> h_lambdas_;
+    //! Device-side temperature scaling factors
+    DeviceBuffer<float> d_lambdas_;
+    //! Current size of the array with temperature scaling factors (lambdas)
+    int numLambdas_ = -1;
+    //! Maximum size of the array with temperature scaling factors (lambdas)
+    int numLambdasAlloc_ = -1;
+
+
+    //! Array that maps atom index onto the temperature scaling group to get scaling parameter
+    DeviceBuffer<unsigned short> d_tempScaleGroups_;
+    //! Current size of the temperature coupling groups array
+    int numTempScaleGroups_ = -1;
+    //! Maximum size of the temperature coupling groups array
+    int numTempScaleGroupsAlloc_ = -1;
+
+    //! Vector with diagonal elements of the Parrinello-Rahman pressure coupling velocity rescale factors
+    Float3 prVelocityScalingMatrixDiagonal_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/leapfrog_gpu_internal.h b/src/include/gromacs/mdlib/leapfrog_gpu_internal.h
new file mode 100644 (file)
index 0000000..44c9ca6
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declarations for backend specific GPU functions for Leap-Frog.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+#ifndef GMX_MDLIB_LEAPFROG_GPU_INTERNAL_H
+#define GMX_MDLIB_LEAPFROG_GPU_INTERNAL_H
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/mdlib/leapfrog_gpu.h"
+
+namespace gmx
+{
+
+/*! \brief Backend-specific function to launch GPU Leap Frog kernel.
+ *
+ * \param numAtoms Total number of atoms.
+ * \param[in,out] d_x Buffer containing initial coordinates, and where the updated ones will be written.
+ * \param[out] d_xp Buffer where a copy of the initial coordinates will be written.
+ * \param[in,out] d_v Buffer containing initial velocities, and where the updated ones will be written.
+ * \param[in]  d_f Buffer containing forces.
+ * \param[in] d_inverseMasses Buffer containing atoms' reciprocal masses.
+ * \param dt Timestep.
+ * \param doTemperatureScaling Whether temperature scaling is needed.
+ * \param numTempScaleValues Number of different T-couple values.
+ * \param d_tempScaleGroups Mapping of atoms into temperature scaling groups.
+ * \param d_lambdas Temperature scaling factors (one per group).
+ * \param prVelocityScalingType Type of Parrinello-Rahman velocity rescaling.
+ * \param prVelocityScalingMatrixDiagonal Diagonal elements of Parrinello-Rahman velocity scaling matrix.
+ * \param deviceStream Device stream for kernel launch.
+ */
+void launchLeapFrogKernel(int                          numAtoms,
+                          DeviceBuffer<Float3>         d_x,
+                          DeviceBuffer<Float3>         d_xp,
+                          DeviceBuffer<Float3>         d_v,
+                          DeviceBuffer<Float3>         d_f,
+                          DeviceBuffer<float>          d_inverseMasses,
+                          float                        dt,
+                          bool                         doTemperatureScaling,
+                          int                          numTempScaleValues,
+                          DeviceBuffer<unsigned short> d_tempScaleGroups,
+                          DeviceBuffer<float>          d_lambdas,
+                          VelocityScalingType          prVelocityScalingType,
+                          Float3                       prVelocityScalingMatrixDiagonal,
+                          const DeviceStream&          deviceStream);
+
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/lincs.h b/src/include/gromacs/mdlib/lincs.h
new file mode 100644 (file)
index 0000000..d588a68
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares interface to LINCS code.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+
+#ifndef GMX_MDLIB_LINCS_H
+#define GMX_MDLIB_LINCS_H
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_mtop_t;
+struct gmx_multisim_t;
+class InteractionDefinitions;
+struct t_commrec;
+struct t_inputrec;
+struct t_nrnb;
+struct t_pbc;
+
+namespace gmx
+{
+template<typename>
+class ArrayRefWithPadding;
+template<typename>
+class ArrayRef;
+enum class ConstraintVariable : int;
+class Lincs;
+template<typename>
+class ListOfLists;
+class ObservablesReducerBuilder;
+
+/*! \brief Return the data for determining constraint RMS relative deviations. */
+ArrayRef<real> lincs_rmsdData(Lincs* lincsd);
+
+/*! \brief Return the RMSD of the constraint. */
+real lincs_rmsd(const Lincs* lincsd);
+
+/*! \brief Initializes and returns the lincs data struct. */
+Lincs* init_lincs(FILE*                            fplog,
+                  const gmx_mtop_t&                mtop,
+                  int                              nflexcon_global,
+                  ArrayRef<const ListOfLists<int>> atomsToConstraintsPerMolType,
+                  bool                             bPLINCS,
+                  int                              nIter,
+                  int                              nProjOrder,
+                  ObservablesReducerBuilder*       observablesReducerBuilder);
+
+/*! \brief Destructs the lincs object when it is not nullptr. */
+void done_lincs(Lincs* li);
+
+/*! \brief Initialize lincs stuff */
+void set_lincs(const InteractionDefinitions& idef,
+               int                           numAtoms,
+               ArrayRef<const real>          invmass,
+               real                          lambda,
+               bool                          bDynamics,
+               const t_commrec*              cr,
+               Lincs*                        li);
+
+/*! \brief Applies LINCS constraints.
+ *
+ * \returns true if the constraining succeeded. */
+bool constrain_lincs(bool                            computeRmsd,
+                     const t_inputrec&               ir,
+                     int64_t                         step,
+                     Lincs*                          lincsd,
+                     ArrayRef<const real>            invmass,
+                     const t_commrec*                cr,
+                     const gmx_multisim_t*           ms,
+                     ArrayRefWithPadding<const RVec> x,
+                     ArrayRefWithPadding<RVec>       xprime,
+                     ArrayRef<RVec>                  min_proj,
+                     const matrix                    box,
+                     t_pbc*                          pbc,
+                     bool                            hasMassPerturbed,
+                     real                            lambda,
+                     real*                           dvdlambda,
+                     real                            invdt,
+                     ArrayRef<RVec>                  v,
+                     bool                            bCalcVir,
+                     tensor                          vir_r_m_dr,
+                     ConstraintVariable              econq,
+                     t_nrnb*                         nrnb,
+                     int                             maxwarn,
+                     int*                            warncount);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/lincs_gpu.h b/src/include/gromacs/mdlib/lincs_gpu.h
new file mode 100644 (file)
index 0000000..febb72a
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declares the class for GPU implementation of LINCS.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+#ifndef GMX_MDLIB_LINCS_GPU_CUH
+#define GMX_MDLIB_LINCS_GPU_CUH
+
+#include <memory>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/mdlib/constr.h"
+#include "gromacs/pbcutil/pbc_aiuc.h"
+
+class InteractionDefinitions;
+
+namespace gmx
+{
+
+//! A pair of atoms indexes
+struct AtomPair
+{
+    //! First atom
+    int i;
+    //! Second atom
+    int j;
+};
+
+/* \brief LINCS parameters and GPU pointers
+ *
+ * This is used to accumulate all the parameters and pointers so they can be passed
+ * to the GPU as a single structure.
+ *
+ */
+struct LincsGpuKernelParameters
+{
+    //! Periodic boundary data
+    PbcAiuc pbcAiuc;
+    //! Order of expansion when inverting the matrix
+    int expansionOrder;
+    //! Number of iterations used to correct the projection
+    int numIterations;
+    //! 1/mass for all atoms (GPU)
+    DeviceBuffer<float> d_inverseMasses;
+    //! Scaled virial tensor (6 floats: [XX, XY, XZ, YY, YZ, ZZ], GPU)
+    DeviceBuffer<float> d_virialScaled;
+    /*! \brief Total number of threads.
+     *
+     *  This covers all constraints and gaps in the ends of the thread blocks
+     *  that are necessary to avoid inter-block synchronizations.
+     *  Should be a multiple of block size (the last block is filled with dummy to the end).
+     */
+    int numConstraintsThreads;
+    //! List of constrained atoms (GPU memory)
+    DeviceBuffer<AtomPair> d_constraints;
+    //! Equilibrium distances for the constraints (GPU)
+    DeviceBuffer<float> d_constraintsTargetLengths;
+    /*! \brief Whether there are coupled constraints.
+     *
+     * In SYCL, the accessors can not be initialized with an empty buffer.
+     * In case there are no coupled constraints, the respective buffers below are
+     * empty. So we need to inform the kernel launcher that these are optional.
+     */
+    bool haveCoupledConstraints = false;
+    //! Number of constraints, coupled with the current one (GPU)
+    DeviceBuffer<int> d_coupledConstraintsCounts;
+    //! List of coupled with the current one (GPU)
+    DeviceBuffer<int> d_coupledConstraintsIndices;
+    //! Elements of the coupling matrix.
+    DeviceBuffer<float> d_matrixA;
+    //! Mass factors (GPU)
+    DeviceBuffer<float> d_massFactors;
+};
+
+/*! \internal \brief Class with interfaces and data for GPU version of LINCS. */
+class LincsGpu
+{
+
+public:
+    /*! \brief Constructor.
+     *
+     * \param[in] numIterations    Number of iteration for the correction of the projection.
+     * \param[in] expansionOrder   Order of the matrix inversion algorithm.
+     * \param[in] deviceContext    Device context (dummy in CUDA).
+     * \param[in] deviceStream     Device command stream.
+     */
+    LincsGpu(int                  numIterations,
+             int                  expansionOrder,
+             const DeviceContext& deviceContext,
+             const DeviceStream&  deviceStream);
+    /*! \brief Destructor.*/
+    ~LincsGpu();
+
+    /*! \brief Apply LINCS.
+     *
+     * Applies LINCS to coordinates and velocities, stored on GPU.
+     * The results are not automatically copied back to the CPU memory.
+     * Method uses this class data structures which should be updated
+     * when needed using set() method.
+     *
+     * \param[in]     d_x               Coordinates before timestep (in GPU memory)
+     * \param[in,out] d_xp              Coordinates after timestep (in GPU memory). The
+     *                                  resulting constrained coordinates will be saved here.
+     * \param[in]     updateVelocities  If the velocities should be updated.
+     * \param[in,out] d_v               Velocities to update (in GPU memory, can be nullptr
+     *                                  if not updated)
+     * \param[in]     invdt             Reciprocal timestep (to scale Lagrange
+     *                                  multipliers when velocities are updated)
+     * \param[in]     computeVirial     If virial should be updated.
+     * \param[in,out] virialScaled      Scaled virial tensor to be updated.
+     * \param[in]     pbcAiuc           PBC data.
+     */
+    void apply(const DeviceBuffer<Float3>& d_x,
+               DeviceBuffer<Float3>        d_xp,
+               bool                        updateVelocities,
+               DeviceBuffer<Float3>        d_v,
+               real                        invdt,
+               bool                        computeVirial,
+               tensor                      virialScaled,
+               const PbcAiuc&              pbcAiuc);
+
+    /*! \brief
+     * Update data-structures (e.g. after NB search step).
+     *
+     * Updates the constraints data and copies it to the GPU. Should be
+     * called if the particles were sorted, redistributed between domains, etc.
+     * This version uses common data formats so it can be called from anywhere
+     * in the code. Does not recycle the data preparation routines from the CPU
+     * version. Works only with simple case when all the constraints in idef are
+     * are handled by a single GPU. Triangles are not handled as special case.
+     *
+     * Information about constraints is taken from:
+     *     idef.il[F_CONSTR].iatoms  --- type (T) of constraint and two atom indexes (i1, i2)
+     *     idef.iparams[T].constr.dA --- target length for constraint of type T
+     *
+     * \param[in] idef      Local topology data to get information on constraints from.
+     * \param[in] numAtoms  Number of atoms.
+     * \param[in] invmass   Inverse masses of atoms.
+     */
+    void set(const InteractionDefinitions& idef, int numAtoms, const real* invmass);
+
+    /*! \brief
+     * Returns whether the maximum number of coupled constraints is supported
+     * by the GPU LINCS code.
+     *
+     * \param[in] mtop The molecular topology
+     */
+    static bool isNumCoupledConstraintsSupported(const gmx_mtop_t& mtop);
+
+private:
+    //! GPU context object
+    const DeviceContext& deviceContext_;
+    //! GPU stream
+    const DeviceStream& deviceStream_;
+
+    //! Parameters and pointers, passed to the GPU kernel
+    LincsGpuKernelParameters kernelParams_;
+
+    //! Scaled virial tensor (6 floats: [XX, XY, XZ, YY, YZ, ZZ])
+    std::vector<float> h_virialScaled_;
+
+    /*! \brief Maximum total number of constraints so far.
+     *
+     * If the new number of constraints is larger then previous maximum, the GPU data arrays are
+     * reallocated.
+     */
+    int numConstraintsThreadsAlloc_;
+
+    /*! \brief Maximum total number of atoms so far.
+     *
+     * If the new number of atoms is larger then previous maximum, the GPU array with masses is
+     * reallocated.
+     */
+    int numAtomsAlloc_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDLIB_LINCS_GPU_CUH
diff --git a/src/include/gromacs/mdlib/lincs_gpu_internal.h b/src/include/gromacs/mdlib/lincs_gpu_internal.h
new file mode 100644 (file)
index 0000000..1dc41f7
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declare backend-specific LINCS GPU functions
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ * \author Alan Gray <alang@nvidia.com>
+ *
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_LINCS_GPU_INTERNAL_H
+#define GMX_MDLIB_LINCS_GPU_INTERNAL_H
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gputraits.h"
+
+class DeviceStream;
+
+namespace gmx
+{
+
+struct LincsGpuKernelParameters;
+
+//! Number of threads in a GPU block
+constexpr static int c_threadsPerBlock = 256;
+
+/*! \brief Backend-specific function to launch LINCS kernel.
+ *
+ * \param kernelParams LINCS parameters.
+ * \param d_x Initial coordinates before the integration.
+ * \param d_xp Coordinates after the integration which will be updated.
+ * \param updateVelocities Whether to also update velocities.
+ * \param d_v Velocities to update (ignored if \p updateVelocities is \c false).
+ * \param invdt Reciprocal of timestep.
+ * \param computeVirial Whether to compute the virial.
+ * \param deviceStream Device stream for kernel launch.
+ */
+void launchLincsGpuKernel(LincsGpuKernelParameters*   kernelParams,
+                          const DeviceBuffer<Float3>& d_x,
+                          DeviceBuffer<Float3>        d_xp,
+                          bool                        updateVelocities,
+                          DeviceBuffer<Float3>        d_v,
+                          real                        invdt,
+                          bool                        computeVirial,
+                          const DeviceStream&         deviceStream);
+
+} // namespace gmx
+
+#endif // GMX_MDLIB_LINCS_GPU_INTERNAL_H
diff --git a/src/include/gromacs/mdlib/makeconstraints.h b/src/include/gromacs/mdlib/makeconstraints.h
new file mode 100644 (file)
index 0000000..0b6143a
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares and implements factory function for Constraints.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+
+#ifndef GMX_MDLIB_MAKECONSTRAINTS_H
+#define GMX_MDLIB_MAKECONSTRAINTS_H
+
+#include <memory>
+
+#include "gromacs/mdlib/constr.h"
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/pulling/pull.h"
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/topology/mtop_util.h"
+
+struct gmx_mtop_t;
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief Support type to help implement makeConstraints().
+ *
+ * This member type of Constraints also inherits from it, so that it
+ * can access the private constructor of Constraints to support the
+ * implementation of the factory function. This approach avoids having
+ * to declare makeConstraints() as a template friend function. */
+struct Constraints::CreationHelper : public Constraints
+{
+public:
+    /*! \brief Constructor that can call the private constructor
+     * of Constraints.
+     *
+     * The parameter pack insulates this helper type from changes
+     * to the arguments to the constructor. */
+    template<typename... Args>
+    CreationHelper(Args&&... args) : Constraints(std::forward<Args>(args)...)
+    {
+    }
+};
+
+/*! \brief Factory function for Constraints.
+ *
+ * We only want an object to manage computing constraints when the
+ * simulation requires one. Checking for whether the object was made
+ * adds overhead to simulations that use constraints, while avoiding
+ * overhead on those that do not, so is a design trade-off we might
+ * reconsider some time.
+ *
+ * Using a private constructor and a factory function ensures that we
+ * can only make a Constraints object when the prerequisites are
+ * satisfied, ie. that something needs them and if necessary has
+ * already been initialized.
+ *
+ * Using the parameter pack insulates the factory function from
+ * changes to the type signature of the constructor that don't
+ * affect the logic here. */
+template<typename... Args>
+std::unique_ptr<Constraints> makeConstraints(const gmx_mtop_t& mtop,
+                                             const t_inputrec& ir,
+                                             pull_t*           pull_work,
+                                             bool              doEssentialDynamics,
+                                             Args&&... args)
+{
+    int numConstraints = (gmx_mtop_ftype_count(mtop, F_CONSTR) + gmx_mtop_ftype_count(mtop, F_CONSTRNC));
+    int numSettles = gmx_mtop_ftype_count(mtop, F_SETTLE);
+    GMX_RELEASE_ASSERT(!ir.bPull || pull_work != nullptr,
+                       "When COM pulling is active, it must be initialized before constraints are "
+                       "initialized");
+    bool doPullingWithConstraints = ir.bPull && pull_have_constraint(*pull_work);
+    if (numConstraints + numSettles == 0 && !doPullingWithConstraints && !doEssentialDynamics)
+    {
+        // No work, so don't make a Constraints object.
+        return nullptr;
+    }
+    return std::make_unique<Constraints::CreationHelper>(
+            mtop, ir, pull_work, std::forward<Args>(args)..., numConstraints, numSettles);
+}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/md_support.h b/src/include/gromacs/mdlib/md_support.h
new file mode 100644 (file)
index 0000000..99e4385
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_MD_SUPPORT_H
+#define GMX_MDLIB_MD_SUPPORT_H
+
+#include <cstdint>
+
+#include "gromacs/mdlib/vcm.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/timing/wallcycle.h"
+
+class gmx_ekindata_t;
+struct gmx_enerdata_t;
+struct gmx_global_stat;
+struct gmx_signalling_t;
+struct t_extmass;
+struct t_forcerec;
+struct t_grpopts;
+struct t_inputrec;
+struct t_nrnb;
+class t_state;
+struct t_trxframe;
+
+namespace gmx
+{
+template<typename T>
+class ArrayRef;
+class MDLogger;
+class ObservablesReducer;
+class SimulationSignaller;
+} // namespace gmx
+
+/* Define a number of flags to better control the information
+ * passed to compute_globals in md.c and global_stat.
+ */
+
+/* we are computing the kinetic energy from average velocities */
+#define CGLO_EKINAVEVEL (1u << 2u)
+/* we are removing the center of mass momenta */
+#define CGLO_STOPCM (1u << 3u)
+/* bGStat is defined in do_md */
+#define CGLO_GSTAT (1u << 4u)
+/* Sum the energy terms in global computation */
+#define CGLO_ENERGY (1u << 6u)
+/* Sum the kinetic energy terms in global computation */
+#define CGLO_TEMPERATURE (1u << 7u)
+/* Sum the kinetic energy terms in global computation */
+#define CGLO_PRESSURE (1u << 8u)
+/* Sum the constraint term in global computation */
+#define CGLO_CONSTRAINT (1u << 9u)
+/* Reading ekin from the trajectory */
+#define CGLO_READEKIN (1u << 10u)
+/* we need to reset the ekin rescaling factor here */
+#define CGLO_SCALEEKIN (1u << 11u)
+
+/*! \brief Return the number of steps that will take place between
+ * intra-simulation communications, given the constraints of the
+ * inputrec. */
+int computeGlobalCommunicationPeriod(const t_inputrec* ir);
+
+/*! \brief Return the number of steps that will take place between
+ * intra-simulation communications, given the constraints of the
+ * inputrec, and write information to log.
+ * Calls computeGlobalCommunicationPeriod(ir) internally. */
+int computeGlobalCommunicationPeriod(const gmx::MDLogger& mdlog, const t_inputrec* ir, const t_commrec* cr);
+
+void rerun_parallel_comm(t_commrec* cr, t_trxframe* fr, gmx_bool* bLastStep);
+
+//! \brief Allocate and initialize node-local state entries
+void set_state_entries(t_state* state, const t_inputrec* ir, bool useModularSimulator);
+
+/* Compute global variables during integration
+ *
+ * Coordinates x are needed for kinetic energy calculation with cosine accelation
+ * and for COM removal with rotational and acceleration correction modes.
+ * Velocities v are needed for kinetic energy calculation and for COM removal.
+ */
+void compute_globals(gmx_global_stat*               gstat,
+                     t_commrec*                     cr,
+                     const t_inputrec*              ir,
+                     t_forcerec*                    fr,
+                     gmx_ekindata_t*                ekind,
+                     gmx::ArrayRef<const gmx::RVec> x,
+                     gmx::ArrayRef<const gmx::RVec> v,
+                     const matrix                   box,
+                     const t_mdatoms*               mdatoms,
+                     t_nrnb*                        nrnb,
+                     t_vcm*                         vcm,
+                     gmx_wallcycle*                 wcycle,
+                     gmx_enerdata_t*                enerd,
+                     tensor                         force_vir,
+                     tensor                         shake_vir,
+                     tensor                         total_vir,
+                     tensor                         pres,
+                     gmx::SimulationSignaller*      signalCoordinator,
+                     const matrix                   lastbox,
+                     gmx_bool*                      bSumEkinhOld,
+                     int                            flags,
+                     int64_t                        step,
+                     gmx::ObservablesReducer*       observablesReducer);
+
+#endif
diff --git a/src/include/gromacs/mdlib/mdatoms.h b/src/include/gromacs/mdlib/mdatoms.h
new file mode 100644 (file)
index 0000000..31af77a
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_MDATOMS_H
+#define GMX_MDLIB_MDATOMS_H
+
+#include <cstdio>
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/utility/real.h"
+#include "gromacs/utility/unique_cptr.h"
+
+struct gmx_mtop_t;
+struct t_inputrec;
+struct t_mdatoms;
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief Contains a C-style t_mdatoms while managing some of its
+ * memory with C++ vectors with allocators.
+ *
+ * \todo The group-scheme kernels needed a plain C-style t_mdatoms, so
+ * this type combines that with the memory management needed for
+ * efficient PME on GPU transfers. The mdatoms_ member should be
+ * removed. */
+class MDAtoms
+{
+    //! C-style mdatoms struct.
+    unique_cptr<t_mdatoms> mdatoms_;
+    //! Memory for chargeA that can be set up for efficient GPU transfer.
+    gmx::PaddedHostVector<real> chargeA_;
+    //! Memory for chargeB that can be set up for efficient GPU transfer.
+    gmx::PaddedHostVector<real> chargeB_;
+
+public:
+    // TODO make this private
+    MDAtoms();
+    ~MDAtoms();
+    //! Getter.
+    t_mdatoms* mdatoms() { return mdatoms_.get(); }
+    //! Const getter.
+    const t_mdatoms* mdatoms() const { return mdatoms_.get(); }
+    /*! \brief Resizes memory for charges of FEP state A.
+     *
+     * \throws std::bad_alloc  If out of memory.
+     */
+    void resizeChargeA(int newSize);
+    /*! \brief Resizes memory for charges of FEP state B.
+     *
+     * \throws std::bad_alloc  If out of memory.
+     */
+    void resizeChargeB(int newSize);
+    /*! \brief Reserves memory for charges of FEP state A.
+     *
+     * \throws std::bad_alloc  If out of memory.
+     */
+    void reserveChargeA(int newCapacity);
+    /*! \brief Reserves memory for charges of FEP state B.
+     *
+     * \throws std::bad_alloc  If out of memory.
+     */
+    void reserveChargeB(int newCapacity);
+    //! Builder function.
+    friend std::unique_ptr<MDAtoms>
+    makeMDAtoms(FILE* fp, const gmx_mtop_t& mtop, const t_inputrec& ir, bool rankHasPmeGpuTask);
+};
+
+//! Builder function for MdAtomsWrapper.
+std::unique_ptr<MDAtoms> makeMDAtoms(FILE* fp, const gmx_mtop_t& mtop, const t_inputrec& ir, bool useGpuForPme);
+
+} // namespace gmx
+
+/*! \brief This routine copies the atoms->atom struct into md.
+ *
+ * \param[in]    mtop     The molecular topology.
+ * \param[in]    inputrec The input record.
+ * \param[in]    nindex   If nindex>=0 we are doing DD.
+ * \param[in]    index    Lookup table for global atom index.
+ * \param[in]    homenr   Number of atoms on this processor.
+ * \param[inout] mdAtoms  Data set up by this routine.
+ *
+ * If index!=NULL only the indexed atoms are copied.
+ * For the masses the A-state (lambda=0) mass is used.
+ * Sets md->lambda = 0.
+ * In free-energy runs, update_mdatoms() should be called after atoms2md()
+ * to set the masses corresponding to the value of lambda at each step.
+ */
+void atoms2md(const gmx_mtop_t&  mtop,
+              const t_inputrec&  inputrec,
+              int                nindex,
+              gmx::ArrayRef<int> index,
+              int                homenr,
+              gmx::MDAtoms*      mdAtoms);
+
+void update_mdatoms(t_mdatoms* md, real lambda);
+/* When necessary, sets all the mass parameters to values corresponding
+ * to the free-energy parameter lambda.
+ * Sets md->lambda = lambda.
+ */
+
+#endif
diff --git a/src/include/gromacs/mdlib/mdebin_bar.h b/src/include/gromacs/mdlib/mdebin_bar.h
new file mode 100644 (file)
index 0000000..5cb6c69
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_MDLIB_MDEBIN_BAR_H
+#define GMX_MDLIB_MDEBIN_BAR_H
+
+#include "gromacs/utility/real.h"
+
+/* The functions & data structures here describe writing
+   energy differences (or their histogram )for use with g_bar */
+
+class delta_h_history_t;
+struct t_enxframe;
+class energyhistory_t;
+struct t_inputrec;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/* Data for one foreign lambda, or derivative. */
+typedef struct
+{
+    std::vector<real>  dh;     /* the raw energy data. */
+    std::vector<float> dhf;    /* raw difference data -- in floats, for storage. */
+    unsigned int       ndh;    /* number of data points */
+    unsigned int       ndhmax; /* the maximum number of points */
+
+    int nhist;                           /* the number of histograms. There can either be
+                                            0 (for no histograms)
+                                            1 (for 'foreign lambda' histograms)
+                                            2 (for derivative histograms: there's
+                                               a 'forward' and 'backward' histogram
+                                               containing the minimum and maximum
+                                               values, respectively). */
+    std::array<std::vector<int>, 2> bin; /* the histogram(s) */
+    double                          dx;  /* the histogram spacing in kJ/mol. This is the
+                                            same for the two histograms? */
+    unsigned int           nbins;        /* the number of bins in the histograms*/
+    std::array<int64_t, 2> x0;           /* the starting point in units of spacing
+                                        of the histogram */
+    std::array<unsigned int, 2> maxbin;  /* highest bin number with data */
+
+    int type;                    /* the block type according to dhbtDH, etc. */
+    int derivative;              /* The derivative direction (as an index in the lambda
+                                    vector) if this delta_h contains derivatives */
+    std::vector<double> lambda;  /* lambda vector (or NULL if not applicable) */
+    int                 nlambda; /* length of the lambda vector */
+    bool                written; /* whether this data has already been written out */
+
+    std::array<int64_t, 5> subblock_meta_l; /* metadata for an mdebin subblock for
+                                           I/O: for histogram counts, etc.*/
+    std::vector<double> subblock_meta_d;    /* metadata subblock for I/O, used for
+                                   communicating doubles (i.e. the lambda
+                                   vector) */
+    std::array<int, 4> subblock_meta_i;     /* metadata subblock for I/O, used for
+                                   communicating ints (i.e. derivative indices,
+                                   etc.) */
+} t_mde_delta_h;
+
+/* the type definition is in mdebin_bar.h */
+struct t_mde_delta_h_coll
+{
+    t_mde_delta_h_coll(const t_inputrec& inputrec);
+
+    std::vector<t_mde_delta_h> dh;  /* the delta h data */
+    int                        ndh; /* the number of delta_h structures */
+
+    int nlambda;     /* number of bar dU delta_h structures */
+    int dh_du_index; /* the delta h data (index into dh) */
+
+    int ndhdl;         /* number of bar dU delta_h structures */
+    int dh_dhdl_index; /* the dhdl data (index into dh) */
+
+    int dh_energy_index;   /* energy output block (index into dh) */
+    int dh_pv_index;       /* pV output block (index into dh) */
+    int dh_expanded_index; /* expanded ensemble output block (index into dh) */
+
+    double start_time;     /* start time of the current dh collection */
+    double delta_time;     /* time difference between samples */
+    bool   start_time_set; /* whether the start time has been set */
+    double start_lambda;   /* starting lambda for continuous motion of state*/
+    double delta_lambda;   /* delta lambda, for continuous motion of state */
+    double temperature;    /* the temperature of the samples*/
+
+    std::vector<double> native_lambda_vec;     /* The lambda vector describing the current
+                                      lambda state if it is set (NULL otherwise) */
+    int              n_lambda_vec;             /* the size of the native lambda vector */
+    std::vector<int> native_lambda_components; /* the native lambda (and by extension,
+                                      foreign lambda) components in terms
+                                      of efptFEP, efptMASS, etc. */
+    int lambda_index;                          /* the lambda_fep_state */
+
+    std::vector<double> subblock_d; /* for writing a metadata mdebin subblock for I/O */
+    std::vector<int>    subblock_i; /* for writing a metadata mdebin subblock for I/O */
+};
+
+/* add a bunch of samples to the delta_h collection
+    dhc = the collection
+    dhdl = the hamiltonian derivatives
+    U = the array with energies: from enerd->enerpart_lambda.
+    time = the current simulation time.
+    current_lambda = current lambda values : primarily useful for continuous processes
+    fep_state = current fep_state
+ */
+
+/* add a bunch of samples - note fep_state is double to allow for better data storage */
+void mde_delta_h_coll_add_dh(t_mde_delta_h_coll*   dhc,
+                             double                fep_state,
+                             double                energy,
+                             double                pV,
+                             gmx::ArrayRef<double> dhdl,
+                             double*               foreign_dU,
+                             double                time);
+
+/* write the data associated with the du blocks collection as a collection
+    of mdebin blocks.
+    dhc = the collection
+    fr = the enxio frame
+    nblock = the current number of blocks */
+void mde_delta_h_coll_handle_block(t_mde_delta_h_coll* dhc, t_enxframe* fr, int nblock);
+
+
+/* reset the collection of delta_h buffers for a new round of
+   data gathering */
+void mde_delta_h_coll_reset(t_mde_delta_h_coll* dhc);
+
+
+/* set the energyhistory variables to save state */
+void mde_delta_h_coll_update_energyhistory(const t_mde_delta_h_coll* dhc, energyhistory_t* enerhist);
+
+/* restore the variables from an energyhistory */
+void mde_delta_h_coll_restore_energyhistory(t_mde_delta_h_coll* dhc, const delta_h_history_t* deltaH);
+
+#endif /* GMX_MDLIB_MDEBIN_BAR_H */
diff --git a/src/include/gromacs/mdlib/mdoutf.h b/src/include/gromacs/mdlib/mdoutf.h
new file mode 100644 (file)
index 0000000..308f08c
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_MDOUTF_H
+#define GMX_MDLIB_MDOUTF_H
+
+#include <stdio.h>
+
+#include "gromacs/fileio/enxio.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/timing/wallcycle.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+
+class energyhistory_t;
+struct gmx_mtop_t;
+struct gmx_multisim_t;
+struct gmx_output_env_t;
+struct ObservablesHistory;
+struct t_commrec;
+struct t_filenm;
+struct t_inputrec;
+
+namespace gmx
+{
+enum class StartingBehavior;
+class IMDOutputProvider;
+struct MDModulesNotifiers;
+struct MdrunOptions;
+class WriteCheckpointDataHolder;
+} // namespace gmx
+
+typedef struct gmx_mdoutf* gmx_mdoutf_t;
+
+/*! \brief Allocate and initialize object to manager trajectory writing output
+ *
+ * Returns a pointer to a data structure with all output file pointers
+ * and names required by mdrun.
+ */
+gmx_mdoutf_t init_mdoutf(FILE*                          fplog,
+                         int                            nfile,
+                         const t_filenm                 fnm[],
+                         const gmx::MdrunOptions&       mdrunOptions,
+                         const t_commrec*               cr,
+                         gmx::IMDOutputProvider*        outputProvider,
+                         const gmx::MDModulesNotifiers& mdModulesNotifiers,
+                         const t_inputrec*              ir,
+                         const gmx_mtop_t&              mtop,
+                         const gmx_output_env_t*        oenv,
+                         gmx_wallcycle*                 wcycle,
+                         gmx::StartingBehavior          startingBehavior,
+                         bool                           simulationsShareState,
+                         const gmx_multisim_t*          ms);
+
+/*! \brief Getter for file pointer */
+ener_file_t mdoutf_get_fp_ene(gmx_mdoutf_t of);
+
+/*! \brief Getter for file pointer */
+FILE* mdoutf_get_fp_dhdl(gmx_mdoutf_t of);
+
+/*! \brief Getter for wallcycle timer */
+gmx_wallcycle* mdoutf_get_wcycle(gmx_mdoutf_t of);
+
+/*! \brief Close TNG files if they are open.
+ *
+ * This also measures the time it takes to close the TNG
+ * files.
+ */
+void mdoutf_tng_close(gmx_mdoutf_t of);
+
+/*! \brief Close all open output files and free the of pointer */
+void done_mdoutf(gmx_mdoutf_t of);
+
+/*! \brief Routine that writes trajectory-like frames.
+ *
+ * Writes data to trn, xtc and/or checkpoint. What is written is
+ * determined by the mdof_flags defined below. Data is collected to
+ * the master node only when necessary. Without domain decomposition
+ * only data from state_local is used and state_global is ignored.
+ *
+ * Note that the mdoutf_write_checkpoint function below allows to
+ * write only the checkpoint file, and does no data gathering.
+ * Except for the modular simulator checkpointing,  the
+ * mdoutf_write_to_trajectory_files function is used for
+ * _all trajectory / checkpoint writing_.
+ *
+ * \param[in] fplog                           File handler to log file.
+ * \param[in] cr                              Communication record.
+ * \param[in] of                              File handler to trajectory file.
+ * \param[in] mdof_flags                      Flags indicating what data is written.
+ * \param[in] natoms                          The total number of atoms in the system.
+ * \param[in] step                            The current time step.
+ * \param[in] t                               The current time.
+ * \param[in] state_local                     Pointer to the local state object.
+ * \param[in] state_global                    Pointer to the global state object.
+ * \param[in] observablesHistory              Pointer to the ObservableHistory object.
+ * \param[in] f_local                         The local forces.
+ * \param[in] modularSimulatorCheckpointData  CheckpointData object used by modular simulator.
+ */
+void mdoutf_write_to_trajectory_files(FILE*                          fplog,
+                                      const t_commrec*               cr,
+                                      gmx_mdoutf_t                   of,
+                                      int                            mdof_flags,
+                                      int                            natoms,
+                                      int64_t                        step,
+                                      double                         t,
+                                      t_state*                       state_local,
+                                      t_state*                       state_global,
+                                      ObservablesHistory*            observablesHistory,
+                                      gmx::ArrayRef<const gmx::RVec> f_local,
+                                      gmx::WriteCheckpointDataHolder* modularSimulatorCheckpointData);
+
+/*! \brief Routine allowing to write checkpoint files directly.
+ *
+ * Unlike mdoutf_write_to_trajectory_files, this will only write a checkpoint file,
+ * and it will not gather the state over the different ranks.
+ *
+ * This should only be called if no other trajectory file writing is needed, and
+ * the global state has all required information. Currently, this is only used by
+ * the modular checkpointing facility.
+ *
+ * \param[in] of                              File handler to trajectory file.
+ * \param[in] fplog                           File handler to log file.
+ * \param[in] cr                              Communication record.
+ * \param[in] step                            The current time step.
+ * \param[in] t                               The current time.
+ * \param[in] state_global                    Pointer to the global state object.
+ * \param[in] observablesHistory              Pointer to the ObservableHistory object.
+ * \param[in] modularSimulatorCheckpointData  CheckpointData object used by modular simulator.
+ */
+void mdoutf_write_checkpoint(gmx_mdoutf_t                    of,
+                             FILE*                           fplog,
+                             const t_commrec*                cr,
+                             int64_t                         step,
+                             double                          t,
+                             t_state*                        state_global,
+                             ObservablesHistory*             observablesHistory,
+                             gmx::WriteCheckpointDataHolder* modularSimulatorCheckpointData);
+
+/*! \brief Get the output interval of box size of uncompressed TNG output.
+ * Returns 0 if no uncompressed TNG file is open.
+ */
+int mdoutf_get_tng_box_output_interval(gmx_mdoutf_t of);
+
+/*! \brief Get the output interval of lambda of uncompressed TNG output.
+ * Returns 0 if no uncompressed TNG file is open.
+ */
+int mdoutf_get_tng_lambda_output_interval(gmx_mdoutf_t of);
+
+/*! \brief Get the output interval of box size of compressed TNG output.
+ * Returns 0 if no compressed TNG file is open.
+ */
+int mdoutf_get_tng_compressed_box_output_interval(gmx_mdoutf_t of);
+
+/*! \brief Get the output interval of lambda of compressed TNG output.
+ * Returns 0 if no compressed TNG file is open.
+ */
+int mdoutf_get_tng_compressed_lambda_output_interval(gmx_mdoutf_t of);
+
+#define MDOF_X (1u << 0u)
+#define MDOF_V (1u << 1u)
+#define MDOF_F (1u << 2u)
+#define MDOF_X_COMPRESSED (1u << 3u)
+#define MDOF_CPT (1u << 4u)
+#define MDOF_IMD (1u << 5u)
+#define MDOF_BOX (1u << 6u)
+#define MDOF_LAMBDA (1u << 7u)
+#define MDOF_BOX_COMPRESSED (1u << 8u)
+#define MDOF_LAMBDA_COMPRESSED (1u << 9u)
+
+#endif
diff --git a/src/include/gromacs/mdlib/membed.h b/src/include/gromacs/mdlib/membed.h
new file mode 100644 (file)
index 0000000..cfab16e
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_MDLIB_MEMBED_H
+#define GMX_MDLIB_MEMBED_H
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_membed_t;
+struct gmx_mtop_t;
+struct t_commrec;
+struct t_filenm;
+struct t_inputrec;
+class t_state;
+
+/* initialisation of membed code */
+gmx_membed_t* init_membed(FILE*          fplog,
+                          int            nfile,
+                          const t_filenm fnm[],
+                          gmx_mtop_t*    mtop,
+                          t_inputrec*    inputrec,
+                          t_state*       state,
+                          t_commrec*     cr,
+                          real*          cpt);
+
+/* rescaling the coordinates voor de membed code */
+void rescale_membed(int step_rel, gmx_membed_t* membed, rvec* x);
+
+void free_membed(gmx_membed_t* membed);
+
+#endif
diff --git a/src/include/gromacs/mdlib/perf_est.h b/src/include/gromacs/mdlib/perf_est.h
new file mode 100644 (file)
index 0000000..cc635d3
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_PERF_EST_H
+#define GMX_MDLIB_PERF_EST_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_mtop_t;
+struct t_inputrec;
+
+void count_bonded_distances(const gmx_mtop_t& mtop, const t_inputrec& ir, double* ndistance_c, double* ndistance_simd);
+/* Count the number of distance calculations in bonded interactions,
+ * separately for plain-C and SIMD bonded functions.
+ * The computational cost is nearly proportional to the numbers.
+ * It is allowed to pass NULL for the last two arguments.
+ */
+
+float pme_load_estimate(const gmx_mtop_t& mtop, const t_inputrec& ir, const matrix box);
+/* Returns an estimate for the relative load of the PME mesh calculation
+ * in the total force calculation.
+ * This estimate is reasonable for recent Intel and AMD x86_64 CPUs.
+ */
+
+#endif
diff --git a/src/include/gromacs/mdlib/rbin.h b/src/include/gromacs/mdlib/rbin.h
new file mode 100644 (file)
index 0000000..3d1bb3d
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_RBIN_H
+#define GMX_MDLIB_RBIN_H
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+struct t_commrec;
+
+typedef struct
+{
+    int     nreal;
+    int     maxreal;
+    double* rbuf;
+} t_bin;
+
+t_bin* mk_bin();
+/* Create a real bin */
+
+void destroy_bin(t_bin* b);
+/* Destroy the bin structure */
+
+void reset_bin(t_bin* b);
+/* Reset number of entries to zero */
+
+int add_binr(t_bin* b, int nr, const real r[]);
+int add_binr(t_bin* b, gmx::ArrayRef<const real> r);
+int add_bind(t_bin* b, int nr, const double r[]);
+int add_bind(t_bin* b, gmx::ArrayRef<const double> r);
+/* Add reals to the bin. Returns index */
+
+void sum_bin(t_bin* b, const t_commrec* cr);
+/* Globally sum the reals in the bin */
+
+void extract_binr(t_bin* b, int index, int nr, real r[]);
+void extract_binr(t_bin* b, int index, gmx::ArrayRef<real> r);
+void extract_bind(t_bin* b, int index, int nr, double r[]);
+void extract_bind(t_bin* b, int index, gmx::ArrayRef<double> r);
+/* Extract values from the bin, starting from index (see add_bin) */
+
+#endif
diff --git a/src/include/gromacs/mdlib/resethandler.h b/src/include/gromacs/mdlib/resethandler.h
new file mode 100644 (file)
index 0000000..d8a4a4f
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares the reset handler class.
+ *
+ * This class resets the various counters based on either the time (master rank sends
+ * checkpointing signal after 49.5% or run time), or based on the number of elapsed
+ * steps (handled locally by all ranks independently). Resets can happen in different
+ * ways:
+ *
+ *     * at a predetermined step (gmx mdrun -resetstep XXX)
+ *     * at half of the number of steps (gmx mdrun -resethway and nsteps set)
+ *     * at half of the max wall time (gmx mdrun -resethway -maxh XX), which is
+ *       implemented triggered when walltime >= 49.5% of max
+ *
+ * If two or more of these reset conditions are set, the first condition which is met
+ * resets the counters, there is no second reset happening. Note also that
+ * -resethway with nsteps set overwrites -resetstep
+ * (gmx mdrun -resethway -nsteps 100000 -resetstep 1000 will result in a reset at step
+ * 50000, not 1000).
+ *
+ * The setting and handling is implemented in private functions. They are only called
+ * if a respective boolean is true. For the trivial case of no reset needed (or no reset
+ * signal setting on any other rank than master), the translation unit of the calling
+ * function is therefore never left. The current implementation also allows the handler
+ * and setters to be ignored once a reset has been done, as a reset is only allowed to
+ * happen once. In the future, many of these cases this will be achieved by adding
+ * (or not adding) handlers / setters to the task graph.
+ *
+ * \author Pascal Merz <pascal.merz@colorado.edu>
+ * \inlibraryapi
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_RESETHANDLER_H
+#define GMX_MDLIB_RESETHANDLER_H
+
+#include "gromacs/compat/pointers.h"
+#include "gromacs/mdlib/simulationsignal.h"
+#include "gromacs/utility/logger.h"
+
+struct gmx_pme_t;
+struct gmx_wallcycle;
+struct gmx_walltime_accounting;
+struct nonbonded_verlet_t;
+struct pme_load_balancing_t;
+struct t_nrnb;
+
+namespace gmx
+{
+
+/*! \brief Reset signals
+ *
+ * Signals set and read by ResetHandler. Possible signals include
+ *   * nothing to signal
+ *   * reset counters (as soon as signal is received)
+ */
+enum class ResetSignal
+{
+    noSignal        = 0,
+    doResetCounters = 1
+};
+
+/*! \libinternal
+ * \brief Class handling the reset of counters
+ *
+ * Master rank sets the reset signal if half the run time is reached.
+ * All ranks receive the reset signal and reset their respective counters.
+ * This also resets the counters if half the time steps have passed (no communication needed).
+ */
+class ResetHandler final
+{
+public:
+    /*! \brief ResetHandler constructor
+     *
+     * Needs a pointer to the signal to communicate between ranks, information on whether
+     * multiple simulations need to be synchronized, and additional data to determine
+     * whether counter resetting takes place at all, and whether the current rank can set
+     * the resetting signal.
+     */
+    ResetHandler(compat::not_null<SimulationSignal*> signal,
+                 bool                                simulationsShareState,
+                 int64_t                             nsteps,
+                 bool                                isMaster,
+                 bool                                resetHalfway,
+                 real                                maximumHoursToRun,
+                 const MDLogger&                     mdlog,
+                 gmx_wallcycle*                      wcycle,
+                 gmx_walltime_accounting*            walltime_accounting);
+
+    /*! \brief Decides whether a reset signal needs to be set
+     *
+     * Reset signal is set if run time is greater than 49.5% of maximal run time.
+     */
+    void setSignal(gmx_walltime_accounting* walltime_accounting)
+    {
+        if (rankCanSetSignal_)
+        {
+            if (setSignalImpl(walltime_accounting))
+            {
+                // need to set the reset signal only once
+                rankCanSetSignal_ = false;
+            }
+        }
+    }
+
+    /*! \brief Decides whether the counters are reset, and performs the reset if needed
+     *
+     * The counters are reset if
+     *
+     *     * the signal for resetting was received, or
+     *     * the (local) number of steps reached the defined counter reset step.
+     *
+     * Note that even if two reset conditions are present (at a specific step and a
+     * specific time), the reset will only take place once, whenever the first condition
+     * is met.
+     */
+    void resetCounters(int64_t                     step,
+                       int64_t                     step_rel,
+                       const MDLogger&             mdlog,
+                       FILE*                       fplog,
+                       const t_commrec*            cr,
+                       nonbonded_verlet_t*         nbv,
+                       t_nrnb*                     nrnb,
+                       const gmx_pme_t*            pme,
+                       const pme_load_balancing_t* pme_loadbal,
+                       gmx_wallcycle*              wcycle,
+                       gmx_walltime_accounting*    walltime_accounting)
+    {
+        if (simulationNeedsReset_)
+        {
+            if (resetCountersImpl(step, step_rel, mdlog, fplog, cr, nbv, nrnb, pme, pme_loadbal, wcycle, walltime_accounting))
+            {
+                // need to reset the counters only once
+                simulationNeedsReset_ = false;
+                rankCanSetSignal_     = false;
+            }
+        }
+    }
+
+private:
+    //! Implementation of the setSignal() function
+    bool setSignalImpl(gmx_walltime_accounting* walltime_accounting);
+
+    //! Implementation of the resetCounters() function
+    bool resetCountersImpl(int64_t                     step,
+                           int64_t                     step_rel,
+                           const MDLogger&             mdlog,
+                           FILE*                       fplog,
+                           const t_commrec*            cr,
+                           nonbonded_verlet_t*         nbv,
+                           t_nrnb*                     nrnb,
+                           const gmx_pme_t*            pme,
+                           const pme_load_balancing_t* pme_loadbal,
+                           gmx_wallcycle*              wcycle,
+                           gmx_walltime_accounting*    walltime_accounting);
+
+    SimulationSignal& signal_;
+
+    bool       rankCanSetSignal_;
+    bool       simulationNeedsReset_;
+    const real maximumHoursToRun_;
+};
+} // namespace gmx
+
+#endif // GMX_MDLIB_RESETHANDLER_H
diff --git a/src/include/gromacs/mdlib/rf_util.h b/src/include/gromacs/mdlib/rf_util.h
new file mode 100644 (file)
index 0000000..653c30e
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_RF_UTIL_H
+#define GMX_MDLIB_RF_UTIL_H
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_mtop_t;
+struct t_forcerec;
+struct t_inputrec;
+
+void calc_rffac(FILE* fplog, real eps_r, real eps_rf, real Rc, real* krf, real* crf);
+/* Determine the reaction-field constants */
+
+#endif
diff --git a/src/include/gromacs/mdlib/settle.h b/src/include/gromacs/mdlib/settle.h
new file mode 100644 (file)
index 0000000..86bf81f
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares interface to SETTLE code.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+
+#ifndef GMX_MDLIB_SETTLE_H
+#define GMX_MDLIB_SETTLE_H
+
+#include "gromacs/topology/idef.h"
+#include "gromacs/utility/alignedallocator.h"
+
+struct gmx_mtop_t;
+struct InteractionList;
+struct t_inputrec;
+struct t_pbc;
+
+namespace gmx
+{
+
+template<typename>
+class ArrayRef;
+template<typename>
+class ArrayRefWithPadding;
+enum class ConstraintVariable : int;
+
+/* \brief Parameters for SETTLE algorithm.
+ *
+ * \todo Remove duplicates, check if recomputing makes more sense in some cases.
+ * \todo Move the projection parameters into separate structure.
+ */
+struct SettleParameters
+{
+    //! Mass of oxygen atom
+    real mO;
+    //! Mass of hydrogen atom
+    real mH;
+    //! Relative hydrogen mass (i.e. mH/(mO+2*mH))
+    real wh;
+    //! Target distance between oxygen and hydrogen
+    real dOH;
+    //! Target distance between hydrogen atoms
+    real dHH;
+    //! Double relative H mass times height of the H-O-H triangle.
+    real ra;
+    //! Height of the H-O-H triangle minus ra.
+    real rb;
+    //! Half the H-H distance.
+    real rc;
+    //! Reciprocal H-H distance
+    real irc2;
+
+    /* Parameters below are used for projection */
+    //! Reciprocal oxygen mass
+    real imO;
+    //! Reciprocal hydrogen mass
+    real imH;
+    //! Reciprocal O-H distance
+    real invdOH;
+    //! Reciprocal H-H distance (again)
+    real invdHH;
+    //! Reciprocal projection matrix
+    matrix invmat;
+};
+
+/*! \brief Computes and returns settle parameters.
+ *
+ * \param[in]  mO     Mass of oxygen atom
+ * \param[in]  mH     Mass of hydrogen atom
+ * \param[in]  invmO  Reciprocal mass of oxygen atom
+ * \param[in]  invmH  Reciprocal mass of hydrogen atom
+ * \param[in]  dOH    Target O-H bond length
+ * \param[in]  dHH    Target H-H bond length
+ */
+SettleParameters settleParameters(real mO, real mH, real dOH, real invmO, real invmH, real dHH);
+
+/*! \libinternal
+ * \brief Data for executing SETTLE constraining
+ */
+class SettleData
+{
+public:
+    //! Constructor
+    SettleData(const gmx_mtop_t& mtop);
+
+    //! Sets the constraints from the interaction list and the masses
+    void setConstraints(const InteractionList&    il_settle,
+                        int                       numHomeAtoms,
+                        gmx::ArrayRef<const real> masses,
+                        gmx::ArrayRef<const real> inverseMasses);
+
+    //! Returns settle parameters for constraining coordinates and forces
+    const SettleParameters& parametersMassWeighted() const { return parametersMassWeighted_; }
+
+    //! Returns settle parameters for constraining forces: all masses are set to 1
+    const SettleParameters& parametersAllMasses1() const { return parametersAllMasses1_; }
+
+    //! Returns the number of SETTLEs
+    int numSettles() const { return numSettles_; }
+
+    //! Returns a pointer to the indices of oxygens for each SETTLE
+    const int* ow1() const { return ow1_.data(); }
+    //! Returns a pointer to the indices of the first hydrogen for each SETTLE
+    const int* hw2() const { return hw2_.data(); }
+    //! Returns a pointer to the indices of the second hydrogen for each SETTLE
+    const int* hw3() const { return hw3_.data(); }
+    //! Returns a pointer to the virial contribution factor (either 1 or 0) for each SETTLE
+    const real* virfac() const { return virfac_.data(); }
+
+    //! Returns whether we should use SIMD intrinsics code
+    bool useSimd() const { return useSimd_; }
+
+private:
+    //! Parameters for SETTLE for coordinates
+    SettleParameters parametersMassWeighted_;
+    //! Parameters with all masses 1, for forces
+    SettleParameters parametersAllMasses1_;
+
+    //! The number of settles on our rank
+    int numSettles_;
+
+    //! Index to OW1 atoms, size numSettles_ + SIMD padding
+    std::vector<int, gmx::AlignedAllocator<int>> ow1_;
+    //! Index to HW2 atoms, size numSettles_ + SIMD padding
+    std::vector<int, gmx::AlignedAllocator<int>> hw2_;
+    //! Index to HW3 atoms, size numSettles_ + SIMD padding
+    std::vector<int, gmx::AlignedAllocator<int>> hw3_;
+    //! Virial factor 0 or 1, size numSettles_ + SIMD padding
+    std::vector<real, gmx::AlignedAllocator<real>> virfac_;
+
+    //! Tells whether we will use SIMD intrinsics code
+    bool useSimd_;
+};
+
+/*! \brief Constrain coordinates using SETTLE.
+ * Can be called on any number of threads.
+ */
+void csettle(const SettleData&               settled,     /* The SETTLE structure */
+             int                             nthread,     /* The number of threads used */
+             int                             thread,      /* Our thread index */
+             const t_pbc*                    pbc,         /* PBC data pointer, can be NULL */
+             ArrayRefWithPadding<const RVec> x,           /* Reference coordinates */
+             ArrayRefWithPadding<RVec>       xprime,      /* New coords, to be settled */
+             real                            invdt,       /* 1/delta_t */
+             ArrayRefWithPadding<RVec>       v,           /* Also constrain v if v!=NULL */
+             bool                            bCalcVirial, /* Calculate the virial contribution */
+             tensor                          vir_r_m_dr,  /* sum r x m delta_r */
+             bool*                           bErrorHasOccurred /* True if a settle error occurred */
+);
+
+/*! \brief Analytical algorithm to subtract the components of derivatives
+ * of coordinates working on settle type constraint.
+ */
+void settle_proj(const SettleData&    settled,
+                 ConstraintVariable   econq,
+                 int                  nsettle,
+                 const int            iatoms[],
+                 const t_pbc*         pbc, /* PBC data pointer, can be NULL  */
+                 ArrayRef<const RVec> x,
+                 ArrayRef<RVec>       der,
+                 ArrayRef<RVec>       derp,
+                 int                  CalcVirAtomEnd,
+                 tensor               vir_r_m_dder);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/settle_gpu.h b/src/include/gromacs/mdlib/settle_gpu.h
new file mode 100644 (file)
index 0000000..998f545
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares class for GPU implementation of SETTLE
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_SETTLE_GPU_H
+#define GMX_MDLIB_SETTLE_GPU_H
+
+#include "gmxpre.h"
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/device_context.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/math/functions.h"
+#include "gromacs/math/invertmatrix.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/mdlib/settle.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/pbcutil/pbc_aiuc.h"
+#include "gromacs/topology/topology.h"
+
+class InteractionDefinitions;
+
+namespace gmx
+{
+
+//! \internal \brief Indices of atoms in a water molecule
+struct WaterMolecule
+{
+    //! Oxygen atom
+    int ow1;
+    //! First hydrogen atom
+    int hw2;
+    //! Second hydrogen atom
+    int hw3;
+};
+
+
+/*! \internal \brief Class with interfaces and data for GPU version of SETTLE. */
+class SettleGpu
+{
+
+public:
+    /*! \brief Create SETTLE object
+     *
+     *  Extracts masses for oxygen and hydrogen as well as the O-H and H-H target distances
+     *  from the topology data (mtop), check their values for consistency and calls the
+     *  following constructor.
+     *
+     * \param[in] mtop           Topology of the system to gen the masses for O and H atoms and
+     *                           target O-H and H-H distances. These values are also checked for
+     *                           consistency.
+     * \param[in] deviceContext  Device context (dummy in CUDA).
+     * \param[in] deviceStream   Device stream to use.
+     */
+    SettleGpu(const gmx_mtop_t& mtop, const DeviceContext& deviceContext, const DeviceStream& deviceStream);
+
+    ~SettleGpu();
+
+    /*! \brief Apply SETTLE.
+     *
+     * Applies SETTLE to coordinates and velocities, stored on GPU. Data at pointers d_xp and
+     * d_v change in the GPU memory. The results are not automatically copied back to the CPU
+     * memory. Method uses this class data structures which should be updated when needed using
+     * update method.
+     *
+     * \param[in]     d_x               Coordinates before timestep (in GPU memory)
+     * \param[in,out] d_xp              Coordinates after timestep (in GPU memory). The
+     *                                  resulting constrained coordinates will be saved here.
+     * \param[in]     updateVelocities  If the velocities should be updated.
+     * \param[in,out] d_v               Velocities to update (in GPU memory, can be nullptr
+     *                                  if not updated)
+     * \param[in]     invdt             Reciprocal timestep (to scale Lagrange
+     *                                  multipliers when velocities are updated)
+     * \param[in]     computeVirial     If virial should be updated.
+     * \param[in,out] virialScaled      Scaled virial tensor to be updated.
+     * \param[in]     pbcAiuc           PBC data.
+     */
+    void apply(const DeviceBuffer<Float3>& d_x,
+               DeviceBuffer<Float3>        d_xp,
+               bool                        updateVelocities,
+               DeviceBuffer<Float3>        d_v,
+               real                        invdt,
+               bool                        computeVirial,
+               tensor                      virialScaled,
+               const PbcAiuc&              pbcAiuc);
+
+    /*! \brief
+     * Update data-structures (e.g. after NB search step).
+     *
+     * Updates the constraints data and copies it to the GPU. Should be
+     * called if the particles were sorted, redistributed between domains, etc.
+     * Does not recycle the data preparation routines from the CPU version.
+     * All three atoms from single water molecule should be handled by the same GPU.
+     *
+     * SETTLEs atom ID's is taken from idef.il[F_SETTLE].iatoms.
+     *
+     * \param[in] idef    System topology
+     */
+    void set(const InteractionDefinitions& idef);
+
+private:
+    //! GPU context object
+    const DeviceContext& deviceContext_;
+    //! GPU stream
+    const DeviceStream& deviceStream_;
+
+    //! Scaled virial tensor (9 reals, GPU)
+    std::vector<float> h_virialScaled_;
+    //! Scaled virial tensor (9 reals, GPU)
+    DeviceBuffer<float> d_virialScaled_;
+
+    //! Number of settles
+    int numSettles_ = 0;
+
+    //! Indexes of atoms (.i for oxygen, .j and.k for hydrogens, CPU)
+    std::vector<WaterMolecule> h_atomIds_;
+    //! Indexes of atoms (.i for oxygen, .j and.k for hydrogens, GPU)
+    DeviceBuffer<WaterMolecule> d_atomIds_;
+    //! Current size of the array of atom IDs
+    int numAtomIds_ = -1;
+    //! Allocated size for the array of atom IDs
+    int numAtomIdsAlloc_ = -1;
+
+    //! Settle parameters
+    SettleParameters settleParameters_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDLIB_SETTLE_GPU_H
diff --git a/src/include/gromacs/mdlib/settle_gpu_internal.h b/src/include/gromacs/mdlib/settle_gpu_internal.h
new file mode 100644 (file)
index 0000000..f88d046
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares backend-specific functions for GPU implementation of SETTLE.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_SETTLE_GPU_INTERNAL_H
+#define GMX_MDLIB_SETTLE_GPU_INTERNAL_H
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/mdlib/settle_gpu.h"
+
+namespace gmx
+{
+
+/*! \brief Apply SETTLE.
+ *
+ * Applies SETTLE to coordinates and velocities, stored on GPU. Data at pointers d_xp and
+ * d_v change in the GPU memory. The results are not automatically copied back to the CPU
+ * memory. Method uses this class data structures which should be updated when needed using
+ * update method.
+ *
+ * \param[in]     numSettles        Number of SETTLE constraints.
+ * \param[in]     d_atomIds         Device buffer with indices of atoms to be SETTLEd.
+ * \param[in]     settleParameters  Parameters for SETTLE constraints.
+ * \param[in]     d_x               Coordinates before timestep (in GPU memory)
+ * \param[in,out] d_xp              Coordinates after timestep (in GPU memory). The
+ *                                  resulting constrained coordinates will be saved here.
+ * \param[in]     updateVelocities  If the velocities should be updated.
+ * \param[in,out] d_v               Velocities to update (in GPU memory, can be nullptr
+ *                                  if not updated)
+ * \param[in]     invdt             Reciprocal timestep (to scale Lagrange
+ *                                  multipliers when velocities are updated)
+ * \param[in]     computeVirial     If virial should be updated.
+ * \param[in,out] virialScaled      Scaled virial tensor to be updated.
+ * \param[in]     pbcAiuc           PBC data.
+ * \param[in]     deviceStream      Device stream to launch kernel in.
+ */
+void launchSettleGpuKernel(int                                numSettles,
+                           const DeviceBuffer<WaterMolecule>& d_atomIds,
+                           const SettleParameters&            settleParameters,
+                           const DeviceBuffer<Float3>&        d_x,
+                           DeviceBuffer<Float3>               d_xp,
+                           bool                               updateVelocities,
+                           DeviceBuffer<Float3>               d_v,
+                           real                               invdt,
+                           bool                               computeVirial,
+                           DeviceBuffer<float>                virialScaled,
+                           const PbcAiuc&                     pbcAiuc,
+                           const DeviceStream&                deviceStream);
+
+} // namespace gmx
+
+#endif // GMX_MDLIB_SETTLE_GPU_INTERNAL_H
diff --git a/src/include/gromacs/mdlib/shake.h b/src/include/gromacs/mdlib/shake.h
new file mode 100644 (file)
index 0000000..f072381
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares interface to SHAKE code.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+
+#ifndef GMX_MDLIB_SHAKE_H
+#define GMX_MDLIB_SHAKE_H
+
+#include "gromacs/math/vec.h"
+#include "gromacs/topology/block.h"
+#include "gromacs/utility/real.h"
+
+struct InteractionList;
+class InteractionDefinitions;
+struct t_inputrec;
+struct t_nrnb;
+struct t_pbc;
+
+namespace gmx
+{
+template<typename T>
+class ArrayRef;
+
+enum class ConstraintVariable : int;
+
+/*! \libinternal
+ * \brief Working data for the SHAKE algorithm
+ */
+struct shakedata
+{
+    //! Returns the number of SHAKE blocks */
+    int numShakeBlocks() const { return sblock.size() - 1; }
+
+    //! The reference constraint vectors
+    std::vector<RVec> rij;
+    //! The reduced mass of the two atoms in each constraint times 0.5
+    std::vector<real> half_of_reduced_mass;
+    //! Multiplicative tolerance on the difference in the square of the constrained distance
+    std::vector<real> distance_squared_tolerance;
+    //! The reference constraint distances squared
+    std::vector<real> constraint_distance_squared;
+    /* SOR stuff */
+    //! SOR delta
+    real delta = 0.1;
+    //! SOR omega
+    real omega = 1.0;
+    //! SOR gamma
+    real gamma = 1000000;
+    //! The SHAKE blocks, block i contains constraints sblock[i]/3 to sblock[i+1]/3 */
+    std::vector<int> sblock = { 0 };
+    /*! \brief Scaled Lagrange multiplier for each constraint.
+     *
+     * Value is -2 * eta from p. 336 of the paper, divided by the
+     * constraint distance. */
+    std::vector<real> scaled_lagrange_multiplier;
+};
+
+//! Make SHAKE blocks when not using DD.
+void make_shake_sblock_serial(shakedata* shaked, InteractionDefinitions* idef, int numAtoms);
+
+//! Make SHAKE blocks when using DD.
+void make_shake_sblock_dd(shakedata* shaked, const InteractionList& ilcon);
+
+/*! \brief Shake all the atoms blockwise. It is assumed that all the constraints
+ * in the idef->shakes field are sorted, to ascending block nr. The
+ * sblock array points into the idef->shakes.iatoms field, with block 0
+ * starting
+ * at sblock[0] and running to ( < ) sblock[1], block n running from
+ * sblock[n] to sblock[n+1]. Array sblock should be large enough.
+ * Return TRUE when OK, FALSE when shake-error
+ */
+bool constrain_shake(FILE*                         log,     /* Log file                        */
+                     shakedata*                    shaked,  /* Total number of atoms   */
+                     gmx::ArrayRef<const real>     invmass, /* Atomic masses           */
+                     const InteractionDefinitions& idef,    /* The interaction def             */
+                     const t_inputrec&             ir,      /* Input record                    */
+                     ArrayRef<const RVec>          x_s,     /* Coords before update            */
+                     ArrayRef<RVec>                xprime,  /* Output coords when constraining x */
+                     ArrayRef<RVec>                vprime,  /* Output coords when constraining v */
+                     const t_pbc*                  pbc,     /* PBC information              */
+                     t_nrnb*                       nrnb,    /* Performance measure          */
+                     real                          lambda,  /* FEP lambda                   */
+                     real*                         dvdlambda,  /* FEP force                    */
+                     real                          invdt,      /* 1/delta_t                    */
+                     ArrayRef<RVec>                v,          /* Also constrain v if not empty  */
+                     bool                          bCalcVir,   /* Calculate r x m delta_r      */
+                     tensor                        vir_r_m_dr, /* sum r x m delta_r            */
+                     bool                          bDumpOnError, /* Dump debugging stuff on error*/
+                     ConstraintVariable econq); /* which type of constraint is occurring */
+
+/*! \brief Regular iterative shake */
+void cshake(const int            iatom[],
+            int                  ncon,
+            int*                 nnit,
+            int                  maxnit,
+            ArrayRef<const real> dist2,
+            ArrayRef<RVec>       xp,
+            const t_pbc*         pbc,
+            ArrayRef<const RVec> rij,
+            ArrayRef<const real> half_of_reduced_mass,
+            real                 omega,
+            ArrayRef<const real> invmass,
+            ArrayRef<const real> distance_squared_tolerance,
+            ArrayRef<real>       scaled_lagrange_multiplier,
+            int*                 nerror);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/sighandler.h b/src/include/gromacs/mdlib/sighandler.h
new file mode 100644 (file)
index 0000000..1f56378
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_SIGHANDLER_H
+#define GMX_MDLIB_SIGHANDLER_H
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include <csignal>
+
+/* NOTE: the terminology is:
+   incoming signals (provided by the operating system, or transmitted from
+   other nodes) lead to stop conditions. These stop conditions should be
+   checked for and acted on by the outer loop of the simulation */
+
+/* the stop conditions. They are explicitly allowed to be compared against
+   each other. */
+enum class StopCondition : sig_atomic_t
+{
+    None = 0,
+    NextNS, /* stop a the next neighbour searching step */
+    Next,   /* stop a the next step */
+    Abort,  /* stop now. (this should never be seen) */
+    Count
+};
+
+/* Our names for the stop conditions.
+   These must match the number given in gmx_stop_cond_t.*/
+const char* enumValueToString(StopCondition enumValue);
+
+/* the externally visible functions: */
+
+/* install the signal handlers that can set the stop condition. */
+void signal_handler_install();
+
+/* get the current stop condition */
+StopCondition gmx_get_stop_condition();
+
+/* set the stop condition upon receiving a remote one */
+void gmx_set_stop_condition(StopCondition recvd_stop_cond);
+
+/*!
+ * \brief Reinitializes the global stop condition.
+ *
+ * Resets any stop condition currently stored in global library state as read or
+ * written with gmx_get_stop_condition() and gmx_set_stop_condition(). Does not
+ * affect the result of gmx_got_usr_signal() gmx_get_signal_name() for
+ * previously terminated simulations.
+ *
+ * The reset is necessary between simulation segments performed in the same
+ * process and should be called only while simulation is idle, such as after
+ * a gmx::Mdrunner has finished its work and simulation results have been processed.
+ */
+void gmx_reset_stop_condition();
+
+/* get the signal name that lead to the current stop condition. */
+const char* gmx_get_signal_name();
+
+/* check whether we received a USR1 signal.
+   The condition is reset once a TRUE value is returned, so this function
+   only returns TRUE once for a single signal. */
+gmx_bool gmx_got_usr_signal();
+
+#endif
diff --git a/src/include/gromacs/mdlib/simulationsignal.h b/src/include/gromacs/mdlib/simulationsignal.h
new file mode 100644 (file)
index 0000000..db42983
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2011,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ *
+ * \brief This file declares functions for inter-rank signalling by mdrun
+ *
+ * This handles details of responding to termination conditions,
+ * coordinating checkpoints, and coordinating multi-simulations.
+ *
+ * \todo Move this to mdrunutility module alongside gathering
+ * multi-simulation communication infrastructure there.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_SIMULATIONSIGNAL_H
+#define GMX_MDLIB_SIMULATIONSIGNAL_H
+
+#include <array>
+
+#include "gromacs/utility/real.h"
+
+struct gmx_multisim_t;
+struct t_commrec;
+
+//! Kinds of simulation conditions to signal about.
+enum
+{
+    eglsCHKPT,
+    eglsSTOPCOND,
+    eglsRESETCOUNTERS,
+    eglsNR
+};
+
+namespace gmx
+{
+
+template<typename T>
+class ArrayRef;
+
+/*!
+ * \brief POD-style object used by mdrun ranks to set and
+ * receive signals within and between simulations.
+ *
+ * Keep in mind that the values of signals are transmitted to other
+ * ranks through an MPI_Reduce after casting them to a real (so the
+ * signals can be sent together with other data). This means that the
+ * only meaningful values are positive, negative or zero.
+ *
+ * isLocal permits (for example) replica-exchange to require that any
+ * checkpointing is synchronized across all simulations, by setting
+ * isLocal to false, so that the trigger for action is set only when
+ * inter-simulation signalling happens. Replica-exchange can
+ * coordinate this at run time when a SimulationSignaller is made. */
+class SimulationSignal
+{
+public:
+    //! Constructor
+    SimulationSignal(bool isSignalLocal = true) : sig(0), set(0), isLocal(isSignalLocal) {}
+    //! The signal set by this rank in do_md().
+    signed char sig;
+    //! The communicated signal that triggers action, which will be equal for all ranks, once communication has occured.
+    signed char set;
+    //! Is the signal in one simulation independent of other simulations?
+    bool isLocal;
+};
+
+//! Convenience typedef for the group of signals used.
+typedef std::array<SimulationSignal, eglsNR> SimulationSignals;
+
+/*!
+ * \brief Object used by mdrun ranks to signal to each other at this step.
+ *
+ * This object has responsibility to read signal values from \c gs,
+ * coordinate communication within and perhaps between simulations,
+ * and set result signal values in \c gs as appropriate.
+ *
+ * It is intended to have a very short lifetime, so should remain easy
+ * to construct and destruct on the stack just when the global
+ * communication occurs. */
+class SimulationSignaller
+{
+public:
+    //! Constructor
+    SimulationSignaller(SimulationSignals*    signals,
+                        const t_commrec*      cr,
+                        const gmx_multisim_t* ms,
+                        bool                  doInterSim,
+                        bool                  doIntraSim);
+    /*! \brief Return a reference to an array of signal values to communicate.
+     *
+     * \return If intra-sim signalling will take place, fill and
+     * return a reference to the array of reals in which signals
+     * will be communicated with the signal values to be
+     * sent. Otherwise return a EmptyArrayRef. */
+    gmx::ArrayRef<real> getCommunicationBuffer();
+    /*! \brief Handle inter-simulation signal communication.
+     *
+     * If an inter-simulation signal should be handled, communicate between
+     * simulation-master ranks, then propagate from the masters to the
+     * rest of the ranks for each simulation. It is the responsibility of
+     * the calling code to ensure that any necessary intra-simulation
+     * signalling has already occurred, e.g. in global_stat(). */
+    void signalInterSim();
+    /*! \brief Propagate signals when appropriate.
+     *
+     * Always propagate an mdrun signal value when doing
+     * inter-simulation signalling; otherwise, propagate it only
+     * if should be propagated within this simulation,
+     * ie. locally. See documentation of SimulationSignal for
+     * details. */
+    void setSignals();
+    //! Convenience wrapper that calls signalInterSim() then setSignals().
+    void finalizeSignals();
+
+private:
+    //! Source and sink for mdrun signals
+    SimulationSignals* signals_;
+    //! Communication object.
+    const t_commrec* cr_;
+    //! Multi-sim handler.
+    const gmx_multisim_t* ms_;
+    //! Do inter-sim communication at this step.
+    bool doInterSim_;
+    //! Do intra-sim communication at this step.
+    bool doIntraSim_;
+    //! Buffer for MPI communication.
+    std::array<real, eglsNR> mpiBuffer_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/splitter.h b/src/include/gromacs/mdlib/splitter.h
new file mode 100644 (file)
index 0000000..b1c7517
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_SPLITTER_H
+#define GMX_MDLIB_SPLITTER_H
+
+#include <cstdio>
+
+#include "gromacs/utility/basedefinitions.h"
+
+class InteractionDefinitions;
+struct t_blocka;
+
+void gen_sblocks(FILE* fp, int at_end, const InteractionDefinitions& idef, t_blocka* sblock, gmx_bool bSettle);
+/* Generate shake blocks from the constraint list. Set bSettle to yes for shake
+ * blocks including settles. You normally do not want this.
+ */
+
+#endif
diff --git a/src/include/gromacs/mdlib/stat.h b/src/include/gromacs/mdlib/stat.h
new file mode 100644 (file)
index 0000000..ec854a7
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_MDLIB_STAT_H
+#define GMX_MDLIB_STAT_H
+
+#include <cstdint>
+
+#include "gromacs/math/vectypes.h"
+
+class gmx_ekindata_t;
+struct gmx_enerdata_t;
+struct t_vcm;
+struct t_inputrec;
+struct t_commrec;
+
+namespace gmx
+{
+template<typename T>
+class ArrayRef;
+class ObservablesReducer;
+} // namespace gmx
+
+typedef struct gmx_global_stat* gmx_global_stat_t;
+
+gmx_global_stat_t global_stat_init(const t_inputrec* ir);
+
+void global_stat_destroy(gmx_global_stat_t gs);
+
+/*! \brief All-reduce energy-like quantities over cr->mpi_comm_mysim  */
+void global_stat(const gmx_global_stat&   gs,
+                 const t_commrec*         cr,
+                 gmx_enerdata_t*          enerd,
+                 tensor                   fvir,
+                 tensor                   svir,
+                 const t_inputrec&        inputrec,
+                 gmx_ekindata_t*          ekind,
+                 t_vcm*                   vcm,
+                 gmx::ArrayRef<real>      sig,
+                 bool                     bSumEkinhOld,
+                 int                      flags,
+                 int64_t                  step,
+                 gmx::ObservablesReducer* observablesReducer);
+
+/*! \brief Returns TRUE if io should be done */
+inline bool do_per_step(int64_t step, int64_t nstep)
+{
+    if (nstep != 0)
+    {
+        return (step % nstep) == 0;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+#endif // GMX_MDLIB_STAT_H
diff --git a/src/include/gromacs/mdlib/stophandler.h b/src/include/gromacs/mdlib/stophandler.h
new file mode 100644 (file)
index 0000000..bcad296
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares StopHandler, a helper class and two stop conditions.
+ *
+ * These classes encapsulate the setting and handling of stop signals.
+ *
+ * StopHandler lives during the lifetime of do_md. It checks via registered stop
+ * conditions whether the simulation should be stopped at the next possible step or
+ * at the next possible neighbor-searching step. It communicates this via signal to
+ * all ranks and communicates this to do_md via stoppingAfterCurrentStep().
+ *
+ * StopHandlerBuilder is owned by the runner, and allows to register stop conditions
+ * at a higher level, outside of do_md. Within do_md, it is creating a StopHandler
+ * object by binding local data and passing a reference to the stop conditions.
+ *
+ * Here, we are implementing two stop conditions: StopConditionTime sets a stop condition
+ * based on the elapsed time (only relevant if the -maxh flag was set), while
+ * StopConditionSignal sets stop conditions via signals received from the operating
+ * systems (SIGINT / SIGTERM).
+ *
+ * The stop conditions are stored as function pointers created by a lambda expression.
+ * They bind to required local data, in the case of StopConditionTime and StopConditionSignal
+ * these are partially owned by do_md. This requires these function pointers to be deleted
+ * at the end of do_md(). This is achieved by having the do_md() specific function pointers
+ * owned by StopHandler, which in turn is owned (via unique_ptr) by do_md().
+ *
+ * \author Pascal Merz <pascal.merz@colorado.edu>
+ * \inlibraryapi
+ * \ingroup module_mdlib
+ */
+
+#ifndef GMX_MDLIB_STOPHANDLER_H
+#define GMX_MDLIB_STOPHANDLER_H
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include "gromacs/compat/pointers.h"
+#include "gromacs/mdlib/sighandler.h"
+#include "gromacs/mdlib/simulationsignal.h"
+
+struct gmx_walltime_accounting;
+
+namespace gmx
+{
+/*! \brief Stop signals
+ *
+ * Signals that stop conditions can send to all ranks. Possible signals include
+ *   * nothing to signal
+ *   * stop at the next neighbor-searching step
+ *   * stop as soon as signal is received
+ */
+enum class StopSignal : int
+{
+    noSignal         = 0,
+    stopAtNextNSStep = 1,
+    stopImmediately  = -1
+};
+
+/*! \brief Convert signed char (as used by SimulationSignal) to StopSignal enum
+ *
+ * * Expected values are
+ *   \p sig ==  0 -- no signal
+ *   \p sig >=  1 -- stop at next NS
+ *   \p sig <= -1 -- stop asap
+ */
+static inline StopSignal convertToStopSignal(signed char sig)
+{
+    if (sig <= -1)
+    {
+        return StopSignal::stopImmediately;
+    }
+    else if (sig >= 1)
+    {
+        return StopSignal::stopAtNextNSStep;
+    }
+    else // sig == 0
+    {
+        return StopSignal::noSignal;
+    }
+}
+
+/*! \libinternal
+ * \brief Class handling the stop signal
+ *
+ * Loops over the registered stop conditions and sets a signal if
+ * requested (currently only done by master rank).
+ * All ranks receive the stop signal and set the respective flag.
+ * The functions are implemented within this header file to avoid leaving
+ * the translation unit unnecessarily.
+ */
+class StopHandler final
+{
+public:
+    /*! \brief StopHandler constructor (will be called by StopHandlerBuilder)
+     *
+     * @param signal Non-null pointer to a signal used for reading and writing of signals
+     * @param simulationShareState Whether this signal needs to be shared across multiple simulations
+     * @param stopConditions Vector of callback functions setting the signal
+     * @param neverUpdateNeighborList Whether simulation keeps same neighbor list forever
+     *
+     * Note: As the StopHandler does not work without this signal, it keeps a non-const reference
+     * to it as a member variable.
+     */
+    StopHandler(compat::not_null<SimulationSignal*>      signal,
+                bool                                     simulationShareState,
+                std::vector<std::function<StopSignal()>> stopConditions,
+                bool                                     neverUpdateNeighborList);
+
+    /*! \brief Decides whether a stop signal shall be sent
+     *
+     * Loops over the stopCondition vector passed at build time (consisting of conditions
+     * registered with StopHandlerBuilder, and conditions built by StopHandlerBuilder by
+     * default), and sets any signal obtained.
+     * Returns as soon as a StopSignal::stopImmediately signal was obtained, or after
+     * checking all registered stop conditions.
+     */
+    void setSignal() const
+    {
+        for (const auto& condition : stopConditions_)
+        {
+            const StopSignal sig = condition();
+            if (sig != StopSignal::noSignal)
+            {
+                signal_.sig = static_cast<signed char>(sig);
+                if (sig == StopSignal::stopImmediately)
+                {
+                    // We don't want this to be overwritten by a less urgent stop
+                    break;
+                }
+            }
+        }
+    }
+
+    /*! \brief Decides whether the simulation shall be stopped after the current step
+     *
+     * The simulation is stopped after the current step if
+     *   * the signal for immediate stop was received, or
+     *   * the signal for stop at the next neighbor-searching step was received, and
+     *     the current step is a neighbor-searching step.
+     */
+    bool stoppingAfterCurrentStep(bool bNS) const
+    {
+        return convertToStopSignal(signal_.set) == StopSignal::stopImmediately
+               || (convertToStopSignal(signal_.set) == StopSignal::stopAtNextNSStep
+                   && (bNS || neverUpdateNeighborlist_));
+    }
+
+private:
+    SimulationSignal&                              signal_;
+    const std::vector<std::function<StopSignal()>> stopConditions_;
+    const bool                                     neverUpdateNeighborlist_;
+};
+
+/*! \libinternal
+ * \brief Class setting the stop signal based on gmx_get_stop_condition()
+ *
+ * Master rank sets the stop signal if required (generally due to SIGINT).
+ */
+class StopConditionSignal final
+{
+public:
+    /*! \brief StopConditionSignal constructor
+     */
+    StopConditionSignal(int nstList, bool makeBinaryReproducibleSimulation, int nstSignalComm);
+
+    /*! \brief Decides whether a stopping signal needs to be set
+     *
+     * Stop signal is set based on the value of gmx_get_stop_condition(): Set signal for
+     * stop at the next neighbor-searching step at first SIGINT / SIGTERM, set signal
+     * for stop at the next step at second SIGINT / SIGTERM.
+     */
+    StopSignal getSignal(FILE* fplog);
+
+private:
+    StopCondition handledStopCondition_;
+    const bool    makeBinaryReproducibleSimulation_;
+    const int     nstSignalComm_;
+    const int     nstList_;
+};
+
+/*! \libinternal
+ * \brief Class setting the stop signal based on maximal run time
+ *
+ * Master rank sets the stop signal if run time exceeds maximal run time.
+ */
+class StopConditionTime final
+{
+public:
+    /*! \brief StopConditionTime constructor
+     */
+    StopConditionTime(int nstList, real maximumHoursToRun, int nstSignalComm);
+
+    /*! \brief Decides whether a stopping signal needs to be set
+     *
+     * Stop signal is set if run time is greater than 99% of maximal run time. Signal will
+     * trigger stopping of the simulation at the next neighbor-searching step.
+     */
+    StopSignal getSignal(bool bNS, int64_t step, FILE* fplog, gmx_walltime_accounting* walltime_accounting);
+
+private:
+    bool signalSent_;
+
+    const real maximumHoursToRun_;
+    const int  nstList_;
+    const int  nstSignalComm_;
+    const bool neverUpdateNeighborlist_;
+};
+
+/*! \libinternal
+ * \brief Class preparing the creation of a StopHandler
+ *
+ * An object of this helper class (owned by the runner) allows to register stop conditions
+ * outside of the actual simulation run via registerStopCondition(), accepting a std::function
+ * object. It then builds a StopHandler object inside do_md, once it can bind to required
+ * local data.
+ *
+ * The registered stop conditions plus the standard MD stop conditions (stop based on
+ * received signal from OS [SIGINT / SIGTERM] or based on maximal run time) are then called
+ * by the Stophandler every step to determine whether the simulation should be stopped.
+ * The registered functions need to be of type `std::function<StopSignal()>`, i.e. not
+ * taking any input arguments and returning a `StopSignal` signal which will get propagated
+ * to all ranks. If the function needs input arguments, these need to be bound (e.g. via
+ * lambda capturing) before being registered with the StopHandlerBuilder.
+ */
+class StopHandlerBuilder final
+{
+public:
+    /*! \brief Register stop condition
+     *
+     * This allows code in the scope of the StopHandlerBuilder (runner level) to inject
+     * stop conditions in simulations. Stop conditions are defined as argument-less functions
+     * which return a StopSignal. The return value of this function is then propagated to all
+     * ranks, and allows to stop the simulation at the next global communication step (returned
+     * signal StopSignal::stopImmediately), or at the next NS step (returned signal
+     * StopSignal::stopAtNextNSStep, allows for exact continuation).
+     *
+     * Arguments needed by the stop condition function need to be bound / captured. If these
+     * arguments are captured by reference or using a pointer, it is the registrant's
+     * responsibility to ensure that these arguments do not go out of scope during the lifetime
+     * of the StopHandlerBuilder.
+     */
+    void registerStopCondition(std::function<StopSignal()> stopCondition);
+
+    /*! \brief Create StopHandler
+     *
+     * Gets called in the scope of the integrator (aka do_md()) to get a pointer to the
+     * StopHandler for the current simulations. Adds the standard MD stop conditions
+     * (e.g. gmx::StopConditionTime, gmx::StopConditionSignal) to the currently registered
+     * stop conditions. Initializes a new StopHandler with this extended vector of
+     * stop conditions. It is the caller's responsibility to make sure arguments passed by
+     * pointer or reference remain valid for the lifetime of the returned StopHandler.
+     */
+    std::unique_ptr<StopHandler> getStopHandlerMD(compat::not_null<SimulationSignal*> signal,
+                                                  bool            simulationShareState,
+                                                  bool            isMaster,
+                                                  int             nstList,
+                                                  bool            makeBinaryReproducibleSimulation,
+                                                  int             nstSignalComm,
+                                                  real            maximumHoursToRun,
+                                                  bool            neverUpdateNeighborList,
+                                                  FILE*           fplog,
+                                                  const int64_t&  step,
+                                                  const gmx_bool& bNS,
+                                                  gmx_walltime_accounting* walltime_accounting);
+
+private:
+    /*! \brief Initial stopConditions
+     *
+     * StopConditions registered via registerStopCondition(). getStopHandlerMD will
+     * copy this vector and add additional conditions before passing the new vector
+     * to the built StopHandler object.
+     */
+    std::vector<std::function<StopSignal()>> stopConditions_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDLIB_STOPHANDLER_H
diff --git a/src/include/gromacs/mdlib/tests/constrtestdata.h b/src/include/gromacs/mdlib/tests/constrtestdata.h
new file mode 100644 (file)
index 0000000..a136ac1
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief SHAKE and LINCS tests header.
+ *
+ * Contains description and constructor for the test data accumulating object,
+ * declares CPU- and GPU-based functions used to apply SHAKE or LINCS on the
+ * test data.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ * \ingroup module_mdlib
+ */
+
+#ifndef GMX_MDLIB_TESTS_CONSTRTESTDATA_H
+#define GMX_MDLIB_TESTS_CONSTRTESTDATA_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/gmxlib/nrnb.h"
+#include "gromacs/hardware/device_management.h"
+#include "gromacs/math/paddedvector.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdlib/gmx_omp_nthreads.h"
+#include "gromacs/mdlib/lincs.h"
+#include "gromacs/mdlib/shake.h"
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/topology/topology.h"
+
+namespace gmx
+{
+namespace test
+{
+
+/* \brief
+ * Constraints test data structure.
+ *
+ * Structure to collect all the necessary data, including system coordinates and topology,
+ * constraints information, etc. The structure can be reset and reused.
+ */
+class ConstraintsTestData
+{
+public:
+    //! Human-friendly name for a system
+    std::string title_;
+    //! Number of atoms
+    int numAtoms_;
+    //! Topology
+    gmx_mtop_t mtop_;
+    //! Masses
+    std::vector<real> masses_;
+    //! Inverse masses
+    std::vector<real> invmass_;
+    //! Input record (info that usually in .mdp file)
+    t_inputrec ir_;
+    //! Local topology
+    std::unique_ptr<InteractionDefinitions> idef_;
+    //! Computational time array (normally used to benchmark performance)
+    t_nrnb nrnb_;
+
+    //! Inverse timestep
+    real invdt_;
+    //! Number of flexible constraints
+    int nflexcon_ = 0;
+    //! Whether the virial should be computed
+    bool computeVirial_;
+    //! Scaled virial
+    tensor virialScaled_;
+    //! If the free energy is computed
+    bool compute_dHdLambda_;
+    //! If there are atoms with perturbed mass
+    bool hasMassPerturbed_ = false;
+    //! Lambda value
+    real lambda_ = 0.0;
+    //! For free energy computation
+    real dHdLambda_;
+    //! For free energy computation (reference value)
+    real dHdLambdaRef_;
+
+    //! Coordinates before the timestep
+    PaddedVector<RVec> x_;
+    //! Coordinates after timestep, output for the constraints
+    PaddedVector<RVec> xPrime_;
+    //! Backup for coordinates (for reset)
+    PaddedVector<RVec> xPrime0_;
+    //! Intermediate set of coordinates (normally used for projection correction)
+    PaddedVector<RVec> xPrime2_;
+    //! Velocities
+    PaddedVector<RVec> v_;
+    //! Backup for velocities (for reset)
+    PaddedVector<RVec> v0_;
+
+    //! Constraints data (type1-i1-j1-type2-i2-j2-...)
+    std::vector<int> constraints_;
+    //! Target lengths for all constraint types
+    std::vector<real> constraintsR0_;
+
+    /*! \brief
+     * Constructor for the object with all parameters and variables needed by constraints algorithms.
+     *
+     * This constructor assembles stubs for all the data structures, required to initialize
+     * and apply LINCS and SHAKE constraints. The coordinates and velocities before constraining
+     * are saved to allow for reset. The constraints data are stored for testing after constraints
+     * were applied.
+     *
+     * \param[in]  title                Human-friendly name of the system.
+     * \param[in]  numAtoms             Number of atoms in the system.
+     * \param[in]  masses               Atom masses. Size of this vector should be equal to numAtoms.
+     * \param[in]  constraints          List of constraints, organized in triples of integers.
+     *                                  First integer is the index of type for a constraint, second
+     *                                  and third are the indices of constrained atoms. The types
+     *                                  of constraints should be sequential but not necessarily
+     *                                  start from zero (which is the way they normally are in
+     *                                  GROMACS).
+     * \param[in]  constraintsR0        Target values for bond lengths for bonds of each type. The
+     *                                  size of this vector should be equal to the total number of
+     *                                  unique types in constraints vector.
+     * \param[in]  computeVirial        Whether the virial should be computed.
+     * \param[in]  compute_dHdLambda    Whether free energy should be computed.
+     * \param[in]  dHdLambdaRef         Reference value for dHdLambda.
+     * \param[in]  initialTime          Initial time.
+     * \param[in]  timestep             Timestep.
+     * \param[in]  x                    Coordinates before integration step.
+     * \param[in]  xPrime               Coordinates after integration step, but before constraining.
+     * \param[in]  v                    Velocities before constraining.
+     * \param[in]  shakeTolerance       Target tolerance for SHAKE.
+     * \param[in]  shakeUseSOR          Use successive over-relaxation method for SHAKE iterations.
+     *                                  The general formula is:
+     *                                     x_n+1 = (1-omega)*x_n + omega*f(x_n),
+     *                                  where omega = 1 if SOR is off and may be < 1 if SOR is on.
+     * \param[in]  lincsNumIterations   Number of iterations used to compute the inverse matrix.
+     * \param[in]  lincsExpansionOrder  The order for algorithm that adjusts the direction of the
+     *                                  bond after constraints are applied.
+     * \param[in]  lincsWarnAngle       The threshold value for the change in bond angle. When
+     *                                  exceeded the program will issue a warning.
+     *
+     */
+    ConstraintsTestData(const std::string&       title,
+                        int                      numAtoms,
+                        std::vector<real>        masses,
+                        std::vector<int>         constraints,
+                        std::vector<real>        constraintsR0,
+                        bool                     computeVirial,
+                        bool                     compute_dHdLambda,
+                        float                    dHdLambdaRef,
+                        real                     initialTime,
+                        real                     timestep,
+                        const std::vector<RVec>& x,
+                        const std::vector<RVec>& xPrime,
+                        const std::vector<RVec>& v,
+                        real                     shakeTolerance,
+                        gmx_bool                 shakeUseSOR,
+                        int                      lincsNumIterations,
+                        int                      lincsExpansionOrder,
+                        real                     lincsWarnAngle);
+
+    /*! \brief
+     * Reset the data structure so it can be reused.
+     *
+     * Set the coordinates and velocities back to their values before
+     * constraining. The scaled virial tensor and dHdLambda are zeroed.
+     *
+     */
+    void reset();
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_MDLIB_TESTS_CONSTRTESTDATA_H
diff --git a/src/include/gromacs/mdlib/tests/constrtestrunners.h b/src/include/gromacs/mdlib/tests/constrtestrunners.h
new file mode 100644 (file)
index 0000000..f2dc538
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief SHAKE and LINCS tests runners.
+ *
+ * Declares test runner class for constraints. The test runner abstract class is used
+ * to unify the interfaces for different constraints methods, running on different
+ * hardware.  This allows to run the same test on the same data using different
+ * implementations of the parent class, that inherit its interfaces.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ * \ingroup module_mdlib
+ */
+
+#ifndef GMX_MDLIB_TESTS_CONSTRTESTRUNNERS_H
+#define GMX_MDLIB_TESTS_CONSTRTESTRUNNERS_H
+
+#include <gtest/gtest.h>
+
+#include "testutils/test_device.h"
+
+#include "constrtestdata.h"
+
+/*
+ * GPU version of constraints is only available with CUDA and SYCL.
+ */
+#define GPU_CONSTRAINTS_SUPPORTED (GMX_GPU_CUDA || GMX_GPU_SYCL)
+
+struct t_pbc;
+
+namespace gmx
+{
+namespace test
+{
+
+/* \brief Constraints test runner interface.
+ *
+ * Wraps the actual implementation of constraints algorithm into common interface.
+ */
+class IConstraintsTestRunner
+{
+public:
+    //! Virtual destructor.
+    virtual ~IConstraintsTestRunner() {}
+    /*! \brief Abstract constraining function. Should be overriden.
+     *
+     * \param[in] testData             Test data structure.
+     * \param[in] pbc                  Periodic boundary data.
+     */
+    virtual void applyConstraints(ConstraintsTestData* testData, t_pbc pbc) = 0;
+
+    /*! \brief Get the name of the implementation.
+     *
+     * \return "<algorithm> on <device>", depending on the actual implementation used. E.g., "LINCS on #0: NVIDIA GeForce GTX 1660 SUPER".
+     */
+    virtual std::string name() = 0;
+};
+
+// Runner for the CPU implementation of SHAKE constraints algorithm.
+class ShakeConstraintsRunner : public IConstraintsTestRunner
+{
+public:
+    //! Default constructor.
+    ShakeConstraintsRunner() {}
+    /*! \brief Apply SHAKE constraints to the test data.
+     *
+     * \param[in] testData             Test data structure.
+     * \param[in] pbc                  Periodic boundary data.
+     */
+    void applyConstraints(ConstraintsTestData* testData, t_pbc pbc) override;
+    /*! \brief Get the name of the implementation.
+     *
+     * \return "SHAKE" string;
+     */
+    std::string name() override { return "SHAKE on CPU"; }
+};
+
+// Runner for the CPU implementation of LINCS constraints algorithm.
+class LincsConstraintsRunner : public IConstraintsTestRunner
+{
+public:
+    //! Default constructor.
+    LincsConstraintsRunner() {}
+    /*! \brief Apply LINCS constraints to the test data on the CPU.
+     *
+     * \param[in] testData             Test data structure.
+     * \param[in] pbc                  Periodic boundary data.
+     */
+    void applyConstraints(ConstraintsTestData* testData, t_pbc pbc) override;
+    /*! \brief Get the name of the implementation.
+     *
+     * \return "LINCS" string;
+     */
+    std::string name() override { return "LINCS on CPU"; }
+};
+
+// Runner for the GPU implementation of LINCS constraints algorithm.
+class LincsDeviceConstraintsRunner : public IConstraintsTestRunner
+{
+public:
+    /*! \brief Constructor. Keeps a copy of the hardware context.
+     *
+     * \param[in] testDevice The device hardware context to be used by the runner.
+     */
+    LincsDeviceConstraintsRunner(const TestDevice& testDevice) : testDevice_(testDevice) {}
+    /*! \brief Apply LINCS constraints to the test data on the GPU.
+     *
+     * \param[in] testData             Test data structure.
+     * \param[in] pbc                  Periodic boundary data.
+     */
+    void applyConstraints(ConstraintsTestData* testData, t_pbc pbc) override;
+    /*! \brief Get the name of the implementation.
+     *
+     * \return "LINCS_GPU" string;
+     */
+    std::string name() override { return "LINCS on " + testDevice_.description(); }
+
+private:
+    //! Test device to be used in the runner.
+    const TestDevice& testDevice_;
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_MDLIB_TESTS_CONSTRTESTRUNNERS_H
diff --git a/src/include/gromacs/mdlib/tests/leapfrogtestdata.h b/src/include/gromacs/mdlib/tests/leapfrogtestdata.h
new file mode 100644 (file)
index 0000000..56305dc
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Tests for the Leap-Frog integrator
+ *
+ * \todo Add anisotropic Parrinello-Rahman and other pressure coupling schemes
+ * \todo Add PBC handling test.
+ * \todo Reference values tests.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ * \ingroup module_mdlib
+ */
+
+#ifndef GMX_MDLIB_TESTS_LEAPFROGTESTDATA_H
+#define GMX_MDLIB_TESTS_LEAPFROGTESTDATA_H
+
+#include <vector>
+
+#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdlib/update.h"
+#include "gromacs/mdtypes/fcdata.h"
+#include "gromacs/mdtypes/group.h"
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/mdatom.h"
+#include "gromacs/mdtypes/state.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+namespace test
+{
+
+/* \brief Declares the class to accumulate the data needed for the Leap-Frog integrator tests
+ *
+ */
+class LeapFrogTestData
+{
+public:
+    //! Number of atoms in the system
+    int numAtoms_;
+    //! Integration timestep
+    real timestep_;
+
+    //! Initial coordinates
+    PaddedVector<RVec> x0_;
+    //! Current coordinates
+    PaddedVector<RVec> x_;
+    //! Coordinates after integrator update
+    PaddedVector<RVec> xPrime_;
+    //! Initial velocities
+    PaddedVector<RVec> v0_;
+    //! Current velocities
+    PaddedVector<RVec> v_;
+    //! External forces
+    PaddedVector<RVec> f_;
+    //! Inverse masses of the particles
+    PaddedVector<real> inverseMasses_;
+    //! Inverse masses of the particles per dimension
+    PaddedVector<RVec> inverseMassesPerDim_;
+
+    //! MD atoms structure in which inverse masses will be passed to the integrator
+    t_mdatoms mdAtoms_;
+    //! Input record (to get integrator type, temperature and pressure coupling)
+    t_inputrec inputRecord_;
+    //! System state
+    t_state state_;
+    //! Force calculation data
+    t_fcdata forceCalculationData_;
+    //! Kinetic energy data (to disable non-equilibrium MD integration)
+    gmx_ekindata_t kineticEnergyData_;
+    //! Update data
+    std::unique_ptr<Update> update_;
+
+    //! Number of temperature coupling groups
+    int numTCoupleGroups_;
+
+    //! If the pressure coupling is enabled
+    bool doPressureCouple_;
+    //! Period between pressure coupling steps
+    float dtPressureCouple_;
+    //! Matrix for Parrinello-Rahman velocity scaling
+    matrix velocityScalingMatrix_;
+
+    /*! \brief Constructor.
+     *
+     * \param[in]  numAtoms          Number of atoms in the system
+     * \param[in]  timestep          Integration timestep
+     * \param[in]  v0                Initial velocity (same for all particles)
+     * \param[in]  f0                External constant force, acting on all particles
+     * \param[in]  numTCoupleGroups  Number of temperature coupling groups (zero for no temperature coupling)
+     * \param[in]  nstpcouple        Number of steps between pressure coupling steps (zero for no pressure coupling)
+     */
+    LeapFrogTestData(int numAtoms, real timestep, const rvec v0, const rvec f0, int numTCoupleGroups, int nstpcouple);
+
+    ~LeapFrogTestData();
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_MDLIB_TESTS_LEAPFROGTESTDATA_H
diff --git a/src/include/gromacs/mdlib/tests/leapfrogtestrunners.h b/src/include/gromacs/mdlib/tests/leapfrogtestrunners.h
new file mode 100644 (file)
index 0000000..117a2d5
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Leap-Frog tests runners.
+ *
+ * Declares test runner class for Leap-Frog algorithm. The test runners abstract
+ * class is used to unify the interfaces for CPU and GPU implementations of the
+ * Leap-Frog algorithm. This allows to run the same test on the same data using
+ * different implementations of the parent class, that inherit its interfaces.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_TESTS_LEAPFROGTESTRUNNERS_H
+#define GMX_MDLIB_TESTS_LEAPFROGTESTRUNNERS_H
+
+#include "config.h"
+
+#include "gromacs/math/vec.h"
+
+#include "testutils/test_device.h"
+
+#include "leapfrogtestdata.h"
+
+/*
+ * LeapFrog is available with CUDA and SYCL.
+ */
+#define GPU_LEAPFROG_SUPPORTED (GMX_GPU_CUDA || GMX_GPU_SYCL)
+
+namespace gmx
+{
+namespace test
+{
+
+/* \brief LeapFrog integrator test runner interface.
+ *
+ * Wraps the actual implementation of LeapFrog algorithm into common interface.
+ */
+class ILeapFrogTestRunner
+{
+public:
+    //! Virtual destructor
+    virtual ~ILeapFrogTestRunner() {}
+    /*! \brief The abstract function that runs the integrator for a given number of steps.
+     *
+     * Should be overriden.
+     *
+     * \param[in]     testData  Data needed for the integrator
+     * \param[in]     numSteps  Total number of steps to run integration for.
+     */
+    virtual void integrate(LeapFrogTestData* testData, int numSteps) = 0;
+
+    /*! \brief Get the human-friendly description of hardware used by the runner.
+     *
+     * \returns String with description of the hardware.
+     */
+    virtual std::string hardwareDescription() = 0;
+};
+
+// Runner for the CPU version of Leap-Frog.
+class LeapFrogHostTestRunner : public ILeapFrogTestRunner
+{
+public:
+    //! Constructor.
+    LeapFrogHostTestRunner() {}
+    /*! \brief Integrate on the CPU for a given number of steps.
+     *
+     * Will update the test data with the integration result.
+     *
+     * \param[in]     testData  Data needed for the integrator
+     * \param[in]     numSteps  Total number of steps to run integration for.
+     */
+    void integrate(LeapFrogTestData* testData, int numSteps) override;
+    /*! \brief Get the hardware description
+     *
+     * \returns "CPU" string.
+     */
+    std::string hardwareDescription() override { return "CPU"; }
+};
+
+// Runner for the CPU version of Leap-Frog.
+class LeapFrogDeviceTestRunner : public ILeapFrogTestRunner
+{
+public:
+    /*! \brief Constructor. Keeps a copy of the hardware context.
+     *
+     * \param[in] testDevice The device hardware context to be used by the runner.
+     */
+    LeapFrogDeviceTestRunner(const TestDevice& testDevice) : testDevice_(testDevice) {}
+    /*! \brief Integrate on the GPU for a given number of steps.
+     *
+     * Copies data from CPU to GPU, integrates the equation of motion
+     * for requested number of steps using Leap-Frog algorithm, copies
+     * the result back.
+     *
+     * \param[in]     testData  Data needed for the integrator
+     * \param[in]     numSteps  Total number of steps to run integration for.
+     */
+    void integrate(LeapFrogTestData* testData, int numSteps) override;
+    /*! \brief Get the hardware description
+     *
+     * \returns A string with GPU description.
+     */
+    std::string hardwareDescription() override { return testDevice_.description(); }
+
+private:
+    //! Test device to be used in the runner.
+    const TestDevice& testDevice_;
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_MDLIB_TESTS_LEAPFROGTESTRUNNERS_H
diff --git a/src/include/gromacs/mdlib/tests/settletestdata.h b/src/include/gromacs/mdlib/tests/settletestdata.h
new file mode 100644 (file)
index 0000000..45d1c94
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief SETTLE tests header.
+ *
+ * Declares the class that accumulates SETTLE test data.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_TESTS_SETTLETESTDATA_H
+#define GMX_MDLIB_TESTS_SETTLETESTDATA_H
+
+#include "gromacs/math/paddedvector.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/topology/topology.h"
+
+namespace gmx
+{
+namespace test
+{
+
+/* \brief SETTLE test data object.
+ *
+ * Initializes and stores data necessary to run SETTLE constraints, including
+ * atom coordinates and velocities, virial, system topology and some parameters.
+ */
+class SettleTestData
+{
+public:
+    //! Number of settles
+    int numSettles_;
+    //! Initial (undisturbed) positions
+    PaddedVector<gmx::RVec> x_;
+    //! Updated water atom positions to constrain
+    PaddedVector<gmx::RVec> xPrime_;
+    //! Water atom velocities to constrain
+    PaddedVector<gmx::RVec> v_;
+    //! SETTLE virial
+    tensor virial_ = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } };
+
+    //! Global topology
+    gmx_mtop_t mtop_;
+    //! Number of atoms
+    int numAtoms_ = 0;
+    //! Atom masses
+    std::vector<real> masses_;
+    //! Reciprocal masses
+    std::vector<real> inverseMasses_;
+    //! Local topology
+    std::unique_ptr<InteractionDefinitions> idef_;
+
+    //! Inverse timestep
+    const real reciprocalTimeStep_ = 1.0 / 0.002;
+    //! Target distance between oxygen and hydrogens
+    const real dOH_ = 0.09572;
+    //! Target distance between hydrogens
+    const real dHH_ = 0.15139;
+    //! Mass of oxygen atom
+    const real oxygenMass_ = 15.9994;
+    //! Mass of hydrogen atom
+    const real hydrogenMass_ = 1.008;
+
+    //! Stride for array with atom indexes
+    const int atomsPerSettle_ = NRAL(F_SETTLE);
+
+    /*! \brief Construct the object and initialize the data structures.
+     *
+     * \param[in] numSettles   Number of SETTLE constraints in the system.
+     *
+     */
+    SettleTestData(int numSettles);
+
+    ~SettleTestData();
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_MDLIB_TESTS_SETTLETESTDATA_H
diff --git a/src/include/gromacs/mdlib/tests/settletestrunners.h b/src/include/gromacs/mdlib/tests/settletestrunners.h
new file mode 100644 (file)
index 0000000..6207e88
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief SETTLE tests runners.
+ *
+ * Declares test runner class for SETTLE algorithm. The test runners abstract
+ * class is used to unify the interfaces for CPU and GPU implementations of the
+ * SETTLE algorithm. This allows to run the same test on the same data using
+ * different implementations of the parent class, that inherit its interfaces.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_TESTS_SETTLETESTRUNNERS_H
+#define GMX_MDLIB_TESTS_SETTLETESTRUNNERS_H
+
+#include <gtest/gtest.h>
+
+#include "testutils/test_device.h"
+
+#include "settletestdata.h"
+
+/*
+ * GPU version of SETTLE is only available with CUDA.
+ */
+#define GPU_SETTLE_SUPPORTED (GMX_GPU_CUDA || GMX_GPU_SYCL)
+
+struct t_pbc;
+
+namespace gmx
+{
+namespace test
+{
+
+/* \brief SETTLE test runner interface.
+ *
+ * Wraps the actual implementation of SETTLE into common interface.
+ */
+class ISettleTestRunner
+{
+public:
+    //! Virtual destructor.
+    virtual ~ISettleTestRunner() {}
+
+    /*! \brief Apply SETTLE using CPU version of the algorithm
+     *
+     * Initializes SETTLE object, applies algorithm, destroys the object. The coordinates, velocities
+     * and virial are updated in the testData object.
+     *
+     * \param[in,out] testData          An object, containing all the data structures needed by SETTLE.
+     * \param[in]     pbc               Periodic boundary setup.
+     * \param[in]     updateVelocities  If the velocities should be updated.
+     * \param[in]     calcVirial        If the virial should be computed.
+     * \param[in]     testDescription   Brief description that will be printed in case of test failure.
+     */
+    virtual void applySettle(SettleTestData*    testData,
+                             t_pbc              pbc,
+                             bool               updateVelocities,
+                             bool               calcVirial,
+                             const std::string& testDescription) = 0;
+    /*! \brief Get the hardware description
+     *
+     * \returns A string, describing hardware used by the runner.
+     */
+    virtual std::string hardwareDescription() = 0;
+};
+
+// Runner for the CPU implementation of SETTLE.
+class SettleHostTestRunner : public ISettleTestRunner
+{
+public:
+    //! Default constructor.
+    SettleHostTestRunner() {}
+    /*! \brief Apply SETTLE using CPU version of the algorithm
+     *
+     * Initializes SETTLE object, applies algorithm, destroys the object. The coordinates, velocities
+     * and virial are updated in the testData object.
+     *
+     * \param[in,out] testData          An object, containing all the data structures needed by SETTLE.
+     * \param[in]     pbc               Periodic boundary setup.
+     * \param[in]     updateVelocities  If the velocities should be updated.
+     * \param[in]     calcVirial        If the virial should be computed.
+     * \param[in]     testDescription   Brief description that will be printed in case of test failure.
+     */
+    void applySettle(SettleTestData*    testData,
+                     t_pbc              pbc,
+                     bool               updateVelocities,
+                     bool               calcVirial,
+                     const std::string& testDescription) override;
+    /*! \brief Get the hardware description
+     *
+     * \returns "CPU" string.
+     */
+    std::string hardwareDescription() override { return "CPU"; }
+};
+
+// Runner for the GPU implementation of SETTLE.
+class SettleDeviceTestRunner : public ISettleTestRunner
+{
+public:
+    /*! \brief Constructor. Keeps a copy of the hardware context.
+     *
+     * \param[in] testDevice The device hardware context to be used by the runner.
+     */
+    SettleDeviceTestRunner(const TestDevice& testDevice) : testDevice_(testDevice) {}
+    /*! \brief Apply SETTLE using GPU version of the algorithm
+     *
+     * Initializes SETTLE object, copied data to the GPU, applies algorithm, copies the data back,
+     * destroys the object. The coordinates, velocities and virial are updated in the testData object.
+     *
+     * \param[in,out] testData          An object, containing all the data structures needed by SETTLE.
+     * \param[in]     pbc               Periodic boundary setup.
+     * \param[in]     updateVelocities  If the velocities should be updated.
+     * \param[in]     calcVirial        If the virial should be computed.
+     * \param[in]     testDescription   Brief description that will be printed in case of test failure.
+     */
+    void applySettle(SettleTestData*    testData,
+                     t_pbc              pbc,
+                     bool               updateVelocities,
+                     bool               calcVirial,
+                     const std::string& testDescription) override;
+    /*! \brief Get the hardware description
+     *
+     * \returns A string with GPU description.
+     */
+    std::string hardwareDescription() override { return testDevice_.description(); }
+
+private:
+    //! Test test device to be used in the runner.
+    const TestDevice& testDevice_;
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_MDLIB_TESTS_SETTLETESTRUNNERS_H
diff --git a/src/include/gromacs/mdlib/tests/watersystem.h b/src/include/gromacs/mdlib/tests/watersystem.h
new file mode 100644 (file)
index 0000000..bdab3ff
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#include "gromacs/math/vectypes.h"
+
+namespace gmx
+{
+
+namespace test
+{
+
+//! Database of 51 water atom input positions (taken from spc216.gro) for use as test inputs.
+static const std::array<gmx::RVec, 51> c_waterPositions(
+        { { { .130, -.041, -.291 },  { .120, -.056, -.192 },  { .044, -.005, -.327 },
+            { -.854, -.406, .477 },  { -.900, -.334, .425 },  { -.858, -.386, .575 },
+            { .351, -.061, .853 },   { .401, -.147, .859 },   { .416, .016, .850 },
+            { -.067, -.796, .873 },  { -.129, -.811, .797 },  { -.119, -.785, .958 },
+            { -.635, -.312, -.356 }, { -.629, -.389, -.292 }, { -.687, -.338, -.436 },
+            { .321, -.919, .242 },   { .403, -.880, .200 },   { .294, -1.001, .193 },
+            { -.404, .735, .728 },   { -.409, .670, .803 },   { -.324, .794, .741 },
+            { .461, -.596, -.135 },  { .411, -.595, -.221 },  { .398, -.614, -.059 },
+            { -.751, -.086, .237 },  { -.811, -.148, .287 },  { -.720, -.130, .152 },
+            { .202, .285, -.364 },   { .122, .345, -.377 },   { .192, .236, -.278 },
+            { -.230, -.485, .081 },  { -.262, -.391, .071 },  { -.306, -.548, .069 },
+            { .464, -.119, .323 },   { .497, -.080, .409 },   { .540, -.126, .258 },
+            { -.462, .107, .426 },   { -.486, .070, .336 },   { -.363, .123, .430 },
+            { .249, -.077, -.621 },  { .306, -.142, -.571 },  { .233, -.110, -.714 },
+            { -.922, -.164, .904 },  { -.842, -.221, .925 },  { -.971, -.204, .827 },
+            { .382, .700, .480 },    { .427, .610, .477 },    { .288, .689, .513 },
+            { .781, .264, -.113 },   { .848, .203, -.070 },   { .708, .283, -.048 } } });
+
+} // namespace test
+
+} // namespace gmx
diff --git a/src/include/gromacs/mdlib/tgroup.h b/src/include/gromacs/mdlib/tgroup.h
new file mode 100644 (file)
index 0000000..9a62f8f
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_TGROUP_H
+#define GMX_MDLIB_TGROUP_H
+
+#include "gromacs/utility/real.h"
+
+class gmx_ekindata_t;
+struct t_grpopts;
+
+real sum_ekin(const t_grpopts* opts, gmx_ekindata_t* ekind, real* dekindlambda, bool bEkinFullStep, bool bScaleEkin);
+/* Sum the group ekins into total ekin and calc temp per group,
+ * return total temperature.
+ */
+
+#endif
diff --git a/src/include/gromacs/mdlib/trajectory_writing.h b/src/include/gromacs/mdlib/trajectory_writing.h
new file mode 100644 (file)
index 0000000..0a34f6e
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_TRAJECTORY_WRITING_H
+#define GMX_MDLIB_TRAJECTORY_WRITING_H
+
+#include <stdio.h>
+
+#include "gromacs/mdlib/mdoutf.h"
+
+class gmx_ekindata_t;
+struct gmx_mtop_t;
+struct ObservablesHistory;
+struct t_commrec;
+struct t_filenm;
+struct t_forcerec;
+
+namespace gmx
+{
+class EnergyOutput;
+}
+
+/*! \brief Wrapper routine for writing trajectories during mdrun
+ *
+ * This routine does communication (e.g. collecting distributed coordinates)
+ */
+void do_md_trajectory_writing(FILE*                          fplog,
+                              struct t_commrec*              cr,
+                              int                            nfile,
+                              const t_filenm                 fnm[],
+                              int64_t                        step,
+                              int64_t                        step_rel,
+                              double                         t,
+                              const t_inputrec*              ir,
+                              t_state*                       state,
+                              t_state*                       state_global,
+                              ObservablesHistory*            observablesHistory,
+                              const gmx_mtop_t&              top_global,
+                              t_forcerec*                    fr,
+                              gmx_mdoutf_t                   outf,
+                              const gmx::EnergyOutput&       energyOutput,
+                              gmx_ekindata_t*                ekind,
+                              gmx::ArrayRef<const gmx::RVec> f,
+                              gmx_bool                       bCPT,
+                              gmx_bool                       bRerunMD,
+                              gmx_bool                       bLastStep,
+                              gmx_bool                       bDoConfOut,
+                              gmx_bool                       bSumEkinhOld);
+
+
+#endif
diff --git a/src/include/gromacs/mdlib/update.h b/src/include/gromacs/mdlib/update.h
new file mode 100644 (file)
index 0000000..a3c4844
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_UPDATE_H
+#define GMX_MDLIB_UPDATE_H
+
+#include <memory>
+
+#include "gromacs/math/paddedvector.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/timing/wallcycle.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+class ekinstate_t;
+class gmx_ekindata_t;
+struct gmx_enerdata_t;
+enum class PbcType;
+struct t_fcdata;
+struct t_graph;
+struct t_grpopts;
+struct t_inputrec;
+struct t_nrnb;
+class t_state;
+enum class ParticleType;
+
+namespace gmx
+{
+class BoxDeformation;
+class Constraints;
+
+
+/*! \libinternal
+ * \brief Contains data for update phase */
+class Update
+{
+public:
+    /*! \brief Constructor
+     *
+     * \param[in] inputRecord     Input record, used to construct SD object.
+     * \param[in] boxDeformation  Periodic box deformation object.
+     */
+    Update(const t_inputrec& inputRecord, BoxDeformation* boxDeformation);
+    //! Destructor
+    ~Update();
+    /*! \brief Get the pointer to updated coordinates
+     *
+     * Update saves the updated coordinates into separate buffer, so that constraints will have
+     * access to both updated and not update coordinates. For that, update owns a separate buffer.
+     * See finish_update(...) for details.
+     *
+     * \returns The pointer to the intermediate coordinates buffer.
+     */
+    PaddedVector<gmx::RVec>* xp();
+    /*!\brief Getter to local copy of box deformation class.
+     *
+     * \returns handle to box deformation class
+     */
+    BoxDeformation* deform() const;
+    /*! \brief Sets data that changes only at domain decomposition time.
+     *
+     * \param[in] numAtoms  Updated number of atoms.
+     * \param[in] cFREEZE   Group index for freezing
+     * \param[in] cTC       Group index for center of mass motion removal
+     */
+    void updateAfterPartition(int                                 numAtoms,
+                              gmx::ArrayRef<const unsigned short> cFREEZE,
+                              gmx::ArrayRef<const unsigned short> cTC);
+
+    /*! \brief Perform numerical integration step.
+     *
+     * Selects the appropriate integrator, based on the input record and performs a numerical integration step.
+     *
+     * \param[in]  inputRecord               Input record.
+     * \param[in]  step                      Current timestep.
+     * \param[in]  homenr                    The number of atoms on this processor.
+     * \param[in]  havePartiallyFrozenAtoms  Whether atoms are frozen along 1 or 2 (not 3) dimensions?
+     * \param[in]  ptype                     The list of particle types.
+     * \param[in]  invMass                   Inverse atomic mass per atom, 0 for vsites and shells.
+     * \param[in]  invMassPerDim             Inverse atomic mass per atom and dimension, 0 for vsites, shells and frozen dimensions
+     * \param[in]  state                     System state object.
+     * \param[in]  f                         Buffer with atomic forces for home particles.
+     * \param[in]  fcdata                    Force calculation data to update distance and orientation restraints.
+     * \param[in]  ekind                     Kinetic energy data (for temperature coupling, energy groups, etc.).
+     * \param[in]  M                         Parrinello-Rahman velocity scaling matrix.
+     * \param[in]  updatePart                What should be updated, coordinates or velocities. This enum only used in VV integrator.
+     * \param[in]  cr                        Comunication record  (Old comment: these shouldn't be here -- need to think about it).
+     * \param[in]  haveConstraints           If the system has constraints.
+     */
+    void update_coords(const t_inputrec&                                inputRecord,
+                       int64_t                                          step,
+                       int                                              homenr,
+                       bool                                             havePartiallyFrozenAtoms,
+                       gmx::ArrayRef<const ParticleType>                ptype,
+                       gmx::ArrayRef<const real>                        invMass,
+                       gmx::ArrayRef<const rvec>                        invMassPerDim,
+                       t_state*                                         state,
+                       const gmx::ArrayRefWithPadding<const gmx::RVec>& f,
+                       t_fcdata*                                        fcdata,
+                       const gmx_ekindata_t*                            ekind,
+                       const matrix                                     M,
+                       int                                              updatePart,
+                       const t_commrec*                                 cr,
+                       bool                                             haveConstraints);
+
+    /*! \brief Finalize the coordinate update.
+     *
+     * Copy the updated coordinates to the main coordinates buffer for the atoms that are not frozen.
+     *
+     * \param[in]  inputRecord      Input record.
+     * \param[in]  havePartiallyFrozenAtoms  Whether atoms are frozen along 1 or 2 (not 3) dimensions?
+     * \param[in]  homenr                    The number of atoms on this processor.
+     * \param[in]  state            System state object.
+     * \param[in]  wcycle           Wall-clock cycle counter.
+     * \param[in]  haveConstraints  If the system has constraints.
+     */
+    void finish_update(const t_inputrec& inputRecord,
+                       bool              havePartiallyFrozenAtoms,
+                       int               homenr,
+                       t_state*          state,
+                       gmx_wallcycle*    wcycle,
+                       bool              haveConstraints);
+
+    /*! \brief Secong part of the SD integrator.
+     *
+     * The first part of integration is performed in the update_coords(...) method.
+     *
+     * \param[in]  inputRecord  Input record.
+     * \param[in]  step         Current timestep.
+     * \param[in]  dvdlambda    Free energy derivative. Contribution to be added to
+     *                          the bonded interactions.
+     * \param[in]  homenr       The number of atoms on this processor.
+     * \param[in]  ptype        The list of particle types.
+     * \param[in]  invMass      Inverse atomic mass per atom, 0 for vsites and shells.
+     * \param[in]  state        System state object.
+     * \param[in]  cr           Comunication record.
+     * \param[in]  nrnb         Cycle counters.
+     * \param[in]  wcycle       Wall-clock cycle counter.
+     * \param[in]  constr       Constraints object. The constraints are applied
+     *                          on coordinates after update.
+     * \param[in]  do_log       If this is logging step.
+     * \param[in]  do_ene       If this is an energy evaluation step.
+     */
+    void update_sd_second_half(const t_inputrec&                 inputRecord,
+                               int64_t                           step,
+                               real*                             dvdlambda,
+                               int                               homenr,
+                               gmx::ArrayRef<const ParticleType> ptype,
+                               gmx::ArrayRef<const real>         invMass,
+                               t_state*                          state,
+                               const t_commrec*                  cr,
+                               t_nrnb*                           nrnb,
+                               gmx_wallcycle*                    wcycle,
+                               gmx::Constraints*                 constr,
+                               bool                              do_log,
+                               bool                              do_ene);
+
+    /*! \brief Performs a leap-frog update without updating \p state so the constrain virial
+     * can be computed.
+     */
+    void update_for_constraint_virial(const t_inputrec&         inputRecord,
+                                      int                       homenr,
+                                      bool                      havePartiallyFrozenAtoms,
+                                      gmx::ArrayRef<const real> invmass,
+                                      gmx::ArrayRef<const rvec> invMassPerDim,
+                                      const t_state&            state,
+                                      const gmx::ArrayRefWithPadding<const gmx::RVec>& f,
+                                      const gmx_ekindata_t&                            ekind);
+
+    /*! \brief Update pre-computed constants that depend on the reference temperature for coupling.
+     *
+     * This could change e.g. in simulated annealing.
+     *
+     * \param[in]  inputRecord  Input record.
+     */
+    void update_temperature_constants(const t_inputrec& inputRecord);
+
+    /*!\brief Getter for the list of the randomize groups.
+     *
+     *  Needed for Andersen temperature control.
+     *
+     * \returns Reference to the groups from the SD data object.
+     */
+    const std::vector<bool>& getAndersenRandomizeGroup() const;
+    /*!\brief Getter for the list of the Boltzmann factors.
+     *
+     *  Needed for Andersen temperature control.
+     *
+     * \returns Reference to the Boltzmann factors from the SD data object.
+     */
+    const std::vector<real>& getBoltzmanFactor() const;
+
+private:
+    //! Implementation type.
+    class Impl;
+    //! Implementation object.
+    std::unique_ptr<Impl> impl_;
+};
+
+}; // namespace gmx
+
+/*
+ * Compute the partial kinetic energy for home particles;
+ * will be accumulated in the calling routine.
+ * The tensor is
+ *
+ * Ekin = SUM(i) 0.5 m[i] v[i] (x) v[i]
+ *
+ *     use v[i] = v[i] - u[i] when calculating temperature
+ *
+ * u must be accumulated already.
+ *
+ * Now also computes the contribution of the kinetic energy to the
+ * free energy
+ *
+ */
+
+
+void init_ekinstate(ekinstate_t* ekinstate, const t_inputrec* ir);
+
+void update_ekinstate(ekinstate_t* ekinstate, const gmx_ekindata_t* ekind);
+
+/*! \brief Restores data from \p ekinstate to \p ekind, then broadcasts it
+   to the rest of the simulation */
+void restore_ekinstate_from_state(const t_commrec* cr, gmx_ekindata_t* ekind, const ekinstate_t* ekinstate);
+
+/*! \brief Computes the atom range for a thread to operate on, ensuring SIMD aligned ranges
+ *
+ * \param[in]  numThreads   The number of threads to divide atoms over
+ * \param[in]  threadIndex  The thread to get the range for
+ * \param[in]  numAtoms     The total number of atoms (on this rank)
+ * \param[out] startAtom    The start of the atom range
+ * \param[out] endAtom      The end of the atom range, note that this is in general not a multiple of the SIMD width
+ */
+void getThreadAtomRange(int numThreads, int threadIndex, int numAtoms, int* startAtom, int* endAtom);
+
+#endif
diff --git a/src/include/gromacs/mdlib/update_constrain_gpu.h b/src/include/gromacs/mdlib/update_constrain_gpu.h
new file mode 100644 (file)
index 0000000..d779a6a
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declaration of high-level functions of GPU implementation of update and constrain class.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+#ifndef GMX_MDLIB_UPDATE_CONSTRAIN_GPU_H
+#define GMX_MDLIB_UPDATE_CONSTRAIN_GPU_H
+
+#include <memory>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/mdtypes/group.h"
+#include "gromacs/timing/wallcycle.h"
+#include "gromacs/utility/arrayref.h"
+
+class DeviceContext;
+class DeviceStream;
+class GpuEventSynchronizer;
+struct gmx_mtop_t;
+enum class PbcType : int;
+class InteractionDefinitions;
+struct t_inputrec;
+struct t_mdatoms;
+struct t_pbc;
+
+namespace gmx
+{
+
+class UpdateConstrainGpu
+{
+
+public:
+    /*! \brief Create Update-Constrain object.
+     *
+     * The constructor is given a non-nullptr \p deviceStream, in which all the update and constrain
+     * routines are executed.
+     *
+     * \param[in] ir                  Input record data: LINCS takes number of iterations and order of
+     *                                projection from it.
+     * \param[in] mtop                Topology of the system: SETTLE gets the masses for O and H atoms
+     *                                and target O-H and H-H distances from this object.
+     * \param[in] numTempScaleValues  Number of temperature scaling groups. Zero for no temperature scaling.
+     * \param[in] deviceContext       GPU device context.
+     * \param[in] deviceStream        GPU stream to use.
+     * \param[in] wcycle              The wallclock counter
+     */
+    UpdateConstrainGpu(const t_inputrec&    ir,
+                       const gmx_mtop_t&    mtop,
+                       int                  numTempScaleValues,
+                       const DeviceContext& deviceContext,
+                       const DeviceStream&  deviceStream,
+                       gmx_wallcycle*       wcycle);
+
+    ~UpdateConstrainGpu();
+
+    /*! \brief Integrate
+     *
+     * This will extract temperature scaling factors from tcstat, transform them into the plain
+     * array and call the normal integrate method.
+     *
+     * \param[in]  fReadyOnDevice           Event synchronizer indicating that the forces are
+     *                                      ready in the device memory.
+     * \param[in]  dt                       Timestep.
+     * \param[in]  updateVelocities         If the velocities should be constrained.
+     * \param[in]  computeVirial            If virial should be updated.
+     * \param[out] virial                   Place to save virial tensor.
+     * \param[in]  doTemperatureScaling     If velocities should be scaled for temperature coupling.
+     * \param[in]  tcstat                   Temperature coupling data.
+     * \param[in]  doParrinelloRahman       If current step is a Parrinello-Rahman pressure coupling step.
+     * \param[in]  dtPressureCouple         Period between pressure coupling steps.
+     * \param[in]  prVelocityScalingMatrix  Parrinello-Rahman velocity scaling matrix.
+     */
+    void integrate(GpuEventSynchronizer*             fReadyOnDevice,
+                   real                              dt,
+                   bool                              updateVelocities,
+                   bool                              computeVirial,
+                   tensor                            virial,
+                   bool                              doTemperatureScaling,
+                   gmx::ArrayRef<const t_grp_tcstat> tcstat,
+                   bool                              doParrinelloRahman,
+                   float                             dtPressureCouple,
+                   const matrix                      prVelocityScalingMatrix);
+
+    /*! \brief Scale coordinates on the GPU for the pressure coupling.
+     *
+     * After pressure coupling step, the box size may change. Hence, the coordinates should be
+     * scaled so that all the particles fit in the new box.
+     *
+     * \param[in] scalingMatrix Coordinates scaling matrix.
+     */
+    void scaleCoordinates(const matrix scalingMatrix);
+
+    /*! \brief Scale velocities on the GPU for the pressure coupling.
+     *
+     * After pressure coupling step, the box size may change. In the C-Rescale algorithm, velocities should be scaled.
+     *
+     * \param[in] scalingMatrix Velocities scaling matrix.
+     */
+    void scaleVelocities(const matrix scalingMatrix);
+
+    /*! \brief Set the pointers and update data-structures (e.g. after NB search step).
+     *
+     * \param[in,out]  d_x                 Device buffer with coordinates.
+     * \param[in,out]  d_v                 Device buffer with velocities.
+     * \param[in]      d_f                 Device buffer with forces.
+     * \param[in]      idef                System topology
+     * \param[in]      md                  Atoms data.
+     */
+    void set(DeviceBuffer<RVec>            d_x,
+             DeviceBuffer<RVec>            d_v,
+             DeviceBuffer<RVec>            d_f,
+             const InteractionDefinitions& idef,
+             const t_mdatoms&              md);
+
+    /*! \brief
+     * Update PBC data.
+     *
+     * Converts PBC data from t_pbc into the PbcAiuc format and stores the latter.
+     *
+     * \param[in] pbcType The type of the periodic boundary (Xyz, NO, XY or Screw).
+     * \param[in] box     The periodic boundary box matrix.
+     */
+    void setPbc(PbcType pbcType, const matrix box);
+
+    /*! \brief Return the synchronizer associated with the event that indicates
+     * that the coordinates are ready on the device.
+     */
+    GpuEventSynchronizer* xUpdatedOnDeviceEvent();
+
+    /*! \brief
+     * Returns whether the maximum number of coupled constraints is supported
+     * by the GPU LINCS code.
+     *
+     * \param[in] mtop The molecular topology
+     */
+    static bool isNumCoupledConstraintsSupported(const gmx_mtop_t& mtop);
+
+    /*! \brief
+     * Returns whether the constraints are supported by the GPU code.
+     *
+     * Currently true for CUDA, false for others.
+     */
+    static bool areConstraintsSupported();
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDLIB_UPDATE_CONSTRAIN_GPU_H
diff --git a/src/include/gromacs/mdlib/update_constrain_gpu_impl.h b/src/include/gromacs/mdlib/update_constrain_gpu_impl.h
new file mode 100644 (file)
index 0000000..bbba2b3
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares GPU implementation class for update and constraints.
+ *
+ * This header file is needed to include from both the device-side
+ * kernels file, and the host-side management code.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_UPDATE_CONSTRAIN_GPU_IMPL_H
+#define GMX_MDLIB_UPDATE_CONSTRAIN_GPU_IMPL_H
+
+#include "gmxpre.h"
+
+#include "config.h"
+
+#include "gromacs/gpu_utils/gpueventsynchronizer.h"
+#include "gromacs/mdlib/leapfrog_gpu.h"
+#include "gromacs/mdlib/lincs_gpu.h"
+#include "gromacs/mdlib/settle_gpu.h"
+#include "gromacs/mdlib/update_constrain_gpu.h"
+#include "gromacs/mdtypes/inputrec.h"
+
+class GpuEventSynchronizer;
+
+namespace gmx
+{
+
+/*! \internal \brief Class with interfaces and data for GPU version of Update-Constraint. */
+class UpdateConstrainGpu::Impl
+{
+
+public:
+    /*! \brief Create Update-Constrain object.
+     *
+     * The constructor is given a non-nullptr \p deviceStream, in which all the update and constrain
+     * routines are executed.
+     *
+     * \param[in] ir                  Input record data: LINCS takes number of iterations and order of
+     *                                projection from it.
+     * \param[in] mtop                Topology of the system: SETTLE gets the masses for O and H atoms
+     *                                and target O-H and H-H distances from this object.
+     * \param[in] numTempScaleValues  Number of temperature scaling groups. Set zero for no temperature coupling.
+     * \param[in] deviceContext       GPU device context.
+     * \param[in] deviceStream        GPU stream to use.
+     * \param[in] wcycle              The wallclock counter
+     */
+    Impl(const t_inputrec&    ir,
+         const gmx_mtop_t&    mtop,
+         int                  numTempScaleValues,
+         const DeviceContext& deviceContext,
+         const DeviceStream&  deviceStream,
+         gmx_wallcycle*       wcycle);
+
+    ~Impl();
+
+    /*! \brief Integrate
+     *
+     * Integrates the equation of motion using Leap-Frog algorithm and applies
+     * LINCS and SETTLE constraints.
+     * If computeVirial is true, constraints virial is written at the provided pointer.
+     * doTempCouple should be true if:
+     *   1. The temperature coupling is enabled.
+     *   2. This is the temperature coupling step.
+     * Parameters virial/lambdas can be nullptr if computeVirial/doTempCouple are false.
+     *
+     * \param[in]  fReadyOnDevice           Event synchronizer indicating that the forces are ready in
+     *                                      the device memory.
+     * \param[in]  dt                       Timestep.
+     * \param[in]  updateVelocities         If the velocities should be constrained.
+     * \param[in]  computeVirial            If virial should be updated.
+     * \param[out] virial                   Place to save virial tensor.
+     * \param[in]  doTemperatureScaling     If velocities should be scaled for temperature coupling.
+     * \param[in]  tcstat                   Temperature coupling data.
+     * \param[in]  doParrinelloRahman       If current step is a Parrinello-Rahman pressure coupling step.
+     * \param[in]  dtPressureCouple         Period between pressure coupling steps.
+     * \param[in]  prVelocityScalingMatrix  Parrinello-Rahman velocity scaling matrix.
+     */
+    void integrate(GpuEventSynchronizer*             fReadyOnDevice,
+                   real                              dt,
+                   bool                              updateVelocities,
+                   bool                              computeVirial,
+                   tensor                            virial,
+                   bool                              doTemperatureScaling,
+                   gmx::ArrayRef<const t_grp_tcstat> tcstat,
+                   bool                              doParrinelloRahman,
+                   float                             dtPressureCouple,
+                   const matrix                      prVelocityScalingMatrix);
+
+    /*! \brief Scale coordinates on the GPU for the pressure coupling.
+     *
+     * After pressure coupling step, the box size may change. Hence, the coordinates should be
+     * scaled so that all the particles fit in the new box.
+     *
+     * \param[in] scalingMatrix Coordinates scaling matrix.
+     */
+    void scaleCoordinates(const matrix scalingMatrix);
+
+    /*! \brief Scale velocities on the GPU for the pressure coupling.
+     *
+     * After pressure coupling step, the box size may change. In the C-Rescale algorithm, velocities should be scaled.
+     *
+     * \param[in] scalingMatrix Velocities scaling matrix.
+     */
+    void scaleVelocities(const matrix scalingMatrix);
+
+    /*! \brief Set the pointers and update data-structures (e.g. after NB search step).
+     *
+     * \param[in,out]  d_x            Device buffer with coordinates.
+     * \param[in,out]  d_v            Device buffer with velocities.
+     * \param[in]      d_f            Device buffer with forces.
+     * \param[in] idef                System topology
+     * \param[in] md                  Atoms data.
+     */
+    void set(DeviceBuffer<Float3>          d_x,
+             DeviceBuffer<Float3>          d_v,
+             DeviceBuffer<Float3>          d_f,
+             const InteractionDefinitions& idef,
+             const t_mdatoms&              md);
+
+    /*! \brief
+     * Update PBC data.
+     *
+     * Converts PBC data from t_pbc into the PbcAiuc format and stores the latter.
+     *
+     * \param[in] pbcType The type of the periodic boundary.
+     * \param[in] box     The periodic boundary box matrix.
+     */
+    void setPbc(PbcType pbcType, const matrix box);
+
+    /*! \brief Return the synchronizer associated with the event indicated that the coordinates are ready on the device.
+     */
+    GpuEventSynchronizer* xUpdatedOnDeviceEvent();
+
+    /*! \brief
+     * Returns whether the maximum number of coupled constraints is supported
+     * by the GPU LINCS code.
+     *
+     * \param[in] mtop The molecular topology
+     */
+    static bool isNumCoupledConstraintsSupported(const gmx_mtop_t& mtop);
+
+private:
+    //! GPU context object
+    const DeviceContext& deviceContext_;
+    //! GPU stream
+    const DeviceStream& deviceStream_;
+    //! GPU kernel launch config
+    KernelLaunchConfig coordinateScalingKernelLaunchConfig_;
+
+    //! Periodic boundary data
+    PbcAiuc pbcAiuc_;
+
+    //! Number of atoms
+    int numAtoms_;
+
+    //! Local copy of the pointer to the device positions buffer
+    DeviceBuffer<Float3> d_x_;
+    //! Local copy of the pointer to the device velocities buffer
+    DeviceBuffer<Float3> d_v_;
+    //! Local copy of the pointer to the device forces buffer
+    DeviceBuffer<Float3> d_f_;
+
+    //! Device buffer for intermediate positions (maintained internally)
+    DeviceBuffer<Float3> d_xp_;
+    //! Number of elements in shifted coordinates buffer
+    int numXp_ = -1;
+    //! Allocation size for the shifted coordinates buffer
+    int numXpAlloc_ = -1;
+
+
+    //! 1/mass for all atoms (GPU)
+    DeviceBuffer<real> d_inverseMasses_;
+    //! Number of elements in reciprocal masses buffer
+    int numInverseMasses_ = -1;
+    //! Allocation size for the reciprocal masses buffer
+    int numInverseMassesAlloc_ = -1;
+
+    //! Leap-Frog integrator
+    std::unique_ptr<LeapFrogGpu> integrator_;
+    //! LINCS GPU object to use for non-water constraints
+    std::unique_ptr<LincsGpu> lincsGpu_;
+    //! SETTLE GPU object for water constrains
+    std::unique_ptr<SettleGpu> settleGpu_;
+
+    //! The event to indicate when the update of coordinates is complete
+    GpuEventSynchronizer xUpdatedOnDeviceEvent_;
+    //! The wallclock counter
+    gmx_wallcycle* wcycle_ = nullptr;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDLIB_UPDATE_CONSTRAIN_GPU_IMPL_H
diff --git a/src/include/gromacs/mdlib/update_constrain_gpu_internal.h b/src/include/gromacs/mdlib/update_constrain_gpu_internal.h
new file mode 100644 (file)
index 0000000..7e0a2d1
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declares GPU implementations of backend-specific update-constraints functions.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_mdlib
+ */
+#ifndef GMX_MDLIB_UPDATE_CONSTRAIN_GPU_INTERNAL_H
+#define GMX_MDLIB_UPDATE_CONSTRAIN_GPU_INTERNAL_H
+
+#include "gmxpre.h"
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/device_stream.h"
+#include "gromacs/gpu_utils/gputraits.h"
+
+class GpuEventSynchronizer;
+
+namespace gmx
+{
+
+/*! \internal \brief Scaling matrix struct.
+ *
+ * \todo Should be generalized.
+ */
+struct ScalingMatrix
+{
+    ScalingMatrix(const matrix m) :
+        xx(m[XX][XX]), yy(m[YY][YY]), zz(m[ZZ][ZZ]), yx(m[YY][XX]), zx(m[ZZ][XX]), zy(m[ZZ][YY])
+    {
+    }
+    float xx, yy, zz, yx, zx, zy;
+};
+
+/*! \brief Launches positions of velocities scaling kernel.
+ *
+ * \param[in] numAtoms       Number of atoms in the system.
+ * \param[in] d_coordinates  Device buffer with position or velocities to be scaled.
+ * \param[in] mu             Scaling matrix.
+ * \param[in] deviceStream   Stream to launch kernel in.
+ */
+void launchScaleCoordinatesKernel(int                  numAtoms,
+                                  DeviceBuffer<Float3> d_coordinates,
+                                  const ScalingMatrix& mu,
+                                  const DeviceStream&  deviceStream);
+
+} // namespace gmx
+
+#endif // GMX_MDLIB_UPDATE_CONSTRAIN_GPU_INTERNAL_H
diff --git a/src/include/gromacs/mdlib/update_vv.h b/src/include/gromacs/mdlib/update_vv.h
new file mode 100644 (file)
index 0000000..2ff6f79
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_UPDATE_VV_H
+#define GMX_MDLIB_UPDATE_VV_H
+
+#include <cstdio>
+
+#include <array>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+class gmx_ekindata_t;
+struct gmx_enerdata_t;
+struct gmx_global_stat;
+struct gmx_wallcycle;
+struct pull_t;
+struct t_commrec;
+struct t_extmass;
+struct t_fcdata;
+struct t_forcerec;
+struct t_inputrec;
+struct t_mdatoms;
+struct t_nrnb;
+class t_state;
+struct t_vcm;
+
+namespace gmx
+{
+class Constraints;
+class ForceBuffers;
+class ObservablesReducer;
+class SimulationSignaller;
+class Update;
+enum class StartingBehavior : int;
+} // namespace gmx
+
+/*! \brief Make the first step of Velocity Verlet integration
+ *
+ * \param[in]  step              Current timestep.
+ * \param[in]  bFirstStep        Is it a first step.
+ * \param[in]  bInitStep         Is it an initialization step.
+ * \param[in]  startingBehavior  Describes whether this is a restart appending to output files.
+ * \param[in]  nstglobalcomm     Will globals be computed on this step.
+ * \param[in]  ir                Input record.
+ * \param[in]  fr                Force record.
+ * \param[in]  cr                Comunication record.
+ * \param[in]  state             Simulation state.
+ * \param[in]  mdatoms           MD atoms data.
+ * \param[in]  fcdata            Force calculation data.
+ * \param[in]  MassQ             Mass/pressure data.
+ * \param[in]  vcm               Center of mass motion removal.
+ * \param[in]  enerd             Energy data.
+ * \param[in]  observablesReducer Pointer to the \c ObservablesReducer object
+ * \param[in]  ekind             Kinetic energy data.
+ * \param[in]  gstat             Storage of thermodynamic parameters data.
+ * \param[out] last_ekin         Kinetic energies of the last step.
+ * \param[in]  bCalcVir          If the virial is computed on this step.
+ * \param[in]  total_vir         Total virial tensor.
+ * \param[in]  shake_vir         Constraints virial.
+ * \param[in]  force_vir         Force virial.
+ * \param[in]  pres              Pressure tensor.
+ * \param[in]  M                 Parrinello-Rahman velocity scaling matrix.
+ * \param[in]  do_log            Do logging on this step.
+ * \param[in]  do_ene            Print energies on this step.
+ * \param[in]  bCalcEner         Compute energies on this step.
+ * \param[in]  bGStat            Collect globals this step.
+ * \param[in]  bStopCM           Stop the center of mass motion on this step.
+ * \param[in]  bTrotter          Do trotter routines this step.
+ * \param[in]  bExchanged        If this is a replica exchange step.
+ * \param[out] bSumEkinhOld      Old kinetic energies will need to be summed up.
+ * \param[out] saved_conserved_quantity  Place to store the conserved energy.
+ * \param[in]  f                 Force buffers.
+ * \param[in]  upd               Update object.
+ * \param[in]  constr            Constraints object.
+ * \param[in]  nullSignaller     Simulation signaller.
+ * \param[in]  trotter_seq       NPT variables.
+ * \param[in]  nrnb              Cycle counters.
+ * \param[in]  fplog             Another logger.
+ * \param[in]  wcycle            Wall-clock cycle counter.
+ */
+void integrateVVFirstStep(int64_t                   step,
+                          bool                      bFirstStep,
+                          bool                      bInitStep,
+                          gmx::StartingBehavior     startingBehavior,
+                          int                       nstglobalcomm,
+                          const t_inputrec*         ir,
+                          t_forcerec*               fr,
+                          t_commrec*                cr,
+                          t_state*                  state,
+                          t_mdatoms*                mdatoms,
+                          t_fcdata*                 fcdata,
+                          t_extmass*                MassQ,
+                          t_vcm*                    vcm,
+                          gmx_enerdata_t*           enerd,
+                          gmx::ObservablesReducer*  observablesReducer,
+                          gmx_ekindata_t*           ekind,
+                          gmx_global_stat*          gstat,
+                          real*                     last_ekin,
+                          bool                      bCalcVir,
+                          tensor                    total_vir,
+                          tensor                    shake_vir,
+                          tensor                    force_vir,
+                          tensor                    pres,
+                          matrix                    M,
+                          bool                      do_log,
+                          bool                      do_ene,
+                          bool                      bCalcEner,
+                          bool                      bGStat,
+                          bool                      bStopCM,
+                          bool                      bTrotter,
+                          bool                      bExchanged,
+                          bool*                     bSumEkinhOld,
+                          real*                     saved_conserved_quantity,
+                          gmx::ForceBuffers*        f,
+                          gmx::Update*              upd,
+                          gmx::Constraints*         constr,
+                          gmx::SimulationSignaller* nullSignaller,
+                          gmx::EnumerationArray<TrotterSequence, std::vector<int>> trotter_seq,
+                          t_nrnb*                                                  nrnb,
+                          FILE*                                                    fplog,
+                          gmx_wallcycle*                                           wcycle);
+
+
+/*! \brief Make the second step of Velocity Verlet integration
+ *
+ * \param[in]  step              Current timestep.
+ * \param[in]  ir                Input record.
+ * \param[in]  fr                Force record.
+ * \param[in]  cr                Comunication record.
+ * \param[in]  state             Simulation state.
+ * \param[in]  mdatoms           MD atoms data.
+ * \param[in]  fcdata            Force calculation data.
+ * \param[in]  MassQ             Mass/pressure data.
+ * \param[in]  vcm               Center of mass motion removal.
+ * \param[in]  pull_work         Pulling data.
+ * \param[in]  enerd             Energy data.
+ * \param[in]  observablesReducer Pointer to the \c ObservablesReducer object
+ * \param[in]  ekind             Kinetic energy data.
+ * \param[in]  gstat             Storage of thermodynamic parameters data.
+ * \param[out] dvdl_constr       FEP data for constraints.
+ * \param[in]  bCalcVir          If the virial is computed on this step.
+ * \param[in]  total_vir         Total virial tensor.
+ * \param[in]  shake_vir         Constraints virial.
+ * \param[in]  force_vir         Force virial.
+ * \param[in]  pres              Pressure tensor.
+ * \param[in]  M                 Parrinello-Rahman velocity scaling matrix.
+ * \param[in]  lastbox           Last recorded PBC box.
+ * \param[in]  do_log            Do logging on this step.
+ * \param[in]  do_ene            Print energies on this step.
+ * \param[in]  bGStat            Collect globals this step.
+ * \param[out] bSumEkinhOld      Old kinetic energies need to be summed up.
+ * \param[in]  f                 Force buffers.
+ * \param[in]  cbuf              Buffer to store intermediate coordinates
+ * \param[in]  upd               Update object.
+ * \param[in]  constr            Constraints object.
+ * \param[in]  nullSignaller     Simulation signaller.
+ * \param[in]  trotter_seq       NPT variables.
+ * \param[in]  nrnb              Cycle counters.
+ * \param[in]  wcycle            Wall-clock cycle counter.
+ */
+void integrateVVSecondStep(int64_t                   step,
+                           const t_inputrec*         ir,
+                           t_forcerec*               fr,
+                           t_commrec*                cr,
+                           t_state*                  state,
+                           t_mdatoms*                mdatoms,
+                           t_fcdata*                 fcdata,
+                           t_extmass*                MassQ,
+                           t_vcm*                    vcm,
+                           pull_t*                   pull_work,
+                           gmx_enerdata_t*           enerd,
+                           gmx::ObservablesReducer*  observablesReducer,
+                           gmx_ekindata_t*           ekind,
+                           gmx_global_stat*          gstat,
+                           real*                     dvdl_constr,
+                           bool                      bCalcVir,
+                           tensor                    total_vir,
+                           tensor                    shake_vir,
+                           tensor                    force_vir,
+                           tensor                    pres,
+                           matrix                    M,
+                           matrix                    lastbox,
+                           bool                      do_log,
+                           bool                      do_ene,
+                           bool                      bGStat,
+                           bool*                     bSumEkinhOld,
+                           gmx::ForceBuffers*        f,
+                           std::vector<gmx::RVec>*   cbuf,
+                           gmx::Update*              upd,
+                           gmx::Constraints*         constr,
+                           gmx::SimulationSignaller* nullSignaller,
+                           gmx::EnumerationArray<TrotterSequence, std::vector<int>> trotter_seq,
+                           t_nrnb*                                                  nrnb,
+                           gmx_wallcycle*                                           wcycle);
+
+
+#endif // GMX_MDLIB_UPDATE_VV_H
diff --git a/src/include/gromacs/mdlib/updategroups.h b/src/include/gromacs/mdlib/updategroups.h
new file mode 100644 (file)
index 0000000..150d2e5
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declares the functions for generating update groups
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+#ifndef GMX_MDLIB_UPDATEGROUPS
+#define GMX_MDLIB_UPDATEGROUPS
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+enum class PbcType : int;
+struct gmx_mtop_t;
+
+namespace gmx
+{
+class MDLogger;
+class RangePartitioning;
+
+/*! \brief Returns a vector with update groups for each moleculetype in \p mtop
+ * or an empty vector when the criteria (see below) are not satisfied.
+ *
+ * An empty vector is returned when at least one moleculetype does not obey
+ * the restrictions of update groups, e.g. more than two constraints in a row.
+ *
+ * Currently valid update groups are:
+ * - a single atom which is not a virtual site and does not have constraints;
+ * - or a group of atoms where all virtual sites are constructed from atoms
+ *   within the group and at least one non-vsite atom is constrained to
+ *   all other non-vsite atoms.
+ *
+ * To have update groups, all virtual sites should be linear 2 or 3 atom
+ * constructions with coefficients >= 0 and sum of coefficients <= 1.
+ *
+ * This vector is generally consumed in constructing an UpdateGroups object.
+ *
+ * \param[in] mtop  The system topology
+ */
+std::vector<RangePartitioning> makeUpdateGroupingsPerMoleculeType(const gmx_mtop_t& mtop);
+
+/*! \brief Returns the maximum update group radius
+ *
+ * \note When \p updateGroups is empty, 0 is returned.
+ *
+ * \param[in] mtop                           The system topology
+ * \param[in] updateGroupingPerMoleculeType  List of update group, size should match the
+ *                                           number of moltypes in \p mtop or be 0
+ * \param[in] temperature                    The maximum reference temperature, pass -1
+ *                                           when unknown or not applicable
+ */
+real computeMaxUpdateGroupRadius(const gmx_mtop_t&                 mtop,
+                                 ArrayRef<const RangePartitioning> updateGroupingPerMoleculeType,
+                                 real                              temperature);
+
+/*! \brief Return the margin required for successful domain decomposition
+ *
+ * \param[in] pbcType    The PBC type in use
+ * \param[in] box        The box in use
+ * \param[in] rlist      The list size in use
+ */
+real computeCutoffMargin(PbcType pbcType, matrix box, real rlist);
+
+/*! \brief Returns whether mtop contains any constraints and/or vsites
+ *
+ * When we have constraints and/or vsites, it is beneficial to use
+ * update groups (when possible) to allow independent update of
+ * groups.*/
+bool systemHasConstraintsOrVsites(const gmx_mtop_t& mtop);
+
+/*! \libinternal
+ * \brief Owns the update grouping and related data */
+class UpdateGroups
+{
+public:
+    //! Default constructor
+    UpdateGroups() = default;
+    //! Constructor when update groups are active
+    UpdateGroups(std::vector<RangePartitioning>&& updateGroupingPerMoleculeType, real maxUpdateGroupRadius);
+
+    bool                              useUpdateGroups() const { return useUpdateGroups_; }
+    real                              maxUpdateGroupRadius() const { return maxUpdateGroupRadius_; }
+    ArrayRef<const RangePartitioning> updateGroupingPerMoleculeType() const
+    {
+        return updateGroupingPerMoleculeType_;
+    }
+
+private:
+    //! Whether update groups are in use
+    const bool useUpdateGroups_ = false;
+    //! The update groupings within each respective molecule type, empty when not in use
+    const std::vector<RangePartitioning> updateGroupingPerMoleculeType_ = {};
+    //! The maximum radius of any update group, 0 when not in use
+    const real maxUpdateGroupRadius_ = 0.0_real;
+};
+
+/*! \brief Builder for update groups.
+ *
+ * Checks the conditions for using update groups, and logs a message
+ * if they cannot be used, along with the reason why not.
+ *
+ * If PP domain decomposition is not in use, there is no reason to use
+ * update groups.
+ *
+ * All molecule types in the system topology must be conform to the
+ * requirements, such that makeUpdateGroupingsPerMoleculeType()
+ * returns a non-empty vector.
+ *
+ * When we have constraints and/or vsites, it is beneficial to use
+ * update groups (when possible) to allow independent update of
+ * groups. But if there are no constraints or vsites, then there is no
+ * need to use update groups at all.
+ *
+ * To use update groups, the large domain-to-domain cutoff distance
+ * should be compatible with the box size.
+ */
+UpdateGroups makeUpdateGroups(const gmx::MDLogger&             mdlog,
+                              std::vector<RangePartitioning>&& updateGroupingPerMoleculeType,
+                              real                             maxUpdateGroupRadius,
+                              bool                             useDomainDecomposition,
+                              bool                             systemHasConstraintsOrVsites,
+                              real                             cutoffMargin);
+
+} // namespace gmx
+
+#endif // GMX_MDLIB_UPDATEGROUPS
diff --git a/src/include/gromacs/mdlib/updategroupscog.h b/src/include/gromacs/mdlib/updategroupscog.h
new file mode 100644 (file)
index 0000000..ca05d95
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declares the UpdateGroupsCog class for managing centers of mass of update groups
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+#ifndef GMX_MDLIB_UPDATEGROUPSCOG
+#define GMX_MDLIB_UPDATEGROUPSCOG
+
+#include <vector>
+
+#include "gromacs/domdec/hashedmap.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxassert.h"
+
+struct gmx_mtop_t;
+namespace gmx
+{
+class RangePartitioning;
+
+/*! \libinternal
+ * \brief Class for managing and computing centers of geometry of update groups
+ */
+class UpdateGroupsCog
+{
+public:
+    /*! \brief Constructor
+     *
+     * The temperature is used for computing the maximum update group
+     * radius.
+     *
+     * \note \p numHomeAtoms only affects the performance up till the first
+     *       call to clear().
+     *
+     * \param[in] mtop                            The global topology
+     * \param[in] updateGroupingsPerMoleculeType  List of update groups for each molecule type in \p mtop
+     * \param[in] temperature                     The maximum reference temperature, pass -1 when unknown or not applicable
+     * \param[in] numHomeAtoms                    Estimate of the number of home atoms per DD cell
+     */
+    UpdateGroupsCog(const gmx_mtop_t&                           mtop,
+                    gmx::ArrayRef<const gmx::RangePartitioning> updateGroupingsPerMoleculeType,
+                    real                                        temperature,
+                    int                                         numHomeAtoms);
+
+    /*! \brief Compute centers of geometry for supplied coordinates
+     *
+     * Coordinates are processed starting after the last index
+     * processed in the previous call to \p addCogs(), unless \p clear()
+     * was called last, in which case processing starts at 0.
+     *
+     * \param[in] globalAtomIndices  List of global atom indices for the atoms belonging to \p coordinates
+     * \param[in] coordinates        List of coordinates to be processed, processing might not start at 0 (see above)
+     */
+    void addCogs(gmx::ArrayRef<const int> globalAtomIndices, gmx::ArrayRef<const gmx::RVec> coordinates);
+
+    /*! \brief Returns the number of centers of geometry currently stored */
+    int numCogs() const { return cogs_.size(); }
+
+    /*! \brief Returns a reference to a center of geometry
+     *
+     * \param[in] cogIndex  The COG requested, should be; 0 <= \p cogIndex < cogs_.size()
+     */
+    RVec& cog(int cogIndex)
+    {
+        GMX_ASSERT(cogIndex >= 0 && static_cast<size_t>(cogIndex) < cogs_.size(),
+                   "cogIndex should be in the range set in this object");
+
+        return cogs_[cogIndex];
+    }
+
+    /*! \brief Returns the COG index given an atom index
+     *
+     * \param[in] atomIndex  The local atom index that maps to the COG
+     */
+    int cogIndex(int atomIndex) const
+    {
+        GMX_ASSERT(atomIndex >= 0 && static_cast<size_t>(atomIndex) < cogIndices_.size(),
+                   "atomIndex should be in the range set in this object");
+
+        return cogIndices_[atomIndex];
+    }
+
+    /*! \brief Return a reference to a center of geometry given an atom index
+     *
+     * \param[in] atomIndex  The atom index that maps to the COG requested
+     */
+    const RVec& cogForAtom(int atomIndex) const { return cogs_[cogIndex(atomIndex)]; }
+
+    /*! \brief Clear the lists of stored center of geometry coordinates */
+    void clear();
+
+    /*! \brief Returns the maximum radius over all update groups */
+    real maxUpdateGroupRadius() const { return maxUpdateGroupRadius_; }
+
+private:
+    /*! \libinternal
+     * \brief Helper struct for mapping atom indices to COG indices, used per molecule block in mtop
+     */
+    struct IndexToGroup
+    {
+        //! \brief Starting index of groups for this molblock
+        int groupStart_;
+        //! \brief The number of atoms in a molecule
+        int numGroupsPerMolecule_;
+        //! \brief Map atom indices to group indices for one molecule
+        std::vector<int> groupIndex_;
+    };
+
+    //! \brief Maps atom indices to COG indices
+    std::vector<int> cogIndices_;
+    //! \brief List of COGs
+    std::vector<RVec> cogs_;
+    //! \brief List of the number of atoms for each COG
+    std::vector<int> numAtomsPerCog_;
+    //! \brief Maps global COG index to local COG index
+    HashedMap<int> globalToLocalMap_;
+    //! \brief Helper data for mapping atom indices to COG indices
+    std::vector<IndexToGroup> indicesPerMoleculeblock_;
+    //! \brief The maximum radius over all update groups
+    real maxUpdateGroupRadius_;
+    //! \brief Reference to mtop this object was constructed with
+    const gmx_mtop_t& mtop_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDLIB_UPDATEGROUPSCOG
diff --git a/src/include/gromacs/mdlib/vcm.h b/src/include/gromacs/mdlib/vcm.h
new file mode 100644 (file)
index 0000000..35fbeb7
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_VCM_H
+#define GMX_MDLIB_VCM_H
+
+#include <stdio.h>
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/real.h"
+
+struct SimulationGroups;
+struct t_inputrec;
+struct t_mdatoms;
+
+namespace gmx
+{
+template<typename T>
+class ArrayRef;
+}
+
+struct t_vcm_thread
+{
+    //! Linear momentum
+    rvec p = { 0 };
+    //! Center of mass
+    rvec x = { 0 };
+    //! Angular momentum
+    rvec j = { 0 };
+    //! Moment of inertia
+    tensor i = { { 0 } };
+    //! Mass
+    real mass = 0;
+};
+
+struct t_vcm
+{
+    //! Number of groups
+    int nr = 0;
+    //! Size of group arrays
+    int size = 0;
+    //! Stride for thread data
+    int stride = 0;
+    //! One of the enums above
+    ComRemovalAlgorithm mode = ComRemovalAlgorithm::Linear;
+    //! The number of dimensions for corr.
+    int ndim = 0;
+    //! The time step for COMM removal
+    real timeStep = 0;
+    //! Number of degrees of freedom
+    std::vector<real> group_ndf;
+    //! Mass per group
+    std::vector<real> group_mass;
+    //! Linear momentum per group
+    std::vector<gmx::RVec> group_p;
+    //! Linear velocity per group
+    std::vector<gmx::RVec> group_v;
+    //! Center of mass per group
+    std::vector<gmx::RVec> group_x;
+    //! Angular momentum per group
+    std::vector<gmx::RVec> group_j;
+    //! Angular velocity (omega)
+    std::vector<gmx::RVec> group_w;
+    //! Moment of inertia per group
+    tensor* group_i = nullptr;
+    //! These two are copies to pointers in
+    std::vector<char*> group_name;
+    //! Tells whether dimensions are frozen per freeze group
+    ivec* nFreeze = nullptr;
+    //! Temporary data per thread and group
+    std::vector<t_vcm_thread> thread_vcm;
+
+    //! Tell whether the integrator conserves momentum
+    bool integratorConservesMomentum = false;
+
+    t_vcm(const SimulationGroups& groups, const t_inputrec& ir);
+    ~t_vcm();
+};
+
+/* print COM removal info to log */
+void reportComRemovalInfo(FILE* fp, const t_vcm& vcm);
+
+
+/* Do a per group center of mass things */
+void calc_vcm_grp(const t_mdatoms&               md,
+                  gmx::ArrayRef<const gmx::RVec> x,
+                  gmx::ArrayRef<const gmx::RVec> v,
+                  t_vcm*                         vcm);
+
+/* Set the COM velocity to zero and potentially correct the COM position.
+ *
+ * Processes the kinetic energy reduced over MPI before removing COM motion.
+ * With mode linear, nullptr can be passed for x.
+ * With acceleration correction nullptr should be passed for x at initialization
+ * and a pointer to the coordinates at normal MD steps.
+ * When fplog != nullptr, a warning is printed to fplog with high COM velocity.
+ */
+void process_and_stopcm_grp(FILE*                    fplog,
+                            t_vcm*                   vcm,
+                            const t_mdatoms&         mdatoms,
+                            gmx::ArrayRef<gmx::RVec> x,
+                            gmx::ArrayRef<gmx::RVec> v);
+
+#endif
diff --git a/src/include/gromacs/mdlib/vsite.h b/src/include/gromacs/mdlib/vsite.h
new file mode 100644 (file)
index 0000000..0606203
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares the VirtualSitesHandler class and vsite standalone functions
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+
+#ifndef GMX_MDLIB_VSITE_H
+#define GMX_MDLIB_VSITE_H
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_domdec_t;
+struct gmx_mtop_t;
+struct t_commrec;
+struct InteractionList;
+struct t_nrnb;
+struct gmx_wallcycle;
+enum class PbcType : int;
+enum class ParticleType : int;
+
+namespace gmx
+{
+class RangePartitioning;
+template<typename T>
+class ArrayRef;
+
+/*! \brief The start value of the vsite indices in the ftype enum
+ *
+ * The validity of the start and end values is checked in makeVirtualSitesHandler().
+ * This is used to avoid loops over all ftypes just to get the vsite entries.
+ * (We should replace the fixed ilist array by only the used entries.)
+ */
+static constexpr int c_ftypeVsiteStart = F_VSITE1;
+//! The start and end value of the vsite indices in the ftype enum
+static constexpr int c_ftypeVsiteEnd = F_VSITEN + 1;
+
+//! Type for storing PBC atom information for all vsite types in the system
+typedef std::array<std::vector<int>, c_ftypeVsiteEnd - c_ftypeVsiteStart> VsitePbc;
+
+//! Whether we calculate vsite positions, velocities, or both
+enum class VSiteOperation
+{
+    Positions,              //!< Calculate only positions
+    Velocities,             //!< Calculate only velocities
+    PositionsAndVelocities, //!< Calculate both positions and velocities
+    Count                   //!< The number of entries
+};
+
+/*! \libinternal
+ * \brief Class that handles construction of vsites and spreading of vsite forces
+ */
+class VirtualSitesHandler
+{
+public:
+    //! Constructor, used only be the makeVirtualSitesHandler() factory function
+    VirtualSitesHandler(const gmx_mtop_t&                 mtop,
+                        gmx_domdec_t*                     domdec,
+                        PbcType                           pbcType,
+                        ArrayRef<const RangePartitioning> updateGroupingPerMoleculeType);
+
+    ~VirtualSitesHandler();
+
+    //! Returns the number of virtual sites acting over multiple update groups
+    int numInterUpdategroupVirtualSites() const;
+
+    //! Set VSites and distribute VSite work over threads, should be called after each DD partitioning
+    void setVirtualSites(ArrayRef<const InteractionList> ilist,
+                         int                             numAtoms,
+                         int                             homenr,
+                         ArrayRef<const ParticleType>    ptype);
+
+    /*! \brief Create positions of vsite atoms based for the local system
+     *
+     * \param[in,out] x          The coordinates
+     * \param[in,out] v          The velocities, needed if operation requires it
+     * \param[in]     box        The box
+     * \param[in]     operation  Whether we calculate positions, velocities, or both
+     */
+    void construct(ArrayRef<RVec> x, ArrayRef<RVec> v, const matrix box, VSiteOperation operation) const;
+
+    //! Tells how to handle virial contributions due to virtual sites
+    enum class VirialHandling : int
+    {
+        None,     //!< Do not compute virial contributions
+        Pbc,      //!< Add contributions working over PBC to shift forces
+        NonLinear //!< Compute contributions due to non-linear virtual sites
+    };
+
+    /*! \brief Spread the force operating on the vsite atoms on the surrounding atoms.
+     *
+     * vsite should point to a valid object.
+     * The virialHandling parameter determines how virial contributions are handled.
+     * If this is set to Linear, shift forces are accumulated into fshift.
+     * If this is set to NonLinear, non-linear contributions are added to virial.
+     * This non-linear correction is required when the virial is not calculated
+     * afterwards from the particle position and forces, but in a different way,
+     * as for instance for the PME mesh contribution.
+     */
+    void spreadForces(ArrayRef<const RVec> x,
+                      ArrayRef<RVec>       f,
+                      VirialHandling       virialHandling,
+                      ArrayRef<RVec>       fshift,
+                      matrix               virial,
+                      t_nrnb*              nrnb,
+                      const matrix         box,
+                      gmx_wallcycle*       wcycle);
+
+private:
+    //! Implementation type.
+    class Impl;
+    //! Implementation object.
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \brief Create positions of vsite atoms based for the local system
+ *
+ * \param[in,out] x        The coordinates
+ * \param[in]     ip       Interaction parameters
+ * \param[in]     ilist    The interaction list
+ */
+void constructVirtualSites(ArrayRef<RVec>                  x,
+                           ArrayRef<const t_iparams>       ip,
+                           ArrayRef<const InteractionList> ilist);
+
+/*! \brief Create positions of vsite atoms for the whole system assuming all molecules are wholex
+ *
+ * \param[in]     mtop  The global topology
+ * \param[in,out] x     The global coordinates
+ */
+void constructVirtualSitesGlobal(const gmx_mtop_t& mtop, ArrayRef<RVec> x);
+
+//! Tells how to handle virial contributions due to virtual sites
+enum class VirtualSiteVirialHandling : int
+{
+    None,     //!< Do not compute virial contributions
+    Pbc,      //!< Add contributions working over PBC to shift forces
+    NonLinear //!< Compute contributions due to non-linear virtual sites
+};
+
+//! Return the number of non-linear virtual site constructions in the system
+int countNonlinearVsites(const gmx_mtop_t& mtop);
+
+/*! \brief Return the number of virtual sites that cross update groups
+ *
+ * \param[in] mtop                           The global topology
+ * \param[in] updateGroupingsPerMoleculeType  Update grouping per molecule type, pass empty when not using update groups
+ */
+int countInterUpdategroupVsites(const gmx_mtop_t&                 mtop,
+                                ArrayRef<const RangePartitioning> updateGroupingsPerMoleculeType);
+
+/*! \brief Create the virtual site handler
+ *
+ * \param[in] mtop                           The global topology
+ * \param[in] cr                             The communication record
+ * \param[in] pbcType                        The type of PBC
+ * \param[in] updateGroupingPerMoleculeType  Update grouping per molecule type, pass
+ *                                           empty when not using update groups
+ * \returns A valid vsite handler object or nullptr when there are no virtual sites
+ */
+std::unique_ptr<VirtualSitesHandler>
+makeVirtualSitesHandler(const gmx_mtop_t&                 mtop,
+                        const t_commrec*                  cr,
+                        PbcType                           pbcType,
+                        ArrayRef<const RangePartitioning> updateGroupingPerMoleculeType);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdlib/wall.h b/src/include/gromacs/mdlib/wall.h
new file mode 100644 (file)
index 0000000..01126f6
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_WALLS_H
+#define GMX_MDLIB_WALLS_H
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+
+struct SimulationGroups;
+struct t_forcerec;
+struct t_inputrec;
+struct t_mdatoms;
+struct t_nrnb;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class ForceWithVirial;
+} // namespace gmx
+
+void make_wall_tables(FILE*                   fplog,
+                      const t_inputrec&       ir,
+                      const char*             tabfn,
+                      const SimulationGroups* groups,
+                      t_forcerec*             fr);
+
+real do_walls(const t_inputrec&                   ir,
+              const t_forcerec&                   fr,
+              const matrix                        box,
+              gmx::ArrayRef<const int>            typeA,
+              gmx::ArrayRef<const int>            typeB,
+              gmx::ArrayRef<const unsigned short> cENER,
+              int                                 homenr,
+              int                                 numPerturbedAtoms,
+              gmx::ArrayRef<const gmx::RVec>      x,
+              gmx::ForceWithVirial*               forceWithVirial,
+              real                                lambda,
+              gmx::ArrayRef<real>                 Vlj,
+              t_nrnb*                             nrnb);
+
+#endif
diff --git a/src/include/gromacs/mdlib/wholemoleculetransform.h b/src/include/gromacs/mdlib/wholemoleculetransform.h
new file mode 100644 (file)
index 0000000..8d42b7e
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declares the WholeMolecules class for generating whole molecules
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+#ifndef GMX_MDLIB_WHOLEMOLECULETRANSFORM_H
+#define GMX_MDLIB_WHOLEMOLECULETRANSFORM_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/pbcutil/mshift.h"
+#include "gromacs/utility/arrayref.h"
+
+class gmx_ga2la_t;
+struct gmx_mtop_t;
+enum class PbcType : int;
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief This class manages a coordinate buffer with molecules not split
+ * over periodic boundary conditions for use in force calculations
+ * which require whole molecules.
+ *
+ * Note: This class should not be used for computation of forces which
+ *       have virial contributions through shift forces.
+ */
+class WholeMoleculeTransform
+{
+public:
+    /*! \brief Constructor
+     *
+     * \param[in] mtop               The global topology use for getting the connections between atoms
+     * \param[in] pbcType            The type of PBC
+     * \param[in] useAtomReordering  Whether we will use atom reordering
+     */
+    WholeMoleculeTransform(const gmx_mtop_t& mtop, PbcType pbcType, bool useAtomReordering);
+
+    /*! \brief Changes the atom order to the one provided
+     *
+     * This method is called after domain repartitioning.
+     * The object should have been constructed with \p useAtomReordering set to \p true.
+     *
+     * \param[in] globalAtomIndices  The global atom indices for the local atoms, size should be the system size
+     * \param[in] ga2la              Global to local atom index lookup (the inverse of \p globalAtomIndices)
+     */
+    void updateAtomOrder(ArrayRef<const int> globalAtomIndices, const gmx_ga2la_t& ga2la);
+
+    /*! \brief Updates the graph when atoms have been shifted by periodic vectors */
+    void updateForAtomPbcJumps(ArrayRef<const RVec> x, const matrix box);
+
+    /*! \brief Create and return coordinates with whole molecules for input coordinates \p x
+     *
+     * \param[in] x  Input coordinates, should not have periodic displacement compared
+     *               with the coordinates passed in the last call to \p updateForAtomPbcJumps().
+     * \param[in] box  The current periodic image vectors
+     *
+     * Note: this operation is not free. If you need whole molecules coordinates
+     * more than once during the force calculation, store the result and reuse it.
+     */
+    ArrayRef<const RVec> wholeMoleculeCoordinates(ArrayRef<const RVec> x, const matrix box);
+
+private:
+    //! The type of PBC
+    PbcType pbcType_;
+    //! The graph
+    t_graph graph_;
+    //! The atom index at which graphGlobalAtomOrderEdges_ starts
+    int globalEdgeAtomBegin_;
+    //! The edges for the global atom order
+    ListOfLists<int> graphGlobalAtomOrderEdges_;
+    //! Buffer for storing coordinates for whole molecules
+    std::vector<RVec> wholeMoleculeCoordinates_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdrun/isimulator.h b/src/include/gromacs/mdrun/isimulator.h
new file mode 100644 (file)
index 0000000..fcddbee
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal
+ * \brief Declares the general simulator interface
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_mdrun
+ */
+#ifndef GMX_MDRUN_ISIMULATOR_H
+#define GMX_MDRUN_ISIMULATOR_H
+
+#include "gromacs/mdlib/stophandler.h"
+
+class energyhistory_t;
+class gmx_ekindata_t;
+struct gmx_enerdata_t;
+struct gmx_enfrot;
+struct gmx_localtop_t;
+struct gmx_mtop_t;
+struct gmx_membed_t;
+struct gmx_multisim_t;
+struct gmx_output_env_t;
+struct gmx_wallcycle;
+struct gmx_walltime_accounting;
+struct ObservablesHistory;
+struct pull_t;
+struct ReplicaExchangeParameters;
+struct t_commrec;
+struct t_forcerec;
+struct t_filenm;
+struct t_inputrec;
+struct t_nrnb;
+struct t_swap;
+class t_state;
+
+namespace gmx
+{
+enum class StartingBehavior;
+class BoxDeformation;
+class Constraints;
+class MdrunScheduleWorkload;
+class IMDOutputProvider;
+struct MDModulesNotifiers;
+class ImdSession;
+class MDLogger;
+class MDAtoms;
+class ObservablesReducerBuilder;
+class StopHandlerBuilder;
+struct MdrunOptions;
+class VirtualSitesHandler;
+
+/*! \internal
+ * \brief The Simulator interface
+ *
+ * This is the general interface for any type of simulation type
+ * run with GROMACS. This allows having a builder return different
+ * Simulator objects based on user input.
+ */
+class ISimulator
+{
+public:
+    /*! \brief The simulation run
+     *
+     * This will be called by the owner of the simulator object. To be redefined
+     * by the child classes. This function is expected to run the simulation.
+     */
+    virtual void run() = 0;
+    //! Standard destructor
+    virtual ~ISimulator() = default;
+};
+
+/*! \internal
+ * \brief The legacy simulator data
+ *
+ * This contains the data passed into the GROMACS simulators from
+ * the Mdrunner object.
+ */
+class LegacySimulatorData
+{
+public:
+    //! The constructor
+    LegacySimulatorData(FILE*                               fplog,
+                        t_commrec*                          cr,
+                        const gmx_multisim_t*               ms,
+                        const MDLogger&                     mdlog,
+                        int                                 nfile,
+                        const t_filenm*                     fnm,
+                        const gmx_output_env_t*             oenv,
+                        const MdrunOptions&                 mdrunOptions,
+                        StartingBehavior                    startingBehavior,
+                        VirtualSitesHandler*                vsite,
+                        Constraints*                        constr,
+                        gmx_enfrot*                         enforcedRotation,
+                        BoxDeformation*                     deform,
+                        IMDOutputProvider*                  outputProvider,
+                        const MDModulesNotifiers&           mdModulesNotifiers,
+                        t_inputrec*                         inputrec,
+                        ImdSession*                         imdSession,
+                        pull_t*                             pull_work,
+                        t_swap*                             swap,
+                        const gmx_mtop_t&                   top_global,
+                        gmx_localtop_t*                     top,
+                        t_state*                            state_global,
+                        t_state*                            state,
+                        ObservablesHistory*                 observablesHistory,
+                        MDAtoms*                            mdAtoms,
+                        t_nrnb*                             nrnb,
+                        gmx_wallcycle*                      wcycle,
+                        t_forcerec*                         fr,
+                        gmx_enerdata_t*                     enerd,
+                        ObservablesReducerBuilder*          observablesReducerBuilder,
+                        gmx_ekindata_t*                     ekind,
+                        MdrunScheduleWorkload*              runScheduleWork,
+                        const ReplicaExchangeParameters&    replExParams,
+                        gmx_membed_t*                       membed,
+                        gmx_walltime_accounting*            walltime_accounting,
+                        std::unique_ptr<StopHandlerBuilder> stopHandlerBuilder,
+                        bool                                doRerun) :
+        fplog(fplog),
+        cr(cr),
+        ms(ms),
+        mdlog(mdlog),
+        nfile(nfile),
+        fnm(fnm),
+        oenv(oenv),
+        mdrunOptions(mdrunOptions),
+        startingBehavior(startingBehavior),
+        vsite(vsite),
+        constr(constr),
+        enforcedRotation(enforcedRotation),
+        deform(deform),
+        outputProvider(outputProvider),
+        mdModulesNotifiers(mdModulesNotifiers),
+        inputrec(inputrec),
+        imdSession(imdSession),
+        pull_work(pull_work),
+        swap(swap),
+        top_global(top_global),
+        top(top),
+        state_global(state_global),
+        state(state),
+        observablesHistory(observablesHistory),
+        mdAtoms(mdAtoms),
+        nrnb(nrnb),
+        wcycle(wcycle),
+        fr(fr),
+        enerd(enerd),
+        observablesReducerBuilder(observablesReducerBuilder),
+        ekind(ekind),
+        runScheduleWork(runScheduleWork),
+        replExParams(replExParams),
+        membed(membed),
+        walltime_accounting(walltime_accounting),
+        stopHandlerBuilder(std::move(stopHandlerBuilder)),
+        doRerun(doRerun)
+    {
+    }
+
+    //! Handles logging.
+    FILE* fplog;
+    //! Handles communication.
+    t_commrec* cr;
+    //! Coordinates multi-simulations.
+    const gmx_multisim_t* ms;
+    //! Handles logging.
+    const MDLogger& mdlog;
+    //! Count of input file options.
+    int nfile;
+    //! Content of input file options.
+    const t_filenm* fnm;
+    //! Handles writing text output.
+    const gmx_output_env_t* oenv;
+    //! Contains command-line options to mdrun.
+    const MdrunOptions& mdrunOptions;
+    //! Whether the simulation will start afresh, or restart with/without appending.
+    const StartingBehavior startingBehavior;
+    //! Handles virtual sites.
+    VirtualSitesHandler* vsite;
+    //! Handles constraints.
+    Constraints* constr;
+    //! Handles enforced rotation.
+    gmx_enfrot* enforcedRotation;
+    //! Handles box deformation.
+    BoxDeformation* deform;
+    //! Handles writing output files.
+    IMDOutputProvider* outputProvider;
+    //! Handles notifications to MDModules for checkpoint writing
+    const MDModulesNotifiers& mdModulesNotifiers;
+    //! Contains user input mdp options. Note: The const-ness is casted away in a few instances, see #3854.
+    const t_inputrec* inputrec;
+    //! The Interactive Molecular Dynamics session.
+    ImdSession* imdSession;
+    //! The pull work object.
+    pull_t* pull_work;
+    //! The coordinate-swapping session.
+    t_swap* swap;
+    //! Full system topology.
+    const gmx_mtop_t& top_global;
+    //! Handle to local simulation topology.
+    gmx_localtop_t* top;
+    //! Full simulation state (only non-nullptr on master rank).
+    t_state* state_global;
+    //! Handle to local state of the simulation.
+    t_state* state;
+    //! History of simulation observables.
+    ObservablesHistory* observablesHistory;
+    //! Atom parameters for this domain.
+    MDAtoms* mdAtoms;
+    //! Manages flop accounting.
+    t_nrnb* nrnb;
+    //! Manages wall cycle accounting.
+    gmx_wallcycle* wcycle;
+    //! Parameters for force calculations.
+    t_forcerec* fr;
+    //! Data for energy output.
+    gmx_enerdata_t* enerd;
+    //! Builder for coordinator of reduction for observables
+    ObservablesReducerBuilder* observablesReducerBuilder;
+    //! Kinetic energy data.
+    gmx_ekindata_t* ekind;
+    //! Schedule of work for each MD step for this task.
+    MdrunScheduleWorkload* runScheduleWork;
+    //! Parameters for replica exchange algorihtms.
+    const ReplicaExchangeParameters& replExParams;
+    //! Parameters for membrane embedding.
+    gmx_membed_t* membed;
+    //! Manages wall time accounting.
+    gmx_walltime_accounting* walltime_accounting;
+    //! Registers stop conditions
+    std::unique_ptr<StopHandlerBuilder> stopHandlerBuilder;
+    //! Whether we're doing a rerun.
+    bool doRerun;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDRUN_ISIMULATOR_H
diff --git a/src/include/gromacs/mdrun/legacymdrunoptions.h b/src/include/gromacs/mdrun/legacymdrunoptions.h
new file mode 100644 (file)
index 0000000..678f741
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2011-2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file declares helper functionality for legacy option handling for mdrun
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \author Erik Lindahl <erik@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ *
+ * \ingroup module_mdrun
+ * \inlibraryapi
+ */
+#ifndef GMX_MDRUN_LEGACYMDRUNOPTIONS_H
+#define GMX_MDRUN_LEGACYMDRUNOPTIONS_H
+
+#include "gromacs/commandline/filenm.h"
+#include "gromacs/commandline/pargs.h"
+#include "gromacs/domdec/options.h"
+#include "gromacs/hardware/hw_info.h"
+#include "gromacs/mdtypes/mdrunoptions.h"
+
+#include "replicaexchange.h"
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief This class provides the same command-line option
+ * functionality to both CLI and API sessions.
+ *
+ * This class should not exist, but is necessary now to introduce
+ * support for the CLI and API without duplicating code. It should be
+ * eliminated following the TODOs below.
+ *
+ * \warning Instances provide lifetime scope for members that do not have
+ *  effective lifetime management or which are frequently accessed unsafely.
+ *  The caller is responsible for keeping a LegacyMdrunOptions object alive
+ *  for as long as any consumers, direct or transitive.
+ *
+ * \todo Modules in mdrun should acquire proper option handling so
+ *       that all of these declarations and defaults are local to the
+ *       modules.
+ *
+ * \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
+{
+public:
+    //! Ongoing collection of mdrun options
+    MdrunOptions mdrunOptions;
+    //! Options for the domain decomposition.
+    DomdecOptions domdecOptions;
+    //! Parallelism-related user options.
+    gmx_hw_opt_t hw_opt;
+    //! Command-line override for the duration of a neighbor list with the Verlet scheme.
+    int nstlist_cmdline = 0;
+    //! Parameters for replica-exchange simulations.
+    ReplicaExchangeParameters replExParams;
+
+    //! Filename options to fill from command-line argument values.
+    std::vector<t_filenm> filenames = { { { efTPR, nullptr, nullptr, ffREAD },
+                                          { efTRN, "-o", nullptr, ffWRITE },
+                                          { efCOMPRESSED, "-x", nullptr, ffOPTWR },
+                                          { efCPT, "-cpi", nullptr, ffOPTRD | ffALLOW_MISSING },
+                                          { efCPT, "-cpo", nullptr, ffOPTWR },
+                                          { efSTO, "-c", "confout", ffWRITE },
+                                          { efEDR, "-e", "ener", ffWRITE },
+                                          { efLOG, "-g", "md", ffWRITE },
+                                          { efXVG, "-dhdl", "dhdl", ffOPTWR },
+                                          { efXVG, "-field", "field", ffOPTWR },
+                                          { efXVG, "-table", "table", ffOPTRD },
+                                          { efXVG, "-tablep", "tablep", ffOPTRD },
+                                          { efXVG, "-tableb", "table", ffOPTRDMULT },
+                                          { efTRX, "-rerun", "rerun", ffOPTRD },
+                                          { efXVG, "-tpi", "tpi", ffOPTWR },
+                                          { efXVG, "-tpid", "tpidist", ffOPTWR },
+                                          { efEDI, "-ei", "sam", ffOPTRD },
+                                          { efXVG, "-eo", "edsam", ffOPTWR },
+                                          { efXVG, "-px", "pullx", ffOPTWR },
+                                          { efXVG, "-pf", "pullf", ffOPTWR },
+                                          { efXVG, "-ro", "rotation", ffOPTWR },
+                                          { efLOG, "-ra", "rotangles", ffOPTWR },
+                                          { efLOG, "-rs", "rotslabs", ffOPTWR },
+                                          { efLOG, "-rt", "rottorque", ffOPTWR },
+                                          { efMTX, "-mtx", "nm", ffOPTWR },
+                                          { efRND, "-multidir", nullptr, ffOPTRDMULT },
+                                          { efXVG, "-awh", "awhinit", ffOPTRD },
+                                          { efDAT, "-membed", "membed", ffOPTRD },
+                                          { efTOP, "-mp", "membed", ffOPTRD },
+                                          { efNDX, "-mn", "membed", ffOPTRD },
+                                          { efXVG, "-if", "imdforces", ffOPTWR },
+                                          { efXVG, "-swap", "swapions", ffOPTWR } } };
+
+    //! Print a warning if any force is larger than this (in kJ/mol nm).
+    real pforce = -1;
+
+    //! The value of the -append option
+    bool appendOption = true;
+
+    /*! \brief Output context for writing text files
+     *
+     * \todo Clarify initialization, ownership, and lifetime. */
+    gmx_output_env_t* oenv = nullptr;
+
+    /*! \brief Command line options, defaults, docs and storage for them to fill. */
+    /*! \{ */
+    rvec        realddxyz                                                           = { 0, 0, 0 };
+    const char* ddrank_opt_choices[static_cast<int>(DdRankOrder::Count) + 1]        = { nullptr,
+                                                                                 "interleave",
+                                                                                 "pp_pme",
+                                                                                 "cartesian",
+                                                                                 nullptr };
+    const char* dddlb_opt_choices[static_cast<int>(DlbOption::Count) + 1]           = { nullptr,
+                                                                              "auto",
+                                                                              "no",
+                                                                              "yes",
+                                                                              nullptr };
+    const char* thread_aff_opt_choices[static_cast<int>(ThreadAffinity::Count) + 1] = { nullptr,
+                                                                                        "auto",
+                                                                                        "on",
+                                                                                        "off",
+                                                                                        nullptr };
+    const char* nbpu_opt_choices[5]    = { nullptr, "auto", "cpu", "gpu", nullptr };
+    const char* pme_opt_choices[5]     = { nullptr, "auto", "cpu", "gpu", nullptr };
+    const char* pme_fft_opt_choices[5] = { nullptr, "auto", "cpu", "gpu", nullptr };
+    const char* bonded_opt_choices[5]  = { nullptr, "auto", "cpu", "gpu", nullptr };
+    const char* update_opt_choices[5]  = { nullptr, "auto", "cpu", "gpu", nullptr };
+    const char* devicesSelectedByUser  = "";
+    const char* userGpuTaskAssignment  = "";
+
+
+    ImdOptions& imdOptions = mdrunOptions.imdOptions;
+
+    t_pargs pa[48] = {
+
+        { "-dd", FALSE, etRVEC, { &realddxyz }, "Domain decomposition grid, 0 is optimize" },
+        { "-ddorder", FALSE, etENUM, { ddrank_opt_choices }, "DD rank order" },
+        { "-npme",
+          FALSE,
+          etINT,
+          { &domdecOptions.numPmeRanks },
+          "Number of separate ranks to be used for PME, -1 is guess" },
+        { "-nt",
+          FALSE,
+          etINT,
+          { &hw_opt.nthreads_tot },
+          "Total number of threads to start (0 is guess)" },
+        { "-ntmpi",
+          FALSE,
+          etINT,
+          { &hw_opt.nthreads_tmpi },
+          "Number of thread-MPI ranks to start (0 is guess)" },
+        { "-ntomp",
+          FALSE,
+          etINT,
+          { &hw_opt.nthreads_omp },
+          "Number of OpenMP threads per MPI rank to start (0 is guess)" },
+        { "-ntomp_pme",
+          FALSE,
+          etINT,
+          { &hw_opt.nthreads_omp_pme },
+          "Number of OpenMP threads per MPI rank to start (0 is -ntomp)" },
+        { "-pin",
+          FALSE,
+          etENUM,
+          { thread_aff_opt_choices },
+          "Whether mdrun should try to set thread affinities" },
+        { "-pinoffset",
+          FALSE,
+          etINT,
+          { &hw_opt.core_pinning_offset },
+          "The lowest logical core number to which mdrun should pin the first thread" },
+        { "-pinstride",
+          FALSE,
+          etINT,
+          { &hw_opt.core_pinning_stride },
+          "Pinning distance in logical cores for threads, use 0 to minimize the number of threads "
+          "per physical core" },
+        { "-gpu_id",
+          FALSE,
+          etSTR,
+          { &devicesSelectedByUser },
+          "List of unique GPU device IDs available to use" },
+        { "-gputasks",
+          FALSE,
+          etSTR,
+          { &userGpuTaskAssignment },
+          "List of GPU device IDs, mapping each PP task on each node to a device" },
+        { "-ddcheck",
+          FALSE,
+          etBOOL,
+          { &domdecOptions.ddBondedChecking },
+          "Check for all bonded interactions with DD" },
+        { "-ddbondcomm",
+          FALSE,
+          etBOOL,
+          { &domdecOptions.useBondedCommunication },
+          "HIDDENUse special bonded atom communication when [TT]-rdd[tt] > cut-off" },
+        { "-rdd",
+          FALSE,
+          etREAL,
+          { &domdecOptions.minimumCommunicationRange },
+          "The maximum distance for bonded interactions with DD (nm), 0 is determine from initial "
+          "coordinates" },
+        { "-rcon",
+          FALSE,
+          etREAL,
+          { &domdecOptions.constraintCommunicationRange },
+          "Maximum distance for P-LINCS (nm), 0 is estimate" },
+        { "-dlb", FALSE, etENUM, { dddlb_opt_choices }, "Dynamic load balancing (with DD)" },
+        { "-dds",
+          FALSE,
+          etREAL,
+          { &domdecOptions.dlbScaling },
+          "Fraction in (0,1) by whose reciprocal the initial DD cell size will be increased in "
+          "order to "
+          "provide a margin in which dynamic load balancing can act while preserving the minimum "
+          "cell size." },
+        { "-ddcsx",
+          FALSE,
+          etSTR,
+          { &domdecOptions.cellSizeX },
+          "HIDDENA string containing a vector of the relative sizes in the x "
+          "direction of the corresponding DD cells. Only effective with static "
+          "load balancing." },
+        { "-ddcsy",
+          FALSE,
+          etSTR,
+          { &domdecOptions.cellSizeY },
+          "HIDDENA string containing a vector of the relative sizes in the y "
+          "direction of the corresponding DD cells. Only effective with static "
+          "load balancing." },
+        { "-ddcsz",
+          FALSE,
+          etSTR,
+          { &domdecOptions.cellSizeZ },
+          "HIDDENA string containing a vector of the relative sizes in the z "
+          "direction of the corresponding DD cells. Only effective with static "
+          "load balancing." },
+        { "-nb", FALSE, etENUM, { nbpu_opt_choices }, "Calculate non-bonded interactions on" },
+        { "-nstlist",
+          FALSE,
+          etINT,
+          { &nstlist_cmdline },
+          "Set nstlist when using a Verlet buffer tolerance (0 is guess)" },
+        { "-tunepme",
+          FALSE,
+          etBOOL,
+          { &mdrunOptions.tunePme },
+          "Optimize PME load between PP/PME ranks or GPU/CPU" },
+        { "-pme", FALSE, etENUM, { pme_opt_choices }, "Perform PME calculations on" },
+        { "-pmefft", FALSE, etENUM, { pme_fft_opt_choices }, "Perform PME FFT calculations on" },
+        { "-bonded", FALSE, etENUM, { bonded_opt_choices }, "Perform bonded calculations on" },
+        { "-update", FALSE, etENUM, { update_opt_choices }, "Perform update and constraints on" },
+        { "-v", FALSE, etBOOL, { &mdrunOptions.verbose }, "Be loud and noisy" },
+        { "-pforce", FALSE, etREAL, { &pforce }, "Print all forces larger than this (kJ/mol nm)" },
+        { "-reprod",
+          FALSE,
+          etBOOL,
+          { &mdrunOptions.reproducible },
+          "Try to avoid optimizations that affect binary reproducibility" },
+        { "-cpt",
+          FALSE,
+          etREAL,
+          { &mdrunOptions.checkpointOptions.period },
+          "Checkpoint interval (minutes)" },
+        { "-cpnum",
+          FALSE,
+          etBOOL,
+          { &mdrunOptions.checkpointOptions.keepAndNumberCheckpointFiles },
+          "Keep and number checkpoint files" },
+        { "-append",
+          FALSE,
+          etBOOL,
+          { &appendOption },
+          "Append to previous output files when continuing from checkpoint instead of adding the "
+          "simulation part number to all file names" },
+        { "-nsteps",
+          FALSE,
+          etINT64,
+          { &mdrunOptions.numStepsCommandline },
+          "Run this number of steps (-1 means infinite, -2 means use mdp option, smaller is "
+          "invalid)" },
+        { "-maxh",
+          FALSE,
+          etREAL,
+          { &mdrunOptions.maximumHoursToRun },
+          "Terminate after 0.99 times this time (hours)" },
+        { "-replex",
+          FALSE,
+          etINT,
+          { &replExParams.exchangeInterval },
+          "Attempt replica exchange periodically with this period (steps)" },
+        { "-nex",
+          FALSE,
+          etINT,
+          { &replExParams.numExchanges },
+          "Number of random exchanges to carry out each exchange interval (N^3 is one suggestion). "
+          " -nex zero or not specified gives neighbor replica exchange." },
+        { "-reseed",
+          FALSE,
+          etINT,
+          { &replExParams.randomSeed },
+          "Seed for replica exchange, -1 is generate a seed" },
+        { "-imdport", FALSE, etINT, { &imdOptions.port }, "HIDDENIMD listening port" },
+        { "-imdwait",
+          FALSE,
+          etBOOL,
+          { &imdOptions.wait },
+          "HIDDENPause the simulation while no IMD client is connected" },
+        { "-imdterm",
+          FALSE,
+          etBOOL,
+          { &imdOptions.terminatable },
+          "HIDDENAllow termination of the simulation from IMD client" },
+        { "-imdpull",
+          FALSE,
+          etBOOL,
+          { &imdOptions.pull },
+          "HIDDENAllow pulling in the simulation from IMD client" },
+        { "-rerunvsite",
+          FALSE,
+          etBOOL,
+          { &mdrunOptions.rerunConstructVsites },
+          "HIDDENRecalculate virtual site coordinates with [TT]-rerun[tt]" },
+        { "-confout",
+          FALSE,
+          etBOOL,
+          { &mdrunOptions.writeConfout },
+          "HIDDENWrite the last configuration with [TT]-c[tt] and force checkpointing at the last "
+          "step" },
+        { "-stepout",
+          FALSE,
+          etINT,
+          { &mdrunOptions.verboseStepPrintInterval },
+          "HIDDENFrequency of writing the remaining wall clock time for the run" },
+        { "-resetstep",
+          FALSE,
+          etINT,
+          { &mdrunOptions.timingOptions.resetStep },
+          "HIDDENReset cycle counters after these many time steps" },
+        { "-resethway",
+          FALSE,
+          etBOOL,
+          { &mdrunOptions.timingOptions.resetHalfway },
+          "HIDDENReset the cycle counters after half the number of steps or halfway "
+          "[TT]-maxh[tt]" }
+    };
+    /*! \} */
+
+    //! Parses the command-line input and prepares to start mdrun.
+    int updateFromCommandLine(int argc, char** argv, ArrayRef<const char*> desc);
+
+    ~LegacyMdrunOptions();
+};
+
+} // end namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdrun/legacysimulator.h b/src/include/gromacs/mdrun/legacysimulator.h
new file mode 100644 (file)
index 0000000..e6a654c
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal
+ * \brief Declares the simulator interface for mdrun
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_mdrun
+ */
+#ifndef GMX_MDRUN_LEGACYSIMULATOR_H
+#define GMX_MDRUN_LEGACYSIMULATOR_H
+
+#include <cstdio>
+
+#include <memory>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+#include "isimulator.h"
+
+namespace gmx
+{
+
+//! Function type for simulator code.
+using SimulatorFunctionType = void();
+
+/*! \internal
+ * \brief Struct to handle setting up and running the different simulation types.
+ *
+ * This struct is a mere aggregate of parameters to pass to run a
+ * simulation, so that future changes to names and types of them consume
+ * less time when refactoring other code.
+ *
+ * Having multiple simulation types as member functions isn't a good
+ * design, and we definitely only intend one to be called, but the
+ * goal is to make it easy to change the names and types of members
+ * without having to make identical changes in several places in the
+ * code. Once many of them have become modules, we should change this
+ * approach.
+ */
+class LegacySimulator : public ISimulator, private LegacySimulatorData
+{
+private:
+    //! Implements the normal MD simulations.
+    SimulatorFunctionType do_md;
+    //! Implements the rerun functionality.
+    SimulatorFunctionType do_rerun;
+    //! Implements steepest descent EM.
+    SimulatorFunctionType do_steep;
+    //! Implements conjugate gradient energy minimization
+    SimulatorFunctionType do_cg;
+    //! Implements onjugate gradient energy minimization using the L-BFGS algorithm
+    SimulatorFunctionType do_lbfgs;
+    //! Implements normal mode analysis
+    SimulatorFunctionType do_nm;
+    //! Implements test particle insertion
+    SimulatorFunctionType do_tpi;
+    //! Implements MiMiC QM/MM workflow
+    SimulatorFunctionType do_mimic;
+    // Use the constructor of the base class
+    using LegacySimulatorData::LegacySimulatorData;
+
+public:
+    // Only builder can construct
+    friend class SimulatorBuilder;
+
+    /*! \brief Function to run the correct SimulatorFunctionType,
+     * based on the .mdp integrator field. */
+    void run() override;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDRUN_LEGACYSIMULATOR_H
diff --git a/src/include/gromacs/mdrun/mdmodules.h b/src/include/gromacs/mdrun/mdmodules.h
new file mode 100644 (file)
index 0000000..1bd23fd
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::MDModules.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_mdrun
+ */
+#ifndef GMX_MDRUN_MDMODULES_H
+#define GMX_MDRUN_MDMODULES_H
+
+#include <memory>
+
+
+struct t_inputrec;
+
+namespace gmx
+{
+
+class ForceProviders;
+class KeyValueTreeObjectBuilder;
+class KeyValueTreeObject;
+class IKeyValueTreeErrorHandler;
+class IKeyValueTreeTransformRules;
+class IMDModule;
+class IMDOutputProvider;
+struct MDModulesNotifiers;
+
+/*! \libinternal \brief
+ * Manages the collection of all modules used for mdrun.
+ *
+ * This class acts as a central place for constructing modules for mdrun
+ * and wiring up dependencies between them.  This class should be the only
+ * place that contains the full list of modules, although in the future, some
+ * code (e.g., in tools) may benefit from the ability to only create one or a
+ * few modules and use them.
+ *
+ * The general idea is that each module takes care of its own data rather than
+ * mdrun having to know about all the details of each type of force calculation.
+ * Initially this is applied for simple things like electric field calculations
+ * but later more complex forces will be supported too.
+ *
+ * Currently, where the set of modules needs to be accessed, either a pointer
+ * to MDModules is passed around, or an instance of IMDOutputProvider or
+ * ForceProviders returned from MDModules.  These objects returned from
+ * MDModules call the corresponding methods in the relevant modules.
+ * In the future, some additional logic may need to be introduced at
+ * the call sites that can also influence the signature of the methods,
+ * similar to what ForceProviders already does for force computation.
+ *
+ * The assignOptionsToModules() and adjustInputrecBasedOnModules() methods of
+ * this class also take responsibility for wiring up the options (and their
+ * defaults) for each module.
+ *
+ * \inlibraryapi
+ * \ingroup module_mdrunutility
+ */
+class MDModules
+{
+public:
+    MDModules();
+    ~MDModules();
+
+    /*! \brief
+     * Initializes a transform from mdp values to sectioned options.
+     *
+     * \see IMdpOptionProvider::initMdpTransform()
+     *
+     * Initializes the combined transform from all modules.
+     */
+    void initMdpTransform(IKeyValueTreeTransformRules* rules);
+
+    /*! \brief Initializes a builder of flat mdp-style key-value pairs
+     * suitable for output.
+     *
+     * If used as input to initMdpTransform(), the key-value pairs
+     * resulting from this function would leave the module
+     * settings unchanged.
+     *
+     * Once the transition from mdp to key-value input is
+     * complete, this method will probably not exist.
+     */
+    void buildMdpOutput(KeyValueTreeObjectBuilder* builder);
+
+    /*! \brief
+     * Sets input parameters from `params` for each module.
+     *
+     * \param[in]  params  Contains keys and values from user
+     *     input (and defaults) to configure modules that have
+     *     registered options with those keys.
+     * \param[out] errorHandler  Called to report errors.
+     */
+    void assignOptionsToModules(const KeyValueTreeObject& params, IKeyValueTreeErrorHandler* errorHandler);
+
+    /*! \brief
+     * Normalizes inputrec parameters to match current code version.
+     *
+     * This orders the parameters in `ir->param` to match the current code
+     * and adds any missing defaults.  It also throws an error if the
+     * inputrec contains parameters that are not recognized by any module.
+     */
+    void adjustInputrecBasedOnModules(t_inputrec* ir);
+
+    /*! \brief
+     * Returns an interface for initializing and finalizing output for modules.
+     */
+    IMDOutputProvider* outputProvider();
+    /*! \brief
+     * Returns an object for computing forces from the modules.
+     */
+    ForceProviders* initForceProviders();
+
+    /*! \brief Subscribe MDModules to simulation setup notifications.
+     *
+     * Allows MDModules to subscribe to notifications that are called back
+     * during the set up of an MD simulation, after the options were
+     * assigned to the modules.
+     */
+    void subscribeToSimulationSetupNotifications();
+
+    /*! \brief Subscribe MDModules to notifications during pre-processing.
+     *
+     * Allows MDModules to subscribe to notifications that are called back
+     * during pre processing an MD simulation, after the options were
+     * assigned to the modules.
+     */
+    void subscribeToPreProcessingNotifications();
+
+    /*!
+     * \brief Add a module to the container.
+     *
+     * An object may be added by a client to the bound MD Modules at run time.
+     * Both the client and the MDModules object may need to extend the life
+     * of the provided object. However, the MDModules container guarantees
+     * to extend the life of a provided object for as long as its consumers
+     * may attempt to use its the interfaces accessible through IMDModule
+     * methods.
+     *
+     * \param module implements some sort of modular functionality for MD.
+     *
+     * \note: There is not yet a way to add a IMDModule object between
+     * creation of the MDModules container and the execution of the various
+     * initialization protocols it supports.
+     *
+     * \internal
+     * Adding a module at an arbitrary point in the MDModules life breaks
+     * some assumptions in the protocol of the other member functions. If
+     * MDModules should not change after some point, we should move this
+     * to a builder class.
+     */
+    void add(std::shared_ptr<IMDModule> module);
+
+    /*! \brief Return a handle to the notifiers used for callbacks between modules.
+     */
+    const MDModulesNotifiers& notifiers();
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdrun/membedholder.h b/src/include/gromacs/mdrun/membedholder.h
new file mode 100644 (file)
index 0000000..a941394
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal
+ * \brief Encapsulates membed methods
+ *
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \ingroup module_mdrun
+ */
+#ifndef GMX_MDRUN_MEMBEDHOLDER_H
+#define GMX_MDRUN_MEMBEDHOLDER_H
+
+#include <cstdio>
+
+#include "gromacs/utility/real.h"
+
+struct gmx_membed_t;
+struct gmx_mtop_t;
+struct t_commrec;
+struct t_filenm;
+struct t_inputrec;
+class t_state;
+
+namespace gmx
+{
+
+/*! \brief Membed SimulatorBuilder parameter type.
+ *
+ * Does not (yet) encapsulate ownership semantics of resources. Simulator is
+ * not (necessarily) granted ownership of resources. Client is responsible for
+ * maintaining the validity of resources for the life time of the Simulator,
+ * then for cleaning up those resources.
+ */
+class MembedHolder
+{
+public:
+    //! Build holder from input information.
+    explicit MembedHolder(int nfile, const t_filenm fnm[]);
+    //! Move is possible.
+    MembedHolder(MembedHolder&& holder) noexcept;
+    //! Move assignment is possible.
+    MembedHolder& operator=(MembedHolder&& holder) noexcept;
+    //! Copy is not allowed.
+    MembedHolder(const MembedHolder&) = delete;
+    //! Copy assignment is not allowed.
+    MembedHolder& operator=(const MembedHolder&) = delete;
+
+    ~MembedHolder();
+
+    //! Get information about membed being used.
+    [[nodiscard]] bool doMembed() const { return doMembed_; }
+
+    /*! \brief
+     * Fully initialize underlying datastructure.
+     *
+     * \param[in] fplog Handle to log file.
+     * \param[in] nfile How many input files are there.
+     * \param[in] fnm   Input file collection datastructure.
+     * \param[in,out] mtop  Handle to mtop, can be modified.
+     * \param[in,out] inputrec Handle to inputrec, can be modified.
+     * \param[in,out] state    Simulation state information, can be modified.
+     * \param[in,out] cr       Communication information.
+     * \param[out]    cpt      Some kind of checkpoint information.
+     */
+    void initializeMembed(FILE*          fplog,
+                          int            nfile,
+                          const t_filenm fnm[],
+                          gmx_mtop_t*    mtop,
+                          t_inputrec*    inputrec,
+                          t_state*       state,
+                          t_commrec*     cr,
+                          real*          cpt);
+
+    //! Get handle to membed object.
+    gmx_membed_t* membed();
+
+private:
+    //! Pointer to membed object. TODO replace with unique_ptr or get rid of this.
+    gmx_membed_t* membed_ = nullptr;
+    //! Whether membed is being used.
+    bool doMembed_ = false;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDRUN_MEMBEDHOLDER_H
diff --git a/src/include/gromacs/mdrun/minimize.h b/src/include/gromacs/mdrun/minimize.h
new file mode 100644 (file)
index 0000000..836760e
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \brief Declares the integrators for energy minimization and NMA
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \ingroup module_mdrun
+ */
+#ifndef GMX_MDRUN_MINIMIZE_H
+#define GMX_MDRUN_MINIMIZE_H
+
+#include "legacysimulator.h"
+
+namespace gmx
+{
+
+} // namespace gmx
+
+#endif // GMX_MDRUN_MINIMIZE_H
diff --git a/src/include/gromacs/mdrun/replicaexchange.h b/src/include/gromacs/mdrun/replicaexchange.h
new file mode 100644 (file)
index 0000000..e8cb9bd
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declares the routines for replica exchange.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ *
+ * \ingroup module_mdrun
+ */
+#ifndef GMX_MDRUN_REPLICAEXCHANGE_H
+#define GMX_MDRUN_REPLICAEXCHANGE_H
+
+#include <cstdio>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_enerdata_t;
+struct gmx_multisim_t;
+struct t_commrec;
+struct t_inputrec;
+class t_state;
+
+/*! \libinternal
+ * \brief The parameters for the replica exchange algorithm. */
+struct ReplicaExchangeParameters
+{
+    //! Interval in steps at which to attempt exchanges, 0 means no replica exchange.
+    int exchangeInterval = 0;
+    //! The number of exchanges to attempt at an exchange step.
+    int numExchanges = 0;
+    //! The random seed, -1 means generate a seed.
+    int randomSeed = -1;
+};
+
+//! Abstract type for replica exchange
+typedef struct gmx_repl_ex* gmx_repl_ex_t;
+
+/*! \brief Setup function.
+ *
+ * Should only be called on the master ranks */
+gmx_repl_ex_t init_replica_exchange(FILE*                            fplog,
+                                    const gmx_multisim_t*            ms,
+                                    int                              numAtomsInSystem,
+                                    const t_inputrec*                ir,
+                                    const ReplicaExchangeParameters& replExParams);
+
+/*! \brief Attempts replica exchange.
+ *
+ * Should be called on all ranks.  When running each replica in
+ * parallel, this routine collects the state on the master rank before
+ * exchange.  With domain decomposition, the global state after
+ * exchange is stored in state and still needs to be redistributed
+ * over the ranks.
+ *
+ * \returns TRUE if the state has been exchanged.
+ */
+gmx_bool replica_exchange(FILE*                 fplog,
+                          const t_commrec*      cr,
+                          const gmx_multisim_t* ms,
+                          gmx_repl_ex_t         re,
+                          t_state*              state,
+                          const gmx_enerdata_t* enerd,
+                          t_state*              state_local,
+                          int64_t               step,
+                          real                  time);
+
+/*! \brief Prints replica exchange statistics to the log file.
+ *
+ * Should only be called on the master ranks */
+void print_replica_exchange_statistics(FILE* fplog, gmx_repl_ex_t re);
+
+#endif
diff --git a/src/include/gromacs/mdrun/runner.h b/src/include/gromacs/mdrun/runner.h
new file mode 100644 (file)
index 0000000..a0c9cdc
--- /dev/null
@@ -0,0 +1,642 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declares the routine running the inetgrators.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \ingroup module_mdrun
+ */
+#ifndef GMX_MDRUN_RUNNER_H
+#define GMX_MDRUN_RUNNER_H
+
+#include <cstdio>
+
+#include <array>
+#include <memory>
+
+#include "gromacs/commandline/filenm.h"
+#include "gromacs/compat/pointers.h"
+#include "gromacs/domdec/options.h"
+#include "gromacs/hardware/hw_info.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/mdrun/mdmodules.h"
+#include "gromacs/mdrun/simulationinputhandle.h"
+#include "gromacs/mdrunutility/handlerestart.h"
+#include "gromacs/mdtypes/mdrunoptions.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxmpi.h"
+#include "gromacs/utility/real.h"
+
+#include "replicaexchange.h"
+
+struct gmx_multisim_t;
+struct gmx_output_env_t;
+struct ReplicaExchangeParameters;
+struct t_fileio;
+
+namespace gmx
+{
+
+// Todo: move to forward declaration headers...
+class MDModules;
+class IRestraintPotential; // defined in restraint/restraintpotential.h
+class RestraintManager;
+class SimulationContext;
+class StopHandlerBuilder;
+
+//! Work-around for GCC bug 58265 still present in CentOS 7 devtoolset-7
+constexpr bool BUGFREE_NOEXCEPT_STRING = std::is_nothrow_move_assignable<std::string>::value;
+
+/*! \libinternal \brief Runner object for supporting setup and execution of mdrun.
+ *
+ * This class has responsibility for the lifetime of data structures
+ * that exist for the life of the simulation, e.g. for logging and
+ * communication.
+ *
+ * It is also responsible for initializing data members that
+ * e.g. correspond to values potentially set by commmand-line
+ * options. Later these will be obtained directly from modules, and
+ * the results of command-line option handling returned directly to
+ * the modules, rather than propagated to them by data members of this
+ * class.
+ *
+ * \todo Most of the attributes should be declared by specific modules
+ * as command-line options. Accordingly, they do not conform to the
+ * naming scheme, because that would make for a lot of noise in the
+ * diff, only to have it change again when the options move to their
+ * modules.
+ *
+ * \todo Preparing logging and MPI contexts could probably be a
+ * higher-level responsibility, so that an Mdrunner would get made
+ * without needing to re-initialize these components (as currently
+ * happens always for the master rank, and differently for the spawned
+ * ranks with thread-MPI).
+ *
+ * \ingroup module_mdrun
+ */
+class Mdrunner
+{
+public:
+    /*! \brief Builder class to manage object creation.
+     *
+     * This class is a member of gmx::Mdrunner so that it can initialize
+     * private members of gmx::Mdrunner.
+     *
+     * It is non-trivial to establish an initialized gmx::Mdrunner invariant,
+     * so objects can be obtained by clients using a Builder or a move.
+     * Clients cannot default initialize or copy gmx::Mdrunner.
+     */
+    class BuilderImplementation;
+
+    ~Mdrunner();
+
+    /*!
+     * \brief Copy not allowed.
+     *
+     * An Mdrunner has unique resources and it is not clear whether any of
+     * one of those resources should be duplicated or shared unless the
+     * specific use case is known. Either build a fresh runner or use a
+     * helper function for clearly indicated behavior. API clarification may
+     * allow unambiguous initialization by copy in future versions.
+     *
+     * \{
+     */
+    Mdrunner(const Mdrunner&) = delete;
+    Mdrunner& operator=(const Mdrunner&) = delete;
+    /* \} */
+
+    /*!
+     * \brief Mdrunner objects can be passed by value via move semantics.
+     *
+     * \param handle runner instance to be moved from.
+     * \{
+     */
+    Mdrunner(Mdrunner&& handle) noexcept;
+    //NOLINTNEXTLINE(performance-noexcept-move-constructor) working around GCC bug 58265 in CentOS 7
+    Mdrunner& operator=(Mdrunner&& handle) noexcept(BUGFREE_NOEXCEPT_STRING);
+    /* \} */
+
+    /*! \brief Driver routine, that calls the different simulation methods. */
+    /*!
+     * Currently, thread-MPI does not spawn threads until during mdrunner() and parallelism
+     * is not initialized until some time during this call...
+     */
+    int mdrunner();
+
+    /*!
+     * \brief Add a potential to be evaluated during MD integration.
+     *
+     * \param restraint MD restraint potential to apply
+     * \param name User-friendly plain-text name to uniquely identify the puller
+     *
+     * This implementation attaches an object providing the gmx::IRestraintPotential
+     * interface.
+     * \todo Mdrunner should fetch such resources from the SimulationContext
+     * rather than offering this public interface.
+     */
+    void addPotential(std::shared_ptr<IRestraintPotential> restraint, const std::string& name);
+
+    /*! \brief Prepare the thread-MPI communicator to have \c
+     * numThreadsToLaunch ranks, by spawning new 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. */
+    void spawnThreads(int numThreadsToLaunch);
+
+    /*! \brief Initializes a new Mdrunner from the master.
+     *
+     * Run this method in a new thread from a master runner to get additional
+     * workers on spawned threads.
+     *
+     * \returns New Mdrunner instance suitable for thread-MPI work on new ranks.
+     *
+     * \internal
+     * \todo clarify (multiple) invariants during MD runner start-up.
+     * The runner state before and after launching threads is distinct enough that
+     * it should be codified in the invariants of different classes. That would
+     * mean that the object returned by this method would be of a different type
+     * than the object held by the client up to the point of call, and its name
+     * would be changed to "launchOnSpawnedThread" or something not including the
+     * word "clone".
+     */
+    Mdrunner cloneOnSpawnedThread() const;
+
+private:
+    /*! \brief Constructor. */
+    explicit Mdrunner(std::unique_ptr<MDModules> mdModules);
+
+    //! Parallelism-related user options.
+    gmx_hw_opt_t hw_opt;
+
+    //! Filenames and properties from command-line argument values.
+    ArrayRef<const t_filenm> filenames;
+
+    /*! \brief Output context for writing text files
+     *
+     * \internal
+     * \todo push this data member down when the information can be queried from an encapsulated resource.
+     */
+    gmx_output_env_t* oenv = nullptr;
+    //! Ongoing collection of mdrun options
+    MdrunOptions mdrunOptions;
+    //! Options for the domain decomposition.
+    DomdecOptions domdecOptions;
+
+    /*! \brief Target short-range interations for "cpu", "gpu", or "auto". Default is "auto".
+     *
+     * \internal
+     * \todo replace with string or enum class and initialize with sensible value.
+     */
+    const char* nbpu_opt = nullptr;
+
+    /*! \brief Target long-range interactions for "cpu", "gpu", or "auto". Default is "auto".
+     *
+     * \internal
+     * \todo replace with string or enum class and initialize with sensible value.
+     */
+    const char* pme_opt = nullptr;
+
+    /*! \brief Target long-range interactions FFT/solve stages for "cpu", "gpu", or "auto". Default is "auto".
+     *
+     * \internal
+     * \todo replace with string or enum class and initialize with sensible value.
+     */
+    const char* pme_fft_opt = nullptr;
+
+    /*! \brief Target bonded interations for "cpu", "gpu", or "auto". Default is "auto".
+     *
+     * \internal
+     * \todo replace with string or enum class and initialize with sensible value.
+     */
+    const char* bonded_opt = nullptr;
+
+    /*! \brief Target update calculation for "cpu", "gpu", or "auto". Default is "auto".
+     *
+     * \internal
+     * \todo replace with string or enum class and initialize with sensible value.
+     */
+    const char* update_opt = nullptr;
+
+    //! Command-line override for the duration of a neighbor list with the Verlet scheme.
+    int nstlist_cmdline = 0;
+    //! Parameters for replica-exchange simulations.
+    ReplicaExchangeParameters replExParams;
+    //! Print a warning if any force is larger than this (in kJ/mol nm).
+    real pforce = -1;
+
+    //! Handle to file used for logging.
+    LogFilePtr logFileGuard = nullptr;
+    //! \brief Non-owning handle to file used for logging.
+    t_fileio* logFileHandle = nullptr;
+
+    /*! \brief Non-owning handle to world communication data structure for task assigment.
+     *
+     * With real MPI, gets a value from the SimulationContext
+     * supplied to the MdrunnerBuilder. With thread-MPI gets a
+     * value after threads have been spawned. */
+    MPI_Comm libraryWorldCommunicator = MPI_COMM_NULL;
+
+    /*! \brief Non-owning handle to communication data structure for the current simulation.
+     *
+     * With real MPI, gets a value from the SimulationContext
+     * supplied to the MdrunnerBuilder. With thread-MPI gets a
+     * value after threads have been spawned. */
+    MPI_Comm simulationCommunicator = MPI_COMM_NULL;
+
+    //! \brief Non-owning handle to multi-simulation handler.
+    gmx_multisim_t* ms = nullptr;
+
+    //! Whether the simulation will start afresh, or restart with/without appending.
+    StartingBehavior startingBehavior = StartingBehavior::NewSimulation;
+
+    /*!
+     * \brief Handle to restraints manager for the current process.
+     *
+     * \internal
+     * Use opaque pointer for this implementation detail.
+     */
+    std::unique_ptr<RestraintManager> restraintManager_;
+
+    /*!
+     * \brief Builder for stop signal handler
+     *
+     * Optionally provided through MdrunnerBuilder. Client may create a
+     * StopHandlerBuilder and register any number of signal providers before
+     * launching the Mdrunner.
+     *
+     * Default is an empty signal handler that will have local signal issuers
+     * added after being passed into the integrator.
+     *
+     * \internal
+     * We do not need a full type specification here, so we use an opaque pointer.
+     */
+    std::unique_ptr<StopHandlerBuilder> stopHandlerBuilder_;
+    //! The modules that comprise mdrun.
+    std::unique_ptr<MDModules> mdModules_;
+
+    //! Non-owning handle to the results of the hardware detection.
+    const gmx_hw_info_t* hwinfo_ = nullptr;
+
+    /*!
+     * \brief Holds simulation input specification provided by client, if any.
+     *
+     * If present on any instance (rank) of a simulation runner, an identical
+     * (or compatible) SimulationInput must be held on all cooperating instances.
+     */
+    SimulationInputHandle inputHolder_;
+};
+
+/*! \libinternal
+ * \brief Build a gmx::Mdrunner.
+ *
+ * Client code (such as `gmx mdrun`) uses this builder to get an initialized Mdrunner.
+ *
+ * A builder allows the library to ensure that client code cannot obtain an
+ * uninitialized or partially initialized runner by refusing to build() if the
+ * client has not provided sufficient or self-consistent direction. Director
+ * code can be implemented for different user interfaces, encapsulating any
+ * run-time functionality that does not belong in the library MD code, such
+ * as command-line option processing or interfacing to external libraries.
+ *
+ * \ingroup module_mdrun
+ *
+ * \internal
+ *
+ * The initial Builder implementation is neither extensible at run time nor
+ * at compile time. Future implementations should evolve to compose the runner,
+ * rather than just consolidating the parameters for initialization, but there
+ * is not yet a firm design for how flexibly module code will be coupled to
+ * the builder and how much of the client interface will be in this Builder
+ * versus Builders provided by the various modules.
+ *
+ * The named components for the initial builder implementation are descriptive
+ * of the state of mdrun at the time, and are not intended to be prescriptive of
+ * future design.
+ * The probable course of GROMACS development is for the modular components that
+ * support MD simulation to independently express their input parameters (required
+ * and optional) and to provide some sort of help to the UI for input preparation.
+ * If each module provides or aids the instantiation of a Director
+ * for the client code, the Directors could be constructed with a handle to this
+ * Builder and it would not need a public interface.
+ *
+ * As the modules are more clearly encapsulated, each module can provide its own
+ * builder, user interface helpers, and/or composable Director code.
+ * The runner and client code will also have to be updated as appropriate
+ * default behavior is clarified for
+ * (a) default behavior of client when user does not provide input,
+ * (b) default behavior of builder when client does not provide input, and
+ * (c) default behavior of runner when builder does not provide input.
+ */
+class MdrunnerBuilder final
+{
+public:
+    /*!
+     * \brief Constructor requires a handle to a SimulationContext to share.
+     *
+     * \param mdModules  The handle to the set of modules active in mdrun
+     * \param context    Required handle to simulation context
+     *
+     * The calling code must guarantee that the
+     * pointer remains valid for the lifetime of the builder, and that the
+     * resources retrieved from the context remain valid for the lifetime of
+     * the runner produced.
+     */
+    explicit MdrunnerBuilder(std::unique_ptr<MDModules>           mdModules,
+                             compat::not_null<SimulationContext*> context);
+
+    //! \cond
+    MdrunnerBuilder()                       = delete;
+    MdrunnerBuilder(const MdrunnerBuilder&) = delete;
+    MdrunnerBuilder& operator=(const MdrunnerBuilder&) = delete;
+    //! \endcond
+
+    /*! \brief Allow transfer of ownership with move semantics.
+     *
+     * \param builder source object to transfer.
+     *
+     * \{
+     */
+    MdrunnerBuilder(MdrunnerBuilder&& builder) noexcept;
+    MdrunnerBuilder& operator=(MdrunnerBuilder&& builder) noexcept;
+    //! \}
+
+    /*!
+     * \brief Get ownership of an initialized gmx::Mdrunner.
+     *
+     * After build() is called, the Builder object should not be used
+     * again. It is an error to call build without first calling all builder
+     * methods described as "required."
+     *
+     * \return A new Mdrunner.
+     *
+     * \throws APIError if a required component has not been added before calling build().
+     */
+    Mdrunner build();
+
+    /*!
+     * \brief Supply the result of hardware detection to the gmx::Mdrunner
+     *
+     * \param hwinfo  Non-owning not-null handle to result of hardware detection.
+     *
+     * \todo It would be better to express this as either a not-null const pointer or
+     * a const reference, but neither of those is consistent with incremental
+     * building of an object. This motivates future work to be able to make a deep copy
+     * of the detection result. See https://gitlab.com/gromacs/gromacs/-/issues/3650 */
+    MdrunnerBuilder& addHardwareDetectionResult(const gmx_hw_info_t* hwinfo);
+
+    /*!
+     * \brief Set up non-bonded short-range force calculations.
+     *
+     * Required. Director code must provide valid options for the non-bonded
+     * interaction code. The builder does not apply any defaults.
+     *
+     * \param nbpu_opt Target short-range interactions for "cpu", "gpu", or "auto".
+     *
+     * Calling must guarantee that the pointed-to C string is valid through
+     * simulation launch.
+     *
+     * \internal
+     * \todo Replace with string or enum that we can have sensible defaults for.
+     * \todo Either the Builder or modular Director code should provide sensible defaults.
+     */
+    MdrunnerBuilder& addNonBonded(const char* nbpu_opt);
+
+    /*!
+     * \brief Set up long-range electrostatics calculations.
+     *
+     * Required. Director code should provide valid options for PME electrostatics,
+     * whether or not PME electrostatics are used. The builder does not apply
+     * any defaults, so client code should be prepared to provide (e.g.) "auto"
+     * in the event no user input or logic provides an alternative argument.
+     *
+     * \param pme_opt Target long-range interactions for "cpu", "gpu", or "auto".
+     * \param pme_fft_opt Target long-range interactions FFT/solve stages for "cpu", "gpu", or "auto".
+     *
+     * Calling must guarantee that the pointed-to C strings are valid through
+     * simulation launch.
+     *
+     * \internal
+     * The arguments are passed as references to elements of arrays of C strings.
+     * \todo Replace with modern strings or (better) enum classes.
+     * \todo Make optional and/or encapsulate into electrostatics module.
+     */
+    MdrunnerBuilder& addElectrostatics(const char* pme_opt, const char* pme_fft_opt);
+
+    /*!
+     * \brief Assign responsibility for tasks for bonded interactions.
+     *
+     * Required. Director code should provide valid options for
+     * bonded interaction task assignment, whether or not such
+     * interactions are present. The builder does not apply any
+     * defaults, so client code should be prepared to provide
+     * (e.g.) "auto" in the event no user input or logic provides
+     * an alternative argument.
+     *
+     * \param bonded_opt Target bonded interactions for "cpu", "gpu", or "auto".
+     *
+     * Calling must guarantee that the pointed-to C strings are valid through
+     * simulation launch.
+     *
+     * \internal
+     * The arguments are passed as references to elements of arrays of C strings.
+     * \todo Replace with modern strings or (better) enum classes.
+     * \todo Make optional and/or encapsulate into task assignment module.
+     */
+    MdrunnerBuilder& addBondedTaskAssignment(const char* bonded_opt);
+
+    /*! \brief
+     * Assign responsibility for tasks for update and constrain calculation.
+     *
+     * Required. Director code should provide valid options for
+     * update and constraint task assignment. The builder does not apply any
+     * defaults, so client code should be prepared to provide
+     * (e.g.) "auto" in the event no user input or logic provides
+     * an alternative argument.
+     *
+     * \param[in] update_opt Target update calculation for "cpu", "gpu", or "auto".
+     *
+     * Calling must guarantee that the pointed-to C strings are valid through
+     * simulation launch.
+     *
+     * \internal
+     * The arguments are passed as references to elements of arrays of C strings.
+     * \todo Replace with modern strings or (better) enum classes.
+     * \todo Make optional and/or encapsulate into task assignment module.
+     */
+    MdrunnerBuilder& addUpdateTaskAssignment(const char* update_opt);
+
+    /*!
+     * \brief Set MD options not owned by some other module.
+     *
+     * Optional. Override simulation parameters
+     *
+     * \param options structure to copy
+     * \param forceWarningThreshold Print a warning if any force is larger than this (in kJ/mol nm) (default -1)
+     * \param startingBehavior Whether the simulation will start afresh, or restart with/without appending.
+     *
+     * \internal
+     * \todo Map these parameters to more appropriate encapsulating types.
+     * Find a better way to indicate "unspecified" than a magic value of the parameter type.
+     */
+    MdrunnerBuilder& addSimulationMethod(const MdrunOptions& options,
+                                         real                forceWarningThreshold,
+                                         StartingBehavior    startingBehavior);
+
+    /*!
+     * \brief Set the domain decomposition module.
+     *
+     * Optional. Overrides default constructed DomdecOptions if provided.
+     *
+     * \param options options with which to construct domain decomposition.
+     *
+     * \internal
+     * \todo revisit whether we should be passing this parameter struct or a higher-level handle of some sort.
+     */
+    MdrunnerBuilder& addDomainDecomposition(const DomdecOptions& options);
+
+    /*!
+     * \brief Set Verlet list manager.
+     *
+     * Optional. Neighbor list existence, type, and parameters are mostly determined
+     * by the simulation parameters loaded elsewhere. This is just an override.
+     *
+     * \param rebuildInterval override for the duration of a neighbor list with the Verlet scheme.
+     */
+    MdrunnerBuilder& addNeighborList(int rebuildInterval);
+
+    /*!
+     * \brief Set replica exchange manager.
+     *
+     * Optional. For guidance on preparing a valid ReplicaExchangeParameters
+     * value, refer to the details in mdrun.cpp, the `t_pargs pa[]` defined there,
+     * and the action of parse_common_args() with regards to that structure.
+     * If not provided by client, a default constructed ReplicaExchangeParameters
+     * is used.
+     *
+     * \param params parameters with which to set up replica exchange.
+     *
+     * \internal
+     * \todo revisit whether we should be passing this parameter struct or a higher-level handle of some sort.
+     */
+    MdrunnerBuilder& addReplicaExchange(const ReplicaExchangeParameters& params);
+
+    /*!
+     * \brief Specify parameters determining hardware resource allocation.
+     *
+     * Optional. If not provided, default-constructed gmx_hw_opt_t will be used.
+     *
+     * \param hardwareOptions Parallelism-related user options.
+     */
+    MdrunnerBuilder& addHardwareOptions(const gmx_hw_opt_t& hardwareOptions);
+
+    /*!
+     * \brief Provide the filenames options structure with option values chosen
+     *
+     * Required. The object is assumed to have been updated by
+     * parse_common_args or equivalent.
+     *
+     * \param filenames Filenames and properties from command-line argument values or defaults.
+     *
+     * \internal
+     * \todo Modules should manage their own filename options and defaults.
+     */
+    MdrunnerBuilder& addFilenames(ArrayRef<const t_filenm> filenames);
+
+    /*!
+     * \brief Provide parameters for setting up output environment.
+     *
+     * Required. Handle is assumed to have been produced by output_env_init
+     * as in parse_common_args.
+     *
+     * \param outputEnvironment Output context for writing text files.
+     *
+     * \internal
+     * \todo Allow client code to set up output environment and provide as a resource.
+     * This parameter is used to set up resources that are dependent on the execution
+     * environment and API context. Such resources should be retrieved by the simulator
+     * from a client-provided resource, but currently the resources are only fully
+     * initialized in Mdrunner.
+     */
+    MdrunnerBuilder& addOutputEnvironment(gmx_output_env_t* outputEnvironment);
+
+    /*!
+     * \brief Provide the filehandle pointer to be used for the MD log.
+     *
+     * Required. Either nullptr if no log should be written, or
+     * valid and open reading for writing.
+     *
+     * \param logFileHandle Non-owning handle to file used for logging.
+     * \internal
+     */
+    MdrunnerBuilder& addLogFile(t_fileio* logFileHandle);
+
+    /*!
+     * \brief Provide a StopHandlerBuilder for the MD stop signal handling.
+     *
+     * Optional. Defaults to empty.
+     *
+     * Client may provide additional (non-default) issuers of simulation stop
+     * signals by preconfiguring the StopHandlerBuilder used later when the
+     * simulation runs.
+     *
+     * \param builder
+     */
+    MdrunnerBuilder& addStopHandlerBuilder(std::unique_ptr<StopHandlerBuilder> builder);
+
+    /*!
+     * \brief Acquire a handle to the SimulationInput.
+     *
+     * Required. SimulationInput will be taking responsibility for some of the
+     * input provided through other methods, such as addFilenames.
+     *
+     * See also issue https://gitlab.com/gromacs/gromacs/-/issues/3374
+     *
+     * \param input Shared ownership of a SimulationInput.
+     */
+    MdrunnerBuilder& addInput(SimulationInputHandle input);
+
+    ~MdrunnerBuilder();
+
+private:
+    std::unique_ptr<Mdrunner::BuilderImplementation> impl_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDRUN_RUNNER_H
diff --git a/src/include/gromacs/mdrun/shellfc.h b/src/include/gromacs/mdrun/shellfc.h
new file mode 100644 (file)
index 0000000..e48874f
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDLIB_SHELLFC_H
+#define GMX_MDLIB_SHELLFC_H
+
+#include <cstdio>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/timing/wallcycle.h"
+#include "gromacs/topology/atoms.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+class DDBalanceRegionHandler;
+struct gmx_enerdata_t;
+struct gmx_enfrot;
+struct gmx_localtop_t;
+struct gmx_multisim_t;
+struct gmx_shellfc_t;
+struct gmx_mtop_t;
+class history_t;
+struct pull_t;
+struct t_forcerec;
+struct t_inputrec;
+struct t_mdatoms;
+struct t_nrnb;
+class t_state;
+class CpuPpLongRangeNonbondeds;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+template<typename>
+class ArrayRefWithPadding;
+class Constraints;
+class ForceBuffersView;
+class ImdSession;
+class MdrunScheduleWorkload;
+class VirtualSitesHandler;
+} // namespace gmx
+
+/*! \brief Initialization function, also predicts the initial shell positions.
+ *
+ * \param fplog Pointer to the log stream. Can be set to \c nullptr to disable verbose log.
+ * \param mtop Pointer to a global system topology object.
+ * \param nflexcon Number of flexible constraints.
+ * \param nstcalcenergy How often are energies calculated. Must be provided for sanity check.
+ * \param usingDomainDecomposition Whether domain decomposition is used. Must be provided for sanity check.
+ * \param usingPmeOnGpu Set to true if GPU will be used for PME calculations. Necessary for proper buffer initialization.
+ *
+ * \returns a pointer to an initialized \c shellfc object.
+ */
+gmx_shellfc_t* init_shell_flexcon(FILE*             fplog,
+                                  const gmx_mtop_t& mtop,
+                                  int               nflexcon,
+                                  int               nstcalcenergy,
+                                  bool              usingDomainDecomposition,
+                                  bool              usingPmeOnGpu);
+
+/* Optimize shell positions */
+void relax_shell_flexcon(FILE*                               log,
+                         const t_commrec*                    cr,
+                         const gmx_multisim_t*               ms,
+                         gmx_bool                            bVerbose,
+                         gmx_enfrot*                         enforcedRotation,
+                         int64_t                             mdstep,
+                         const t_inputrec*                   inputrec,
+                         gmx::ImdSession*                    imdSession,
+                         pull_t*                             pull_work,
+                         gmx_bool                            bDoNS,
+                         int                                 force_flags,
+                         const gmx_localtop_t*               top,
+                         gmx::Constraints*                   constr,
+                         gmx_enerdata_t*                     enerd,
+                         int                                 natoms,
+                         gmx::ArrayRefWithPadding<gmx::RVec> x,
+                         gmx::ArrayRefWithPadding<gmx::RVec> v,
+                         const matrix                        box,
+                         gmx::ArrayRef<real>                 lambda,
+                         const history_t*                    hist,
+                         gmx::ForceBuffersView*              f,
+                         tensor                              force_vir,
+                         const t_mdatoms&                    md,
+                         CpuPpLongRangeNonbondeds*           longRangeNonbondeds,
+                         t_nrnb*                             nrnb,
+                         gmx_wallcycle*                      wcycle,
+                         gmx_shellfc_t*                      shfc,
+                         t_forcerec*                         fr,
+                         gmx::MdrunScheduleWorkload*         runScheduleWork,
+                         double                              t,
+                         rvec                                mu_tot,
+                         gmx::VirtualSitesHandler*           vsite,
+                         const DDBalanceRegionHandler&       ddBalanceRegionHandler);
+
+/* Print some final output and delete shellfc */
+void done_shellfc(FILE* fplog, gmx_shellfc_t* shellfc, int64_t numSteps);
+
+/*! \brief Count the different particle types in a system
+ *
+ * Routine prints a warning to stderr in case an unknown particle type
+ * is encountered.
+ * \param[in]  fplog Print what we have found if not NULL
+ * \param[in]  mtop  Molecular topology.
+ * \returns Array holding the number of particles of a type
+ */
+gmx::EnumerationArray<ParticleType, int> countPtypes(FILE* fplog, const gmx_mtop_t& mtop);
+
+#endif
diff --git a/src/include/gromacs/mdrun/simulationcontext.h b/src/include/gromacs/mdrun/simulationcontext.h
new file mode 100644 (file)
index 0000000..c570273
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GROMACS_SIMULATIONCONTEXT_H
+#define GROMACS_SIMULATIONCONTEXT_H
+
+/*! \libinternal
+ * \file
+ * \brief Provide ways for client code to own simulation resources.
+ *
+ * For `gmx mdrun` to be implemented as a client program, public API needs to
+ * provide a way to create and manipulate the SimulationContext.
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ * \ingroup module_mdrun
+ * \inlibraryapi
+ */
+
+#include <memory>
+#include <string>
+
+#include "gromacs/mdrunutility/multisim.h"
+#include "gromacs/utility/gmxmpi.h"
+
+struct gmx_multisim_t;
+
+namespace gmx
+{
+
+template<typename T>
+class ArrayRef;
+
+/*! \cond libapi
+ * \libinternal
+ * \brief Simulation environment and configuration.
+ *
+ * SimulationContext allows a simulation module (\relates gmx::mdrun) to retrieve
+ * runtime parameters and resources from client code. The client retains ownership
+ * of the context and its resources, with exceptions as noted.
+ *
+ * A client must share ownership of some resources and transfer ownership of
+ * other resources to create or configure the context. The simulation may in
+ * turn consume or borrow some resources from the context. This is a new
+ * framework that will evolve in the contexts of
+ * https://gitlab.com/gromacs/gromacs/-/issues/2375
+ * https://gitlab.com/gromacs/gromacs/-/issues/2587
+ *
+ * The public interface of SimulationContext is not yet well-specified.
+ * Client code can create an instance with gmx::createSimulationContext()
+ *
+ * \warning The SimulationContext does not yet encapsulate the resources allocated
+ * to the simulator. See https://gitlab.com/gromacs/gromacs/-/issues/3650
+ *
+ * \warning On thread-MPI code paths, the SimulationContext *does not* represent
+ * constraints on the number of simulation ranks, nor does it represent initialized
+ * communication resources.
+ *
+ * \todo Clarify and strengthen the invariant represented by SimulationContext.
+ *
+ * \todo This class should also handle aspects of simulation
+ * environment such as working directory and environment variables.
+ *
+ * \ingroup module_mdrun
+ * \inlibraryapi
+ *
+ * \internal
+ * This is a minimal placeholder for a more complete implementation.
+ * Interfaces for different API levels are not yet final.
+ * \todo Impose sensible access restrictions.
+ * Either the SimulationContext should be passed to the Mdrunner as logically constant or
+ * a separate handle class can provide access to resources that have been
+ * allocated by (negotiated with) the client for the current simulation
+ * (or simulation segment).
+ * Non-const accessors to shared resources need to be associated with update
+ * signals that the simulation components (modules and runner) can subscribe to.
+ *
+ * Also reference https://gitlab.com/gromacs/gromacs/-/issues/2587
+ */
+class SimulationContext final
+{
+public:
+    /*!
+     * \brief Object must be initialized with non-default constructor.
+     */
+    SimulationContext() = delete;
+    /*!
+     * \brief Initialize from borrowed communicator.
+     *
+     * \param communicator Communicator for all collaborating simulation processes.
+     * \param multiSimDirectoryNames  Names of any directories used with -multidir
+     *
+     * Caller is responsible for keeping *communicator* valid for the life of SimulationContext,
+     * and then freeing the communicator, if appropriate.
+     *
+     * With an external MPI library (non-thread-MPI chosen when configuring with CMake),
+     * the client promises that MPI has been initialized (such as by calling gmx::init()).
+     * This communicator is "borrowed" (not duplicated) from the caller.
+     * Additional communicators may be split from the provided communicator
+     * during the life of the SimulationContext or its consumers.
+     *
+     * With thread-MPI, *communicator* must be MPI_COMM_NULL.
+     * The communicator is set up later
+     * during the process of spawning the threads that will be the MPI
+     * ranks. (Multi-simulation is not supported with thread-MPI.)
+     *
+     * \todo Refine distribution of environment management.
+     *       There should be a context level at which only the current simulator directory matters,
+     *       and a level above which encapsulates multisim details in a specialized type.
+     */
+    explicit SimulationContext(MPI_Comm communicator, ArrayRef<const std::string> multiSimDirectoryNames);
+
+    /*!
+     * \brief MPI resources for the entire simulation context.
+     *
+     * With an external MPI library (non-thread-MPI chosen when configuring with CMake),
+     * gmx::init() has called MPI_Init and the provided communicator is valid to use.
+     * The communicator is "borrowed" (not duplicated) from the caller.
+     *
+     * With thread-MPI, the communicator is set up later
+     * during the process of spawning the threads that will be the MPI
+     * ranks.
+     */
+    MPI_Comm libraryWorldCommunicator_ = MPI_COMM_NULL;
+
+    /*!
+     * \brief MPI communicator object for this simulation.
+     *
+     * With an external MPI library (non-thread-MPI chosen when configuring with CMake),
+     * gmx::init() has called MPI_Init and the provided communicator is valid to use.
+     * The communicator is "borrowed" (not duplicated) from the caller.
+     *
+     * With thread-MPI, the communicator is set up later
+     * during the process of spawning the threads that will be the MPI
+     * ranks.
+     */
+    MPI_Comm simulationCommunicator_ = MPI_COMM_NULL;
+
+    /*!
+     * \brief Multi-sim handler (if required by e.g. gmx mdrun
+     * -multidir; only supported with real MPI)
+     *
+     * If a multi-simulation is in use, then its
+     * communicator(s) are found in multiSimulation_. This
+     * communicator is that of all ranks from all simulations, and
+     * will later be split into one for each simulation.
+     * TODO: Perhaps (for simplicity) communicator splitting
+     *       can be undertaken during multi-sim setup (acquisition of the multisim resource).
+     *
+     * Multi-simulation is not supported with thread-MPI.
+     */
+    std::unique_ptr<gmx_multisim_t> multiSimulation_;
+};
+//! \endcond
+
+} // end namespace gmx
+
+#endif // GROMACS_SIMULATIONCONTEXT_H
diff --git a/src/include/gromacs/mdrun/simulationinput.h b/src/include/gromacs/mdrun/simulationinput.h
new file mode 100644 (file)
index 0000000..9c7d225
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Utilities for interacting with SimulationInput.
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ * \ingroup module_mdrun
+ * \inlibraryapi
+ */
+
+#ifndef GMX_MDRUN_SIMULATIONINPUT_H
+#define GMX_MDRUN_SIMULATIONINPUT_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/mdrun/simulationinputhandle.h"
+#include "gromacs/mdtypes/checkpointdata.h"
+#include "gromacs/utility/mdmodulesnotifiers.h"
+
+// Forward declarations for types from other modules that are opaque to the public API.
+// TODO: Document the sources of these symbols or import a (self-documenting) fwd header.
+struct gmx_mtop_t;
+struct t_commrec;
+struct t_fileio;
+struct t_inputrec;
+class t_state;
+struct ObservablesHistory;
+struct PartialDeserializedTprFile;
+
+namespace gmx
+{
+/*
+ * \brief Prescription for molecular simulation.
+ *
+ * In the first implementation, this is a POD struct to allow removal of direct
+ * references to TPR and CPT files from Mdrunner. The interface for SimulationInput
+ * should be considered to be *completely unspecified* until resolution of
+ * https://gitlab.com/gromacs/gromacs/-/issues/3374
+ *
+ * Clients should use the utility functions defined in simulationinpututility.h
+ *
+ * Design note: It is probably sufficient for future versions to compose SimulationInput
+ * through a Builder rather than to subclass an Interface or base class. Outside of this
+ * translation unit, we should avoid coupling to the class definition until/unless we
+ * develop a much better understanding of simulation input portability.
+ */
+class SimulationInput
+{
+public:
+    SimulationInput(const char* tprFilename, const char* cpiFilename);
+
+    std::string tprFilename_;
+    std::string cpiFilename_;
+};
+
+/*! \brief Get the global simulation input.
+ *
+ * Acquire global simulation data structures from the SimulationInput handle.
+ * Note that global data is returned in the calling thread. In parallel
+ * computing contexts, the client is responsible for calling only where needed.
+ *
+ * Example:
+ *    if (SIMMASTER(cr))
+ *    {
+ *        // Only the master rank has the global state
+ *        globalState = globalSimulationState(simulationInput);
+ *
+ *        // Read (nearly) all data required for the simulation
+ *        applyGlobalInputRecord(simulationInput, inputrec);
+ *        applyGlobalTopology(simulationInput, &mtop);
+ *     }
+ *
+ * \todo Factor the logic for global/local and master-rank-checks.
+ * The SimulationInput utilities should behave properly for the various distributed data scenarios.
+ * Consider supplying data directly to the consumers rather than exposing the
+ * implementation details of the legacy aggregate types.
+ *
+ * \{
+ */
+// TODO: Remove this monolithic detail as member data can be separately cached and managed. (#3374)
+// Note that clients still need tpxio.h for PartialDeserializedTprFile.
+void applyGlobalSimulationState(const SimulationInput&      simulationInput,
+                                PartialDeserializedTprFile* partialDeserializedTpr,
+                                t_state*                    globalState,
+                                t_inputrec*                 inputrec,
+                                gmx_mtop_t*                 globalTopology);
+// TODO: Implement the following, pending further discussion re #3374.
+std::unique_ptr<t_state> globalSimulationState(const SimulationInput&);
+void                     applyGlobalInputRecord(const SimulationInput&, t_inputrec*);
+void                     applyGlobalTopology(const SimulationInput&, gmx_mtop_t*);
+//! \}
+
+/*! \brief Initialize local stateful simulation data.
+ *
+ * Establish an invariant for the simulator at a trajectory point.
+ * Call on all ranks (after domain decomposition and task assignments).
+ *
+ * After this call, the simulator has all of the information it will
+ * receive in order to advance a trajectory from the current step.
+ * Checkpoint information has been applied, if applicable, and stateful
+ * data has been (re)initialized.
+ *
+ * \warning Mdrunner instances do not clearly distinguish between global and local
+ * versions of t_state.
+ *
+ * \todo Factor the distributed data aspects of simulation input data into the
+ *       SimulationInput implementation.
+ *
+ * \todo Consider refactoring to decouple the checkpoint facility from its consumers
+ *       (state, observablesHistory, mdModulesNotifiers, and parts of ir).
+ *
+ * \warning It is the caller’s responsibility to make sure that
+ * preconditions are satisfied for the parameter objects.
+ *
+ * \see globalSimulationState()
+ * \see applyGlobalInputRecord()
+ * \see applyGlobalTopology()
+ */
+void applyLocalState(const SimulationInput&         simulationInput,
+                     t_fileio*                      logfio,
+                     const t_commrec*               cr,
+                     int*                           dd_nc,
+                     t_inputrec*                    ir,
+                     t_state*                       state,
+                     ObservablesHistory*            observablesHistory,
+                     bool                           reproducibilityRequested,
+                     const MDModulesNotifiers&      notifiers,
+                     gmx::ReadCheckpointDataHolder* modularSimulatorCheckpointData,
+                     bool                           useModularSimulator);
+
+} // end namespace gmx
+
+#endif // GMX_MDRUN_SIMULATIONINPUT_H
diff --git a/src/include/gromacs/mdrun/simulationinputhandle.h b/src/include/gromacs/mdrun/simulationinputhandle.h
new file mode 100644 (file)
index 0000000..d5e5bde
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief Public interface for SimulationInput facilities.
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ * \ingroup module_mdrun
+ * \inpublicapi
+ */
+
+#ifndef GMX_MDRUN_SIMULATIONINPUTHANDLE_H
+#define GMX_MDRUN_SIMULATIONINPUTHANDLE_H
+
+#include <memory>
+
+namespace gmx
+{
+
+// Forward declarations for types from other modules that are opaque to the public API.
+class LegacyMdrunOptions;
+
+/*!
+ * \brief Prescription for molecular simulation.
+ *
+ * Represent the complete and unique information needed to generate a simulation
+ * trajectory segment. SimulationInput objects are opaque to the public API.
+ * Ownership can be managed with SimulationInputHolder objects. Clients can
+ * acquire owning references to SimulationInput objects (as SimulationInputHolder)
+ * through makeSimulationInput() or from other SimulationInputHolders.
+ *
+ * A SimulationInput object represents an immutable source of data, and is safe
+ * to share. A SimulationInput object may have internal state to support
+ * performance optimizations when shared by multiple SimulationInputHolders.
+ * The SimulationInput is guaranteed to live at least as long as any associated
+ * SimulationInputHolders. The API does not specify whether it may persist
+ * longer internally or be reused for later equivalent requests.
+ *
+ * \see SimulationInputHandle
+ * \see makeSimulationInput()
+ *
+ * \internal
+ * SimulationInput has no public interface yet, but we need a forward declaration for the
+ * library symbol. Library interface provided through simulationinput.h
+ * See also https://gitlab.com/gromacs/gromacs/-/issues/3379 for design and development road map.
+ */
+class SimulationInput;
+
+/*! \cond internal
+ * Client software developers do not interact directly with the contents of gmx::detail,
+ * but some declarations and definitions are necessary in the public headers, such as
+ * forward declarations of implementation classes and definitions of custom deleters.
+ */
+namespace detail
+{
+/*!
+ * \brief Private implementation class;
+ */
+class SimulationInputHandleImpl;
+
+/*!
+ * \brief Explicit deleter details for SimulationInputHolderImpl.
+ *
+ * SimulationInputHolderImpl objects are created by the GROMACS library, but
+ * are destroyed when the SimulationInputHolder goes out of scope in the client
+ * code, which may be linked to a different allocator.
+ * We want to make sure that objects are allocated and deallocated with the same
+ * allocator, so we avoid the default deleter in unique_ptrs and compile allocation
+ * and deallocation code in the same translation unit.
+ *
+ * Note that this does not solve potential ABI incompatibilities between the
+ * unique_ptr implementations themselves, but we need to consider ABI
+ * compatibility goals and challenges as well as supported use cases and
+ * ownership semantics.
+ */
+struct SimulationInputHandleImplDeleter
+{
+    /*! \cond */
+    SimulationInputHandleImplDeleter();
+    SimulationInputHandleImplDeleter(const SimulationInputHandleImplDeleter& deleter) noexcept;
+    SimulationInputHandleImplDeleter(SimulationInputHandleImplDeleter&& deleter) noexcept;
+    SimulationInputHandleImplDeleter& operator=(const SimulationInputHandleImplDeleter& deleter) noexcept;
+    SimulationInputHandleImplDeleter& operator=(SimulationInputHandleImplDeleter&& deleter) noexcept;
+    void                              operator()(SimulationInputHandleImpl* impl) const;
+    /*! \endcond */
+};
+} // end namespace detail
+/*! \endcond internal */
+
+/*!
+ * \brief Owning handle to a SimulationInput object.
+ *
+ * SimulationInput objects are logically immutable, so ownership may be shared
+ * by multiple SimulationInputHolders.
+ *
+ * Acquire a SimulationInputHolder with makeSimulationInput() and pass to (e.g.)
+ * gmx::MdrunnerBuilder::addInput()
+ *
+ * SimulationInput has no public API yet.
+ * \see https://gitlab.com/gromacs/gromacs/-/issues/3379
+ */
+class SimulationInputHandle
+{
+public:
+    /*! \cond internal */
+    SimulationInputHandle();
+    ~SimulationInputHandle();
+
+    SimulationInputHandle(const SimulationInputHandle& source);
+    SimulationInputHandle(SimulationInputHandle&&) noexcept = default;
+
+    SimulationInputHandle& operator=(const SimulationInputHandle& rhs);
+    SimulationInputHandle& operator=(SimulationInputHandle&&) noexcept = default;
+
+    /*!
+     * \brief Take ownership of private implementation object to produce a new public holder.
+     */
+    explicit SimulationInputHandle(std::unique_ptr<detail::SimulationInputHandleImpl> impl);
+    /*! \endcond */
+
+    /*!
+     * \brief Access opaque SimulationInput pointer.
+     *
+     * \return Borrowed access to the SimulationInput, if present.
+     */
+    [[nodiscard]] SimulationInput* get() const noexcept;
+
+    /*!
+     * \brief Boolean context returns true if an input is held, else false.
+     */
+    operator bool() const;
+
+private:
+    std::unique_ptr<detail::SimulationInputHandleImpl, detail::SimulationInputHandleImplDeleter> impl_;
+};
+
+/*! \brief Direct the construction of a SimulationInput.
+ *
+ * \warning Creation methods for SimulationInput resources are under rapid development.
+ * Reference https://gitlab.com/gromacs/gromacs/-/issues/3652
+ *
+ * \param options library-internal container holding partially processed user input.
+ *
+ * \ingroup module_mdrun
+ *
+ * \internal
+ * This isn't really a public API function until its arguments are obtainable
+ * through the public API.
+ *
+ * Design notes: SimulationInput creation will warrant a builder protocol, and
+ * this helper can evolve into a director to apply the contents of LegacyMdrunOptions,
+ * while such an operation is still relevant.
+ *
+ * Example:
+ *     // After preparing a LegacyMdrunOptions and calling handleRestart()...
+ *     SimulationInputBuilder builder;
+ *     auto simulationInputHandle = makeSimulationInput(options, &builder);
+ *
+ *     // In addition to MdrunnerBuilder::addFiles(),
+ *     mdrunnerBuilder.addInput(simulationInputHandle.get());
+ *
+ */
+SimulationInputHandle makeSimulationInput(const LegacyMdrunOptions& options);
+
+} // end namespace gmx
+
+#endif // GMX_MDRUN_SIMULATIONINPUTHANDLE_H
diff --git a/src/include/gromacs/mdrun/simulatorbuilder.h b/src/include/gromacs/mdrun/simulatorbuilder.h
new file mode 100644 (file)
index 0000000..787dc72
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019-2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal
+ * \brief Declares the simulator builder for mdrun
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_mdrun
+ */
+#ifndef GMX_MDRUN_SIMULATORBUILDER_H
+#define GMX_MDRUN_SIMULATORBUILDER_H
+
+#include <memory>
+
+
+class energyhistory_t;
+class gmx_ekindata_t;
+struct gmx_enerdata_t;
+struct gmx_enfrot;
+struct gmx_localtop_t;
+struct gmx_mtop_t;
+struct gmx_multisim_t;
+struct gmx_output_env_t;
+struct gmx_wallcycle;
+struct gmx_walltime_accounting;
+struct ObservablesHistory;
+struct pull_t;
+struct ReplicaExchangeParameters;
+struct t_commrec;
+struct t_filenm;
+struct t_forcerec;
+struct t_inputrec;
+struct t_nrnb;
+class t_state;
+struct t_swap;
+
+namespace gmx
+{
+class BoxDeformation;
+class Constraints;
+class IMDOutputProvider;
+class ImdSession;
+class ISimulator;
+class MdrunScheduleWorkload;
+class MembedHolder;
+class MDAtoms;
+class MDLogger;
+struct MDModulesNotifiers;
+struct MdrunOptions;
+class ObservablesReducerBuilder;
+class ReadCheckpointDataHolder;
+enum class StartingBehavior;
+class StopHandlerBuilder;
+class VirtualSitesHandler;
+
+/*! \brief
+ * Simulation configuation settings.
+ */
+struct SimulatorConfig
+{
+public:
+    //! Build from settings for this simulation.
+    SimulatorConfig(const MdrunOptions&    mdrunOptions,
+                    StartingBehavior       startingBehavior,
+                    MdrunScheduleWorkload* runScheduleWork) :
+        mdrunOptions_(mdrunOptions), startingBehavior_(startingBehavior), runScheduleWork_(runScheduleWork)
+    {
+    }
+    // TODO: Specify copy and move semantics.
+
+    //! Handle to user options.
+    const MdrunOptions& mdrunOptions_;
+    //! How are we starting the simulation.
+    StartingBehavior startingBehavior_;
+    //! How are we scheduling the tasks for this simulation.
+    MdrunScheduleWorkload* runScheduleWork_;
+};
+
+
+/*! \brief
+ * Data for a specific simulation state.
+ *
+ * \todo Think of a better name and annoy people that forget
+ *       to add documentation for their code.
+ */
+struct SimulatorStateData
+{
+    //! Build collection of current state data.
+    SimulatorStateData(t_state*            globalState,
+                       t_state*            localState,
+                       ObservablesHistory* observablesHistory,
+                       gmx_enerdata_t*     enerdata,
+                       gmx_ekindata_t*     ekindata) :
+        globalState_p(globalState),
+        localState_p(localState),
+        observablesHistory_p(observablesHistory),
+        enerdata_p(enerdata),
+        ekindata_p(ekindata)
+    {
+    }
+
+    //! Can perform copy of current state.
+    SimulatorStateData(const SimulatorStateData& simulatorStateData) = default;
+
+    //! Handle to global state of the simulation.
+    t_state* globalState_p;
+    //! Handle to local state of the simulation.
+    t_state* localState_p;
+    //! Handle to current simulation history.
+    ObservablesHistory* observablesHistory_p;
+    //! Handle to collected data for energy groups.
+    gmx_enerdata_t* enerdata_p;
+    //! Handle to collected data for kinectic energy.
+    gmx_ekindata_t* ekindata_p;
+};
+
+/*! \brief
+ * Collection of environmental information for a simulation.
+ *
+ * \todo Fix doxygen checks.
+ */
+class SimulatorEnv
+{
+public:
+    //! Build from current simulation environment.
+    SimulatorEnv(FILE*                      fplog,
+                 t_commrec*                 commRec,
+                 gmx_multisim_t*            multisimCommRec,
+                 const MDLogger&            logger,
+                 gmx_output_env_t*          outputEnv,
+                 ObservablesReducerBuilder* observablesReducerBuilder) :
+        fplog_{ fplog }, commRec_{ commRec }, multisimCommRec_{ multisimCommRec }, logger_{ logger }, outputEnv_{ outputEnv }, observablesReducerBuilder_{ observablesReducerBuilder }
+    {
+    }
+
+    //! Handle to log file.
+    FILE* fplog_;
+    //! Handle to communication record.
+    t_commrec* commRec_;
+    //! Handle to multisim communication record.
+    const gmx_multisim_t* multisimCommRec_;
+    //! Handle to propper logging framework.
+    const MDLogger& logger_;
+    //! Handle to file output handling.
+    const gmx_output_env_t* outputEnv_;
+    //! Builder for coordinator of reduction for observables
+    ObservablesReducerBuilder* observablesReducerBuilder_;
+};
+
+/*! \brief
+ * Collection of profiling information.
+ */
+class Profiling
+{
+public:
+    //! Build profiling information collection.
+    Profiling(t_nrnb* nrnb, gmx_walltime_accounting* walltimeAccounting, gmx_wallcycle* wallCycle) :
+        nrnb(nrnb), wallCycle(wallCycle), walltimeAccounting(walltimeAccounting)
+    {
+    }
+
+    //! Handle to datastructure.
+    t_nrnb* nrnb;
+    //! Handle to wallcycle stuff.
+    gmx_wallcycle* wallCycle;
+    //! Handle to wallcycle time accounting stuff.
+    gmx_walltime_accounting* walltimeAccounting;
+};
+
+/*! \brief
+ * Collection of constraint parameters.
+ */
+class ConstraintsParam
+{
+public:
+    //! Build collection with handle to actual objects.
+    ConstraintsParam(Constraints* constraints, gmx_enfrot* enforcedRotation, VirtualSitesHandler* vSite) :
+        constr(constraints), enforcedRotation(enforcedRotation), vsite(vSite)
+    {
+    }
+
+    //! Handle to constraint object.
+    Constraints* constr;
+    //! Handle to information about using enforced rotation.
+    gmx_enfrot* enforcedRotation;
+    //! Handle to vsite stuff.
+    VirtualSitesHandler* vsite;
+};
+
+/*! \brief
+ * Collection of legacy input information.
+ */
+class LegacyInput
+{
+public:
+    //! Build collection from legacy input data.
+    LegacyInput(int filenamesSize, const t_filenm* filenamesData, t_inputrec* inputRec, t_forcerec* forceRec) :
+        numFile(filenamesSize), filenames(filenamesData), inputrec(inputRec), forceRec(forceRec)
+    {
+    }
+
+    //! Number of input files.
+    int numFile;
+    //! File names.
+    const t_filenm* filenames;
+    //! Handle to simulation input record.
+    t_inputrec* inputrec;
+    //! Handle to simulation force record.
+    t_forcerec* forceRec;
+};
+
+/*! \brief SimulatorBuilder parameter type for InteractiveMD.
+ *
+ * Conveys a non-owning pointer to implementation details.
+ *
+ * \todo If adding doxygen stubs actual add the full stub.
+ */
+class InteractiveMD
+{
+public:
+    //! Create handle to IMD information.
+    explicit InteractiveMD(ImdSession* imdSession) : imdSession(imdSession) {}
+
+    //! Internal handle to IMD info.
+    ImdSession* imdSession;
+};
+
+class SimulatorModules
+{
+public:
+    SimulatorModules(IMDOutputProvider* mdOutputProvider, const MDModulesNotifiers& notifiers) :
+        outputProvider(mdOutputProvider), mdModulesNotifiers(notifiers)
+    {
+    }
+
+    IMDOutputProvider*        outputProvider;
+    const MDModulesNotifiers& mdModulesNotifiers;
+};
+
+class CenterOfMassPulling
+{
+public:
+    explicit CenterOfMassPulling(pull_t* pullWork) : pull_work(pullWork) {}
+
+    pull_t* pull_work;
+};
+
+/*! \brief
+ * Parameter type for IonSwapping SimulatorBuilder component.
+ *
+ * Conveys a non-owning pointer to implementation details.
+ *
+ * \todo Add full information.
+ */
+class IonSwapping
+{
+public:
+    //! Create handle.
+    IonSwapping(t_swap* ionSwap) : ionSwap(ionSwap) {}
+
+    //! Internal storage for handle.
+    t_swap* ionSwap;
+};
+
+/*! \brief
+ * Collection of handles to topology information.
+ */
+class TopologyData
+{
+public:
+    //! Build collection from simulation data.
+    TopologyData(const gmx_mtop_t& globalTopology, gmx_localtop_t* localTopology, MDAtoms* mdAtoms) :
+        top_global(globalTopology), localTopology(localTopology), mdAtoms(mdAtoms)
+    {
+    }
+
+    //! Handle to global simulation topology.
+    const gmx_mtop_t& top_global;
+    //! Handle to local simulation topology.
+    gmx_localtop_t* localTopology;
+    //! Handle to information about MDAtoms.
+    MDAtoms* mdAtoms;
+};
+
+/*! \brief
+ * Handle to information about the box.
+ *
+ * Design note: The client may own the BoxDeformation via std::unique_ptr, but we are not
+ * transferring ownership at this time. (May be the subject of future changes.)
+ */
+class BoxDeformationHandle
+{
+public:
+    //! Build handle to box stuff.
+    BoxDeformationHandle(BoxDeformation* boxDeformation) : deform(boxDeformation) {}
+
+    //! Internal storage for handle.
+    BoxDeformation* deform;
+};
+
+/*! \libinternal
+ * \brief Class preparing the creation of Simulator objects
+ *
+ * Objects of this class build Simulator objects, which in turn are used to
+ * run molecular simulations.
+ */
+class SimulatorBuilder
+{
+public:
+    void add(MembedHolder&& membedHolder);
+
+    void add(std::unique_ptr<StopHandlerBuilder> stopHandlerBuilder)
+    {
+        stopHandlerBuilder_ = std::move(stopHandlerBuilder);
+    }
+
+    void add(SimulatorStateData&& simulatorStateData)
+    {
+        simulatorStateData_ = std::make_unique<SimulatorStateData>(simulatorStateData);
+    }
+
+    void add(SimulatorConfig&& simulatorConfig)
+    {
+        // Note: SimulatorConfig appears to the compiler to be trivially copyable,
+        // but this may not be safe and may change in the future.
+        simulatorConfig_ = std::make_unique<SimulatorConfig>(simulatorConfig);
+    }
+
+    void add(SimulatorEnv&& simulatorEnv)
+    {
+        simulatorEnv_ = std::make_unique<SimulatorEnv>(simulatorEnv);
+    }
+
+    void add(Profiling&& profiling) { profiling_ = std::make_unique<Profiling>(profiling); }
+
+    void add(ConstraintsParam&& constraintsParam)
+    {
+        constraintsParam_ = std::make_unique<ConstraintsParam>(constraintsParam);
+    }
+
+    void add(LegacyInput&& legacyInput)
+    {
+        legacyInput_ = std::make_unique<LegacyInput>(legacyInput);
+    }
+
+    void add(ReplicaExchangeParameters&& replicaExchangeParameters);
+
+    void add(InteractiveMD&& interactiveMd)
+    {
+        interactiveMD_ = std::make_unique<InteractiveMD>(interactiveMd);
+    }
+
+    void add(SimulatorModules&& simulatorModules)
+    {
+        simulatorModules_ = std::make_unique<SimulatorModules>(simulatorModules);
+    }
+
+    void add(CenterOfMassPulling&& centerOfMassPulling)
+    {
+        centerOfMassPulling_ = std::make_unique<CenterOfMassPulling>(centerOfMassPulling);
+    }
+
+    void add(IonSwapping&& ionSwapping)
+    {
+        ionSwapping_ = std::make_unique<IonSwapping>(ionSwapping);
+    }
+
+    void add(TopologyData&& topologyData)
+    {
+        topologyData_ = std::make_unique<TopologyData>(topologyData);
+    }
+
+    void add(BoxDeformationHandle&& boxDeformation)
+    {
+        boxDeformation_ = std::make_unique<BoxDeformationHandle>(boxDeformation);
+    }
+
+    /*!
+     * \brief Pass the read checkpoint data for modular simulator
+     *
+     * Note that this is currently the point at which the ReadCheckpointDataHolder
+     * is fully filled. Consequently it stops being an object at which read
+     * operations from file are targeted, and becomes a read-only object from
+     * which elements read their data to recreate an earlier internal state.
+     *
+     * Currently, this behavior change is not enforced. Once input reading and
+     * simulator builder have matured, these restrictions could be imposed.
+     *
+     * See #3656
+     */
+    void add(std::unique_ptr<ReadCheckpointDataHolder> modularSimulatorCheckpointData);
+
+    /*! \brief Build a Simulator object based on input data
+     *
+     * Return a pointer to a simulation object. The use of a parameter
+     * pack insulates the builder from changes to the arguments of the
+     * Simulator objects.
+     *
+     * \throws gmx::APIError if expected set-up methods have not been called before build()
+     *
+     * \return  Unique pointer to a Simulator object
+     */
+    std::unique_ptr<ISimulator> build(bool useModularSimulator);
+
+private:
+    // Note: we use std::unique_ptr instead of std::optional because we want to
+    // allow for opaque types at the discretion of the module developer.
+    /*! \brief Collection of handles to  individual information. */
+    /*! \{ */
+    std::unique_ptr<SimulatorConfig>           simulatorConfig_;
+    std::unique_ptr<MembedHolder>              membedHolder_;
+    std::unique_ptr<StopHandlerBuilder>        stopHandlerBuilder_;
+    std::unique_ptr<SimulatorStateData>        simulatorStateData_;
+    std::unique_ptr<SimulatorEnv>              simulatorEnv_;
+    std::unique_ptr<Profiling>                 profiling_;
+    std::unique_ptr<ConstraintsParam>          constraintsParam_;
+    std::unique_ptr<LegacyInput>               legacyInput_;
+    std::unique_ptr<ReplicaExchangeParameters> replicaExchangeParameters_;
+    std::unique_ptr<InteractiveMD>             interactiveMD_;
+    std::unique_ptr<SimulatorModules>          simulatorModules_;
+    std::unique_ptr<CenterOfMassPulling>       centerOfMassPulling_;
+    std::unique_ptr<IonSwapping>               ionSwapping_;
+    std::unique_ptr<TopologyData>              topologyData_;
+    std::unique_ptr<BoxDeformationHandle>      boxDeformation_;
+    //! Contains checkpointing data for the modular simulator
+    std::unique_ptr<ReadCheckpointDataHolder> modularSimulatorCheckpointData_;
+    /*! \} */
+};
+
+} // namespace gmx
+
+#endif // GMX_MDRUN_SIMULATORBUILDER_SIMULATORBUILDER_H
diff --git a/src/include/gromacs/mdrunutility/freeenergy.h b/src/include/gromacs/mdrunutility/freeenergy.h
new file mode 100644 (file)
index 0000000..d722012
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares helper functions for mdrun pertaining to free energy calculations.
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_mdrunutility
+ * \inlibraryapi
+ */
+
+#ifndef GMX_MDRUNUTILITY_FREEENERGY_H
+#define GMX_MDRUNUTILITY_FREEENERGY_H
+
+struct ReplicaExchangeParameters;
+struct t_inputrec;
+
+namespace gmx
+{
+
+/*! \brief Compute the period at which FEP calculation is performed
+ *
+ * This harmonizes the free energy calculation period specified by
+ * `nstdhdl` with the periods specified by expanded ensemble,
+ * replica exchange, and AWH.
+ *
+ * \param inputrec      The input record
+ * \param replExParams  The replica exchange parameters
+ * \return              The period required by the involved algorithms
+ */
+int computeFepPeriod(const t_inputrec& inputrec, const ReplicaExchangeParameters& replExParams);
+
+} // namespace gmx
+
+#endif // GMX_MDRUNUTILITY_FREEENERGY_H
diff --git a/src/include/gromacs/mdrunutility/handlerestart.h b/src/include/gromacs/mdrunutility/handlerestart.h
new file mode 100644 (file)
index 0000000..7e586c6
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \defgroup module_mdrunutility Implementation of mdrun utility functionality
+ * \ingroup group_mdrun
+ *
+ * \brief This module contains code that implements general
+ * infrastructure for mdrun that does not suit any other module.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file declares functions for mdrun to call to manage the
+ * details of doing a restart (ie. reading checkpoints, appending
+ * output files).
+ *
+ * \todo There may be other code in runner.cpp etc. that can usefully
+ * live here
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Erik Lindahl <erik@kth.se>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_mdrunutility
+ */
+
+#ifndef GMX_MDRUNUTILITY_HANDLERESTART_H
+#define GMX_MDRUNUTILITY_HANDLERESTART_H
+
+#include "gromacs/mdrunutility/logging.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxmpi.h"
+
+struct gmx_multisim_t;
+struct t_filenm;
+
+namespace gmx
+{
+
+enum class AppendingBehavior;
+
+//! Enumeration for describing how mdrun is (re)starting
+enum class StartingBehavior : int
+{
+    //! Restarting with appending, if a checkpoint is supplied and other conditions are met.
+    RestartWithAppending,
+    //! Restarting without appending, when a checkpoint is supplied.
+    RestartWithoutAppending,
+    //! Not restarting
+    NewSimulation,
+    //! Mark the end of the enumeration
+    Count
+};
+
+/*! \brief Handle startup of mdrun, particularly regarding -cpi and -append
+ *
+ * If there is a checkpoint file, then prepare to start from that
+ * state. If possible/required, do so with appending. If some files
+ * are not found when appending should be done, we will instead issue
+ * a fatal error to avoid unintentional problems.
+ *
+ * If there is no checkpoint file, we return a value to indicate a new
+ * simulation is starting.
+ *
+ * On return, \p fnm is updated with suffix strings for part numbers if we are
+ * doing a restart from checkpoint and are not appending.
+ *
+ * The routine also does communication to coordinate behaviour between
+ * all simulations, including for error conditions.
+ *
+ * \throws FileIOError             When the filesystem behavior prevents the
+ *                                 user's choices being implemented.
+ * \throws InconsistentInputError  When the users's choices cannot be implemented.
+ * \throws GromacsException        On ranks upon which the error condition was
+ *                                 not detected.
+ *
+ * \param[in]    isSimulationMaster Whether this rank is the master rank of a simulation
+ * \param[in]    communicator       MPI communicator
+ * \param[in]    ms                 Handles multi-simulations.
+ * \param[in]    appendingBehavior  User choice for appending
+ * \param[in]    nfile              Size of fnm struct
+ * \param[inout] fnm                Filename parameters to mdrun
+ *
+ * \return  Description of how mdrun is starting */
+std::tuple<StartingBehavior, LogFilePtr> handleRestart(bool                  isSimulationMaster,
+                                                       MPI_Comm              communicator,
+                                                       const gmx_multisim_t* ms,
+                                                       AppendingBehavior     appendingBehavior,
+                                                       int                   nfile,
+                                                       t_filenm              fnm[]);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdrunutility/logging.h b/src/include/gromacs/mdrunutility/logging.h
new file mode 100644 (file)
index 0000000..c0894aa
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declares the MD log file handling routines.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_mdrunutility
+ */
+#ifndef GMX_MDRUNUTILITY_LOGGING_H
+#define GMX_MDRUNUTILITY_LOGGING_H
+
+#include <memory>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/unique_cptr.h"
+
+struct t_fileio;
+
+namespace gmx
+{
+
+/*! \brief Close the log file */
+void closeLogFile(t_fileio* logfio);
+
+//! Simple guard pointer See unique_cptr for details.
+using LogFilePtr = std::unique_ptr<t_fileio, functor_wrapper<t_fileio, closeLogFile>>;
+
+/*! \brief Open the log file for writing/appending.
+ *
+ * \throws FileIOError when the log file cannot be opened. */
+LogFilePtr openLogFile(const char* lognm, bool appendFiles);
+
+/*! \brief Prepare to use the open log file when appending.
+ *
+ * Does not throw.
+ */
+void prepareLogAppending(FILE* fplog);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdrunutility/multisim.h b/src/include/gromacs/mdrunutility/multisim.h
new file mode 100644 (file)
index 0000000..ef41a38
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declares the multi-simulation support routines.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_mdrunutility
+ */
+#ifndef GMX_MDRUNUTILITY_MULTISIM_H
+#define GMX_MDRUNUTILITY_MULTISIM_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxmpi.h"
+
+namespace gmx
+{
+class MDLogger;
+}
+
+struct gmx_multisim_t;
+struct t_commrec;
+
+/*! \libinternal
+ * \brief Builder function for gmx_multisim_t
+ *
+ * \param[in]  worldComm   MPI communicator to split when
+ *                         multi-simulation is requested.
+ * \param[in]  multidirs   Strings naming the subdirectories when
+ *                         multi-simulation is requested, otherwise empty
+ *
+ * Splits \c worldComm into \c multidirs.size() separate
+ * simulations, if >1, and creates a communication structure
+ * between the master ranks of these simulations.
+ *
+ * Valid to call regardless of build configuration, but \c
+ * multidirs must be empty unless a real MPI build is used.
+ *
+ * \throws NotImplementedError     when \c multidirs is non-empty unless using real MPI is true
+ * \throws NotImplementedError     when \c multidirs has exactly one element
+ * \throws InconsistentInputError  when the number of MPI ranks is not a multiple of the number of \c multidirs
+ * \throws FileIOError             when the simulation cannot change to the working directory in \c multidirs
+ */
+std::unique_ptr<gmx_multisim_t> buildMultiSimulation(MPI_Comm                         worldComm,
+                                                     gmx::ArrayRef<const std::string> multidirs);
+
+/*! \libinternal
+ * \brief Coordinate multi-simulation resources for mdrun
+ *
+ * \todo Change this to class
+ */
+struct gmx_multisim_t
+{
+    /*! \brief Constructor
+     *
+     * \param[in]  numSimulations   The number of simulations in the MPI world.
+     * \param[in]  simulationIndex  The index of this simulation in the set of simulations.
+     * \param[in]  mastersComm      On master ranks, the communicator among master ranks;
+     *                              otherwise MPI_COMM_NULL.
+     * \param[in]  simulationComm   The communicator among ranks of this simulation.
+     *
+     * Assumes ownership of the communicators if they are neither
+     * MPI_COMM_WORLD nor MPI_COMM_NULL. If so, upon destruction will
+     * call MPI_Comm_free on them.
+     */
+    gmx_multisim_t(int numSimulations, int simulationIndex, MPI_Comm mastersComm, MPI_Comm simulationComm);
+    //! Destructor
+    ~gmx_multisim_t();
+
+    //! The number of simulations in the set of multi-simulations
+    int numSimulations_ = 1;
+    //! The index of the simulation that owns this object within the set
+    int simulationIndex_ = 0;
+    //! The MPI communicator between master ranks of simulations, valid only on master ranks.
+    MPI_Comm mastersComm_ = MPI_COMM_NULL;
+    //! The MPI communicator between ranks of this simulation.
+    MPI_Comm simulationComm_ = MPI_COMM_NULL;
+};
+
+//! Calculate the sum over the simulations of an array of ints
+void gmx_sumi_sim(int nr, int r[], const gmx_multisim_t* ms);
+
+//! Calculate the sum over the simulations of an array of large ints
+void gmx_sumli_sim(int nr, int64_t r[], const gmx_multisim_t* ms);
+
+//! Calculate the sum over the simulations of an array of floats
+void gmx_sumf_sim(int nr, float r[], const gmx_multisim_t* ms);
+
+//! Calculate the sum over the simulations of an array of doubles
+void gmx_sumd_sim(int nr, double r[], const gmx_multisim_t* ms);
+
+/*! \brief Return a vector containing the gathered values of \c
+ * localValue found on the master rank of each simulation. */
+std::vector<int> gatherIntFromMultiSimulation(const gmx_multisim_t* ms, int localValue);
+
+/*! \brief Check if val is the same on all simulations for a mdrun
+ * -multidir run
+ *
+ * The string name is used to print to the log file and in a fatal error
+ * if the val's don't match. If bQuiet is true and the check passes,
+ * no output is written. */
+void check_multi_int(FILE* log, const gmx_multisim_t* ms, int val, const char* name, gmx_bool bQuiet);
+/*! \copydoc check_multi_int() */
+void check_multi_int64(FILE* log, const gmx_multisim_t* ms, int64_t val, const char* name, gmx_bool bQuiet);
+
+#if GMX_DOUBLE
+//! Convenience define for sum of reals
+#    define gmx_sum_sim gmx_sumd_sim
+#else
+//! Convenience define for sum of reals
+#    define gmx_sum_sim gmx_sumf_sim
+#endif
+
+//! Are we doing multiple independent simulations?
+static bool inline isMultiSim(const gmx_multisim_t* ms)
+{
+    return ms != nullptr;
+}
+
+/*! \brief Return whether this rank is the master rank of a
+ * simulation, using \c ms (if it is valid) and otherwise \c
+ * communicator */
+bool findIsSimulationMasterRank(const gmx_multisim_t* ms, MPI_Comm communicator);
+
+//! Are we the master simulation of a possible multi-simulation?
+bool isMasterSim(const gmx_multisim_t* ms);
+
+/*! \brief Are we the master rank (of the master simulation, for a multi-sim).
+ *
+ * This rank prints the remaining run time etc. */
+bool isMasterSimMasterRank(const gmx_multisim_t* ms, bool isMaster);
+
+/*! \brief Log the initial state of the multi-sim
+ *
+ * The simulations may be at different steps, etc so we
+ * report that.
+ *
+ * \param[in]  ms                     The multi-sum object
+ * \param[in]  cr                     The commrec object
+ * \param[in]  mdlog                  Logger
+ * \param[in]  simulationsShareState  Whether the simulations share state
+ * \param[in]  numSteps               The number of steps in this simulation
+ * \param[in]  initialStep            The initial step for this simulation
+ */
+void logInitialMultisimStatus(const gmx_multisim_t* ms,
+                              const t_commrec*      cr,
+                              const gmx::MDLogger&  mdlog,
+                              bool                  simulationsShareState,
+                              int                   numSteps,
+                              int                   initialStep);
+
+#endif
diff --git a/src/include/gromacs/mdrunutility/printtime.h b/src/include/gromacs/mdrunutility/printtime.h
new file mode 100644 (file)
index 0000000..52caa9e
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Declares functions that write timestamps to e.g. log files.
+ *
+ * \ingroup module_mdrunutility
+ * \inlibraryapi
+ */
+#ifndef GMX_MDRUNUTILITY_PRINTTIME_H
+#define GMX_MDRUNUTILITY_PRINTTIME_H
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+
+struct gmx_walltime_accounting;
+struct t_commrec;
+struct t_inputrec;
+
+//! Print time to \c out.
+void print_time(FILE*                    out,
+                gmx_walltime_accounting* walltime_accounting,
+                int64_t                  step,
+                const t_inputrec*        ir,
+                const t_commrec*         cr);
+
+/*! \brief Print date, time, MPI rank and a description of this point
+ * in time.
+ *
+ * \param[in] log       logfile, or NULL to suppress output
+ * \param[in] rank      MPI rank to include in the output
+ * \param[in] title     Description to include in the output
+ * \param[in] the_time  Seconds since the epoch, e.g. as reported by gmx_gettime
+ */
+void print_date_and_time(FILE* log, int rank, const char* title, double the_time);
+
+//! Print start time to \c fplog.
+void print_start(FILE* fplog, const t_commrec* cr, gmx_walltime_accounting* walltime_accounting, const char* name);
+
+#endif
diff --git a/src/include/gromacs/mdrunutility/tests/threadaffinitytest.h b/src/include/gromacs/mdrunutility/tests/threadaffinitytest.h
new file mode 100644 (file)
index 0000000..cda3de4
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDRUNUTILITY_TESTS_THREADAFFINITYTEST_H
+#define GMX_MDRUNUTILITY_TESTS_THREADAFFINITYTEST_H
+
+#include <initializer_list>
+#include <memory>
+
+#include <gmock/gmock.h>
+
+#include "gromacs/hardware/hw_info.h"
+#include "gromacs/mdrunutility/threadaffinity.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/logger.h"
+#include "gromacs/utility/physicalnodecommunicator.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "testutils/loggertest.h"
+
+struct t_commrec;
+
+namespace gmx
+{
+
+class HardwareTopology;
+
+namespace test
+{
+
+class MockThreadAffinityAccess : public IThreadAffinityAccess
+{
+public:
+    MockThreadAffinityAccess();
+    ~MockThreadAffinityAccess() override;
+
+    void setSupported(bool supported) { supported_ = supported; }
+
+    bool isThreadAffinitySupported() const override { return supported_; }
+    MOCK_METHOD1(setCurrentThreadAffinityToCore, bool(int core));
+
+private:
+    bool supported_;
+};
+
+class ThreadAffinityTestHelper
+{
+public:
+    ThreadAffinityTestHelper();
+    ~ThreadAffinityTestHelper();
+
+    void setAffinitySupported(bool supported) { affinityAccess_.setSupported(supported); }
+    void setAffinityOption(ThreadAffinity affinityOption)
+    {
+        hwOpt_.threadAffinity = affinityOption;
+    }
+    void setOffsetAndStride(int offset, int stride)
+    {
+        hwOpt_.core_pinning_offset = offset;
+        hwOpt_.core_pinning_stride = stride;
+    }
+
+    void setPhysicalNodeId(int nodeId) { physicalNodeId_ = nodeId; }
+
+    void setLogicalProcessorCount(int logicalProcessorCount);
+
+    void setTotNumThreadsIsAuto(bool isAuto) { hwOpt_.totNumThreadsIsAuto = isAuto; }
+
+    void expectAffinitySet(int core)
+    {
+        EXPECT_CALL(affinityAccess_, setCurrentThreadAffinityToCore(core));
+    }
+    void expectAffinitySet(std::initializer_list<int> cores)
+    {
+        for (int core : cores)
+        {
+            expectAffinitySet(core);
+        }
+    }
+    // NOLINTNEXTLINE readability-convert-member-functions-to-static
+    void expectAffinitySetThatFails(int core)
+    {
+        using ::testing::Return;
+        EXPECT_CALL(affinityAccess_, setCurrentThreadAffinityToCore(core)).WillOnce(Return(false));
+    }
+
+    void expectWarningMatchingRegex(const char* re) { expectWarningMatchingRegexIf(re, true); }
+    void expectWarningMatchingRegexIf(const char* re, bool condition)
+    {
+        expectLogMessageMatchingRegexIf(MDLogger::LogLevel::Warning, re, condition);
+    }
+    void expectInfoMatchingRegex(const char* re) { expectInfoMatchingRegexIf(re, true); }
+    void expectInfoMatchingRegexIf(const char* re, bool condition)
+    {
+        expectLogMessageMatchingRegexIf(MDLogger::LogLevel::Info, re, condition);
+    }
+    void expectGenericFailureMessage() { expectGenericFailureMessageIf(true); }
+    void expectGenericFailureMessageIf(bool condition)
+    {
+        expectWarningMatchingRegexIf("NOTE: Thread affinity was not set.", condition);
+    }
+    void expectPinningMessage(bool userSpecifiedStride, int stride)
+    {
+        std::string pattern = formatString(
+                "Pinning threads .* %s.* stride of %d", userSpecifiedStride ? "user" : "auto", stride);
+        expectInfoMatchingRegex(pattern.c_str());
+    }
+    void expectLogMessageMatchingRegexIf(MDLogger::LogLevel level, const char* re, bool condition)
+    {
+        if (condition)
+        {
+            logHelper_.expectEntryMatchingRegex(level, re);
+        }
+    }
+
+    void setAffinity(int numThreadsOnThisRank)
+    {
+        if (hwTop_ == nullptr)
+        {
+            setLogicalProcessorCount(1);
+        }
+        gmx::PhysicalNodeCommunicator comm(MPI_COMM_WORLD, physicalNodeId_);
+        int                           numThreadsOnThisNode, indexWithinNodeOfFirstThreadOnThisRank;
+        analyzeThreadsOnThisNode(
+                comm, numThreadsOnThisRank, &numThreadsOnThisNode, &indexWithinNodeOfFirstThreadOnThisRank);
+        gmx_set_thread_affinity(logHelper_.logger(),
+                                cr_,
+                                &hwOpt_,
+                                *hwTop_,
+                                numThreadsOnThisRank,
+                                numThreadsOnThisNode,
+                                indexWithinNodeOfFirstThreadOnThisRank,
+                                &affinityAccess_);
+    }
+
+private:
+    t_commrec*                        cr_;
+    gmx_hw_opt_t                      hwOpt_;
+    std::unique_ptr<HardwareTopology> hwTop_;
+    MockThreadAffinityAccess          affinityAccess_;
+    LoggerTestHelper                  logHelper_;
+    int                               physicalNodeId_;
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdrunutility/threadaffinity.h b/src/include/gromacs/mdrunutility/threadaffinity.h
new file mode 100644 (file)
index 0000000..1983c58
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares functions for managing mdrun thread affinity.
+ *
+ * \inlibraryapi
+ * \ingroup module_mdrunutility
+ */
+#ifndef GMX_MDRUNUTILITY_THREADAFFINITY_H
+#define GMX_MDRUNUTILITY_THREADAFFINITY_H
+
+#include <cstdio>
+
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_hw_opt_t;
+struct gmx_multisim_t;
+struct t_commrec;
+
+namespace gmx
+{
+
+class HardwareTopology;
+class MDLogger;
+class PhysicalNodeCommunicator;
+
+class IThreadAffinityAccess
+{
+public:
+    virtual bool isThreadAffinitySupported() const        = 0;
+    virtual bool setCurrentThreadAffinityToCore(int core) = 0;
+
+protected:
+    virtual ~IThreadAffinityAccess();
+};
+
+} // namespace gmx
+
+/*! \brief Communicates within physical nodes to discover the
+ * distribution of threads over ranks. */
+void analyzeThreadsOnThisNode(const gmx::PhysicalNodeCommunicator& physicalNodeComm,
+                              int                                  numThreadsOnThisRank,
+                              int*                                 numThreadsOnThisNode,
+                              int*                                 intraNodeThreadOffset);
+
+/*! \brief
+ * Sets the thread affinity using the requested setting stored in hw_opt.
+ *
+ * See analyzeThreadsOnThisNode(), which prepares some of the input.
+ *
+ * \param[out] mdlog                  Logger.
+ * \param[in]  cr                     Communication handler.
+ * \param[in]  hw_opt                 Accesses user choices for thread affinity handling.
+ * \param[in]  hwTop                  Detected hardware topology.
+ * \param[in]  numThreadsOnThisRank   The number of threads on this rank.
+ * \param[in]  numThreadsOnThisNode   The number of threads on all ranks of this node.
+ * \param[in]  intraNodeThreadOffset  The index of the first hardware thread of this rank
+ *   in the set of all the threads of all MPI ranks within a node (ordered by MPI rank ID).
+ * \param[in]  affinityAccess         Interface for low-level access to affinity details.
+ */
+void gmx_set_thread_affinity(const gmx::MDLogger&         mdlog,
+                             const t_commrec*             cr,
+                             const gmx_hw_opt_t*          hw_opt,
+                             const gmx::HardwareTopology& hwTop,
+                             int                          numThreadsOnThisRank,
+                             int                          numThreadsOnThisNode,
+                             int                          intraNodeThreadOffset,
+                             gmx::IThreadAffinityAccess*  affinityAccess);
+
+/*! \brief
+ * Checks the process affinity mask and if it is found to be non-zero,
+ * will honor it and disable mdrun internal affinity setting.
+ *
+ * This function should be called first before the OpenMP library gets
+ * initialized with the last argument FALSE (which will detect affinity
+ * set by external tools like taskset), and later, after the OpenMP
+ * initialization, with the last argument TRUE to detect affinity changes
+ * made by the OpenMP library.
+ *
+ * Note that this will only work on Linux as we use a GNU feature.
+ * With bAfterOpenmpInit false, it will also detect whether OpenMP environment
+ * variables for setting the affinity are set.
+ */
+void gmx_check_thread_affinity_set(const gmx::MDLogger& mdlog,
+                                   gmx_hw_opt_t*        hw_opt,
+                                   int                  ncpus,
+                                   gmx_bool             bAfterOpenmpInit);
+
+#endif
diff --git a/src/include/gromacs/mdspan/accessor_policy.h b/src/include/gromacs/mdspan/accessor_policy.h
new file mode 100644 (file)
index 0000000..2a7d1e9
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*
+ * This file is a modified version of original work of Sandia Corporation.
+ * In the spirit of the original code, this particular file can be distributed
+ * on the terms of Sandia Corporation.
+ */
+/*
+ *                         Kokkos v. 2.0
+ *               Copyright (2014) Sandia Corporation
+ *
+ * Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
+ * the U.S. Government retains certain rights in this software.
+ *
+ * Kokkos is licensed under 3-clause BSD terms of use:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Corporation nor the names of the
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+ */
+/*! \libinternal \file
+ * \brief Declares accessor policies for mdspan.
+ *
+ * Implement ways how to convert a linear offset from a pointer to memory access.
+ * \author David Hollman <dshollm@sandia.gov>
+ * \author Christian Blau <cblau@gwdg.de>
+ * \libinternal
+ * \ingroup mdspan
+ */
+#ifndef MDSPAN_ACCESSOR_POLICY_H
+#define MDSPAN_ACCESSOR_POLICY_H
+
+#include <cstddef>
+
+namespace gmx
+{
+
+/*! \libinternal \brief The most basic memory access model for mdspan.
+ * \tparam ElementType the type held in memory to be accessed
+ */
+template<class ElementType>
+class accessor_basic
+{
+public:
+    //! Type of element to be accessed.
+    using element_type = ElementType;
+    //! Pointer to element to be accessed.
+    using pointer = ElementType*;
+    //! How to determine a memory offset, provided by self accessor.
+    using offset_policy = accessor_basic;
+    //! Type of references.
+    using reference = ElementType&;
+
+    /*! \brief Shift a pointer by an offset.
+     * \param[in] p Pointer to reference memory location.
+     * \param[in] i offset from memory location.
+     * \returns pointer to offset memory location.
+     */
+    constexpr typename offset_policy::pointer offset(pointer p, ptrdiff_t i) const noexcept
+    {
+        return typename offset_policy::pointer(p + i);
+    }
+
+    /*! \brief Access element from an offset to given pointer.
+     * \param[in] p Pointer to reference memory location.
+     * \param[in] i offset from memory location.
+     * \returns reference to element stored at offset from memory location.
+     */
+    constexpr reference access(pointer p, ptrdiff_t i) const noexcept { return p[i]; }
+
+    /*! \brief Decay pointer to pointer to ElementType.
+     * NOTE This function does nothing, because it is the trivial implementation of an accessor.
+     * \returns input pointer as pointer to ElementType
+     */
+    constexpr ElementType* decay(pointer p) const noexcept { return p; }
+};
+
+} // namespace gmx
+#endif /* end of include guard: MDSPAN_ACCESSOR_POLICY_H */
diff --git a/src/include/gromacs/mdspan/extensions.h b/src/include/gromacs/mdspan/extensions.h
new file mode 100644 (file)
index 0000000..cff8eee
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal
+ * \file
+ * \brief GROMACS extensions to mdspan.
+ *
+ * \author Christian Blau <cblau@gwdg.de>
+ * \inlibraryapi
+ * \ingroup module_mdspan
+ */
+
+#ifndef GMX_MDSPAN_EXTENSIONS_H_
+#define GMX_MDSPAN_EXTENSIONS_H_
+
+#include <algorithm>
+#include <functional>
+
+#include "gromacs/mdspan/mdspan.h"
+
+namespace gmx
+{
+
+/*! \brief
+ * Free begin function addressing memory of a contiguously laid out basic_mdspan.
+ *
+ * \note Changing the elements that basic_mdspan views does not change
+ *       the view itself, so a single begin that takes a const view suffices.
+ */
+template<class BasicMdspan>
+constexpr std::enable_if_t<BasicMdspan::is_always_contiguous(), typename BasicMdspan::pointer>
+begin(const BasicMdspan& basicMdspan)
+{
+    return basicMdspan.data();
+}
+
+/*! \brief
+ * Free end function addressing memory of a contiguously laid out basic_mdspan.
+ *
+ * \note Changing the elements that basic_mdspan views does not change
+ *       the view itself, so a single end that takes a const view suffices.
+ */
+template<class BasicMdspan>
+constexpr std::enable_if_t<BasicMdspan::is_always_contiguous(), typename BasicMdspan::pointer>
+end(const BasicMdspan& basicMdspan)
+{
+    return basicMdspan.data() + basicMdspan.mapping().required_span_size();
+}
+
+//! Convenience type for often-used two dimensional extents
+using dynamicExtents2D = extents<dynamic_extent, dynamic_extent>;
+
+//! Convenience type for often-used three dimensional extents
+using dynamicExtents3D = extents<dynamic_extent, dynamic_extent, dynamic_extent>;
+
+//! Elementwise addition
+template<class BasicMdspan>
+constexpr BasicMdspan addElementwise(const BasicMdspan& span1, const BasicMdspan& span2)
+{
+    BasicMdspan result(span1);
+    std::transform(begin(span1),
+                   end(span1),
+                   begin(span2),
+                   begin(result),
+                   std::plus<typename BasicMdspan::element_type>());
+    return result;
+}
+
+//! Elementwise subtraction - left minus right
+template<class BasicMdspan>
+constexpr BasicMdspan subtractElementwise(const BasicMdspan& span1, const BasicMdspan& span2)
+{
+    BasicMdspan result(span1);
+    std::transform(begin(span1),
+                   end(span1),
+                   begin(span2),
+                   begin(result),
+                   std::minus<typename BasicMdspan::element_type>());
+    return result;
+}
+
+//! Elementwise multiplication
+template<class BasicMdspan>
+constexpr BasicMdspan multiplyElementwise(const BasicMdspan& span1, const BasicMdspan& span2)
+{
+    BasicMdspan result(span1);
+    std::transform(begin(span1),
+                   end(span1),
+                   begin(span2),
+                   begin(result),
+                   std::multiplies<typename BasicMdspan::element_type>());
+    return result;
+}
+
+//! Elementwise division - left / right
+template<class BasicMdspan>
+constexpr BasicMdspan divideElementwise(const BasicMdspan& span1, const BasicMdspan& span2)
+{
+    BasicMdspan result(span1);
+    std::transform(begin(span1),
+                   end(span1),
+                   begin(span2),
+                   begin(result),
+                   std::divides<typename BasicMdspan::element_type>());
+    return result;
+}
+
+} // namespace gmx
+
+#endif // GMX_MDSPAN_EXTENSIONS_H_
diff --git a/src/include/gromacs/mdspan/extents.h b/src/include/gromacs/mdspan/extents.h
new file mode 100644 (file)
index 0000000..64d135c
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*
+ * This file is a modified version of original work of Sandia Corporation.
+ * In the spirit of the original code, this particular file can be distributed
+ * on the terms of Sandia Corporation.
+ */
+/*
+ *                          Kokkos v. 2.0
+ *               Copyright (2014) Sandia Corporation
+ *
+ * Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
+ * the U.S. Government retains certain rights in this software.
+ *
+ * Kokkos is licensed under 3-clause BSD terms of use:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Corporation nor the names of the
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+ */
+/*! \libinternal \file
+ * \brief Declares gmx::extents for mdspan.
+ *
+ * \author Christian Trott <crtrott@sandia.gov>
+ * \author Ronan Keryell <ronan.keryell@xilinx.com>
+ * \author Carter Edwards <hedwards@nvidia.com>
+ * \author David Hollman <dshollm@sandia.gov>
+ * \author Christian Blau <cblau@gwdg.de>
+ * \ingroup mdspan
+ */
+#ifndef MDSPAN_EXTENTS_H
+#define MDSPAN_EXTENTS_H
+
+#include <cstddef>
+
+#include <array>
+
+namespace gmx
+{
+
+/*! \brief Define constant that signals dynamic extent.
+ */
+enum : std::ptrdiff_t
+{
+    dynamic_extent = -1
+};
+
+template<std::ptrdiff_t... StaticExtents>
+class extents;
+
+template<std::ptrdiff_t... LHS, std::ptrdiff_t... RHS>
+constexpr bool operator==(const extents<LHS...>& lhs, const extents<RHS...>& rhs) noexcept;
+
+template<std::ptrdiff_t... LHS, std::ptrdiff_t... RHS>
+constexpr bool operator!=(const extents<LHS...>& lhs, const extents<RHS...>& rhs) noexcept;
+
+namespace detail
+{
+
+template<int R, std::ptrdiff_t... StaticExtents>
+struct extents_analyse;
+
+/*! \libinternal \brief Enable querying extent of specific rank by splitting
+ * a static extents off the variadic template arguments.
+ *
+ */
+template<int R, std::ptrdiff_t E0, std::ptrdiff_t... StaticExtents>
+struct extents_analyse<R, E0, StaticExtents...>
+{
+
+    //! The extent analysis of the next lower rank.
+    using next_extents_analyse = extents_analyse<R - 1, StaticExtents...>;
+
+    /*! \brief Accumulate the total rank from all extents.
+     * \returns incremented rank of the next extent
+     */
+    static constexpr std::size_t rank() noexcept { return next_extents_analyse::rank() + 1; }
+    /*! \brief Accumulate the dynamic rank from all extents.
+     * This extent is static, so hand down query to the next extent analysis.
+     * \returns the dynamic rank of the next extent analysis.
+     */
+    static constexpr std::size_t rank_dynamic() noexcept
+    {
+        return next_extents_analyse::rank_dynamic();
+    }
+
+    //! Store analysis of the next extent of next lower rank.
+    next_extents_analyse next;
+
+    //! Trivial constructor.
+    constexpr extents_analyse() : next() {}
+
+    /*! \brief Construction from dynamic extents hands the extents down
+     * to the next extents analysis of lower rank.
+     * \param[in] de dynamic extents
+     */
+    template<class... DynamicExtents>
+    constexpr extents_analyse(DynamicExtents... de) : next(de...)
+    {
+    }
+
+    /*! \brief Construct from an array of dynamic extentes and rank.
+     * Hand down the dynamic rank parameters to the next extents analysis rank
+     * \param[in] de dynamic extents
+     * \param[in] r rank to read from the dynamic extent
+     */
+    template<std::size_t Rank>
+    constexpr extents_analyse(const std::array<std::ptrdiff_t, Rank>& de, const std::size_t r) :
+        next(de, r)
+    {
+    }
+
+    //! Copy constructor.
+    template<std::ptrdiff_t... OtherStaticExtents>
+    extents_analyse(extents_analyse<R, OtherStaticExtents...> rhs) : next(rhs.next)
+    {
+    }
+
+    //! Assignment operator.
+    template<std::ptrdiff_t... OtherStaticExtents>
+    extents_analyse& operator=(extents_analyse<R, OtherStaticExtents...> rhs)
+    {
+        next = rhs.next;
+        return *this;
+    }
+
+    /*! \brief Report extent of dimension r.
+     * \param[in] r the dimension to query
+     * \returns the extent in dimension r.
+     */
+    constexpr std::ptrdiff_t extent(const std::size_t r) const noexcept
+    {
+        return (r == R) ? E0 : next.extent(r);
+    }
+    /*! \brief Report the static extent of dimension r.
+     * \param[in] r the dimension to query
+     * \returns the static extent in dimension r.
+     */
+    static constexpr std::ptrdiff_t static_extent(const std::size_t r) noexcept
+    {
+        return (r == R) ? E0 : next_extents_analyse::static_extent(r);
+    }
+
+    //! Returns the extent with the first dimension sliced off
+    constexpr auto sliced_extents() const noexcept { return next; }
+};
+
+/*! \libinternal \brief Enable querying extent of specific rank by splitting
+ * a dynamic extent off the variadic template arguments.
+ */
+template<int R, std::ptrdiff_t... StaticExtents>
+struct extents_analyse<R, dynamic_extent, StaticExtents...>
+{
+    //! The extent analysis of the next lower rank.
+    using next_extents_analyse = extents_analyse<R - 1, StaticExtents...>;
+    /*! \brief Accumulate the total rank from all extents.
+     * \returns incremented rank of the next extent
+     */
+    static constexpr std::size_t rank() noexcept { return next_extents_analyse::rank() + 1; }
+    /*! \brief Accumulate the dynamic rank from all extents.
+     * \returns the dynamic rank of the next extent analysis.
+     */
+    static constexpr std::size_t rank_dynamic() noexcept
+    {
+        return next_extents_analyse::rank_dynamic() + 1;
+    }
+
+    //! Store analysis of the next extent of next lower rank.
+    next_extents_analyse next;
+    //! The dynamic extent of this rank
+    std::ptrdiff_t this_extent;
+
+    //! Trivial constructor.
+    extents_analyse() : next(), this_extent(0) {}
+
+    /*! \brief Construction from dynamic extents hands the extents down
+     * to the next extents analysis of lower rank.
+     * \param[in] E the dynamic extent of this rank.
+     * \param[in] de dynamic extents
+     */
+    template<class... DynamicExtents>
+    extents_analyse(std::ptrdiff_t E, DynamicExtents... de) : next(de...), this_extent(E)
+    {
+    }
+
+    /*! \brief Construct from an array of dynamic extentes and rank.
+     * Hand down the dynamic rank parameters to the next extents analysis rank
+     * \param[in] de dynamic extents
+     * \param[in] r rank to read from the dynamic extent
+     */
+    template<std::size_t Rank>
+    extents_analyse(const std::array<std::ptrdiff_t, Rank>& de, const std::size_t r) :
+        next(de, r + 1), this_extent(de[r])
+    {
+    }
+
+    //! Copy constructor.
+    template<std::ptrdiff_t... OtherStaticExtents>
+    extents_analyse(extents_analyse<R, OtherStaticExtents...> rhs) :
+        next(rhs.next), this_extent(rhs.extent(R))
+    {
+    }
+
+    //! Assignment operator.
+    template<std::ptrdiff_t... OtherStaticExtents>
+    extents_analyse& operator=(extents_analyse<R, OtherStaticExtents...> rhs)
+    {
+        next        = rhs.next;
+        this_extent = rhs.extent(R);
+        return *this;
+    }
+
+    /*! \brief Report extent of dimension r.
+     * \param[in] r the dimension to query
+     * \returns the extent in dimension r.
+     */
+    constexpr std::ptrdiff_t extent(const std::size_t r) const noexcept
+    {
+        return (r == R) ? this_extent : next.extent(r);
+    }
+    /*! \brief Report the static extent of dimension r.
+     * \param[in] r the dimension to query
+     * \returns the static extent in dimension r.
+     */
+    static constexpr std::ptrdiff_t static_extent(const std::size_t r) noexcept
+    {
+        return (r == R) ? dynamic_extent : next_extents_analyse::static_extent(r);
+    }
+
+    //! Returns the extent with the first dimension sliced off
+    constexpr auto sliced_extents() const noexcept { return next; }
+};
+
+/*! \libinternal \brief Specialisation for rank 0 extents analysis.
+ * Ends recursive rank analysis.
+ */
+template<>
+struct extents_analyse<0>
+{
+    /*! \brief Rank of extent of rank 0.
+     * \returns 0
+     */
+    static constexpr std::size_t rank() noexcept { return 0; }
+    /*! \brief Dynamic rank of extent of rank 0.
+     * \returns 0
+     */
+    static constexpr std::size_t rank_dynamic() noexcept { return 0; }
+
+    //! Trivial constructor.
+    constexpr extents_analyse() {}
+
+    //! Construct from array and rank, doing nothing.
+    template<std::size_t Rank>
+    extents_analyse(const std::array<std::ptrdiff_t, Rank>& /*de*/, const std::size_t /*r*/)
+    {
+    }
+
+    // extents_analyse & operator=(extents_analyse) = default;
+
+    /*! \brief Extent of rank 0 is 1, ensuring that product of extents yields required size and not zero.
+     * NOTE changed from ORNL reference implementation in making this static constexpr instead of constexpr .. const
+     */
+    static constexpr std::ptrdiff_t extent(const std::size_t /*r*/) noexcept { return 1; }
+
+    //! Static extent of rank 0 is 1, ensuring that product of extents yields required size and not zero.
+    static constexpr std::ptrdiff_t static_extent(const std::size_t /*r*/) noexcept { return 1; }
+};
+
+template<std::ptrdiff_t E0, std::ptrdiff_t... StaticExtents>
+struct sliced_extents
+{
+    using type = extents<StaticExtents...>;
+};
+} // namespace detail
+
+/*! \libinternal \brief Multidimensional extents with static and dynamic dimensions.
+ *
+ * Describes a multidimensional index space of rank R.
+ * This is equivalent to the Cartesian product space of integer intervals
+ * [0, N_0) x [0, N_1) x ... x [0,N_{R-1} )
+ *
+ * Confer to P0009r8 of the Library Evolution Working Group and mdspan.extents
+ *
+ * \tparam StaticExtents rank number of extents, where the dynamic_extent
+ * constant for static extent is used to signal a dynamic extent.
+ */
+template<std::ptrdiff_t... StaticExtents>
+class extents
+{
+private:
+    using extents_analyse_t = detail::extents_analyse<sizeof...(StaticExtents), StaticExtents...>;
+
+public:
+    //! Type used to index elements.
+    using index_type = std::ptrdiff_t;
+    //! Trivial constructor
+    constexpr extents() noexcept {}
+    //! Move constructor
+    constexpr extents(extents&&) noexcept = default;
+    //! Copy constructor.
+    constexpr extents(const extents&) noexcept = default;
+    /*! \brief Construct with dynamic extents.
+     *
+     * Allows for extents(u,v,w..) syntax when setting dynamic extents
+     *
+     * \tparam IndexType type of index
+     * \param[in] dn first dynamic index
+     * \param[in] DynamicExtents parameter pack
+     */
+    template<class... IndexType>
+    constexpr extents(std::ptrdiff_t dn, IndexType... DynamicExtents) noexcept :
+        impl(dn, DynamicExtents...)
+    {
+        static_assert(1 + sizeof...(DynamicExtents) == rank_dynamic(), "");
+    }
+
+    /*! \brief Construct from array of dynamic extents.
+     *
+     * Allows for extents({{u,v,w..}}) syntax when setting dynamic extents
+     *
+     * \param[in] dynamic_extents array of dynamic rank size containing extents
+     */
+    constexpr extents(const std::array<std::ptrdiff_t, extents_analyse_t::rank_dynamic()> dynamic_extents) noexcept
+        :
+        impl(dynamic_extents, 0)
+    {
+    }
+
+    //! Copy constructor
+    template<std::ptrdiff_t... OtherStaticExtents>
+    extents(const extents<OtherStaticExtents...>& other) : impl(other.impl)
+    {
+    }
+
+    //! Default move assignment
+    extents& operator=(extents&&) noexcept = default;
+    //! Default copy assignment
+    extents& operator=(const extents&) noexcept = default;
+    //! Copy assignment
+    template<std::ptrdiff_t... OtherStaticExtents>
+    extents& operator=(const extents<OtherStaticExtents...>& other)
+    {
+        impl = other.impl;
+        return *this;
+    }
+    //! Default destructor
+    ~extents() = default;
+
+    // [mdspan.extents.obs]
+    /*! \brief The rank of the extent.
+     * \returns the rank all extents together
+     */
+    static constexpr std::size_t rank() noexcept { return sizeof...(StaticExtents); }
+    /*! \brief The rank of the dynamic extents.
+     * \returns Only the dynamic extents.
+     */
+    static constexpr std::size_t rank_dynamic() noexcept
+    {
+        return extents_analyse_t::rank_dynamic();
+    }
+    /*! \brief The rank of the static extents.
+     * \returns Only the static extents.
+     */
+    static constexpr index_type static_extent(std::size_t k) noexcept
+    {
+        return extents_analyse_t::static_extent(rank() - k);
+    }
+    /*! \brief The extent along a specific dimension.
+     * \param[in] k the dimension
+     * \returns the extent along that dimension
+     */
+    constexpr index_type extent(std::size_t k) const noexcept { return impl.extent(rank() - k); }
+    //! Returns the extent with the first dimension sliced off
+    constexpr auto sliced_extents() const noexcept
+    {
+        return typename detail::sliced_extents<StaticExtents...>::type(impl.sliced_extents());
+    }
+
+private:
+    extents(extents_analyse_t o) : impl(o) {}
+    //! For copy assignment, extents are friends of extents.
+    template<std::ptrdiff_t...>
+    friend class extents;
+    //! The implementation class.
+    extents_analyse_t impl;
+};
+
+
+/*! \brief Comparison operator.
+ * \returns true if extents are equal
+ */
+template<std::ptrdiff_t... LHS, std::ptrdiff_t... RHS>
+constexpr bool operator==(const extents<LHS...>& lhs, const extents<RHS...>& rhs) noexcept
+{
+    bool equal = lhs.rank() == rhs.rank();
+    for (std::size_t r = 0; r < lhs.rank(); r++)
+    {
+        equal = equal && (lhs.extent(r) == rhs.extent(r));
+    }
+    return equal;
+}
+
+/*! \brief Check for non-equality.
+ * \returns true if extents are unequal
+ */
+template<std::ptrdiff_t... LHS, std::ptrdiff_t... RHS>
+constexpr bool operator!=(const extents<LHS...>& lhs, const extents<RHS...>& rhs) noexcept
+{
+    return !(lhs == rhs);
+}
+
+} // namespace gmx
+#endif /* end of include guard: MDSPAN_EXTENTS_H */
diff --git a/src/include/gromacs/mdspan/layouts.h b/src/include/gromacs/mdspan/layouts.h
new file mode 100644 (file)
index 0000000..7482508
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*
+ * This file is a modified version of original work of Sandia Corporation.
+ * In the spirit of the original code, this particular file can be distributed
+ * on the terms of Sandia Corporation.
+ */
+/*
+ *                          Kokkos v. 2.0
+ *               Copyright (2014) Sandia Corporation
+ *
+ * Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
+ * the U.S. Government retains certain rights in this software.
+ *
+ * Kokkos is licensed under 3-clause BSD terms of use:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Corporation nor the names of the
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+ */
+/*! \libinternal \file
+ * \brief Declares gmx::layout_right for mdspan.
+ *
+ * \author David Hollman <dshollm@sandia.gov>
+ * \author Christian Blau <cblau@gwdg.de>
+ * \ingroup mdspan
+ */
+#ifndef MDSPAN_LAYOUTS_H
+#define MDSPAN_LAYOUTS_H
+#include <cstddef>
+
+#include <type_traits>
+
+namespace gmx
+{
+
+/*! \libinternal \brief Right-aligned array layout indexer.
+ * Carries the mapping class performing the translation from multidimensional
+ * index to one-dimensional number.
+ */
+class layout_right
+{
+
+public:
+    /*! \libinternal \brief Mapping from multidimensional indices within extents to 1D index.
+     * \tparam Extents the extents of the multidimensional integers for the mapping.
+     */
+    template<class Extents>
+    class mapping
+    {
+    private:
+        //! The extents.
+        Extents m_extents;
+
+    public:
+        //! exposing the type of indices
+        using index_type = ptrdiff_t;
+        //! exposing the type of the extents
+        using extents_type = Extents;
+        //! Default constructor.
+        constexpr mapping() noexcept = default;
+        //! Default move constructor.
+        constexpr mapping(mapping&&) noexcept = default;
+        //! Default copy constructor.
+        constexpr mapping(const mapping&) noexcept = default;
+        //! Default move assignment
+        mapping& operator=(mapping&&) noexcept = default;
+        //! Default copy assignment
+        mapping& operator=(const mapping&) noexcept = default;
+        /*! \brief Construct mapping, setting extents
+         * \param[in] ext the extents
+         */
+        constexpr mapping(const Extents& ext) noexcept : m_extents(ext) {}
+        /*! \brief Return the extents.
+         * \returns extents
+         */
+        constexpr const Extents& extents() const noexcept { return m_extents; }
+
+    private:
+        /* \brief End recursion helper function for static offset calculation.
+         * \param[in] sum The accumulated offset over all dimensions
+         * \returns The offset.
+         */
+        static constexpr index_type offset(const size_t /*r*/, const ptrdiff_t sum) { return sum; }
+
+        /* \brief Statically calculate offset from index and extent.
+         * For a multidimensional index (i0,i1,..,in), in a right memory
+         * layout, 'i0' denotes the slowest moving dimension and
+         * 'in' the fastest moving dimension.
+         * The overall offset within extents N = (N0,..,Nn) is then
+         * offest = i0 * N1 * .. * Nn + i1 * N2 * .. * Nn + in-1 * Nn + in
+         *        = (((i0*N1+i1)*N2+i2)*N3+i3) ...
+         * \param[in] r current rank
+         * \param[in] sum current sum up to this rank
+         * \param[in] i index
+         * \oaram[in] indices The rest of the paramter pack.
+         * \returns The offset.
+         */
+        template<class... Indices>
+        inline constexpr index_type
+        offset(const size_t r, ptrdiff_t sum, const index_type i, Indices... indices) const noexcept
+        {
+            return offset(r + 1, sum * m_extents.extent(r) + i, indices...);
+        }
+
+    public:
+        /*! \brief Return the size of the underlying one-dimensional
+         * data structure, so that the mapping is always valid.
+         *
+         * \returns number of span elements
+         */
+        constexpr index_type required_span_size() const noexcept
+        {
+            index_type size = 1;
+            for (size_t r = 0; r < m_extents.rank(); r++)
+            {
+                size *= m_extents.extent(r);
+            }
+            return size;
+        }
+
+        /*! \brief Map the multidimensional indices to 1D.
+         * Requires number of indicies have the same dimensionality as the mapping.
+         * \tparam Indices type of the indices to be mapped
+         * \param[in] indices the indices to be mapped
+         * \returns One-dimensional integer index.
+         */
+        template<class... Indices>
+        std::enable_if_t<sizeof...(Indices) == Extents::rank(), index_type> constexpr
+        operator()(Indices... indices) const noexcept
+        {
+            return offset(0, 0, indices...);
+        }
+
+        //! Report that this mapping is always unique.
+        static constexpr bool is_always_unique() noexcept { return true; }
+        //! Report that this mapping is always contiguous.
+        static constexpr bool is_always_contiguous() noexcept { return true; }
+        //! Report that this mapping is always strided.
+        static constexpr bool is_always_strided() noexcept { return true; }
+
+        //! Report that this mapping is unique.
+        constexpr bool is_unique() const noexcept { return true; }
+        //! Report that this mapping is contiguous.
+        constexpr bool is_contiguous() const noexcept { return true; }
+        //! Report that this mapping is strided.
+        constexpr bool is_strided() const noexcept { return true; }
+        /*!\brief Return the stride of dimension r.
+         * \param[in] R rank of the stride to be queried.
+         * \returns the stride along dimension r.
+         */
+        constexpr index_type stride(const size_t R) const noexcept
+        {
+            ptrdiff_t stride = 1;
+            for (size_t r = m_extents.rank() - 1; r > R; r--)
+            {
+                stride *= m_extents.extent(r);
+            }
+            return stride;
+        }
+
+    }; // class mapping
+
+}; // class layout_right
+
+} // namespace gmx
+#endif /* end of include guard: MDSPAN_LAYOUTS_H */
diff --git a/src/include/gromacs/mdspan/mdspan.h b/src/include/gromacs/mdspan/mdspan.h
new file mode 100644 (file)
index 0000000..b3eb1b8
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*
+ * This file is a modified version of original work of Sandia Corporation.
+ * In the spirit of the original code, this particular file can be distributed
+ * on the terms of Sandia Corporation.
+ */
+/*
+ *                          Kokkos v. 2.0
+ *               Copyright (2014) Sandia Corporation
+ *
+ * Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
+ * the U.S. Government retains certain rights in this software.
+ *
+ * Kokkos is licensed under 3-clause BSD terms of use:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Corporation nor the names of the
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+ */
+/*! \libinternal \file
+ * \brief Declares gmx::mdspan
+ *
+ * \author Christian Trott <crtrott@sandia.gov>
+ * \author Ronan Keryell <ronan.keryell@xilinx.com>
+ * \author Carter Edwards <hedwards@nvidia.com>
+ * \author David Hollman <dshollm@sandia.gov>
+ * \author Christian Blau <cblau@gwdg.de>
+ * \inlibraryapi
+ * \ingroup mdspan
+ */
+#ifndef MDSPAN_MDSPAN_H
+#define MDSPAN_MDSPAN_H
+
+#include <array>
+#include <type_traits>
+
+#include "accessor_policy.h"
+#include "extents.h"
+#include "layouts.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief Multidimensional array indexing and memory access with flexible mapping and access model.
+ *
+ * \tparam ElementType Type of elemnt to be viewed
+ * \tparam Extents The dimensions of the multidimenisonal array to view.
+ * \tparam LayoutPolicy Describes is the memory layout of the multidimensional array; right by default.
+ * \tparam AccessorPolicy Describes memory access model.
+ */
+template<class ElementType, class Extents, class LayoutPolicy = layout_right, class AccessorPolicy = accessor_basic<ElementType>>
+class basic_mdspan
+{
+public:
+    //! Expose type used to define the extents of the data.
+    using extents_type = Extents;
+    //! Expose type used to define the layout of the data.
+    using layout_type = LayoutPolicy;
+    //! Expose type used to define the memory access model of the data.
+    using accessor_type = AccessorPolicy;
+    //! Expose type used to map multidimensional indices to one-dimensioal indices.
+    using mapping_type = typename layout_type::template mapping<extents_type>;
+    //! Exposes the type of stored element.
+    using element_type = typename accessor_type::element_type;
+    //! Expose the underlying type of the stored elements.
+    using value_type = std::remove_cv_t<element_type>;
+    //! Expose the type used for indexing.
+    using index_type = ptrdiff_t;
+    //! Expose type for index differences.
+    using difference_type = ptrdiff_t;
+    //! Expose underlying pointer to data type.
+    using pointer = typename accessor_type::pointer;
+    //! Expose reference to data type.
+    using reference = typename accessor_type::reference;
+
+    //! Trivial constructor
+    constexpr basic_mdspan() noexcept : acc_(), map_(), ptr_() {}
+    //! Move constructor
+    constexpr basic_mdspan(basic_mdspan&& other) noexcept = default;
+    //! copy constructor
+    constexpr basic_mdspan(const basic_mdspan& other) noexcept = default;
+    //! Copy assignment
+    basic_mdspan& operator=(const basic_mdspan& other) noexcept = default;
+    //! Move assignment
+    basic_mdspan& operator=(basic_mdspan&& other) noexcept = default;
+
+    //! Copy constructor
+    template<class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherAccessor>
+    constexpr basic_mdspan(
+            const basic_mdspan<OtherElementType, OtherExtents, OtherLayoutPolicy, OtherAccessor>& rhs) noexcept
+        :
+        acc_(rhs.acc_), map_(rhs.map_), ptr_(rhs.ptr_)
+    {
+    }
+    //! Copy assignment constructor
+    template<class OtherElementType, class OtherExtents, class OtherLayoutPolicy, class OtherAccessor>
+    basic_mdspan&
+    operator=(const basic_mdspan<OtherElementType, OtherExtents, OtherLayoutPolicy, OtherAccessor>& rhs) noexcept
+    {
+        acc_ = rhs.acc_;
+        map_ = rhs.map_;
+        ptr_ = rhs.ptr_;
+        return *this;
+    }
+
+    /*!\brief Construct mdspan by setting the dynamic extents and pointer to data.
+     * \param[in] ptr Pointer to data to be accessed by this span
+     * \param[in] DynamicExtents
+     * \tparam IndexType index type to describe dynamic extents
+     */
+    template<class... IndexType>
+    explicit constexpr basic_mdspan(pointer ptr, IndexType... DynamicExtents) noexcept :
+        acc_(accessor_type()), map_(extents_type(DynamicExtents...)), ptr_(ptr)
+    {
+    }
+    /*! \brief Construct from array describing dynamic extents.
+     * \param[in] ptr Pointer to data to be accessed by this span
+     * \param[in] dynamic_extents Array the size of dynamic extents.
+     */
+    constexpr basic_mdspan(pointer                                                    ptr,
+                           const std::array<ptrdiff_t, extents_type::rank_dynamic()>& dynamic_extents) :
+        acc_(accessor_type()), map_(extents_type(dynamic_extents)), ptr_(ptr)
+    {
+    }
+    /*! \brief Construct from pointer and mapping.
+     * \param[in] ptr Pointer to data to be accessed by this span
+     * \param[in] m Mapping from multidimenisonal indices to one-dimensional offset.
+     */
+    constexpr basic_mdspan(pointer ptr, const mapping_type& m) noexcept :
+        acc_(accessor_type()), map_(m), ptr_(ptr)
+    {
+    }
+    /*! \brief Construct with pointer, mapping and accessor.
+     * \param[in] ptr Pointer to data to be accessed by this span
+     * \param[in] m Mapping from multidimenisonal indices to one-dimensional offset.
+     * \param[in] a Accessor implementing memory access model.
+     */
+    constexpr basic_mdspan(pointer ptr, const mapping_type& m, const accessor_type& a) noexcept :
+        acc_(a), map_(m), ptr_(ptr)
+    {
+    }
+    /*! \brief Construct mdspan from multidimensional arrays implemented with mdspan
+     *
+     * Requires the container to have a view_type describing the mdspan, which is
+     * accessible through an asView() call
+     *
+     *  This allows functions to declare mdspans as arguments, but take e.g. multidimensional
+     *  arrays implicitly during the function call
+     * \tparam U container type
+     * \param[in] other mdspan-implementing container
+     */
+    template<typename U, typename = std::enable_if_t<std::is_same_v<typename std::remove_reference_t<U>::view_type::element_type, ElementType>>>
+    constexpr basic_mdspan(U&& other) : basic_mdspan(other.asView())
+    {
+    }
+    /*! \brief Construct mdspan of const Elements from multidimensional arrays implemented with mdspan
+     *
+     * Requires the container to have a const_view_type describing the mdspan, which is
+     * accessible through an asConstView() call
+     *
+     *  This allows functions to declare mdspans as arguments, but take e.g. multidimensional
+     *  arrays implicitly during the function call
+     * \tparam U container type
+     * \param[in] other mdspan-implementing container
+     */
+    template<typename U, typename = std::enable_if_t<std::is_same_v<typename std::remove_reference_t<U>::const_view_type::element_type, ElementType>>>
+    constexpr basic_mdspan(const U& other) : basic_mdspan(other.asConstView())
+    {
+    }
+    /*! \brief Brace operator to access multidimensional array element.
+     * \param[in] indices The multidimensional indices of the object.
+     * Requires rank() == sizeof...(IndexType). Slicing is implemented via sub_span.
+     * \returns reference to element at indices.
+     */
+    template<class... IndexType>
+    constexpr std::enable_if_t<sizeof...(IndexType) == extents_type::rank(), reference>
+    operator()(IndexType... indices) const noexcept
+    {
+        return acc_.access(ptr_, map_(indices...));
+    }
+    /*! \brief Canonical bracket operator for one-dimensional arrays.
+     * Allows mdspan to act like array in one-dimension.
+     * Enabled only when rank==1.
+     * \param[in] i one-dimensional index
+     * \returns reference to element stored at position i
+     */
+    template<class IndexType>
+    constexpr std::enable_if_t<std::is_integral_v<IndexType> && extents_type::rank() == 1, reference>
+    operator[](const IndexType& i) const noexcept
+    {
+        return acc_.access(ptr_, map_(i));
+    }
+    /*! \brief Bracket operator for multi-dimensional arrays.
+     *
+     * \note Prefer operator() for better compile-time and run-time performance
+     *
+     * Slices two- and higher-dimensional arrays along a given slice by
+     * returning a new basic_mdspan that drops the first extent and indexes
+     * the remaining extents
+     *
+     * \note Currently only implemented for layout_right
+     * \note For layout_right this implementation has significant
+     *       performance benefits over implementing a more general slicing
+     *       operator with a strided layout
+     * \note Enabled only when rank() > 1
+     *
+     * \tparam IndexType integral tyoe for the index that enables indexing
+     *                   with, e.g., int or size_t
+     * \param[in] index  one-dimensional index of the slice to be indexed
+     *
+     * \returns basic_mdspan that is sliced at the given index
+     */
+    template<class IndexType,
+             typename sliced_mdspan_type = basic_mdspan<element_type, decltype(extents_type().sliced_extents()), LayoutPolicy, AccessorPolicy>>
+    constexpr std::enable_if_t<std::is_integral_v<IndexType> && (extents_type::rank() > 1)
+                                       && std::is_same_v<LayoutPolicy, layout_right>,
+                               sliced_mdspan_type>
+    operator[](const IndexType index) const noexcept
+    {
+        return sliced_mdspan_type(ptr_ + index * stride(0), extents().sliced_extents());
+    }
+    //! Report the rank.
+    static constexpr int rank() noexcept { return extents_type::rank(); }
+    //! Report the dynamic rank.
+    static constexpr int rank_dynamic() noexcept { return extents_type::rank_dynamic(); }
+    /*! \brief Return the static extent.
+     * \param[in] k dimension to query for static extent
+     * \returns static extent along specified dimension
+     */
+    constexpr index_type static_extent(size_t k) const noexcept
+    {
+        return map_.extents().static_extent(k);
+    }
+
+    /*! \brief Return the extent.
+     * \param[in] k dimension to query for extent
+     * \returns extent along specified dimension
+     */
+    constexpr index_type extent(int k) const noexcept { return map_.extents().extent(k); }
+
+    //! Return all extents
+    constexpr const extents_type& extents() const noexcept { return map_.extents(); }
+    //! Report if mappings for this basic_span is always unique.
+    static constexpr bool is_always_unique() noexcept { return mapping_type::is_always_unique(); }
+    //! Report if mapping for this basic_span is always strided
+    static constexpr bool is_always_strided() noexcept { return mapping_type::is_always_strided(); }
+    //! Report if mapping for this basic_span is always is_contiguous
+    static constexpr bool is_always_contiguous() noexcept
+    {
+        return mapping_type::is_always_contiguous();
+    }
+    //! Report if the currently applied map is unique
+    constexpr bool is_unique() const noexcept { return map_.is_unique(); }
+    //! Report if the currently applied map is strided
+    constexpr bool is_strided() const noexcept { return map_.is_strided(); }
+    //! Report if the currently applied map is contiguous
+    constexpr bool is_contiguous() const noexcept { return map_.is_contiguous(); }
+    //! Report stride along a specific rank.
+    constexpr index_type stride(size_t r) const noexcept { return map_.stride(r); }
+    //! Return the currently applied mapping.
+    constexpr mapping_type mapping() const noexcept { return map_; }
+    //! Return the memory access model.
+    constexpr accessor_type accessor() const noexcept { return acc_; }
+    //! Return pointer to underlying data
+    constexpr pointer data() const noexcept { return ptr_; }
+
+private:
+    //! The memory access model
+    accessor_type acc_;
+    //! The transformation from multidimenisonal index to memory offset.
+    mapping_type map_;
+    //! Memory location handle
+    pointer ptr_;
+};
+
+//! basic_mdspan with wrapped indices, basic_accessor policiy and right-aligned  memory layout.
+template<class T, ptrdiff_t... Indices>
+using mdspan = basic_mdspan<T, extents<Indices...>, layout_right, accessor_basic<T>>;
+
+} // namespace gmx
+
+#endif /* end of include guard: MDSPAN_MDSPAN_H */
diff --git a/src/include/gromacs/mdtypes/atominfo.h b/src/include/gromacs/mdtypes/atominfo.h
new file mode 100644 (file)
index 0000000..104b1f0
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file makes declarations used for storing bitfields
+ * describing each atom so that other modules can efficiently process
+ * them.
+ *
+ * \inlibraryapi
+ */
+
+#ifndef GMX_MDTYPES_ATOMINFO_H
+#define GMX_MDTYPES_ATOMINFO_H
+
+#include <vector>
+
+namespace gmx
+{
+
+/*! \brief Constants whose bit describes a property of an atom in
+ * AtomInfoWithinMoleculeBlock.atomInfo.
+ *
+ * No bit should exceed 1 << 63, so that it fits into a 64-bit
+ * integer.
+ *
+ * Since the tpx format support max 256 energy groups, we do the same
+ * here, reserving bits 0-7 for the energy-group ID.
+ */
+//! \{
+static constexpr int64_t sc_atomInfo_FreeEnergyPerturbation            = 1 << 15;
+static constexpr int64_t sc_atomInfo_HasPerturbedChargeIn14Interaction = 1 << 16;
+static constexpr int64_t sc_atomInfo_Exclusion                         = 1 << 17;
+static constexpr int64_t sc_atomInfo_Constraint                        = 1 << 20;
+static constexpr int64_t sc_atomInfo_Settle                            = 1 << 21;
+static constexpr int64_t sc_atomInfo_BondCommunication                 = 1 << 22;
+static constexpr int64_t sc_atomInfo_HasVdw                            = 1 << 23;
+static constexpr int64_t sc_atomInfo_HasCharge                         = 1 << 24;
+//! \}
+//! The first 8 bits are reserved for energy-group ID
+static constexpr int64_t sc_atomInfo_EnergyGroupIdMask = 0b11111111;
+
+/*! \brief Contains information about each atom in a molecule block of
+ * the global topology. */
+struct AtomInfoWithinMoleculeBlock
+{
+    //! Index within the system of the first atom in the molecule block
+    int indexOfFirstAtomInMoleculeBlock = 0;
+    //! Index within the system of the last atom in the molecule block
+    int indexOfLastAtomInMoleculeBlock = 0;
+    /*! \brief Atom info for each atom in the block.
+     *
+     * The typical case is that all atoms are identical for each
+     * molecule of the block, and if so this vector has size equal to
+     * the number of atoms in the molecule.
+     *
+     * An example of an atypical case is QM/MM, where multiple
+     * molecules might be present and different molecules have
+     * different atoms within any one QM group or region. Now there are
+     * multiple kinds of molecules with the same connectivity, so we simply
+     * write out the atom info for the entire molecule block. Then the
+     * size equals the product of the number of atoms in the
+     * molecule and the number of molecules.
+     *
+     * The vector needs to be indexed accordingly.
+     */
+    std::vector<int64_t> atomInfo;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdtypes/awh_correlation_history.h b/src/include/gromacs/mdtypes/awh_correlation_history.h
new file mode 100644 (file)
index 0000000..47a1fb8
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * Contains datatypes and function declarations needed by AWH to
+ * have its force correlation data checkpointed.
+ *
+ * \author Viveca Lindahl
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDTYPES_AWH_CORRELATION_HISTORY_H
+#define GMX_MDTYPES_AWH_CORRELATION_HISTORY_H
+
+#include <vector>
+
+namespace gmx
+{
+
+/*! \cond INTERNAL */
+
+//! Correlation block averaging data.
+struct CorrelationBlockDataHistory
+{
+    double blockSumWeight;       /**< Sum weights for current block. */
+    double blockSumSquareWeight; /**< Sum weights^2 for current block. */
+    double blockSumWeightX;      /**< Weighted sum of x for current block. */
+    double blockSumWeightY;      /**< Weighted sum of y for current block. */
+    double sumOverBlocksSquareBlockWeight; /**< Sum over all blocks in the simulation of block weight^2 over the whole simulation. */
+    double sumOverBlocksBlockSquareWeight; /**< Sum over all blocks in the simulation of weight^2 over the whole simulation. */
+    double sumOverBlocksBlockWeightBlockWeightX; /**< Sum over all blocks in the simulation of block weight times blockSumWeightX over the whole simulation. */
+    double sumOverBlocksBlockWeightBlockWeightY; /**< Sum over all blocks in the simulation of block weight times blockSumWeightY over the whole simulation. */
+    double blockLength; /**< The length of each block used for block averaging. */
+    int    previousBlockIndex; /**< The last block index data was added to (needed only for block length in terms of time). */
+    double correlationIntegral; /**< The time integral of the correlation function of x and y, corr(x(0), y(t)). */
+};
+
+//! Grid of local correlation matrices.
+struct CorrelationGridHistory
+{
+    /* These counts here since we curently need them for initializing the correlation grid when reading a checkpoint */
+    int numCorrelationTensors =
+            0; /**< Number correlation tensors in the grid (equal to the number of points). */
+    int tensorSize = 0; /**< The number of stored correlation matrix elements. */
+    int blockDataListSize =
+            0; /**< To be able to increase the block length later on, data is saved for several block lengths for each element. */
+
+    /* We store all tensor sequentially in a buffer */
+    std::vector<CorrelationBlockDataHistory> blockDataBuffer; /**< Buffer that contains the correlation data. */
+};
+
+/*! \endcond */
+
+/*! \brief
+ * Initialize correlation grid history, sets all sizes.
+ *
+ * \param[in,out] correlationGridHistory  Correlation grid history for master rank.
+ * \param[in] numCorrelationTensors       Number of correlation tensors in the grid.
+ * \param[in] tensorSize                  Number of correlation elements in each tensor.
+ * \param[in] blockDataListSize           The number of blocks in the list of each tensor element.
+ */
+void initCorrelationGridHistory(CorrelationGridHistory* correlationGridHistory,
+                                int                     numCorrelationTensors,
+                                int                     tensorSize,
+                                int                     blockDataListSize);
+
+} // namespace gmx
+
+#endif /* GMX_MDTYPES_AWH_CORRELATION_HISTORY_H */
diff --git a/src/include/gromacs/mdtypes/awh_history.h b/src/include/gromacs/mdtypes/awh_history.h
new file mode 100644 (file)
index 0000000..7e1a4e4
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * Contains datatypes and function declarations needed by AWH to
+ * have its data checkpointed.
+ *
+ * \author Viveca Lindahl
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDTYPES_AWHHISTORY_H
+#define GMX_MDTYPES_AWHHISTORY_H
+
+#include <cstdint>
+
+#include <vector>
+
+#include "gromacs/mdtypes/awh_correlation_history.h"
+
+namespace gmx
+{
+enum class CheckpointDataOperation;
+template<CheckpointDataOperation operation>
+class CheckpointData;
+
+/*! \cond INTERNAL */
+
+//! Grid point state history data.
+struct AwhPointStateHistory
+{
+    double  bias;                /**< Current biasing function estimate */
+    double  free_energy;         /**< Current estimate of the convolved free energy/PMF. */
+    double  target;              /**< Current target distribution, normalized to 1 */
+    double  weightsum_iteration; /**< Accumulated weight this iteration (1 replica) */
+    double  weightsum_covering;  /**< Accumulated weights for covering checks */
+    double  weightsum_tot;       /**< Accumulated weights, never reset */
+    double  weightsum_ref;       /**< The reference weight histogram determining the f updates */
+    int64_t last_update_index;   /**< The last update that was performed at this point. */
+    double  log_pmfsum;          /**< Logarithm of the PMF histogram (for 1 replica) */
+    double  visits_iteration;    /**< Visits to this bin this iteration (1 replica) */
+    double  visits_tot;          /**< Accumulated visits to this bin */
+};
+
+//! The global AWH bias history state, contains most data of the corresponding struct in awh.h.
+struct AwhBiasStateHistory
+{
+    int     umbrellaGridpoint; /**< Index for the current umbrella reference coordinate point (for umbrella potential type) */
+    int     origin_index_updatelist; /**< Point index of the origin of the subgrid that has been touched since last update. */
+    int     end_index_updatelist; /**< Point index of the end of the subgrid that has been touched since last update. */
+    bool    in_initial;           /**< True if in the intial stage. */
+    bool    equilibrateHistogram; /**< True if histogram needs equilibration. */
+    double  histSize;             /**< Size of reference weight histogram. */
+    double  logScaledSampleWeight; /**< The log of the current sample weight, scaled because of the histogram rescaling. */
+    double  maxLogScaledSampleWeight; /**< Maximum sample weight obtained for previous (smaller) histogram sizes. */
+    int64_t numUpdates; /**< The number of updates. */
+
+    /*! \brief Constructor. */
+    AwhBiasStateHistory() :
+        umbrellaGridpoint(0),
+        origin_index_updatelist(0),
+        end_index_updatelist(0),
+        in_initial(false),
+        equilibrateHistogram(false),
+        histSize(0),
+        logScaledSampleWeight(0),
+        maxLogScaledSampleWeight(0),
+        numUpdates(0)
+    {
+    }
+};
+
+//! AWH bias history data. Note that this is a copy of an AWH internal struct.
+struct AwhBiasHistory
+{
+    std::vector<AwhPointStateHistory> pointState; /**< History for grid coordinate points. */
+
+    AwhBiasStateHistory    state;                /**< The global state of the AWH bias. */
+    CorrelationGridHistory forceCorrelationGrid; /**< History for force correlation statistics. */
+};
+
+//! A collection of AWH bias history data. */
+struct AwhHistory
+{
+    std::vector<AwhBiasHistory> bias; /**< History for each bias. */
+    double potentialOffset;           /**< The offset of the bias potential due to bias updates. */
+
+    /*! \brief Constructor. */
+    AwhHistory() : potentialOffset(0) {}
+
+    /*! \brief Allows to read and write checkpoint within modular simulator
+     *
+     * \tparam operation  Whether we're reading or writing
+     * \param checkpointData  The CheckpointData object
+     */
+    template<CheckpointDataOperation operation>
+    void doCheckpoint(CheckpointData<operation> checkpointData);
+};
+
+/*! \endcond */
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdtypes/awh_params.h b/src/include/gromacs/mdtypes/awh_params.h
new file mode 100644 (file)
index 0000000..930bd06
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * Declares AWH parameter data types.
+ *
+ * Besides internal use by the AWH module, the AWH parameters are needed
+ * for reading the user input (mdp) file and for reading and writing the
+ * parameters to the mdrun input (tpr) file.
+ *
+ * \author Viveca Lindahl
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDTYPES_AWH_PARAMS_H
+#define GMX_MDTYPES_AWH_PARAMS_H
+
+#include <vector>
+
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/classhelpers.h"
+
+struct t_inpfile;
+struct t_inputrec;
+struct pull_params_t;
+using warninp_t = struct warninp*;
+
+namespace gmx
+{
+
+class ISerializer;
+//! Target distribution enum.
+enum class AwhTargetType : int
+{
+    Constant,
+    Cutoff,
+    Boltzmann,
+    LocalBoltzmann,
+    Count,
+    Default = Constant
+};
+//! String for target distribution.
+const char* enumValueToString(AwhTargetType enumValue);
+
+//! Weight histogram growth enum.
+enum class AwhHistogramGrowthType : int
+{
+    ExponentialLinear,
+    Linear,
+    Count,
+    Default = ExponentialLinear
+};
+//! String for weight histogram growth
+const char* enumValueToString(AwhHistogramGrowthType enumValue);
+
+//! AWH potential type enum.
+enum class AwhPotentialType : int
+{
+    Convolved,
+    Umbrella,
+    Count,
+    Default = Convolved
+};
+//! String for AWH potential type
+const char* enumValueToString(AwhPotentialType enumValue);
+
+//! AWH bias reaction coordinate provider
+enum class AwhCoordinateProviderType : int
+{
+    Pull,
+    FreeEnergyLambda,
+    Count,
+    Default = Pull
+};
+//! String for AWH bias reaction coordinate provider.
+const char* enumValueToString(AwhCoordinateProviderType enumValue);
+
+class AwhDimParams
+{
+public:
+    //! Constructor from input file.
+    AwhDimParams(std::vector<t_inpfile>* inp, const std::string& prefix, warninp_t wi, bool bComment);
+    //! Constructor to generate from file reading.
+    explicit AwhDimParams(ISerializer* serializer);
+
+    //! Move constructor.
+    AwhDimParams(AwhDimParams&&) = default;
+    //! Move assignment operator.
+    AwhDimParams& operator=(AwhDimParams&&) = default;
+    //! Delete copy constructor.
+    AwhDimParams(const AwhDimParams&) = delete;
+    //! Delete copy assignment.
+    AwhDimParams& operator=(const AwhDimParams&) = delete;
+
+    //! Which module is providing the reaction coordinate.
+    AwhCoordinateProviderType coordinateProvider() const { return eCoordProvider_; }
+    //! Index for reaction coordinate in provider.
+    int coordinateIndex() const { return coordIndex_; }
+    //! Start value for interval.
+    double origin() const { return origin_; }
+    //! End value for interval.
+    double end() const { return end_; }
+    //! Period for the dimension.
+    double period() const { return period_; }
+    //! Set period value dependent on state.
+    void setPeriod(double period) { period_ = period; }
+    //! Force constant for this dimension.
+    double forceConstant() const { return forceConstant_; }
+    //! Estimated diffusion constant.
+    double diffusion() const { return diffusion_; }
+    //! Initial value for coordinate.
+    double initialCoordinate() const { return coordValueInit_; }
+    //! Set initial coordinate value dependent on state.
+    void setInitialCoordinate(double initialCoordinate) { coordValueInit_ = initialCoordinate; }
+    //! Diameter needed to be sampled.
+    double coverDiameter() const { return coverDiameter_; }
+    //! Write datastructure.
+    void serialize(ISerializer* serializer);
+
+private:
+    //! The module providing the reaction coordinate.
+    AwhCoordinateProviderType eCoordProvider_;
+    //! Index of reaction coordinate in the provider.
+    int coordIndex_ = 0;
+    //! Start value of the interval.
+    double origin_ = 0.0;
+    //! End value of the interval.
+    double end_ = 0.0;
+    //! The period of this dimension (= 0 if not periodic).
+    double period_ = 0.0;
+    //! The force constant in kJ/mol/nm^2, kJ/mol/rad^2
+    double forceConstant_ = 0.0;
+    //! Estimated diffusion constant in units of nm^2/ps or rad^2/ps or ps^-1.
+    double diffusion_ = 0.0;
+    //! The initial coordinate value.
+    double coordValueInit_ = 0.0;
+    //! The diameter that needs to be sampled around a point before it is considered covered.
+    double coverDiameter_ = 0.0;
+};
+
+class AwhBiasParams
+{
+public:
+    //! Constructor from input file.
+    AwhBiasParams(std::vector<t_inpfile>* inp, const std::string& prefix, warninp_t wi, bool bComment);
+    //! Constructor to generate from file reading.
+    explicit AwhBiasParams(ISerializer* serializer);
+
+    //! Move constructor.
+    AwhBiasParams(AwhBiasParams&&) = default;
+    //! Move assignment operator.
+    AwhBiasParams& operator=(AwhBiasParams&&) = default;
+    //! Delete copy constructor.
+    AwhBiasParams(const AwhBiasParams&) = delete;
+    //! Delete copy assignment.
+    AwhBiasParams& operator=(const AwhBiasParams&) = delete;
+
+    //! Which target distribution is searched.
+    AwhTargetType targetDistribution() const { return eTarget_; }
+    //! Beta scaling to reach target distribution.
+    double targetBetaScaling() const { return targetBetaScaling_; }
+    //! Cutoff for target.
+    double targetCutoff() const { return targetCutoff_; }
+    //! Which kind of growth to use.
+    AwhHistogramGrowthType growthType() const { return eGrowth_; }
+    //! User provided PMF estimate.
+    bool userPMFEstimate() const { return bUserData_; }
+    //! Estimated initial free energy error in kJ/mol.
+    double initialErrorEstimate() const { return errorInitial_; }
+    //! Dimensions of coordinate space.
+    int ndim() const { return dimParams_.size(); }
+    //! Number of groups to share this bias with.
+    int shareGroup() const { return shareGroup_; }
+    //! If the simulation starts with equilibrating histogram.
+    bool equilibrateHistogram() const { return equilibrateHistogram_; }
+    //! Access to dimension parameters.
+    ArrayRef<AwhDimParams> dimParams() { return dimParams_; }
+    //! Const access to dimension parameters.
+    ArrayRef<const AwhDimParams> dimParams() const { return dimParams_; }
+    //! Write datastructure.
+    void serialize(ISerializer* serializer);
+
+private:
+    //! AWH parameters per dimension.
+    std::vector<AwhDimParams> dimParams_;
+    //! Type of target distribution.
+    AwhTargetType eTarget_;
+    //! Beta scaling value for Boltzmann type target distributions.
+    double targetBetaScaling_;
+    //! Free energy cutoff value for cutoff type target distribution in kJ/mol.
+    double targetCutoff_;
+    //! How the biasing histogram grows.
+    AwhHistogramGrowthType eGrowth_;
+    //! Is there a user-defined initial PMF estimate and target estimate?
+    bool bUserData_;
+    //! Estimated initial free energy error in kJ/mol.
+    double errorInitial_;
+    //! When >0, the bias is shared with biases of the same group and across multiple simulations when shareBiasMultisim=true
+    int shareGroup_;
+    //! True if the simulation starts out by equilibrating the histogram.
+    bool equilibrateHistogram_;
+};
+/*! \internal
+ * \brief Structure holding parameter information for AWH.
+ */
+class AwhParams
+{
+public:
+    //! Constructor from input file.
+    AwhParams(std::vector<t_inpfile>* inp, warninp_t wi);
+    //! Constructor used to generate awh parameter from file reading.
+    explicit AwhParams(ISerializer* serializer);
+
+    //! Move constructor.
+    AwhParams(AwhParams&&) = default;
+    //! Move assignment operator.
+    AwhParams& operator=(AwhParams&&) = default;
+    //! Delete copy constructor.
+    AwhParams(const AwhParams&) = delete;
+    //! Delete copy assignment.
+    AwhParams& operator=(const AwhParams&) = delete;
+
+    //! Get number of biases.
+    int numBias() const { return awhBiasParams_.size(); }
+    //! Get access to bias parameters.
+    ArrayRef<AwhBiasParams> awhBiasParams() { return awhBiasParams_; }
+    //! Const access to bias parameters.
+    ArrayRef<const AwhBiasParams> awhBiasParams() const { return awhBiasParams_; }
+    //! What king of potential is being used. \todo should use actual enum class.
+    AwhPotentialType potential() const { return potentialEnum_; }
+    //! Seed used for starting AWH.
+    int64_t seed() const { return seed_; }
+    //! Output step interval.
+    int nstout() const { return nstOut_; }
+    //! Number of samples per coordinate sample.
+    int nstSampleCoord() const { return nstSampleCoord_; }
+    //! Number of samples per free energy update.
+    int numSamplesUpdateFreeEnergy() const { return numSamplesUpdateFreeEnergy_; }
+    //! If biases are shared in multisim.
+    bool shareBiasMultisim() const { return shareBiasMultisim_; }
+    //! Serialize awh parameters.
+    void serialize(ISerializer* serializer);
+
+private:
+    //! AWH bias parameters.
+    std::vector<AwhBiasParams> awhBiasParams_;
+    //! Random seed.
+    int64_t seed_;
+    //! Output step interval.
+    int nstOut_;
+    //! Number of samples per coordinate sample (also used for PMF)
+    int nstSampleCoord_;
+    //! Number of samples per free energy update.
+    int numSamplesUpdateFreeEnergy_;
+    //! Type of potential.
+    AwhPotentialType potentialEnum_;
+    //! Whether to share biases with shareGroup>0 between multi-simulations.
+    bool shareBiasMultisim_;
+};
+
+} // namespace gmx
+
+#endif /* GMX_MDTYPES_AWH_PARAMS_H */
diff --git a/src/include/gromacs/mdtypes/checkpointdata.h b/src/include/gromacs/mdtypes/checkpointdata.h
new file mode 100644 (file)
index 0000000..4d741a8
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Provides the checkpoint data structure for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MODULARSIMULATOR_CHECKPOINTDATA_H
+#define GMX_MODULARSIMULATOR_CHECKPOINTDATA_H
+
+#include <optional>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/keyvaluetreebuilder.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+class ISerializer;
+
+/*! \libinternal
+ * \brief The operations on CheckpointData
+ *
+ * This enum defines the two modes of operation on CheckpointData objects,
+ * reading and writing. This allows to template all access functions, which
+ * in turn enables clients to write a single function for read and write
+ * access, eliminating the risk of having read and write functions getting
+ * out of sync.
+ *
+ * \ingroup module_modularsimulator
+ */
+enum class CheckpointDataOperation
+{
+    Read,
+    Write,
+    Count
+};
+
+/*! \internal
+ * \brief Get an ArrayRef whose const-ness is defined by the checkpointing operation
+ *
+ * \tparam operation  Whether we are reading or writing
+ * \tparam T          The type of values stored in the ArrayRef
+ * \param container   The container the ArrayRef is referencing to
+ * \return            The ArrayRef
+ *
+ * \see ArrayRef
+ *
+ * \ingroup module_modularsimulator
+ */
+template<CheckpointDataOperation operation, typename T>
+ArrayRef<std::conditional_t<operation == CheckpointDataOperation::Write || std::is_const<T>::value, const typename T::value_type, typename T::value_type>>
+makeCheckpointArrayRef(T& container)
+{
+    return container;
+}
+
+/*! \internal
+ * \brief Get an ArrayRef to a C array whose const-ness is defined by the checkpointing operation
+ *
+ * \tparam operation  Whether we are reading or writing
+ * \tparam T          The type of values stored in the ArrayRef
+ * \param begin       Pointer to the beginning of array.
+ * \param size        Number of elements in array.
+ * \return            The ArrayRef
+ *
+ * \see ArrayRef
+ *
+ * \ingroup module_modularsimulator
+ */
+template<CheckpointDataOperation operation, typename T>
+ArrayRef<std::conditional_t<operation == CheckpointDataOperation::Write || std::is_const<T>::value, const T, T>>
+makeCheckpointArrayRefFromArray(T* begin, size_t size)
+{
+    return ArrayRef<T>(begin, begin + size);
+}
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Struct allowing to check if data is serializable through the KeyValueTree serializer
+ *
+ * This list of types is copied from ValueSerializer::initSerializers()
+ * Having this here allows us to catch errors at compile time
+ * instead of having cryptic runtime errors
+ */
+template<typename T>
+struct IsSerializableType
+{
+    static bool const value = std::is_same<T, std::string>::value || std::is_same<T, bool>::value
+                              || std::is_same<T, int>::value || std::is_same<T, int64_t>::value
+                              || std::is_same<T, float>::value || std::is_same<T, double>::value;
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Struct allowing to check if enum has a serializable underlying type
+ */
+//! {
+template<typename T, bool = std::is_enum<T>::value>
+struct IsSerializableEnum
+{
+    static bool const value = IsSerializableType<std::underlying_type_t<T>>::value;
+};
+template<typename T>
+struct IsSerializableEnum<T, false>
+{
+    static bool const value = false;
+};
+//! }
+
+/*! \libinternal
+ * \ingroup module_modularsimulator
+ * \brief Data type hiding checkpoint implementation details
+ *
+ * This data type allows to separate the implementation details of the
+ * checkpoint writing / reading from the implementation of the checkpoint
+ * clients. Checkpoint clients interface via the methods of the CheckpointData
+ * object, and do not need knowledge of data types used to store the data.
+ *
+ * Templating allows checkpoint clients to have symmetric (templated)
+ * implementations for checkpoint reading and writing.
+ *
+ * CheckpointData objects are dispatched via [Write|Read]CheckpointDataHolder
+ * objects, which interact with the checkpoint reading from / writing to
+ * file.
+ */
+
+template<CheckpointDataOperation operation>
+class CheckpointData;
+
+//! Convenience shortcut for reading checkpoint data.
+using ReadCheckpointData = CheckpointData<CheckpointDataOperation::Read>;
+//! Convenience shortcut for writing checkpoint data.
+using WriteCheckpointData = CheckpointData<CheckpointDataOperation::Write>;
+
+template<>
+class CheckpointData<CheckpointDataOperation::Read>
+{
+public:
+    /*! \brief Read or write a single value from / to checkpoint
+     *
+     * Allowed scalar types include std::string, bool, int, int64_t,
+     * float, double, or any enum with one of the previously mentioned
+     * scalar types as underlying type. Type compatibility is checked
+     * at compile time.
+     *
+     * \tparam operation  Whether we are reading or writing
+     * \tparam T          The type of the value
+     * \param key         The key to [read|write] the value [from|to]
+     * \param value       The value to [read|write]
+     */
+    //! {
+    template<typename T>
+    std::enable_if_t<IsSerializableType<T>::value, void> scalar(const std::string& key, T* value) const;
+    template<typename T>
+    std::enable_if_t<IsSerializableEnum<T>::value, void> enumScalar(const std::string& key, T* value) const;
+    //! }
+
+    /*! \brief Read or write an ArrayRef from / to checkpoint
+     *
+     * Allowed types stored in the ArrayRef include std::string, bool, int,
+     * int64_t, float, double, and gmx::RVec. Type compatibility is checked
+     * at compile time.
+     *
+     * \tparam operation  Whether we are reading or writing
+     * \tparam T          The type of values stored in the ArrayRef
+     * \param key         The key to [read|write] the ArrayRef [from|to]
+     * \param values      The ArrayRef to [read|write]
+     */
+    //! {
+    // Read ArrayRef of scalar
+    template<typename T>
+    std::enable_if_t<IsSerializableType<T>::value, void> arrayRef(const std::string& key,
+                                                                  ArrayRef<T>        values) const;
+    // Read ArrayRef of RVec
+    void arrayRef(const std::string& key, ArrayRef<RVec> values) const;
+    //! }
+
+    /*! \brief Read or write a tensor from / to checkpoint
+     *
+     * \tparam operation  Whether we are reading or writing
+     * \param key         The key to [read|write] the tensor [from|to]
+     * \param values      The tensor to [read|write]
+     */
+    void tensor(const std::string& key, ::tensor values) const;
+
+    /*! \brief Return a subset of the current CheckpointData
+     *
+     * \tparam operation  Whether we are reading or writing
+     * \param key         The key to [read|write] the sub data [from|to]
+     * \return            A CheckpointData object representing a subset of the current object
+     */
+    //!{
+    CheckpointData subCheckpointData(const std::string& key) const;
+    //!}
+
+private:
+    //! KV tree read from checkpoint
+    const KeyValueTreeObject* inputTree_ = nullptr;
+
+    //! Construct an input checkpoint data object
+    explicit CheckpointData(const KeyValueTreeObject& inputTree);
+
+    // Only holders should build
+    friend class ReadCheckpointDataHolder;
+};
+
+template<>
+class CheckpointData<CheckpointDataOperation::Write>
+{
+public:
+    //! \copydoc CheckpointData<CheckpointDataOperation::Read>::scalar
+    //! {
+    template<typename T>
+    std::enable_if_t<IsSerializableType<T>::value, void> scalar(const std::string& key, const T* value);
+    template<typename T>
+    std::enable_if_t<IsSerializableEnum<T>::value, void> enumScalar(const std::string& key, const T* value);
+    //! }
+
+    //! \copydoc CheckpointData<CheckpointDataOperation::Read>::arrayRef
+    //! {
+    // Write ArrayRef of scalar
+    template<typename T>
+    std::enable_if_t<IsSerializableType<T>::value, void> arrayRef(const std::string& key,
+                                                                  ArrayRef<const T>  values);
+    // Write ArrayRef of RVec
+    void arrayRef(const std::string& key, ArrayRef<const RVec> values);
+    //! }
+
+    //! \copydoc CheckpointData<CheckpointDataOperation::Read>::tensor
+    void tensor(const std::string& key, const ::tensor values);
+
+    //! \copydoc CheckpointData<CheckpointDataOperation::Read>::subCheckpointData
+    CheckpointData subCheckpointData(const std::string& key);
+
+private:
+    //! Builder for the tree to be written to checkpoint
+    std::optional<KeyValueTreeObjectBuilder> outputTreeBuilder_ = std::nullopt;
+
+    //! Construct an output checkpoint data object
+    explicit CheckpointData(KeyValueTreeObjectBuilder&& outputTreeBuilder);
+
+    // Only holders should build
+    friend class WriteCheckpointDataHolder;
+};
+
+/*! \brief Read a checkpoint version enum variable
+ *
+ * This reads the checkpoint version from file. The read version is returned.
+ *
+ * If the read version is more recent than the code version, this throws an error, since
+ * we cannot know what has changed in the meantime. Using newer checkpoint files with
+ * old code is not a functionality we can offer. Note, however, that since the checkpoint
+ * version is saved by module, older checkpoint files of all simulations that don't use
+ * that specific module can still be used.
+ *
+ * Allowing backwards compatibility of files (i.e., reading an older checkpoint file with
+ * a newer version of the code) is in the responsibility of the caller module. They can
+ * use the returned file checkpoint version to do that:
+ *
+ *     const auto fileVersion = checkpointVersion(checkpointData, "version", c_currentVersion);
+ *     if (fileVersion >= CheckpointVersion::AddedX)
+ *     {
+ *         checkpointData->scalar("x", &x_));
+ *     }
+ *
+ * @tparam VersionEnum     The type of the checkpoint version enum
+ * @param  checkpointData  A reading checkpoint data object
+ * @param  key             The key under which the version is saved - also used for error output
+ * @param  programVersion  The checkpoint version of the current code
+ * @return                 The checkpoint version read from file
+ */
+template<typename VersionEnum>
+VersionEnum checkpointVersion(const ReadCheckpointData* checkpointData,
+                              const std::string&        key,
+                              const VersionEnum         programVersion)
+{
+    VersionEnum fileVersion;
+    checkpointData->enumScalar(key, &fileVersion);
+    if (fileVersion > programVersion)
+    {
+        throw FileIOError(
+                formatString("The checkpoint file contains a %s that is more recent than the "
+                             "current program version and is not backward compatible.",
+                             key.c_str()));
+    }
+    return fileVersion;
+}
+
+/*! \brief Write the current code checkpoint version enum variable
+ *
+ * Write the current program checkpoint version to the checkpoint data object.
+ * Returns the written checkpoint version to mirror the signature of the reading version.
+ *
+ * @tparam VersionEnum     The type of the checkpoint version enum
+ * @param  checkpointData  A writing checkpoint data object
+ * @param  key             The key under which the version is saved
+ * @param  programVersion  The checkpoint version of the current code
+ * @return                 The checkpoint version written to file
+ */
+template<typename VersionEnum>
+VersionEnum checkpointVersion(WriteCheckpointData* checkpointData,
+                              const std::string&   key,
+                              const VersionEnum    programVersion)
+{
+    checkpointData->enumScalar(key, &programVersion);
+    return programVersion;
+}
+
+inline ReadCheckpointData::CheckpointData(const KeyValueTreeObject& inputTree) :
+    inputTree_(&inputTree)
+{
+}
+
+inline WriteCheckpointData::CheckpointData(KeyValueTreeObjectBuilder&& outputTreeBuilder) :
+    outputTreeBuilder_(outputTreeBuilder)
+{
+}
+/*! \libinternal
+ * \brief Holder for read checkpoint data
+ *
+ * A ReadCheckpointDataHolder object is passed to the checkpoint reading
+ * functionality, and then passed into the SimulatorBuilder object. It
+ * holds the KV-tree read from file and dispatches CheckpointData objects
+ * to the checkpoint clients.
+ */
+class ReadCheckpointDataHolder
+{
+public:
+    //! Check whether a key exists
+    [[nodiscard]] bool keyExists(const std::string& key) const;
+
+    //! Return vector of existing keys
+    [[nodiscard]] std::vector<std::string> keys() const;
+
+    //! Deserialize serializer content into the CheckpointData object
+    void deserialize(ISerializer* serializer);
+
+    /*! \brief Return a subset of the current CheckpointData
+     *
+     * \param key         The key to [read|write] the sub data [from|to]
+     * \return            A CheckpointData object representing a subset of the current object
+     */
+    [[nodiscard]] ReadCheckpointData checkpointData(const std::string& key) const;
+
+    //! Write the contents of the Checkpoint to file
+    void dump(FILE* out) const;
+
+private:
+    //! KV-tree read from checkpoint
+    KeyValueTreeObject checkpointTree_;
+};
+
+/*! \libinternal
+ * \brief Holder for write checkpoint data
+ *
+ * The WriteCheckpointDataHolder object holds the KV-tree builder and
+ * dispatches CheckpointData objects to the checkpoint clients to save
+ * their respective data. It is then passed to the checkpoint writing
+ * functionality.
+ */
+class WriteCheckpointDataHolder
+{
+public:
+    //! Serialize the content of the CheckpointData object
+    void serialize(ISerializer* serializer);
+
+    /*! \brief Return a subset of the current CheckpointData
+     *
+     * \param key         The key to [read|write] the sub data [from|to]
+     * \return            A CheckpointData object representing a subset of the current object
+     */
+    [[nodiscard]] WriteCheckpointData checkpointData(const std::string& key);
+
+    /*! \brief
+     */
+    [[nodiscard]] bool empty() const;
+
+private:
+    //! KV-tree builder
+    KeyValueTreeBuilder outputTreeBuilder_;
+    //! Whether any checkpoint data object has been requested
+    bool hasCheckpointDataBeenRequested_ = false;
+};
+
+// Function definitions - here to avoid template-related linker problems
+// doxygen doesn't like these...
+//! \cond
+template<typename T>
+std::enable_if_t<IsSerializableType<T>::value, void> ReadCheckpointData::scalar(const std::string& key,
+                                                                                T* value) const
+{
+    GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
+    *value = (*inputTree_)[key].cast<T>();
+}
+
+template<typename T>
+std::enable_if_t<IsSerializableEnum<T>::value, void> ReadCheckpointData::enumScalar(const std::string& key,
+                                                                                    T* value) const
+{
+    GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
+    std::underlying_type_t<T> castValue;
+    castValue = (*inputTree_)[key].cast<std::underlying_type_t<T>>();
+    *value    = static_cast<T>(castValue);
+}
+
+template<typename T>
+inline std::enable_if_t<IsSerializableType<T>::value, void>
+WriteCheckpointData::scalar(const std::string& key, const T* value)
+{
+    GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
+    outputTreeBuilder_->addValue(key, *value);
+}
+
+template<typename T>
+inline std::enable_if_t<IsSerializableEnum<T>::value, void>
+WriteCheckpointData::enumScalar(const std::string& key, const T* value)
+{
+    GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
+    auto castValue = static_cast<std::underlying_type_t<T>>(*value);
+    outputTreeBuilder_->addValue(key, castValue);
+}
+
+template<typename T>
+inline std::enable_if_t<IsSerializableType<T>::value, void>
+ReadCheckpointData::arrayRef(const std::string& key, ArrayRef<T> values) const
+{
+    GMX_RELEASE_ASSERT(inputTree_, "No input checkpoint data available.");
+    GMX_RELEASE_ASSERT(values.size() >= (*inputTree_)[key].asArray().values().size(),
+                       "Read vector does not fit in passed ArrayRef.");
+    auto outputIt  = values.begin();
+    auto inputIt   = (*inputTree_)[key].asArray().values().begin();
+    auto outputEnd = values.end();
+    auto inputEnd  = (*inputTree_)[key].asArray().values().end();
+    for (; outputIt != outputEnd && inputIt != inputEnd; outputIt++, inputIt++)
+    {
+        *outputIt = inputIt->cast<T>();
+    }
+}
+
+template<typename T>
+inline std::enable_if_t<IsSerializableType<T>::value, void>
+WriteCheckpointData::arrayRef(const std::string& key, ArrayRef<const T> values)
+{
+    GMX_RELEASE_ASSERT(outputTreeBuilder_, "No output checkpoint data available.");
+    auto builder = outputTreeBuilder_->addUniformArray<T>(key);
+    for (const auto& value : values)
+    {
+        builder.addValue(value);
+    }
+}
+
+inline void ReadCheckpointData::arrayRef(const std::string& key, ArrayRef<RVec> values) const
+{
+    GMX_RELEASE_ASSERT(values.size() >= (*inputTree_)[key].asArray().values().size(),
+                       "Read vector does not fit in passed ArrayRef.");
+    auto outputIt  = values.begin();
+    auto inputIt   = (*inputTree_)[key].asArray().values().begin();
+    auto outputEnd = values.end();
+    auto inputEnd  = (*inputTree_)[key].asArray().values().end();
+    for (; outputIt != outputEnd && inputIt != inputEnd; outputIt++, inputIt++)
+    {
+        auto storedRVec = inputIt->asObject()["RVec"].asArray().values();
+        *outputIt = { storedRVec[XX].cast<real>(), storedRVec[YY].cast<real>(), storedRVec[ZZ].cast<real>() };
+    }
+}
+
+inline void WriteCheckpointData::arrayRef(const std::string& key, ArrayRef<const RVec> values)
+{
+    auto builder = outputTreeBuilder_->addObjectArray(key);
+    for (const auto& value : values)
+    {
+        auto subbuilder = builder.addObject();
+        subbuilder.addUniformArray("RVec", { value[XX], value[YY], value[ZZ] });
+    }
+}
+
+inline void ReadCheckpointData::tensor(const std::string& key, ::tensor values) const
+{
+    auto array     = (*inputTree_)[key].asArray().values();
+    values[XX][XX] = array[0].cast<real>();
+    values[XX][YY] = array[1].cast<real>();
+    values[XX][ZZ] = array[2].cast<real>();
+    values[YY][XX] = array[3].cast<real>();
+    values[YY][YY] = array[4].cast<real>();
+    values[YY][ZZ] = array[5].cast<real>();
+    values[ZZ][XX] = array[6].cast<real>();
+    values[ZZ][YY] = array[7].cast<real>();
+    values[ZZ][ZZ] = array[8].cast<real>();
+}
+
+inline void WriteCheckpointData::tensor(const std::string& key, const ::tensor values)
+{
+    auto builder = outputTreeBuilder_->addUniformArray<real>(key);
+    builder.addValue(values[XX][XX]);
+    builder.addValue(values[XX][YY]);
+    builder.addValue(values[XX][ZZ]);
+    builder.addValue(values[YY][XX]);
+    builder.addValue(values[YY][YY]);
+    builder.addValue(values[YY][ZZ]);
+    builder.addValue(values[ZZ][XX]);
+    builder.addValue(values[ZZ][YY]);
+    builder.addValue(values[ZZ][ZZ]);
+}
+
+inline ReadCheckpointData ReadCheckpointData::subCheckpointData(const std::string& key) const
+{
+    return CheckpointData((*inputTree_)[key].asObject());
+}
+
+inline WriteCheckpointData WriteCheckpointData::subCheckpointData(const std::string& key)
+{
+    return CheckpointData(outputTreeBuilder_->addObject(key));
+}
+
+//! \endcond
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_CHECKPOINTDATA_H
diff --git a/src/include/gromacs/mdtypes/commrec.h b/src/include/gromacs/mdtypes/commrec.h
new file mode 100644 (file)
index 0000000..69ed6a2
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDTYPES_COMMREC_H
+#define GMX_MDTYPES_COMMREC_H
+
+#include <stddef.h>
+
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/gmxmpi.h"
+
+struct gmx_domdec_t;
+
+#define DUTY_PP (1U << 0U)
+#define DUTY_PME (1U << 1U)
+
+//! Whether the current DD role is master or slave
+enum class DDRole
+{
+    Master,
+    Agent
+};
+
+//! Whether one or more ranks are used
+enum class NumRanks
+{
+    Single,
+    Multiple
+};
+
+typedef struct
+{
+    int      bUse;
+    MPI_Comm comm_intra;
+    int      rank_intra;
+    MPI_Comm comm_inter;
+
+} gmx_nodecomm_t;
+
+struct t_commrec
+{
+    /* The nodeids in one sim are numbered sequentially from 0.
+     * All communication within some simulation should happen
+     * in mpi_comm_mysim, or its subset mpi_comm_mygroup.
+     */
+    //! The rank-id in mpi_comm_mysim;
+    int sim_nodeid;
+    //! The number of ranks in mpi_comm_mysim
+    int nnodes;
+    //! The number of separate PME ranks, 0 when no separate PME ranks are used
+    int npmenodes;
+
+    //! The rank-id in mpi_comm_mygroup;
+    int nodeid;
+
+    /* MPI communicators within a single simulation
+     * Note: other parts of the code may further subset these communicators.
+     */
+    MPI_Comm mpi_comm_mysim;   /* communicator including all ranks of
+                                  a single simulation */
+    MPI_Comm mpi_comm_mygroup; /* subset of mpi_comm_mysim including only
+                                  the ranks in the same group (PP or PME) */
+    //! The number of ranks in mpi_comm_mygroup
+    int sizeOfMyGroupCommunicator;
+
+    //! The communicator used before DD was initialized
+    MPI_Comm mpiDefaultCommunicator;
+    int      sizeOfDefaultCommunicator;
+    int      rankInDefaultCommunicator;
+
+    gmx_nodecomm_t nc;
+
+    /* For domain decomposition */
+    gmx_domdec_t* dd;
+
+    /* The duties of this node, see the DUTY_ defines above.
+     * This should be read through thisRankHasDuty() or getThisRankDuties().
+     */
+    int duty;
+};
+
+/*! \brief
+ * Returns the rank's duty, and asserts that it has been initialized.
+ */
+inline int getThisRankDuties(const t_commrec* cr)
+{
+    GMX_ASSERT(cr, "Invalid commrec pointer");
+    GMX_ASSERT(cr->duty != 0, "Commrec duty was not initialized!");
+    return cr->duty;
+}
+
+/*! \brief
+ * A convenience getter for the commrec duty assignment;
+ * asserts that duty is actually valid (have been initialized).
+ *
+ * \param[in] cr    Communication structure pointer
+ * \param[in] duty  A single duty's corresponding DUTY_ flag. Combinations are not supported.
+ *
+ * \returns Whether this duty is assigned to this rank.
+ */
+inline bool thisRankHasDuty(const t_commrec* cr, int duty)
+{
+    GMX_ASSERT((duty == DUTY_PME) || (duty == DUTY_PP), "Invalid duty type");
+    return (getThisRankDuties(cr) & duty) != 0;
+}
+
+/*! \brief True if this is a simulation with more than 1 rank
+ *
+ * In particular, this is true for multi-rank runs with TPI and NM, because
+ * they use a decomposition that is not the domain decomposition used by
+ * other simulation types. */
+#define PAR(cr) ((cr)->sizeOfDefaultCommunicator > 1)
+
+//! True of this is the master node
+#define MASTER(cr) (((cr)->rankInDefaultCommunicator == 0) || !PAR(cr))
+
+// Note that currently, master is always PP master, so this is equivalent to MASTER(cr)
+//! True if this is the particle-particle master
+#define SIMMASTER(cr) ((MASTER(cr) && thisRankHasDuty((cr), DUTY_PP)) || !PAR(cr))
+
+//! The node id for this rank
+#define RANK(cr, nodeid) (nodeid)
+
+//! The node id for the master
+#define MASTERRANK(cr) (0)
+
+/*! \brief Returns whether the domain decomposition machinery is active and reorders atoms
+ *
+ * This tells whether atoms are reordered at pair search steps. When the return value
+ * is true, atoms are not in the order of the input and mtop.
+ *
+ * Note that when the return value is true, there are not necessarily
+ * multiple domains. The domain decomposition machinery is also active and
+ * reorders the atoms also with a single MPI rank, or 1 PP and 1 PME rank,
+ * with most integrators. Only a few special non-integrator "integrators"
+ * do not (yet) support the domain decomposition machinery and therefore
+ * this function is still needed.
+ */
+static bool inline haveDDAtomOrdering(const t_commrec& cr)
+{
+    return cr.dd != nullptr;
+}
+
+/*! \brief Returns whether we have actual domain decomposition for the particle-particle interactions
+ *
+ * Will return false when we use 1 rank for PP and 1 for PME
+ */
+static bool inline havePPDomainDecomposition(const t_commrec* cr)
+{
+    /* NOTE: It would be better to use cr->dd->nnodes, but we do not want
+     *       to pull in a dependency on domdec.h into this file.
+     */
+    GMX_ASSERT(cr != nullptr, "Invalid call of havePPDomainDecomposition before commrec is made");
+    GMX_ASSERT(cr->npmenodes >= 0,
+               "Invalid call of havePPDomainDecomposition before MPMD automated decomposition was "
+               "chosen.");
+    return (cr->dd != nullptr && cr->nnodes - cr->npmenodes > 1);
+}
+
+#endif
diff --git a/src/include/gromacs/mdtypes/df_history.h b/src/include/gromacs/mdtypes/df_history.h
new file mode 100644 (file)
index 0000000..abcdd0a
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_GMXLIB_DF_HISTORY_H
+#define GMX_GMXLIB_DF_HISTORY_H
+
+struct df_history_t;
+
+void init_df_history(df_history_t* dfhist, int nlambda);
+
+void done_df_history(df_history_t* dfhist);
+
+void copy_df_history(df_history_t* df_dest, df_history_t* df_source);
+
+#endif
diff --git a/src/include/gromacs/mdtypes/edsamhistory.h b/src/include/gromacs/mdtypes/edsamhistory.h
new file mode 100644 (file)
index 0000000..6500061
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * This file contains data types containing essential dynamics and
+ * flooding data to be stored in the checkpoint file.
+ */
+
+#ifndef GMX_MDLIB_EDSAMHISTORY_H
+#define GMX_MDLIB_EDSAMHISTORY_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+/* Helper structure to be able to make essential dynamics / flooding group(s) whole
+ *
+ * If one uses essential dynamics or flooding on a group of atoms from
+ * more than one molecule, we cannot make this group whole with
+ * do_pbc_first_mtop(). We assume that the ED group has the correct PBC
+ * representation at the beginning of the simulation and keep track
+ * of the shifts to always get it into that representation.
+ * For proper restarts from a checkpoint we store the positions of the
+ * reference group at the time of checkpoint writing.
+ */
+typedef struct edsamhistory_t
+{
+    gmx_bool bFromCpt; // Did we start from a checkpoint file?
+    int      nED;      // No. of ED/Flooding data sets, if <1 no ED
+    int*     nref;     // No. of atoms in i'th reference structure
+    int*     nav;      // Same for average structure
+    rvec**   old_sref; // Positions of the reference atoms at the last time step (with correct PBC representation)
+    rvec**   old_sref_p; // Pointer to these positions
+    rvec**   old_sav;    // Same for the average positions
+    rvec**   old_sav_p;  // Pointer to these positions
+} edsamhistory_t;
+
+#endif
diff --git a/src/include/gromacs/mdtypes/enerdata.h b/src/include/gromacs/mdtypes/enerdata.h
new file mode 100644 (file)
index 0000000..c24a30b
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDTYPES_TYPES_ENERDATA_H
+#define GMX_MDTYPES_TYPES_ENERDATA_H
+
+#include <array>
+#include <utility>
+#include <vector>
+
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+struct t_commrec;
+struct t_lambda;
+
+// The non-bonded energy terms accumulated for energy group pairs
+enum class NonBondedEnergyTerms : int
+{
+    CoulombSR,
+    LJSR,
+    BuckinghamSR,
+    Coulomb14,
+    LJ14,
+    Count
+};
+
+// Struct for accumulating non-bonded energies between energy group pairs
+struct gmx_grppairener_t
+{
+    gmx_grppairener_t(int numEnergyGroups) : nener(numEnergyGroups * numEnergyGroups)
+    {
+        for (auto& term : energyGroupPairTerms)
+        {
+            term.resize(nener);
+        }
+    }
+
+    void clear();
+
+    int nener; /* The number of energy group pairs */
+    gmx::EnumerationArray<NonBondedEnergyTerms, std::vector<real>> energyGroupPairTerms; /* Energy terms for each pair of groups */
+};
+
+//! Accumulates free-energy foreign lambda energies and dH/dlamba
+class ForeignLambdaTerms
+{
+public:
+    /*! \brief Constructor
+     *
+     * \param[in] numLambdas  The number of foreign lambda values
+     */
+    ForeignLambdaTerms(int numLambdas);
+
+    //! Returns the number of foreign lambda values
+    int numLambdas() const { return numLambdas_; }
+
+    //! Returns the H(lambdaIndex) - H(lambda_current)
+    double deltaH(int lambdaIndex) const { return energies_[1 + lambdaIndex] - energies_[0]; }
+
+    /*! \brief Returns a list of partial energies, the part which depends on lambda),
+     * current lambda in entry 0, foreign lambda i in entry 1+i
+     *
+     * Note: the potential terms needs to be finalized before calling this method.
+     */
+    gmx::ArrayRef<double> energies()
+    {
+        GMX_ASSERT(finalizedPotentialContributions_, "Should be finalized");
+        return energies_;
+    }
+
+    /*! \brief Returns a list of partial energies, the part which depends on lambda),
+     * current lambda in entry 0, foreign lambda i in entry 1+i
+     *
+     * Note: the potential terms needs to be finalized before calling this method.
+     */
+    gmx::ArrayRef<const double> energies() const
+    {
+        GMX_ASSERT(finalizedPotentialContributions_, "Should be finalized");
+        return energies_;
+    }
+
+    /*! \brief Adds an energy and dV/dl constribution to lambda list index \p listIndex
+     *
+     * This should only be used for terms with non-linear dependence on lambda
+     * The value passed as listIndex should be 0 for the current lambda
+     * and 1+i for foreign lambda index i.
+     */
+    void accumulate(int listIndex, double energy, double dvdl)
+    {
+        GMX_ASSERT(!finalizedPotentialContributions_,
+                   "Can only accumulate with an unfinalized object");
+
+        energies_[listIndex] += energy;
+        dhdl_[listIndex] += dvdl;
+    }
+
+    /*! \brief Finalizes the potential (non-kinetic) terms
+     *
+     * Note: This can be called multiple times during the same force calculations
+     * without affecting the results.
+     *
+     * \param[in] dvdlLinear  List of dV/dlambda contributions of size efptNR with depend linearly on lambda
+     * \param[in] lambda      Lambda values for the efptNR contribution types
+     * \param[in] fepvals     Free-energy parameters
+     */
+    void finalizePotentialContributions(gmx::ArrayRef<const double> dvdlLinear,
+                                        gmx::ArrayRef<const real>   lambda,
+                                        const t_lambda&             fepvals);
+
+    /*! \brief Accumulates the kinetic and constraint free-energy contributions
+     *
+     * \param[in] energyTerms  List of energy terms, pass \p term in \p gmx_enerdata_t
+     * \param[in] dhdlMass     The mass dependent contribution to dH/dlambda
+     * \param[in] lambda       Lambda values for the efptNR contribution types
+     * \param[in] fepvals      Free-energy parameters
+     */
+    void finalizeKineticContributions(gmx::ArrayRef<const real> energyTerms,
+                                      double                    dhdlMass,
+                                      gmx::ArrayRef<const real> lambda,
+                                      const t_lambda&           fepvals);
+
+    /*! \brief Returns a pair of lists of deltaH and dH/dlambda
+     *
+     * Both lists are of size numLambdas() and are indexed with the lambda index.
+     *
+     * Note: should only be called after the object has been finalized by a call to
+     * accumulateLinearPotentialComponents() (is asserted).
+     *
+     * \param[in] cr  Communication record, used to reduce the terms when !=nullptr
+     */
+    std::pair<std::vector<double>, std::vector<double>> getTerms(const t_commrec* cr) const;
+
+    //! Sets all terms to 0
+    void zeroAllTerms();
+
+private:
+    //! As accumulate(), but for kinetic contributions
+    void accumulateKinetic(int listIndex, double energy, double dhdl);
+
+    //! Add a dH/dl contribution that does not depend on lambda to all foreign dH/dl terms
+    void addConstantDhdl(double dhdl);
+
+    //! The number of foreign lambdas
+    int numLambdas_;
+    //! Storage for foreign lambda energies
+    std::vector<double> energies_;
+    //! Storage for foreign lambda dH/dlambda
+    std::vector<double> dhdl_;
+    //! Tells whether all potential energy contributions have been accumulated
+    bool finalizedPotentialContributions_ = false;
+};
+
+//! Struct for accumulating all potential energy terms and some kinetic energy terms
+struct gmx_enerdata_t
+{
+    /*! \brief
+     * Constructor with specific number of energy groups and lambdas.
+     *
+     * \param[in] numEnergyGroups Number of energy groups used.
+     * \param[in] numFepLambdas   Number of free energy lambdas, zero if none.
+     */
+    gmx_enerdata_t(int numEnergyGroups, int numFepLambdas);
+
+    //! The energies for all different interaction types
+    std::array<real, F_NRE> term = { 0 };
+    //! Energy group pair non-bonded energies
+    struct gmx_grppairener_t grpp;
+    //! Contributions to dV/dlambda with linear dependence on lambda
+    gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, double> dvdl_lin = { 0 };
+    //! Contributions to dV/dlambda with non-linear dependence on lambda
+    gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, double> dvdl_nonlin = { 0 };
+    /* The idea is that dvdl terms with linear lambda dependence will be added
+     * automatically to enerpart_lambda. Terms with non-linear lambda dependence
+     * should explicitly determine the energies at foreign lambda points
+     * when n_lambda > 0. */
+
+    //! Foreign lambda energies and dH/dl
+    ForeignLambdaTerms foreignLambdaTerms;
+};
+
+#endif
diff --git a/src/include/gromacs/mdtypes/energyhistory.h b/src/include/gromacs/mdtypes/energyhistory.h
new file mode 100644 (file)
index 0000000..d86ddd9
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ *
+ * \brief
+ * This file contains datatypes for energy statistics history.
+ *
+ * \author Berk Hess
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDLIB_ENERGYHISTORY_H
+#define GMX_MDLIB_ENERGYHISTORY_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/utility/real.h"
+
+//! \cond INTERNAL
+
+namespace gmx
+{
+enum class CheckpointDataOperation;
+template<CheckpointDataOperation operation>
+class CheckpointData;
+} // namespace gmx
+
+//! \brief Energy history for delta_h histograms in between energy file frames
+class delta_h_history_t
+{
+public:
+    //! Vector (size number of intermediate data points) of vector of Hamiltonian differences for each foreign lambda
+    std::vector<std::vector<real>> dh;
+    //! The start time of these energy diff blocks
+    double start_time;
+    //! Lambda at start time
+    double start_lambda;
+    //! Whether the lambda value is set. Here for backward-compatibility.
+    bool start_lambda_set;
+
+    //! Read / write data from / to checkpoint object
+    template<gmx::CheckpointDataOperation operation>
+    void doCheckpoint(gmx::CheckpointData<operation> checkpointData);
+
+    delta_h_history_t() : start_time(0), start_lambda(0), start_lambda_set(false) {}
+};
+
+
+//! \brief Energy statistics history, only used for output and reporting
+class energyhistory_t
+{
+public:
+    int64_t             nsteps;       //! The number of steps in the history
+    int64_t             nsum;         //! Nr. of steps in the ener_ave and ener_sum
+    std::vector<double> ener_ave;     //! Energy terms difference^2 sum to get fluctuations
+    std::vector<double> ener_sum;     //! Energy terms sum
+    int64_t             nsteps_sim;   //! The number of steps in ener_sum_sim
+    int64_t             nsum_sim;     //! The number of frames in ener_sum_sim
+    std::vector<double> ener_sum_sim; //! Energy term history sum of the whole sim
+
+    //! History for energy difference for foreign lambdas (useful for BAR)
+    std::unique_ptr<delta_h_history_t> deltaHForeignLambdas;
+
+    //! Read / write data from / to checkpoint object
+    template<gmx::CheckpointDataOperation operation>
+    void doCheckpoint(gmx::CheckpointData<operation> checkpointData);
+
+    energyhistory_t() : nsteps(0), nsum(0), nsteps_sim(0), nsum_sim(0), ener_sum_sim(0) {}
+};
+
+//! \endcond
+
+#endif
diff --git a/src/include/gromacs/mdtypes/fcdata.h b/src/include/gromacs/mdtypes/fcdata.h
new file mode 100644 (file)
index 0000000..3f44cb1
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDTYPES_FCDATA_H
+#define GMX_MDTYPES_FCDATA_H
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <vector>
+
+#include "gromacs/domdec/localatomset.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/real.h"
+
+enum class DistanceRestraintWeighting : int;
+class gmx_ga2la_t;
+struct gmx_mtop_t;
+struct gmx_multisim_t;
+struct t_inputrec;
+class t_state;
+
+namespace gmx
+{
+class LocalAtomSetManager;
+}
+
+typedef real rvec5[5];
+
+/* Distance restraining stuff */
+typedef struct t_disresdata
+{
+    DistanceRestraintWeighting dr_weighting; /* Weighting of pairs in one restraint              */
+    bool                       dr_bMixed;    /* Use sqrt of the instantaneous times              *
+                                              * the time averaged violation                      */
+    real dr_fc;                              /* Force constant for disres,                       *
+                                              * which is multiplied by a (possibly)              *
+                                              * different factor for each restraint              */
+    real  dr_tau;                            /* Time constant for disres                         */
+    real  ETerm;                             /* multiplication factor for time averaging         */
+    real  ETerm1;                            /* 1 - ETerm1                                       */
+    real  exp_min_t_tau;                     /* Factor for slowly switching on the force         */
+    int   nres;                              /* The number of distance restraints                */
+    int   npair;                             /* The number of distance restraint pairs           */
+    int   type_min;                          /* The minimum iparam type index for restraints     */
+    real  sumviol;                           /* The sum of violations                            */
+    real* rt;                                /* The instantaneous distance (npair)               */
+    real* rm3tav;                            /* The time averaged distance (npair)               */
+    real* Rtl_6;                             /* The instantaneous r^-6 (nres)                    */
+    real* Rt_6;                              /* The instantaneous ensemble averaged r^-6 (nres)  */
+    real* Rtav_6;                            /* The time and ensemble averaged r^-6 (nres)       */
+    int   nsystems;                          /* The number of systems for ensemble averaging     */
+
+    /* TODO: Implement a proper solution for parallel disre indexing */
+    const t_iatom* forceatomsStart; /* Pointer to the start of the disre forceatoms */
+} t_disresdata;
+
+/* All coefficients for the matrix equation for the orientation tensor */
+struct OriresMatEq
+{
+    real rhs[5];    /* The right hand side of the matrix equation */
+    real mat[5][5]; /* The matrix                                 */
+};
+
+//! \brief Orientation restraining stuff
+struct t_oriresdata
+{
+    /*! \brief Constructor
+     *
+     * \param[in] fplog  Log file, can be nullptr
+     * \param[in] mtop   The global topology
+     * \param[in] ir     The input record
+     * \param[in] ms     The multisim communicator, pass nullptr to avoid ensemble averaging
+     * \param[in] globalState  The global state, references are set to members
+     * \param[in,out] localAtomSetManager  The local atom set manager
+     *
+     * \throws InvalidInputError when there is domain decomposition, fewer than 5 restraints,
+     *         periodic molecules or more than 1 molecule for a moleculetype with restraints.
+     */
+    t_oriresdata(FILE*                     fplog,
+                 const gmx_mtop_t&         mtop,
+                 const t_inputrec&         ir,
+                 const gmx_multisim_t*     ms,
+                 t_state*                  globalState,
+                 gmx::LocalAtomSetManager* localAtomSetManager);
+
+    //! Destructor
+    ~t_oriresdata();
+
+    //! Returns the local atom set for fitting
+    const gmx::LocalAtomSet& fitLocalAtomSet() const { return fitLocalAtomSet_; }
+
+    //! Returns the list of reference coordinates
+    gmx::ArrayRef<const gmx::RVec> referenceCoordinates() const { return referenceCoordinates_; }
+
+    //! Returns the list of masses for fitting
+    gmx::ArrayRef<const real> fitMasses() const { return fitMasses_; }
+
+    //! Returns the list of local atoms for fitting, matching the order of referenceCoordinates
+    gmx::ArrayRef<const int> fitLocalAtomIndices() const { return fitLocalAtomIndices_; }
+
+    //! Returns the list of coordinates for temporary use, size matches referenceCoordinates
+    gmx::ArrayRef<gmx::RVec> xTmp() { return xTmp_; }
+
+    //! Returns the factor for initializing the time averaging
+    real timeAveragingInitFactor() const { return *timeAveragingInitFactor_; }
+
+    //! Returns a const view on the time averaged D tensor history
+    gmx::ArrayRef<const real> DTensorsTimeAveragedHistory() const
+    {
+        return DTensorsTimeAveragedHistory_;
+    }
+
+    //! Updates the history with the current values
+    void updateHistory();
+
+    //! Force constant for the restraints
+    real fc;
+    //! Multiplication factor for time averaging
+    real edt;
+    //! 1 - edt
+    real edt_1;
+    //! Factor for slowly switching on the force
+    real exp_min_t_tau;
+    //! The number of orientation restraints
+    const int numRestraints;
+    //! The number of experiments
+    int numExperiments;
+    //! The minimum iparam type index for restraints
+    int typeMin;
+
+private:
+    //! List of local atom corresponding to the fit group
+    gmx::LocalAtomSet fitLocalAtomSet_;
+    //! The reference coordinates for the fit
+    std::vector<gmx::RVec> referenceCoordinates_;
+    //! The masses for fitting
+    std::vector<real> fitMasses_;
+    //! List of reference atoms for fitting
+    std::vector<int> fitLocalAtomIndices_;
+    //! Temporary array, used for fitting
+    std::vector<gmx::RVec> xTmp_;
+    //! The factor for initializing the time averaging, only present when time averaging is used
+    //! This references the value stored in the global state, which depends on time.
+    std::optional<std::reference_wrapper<real>> timeAveragingInitFactor_;
+    //! View on the time averaged history of the orientation tensors
+    gmx::ArrayRef<real> DTensorsTimeAveragedHistory_;
+
+public:
+    //! Rotation matrix to rotate to the reference coordinates
+    matrix rotationMatrix;
+    //! Array of order tensors, one for each experiment
+    tensor* orderTensors = nullptr;
+    //! The order tensor D for all restraints
+    rvec5* DTensors = nullptr;
+    //! The ensemble averaged D for all restraints
+    rvec5* DTensorsEnsembleAv = nullptr;
+    //! The time and ensemble averaged D restraints
+    rvec5* DTensorsTimeAndEnsembleAv = nullptr;
+    //! The calculated instantaneous orientations
+    std::vector<real> orientations;
+    //! The calculated emsemble averaged orientations
+    gmx::ArrayRef<real> orientationsEnsembleAv;
+    //! Buffer for the calculated emsemble averaged orientations, only used with ensemble averaging
+    std::vector<real> orientationsEnsembleAvBuffer;
+    //! The calculated time and ensemble averaged orientations
+    gmx::ArrayRef<real> orientationsTimeAndEnsembleAv;
+    //! The weighted (using kfac) RMS deviation
+    std::vector<real> orientationsTimeAndEnsembleAvBuffer;
+    //! Buffer for the weighted (using kfac) RMS deviation, only used with time averaging
+    real rmsdev;
+    //! An temporary array of matrix + rhs
+    std::vector<OriresMatEq> tmpEq;
+    //! The number of eigenvalues + eigenvectors per experiment
+    static constexpr int c_numEigenRealsPerExperiment = 12;
+    //! Eigenvalues/vectors, for output only (numExperiments x 12)
+    std::vector<real> eigenOutput;
+
+    // variables for diagonalization with diagonalize_orires_tensors()
+    //! Tensor to diagonalize
+    std::array<gmx::DVec, DIM> M;
+    //! Eigenvalues
+    std::array<double, DIM> eig_diag;
+    //! Eigenvectors
+    std::array<gmx::DVec, DIM> v;
+
+    // Default copy and assign would be incorrect and manual versions are not yet implemented.
+    GMX_DISALLOW_COPY_AND_ASSIGN(t_oriresdata);
+};
+
+/* Cubic spline table for tabulated bonded interactions */
+struct bondedtable_t
+{
+    int               n;     /* n+1 is the number of points */
+    real              scale; /* distance between two points */
+    std::vector<real> data;  /* the actual table data, per point there are 4 numbers */
+};
+
+/*
+ * Data struct used in the force calculation routines
+ * for storing the tables for bonded interactions and
+ * for storing information which is needed in following steps
+ * (for instance for time averaging in distance retraints)
+ * or for storing output, since force routines only return the potential.
+ */
+struct t_fcdata
+{
+    std::vector<bondedtable_t> bondtab;
+    std::vector<bondedtable_t> angletab;
+    std::vector<bondedtable_t> dihtab;
+
+    // TODO: Convert to C++ and unique_ptr (currently this data is not freed)
+    t_disresdata*                 disres = nullptr;
+    std::unique_ptr<t_oriresdata> orires;
+};
+
+#endif
diff --git a/src/include/gromacs/mdtypes/forcebuffers.h b/src/include/gromacs/mdtypes/forcebuffers.h
new file mode 100644 (file)
index 0000000..86d52d5
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * This file contains the definition of a container for force buffers.
+ *
+ * \author Berk Hess
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDTYPES_FORCEBUFFERS_H
+#define GMX_MDTYPES_FORCEBUFFERS_H
+
+#include <memory>
+
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/vectypes.h"
+
+namespace gmx
+{
+template<typename T>
+class ArrayRef;
+template<typename T>
+class ArrayRefWithPadding;
+
+enum class PinningPolicy : int;
+
+/*! \libinternal \brief A view of the force buffer
+ *
+ * This is used for read and/or write access to the force buffer.
+ */
+class ForceBuffersView
+{
+public:
+    //! Constructor, creates a view to \p force
+    ForceBuffersView(const ArrayRefWithPadding<RVec>& force,
+                     const ArrayRefWithPadding<RVec>& forceMtsCombined,
+                     const bool                       useForceMtsCombined) :
+        force_(force), forceMtsCombined_(forceMtsCombined), useForceMtsCombined_(useForceMtsCombined)
+    {
+    }
+
+    //! Copy constructor deleted to avoid creating non-const from const
+    ForceBuffersView(const ForceBuffersView& o) = delete;
+
+    //! Move constructor deleted, but could be implemented
+    ForceBuffersView(ForceBuffersView&& o) = delete;
+
+    ~ForceBuffersView() = default;
+
+    //! Copy assignment deleted to avoid creating non-const from const
+    ForceBuffersView& operator=(const ForceBuffersView& v) = delete;
+
+    //! Move assignment, moves the view
+    ForceBuffersView& operator=(ForceBuffersView&& v) = default;
+
+    //! Returns a const arrayref to the force buffer without padding
+    ArrayRef<const RVec> force() const { return force_.unpaddedConstArrayRef(); }
+
+    //! Returns an arrayref to the force buffer without padding
+    ArrayRef<RVec> force() { return force_.unpaddedArrayRef(); }
+
+    //! Returns an ArrayRefWithPadding to the force buffer
+    ArrayRefWithPadding<RVec> forceWithPadding() { return force_; }
+
+    //! Returns a const arrayref to the MTS force buffer without padding
+    ArrayRef<const RVec> forceMtsCombined() const
+    {
+        GMX_ASSERT(useForceMtsCombined_, "Need the MTS buffer");
+        return forceMtsCombined_.unpaddedConstArrayRef();
+    }
+
+    //! Returns an arrayref to the MTS force buffer without padding
+    ArrayRef<RVec> forceMtsCombined()
+    {
+        GMX_ASSERT(useForceMtsCombined_, "Need the MTS buffer");
+        return forceMtsCombined_.unpaddedArrayRef();
+    }
+
+    //! Returns an ArrayRefWithPadding to the MTS force buffer
+    ArrayRefWithPadding<RVec> forceMtsCombinedWithPadding()
+    {
+        GMX_ASSERT(useForceMtsCombined_, "Need the MTS buffer");
+        return forceMtsCombined_;
+    }
+
+private:
+    //! The force buffer
+    ArrayRefWithPadding<RVec> force_;
+    //! The force buffer for combined fast and slow forces with MTS
+    ArrayRefWithPadding<RVec> forceMtsCombined_;
+    // GCC 9 complains about unused attribute "unused" as it never warns about unused members,
+    // while clang requires it to avoid -Wunused
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wattributes"
+    //! Whether we use forceMtsCombined_
+    gmx_used_in_debug bool useForceMtsCombined_;
+#pragma GCC diagnostic pop
+};
+
+/*! \libinternal \brief Object that holds the force buffers
+ *
+ * Contains a normal force buffer and optionally a force buffer for combined fast and slow
+ * forces for use with multiple time stepping.
+ * More buffers can be added when needed. Those should also be added
+ * to ForceBuffersView.
+ * The force buffer (not forceMtsCombined) can be pinned for efficient transfer to/from GPUs.
+ * All access happens through the ForceBuffersView object.
+ */
+class ForceBuffers
+{
+public:
+    //! Constructor, creates an empty force buffer with pinning not active and no MTS force buffer
+    ForceBuffers();
+
+    /*! \brief Constructor, with options for using the MTS force buffer and the pinning policy
+     *
+     * \param[in] useForceMtsCombined  Whether to enable use of the forceMtsCombined buffer
+     * \param[in] pinningPolicy        The pinning policy for the force (not MTS) buffer
+     */
+    ForceBuffers(bool useForceMtsCombined, PinningPolicy pinningPolicy);
+
+    //! Copy constructor deleted, but could be implemented
+    ForceBuffers(const ForceBuffers& o) = delete;
+
+    //! Move constructor deleted, but could be implemented
+    ForceBuffers(ForceBuffers&& o) = delete;
+
+    ~ForceBuffers();
+
+    //! Copy assignment operator, sets the pinning policy to CannotBePinned
+    ForceBuffers& operator=(ForceBuffers const& o);
+
+    //! Move assignment operator, deleted but could be implemented
+    ForceBuffers& operator=(ForceBuffers&& o) = delete;
+
+    //! Returns a const view to the force buffers
+    const ForceBuffersView& view() const { return view_; }
+
+    //! Returns a view to the force buffer
+    ForceBuffersView& view() { return view_; }
+
+    //! Resize the force buffer
+    void resize(int numAtoms);
+
+    /*! \brief Returns the active pinning policy.
+     *
+     * Does not throw.
+     */
+    PinningPolicy pinningPolicy() const;
+
+private:
+    //! The force buffer
+    PaddedHostVector<RVec> force_;
+    //! Force buffer with combined fast and slow forces for use with multiple time stepping
+    PaddedHostVector<RVec> forceMtsCombined_;
+    //! The view to the force buffer
+    ForceBuffersView view_;
+    //! Whether we use forceMtsCombined_
+    bool useForceMtsCombined_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdtypes/forceoutput.h b/src/include/gromacs/mdtypes/forceoutput.h
new file mode 100644 (file)
index 0000000..5230022
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * This file contains the definition of a container for force and virial
+ * output.
+ *
+ * Currently the only container defined here is one used in algorithms
+ * that provide their own virial tensor contribution.
+ * We can consider adding another containter for forces and shift forces.
+ *
+ * \author Berk Hess
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDTYPES_FORCEOUTPUT_H
+#define GMX_MDTYPES_FORCEOUTPUT_H
+
+#include "gromacs/math/arrayrefwithpadding.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief Container for force and virial for algorithms that compute shift forces for virial calculation
+ *
+ * This force output class should be used when computing forces whos virial contribution
+ * is computed using the "single sum virial" algorithm (see the reference manual for
+ * details). To handle the virial contributions of forces working between periodic
+ * images correctly, so-called "shift forces" need to be accumulated for the different
+ * periodic images.
+ */
+class ForceWithShiftForces
+{
+public:
+    /*! \brief Constructor
+     *
+     * \param[in] force          A force buffer that will be used for storing forces
+     * \param[in] computeVirial  True when algorithms are required to provide their virial contribution (for the current force evaluation)
+     * \param[in] shiftForces    A shift forces buffer of size c_numShiftVectors, only used with \p computeVirial = true
+     */
+    ForceWithShiftForces(const gmx::ArrayRefWithPadding<gmx::RVec>& force,
+                         const bool                                 computeVirial,
+                         const gmx::ArrayRef<gmx::RVec>&            shiftForces) :
+        force_(force),
+        computeVirial_(computeVirial),
+        shiftForces_(computeVirial ? shiftForces : gmx::ArrayRef<gmx::RVec>())
+    {
+        GMX_ASSERT(!computeVirial || !shiftForces.empty(),
+                   "We need a valid shift force buffer when computing the virial");
+    }
+
+    //! Returns an arrayref to the force buffer without padding
+    gmx::ArrayRef<gmx::RVec> force() { return force_.unpaddedArrayRef(); }
+
+    //! Returns a const arrayref to the force buffer without padding
+    gmx::ArrayRef<const gmx::RVec> force() const { return force_.unpaddedConstArrayRef(); }
+
+    //! Returns whether the virial needs to be computed
+    bool computeVirial() const { return computeVirial_; }
+
+    //! Returns the shift forces buffer
+    gmx::ArrayRef<gmx::RVec> shiftForces() { return shiftForces_; }
+
+    //! Returns a const shift forces buffer
+    gmx::ArrayRef<const gmx::RVec> shiftForces() const { return shiftForces_; }
+
+    //! Returns a reference to the boolean which tells whether we have spread forces on vsites
+    bool& haveSpreadVsiteForces() { return haveSpreadVsiteForces_; }
+
+private:
+    //! The force buffer
+    gmx::ArrayRefWithPadding<gmx::RVec> force_;
+    //! True when virial computation is requested
+    bool computeVirial_;
+    //! A buffer for storing the shift forces, size c_numShiftVectors
+    gmx::ArrayRef<gmx::RVec> shiftForces_;
+    //! Tells whether we have spread the vsite forces
+    bool haveSpreadVsiteForces_ = false;
+};
+
+/*! \libinternal \brief Container for force and virial for algorithms that provide their own virial tensor contribution
+ *
+ * \note The \p force_ data member is a reference to an external force buffer.
+ */
+class ForceWithVirial
+{
+public:
+    /*! \brief Constructor
+     *
+     * \param[in] force          A force buffer that will be used for storing forces
+     * \param[in] computeVirial  True when algorithms are required to provide their virial contribution (for the current force evaluation)
+     */
+    ForceWithVirial(const ArrayRef<RVec>& force, const bool computeVirial) :
+        force_(force), computeVirial_(computeVirial)
+    {
+        for (int dim1 = 0; dim1 < DIM; dim1++)
+        {
+            for (int dim2 = 0; dim2 < DIM; dim2++)
+            {
+                virial_[dim1][dim2] = 0;
+            }
+        }
+    }
+
+    /*! \brief Adds a virial contribution
+     *
+     * \note Can be called with \p computeVirial=false.
+     * \note It is recommended to accumulate the virial contributions
+     *       of a module internally before calling this method, as that
+     *       will reduce rounding errors.
+     *
+     * \param[in] virial  The virial contribution to add
+     */
+    void addVirialContribution(const matrix virial)
+    {
+        if (computeVirial_)
+        {
+            for (int dim1 = 0; dim1 < DIM; dim1++)
+            {
+                for (int dim2 = 0; dim2 < DIM; dim2++)
+                {
+                    virial_[dim1][dim2] += virial[dim1][dim2];
+                }
+            }
+        }
+    }
+
+    /*! \brief Adds a virial diagonal contribution
+     *
+     * \note Can be called with \p computeVirial=false.
+     * \note It is recommended to accumulate the virial contributions
+     *       of a module internally before calling this method, as that
+     *       will reduce rounding errors.
+     *
+     * \param[in] virial  The virial contribution to add
+     */
+    void addVirialContribution(const RVec virial)
+    {
+        if (computeVirial_)
+        {
+            for (int dim = 0; dim < DIM; dim++)
+            {
+                virial_[dim][dim] += virial[dim];
+            }
+        }
+    }
+
+    /*! \brief Returns the accumulated virial contributions
+     */
+    const matrix& getVirial() const { return virial_; }
+
+    const ArrayRef<RVec> force_;         //!< Force accumulation buffer reference
+    const bool           computeVirial_; //!< True when algorithms are required to provide their virial contribution (for the current force evaluation)
+private:
+    matrix virial_; //!< Virial accumulation buffer
+};
+
+/*! \libinternal \brief Force and virial output buffers for use in force computation
+ */
+class ForceOutputs
+{
+public:
+    //! Constructor
+    ForceOutputs(const ForceWithShiftForces& forceWithShiftForces,
+                 bool                        haveForceWithVirial,
+                 const ForceWithVirial&      forceWithVirial) :
+        forceWithShiftForces_(forceWithShiftForces),
+        haveForceWithVirial_(haveForceWithVirial),
+        forceWithVirial_(forceWithVirial)
+    {
+    }
+
+    //! Returns a reference to the force with shift forces object
+    ForceWithShiftForces& forceWithShiftForces() { return forceWithShiftForces_; }
+
+    //! Return whether there are forces with direct virial contributions
+    bool haveForceWithVirial() const { return haveForceWithVirial_; }
+
+    //! Returns a reference to the force with virial object
+    ForceWithVirial& forceWithVirial() { return forceWithVirial_; }
+
+private:
+    //! Force output buffer used by legacy modules (without SIMD padding)
+    ForceWithShiftForces forceWithShiftForces_;
+    //! Whether we have forces with direct virial contributions
+    bool haveForceWithVirial_;
+    //! Force with direct virial contribution (if there are any; without SIMD padding)
+    ForceWithVirial forceWithVirial_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdtypes/forcerec.h b/src/include/gromacs/mdtypes/forcerec.h
new file mode 100644 (file)
index 0000000..dffb712
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDTYPES_TYPES_FORCEREC_H
+#define GMX_MDTYPES_TYPES_FORCEREC_H
+
+#include <array>
+#include <memory>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/atominfo.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/pbcutil/ishift.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+#include "locality.h"
+
+/* Abstract type for PME that is defined only in the routine that use them. */
+struct gmx_pme_t;
+struct nonbonded_verlet_t;
+struct bonded_threading_t;
+class DeviceContext;
+class DispersionCorrection;
+class ListedForces;
+class CpuPpLongRangeNonbondeds;
+struct t_fcdata;
+struct t_forcetable;
+struct interaction_const_t;
+
+namespace gmx
+{
+class DeviceStreamManager;
+class ListedForcesGpu;
+class GpuForceReduction;
+class ForceProviders;
+class StatePropagatorDataGpu;
+class PmePpCommGpu;
+class WholeMoleculeTransform;
+} // namespace gmx
+
+/* Value to be used in mdrun for an infinite cut-off.
+ * Since we need to compare with the cut-off squared,
+ * this value should be slighlty smaller than sqrt(GMX_FLOAT_MAX).
+ */
+#define GMX_CUTOFF_INF 1E+18
+//! Check the cuttoff
+real cutoff_inf(real cutoff);
+
+/* Forward declaration of type for managing Ewald tables */
+struct gmx_ewald_tab_t;
+
+/*! \brief Helper force buffers for ForceOutputs
+ *
+ * This class stores intermediate force buffers that are used
+ * internally in the force calculation and which are reduced into
+ * the output force buffer passed to the force calculation.
+ */
+class ForceHelperBuffers
+{
+public:
+    /*! \brief Constructs helper buffers
+     *
+     * When the forces that will be accumulated with help of these buffers
+     * have direct virial contributions, set the parameter to true, so
+     * an extra force buffer is available for these forces to enable
+     * correct virial computation.
+     */
+    ForceHelperBuffers(bool haveDirectVirialContributions);
+
+    //! Returns whether we have a direct virial contribution force buffer
+    bool haveDirectVirialContributions() const { return haveDirectVirialContributions_; }
+
+    //! Returns the buffer for direct virial contributions
+    gmx::ArrayRef<gmx::RVec> forceBufferForDirectVirialContributions()
+    {
+        GMX_ASSERT(haveDirectVirialContributions_, "Buffer can only be requested when present");
+        return forceBufferForDirectVirialContributions_;
+    }
+
+    //! Returns the buffer for shift forces, size c_numShiftVectors
+    gmx::ArrayRef<gmx::RVec> shiftForces() { return shiftForces_; }
+
+    //! Resizes the direct virial contribution buffer, when present
+    void resize(int numAtoms);
+
+private:
+    //! True when we have contributions that are directly added to the virial
+    bool haveDirectVirialContributions_ = false;
+    //! Force buffer for force computation with direct virial contributions
+    std::vector<gmx::RVec> forceBufferForDirectVirialContributions_;
+    //! Shift force array for computing the virial, size c_numShiftVectors
+    std::vector<gmx::RVec> shiftForces_;
+};
+// NOLINTNEXTLINE (clang-analyzer-optin.performance.Padding)
+struct t_forcerec
+{
+    // Declare an explicit constructor and destructor, so they can be
+    // implemented in a single source file, so that not every source
+    // file that includes this one needs to understand how to find the
+    // destructors of the objects pointed to by unique_ptr members.
+    t_forcerec();
+    ~t_forcerec();
+
+    std::unique_ptr<interaction_const_t> ic;
+
+    /* PBC stuff */
+    PbcType pbcType = PbcType::Xyz;
+    //! Tells whether atoms inside a molecule can be in different periodic images,
+    //  i.e. whether we need to take into account PBC when computing distances inside molecules.
+    //  This determines whether PBC must be considered for e.g. bonded interactions.
+    bool            bMolPBC     = false;
+    RefCoordScaling rc_scaling  = RefCoordScaling::No;
+    gmx::RVec       posres_com  = { 0, 0, 0 };
+    gmx::RVec       posres_comB = { 0, 0, 0 };
+
+    bool use_simd_kernels = false;
+
+    /* Interaction for calculated in kernels. In many cases this is similar to
+     * the electrostatics settings in the inputrecord, but the difference is that
+     * these variables always specify the actual interaction in the kernel - if
+     * we are tabulating reaction-field the inputrec will say reaction-field, but
+     * the kernel interaction will say cubic-spline-table. To be safe we also
+     * have a kernel-specific setting for the modifiers - if the interaction is
+     * tabulated we already included the inputrec modification there, so the kernel
+     * modification setting will say 'none' in that case.
+     */
+    NbkernelElecType     nbkernel_elec_interaction = NbkernelElecType::None;
+    NbkernelVdwType      nbkernel_vdw_interaction  = NbkernelVdwType::None;
+    InteractionModifiers nbkernel_elec_modifier    = InteractionModifiers::None;
+    InteractionModifiers nbkernel_vdw_modifier     = InteractionModifiers::None;
+
+    /* Cut-Off stuff.
+     * Infinite cut-off's will be GMX_CUTOFF_INF (unlike in t_inputrec: 0).
+     */
+    real rlist = 0;
+
+    /* Charge sum for topology A/B ([0]/[1]) for Ewald corrections */
+    std::array<double, 2> qsum  = { 0 };
+    std::array<double, 2> q2sum = { 0 };
+    std::array<double, 2> c6sum = { 0 };
+
+    /* Dispersion correction stuff */
+    std::unique_ptr<DispersionCorrection> dispersionCorrection;
+
+    /* Fudge factors */
+    real fudgeQQ = 0;
+
+    std::unique_ptr<t_forcetable> pairsTable; /* for 1-4 interactions, [pairs] and [pairs_nb] */
+
+    /* Free energy */
+    FreeEnergyPerturbationType efep = FreeEnergyPerturbationType::No;
+
+    /* Information about atom properties for the molecule blocks in the global topology */
+    std::vector<gmx::AtomInfoWithinMoleculeBlock> atomInfoForEachMoleculeBlock;
+    /* Information about atom properties for local and non-local atoms */
+    std::vector<int64_t> atomInfo;
+
+    std::vector<gmx::RVec> shift_vec;
+
+    std::unique_ptr<gmx::WholeMoleculeTransform> wholeMoleculeTransform;
+
+    /* The Nbnxm Verlet non-bonded machinery */
+    std::unique_ptr<nonbonded_verlet_t> nbv;
+
+    /* The wall tables (if used) */
+    int                                                     nwall = 0;
+    std::vector<std::vector<std::unique_ptr<t_forcetable>>> wall_tab;
+
+    /* The number of atoms participating in do_force_lowlevel */
+    int natoms_force = 0;
+    /* The number of atoms participating in force calculation and constraints */
+    int natoms_force_constr = 0;
+
+    /* List of helper buffers for ForceOutputs, one for each time step with MTS */
+    std::vector<ForceHelperBuffers> forceHelperBuffers;
+
+    /* Data for PPPM/PME/Ewald */
+    gmx_pme_t*   pmedata                = nullptr;
+    LongRangeVdW ljpme_combination_rule = LongRangeVdW::Geom;
+
+    /* Non bonded Parameter lists */
+    int               ntype          = 0; /* Number of atom types */
+    bool              haveBuckingham = false;
+    std::vector<real> nbfp;
+    std::vector<real> ljpme_c6grid; /* C6-values used on grid in LJPME */
+
+    /* Energy group pair flags */
+    int* egp_flags = nullptr;
+
+    /* Shell molecular dynamics flexible constraints */
+    real fc_stepsize = 0;
+
+    /* If > 0 signals Test Particle Insertion,
+     * the value is the number of atoms of the molecule to insert
+     * Only the energy difference due to the addition of the last molecule
+     * should be calculated.
+     */
+    int n_tpi = 0;
+
+    /* Limit for printing large forces, negative is don't print */
+    real print_force = 0;
+
+    /* User determined parameters, copied from the inputrec */
+    int  userint1  = 0;
+    int  userint2  = 0;
+    int  userint3  = 0;
+    int  userint4  = 0;
+    real userreal1 = 0;
+    real userreal2 = 0;
+    real userreal3 = 0;
+    real userreal4 = 0;
+
+    /* Data for special listed force calculations */
+    std::unique_ptr<t_fcdata> fcdata;
+
+    // The listed forces calculation data, 1 entry or multiple entries with multiple time stepping
+    std::vector<ListedForces> listedForces;
+
+    // The listed forces calculation data for GPU
+    std::unique_ptr<gmx::ListedForcesGpu> listedForcesGpu;
+
+    // The long range non-bonded forces
+    std::unique_ptr<CpuPpLongRangeNonbondeds> longRangeNonbondeds;
+
+    gmx::ForceProviders* forceProviders = nullptr;
+
+    // The stateGpu object is created in runner, forcerec just keeps the copy of the pointer.
+    // TODO: This is not supposed to be here. StatePropagatorDataGpu should be a part of
+    //       general StatePropagatorData object that is passed around
+    gmx::StatePropagatorDataGpu* stateGpu = nullptr;
+    // TODO: Should not be here. This is here only to pass the pointer around.
+    gmx::DeviceStreamManager* deviceStreamManager = nullptr;
+
+    //! GPU device context
+    DeviceContext* deviceContext = nullptr;
+
+    /* For PME-PP GPU communication */
+    std::unique_ptr<gmx::PmePpCommGpu> pmePpCommGpu;
+
+    /* For GPU force reduction (on both local and non-local atoms) */
+    gmx::EnumerationArray<gmx::AtomLocality, std::unique_ptr<gmx::GpuForceReduction>> gpuForceReduction;
+};
+
+/* Important: Starting with Gromacs-4.6, the values of c6 and c12 in the nbfp array have
+ * been scaled by 6.0 or 12.0 to save flops in the kernels. We have corrected this everywhere
+ * in the code, but beware if you are using these macros externally.
+ */
+#define C6(nbfp, ntp, ai, aj) (nbfp)[2 * ((ntp) * (ai) + (aj))]
+#define C12(nbfp, ntp, ai, aj) (nbfp)[2 * ((ntp) * (ai) + (aj)) + 1]
+#define BHAMC(nbfp, ntp, ai, aj) (nbfp)[3 * ((ntp) * (ai) + (aj))]
+#define BHAMA(nbfp, ntp, ai, aj) (nbfp)[3 * ((ntp) * (ai) + (aj)) + 1]
+#define BHAMB(nbfp, ntp, ai, aj) (nbfp)[3 * ((ntp) * (ai) + (aj)) + 2]
+
+#endif
diff --git a/src/include/gromacs/mdtypes/group.h b/src/include/gromacs/mdtypes/group.h
new file mode 100644 (file)
index 0000000..27ebd8e
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDTYPES_GROUP_H
+#define GMX_MDTYPES_GROUP_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+#include "gromacs/utility/smalloc.h"
+
+struct t_grp_tcstat
+{
+    //! Temperature at half step
+    real Th = 0;
+    //! Temperature at full step
+    real T = 0;
+    //! Kinetic energy at half step
+    tensor ekinh = { { 0 } };
+    //! Kinetic energy at old half step
+    tensor ekinh_old = { { 0 } };
+    //! Kinetic energy at full step
+    tensor ekinf = { { 0 } };
+    //! Berendsen coupling lambda
+    real lambda = 0;
+    //! Scaling factor for NHC- full step
+    double ekinscalef_nhc = 0;
+    //! Scaling factor for NHC- half step
+    double ekinscaleh_nhc = 0;
+    //! Scaling factor for NHC- velocity
+    double vscale_nhc = 0;
+};
+
+struct t_cos_acc
+{
+    //! The acceleration for the cosine profile
+    real cos_accel = 0;
+    //! The cos momenta of home particles
+    real mvcos = 0;
+    //! The velocity of the cosine profile
+    real vcos = 0;
+};
+
+class gmx_ekindata_t
+{
+public:
+    gmx_ekindata_t(int numTempCoupleGroups, real cos_accel, int numThreads);
+    //! The number of T-coupling groups
+    int ngtc;
+    //! T-coupling data
+    std::vector<t_grp_tcstat> tcstat;
+    //! Allocated locations for *_work members
+    tensor** ekin_work_alloc = nullptr;
+    //! Work arrays for tcstat per thread
+    tensor** ekin_work = nullptr;
+    //! Work location for dekindl per thread
+    real** dekindl_work = nullptr;
+    //! overall kinetic energy
+    tensor ekin = { { 0 } };
+    //! overall 1/2 step kinetic energy
+    tensor ekinh = { { 0 } };
+    //! dEkin/dlambda at half step
+    real dekindl = 0;
+    //! dEkin/dlambda at old half step
+    real dekindl_old = 0;
+    //! Cosine acceleration data
+    t_cos_acc cosacc;
+
+    ~gmx_ekindata_t();
+
+private:
+    //! For size of ekin_work
+    int nthreads_ = 0;
+};
+
+#define GID(igid, jgid, gnr) \
+    (((igid) < (jgid)) ? ((igid) * (gnr) + (jgid)) : ((jgid) * (gnr) + (igid)))
+
+#endif
diff --git a/src/include/gromacs/mdtypes/iforceprovider.h b/src/include/gromacs/mdtypes/iforceprovider.h
new file mode 100644 (file)
index 0000000..9b65e0f
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::IForceProvider and ForceProviders.
+ *
+ * See \ref page_mdmodules for an overview of this and associated interfaces.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \author Carsten Kutzner <ckutzne@gwdg.de>
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+#ifndef GMX_MDTYPES_IFORCEPROVIDER_H
+#define GMX_MDTYPES_IFORCEPROVIDER_H
+
+#include <memory>
+
+#include "gromacs/math/vec.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxassert.h"
+
+struct gmx_enerdata_t;
+struct t_commrec;
+struct t_forcerec;
+
+namespace gmx
+{
+
+template<typename T>
+class ArrayRef;
+class ForceWithVirial;
+
+
+/*! \libinternal \brief
+ * Helper struct that bundles data for passing it over to the force providers
+ *
+ * This is a short-lived container that bundles up all necessary input data for the
+ * force providers. Its only purpose is to allow calling forceProviders->calculateForces()
+ * with just two arguments, one being the container for the input data,
+ * the other the container for the output data.
+ *
+ * Both ForceProviderInput as well as ForceProviderOutput only package existing
+ * data structs together for handing it over to calculateForces(). Apart from the
+ * POD entries they own nothing.
+ */
+class ForceProviderInput
+{
+public:
+    /*! \brief Constructor assembles all necessary force provider input data
+     *
+     * \param[in]  x        Atomic positions.
+     * \param[in]  homenr   Number of atoms on the domain.
+     * \param[in]  chargeA  Atomic charges for atoms on the domain.
+     * \param[in]  massT    Atomic masses for atoms on the domain.
+     * \param[in]  time     The current time in the simulation.
+     * \param[in]  box      The simulation box.
+     * \param[in]  cr       Communication record structure.
+     */
+    ForceProviderInput(ArrayRef<const RVec> x,
+                       int                  homenr,
+                       ArrayRef<const real> chargeA,
+                       ArrayRef<const real> massT,
+                       double               time,
+                       const matrix         box,
+                       const t_commrec&     cr) :
+        x_(x), homenr_(homenr), chargeA_(chargeA), massT_(massT), t_(time), cr_(cr)
+    {
+        copy_mat(box, box_);
+    }
+
+    ArrayRef<const RVec> x_; //!< The atomic positions
+    int                  homenr_;
+    ArrayRef<const real> chargeA_;
+    ArrayRef<const real> massT_;
+    double               t_; //!< The current time in the simulation
+    matrix               box_ = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 } }; //!< The simulation box
+    const t_commrec&     cr_; //!< Communication record structure
+};
+
+/*! \brief Take pointer, check if valid, return reference
+ */
+template<class T>
+T& makeRefFromPointer(T* ptr)
+{
+    GMX_ASSERT(ptr != nullptr, "got null pointer");
+    return *ptr;
+}
+
+/*! \libinternal \brief
+ * Helper struct bundling the output data of a force provider
+ *
+ * Same as for the ForceProviderInput class, but these variables can be written as well.
+ */
+class ForceProviderOutput
+{
+public:
+    /*! \brief Constructor assembles all necessary force provider output data
+     *
+     * \param[in,out]  forceWithVirial  Container for force and virial
+     * \param[in,out]  enerd            Structure containing energy data
+     */
+    ForceProviderOutput(ForceWithVirial* forceWithVirial, gmx_enerdata_t* enerd) :
+        forceWithVirial_(makeRefFromPointer(forceWithVirial)), enerd_(makeRefFromPointer(enerd))
+    {
+    }
+
+    ForceWithVirial& forceWithVirial_; //!< Container for force and virial
+    gmx_enerdata_t&  enerd_;           //!< Structure containing energy data
+};
+
+
+/*! \libinternal \brief
+ * Interface for a component that provides forces during MD.
+ *
+ * Modules implementing IMDModule generally implement this internally, and use
+ * IMDModule::initForceProviders() to register their implementation in
+ * ForceProviders.
+ *
+ * The interface most likely requires additional generalization for use in
+ * other modules than the current electric field implementation.
+ *
+ * The forces that are produced by force providers are not taken into account
+ * in the calculation of the virial. When applicable, the provider should
+ * compute its own virial contribution.
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+class IForceProvider
+{
+public:
+    /*! \brief
+     * Computes forces.
+     *
+     * \param[in]    forceProviderInput    struct that collects input data for the force providers
+     * \param[in,out] forceProviderOutput   struct that collects output data of the force providers
+     */
+    virtual void calculateForces(const ForceProviderInput& forceProviderInput,
+                                 ForceProviderOutput*      forceProviderOutput) = 0;
+
+protected:
+    ~IForceProvider() {}
+};
+
+/*! \libinternal \brief
+ * Evaluates forces from a collection of gmx::IForceProvider.
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+class ForceProviders
+{
+public:
+    ForceProviders();
+    ~ForceProviders();
+
+    /*! \brief
+     * Adds a provider.
+     */
+    void addForceProvider(gmx::IForceProvider* provider);
+
+    //! Whether there are modules added.
+    bool hasForceProvider() const;
+
+    //! Computes forces.
+    void calculateForces(const gmx::ForceProviderInput& forceProviderInput,
+                         gmx::ForceProviderOutput*      forceProviderOutput) const;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdtypes/imdmodule.h b/src/include/gromacs/mdtypes/imdmodule.h
new file mode 100644 (file)
index 0000000..2f5dde5
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::IMDModule.
+ *
+ * See \ref page_mdmodules for an overview of this and associated interfaces.
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+#ifndef GMX_MDTYPES_IMDMODULE_H
+#define GMX_MDTYPES_IMDMODULE_H
+
+
+namespace gmx
+{
+
+class ForceProviders;
+class IMDOutputProvider;
+class IMdpOptionProvider;
+struct MDModulesNotifiers;
+
+/*! \libinternal \brief
+ * Extension module for \Gromacs simulations.
+ *
+ * The methods that return other interfaces can in the future return null for
+ * those interfaces that the module does not need to implement, but currently
+ * the callers are not prepared to generically handle various cases.
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+class IMDModule
+{
+public:
+    virtual ~IMDModule() {}
+
+    //! Returns an interface for handling mdp input (and tpr I/O).
+    virtual IMdpOptionProvider* mdpOptionProvider() = 0;
+    //! Returns an interface for handling output files during simulation.
+    virtual IMDOutputProvider* outputProvider() = 0;
+    //! Initializes force providers from this module.
+    virtual void initForceProviders(ForceProviders* forceProviders) = 0;
+    //! Subscribe to simulation setup notifications
+    virtual void subscribeToSimulationSetupNotifications(MDModulesNotifiers* notifiers) = 0;
+    //! Subscribe to pre processing notifications
+    virtual void subscribeToPreProcessingNotifications(MDModulesNotifiers* notifiers) = 0;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdtypes/imdoutputprovider.h b/src/include/gromacs/mdtypes/imdoutputprovider.h
new file mode 100644 (file)
index 0000000..6feba4e
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::IMDOutputProvider.
+ *
+ * See \ref page_mdmodules for an overview of this and associated interfaces.
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+#ifndef GMX_MDTYPES_IMDOUTPUTPROVIDER_H
+#define GMX_MDTYPES_IMDOUTPUTPROVIDER_H
+
+#include <cstdio>
+
+struct gmx_output_env_t;
+struct t_filenm;
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Interface for handling additional output files during a simulation.
+ *
+ * This interface provides a mechanism for additional modules to initialize
+ * and finalize output files during the simulation.  Writing values to the
+ * output files is currently handled elsewhere (e.g., when the module has
+ * computed its forces).
+ *
+ * The interface is not very generic, as it has been written purely based on
+ * extraction of existing functions related to electric field handling.
+ * Also, the command-line parameters to specify the output files cannot be
+ * specified by the module, but are hard-coded in mdrun.
+ * This needs to be generalized when more modules are moved to use the
+ * interface.
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+class IMDOutputProvider
+{
+public:
+    /*! \brief
+     * Initializes file output from a simulation run.
+     *
+     * \param[in] fplog File pointer for log messages
+     * \param[in] nfile Number of files
+     * \param[in] fnm   Array of filenames and properties
+     * \param[in] bAppendFiles Whether or not we should append to files
+     * \param[in] oenv  The output environment for xvg files
+     */
+    virtual void initOutput(FILE*                   fplog,
+                            int                     nfile,
+                            const t_filenm          fnm[],
+                            bool                    bAppendFiles,
+                            const gmx_output_env_t* oenv) = 0;
+
+    //! Finalizes output from a simulation run.
+    virtual void finishOutput() = 0;
+
+protected:
+    virtual ~IMDOutputProvider() {}
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdtypes/imdpoptionprovider.h b/src/include/gromacs/mdtypes/imdpoptionprovider.h
new file mode 100644 (file)
index 0000000..63bda6e
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::IMdpOptionProvider.
+ *
+ * See \ref page_mdmodules for an overview of this and associated interfaces.
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+#ifndef GMX_MDTYPES_IMDPOPTIONPROVIDER_H
+#define GMX_MDTYPES_IMDPOPTIONPROVIDER_H
+
+namespace gmx
+{
+
+class IKeyValueTreeTransformRules;
+class IOptionsContainerWithSections;
+class KeyValueTreeObjectBuilder;
+
+/*! \libinternal \brief
+ * Interface for handling mdp/tpr input to a mdrun module.
+ *
+ * This interface provides a mechanism for modules to contribute data that
+ * traditionally has been kept in t_inputrec.  This is essentially parameters
+ * read from an mdp file and subsequently stored in a tpr file.
+ *
+ * The main method to implement is initMdpOptions().  This declares a set of
+ * options in nested sections.  When declaring options, the module defines its
+ * own internal variables where the values will be stored (see \ref
+ * module_options for an overview).  The section structure for the options also
+ * defines an internal data representation in the form of a generic key-value
+ * tree.  This is used to store the values in the tpr file, and also for
+ * broadcasting, gmx dump, and gmx check.
+ *
+ * Implementation of initMdpTransform() is required for populating the options
+ * from the current flat mdp format.  It specifies how the mdp parameters map
+ * into the options structure/internal key-value tree specified in
+ * initMdpOptions().
+ *
+ * See \ref page_mdmodules for more details on how mdp parsing and related
+ * functionality works with this interface.
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+class IMdpOptionProvider
+{
+public:
+    /*! \brief
+     * Initializes a transform from mdp values to sectioned options.
+     *
+     * The transform is specified from a flat KeyValueTreeObject that
+     * contains each mdp value as a property, to a structured
+     * key-value tree that should match the options defined in
+     * initMdpOptions().
+     *
+     * This method may be removed once the flat mdp file is replaced with a
+     * more structure input file (that can be directly read into the
+     * internal key-value tree), and there is no longer any need for
+     * backward compatibility with old files.
+     */
+    virtual void initMdpTransform(IKeyValueTreeTransformRules* transform) = 0;
+    /*! \brief
+     * Initializes options that declare input (mdp) parameters for this
+     * module.
+     */
+    virtual void initMdpOptions(IOptionsContainerWithSections* options) = 0;
+    //! Prepares to write a flat key-value tree like an mdp file.
+    virtual void buildMdpOutput(KeyValueTreeObjectBuilder* builder) const = 0;
+
+protected:
+    virtual ~IMdpOptionProvider() {}
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdtypes/inputrec.h b/src/include/gromacs/mdtypes/inputrec.h
new file mode 100644 (file)
index 0000000..a7e8e9c
--- /dev/null
@@ -0,0 +1,703 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDTYPES_INPUTREC_H
+#define GMX_MDTYPES_INPUTREC_H
+
+#include <cstdio>
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/real.h"
+
+#define EGP_EXCL (1 << 0)
+#define EGP_TABLE (1 << 1)
+
+struct gmx_enfrot;
+struct gmx_enfrotgrp;
+struct pull_params_t;
+
+namespace gmx
+{
+class Awh;
+class AwhParams;
+class KeyValueTreeObject;
+struct MtsLevel;
+} // namespace gmx
+
+struct t_grpopts
+{
+    //! Number of T-Coupl groups
+    int ngtc = 0;
+    //! Number of of Nose-Hoover chains per group
+    int nhchainlength = 0;
+    //! Number of Freeze groups
+    int ngfrz = 0;
+    //! Number of Energy groups
+    int ngener = 0;
+    //! Number of degrees of freedom in a group
+    real* nrdf = nullptr;
+    //! Coupling temperature   per group
+    real* ref_t = nullptr;
+    //! No/simple/periodic simulated annealing for each group
+    SimulatedAnnealing* annealing = nullptr;
+    //! Number of annealing time points per group
+    int* anneal_npoints = nullptr;
+    //! For each group: Time points
+    real** anneal_time = nullptr;
+    //! For each group: Temperature at these times. Final temp after all intervals is ref_t
+    real** anneal_temp = nullptr;
+    //! Tau coupling time
+    real* tau_t = nullptr;
+    //! Whether the group will be frozen in each direction
+    ivec* nFreeze = nullptr;
+    //! Exclusions/tables of energy group pairs
+    int* egp_flags = nullptr;
+
+    /* QMMM stuff */
+    //! Number of QM groups
+    int ngQM = 0;
+};
+
+struct t_simtemp
+{
+    //! Simulated temperature scaling; linear or exponential
+    SimulatedTempering eSimTempScale = SimulatedTempering::Default;
+    //! The low temperature for simulated tempering
+    real simtemp_low = 0;
+    //! The high temperature for simulated tempering
+    real simtemp_high = 0;
+    //! The range of temperatures used for simulated tempering
+    std::vector<real> temperatures;
+};
+
+struct t_lambda
+{
+    //! The frequency for calculating dhdl
+    int nstdhdl = 0;
+    //! Fractional value of lambda (usually will use init_fep_state, this will only be for slow growth, and for legacy free energy code. Only has a valid value if positive)
+    double init_lambda = 0;
+    //! The initial number of the state
+    int init_fep_state = 0;
+    //! Change of lambda per time step (fraction of (0.1)
+    double delta_lambda = 0;
+    //! Print no, total or potential energies in dhdl
+    FreeEnergyPrintEnergy edHdLPrintEnergy = FreeEnergyPrintEnergy::Default;
+    //! The number of foreign lambda points
+    int n_lambda = 0;
+    //! The array of all lambda values
+    gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, std::vector<double>> all_lambda;
+    //! The number of neighboring lambda states to calculate the energy for in up and down directions (-1 for all)
+    int lambda_neighbors = 0;
+    //! The first lambda to calculate energies for
+    int lambda_start_n = 0;
+    //! The last lambda +1 to calculate energies for
+    int lambda_stop_n = 0;
+    //! Free energy soft-core parameter
+    real sc_alpha = 0;
+    //! Lambda power for soft-core interactions
+    int sc_power = 0;
+    //! R power for soft-core interactions
+    real sc_r_power = 0;
+    //! Free energy soft-core sigma when c6 or c12=0
+    real sc_sigma = 0;
+    //! Free energy soft-core sigma for ?????
+    real sc_sigma_min = 0;
+    //! Use softcore for the coulomb portion as well (default FALSE)
+    bool bScCoul = false;
+    //! The specific soft-core function to use
+    SoftcoreType softcoreFunction = SoftcoreType::Beutler;
+    //! scale for the linearization point for the vdw interaction with gapsys soft-core
+    real scGapsysScaleLinpointLJ = 0.85;
+    //! scale for the linearization point for the coulomb interaction with gapsys soft-core
+    real scGapsysScaleLinpointQ = 0.3;
+    //! lower bound for c12/c6 in gapsys soft-core
+    real scGapsysSigmaLJ = 0.3;
+    //! Whether to print the dvdl term associated with this term; if it is not specified as separate, it is lumped with the FEP term
+    gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, bool> separate_dvdl;
+    //! Whether to write a separate dhdl.xvg file note: NOT a gmx_bool, but an enum
+    SeparateDhdlFile separate_dhdl_file = SeparateDhdlFile::Default;
+    //! Whether to calculate+write dhdl derivatives note: NOT a gmx_bool, but an enum
+    DhDlDerivativeCalculation dhdl_derivatives = DhDlDerivativeCalculation::Default;
+    //! The maximum table size for the dH histogram
+    int dh_hist_size = 0;
+    //! The spacing for the dH histogram
+    double dh_hist_spacing = 0;
+};
+
+struct t_expanded
+{
+    //! The frequency of expanded ensemble state changes
+    int nstexpanded = 0;
+    //! Which type of move updating do we use for lambda monte carlo (or no for none)
+    LambdaWeightCalculation elamstats = LambdaWeightCalculation::Default;
+    //! What move set will be we using for state space moves
+    LambdaMoveCalculation elmcmove = LambdaMoveCalculation::Default;
+    //! The method we use to decide of we have equilibrated the weights
+    LambdaWeightWillReachEquilibrium elmceq = LambdaWeightWillReachEquilibrium::Default;
+    //! The minumum number of samples at each lambda for deciding whether we have reached a minimum
+    int equil_n_at_lam = 0;
+    //! Wang-Landau delta at which we stop equilibrating weights
+    real equil_wl_delta = 0;
+    //! Use the ratio of weights (ratio of minimum to maximum) to decide when to stop equilibrating
+    real equil_ratio = 0;
+    //! After equil_steps steps we stop equilibrating the weights
+    int equil_steps = 0;
+    //! After equil_samples total samples (steps/nstfep), we stop equilibrating the weights
+    int equil_samples = 0;
+    //! Random number seed for lambda mc switches
+    int lmc_seed = 0;
+    //! Whether to use minumum variance weighting
+    bool minvar = false;
+    //! The number of samples needed before kicking into minvar routine
+    int minvarmin = 0;
+    //! The offset for the variance in MinVar
+    real minvar_const = 0;
+    //! Range of cvalues used for BAR
+    int c_range = 0;
+    //! Whether to print symmetrized matrices
+    bool bSymmetrizedTMatrix = false;
+    //! How frequently to print the transition matrices
+    int nstTij = 0;
+    //! Number of repetitions in the MC lambda jumps MRS -- VERIFY THIS
+    int lmc_repeats = 0;
+    //! Minimum number of samples for each state before free sampling MRS -- VERIFY THIS!
+    int lmc_forced_nstart = 0;
+    //! Distance in lambda space for the gibbs interval
+    int gibbsdeltalam = 0;
+    //! Scaling factor for Wang-Landau
+    real wl_scale = 0;
+    //! Ratio between largest and smallest number for freezing the weights
+    real wl_ratio = 0;
+    //! Starting delta for Wang-Landau
+    real init_wl_delta = 0;
+    //! Use one over t convergence for Wang-Landau when the delta get sufficiently small
+    bool bWLoneovert = false;
+    //! Did we initialize the weights? TODO: REMOVE FOR 5.0, no longer needed with new logic
+    bool bInit_weights = false;
+    //! To override the main temperature, or define it if it's not defined
+    real mc_temp = 0;
+    //! User-specified initial weights to start with
+    std::vector<real> init_lambda_weights;
+};
+
+struct t_rotgrp
+{
+    //! Rotation type for this group
+    EnforcedRotationGroupType eType;
+    //! Use mass-weighed positions?
+    bool bMassW;
+    //! Number of atoms in the group
+    int nat;
+    //! The global atoms numbers
+    int* ind;
+    //! The reference positions
+    rvec* x_ref;
+    //! The normalized rotation vector
+    rvec inputVec;
+    //! Rate of rotation (degree/ps)
+    real rate;
+    //! Force constant (kJ/(mol nm^2)
+    real k;
+    //! Pivot point of rotation axis (nm)
+    rvec pivot;
+    //! Type of fit to determine actual group angle
+    RotationGroupFitting eFittype;
+    //! Number of angles around the reference angle for which the rotation potential is also evaluated (for fit type 'potential' only)
+    int PotAngle_nstep;
+    //! Distance between two angles in degrees (for fit type 'potential' only)
+    real PotAngle_step;
+    //! Slab distance (nm)
+    real slab_dist;
+    //! Minimum value the gaussian must have so that the force is actually evaluated
+    real min_gaussian;
+    //! Additive constant for radial motion2 and flexible2 potentials (nm^2)
+    real eps;
+};
+
+struct t_rot
+{
+    //! Number of rotation groups
+    int ngrp;
+    //! Output frequency for main rotation outfile
+    int nstrout;
+    //! Output frequency for per-slab data
+    int nstsout;
+    //! Groups to rotate
+    t_rotgrp* grp;
+};
+
+struct t_IMD
+{
+    //! Number of interactive atoms
+    int nat;
+    //! The global indices of the interactive atoms
+    int* ind;
+};
+
+struct t_swapGroup
+{
+    //! Name of the swap group, e.g. NA, CL, SOL
+    char* molname;
+    //! Number of atoms in this group
+    int nat;
+    //! The global ion group atoms numbers
+    int* ind;
+    //! Requested number of molecules of this type per compartment
+    gmx::EnumerationArray<Compartment, int> nmolReq;
+};
+
+struct t_swapcoords
+{
+    //! Period between when a swap is attempted
+    int nstswap;
+    //! Use mass-weighted positions in split group
+    bool massw_split[2];
+    /*! \brief Split cylinders defined by radius, upper and lower
+     * extension. The split cylinders define the channels and are
+     * each anchored in the center of the split group */
+    /**@{*/
+    real cyl0r, cyl1r;
+    real cyl0u, cyl1u;
+    real cyl0l, cyl1l;
+    /**@}*/
+    //! Coupling constant (number of swap attempt steps)
+    int nAverage;
+    //! Ion counts may deviate from the requested values by +-threshold before a swap is done
+    real threshold;
+    //! Offset of the swap layer (='bulk') with respect to the compartment-defining layers
+    gmx::EnumerationArray<Compartment, real> bulkOffset;
+    //! Number of groups to be controlled
+    int ngrp;
+    //! All swap groups, including split and solvent
+    t_swapGroup* grp;
+};
+
+struct t_inputrec // NOLINT (clang-analyzer-optin.performance.Padding)
+{
+    t_inputrec();
+    explicit t_inputrec(const t_inputrec&) = delete;
+    t_inputrec& operator=(const t_inputrec&) = delete;
+    ~t_inputrec();
+
+    //! Integration method
+    IntegrationAlgorithm eI = IntegrationAlgorithm::Default;
+    //! Number of steps to be taken
+    int64_t nsteps = 0;
+    //! Used in checkpointing to separate chunks
+    int simulation_part = 0;
+    //! Start at a stepcount >0 (used w. convert-tpr)
+    int64_t init_step = 0;
+    //! Frequency of energy calc. and T/P coupl. upd.
+    int nstcalcenergy = 0;
+    //! Group or verlet cutoffs
+    CutoffScheme cutoff_scheme = CutoffScheme::Default;
+    //! Number of steps before pairlist is generated
+    int nstlist = 0;
+    //! Number of steps after which center of mass motion is removed
+    int nstcomm = 0;
+    //! Center of mass motion removal algorithm
+    ComRemovalAlgorithm comm_mode = ComRemovalAlgorithm::Default;
+    //! Number of steps after which print to logfile
+    int nstlog = 0;
+    //! Number of steps after which X is output
+    int nstxout = 0;
+    //! Number of steps after which V is output
+    int nstvout = 0;
+    //! Number of steps after which F is output
+    int nstfout = 0;
+    //! Number of steps after which energies printed
+    int nstenergy = 0;
+    //! Number of steps after which compressed trj (.xtc,.tng) is output
+    int nstxout_compressed = 0;
+    //! Initial time (ps)
+    double init_t = 0;
+    //! Time step (ps)
+    double delta_t = 0;
+    //! Whether we use multiple time stepping
+    bool useMts = false;
+    //! The multiple time stepping levels
+    std::vector<gmx::MtsLevel> mtsLevels;
+    //! Precision of x in compressed trajectory file
+    real x_compression_precision = 0;
+    //! Requested fourier_spacing, when nk? not set
+    real fourier_spacing = 0;
+    //! Number of k vectors in x dimension for fourier methods for long range electrost.
+    int nkx = 0;
+    //! Number of k vectors in y dimension for fourier methods for long range electrost.
+    int nky = 0;
+    //! Number of k vectors in z dimension for fourier methods for long range electrost.
+    int nkz = 0;
+    //! Interpolation order for PME
+    int pme_order = 0;
+    //! Real space tolerance for Ewald, determines the real/reciprocal space relative weight
+    real ewald_rtol = 0;
+    //! Real space tolerance for LJ-Ewald
+    real ewald_rtol_lj = 0;
+    //! Normal/3D ewald, or pseudo-2D LR corrections
+    EwaldGeometry ewald_geometry = EwaldGeometry::Default;
+    //! Epsilon for PME dipole correction
+    real epsilon_surface = 0;
+    //! Type of combination rule in LJ-PME
+    LongRangeVdW ljpme_combination_rule = LongRangeVdW::Default;
+    //! Type of periodic boundary conditions
+    PbcType pbcType = PbcType::Default;
+    //! Periodic molecules
+    bool bPeriodicMols = false;
+    //! Continuation run: starting state is correct (ie. constrained)
+    bool bContinuation = false;
+    //! Temperature coupling
+    TemperatureCoupling etc = TemperatureCoupling::Default;
+    //! Interval in steps for temperature coupling
+    int nsttcouple = 0;
+    //! Whether to print nose-hoover chains
+    bool bPrintNHChains = false;
+    //! Pressure coupling
+    PressureCoupling epc = PressureCoupling::Default;
+    //! Pressure coupling type
+    PressureCouplingType epct = PressureCouplingType::Default;
+    //! Interval in steps for pressure coupling
+    int nstpcouple = 0;
+    //! Pressure coupling time (ps)
+    real tau_p = 0;
+    //! Reference pressure (kJ/(mol nm^3))
+    tensor ref_p = { { 0 } };
+    //! Compressibility ((mol nm^3)/kJ)
+    tensor compress = { { 0 } };
+    //! How to scale absolute reference coordinates
+    RefCoordScaling refcoord_scaling = RefCoordScaling::Default;
+    //! The COM of the posres atoms
+    rvec posres_com = { 0, 0, 0 };
+    //! The B-state COM of the posres atoms
+    rvec posres_comB = { 0, 0, 0 };
+    //! Random seed for Andersen thermostat (obsolete)
+    int andersen_seed = 0;
+    //! Per atom pair energy drift tolerance (kJ/mol/ps/atom) for list buffer
+    real verletbuf_tol = 0;
+    //! Short range pairlist cut-off (nm)
+    real rlist = 0;
+    //! Radius for test particle insertion
+    real rtpi = 0;
+    //! Type of electrostatics treatment
+    CoulombInteractionType coulombtype = CoulombInteractionType::Default;
+    //! Modify the Coulomb interaction
+    InteractionModifiers coulomb_modifier = InteractionModifiers::Default;
+    //! Coulomb switch range start (nm)
+    real rcoulomb_switch = 0;
+    //! Coulomb cutoff (nm)
+    real rcoulomb = 0;
+    //! Relative dielectric constant
+    real epsilon_r = 0;
+    //! Relative dielectric constant of the RF
+    real epsilon_rf = 0;
+    //! Always false (no longer supported)
+    bool implicit_solvent = false;
+    //! Type of Van der Waals treatment
+    VanDerWaalsType vdwtype = VanDerWaalsType::Default;
+    //! Modify the Van der Waals interaction
+    InteractionModifiers vdw_modifier = InteractionModifiers::Default;
+    //! Van der Waals switch range start (nm)
+    real rvdw_switch = 0;
+    //! Van der Waals cutoff (nm)
+    real rvdw = 0;
+    //! Perform Long range dispersion corrections
+    DispersionCorrectionType eDispCorr = DispersionCorrectionType::Default;
+    //! Extension of the table beyond the cut-off, as well as the table length for 1-4 interac.
+    real tabext = 0;
+    //! Tolerance for shake
+    real shake_tol = 0;
+    //! Free energy calculations
+    FreeEnergyPerturbationType efep = FreeEnergyPerturbationType::Default;
+    //! Data for the FEP state
+    std::unique_ptr<t_lambda> fepvals;
+    //! Whether to do simulated tempering
+    bool bSimTemp = false;
+    //! Variables for simulated tempering
+    std::unique_ptr<t_simtemp> simtempvals;
+    //! Whether expanded ensembles are used
+    bool bExpanded = false;
+    //! Expanded ensemble parameters
+    std::unique_ptr<t_expanded> expandedvals;
+    //! Type of distance restraining
+    DistanceRestraintRefinement eDisre = DistanceRestraintRefinement::Default;
+    //! Force constant for time averaged distance restraints
+    real dr_fc = 0;
+    //! Type of weighting of pairs in one restraints
+    DistanceRestraintWeighting eDisreWeighting = DistanceRestraintWeighting::Default;
+    //! Use combination of time averaged and instantaneous violations
+    bool bDisreMixed = false;
+    //! Frequency of writing pair distances to enx
+    int nstdisreout = 0;
+    //! Time constant for memory function in disres
+    real dr_tau = 0;
+    //! Force constant for orientational restraints
+    real orires_fc = 0;
+    //! Time constant for memory function in orires
+    real orires_tau = 0;
+    //! Frequency of writing tr(SD) to energy output
+    int nstorireout = 0;
+    //! The stepsize for updating
+    real em_stepsize = 0;
+    //! The tolerance
+    real em_tol = 0;
+    //! Number of iterations for convergence of steepest descent in relax_shells
+    int niter = 0;
+    //! Stepsize for directional minimization in relax_shells
+    real fc_stepsize = 0;
+    //! Number of steps after which a steepest descents step is done while doing cg
+    int nstcgsteep = 0;
+    //! Number of corrections to the Hessian to keep
+    int nbfgscorr = 0;
+    //! Type of constraint algorithm
+    ConstraintAlgorithm eConstrAlg = ConstraintAlgorithm::Default;
+    //! Order of the LINCS Projection Algorithm
+    int nProjOrder = 0;
+    //! Warn if any bond rotates more than this many degrees
+    real LincsWarnAngle = 0;
+    //! Number of iterations in the final LINCS step
+    int nLincsIter = 0;
+    //! Use successive overrelaxation for shake
+    bool bShakeSOR = false;
+    //! Friction coefficient for BD (amu/ps)
+    real bd_fric = 0;
+    //! Random seed for SD and BD
+    int64_t ld_seed = 0;
+    //! The number of walls
+    int nwall = 0;
+    //! The type of walls
+    WallType wall_type = WallType::Default;
+    //! The potentail is linear for r<=wall_r_linpot
+    real wall_r_linpot = 0;
+    //! The atom type for walls
+    int wall_atomtype[2] = { 0, 0 };
+    //! Number density for walls
+    real wall_density[2] = { 0, 0 };
+    //! Scaling factor for the box for Ewald
+    real wall_ewald_zfac = 0;
+
+    /* COM pulling data */
+    //! Do we do COM pulling?
+    bool bPull = false;
+    //! The data for center of mass pulling
+    std::unique_ptr<pull_params_t> pull;
+
+    /* AWH bias data */
+    //! Whether to use AWH biasing for PMF calculations
+    bool bDoAwh = false;
+    //! AWH biasing parameters
+    std::unique_ptr<gmx::AwhParams> awhParams;
+
+    /* Enforced rotation data */
+    //! Whether to calculate enforced rotation potential(s)
+    bool bRot = false;
+    //! The data for enforced rotation potentials
+    t_rot* rot = nullptr;
+
+    //! Whether to do ion/water position exchanges (CompEL)
+    SwapType eSwapCoords = SwapType::Default;
+    //! Swap data structure.
+    t_swapcoords* swap = nullptr;
+
+    //! Whether the tpr makes an interactive MD session possible.
+    bool bIMD = false;
+    //! Interactive molecular dynamics
+    t_IMD* imd = nullptr;
+
+    //! Acceleration for viscosity calculation
+    real cos_accel = 0;
+    //! Triclinic deformation velocities (nm/ps)
+    tensor deform = { { 0 } };
+    /*! \brief User determined parameters */
+    /**@{*/
+    int  userint1  = 0;
+    int  userint2  = 0;
+    int  userint3  = 0;
+    int  userint4  = 0;
+    real userreal1 = 0;
+    real userreal2 = 0;
+    real userreal3 = 0;
+    real userreal4 = 0;
+    /**@}*/
+    //! Group options
+    t_grpopts opts;
+    //! QM/MM calculation
+    bool bQMMM = false;
+
+    /* Fields for removed features go here (better caching) */
+    //! Whether AdResS is enabled - always false if a valid .tpr was read
+    bool bAdress = false;
+    //! Whether twin-range scheme is active - always false if a valid .tpr was read
+    bool useTwinRange = false;
+    //! Whether we have constant acceleration - removed in GROMACS 2022
+    bool useConstantAcceleration = false;
+
+    //! KVT object that contains input parameters converted to the new style.
+    gmx::KeyValueTreeObject* params = nullptr;
+
+    //! KVT for storing simulation parameters that are not part of the mdp file.
+    std::unique_ptr<gmx::KeyValueTreeObject> internalParameters;
+};
+
+int ir_optimal_nstcalcenergy(const t_inputrec* ir);
+
+int tcouple_min_integration_steps(TemperatureCoupling etc);
+
+int ir_optimal_nsttcouple(const t_inputrec* ir);
+
+int pcouple_min_integration_steps(PressureCoupling epc);
+
+int ir_optimal_nstpcouple(const t_inputrec* ir);
+
+/* Returns if the Coulomb force or potential is switched to zero */
+bool ir_coulomb_switched(const t_inputrec* ir);
+
+/* Returns if the Coulomb interactions are zero beyond the rcoulomb.
+ * Note: always returns TRUE for the Verlet cut-off scheme.
+ */
+bool ir_coulomb_is_zero_at_cutoff(const t_inputrec* ir);
+
+/* As ir_coulomb_is_zero_at_cutoff, but also returns TRUE for user tabulated
+ * interactions, since these might be zero beyond rcoulomb.
+ */
+bool ir_coulomb_might_be_zero_at_cutoff(const t_inputrec* ir);
+
+/* Returns if the Van der Waals force or potential is switched to zero */
+bool ir_vdw_switched(const t_inputrec* ir);
+
+/* Returns if the Van der Waals interactions are zero beyond the rvdw.
+ * Note: always returns TRUE for the Verlet cut-off scheme.
+ */
+bool ir_vdw_is_zero_at_cutoff(const t_inputrec* ir);
+
+/* As ir_vdw_is_zero_at_cutoff, but also returns TRUE for user tabulated
+ * interactions, since these might be zero beyond rvdw.
+ */
+bool ir_vdw_might_be_zero_at_cutoff(const t_inputrec* ir);
+
+/*! \brief Free memory from input record.
+ *
+ * All arrays and pointers will be freed.
+ *
+ * \param[in] ir The data structure
+ */
+void done_inputrec(t_inputrec* ir);
+
+void pr_inputrec(FILE* fp, int indent, const char* title, const t_inputrec* ir, bool bMDPformat);
+
+void cmp_inputrec(FILE* fp, const t_inputrec* ir1, const t_inputrec* ir2, real ftol, real abstol);
+
+void comp_pull_AB(FILE* fp, const pull_params_t& pull, real ftol, real abstol);
+
+
+bool inputrecDeform(const t_inputrec* ir);
+
+bool inputrecDynamicBox(const t_inputrec* ir);
+
+bool inputrecPreserveShape(const t_inputrec* ir);
+
+bool inputrecNeedMutot(const t_inputrec* ir);
+
+bool inputrecTwinRange(const t_inputrec* ir);
+
+bool inputrecExclForces(const t_inputrec* ir);
+
+bool inputrecNptTrotter(const t_inputrec* ir);
+
+bool inputrecNvtTrotter(const t_inputrec* ir);
+
+bool inputrecNphTrotter(const t_inputrec* ir);
+
+/*! \brief Return true if the simulation is 2D periodic with two walls. */
+bool inputrecPbcXY2Walls(const t_inputrec* ir);
+
+//! \brief Return true if the simulation has frozen atoms (non-trivial freeze groups).
+bool inputrecFrozenAtoms(const t_inputrec* ir);
+
+/*! \brief Returns true for MD integator with T and/or P-coupling that supports
+ * calculating a conserved energy quantity.
+ *
+ * Note that dynamical integrators without T and P coupling (ie NVE)
+ * return false, i.e. the return value refers to whether there
+ * is a conserved quantity different than the total energy.
+ */
+bool integratorHasConservedEnergyQuantity(const t_inputrec* ir);
+
+/*! \brief Returns true when temperature is coupled or constant. */
+bool integratorHasReferenceTemperature(const t_inputrec* ir);
+
+/*! \brief Return the number of bounded dimensions
+ *
+ * \param[in] ir The input record with MD parameters
+ * \return the number of dimensions in which
+ * the coordinates of the particles are bounded, starting at X.
+ */
+int inputrec2nboundeddim(const t_inputrec* ir);
+
+/*! \brief Returns the number of degrees of freedom in center of mass motion
+ *
+ * \param[in] ir  The inputrec structure
+ * \return the number of degrees of freedom of the center of mass
+ */
+int ndof_com(const t_inputrec* ir);
+
+/*! \brief Returns the maximum reference temperature over all coupled groups
+ *
+ * Returns 0 for energy minimization and normal mode computation.
+ * Returns -1 for MD without temperature coupling.
+ *
+ * \param[in] ir  The inputrec structure
+ */
+real maxReferenceTemperature(const t_inputrec& ir);
+
+/*! \brief Returns whether there is an Ewald surface contribution
+ */
+bool haveEwaldSurfaceContribution(const t_inputrec& ir);
+
+/*! \brief Check if calculation of the specific FEP type was requested.
+ *
+ * \param[in] ir       Input record.
+ * \param[in] fepType  Free-energy perturbation type to check for.
+ *
+ * \returns If the \p fepType is perturbed in this run.
+ */
+bool haveFreeEnergyType(const t_inputrec& ir, int fepType);
+
+#endif /* GMX_MDTYPES_INPUTREC_H */
diff --git a/src/include/gromacs/mdtypes/interaction_const.h b/src/include/gromacs/mdtypes/interaction_const.h
new file mode 100644 (file)
index 0000000..6f10441
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDTYPES_INTERACTION_CONST_H
+#define GMX_MDTYPES_INTERACTION_CONST_H
+
+#include <cstdio>
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/real.h"
+
+struct t_lambda;
+struct t_inputrec;
+struct gmx_mtop_t;
+
+/* Used with force switching or a constant potential shift:
+ * rsw       = max(r - r_switch, 0)
+ * force/p   = r^-(p+1) + c2*rsw^2 + c3*rsw^3
+ * potential = r^-p + c2/3*rsw^3 + c3/4*rsw^4 + cpot
+ * With a constant potential shift c2 and c3 are both 0.
+ */
+struct shift_consts_t
+{
+    real c2;
+    real c3;
+    real cpot;
+};
+
+/* Used with potential switching:
+ * rsw        = max(r - r_switch, 0)
+ * sw         = 1 + c3*rsw^3 + c4*rsw^4 + c5*rsw^5
+ * dsw        = 3*c3*rsw^2 + 4*c4*rsw^3 + 5*c5*rsw^4
+ * force      = force*dsw - potential*sw
+ * potential *= sw
+ */
+struct switch_consts_t
+{
+    real c3;
+    real c4;
+    real c5;
+};
+
+/* Convenience type for vector with aligned memory */
+template<typename T>
+using AlignedVector = std::vector<T, gmx::AlignedAllocator<T>>;
+
+/* Force/energy interpolation tables for Ewald long-range corrections
+ *
+ * Interpolation is linear for the force, quadratic for the potential.
+ */
+struct EwaldCorrectionTables
+{
+    // 1/table_spacing, units 1/nm
+    real scale = 0;
+    // Force table
+    AlignedVector<real> tableF;
+    // Energy table
+    AlignedVector<real> tableV;
+    // Coulomb force+energy table, size of array is tabq_size*4,
+    // entry quadruplets are: F[i], F[i+1]-F[i], V[i], 0,
+    // this is used with 4-wide SIMD for aligned loads
+    AlignedVector<real> tableFDV0;
+};
+
+/* The physical interaction parameters for non-bonded interaction calculations
+ *
+ * This struct contains copies of the physical interaction parameters
+ * from the user input as well as processed values that are need in
+ * non-bonded interaction kernels.
+ *
+ * The default constructor gives plain Coulomb and LJ interactions cut off
+ * a 1 nm without potential shifting and a Coulomb pre-factor of 1.
+ */
+struct interaction_const_t
+{
+    /* This struct contains the soft-core parameters from t_lambda,
+     * but processed for direct use in the kernels.
+     */
+    struct SoftCoreParameters
+    {
+        // Constructor
+        SoftCoreParameters(const t_lambda& fepvals);
+
+        // Alpha parameter for Van der Waals interactions
+        real alphaVdw;
+        // Alpha parameter for Coulomb interactions
+        real alphaCoulomb;
+        // Exponent for the dependence of the soft-core on lambda
+        int lambdaPower;
+        // Value for sigma^6 for LJ interaction with C6<=0 and/or C12<=0
+        real sigma6WithInvalidSigma;
+        // Minimum value for sigma^6, used when soft-core is applied to Coulomb interactions
+        real sigma6Minimum;
+        // soft-core function
+        SoftcoreType softcoreType;
+        // (gapsys sc) linearization point scaling for vdW interactions
+        real gapsysScaleLinpointVdW;
+        // (gapsys sc) linearization point scaling for Coulomb interactions
+        real gapsysScaleLinpointCoul;
+        // (gapsys sc) lower bound/replacement for c12/c6 in vdw interactions
+        real gapsysSigma6VdW;
+    };
+
+    /* VdW */
+    VanDerWaalsType        vdwtype          = VanDerWaalsType::Cut;
+    InteractionModifiers   vdw_modifier     = InteractionModifiers::None;
+    double                 reppow           = 12;
+    real                   rvdw             = 1;
+    real                   rvdw_switch      = 0;
+    struct shift_consts_t  dispersion_shift = { 0, 0, 0 };
+    struct shift_consts_t  repulsion_shift  = { 0, 0, 0 };
+    struct switch_consts_t vdw_switch       = { 0, 0, 0 };
+    bool                   useBuckingham    = false;
+    real                   buckinghamBMax   = 0;
+
+    /* type of electrostatics */
+    CoulombInteractionType eeltype          = CoulombInteractionType::Cut;
+    InteractionModifiers   coulomb_modifier = InteractionModifiers::None;
+
+    /* Coulomb */
+    real rcoulomb        = 1;
+    real rcoulomb_switch = 0;
+
+    /* PME/Ewald */
+    real         ewaldcoeff_q  = 0;
+    real         ewaldcoeff_lj = 0;
+    LongRangeVdW ljpme_comb_rule = LongRangeVdW::Geom; /* LJ combination rule for the LJ PME mesh part */
+    real         sh_ewald        = 0; /* -sh_ewald is added to the direct space potential */
+    real         sh_lj_ewald = 0;     /* sh_lj_ewald is added to the correction potential */
+
+    /* Dielectric constant resp. multiplication factor for charges */
+    real epsilon_r = 1;
+    real epsfac    = 1;
+
+    /* Constants for reaction-field or plain cut-off */
+    //! Dielectric constant for reaction field beyond the cutoff distance
+    real reactionFieldPermitivity = 1;
+    //! Coefficient for reaction field; scales relation between epsilon_r and reactionFieldPermitivity
+    real reactionFieldCoefficient = 0;
+    //! Constant shift to reaction field Coulomb interaction to make potential an integral of force
+    real reactionFieldShift = 0;
+
+    // Coulomb Ewald correction table
+    std::unique_ptr<EwaldCorrectionTables> coulombEwaldTables;
+    // Van der Waals Ewald correction table
+    std::unique_ptr<EwaldCorrectionTables> vdwEwaldTables;
+
+    // Free-energy parameters, only present when free-energy calculations are requested
+    std::unique_ptr<SoftCoreParameters> softCoreParameters;
+};
+
+/*! \brief Construct interaction constants
+ *
+ * This data is used (particularly) by search and force code for
+ * short-range interactions. Many of these are constant for the whole
+ * simulation; some are constant only after PME tuning completes.
+ */
+interaction_const_t init_interaction_const(FILE*             fp,
+                                           const t_inputrec& ir,
+                                           const gmx_mtop_t& mtop,
+                                           bool              systemHasNetCharge);
+
+#endif
diff --git a/src/include/gromacs/mdtypes/locality.h b/src/include/gromacs/mdtypes/locality.h
new file mode 100644 (file)
index 0000000..0ddfa88
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Defines atom and atom interaction locality enums
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDTYPES_LOCALITY_H
+#define GMX_MDTYPES_LOCALITY_H
+
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/exceptions.h"
+
+namespace gmx
+{
+
+/*! \brief Atom locality indicator: local, non-local, all.
+ *
+ * Used for calls to:
+ * gridding, force calculation, x/f buffer operations
+ */
+enum class AtomLocality : int
+{
+    Local    = 0, //!< Local atoms
+    NonLocal = 1, //!< Non-local atoms
+    All      = 2, //!< Both local and non-local atoms
+    Count    = 3  //!< The number of atom locality types
+};
+
+/*! \brief Get the human-friendly name for atom localities.
+ *
+ * \param[in] enumValue The enum value to get the name for.
+ */
+[[maybe_unused]] static const char* enumValueToString(AtomLocality enumValue)
+{
+    static constexpr gmx::EnumerationArray<AtomLocality, const char*> atomLocalityNames = {
+        "Local", "Non-local", "All"
+    };
+    return atomLocalityNames[enumValue];
+}
+/*! \brief Interaction locality indicator: local, non-local, all.
+ *
+ * Used for calls to:
+ * pair-search, force calculation, x/f buffer operations
+ */
+enum class InteractionLocality : int
+{
+    Local    = 0, //!< Interactions between local atoms only
+    NonLocal = 1, //!< Interactions between non-local and (non-)local atoms
+    Count    = 2  //!< The number of interaction locality types
+};
+
+/*! \brief Get the human-friendly name for interaction localities.
+ *
+ * \param[in] enumValue The enum value to get the name for.
+ */
+[[maybe_unused]] static const char* enumValueToString(InteractionLocality enumValue)
+{
+    static constexpr gmx::EnumerationArray<InteractionLocality, const char*> interactionLocalityNames = {
+        "Local", "Non-local"
+    };
+    return interactionLocalityNames[enumValue];
+}
+
+/*! \brief Convert atom locality to interaction locality.
+ *
+ *  In the current implementation the this is straightforward conversion:
+ *  local to local, non-local to non-local.
+ *
+ *  \param[in] atomLocality Atom locality specifier
+ *  \returns                Interaction locality corresponding to the atom locality passed.
+ */
+static inline InteractionLocality atomToInteractionLocality(const AtomLocality atomLocality)
+{
+
+    /* determine interaction locality from atom locality */
+    if (atomLocality == AtomLocality::Local)
+    {
+        return InteractionLocality::Local;
+    }
+    else if (atomLocality == AtomLocality::NonLocal)
+    {
+        return InteractionLocality::NonLocal;
+    }
+    else
+    {
+        GMX_THROW(gmx::InconsistentInputError(
+                "Only Local and NonLocal atom locities can be converted to interaction locality."));
+    }
+}
+
+} // namespace gmx
+
+#endif // GMX_MDTYPES_LOCALITY_H
diff --git a/src/include/gromacs/mdtypes/md_enums.h b/src/include/gromacs/mdtypes/md_enums.h
new file mode 100644 (file)
index 0000000..5f5c303
--- /dev/null
@@ -0,0 +1,855 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares enumerated types used throughout the code.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \inpublicapi
+ * \ingroup module_mdtypes
+ */
+#ifndef GMX_MDTYPES_MD_ENUMS_H
+#define GMX_MDTYPES_MD_ENUMS_H
+
+/*! \brief Return a string from a list of strings
+ *
+ * If index if within 0 .. max_index-1 returns the corresponding string
+ * or "no name defined" otherwise, in other words this is a range-check that does
+ * not crash.
+ * \param[in] index     The index in the array
+ * \param[in] max_index The length of the array
+ * \param[in] names     The array
+ * \return the correct string or "no name defined"
+ */
+const char* enum_name(int index, int max_index, const char* const names[]);
+
+/*! \brief Enum for setting answer to yes or no
+ */
+enum class Boolean : int
+{
+    No,
+    Yes,
+    Count,
+    Default = No
+};
+
+//! Return name of boolean selection.
+const char* enumValueToString(Boolean enumValue);
+//! Return name of boolean selection for actual bool.
+const char* booleanValueToString(bool value);
+
+//! \brief The two compartments for CompEL setups.
+enum class Compartment : int
+{
+    A,
+    B,
+    Count
+};
+
+/*! \brief The channels that define with their COM the compartment boundaries in CompEL setups.
+ *
+ * In principle one could also use modified setups with more than two channels.
+ */
+enum class Channel : int
+{
+    Zero,
+    One,
+    Count
+};
+
+/*! \brief Temperature coupling type
+ *
+ * yes is an alias for berendsen
+ *
+ * Note: Keep `Count` as the second-to-last entry, and `Default` as the last entry -
+ *       this is needed to keep EnumerationWrapper, EnumerationArray and (de)serialization
+ *       working.
+ */
+enum class TemperatureCoupling : int
+{
+    No,
+    Berendsen,
+    NoseHoover,
+    Yes,
+    Andersen,
+    AndersenMassive,
+    VRescale,
+    Count,
+    Default = No
+};
+//! Return names of temperature coupling schemes
+const char* enumValueToString(TemperatureCoupling enumValue);
+//! Return whether this is andersen coupling
+#define ETC_ANDERSEN(e) \
+    (((e) == TemperatureCoupling::AndersenMassive) || ((e) == TemperatureCoupling::Andersen))
+
+/*! \brief Pressure coupling types
+ *
+ * isotropic is an alias for berendsen
+ *
+ * Note: Keep `Count` as the second-to-last entry, and `Default` as the last entry -
+ *       this is needed to keep EnumerationWrapper, EnumerationArray and (de)serialization
+ *       working.
+ */
+enum class PressureCoupling : int
+{
+    No,
+    Berendsen,
+    ParrinelloRahman,
+    Isotropic,
+    Mttk,
+    CRescale,
+    Count,
+    Default = No
+};
+//! Return names of pressure coupling schemes
+const char* enumValueToString(PressureCoupling enumValue);
+
+//! Flat-bottom posres geometries
+enum
+{
+    efbposresZERO,
+    efbposresSPHERE,
+    efbposresCYLINDER,
+    efbposresX,
+    efbposresY,
+    efbposresZ,
+    efbposresCYLINDERX,
+    efbposresCYLINDERY,
+    efbposresCYLINDERZ,
+    efbposresNR
+};
+
+//! Relative coordinate scaling type for position restraints.
+enum class RefCoordScaling : int
+{
+    No,
+    All,
+    Com,
+    Count,
+    Default = No
+};
+
+//! String corresponding to relative coordinate scaling.
+const char* enumValueToString(RefCoordScaling enumValue);
+
+//! Trotter decomposition extended variable parts.
+enum
+{
+    etrtNONE,
+    etrtNHC,
+    etrtBAROV,
+    etrtBARONHC,
+    etrtNHC2,
+    etrtBAROV2,
+    etrtBARONHC2,
+    etrtVELOCITY1,
+    etrtVELOCITY2,
+    etrtPOSITION,
+    etrtSKIPALL,
+    etrtNR
+};
+
+//! Sequenced parts of the trotter decomposition.
+enum class TrotterSequence : int
+{
+    Zero,
+    One,
+    Two,
+    Three,
+    Four,
+    Count
+};
+
+//! Pressure coupling type
+enum class PressureCouplingType : int
+{
+    Isotropic,
+    SemiIsotropic,
+    Anisotropic,
+    SurfaceTension,
+    Count,
+    Default = Isotropic
+};
+//! String corresponding to pressure coupling type
+const char* enumValueToString(PressureCouplingType enumValue);
+
+//! \\brief Cutoff scheme
+enum class CutoffScheme : int
+{
+    Verlet,
+    Group,
+    Count,
+    Default = Verlet
+};
+//! String corresponding to cutoff scheme
+const char* enumValueToString(CutoffScheme enumValue);
+
+/*! \brief Coulomb / VdW interaction modifiers.
+ *
+ * grompp replaces eintmodPOTSHIFT_VERLET_UNSUPPORTED by eintmodPOTSHIFT.
+ * Exactcutoff is only used by Reaction-field-zero, and is not user-selectable.
+ */
+enum class InteractionModifiers : int
+{
+    PotShiftVerletUnsupported,
+    PotShift,
+    None,
+    PotSwitch,
+    ExactCutoff,
+    ForceSwitch,
+    Count,
+    Default = PotShiftVerletUnsupported
+};
+//! String corresponding to interaction modifiers
+const char* enumValueToString(InteractionModifiers enumValue);
+
+/*! \brief Cut-off treatment for Coulomb */
+enum class CoulombInteractionType : int
+{
+    Cut,
+    RF,
+    GRFNotused,
+    Pme,
+    Ewald,
+    P3mAD,
+    Poisson,
+    Switch,
+    Shift,
+    User,
+    GBNotused,
+    RFNecUnsupported,
+    EncadShiftNotused,
+    PmeUser,
+    PmeSwitch,
+    PmeUserSwitch,
+    RFZero,
+    Count,
+    Default = Cut
+};
+//! String corresponding to Coulomb treatment
+const char* enumValueToString(CoulombInteractionType enumValue);
+
+//! Ewald geometry.
+enum class EwaldGeometry : int
+{
+    ThreeD,
+    ThreeDC,
+    Count,
+    Default = ThreeD
+};
+//! String corresponding to Ewald geometry
+const char* enumValueToString(EwaldGeometry enumValue);
+
+//! Macro telling us whether we use reaction field
+#define EEL_RF(e)                                                                   \
+    ((e) == CoulombInteractionType::RF || (e) == CoulombInteractionType::GRFNotused \
+     || (e) == CoulombInteractionType::RFNecUnsupported || (e) == CoulombInteractionType::RFZero)
+
+//! Macro telling us whether we use PME
+#define EEL_PME(e)                                                                             \
+    ((e) == CoulombInteractionType::Pme || (e) == CoulombInteractionType::PmeSwitch            \
+     || (e) == CoulombInteractionType::PmeUser || (e) == CoulombInteractionType::PmeUserSwitch \
+     || (e) == CoulombInteractionType::P3mAD)
+//! Macro telling us whether we use PME or full Ewald
+#define EEL_PME_EWALD(e) (EEL_PME(e) || (e) == CoulombInteractionType::Ewald)
+//! Macro telling us whether we use full electrostatics of any sort
+#define EEL_FULL(e) (EEL_PME_EWALD(e) || (e) == CoulombInteractionType::Poisson)
+//! Macro telling us whether we use user defined electrostatics
+#define EEL_USER(e)                                                                \
+    ((e) == CoulombInteractionType::User || (e) == CoulombInteractionType::PmeUser \
+     || (e) == (CoulombInteractionType::PmeUserSwitch))
+
+//! Van der Waals interaction treatment
+enum class VanDerWaalsType : int
+{
+    Cut,
+    Switch,
+    Shift,
+    User,
+    EncadShiftUnused,
+    Pme,
+    Count,
+    Default = Cut
+};
+//! String corresponding to Van der Waals treatment
+const char* enumValueToString(VanDerWaalsType enumValue);
+
+//! Type of long-range VdW treatment of combination rules
+enum class LongRangeVdW : int
+{
+    Geom,
+    LB,
+    Count,
+    Default = Geom
+};
+//! String for LJPME combination rule treatment
+const char* enumValueToString(LongRangeVdW enumValue);
+
+//! Macro to tell us whether we use LJPME
+#define EVDW_PME(e) ((e) == VanDerWaalsType::Pme)
+
+/*! \brief Integrator algorithm
+ *
+ * eiSD2 has been removed, but we keep a renamed enum entry,
+ * so we can refuse to do MD with such .tpr files.
+ * eiVV is normal velocity verlet
+ * eiVVAK uses 1/2*(KE(t-dt/2)+KE(t+dt/2)) as the kinetic energy,
+ * and the half step kinetic energy for temperature control
+ */
+enum class IntegrationAlgorithm : int
+{
+    MD,
+    Steep,
+    CG,
+    BD,
+    SD2Removed,
+    NM,
+    LBFGS,
+    TPI,
+    TPIC,
+    SD1,
+    VV,
+    VVAK,
+    Mimic,
+    Count,
+    Default = MD
+};
+//! Name of the integrator algorithm
+const char* enumValueToString(IntegrationAlgorithm enumValue);
+//! Do we use MiMiC QM/MM?
+#define EI_MIMIC(e) ((e) == IntegrationAlgorithm::Mimic)
+//! Do we use velocity Verlet
+#define EI_VV(e) ((e) == IntegrationAlgorithm::VV || (e) == IntegrationAlgorithm::VVAK)
+//! Do we use molecular dynamics
+#define EI_MD(e) ((e) == IntegrationAlgorithm::MD || EI_VV(e) || EI_MIMIC(e))
+//! Do we use stochastic dynamics
+#define EI_SD(e) ((e) == IntegrationAlgorithm::SD1)
+//! Do we use any stochastic integrator
+#define EI_RANDOM(e) (EI_SD(e) || (e) == IntegrationAlgorithm::BD)
+/*above integrators may not conserve momenta*/
+//! Do we use any type of dynamics
+#define EI_DYNAMICS(e) (EI_MD(e) || EI_RANDOM(e))
+//! Or do we use minimization
+#define EI_ENERGY_MINIMIZATION(e)                                          \
+    ((e) == IntegrationAlgorithm::Steep || (e) == IntegrationAlgorithm::CG \
+     || (e) == IntegrationAlgorithm::LBFGS)
+//! Do we apply test particle insertion
+#define EI_TPI(e) ((e) == IntegrationAlgorithm::TPI || (e) == IntegrationAlgorithm::TPIC)
+//! Do we deal with particle velocities
+#define EI_STATE_VELOCITY(e) (EI_MD(e) || EI_SD(e))
+
+//! Constraint algorithm
+enum class ConstraintAlgorithm : int
+{
+    Lincs,
+    Shake,
+    Count,
+    Default = Lincs
+};
+//! String corresponding to constraint algorithm
+const char* enumValueToString(ConstraintAlgorithm enumValue);
+
+//! Distance restraint refinement algorithm
+enum class DistanceRestraintRefinement : int
+{
+    None,
+    Simple,
+    Ensemble,
+    Count,
+    Default = None
+};
+//! String corresponding to distance restraint algorithm
+const char* enumValueToString(DistanceRestraintRefinement enumValue);
+
+//! Distance restraints weighting type
+enum class DistanceRestraintWeighting : int
+{
+    Conservative,
+    Equal,
+    Count,
+    Default = Conservative
+};
+//! String corresponding to distance restraint weighting
+const char* enumValueToString(DistanceRestraintWeighting enumValue);
+
+//! Combination rule algorithm.
+enum class CombinationRule : int
+{
+    None,
+    Geometric,
+    Arithmetic,
+    GeomSigEps,
+    Count,
+    Default = Geometric
+};
+//! String for combination rule algorithm
+const char* enumValueToString(CombinationRule enumValue);
+
+//! Van der Waals potential.
+enum class VanDerWaalsPotential : int
+{
+    None,
+    LJ,
+    Buckingham,
+    Count,
+    Default = LJ
+};
+//! String corresponding to Van der Waals potential
+const char* enumValueToString(VanDerWaalsPotential enumValue);
+
+//! Simulated tempering methods.
+enum class SimulatedTempering : int
+{
+    Geometric,
+    Exponential,
+    Linear,
+    Count,
+    Default = Geometric
+};
+//! String corresponding to simulated tempering
+const char* enumValueToString(SimulatedTempering enumValue);
+
+/*! \brief Free energy perturbation type
+ */
+enum class FreeEnergyPerturbationType : int
+{
+    //! there are no evaluations at other states
+    No,
+    //! treated equivalently to Static
+    Yes,
+    //! then lambdas do not change during the simulation
+    Static,
+    //! then the states change monotonically throughout the simulation
+    SlowGrowth,
+    //! then expanded ensemble simulations are occuring
+    Expanded,
+    Count,
+    Default = No
+};
+//! String corresponding to FEP type.
+const char* enumValueToString(FreeEnergyPerturbationType enumValue);
+
+//! Free energy pertubation coupling types.
+enum class FreeEnergyPerturbationCouplingType : int
+{
+    Fep,
+    Mass,
+    Coul,
+    Vdw,
+    Bonded,
+    Restraint,
+    Temperature,
+    Count,
+    Default = Fep
+};
+//! String for FEP coupling type
+const char* enumValueToString(FreeEnergyPerturbationCouplingType enumValue);
+//! String for FEP coupling type, singular mention.
+const char* enumValueToStringSingular(FreeEnergyPerturbationCouplingType enumValue);
+
+/*! \brief What to print for free energy calculations
+ *
+ * Printing the energy to the free energy dhdl file.
+ * Yes is an alias to Total, and
+ * will be converted in readir, so we never have to account for it in code.
+ */
+enum class FreeEnergyPrintEnergy : int
+{
+    No,
+    Total,
+    Potential,
+    Yes,
+    Count,
+    Default = No
+};
+//! String corresponding to printing of free energy
+const char* enumValueToString(FreeEnergyPrintEnergy enumValue);
+
+/*! \brief How the lambda weights are calculated
+ */
+enum class LambdaWeightCalculation : int
+{
+    //! don't calculate
+    No,
+    //! using the metropolis criteria
+    Metropolis,
+    //! using the Barker critera for transition weights, also called unoptimized Bennett
+    Barker,
+    //! using Barker + minimum variance for weights
+    Minvar,
+    //! Wang-Landu (using visitation counts)
+    WL,
+    //! Weighted Wang-Landau (using optimized Gibbs weighted visitation counts)
+    WWL,
+    Count,
+    Default = No
+};
+//! String corresponding to lambda weights
+const char* enumValueToString(LambdaWeightCalculation enumValue);
+//! Macro telling us whether we use expanded ensemble
+#define ELAMSTATS_EXPANDED(e) ((e) > LambdaWeightCalculation::No)
+//! Macro telling us whether we use some kind of Wang-Landau
+#define EWL(e) ((e) == LambdaWeightCalculation::WL || (e) == LambdaWeightCalculation::WWL)
+
+/*! \brief How moves in lambda are calculated
+ */
+enum class LambdaMoveCalculation : int
+{
+    //! don't calculate move
+    No,
+    //! using the Metropolis criteria, and 50% up and down
+    Metropolis,
+    //! using the Barker criteria, and 50% up and down
+    Barker,
+    //! computing the transition using the marginalized probabilities of the lambdas
+    Gibbs,
+    /*! \brief
+     * using the metropolized version of Gibbs
+     *
+     * Monte Carlo Strategies in Scientific computing, Liu, p. 134
+     */
+    MetropolisGibbs,
+    Count,
+    Default = No
+};
+//! String corresponding to lambda moves
+const char* enumValueToString(LambdaMoveCalculation enumValue);
+
+/*! \brief How we decide whether weights have reached equilibrium
+ */
+enum class LambdaWeightWillReachEquilibrium : int
+{
+    //! never stop, weights keep going
+    No,
+    //! fix the weights from the beginning; no movement
+    Yes,
+    //! stop when the WL-delta falls below a certain level
+    WLDelta,
+    //! stop when we have a certain number of samples at every step
+    NumAtLambda,
+    //! stop when we've run a certain total number of steps
+    Steps,
+    //! stop when we've run a certain total number of samples
+    Samples,
+    //! stop when the ratio of samples (lowest to highest) is sufficiently large
+    Ratio,
+    Count,
+    Default = No
+};
+//! String corresponding to equilibrium algorithm
+const char* enumValueToString(LambdaWeightWillReachEquilibrium enumValue);
+
+/*! \brief separate_dhdl_file selection
+ *
+ * NOTE: YES is the first one. Do NOT interpret this one as a gmx_bool
+ * Why was this done this way, just .........
+ */
+enum class SeparateDhdlFile : int
+{
+    Yes,
+    No,
+    Count,
+    Default = Yes
+};
+//! String corresponding to separate DHDL file selection
+const char* enumValueToString(SeparateDhdlFile enumValue);
+
+/*! \brief dhdl_derivatives selection \
+ *
+ * NOTE: YES is the first one. Do NOT interpret this one as a gmx_bool
+ * Why was this done this way, just .........
+ */
+enum class DhDlDerivativeCalculation : int
+{
+    Yes,
+    No,
+    Count,
+    Default = Yes
+};
+//! String for DHDL derivatives
+const char* enumValueToString(DhDlDerivativeCalculation enumValue);
+
+/*! \brief soft-core function \
+ *
+ * Distinguishes between soft-core functions in the input.
+ */
+enum class SoftcoreType : int
+{
+    Beutler,
+    Gapsys,
+    Count,
+    Default = Beutler
+};
+//! Strings for softcore function names
+const char* enumValueToString(SoftcoreType enumValue);
+
+/*! \brief soft-core function as parameter to the nb-fep kernel/14-interaction.\
+ *
+ * Distinguishes between soft-core functions internally. This is different
+ * from SoftcoreType in that it offers 'None' which is not exposed to the user.
+ */
+enum class KernelSoftcoreType : int
+{
+    Beutler,
+    Gapsys,
+    None,
+    Count,
+    Default = Beutler
+};
+//! Strings for softcore function names
+const char* enumValueToString(KernelSoftcoreType enumValue);
+
+/*! \brief Solvent model
+ *
+ * Distinguishes classical water types with 3 or 4 particles
+ */
+enum class SolventModel : int
+{
+    No,
+    Spc,
+    Tip4p,
+    Count,
+    Default = Spc
+};
+//! String corresponding to solvent type
+const char* enumValueToString(SolventModel enumValue);
+
+//! Dispersion correction.
+enum class DispersionCorrectionType : int
+{
+    No,
+    EnerPres,
+    Ener,
+    AllEnerPres,
+    AllEner,
+    Count,
+    Default = No
+};
+//! String corresponding to dispersion corrections
+const char* enumValueToString(DispersionCorrectionType enumValue);
+
+//! Algorithm for simulated annealing.
+enum class SimulatedAnnealing : int
+{
+    No,
+    Single,
+    Periodic,
+    Count,
+    Default = No
+};
+//! String for simulated annealing
+const char* enumValueToString(SimulatedAnnealing enumValue);
+
+//! Wall types.
+enum class WallType : int
+{
+    NineThree,
+    TenFour,
+    Table,
+    TwelveSix,
+    Count,
+    Default = NineThree
+};
+//! String corresponding to wall type
+const char* enumValueToString(WallType enumValue);
+
+//! Pulling algorithm.
+enum class PullingAlgorithm : int
+{
+    Umbrella,
+    Constraint,
+    ConstantForce,
+    FlatBottom,
+    FlatBottomHigh,
+    External,
+    Count,
+    Default = Umbrella
+};
+//! String for pulling algorithm
+const char* enumValueToString(PullingAlgorithm enumValue);
+
+//! Control of pull groups
+enum class PullGroupGeometry : int
+{
+    Distance,
+    Direction,
+    Cylinder,
+    DirectionPBC,
+    DirectionRelative,
+    Angle,
+    Dihedral,
+    AngleAxis,
+    Transformation,
+    Count,
+    Default = Distance
+};
+//! String for pull groups
+const char* enumValueToString(PullGroupGeometry enumValue);
+
+//! Enforced rotation groups.
+enum class EnforcedRotationGroupType : int
+{
+    Iso,
+    Isopf,
+    Pm,
+    Pmpf,
+    Rm,
+    Rmpf,
+    Rm2,
+    Rm2pf,
+    Flex,
+    Flext,
+    Flex2,
+    Flex2t,
+    Count,
+    Default = Iso
+};
+//! Rotation group names
+const char* enumValueToString(EnforcedRotationGroupType enumValue);
+//! String for rotation group origin names
+const char* enumValueToLongString(EnforcedRotationGroupType enumValue);
+
+//! Rotation group fitting type
+enum class RotationGroupFitting : int
+{
+    Rmsd,
+    Norm,
+    Pot,
+    Count,
+    Default = Rmsd
+};
+//! String corresponding to rotation group fitting
+const char* enumValueToString(RotationGroupFitting enumValue);
+
+/*! \brief Direction along which ion/water swaps happen
+ *
+ * Part of "Computational Electrophysiology" (CompEL) setups
+ */
+enum class SwapType : int
+{
+    No,
+    X,
+    Y,
+    Z,
+    Count,
+    Default = No
+};
+//! Names for swapping
+const char* enumValueToString(SwapType enumValue);
+
+/*! \brief Swap group splitting type
+ *
+ * These are just the fixed groups we need for any setup. In t_swap's grp
+ * entry after that follows the variable number of swap groups.
+ */
+enum class SwapGroupSplittingType : int
+{
+    Split0,
+    Split1,
+    Solvent,
+    Count,
+    Default = Solvent
+};
+//! String for swap group splitting
+const char* enumValueToString(SwapGroupSplittingType enumValue);
+
+/*! \brief Types of electrostatics calculations
+ *
+ * Types of electrostatics calculations available inside nonbonded kernels.
+ * Note that these do NOT necessarily correspond to the user selections
+ * in the MDP file; many interactions for instance map to tabulated kernels.
+ */
+enum class NbkernelElecType : int
+{
+    None,
+    Coulomb,
+    ReactionField,
+    CubicSplineTable,
+    Ewald,
+    Count,
+    Default = None
+};
+//! String corresponding to electrostatics kernels
+const char* enumValueToString(NbkernelElecType enumValue);
+
+/*! \brief Types of vdw calculations available
+ *
+ * Types of vdw calculations available inside nonbonded kernels.
+ * Note that these do NOT necessarily correspond to the user selections
+ * in the MDP file; many interactions for instance map to tabulated kernels.
+ */
+enum class NbkernelVdwType : int
+{
+    None,
+    LennardJones,
+    Buckingham,
+    CubicSplineTable,
+    LJEwald,
+    Count,
+    Default = None
+};
+//! String corresponding to VdW kernels
+const char* enumValueToString(NbkernelVdwType enumValue);
+
+//! Center of mass motion removal algorithm.
+enum class ComRemovalAlgorithm : int
+{
+    Linear,
+    Angular,
+    No,
+    LinearAccelerationCorrection,
+    Count,
+    Default = Linear
+};
+//! String corresponding to COM removal
+const char* enumValueToString(ComRemovalAlgorithm enumValue);
+
+//! Enumeration that contains all supported periodic boundary setups.
+enum class PbcType : int
+{
+    Xyz     = 0, //!< Periodic boundaries in all dimensions.
+    No      = 1, //!< No periodic boundaries.
+    XY      = 2, //!< Only two dimensions are periodic.
+    Screw   = 3, //!< Screw.
+    Unset   = 4, //!< The type of PBC is not set or invalid.
+    Count   = 5,
+    Default = Xyz
+};
+
+#endif /* GMX_MDTYPES_MD_ENUMS_H */
diff --git a/src/include/gromacs/mdtypes/mdatom.h b/src/include/gromacs/mdtypes/mdatom.h
new file mode 100644 (file)
index 0000000..91ee9f0
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \brief
+ * Declares mdatom data structure.
+ *
+ * \inpublicapi
+ * \ingroup module_mdtypes
+ */
+#ifndef GMX_MDTYPES_MDATOM_H
+#define GMX_MDTYPES_MDATOM_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+enum class ParticleType : int;
+
+typedef struct t_mdatoms
+{
+    //! Total mass in state A
+    real tmassA;
+    //! Total mass in state B
+    real tmassB;
+    //! Total mass
+    real tmass;
+    //! Number of atoms in arrays
+    int nr;
+    //! Number of elements in arrays
+    int nalloc;
+    //! Number of energy groups
+    int nenergrp;
+    //! Do we have multiple center of mass motion removal groups
+    bool bVCMgrps;
+    //! Do we have any virtual sites?
+    bool haveVsites;
+    //! Do we have atoms that are frozen along 1 or 2 (not 3) dimensions?
+    bool havePartiallyFrozenAtoms;
+    //! Number of perturbed atoms
+    int nPerturbed;
+    //! Number of atoms for which the mass is perturbed
+    int nMassPerturbed;
+    //! Number of atoms for which the charge is perturbed
+    int nChargePerturbed;
+    //! Number of atoms for which the type is perturbed
+    int nTypePerturbed;
+    //! Do we have orientation restraints
+    bool bOrires;
+    //! Atomic mass in A state
+    real* massA;
+    //! Atomic mass in B state
+    real* massB;
+    //! Atomic mass in present state
+    real* massT;
+    //! Inverse atomic mass per atom, 0 for vsites and shells
+    real* invmass;
+    //! Inverse atomic mass per atom and dimension, 0 for vsites, shells and frozen dimensions
+    rvec* invMassPerDim;
+    //! Atomic charge in A state
+    real* chargeA;
+    //! Atomic charge in B state
+    real* chargeB;
+    //! Dispersion constant C6 in A state
+    real* sqrt_c6A;
+    //! Dispersion constant C6 in A state
+    real* sqrt_c6B;
+    //! Van der Waals radius sigma in the A state
+    real* sigmaA;
+    //! Van der Waals radius sigma in the B state
+    real* sigmaB;
+    //! Van der Waals radius sigma^3 in the A state
+    real* sigma3A;
+    //! Van der Waals radius sigma^3 in the B state
+    real* sigma3B;
+    //! Is this atom perturbed
+    bool* bPerturbed;
+    //! Type of atom in the A state
+    int* typeA;
+    //! Type of atom in the B state
+    int* typeB;
+    //! Particle type
+    ParticleType* ptype;
+    //! Group index for temperature coupling
+    unsigned short* cTC;
+    //! Group index for energy matrix
+    unsigned short* cENER;
+    //! Group index for freezing
+    unsigned short* cFREEZE;
+    //! Group index for center of mass motion removal
+    unsigned short* cVCM;
+    //! Group index for user 1
+    unsigned short* cU1;
+    //! Group index for user 2
+    unsigned short* cU2;
+    //! Group index for orientation restraints
+    unsigned short* cORF;
+    //! Number of atoms on this processor. TODO is this still used?
+    int homenr;
+    //! The lambda value used to create the contents of the struct
+    real lambda;
+} t_mdatoms;
+
+#endif
diff --git a/src/include/gromacs/mdtypes/mdrunoptions.h b/src/include/gromacs/mdtypes/mdrunoptions.h
new file mode 100644 (file)
index 0000000..a36c9f4
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file declares helper functionality for legacy option handling for mdrun
+ *
+ * It is likely that much of this content will move closer to the
+ * functionality that supports the respective features. For example,
+ * modules that change behaviour according to whether it is a rerun
+ * could register themselves with the rerun module and get notified at
+ * setup time to set their own boolean, rather than rely on a central
+ * glob of mdrun options being passed around.
+ *
+ * \ingroup module_mdtypes
+ * \inlibraryapi
+ */
+#ifndef GMX_MDTYPES_MDRUNOPTIONS_H
+#define GMX_MDTYPES_MDRUNOPTIONS_H
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+//! Enumeration for mdrun appending behavior
+enum class AppendingBehavior
+{
+    //! Append only if user command-line and file input is correct
+    Auto,
+    //! Must append
+    Appending,
+    //! Must not append
+    NoAppending
+};
+
+//! \internal \brief Options for writing checkpoint files
+struct CheckpointOptions
+{
+    //! True means keep all checkpoint file and add the step number to the name
+    gmx_bool keepAndNumberCheckpointFiles = FALSE;
+    //! The period in minutes for writing checkpoint files
+    real period = 15;
+};
+
+//! \internal \brief Options for timing (parts of) mdrun
+struct TimingOptions
+{
+    //! Reset timers at the start of this MD step, -1 means do not reset
+    int resetStep = -1;
+    //! If true, reset timers half-way the run
+    gmx_bool resetHalfway = FALSE;
+};
+
+//! \internal \brief Options for IMD
+struct ImdOptions
+{
+    //! IMD listening port
+    int port = 8888;
+    //! If true, pause the simulation while no IMD client is connected
+    gmx_bool wait = FALSE;
+    //! If true, allow termination of the simulation from IMD client
+    gmx_bool terminatable = FALSE;
+    //! If true, allow COM pulling in the simulation from IMD client
+    gmx_bool pull = FALSE;
+};
+
+//! \internal \brief Collection of all options of mdrun that are not processed separately
+struct MdrunOptions
+{
+    //! Re-compute energies, and possibly forces, for frames from an input tracjectory
+    gmx_bool rerun = FALSE;
+    //! Re-construct virual sites durin a rerun simulation
+    gmx_bool rerunConstructVsites = FALSE;
+    //! Try to make the simulation binary reproducible
+    gmx_bool reproducible = FALSE;
+    //! Write confout.gro at the end of the run
+    gmx_bool writeConfout = TRUE;
+    //! User option for appending.
+    AppendingBehavior appendingBehavior = AppendingBehavior::Auto;
+    //! Options for checkpointing th simulation
+    CheckpointOptions checkpointOptions;
+    //! Number of steps to run, -2 is use inputrec, -1 is infinite
+    int64_t numStepsCommandline = -2;
+    //! Maximum duration of this simulation in wall-clock hours, -1 is no limit
+    real maximumHoursToRun = -1;
+    //! Options for timing the run
+    TimingOptions timingOptions;
+    //! If true and supported, will tune the PP-PME load balance
+    gmx_bool tunePme = TRUE;
+    //! True if the user explicitly set the -ntomp command line option
+    gmx_bool ntompOptionIsSet = FALSE;
+    //! Options for IMD
+    ImdOptions imdOptions;
+    //! Increase the verbosity level in the logging and/or stdout/stderr
+    gmx_bool verbose = FALSE;
+    //! If verbose=true, print remaining runtime at this step interval
+    int verboseStepPrintInterval = 100;
+};
+
+} // end namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdtypes/multipletimestepping.h b/src/include/gromacs/mdtypes/multipletimestepping.h
new file mode 100644 (file)
index 0000000..f59a5b4
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MULTIPLETIMESTEPPING_H
+#define GMX_MULTIPLETIMESTEPPING_H
+
+#include <string>
+#include <vector>
+
+#include <bitset>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+struct t_inputrec;
+
+namespace gmx
+{
+
+//! Force group available for selection for multiple time step integration
+enum class MtsForceGroups : int
+{
+    LongrangeNonbonded, //!< PME-mesh or Ewald for electrostatics and/or LJ
+    Nonbonded,          //!< Non-bonded pair interactions
+    Pair,               //!< Bonded pair interactions
+    Dihedral,           //!< Dihedrals, including cmap (not restraints)
+    Angle,              //!< Bonded angle potentials (not restraints)
+    Pull,               //!< COM pulling
+    Awh,                //!< Accelerated weight histogram method
+    Count               //!< The number of groups above
+};
+
+//! Names for the MTS force groups
+static const gmx::EnumerationArray<MtsForceGroups, std::string> mtsForceGroupNames = {
+    "longrange-nonbonded", "nonbonded", "pair", "dihedral", "angle", "pull", "awh"
+};
+
+//! Setting for a single level for multiple time step integration
+struct MtsLevel
+{
+    //! The force group selection for this level;
+    std::bitset<static_cast<int>(MtsForceGroups::Count)> forceGroups;
+    //! The factor between the base, fastest, time step and the time step for this level
+    int stepFactor;
+};
+
+/*! \brief Returns the MTS level at which a force group is to be computed
+ *
+ * \param[in] mtsLevels  List of force groups for each MTS level, can be empty without MTS
+ * \param[in] mtsForceGroup  The force group to query the MTS level for
+ */
+static inline int forceGroupMtsLevel(ArrayRef<const MtsLevel> mtsLevels, const MtsForceGroups mtsForceGroup)
+{
+    GMX_ASSERT(mtsLevels.empty() || mtsLevels.size() == 2, "Only 0 or 2 MTS levels are supported");
+
+    return (mtsLevels.empty() || mtsLevels[0].forceGroups[static_cast<int>(mtsForceGroup)]) ? 0 : 1;
+};
+
+/*! \brief Returns the interval in steps at which the non-bonded pair forces are calculated
+ *
+ * Note: returns 1 when multiple time-stepping is not activated.
+ */
+int nonbondedMtsFactor(const t_inputrec& ir);
+
+//! Struct for passing the MTS mdp options to setupMtsLevels()
+struct GromppMtsOpts
+{
+    //! The number of MTS levels
+    int numLevels = 0;
+    //! The names of the force groups assigned by the user to level 2, internal index 1
+    std::string level2Forces;
+    //! The step factor assigned by the user to level 2, internal index 1
+    int level2Factor = 0;
+};
+
+/*! \brief Sets up and returns the MTS levels and checks requirements of MTS
+ *
+ * Appends errors about allowed input values ir to errorMessages, when not nullptr.
+ *
+ * \param[in]     mtsOpts        Options for setting the MTS levels
+ * \param[in,out] errorMessages  List of error messages, can be nullptr
+ */
+std::vector<MtsLevel> setupMtsLevels(const GromppMtsOpts& mtsOpts, std::vector<std::string>* errorMessages);
+
+/*! \brief Returns whether we use MTS and the MTS setup is internally valid
+ *
+ * Note that setupMtsLevels would have returned at least one error message
+ * when this function returns false
+ */
+bool haveValidMtsSetup(const t_inputrec& ir);
+
+/*! \brief Checks whether the MTS requirements on other algorithms and output frequencies are met
+ *
+ * Note: exits with an assertion failure when
+ * ir.useMts == true && haveValidMtsSetup(ir) == false
+ *
+ * \param[in] ir  Complete input record
+ * \returns list of error messages, empty when all MTS requirements are met
+ */
+std::vector<std::string> checkMtsRequirements(const t_inputrec& ir);
+
+} // namespace gmx
+
+#endif /* GMX_MULTIPLETIMESTEPPING_H */
diff --git a/src/include/gromacs/mdtypes/nblist.h b/src/include/gromacs/mdtypes/nblist.h
new file mode 100644 (file)
index 0000000..ff73751
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2015,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_MDTYPES_NBLIST_H
+#define GMX_MDTYPES_NBLIST_H
+
+#include <vector>
+
+
+struct t_nblist
+{
+    int              nri    = 0; /* Current number of i particles         */
+    int              maxnri = 0; /* Max number of i particles     */
+    int              nrj    = 0; /* Current number of j particles         */
+    int              maxnrj = 0; /* ;Max number of j particles    */
+    std::vector<int> iinr;       /* The i-elements                        */
+    std::vector<int> gid;        /* Index in energy arrays                */
+    std::vector<int> shift;      /* Shift vector index                    */
+    std::vector<int> jindex;     /* Index in jjnr                         */
+    std::vector<int> jjnr;       /* The j-atom list                       */
+    std::vector<int> excl_fep;   /* Exclusions for FEP with Verlet scheme */
+};
+
+#endif /* GMX_MDTYPES_NBLIST_H */
diff --git a/src/include/gromacs/mdtypes/observableshistory.h b/src/include/gromacs/mdtypes/observableshistory.h
new file mode 100644 (file)
index 0000000..d2ba1d8
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * This file contains the definition of a container for history data
+ * for simulation observables.
+ *
+ * The container is used for storing the simulation state data that needs
+ * to be written to / read from checkpoint file. This struct should only
+ * contain pure observable data. Microstate data should be in t_state.
+ * The state of the mdrun machinery is also stored elsewhere.
+ *
+ * \author Berk Hess
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDLIB_OBSERVABLESHISTORY_H
+#define GMX_MDLIB_OBSERVABLESHISTORY_H
+
+#include <memory>
+
+class energyhistory_t;
+class PullHistory;
+struct edsamhistory_t;
+struct swaphistory_t;
+
+/*! \libinternal \brief Observables history, for writing/reading to/from checkpoint file
+ */
+struct ObservablesHistory
+{
+    //! History for energy observables, used for output only
+    std::unique_ptr<energyhistory_t> energyHistory;
+
+    //! History for pulling observables, used for output only
+    std::unique_ptr<PullHistory> pullHistory;
+
+    //! Essential dynamics and flooding history
+    std::unique_ptr<edsamhistory_t> edsamHistory;
+
+    //! Ion/water position swapping history
+    std::unique_ptr<swaphistory_t> swapHistory;
+
+    ObservablesHistory();
+
+    ~ObservablesHistory();
+};
+
+#endif
diff --git a/src/include/gromacs/mdtypes/observablesreducer.h b/src/include/gromacs/mdtypes/observablesreducer.h
new file mode 100644 (file)
index 0000000..4d65067
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::ObservablesReducer and builder
+ *
+ * Periodically modules implementing MD simulations need to
+ * communicate with all collaborating ranks to do things like compute
+ * observables like total energies, signal conditions, and check
+ * internal consistency. This communication synchronizes all
+ * participating ranks, which limits scaling and performance, so it is
+ * done rarely (typically once per step) and only when required.
+ *
+ * Modules may provide data of type double to be reduced across
+ * all ranks via an MPI all-reduce with MPI_SUM. Double-precision
+ * floating-point is chosen so that no meaningful precision is lost
+ * e.g. in computing energies, while also permitting integral or
+ * boolean messages to be passed as double-precision floating-point
+ * values.
+ *
+ * Different modules typically need to communicate on different MD
+ * steps, so in principle one might optimize by filling a
+ * std::vector<double> with the values required on the current
+ * step. However, that requires that each module produce and then copy
+ * to the reduction buffer the data for this step. The typical amount
+ * of data required even if all modules need to participate (ie.
+ * hundreds of doubles) is smaller than the message headers that are
+ * used by the underlying network transport protocol. So optimizing
+ * for minimum message size is not particularly effective because it
+ * does not meaningfully reduce the total time taken to communicate.
+ *
+ * Instead, we always reduce a buffer of the size that would be needed
+ * if all active modules required communication this step. Then no
+ * module needs to copy data merely to achieve reduction. To achieve
+ * this, each module needs a stable view of memory into which it can
+ * store data for which reduction is desired. It also means that
+ * modules not active in the current simulation do not contribute to
+ * the workload at run time. Also, modules that are active but don't
+ * need communication at any particular MD step can passively opt out
+ * and that incurs no overhead.
+ *
+ * The functionality is separated two main components, one that does
+ * work during the simulation, and a builder that is used only during
+ * setup time. This separates the responsibilities of
+ * - allowing subscription and building the communication buffer, from
+ * - orchestrating the minimum activity needed for this MD step.
+ *
+ * The interaction diagrams for those two workflows are depicted
+ * below.
+ *
+\msc
+wordwraparcs=true,
+hscale="2";
+
+runner [label="runner"],
+builder [label="builder"],
+moduleA [label="moduleA"],
+moduleB [label="moduleB"],
+observablesReducer [label="observablesReducer"];
+
+runner =>> builder [label="makes"];
+
+runner =>> moduleA [label="makes"];
+runner =>> moduleA [label="passes builder to"];
+moduleA =>> builder [label="subscribes itself to"];
+
+runner =>> moduleB [label="makes"];
+runner =>> moduleB [label="passes builder to"];
+moduleB =>> builder [label="subscribes itself to"];
+
+runner =>> builder [label="calls build()"];
+builder =>> builder [label="makes communication\nbuffer"];
+builder =>> moduleA [label="notifies of\ncallback and view"];
+builder =>> moduleB [label="notifies of\ncallback and view"];
+builder =>> observablesReducer [label="makes"];
+
+\endmsc
+
+Once the \c observablesReducer is built, the builder may be
+destructed.
+
+The \c observablesReducer and its modules operate entirely by
+passing callbacks.
+
+\msc
+wordwraparcs=true,
+hscale="2";
+
+runner [label="runner"],
+moduleA [label="moduleA"],
+moduleB [label="moduleB"],
+observablesReducer [label="observablesReducer"],
+compute_globals [label="compute_globals()"];
+
+runner =>> moduleA [label="asks for work"];
+moduleA =>> moduleA [label="Produces values\nto reduce"];
+moduleA =>> observablesReducer [label="requires reduction from"];
+
+runner =>> moduleB [label="asks for work"];
+moduleB =>> moduleB [label="Produces values\nto reduce"];
+moduleB =>> observablesReducer [label="requires reduction from"];
+
+runner =>> runner [label="Does other things also"];
+runner =>> compute_globals [label="asks to do reduction"];
+compute_globals =>> compute_globals [label="prepares data to\nreduce in\nlegacy style"];
+compute_globals =>> observablesReducer [label="asks for\nbuffer view"];
+observablesReducer =>> compute_globals [label="provides\nbuffer view"];
+compute_globals =>> compute_globals [label="Does MPI_Allreduce"];
+compute_globals =>> observablesReducer [label="notifies after\nreduction"];
+observablesReducer =>> moduleA [label="notifies after reduction"];
+moduleA =>> moduleA [label="Uses reduced values"];
+moduleA =>> observablesReducer [label="returns"];
+observablesReducer =>> moduleB [label="notifies after reduction"];
+moduleB =>> moduleB [label="Uses reduced values"];
+moduleB =>> observablesReducer [label="returns"];
+observablesReducer =>> observablesReducer [label="zeroes reduction buffer"];
+observablesReducer =>> compute_globals [label="returns"];
+
+runner =>> observablesReducer [label="notifies at end of step"];
+
+
+\endmsc
+ *
+ * Three callbacks are produced and called per participating module:
+ *
+ * 1. One produced by the module and passed to the builder so that
+ *    later the ObservablesReducer can call it to notify the module
+ *    that reduction is complete.
+ * 2. One produced by the builder and returned to the module so the
+ *    latter can call it to require reduction when it wishes
+ * 3. One produced by the module and passed to the builder so the
+ *    latter can call it to notify the former of the buffer view
+ *    it should use in the first callback and receive a copy
+ *    of the second callback.
+ *
+ * Modules often request that reduction occur "soon" ie. this step or
+ * next step, depending whether reduction has already take place this
+ * MD step. However they are also able to request reduction to occur
+ * "eventually" ie. only whenever some other module requires it, so
+ * the total number of reductions is minimized. Naturally, the
+ * callback to such a module happens only after the eventual
+ * reduction, which may happen on the same step or a later one. If a
+ * module makes more than one "eventually" reduction request before
+ * reduction takes place, the callback to that module will be called
+ * multiple times when eventually reduction does take place. It is the
+ * responsibility of the module to refrain from making those requests
+ * if the multiple callbacks would be a problem (e.g. maintain an
+ * internal record of whether a reduction request has been made).
+ * Modules are not required to set any value for reduction unless they
+ * are requesting reduction.
+ *
+ * An ObservablesReducer object is intended to replace the use of \c
+ * compute_globals() by simulations, as
+ * https://gitlab.com/gromacs/gromacs/-/issues/3887 progresses. When
+ * no modules using the legacy style communication remain, it is
+ * anticipated that this class will change to contain an MPI
+ * communicator to use to implement the MPI_Allreduce internally.  At
+ * that time, communicationBuffer() and reductionComplete() will
+ * likely change into a doReduction() method, or similar. The flow of
+ * the whole propagator loop will now be less clear inasmuch as the
+ * responsibility for requesting reduction now lies with each module,
+ * however this is probably still more clear than the large forest of
+ * flags that resulted from all modules having to have their control
+ * logic in the propagator loop.
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+#ifndef GMX_MDTYPES_OBSERVABLESREDUCER_H
+#define GMX_MDTYPES_OBSERVABLESREDUCER_H
+
+#include <cstdint>
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+
+class ObservablesReducer;
+using Step = int64_t;
+
+/*! \brief Control whether reduction is required soon. */
+enum class ReductionRequirement : int
+{
+    //! Reduce whenever the runner next checks with the ObservablesReducer.
+    Soon,
+    /*! \brief Reduce whenever the runner next checks with the
+     * ObservablesReducer after some module requires reduction Soon */
+    Eventually
+};
+
+/*! \brief Report whether the reduction has happened this step */
+enum class ObservablesReducerStatus : int
+{
+    //! Reduction has not yet happened this step
+    ReadyToReduce,
+    //! Reduction has happened this step
+    AlreadyReducedThisStep
+};
+
+/*! \libinternal \brief
+ * Builder for ObservablesReducer
+ *
+ * Receives subscriptions from MD modules. Caller should call \c
+ * build() once all subscriptions have been received, and then not
+ * attempt any further subscriptions or builds. At that time, the
+ * builder may be destructed.
+ *
+ * This builder will
+ * - receive all subscriptions from MD modules, then
+ * - build the communication buffer used by the subscribers,
+ * - build the \c ObservablesReducer object that manages the
+ *   lifetime of that buffer, and
+ * - notify the subscribers via callback of the view of that buffer
+ *   that is theirs to use and a further callback to require
+ *   reduction of that buffer.
+ * See also the interaction diagram in the documentation
+ * for observablesreducer.h file.
+ *
+ * Note that the builder callbacks do not follow the approach of \c
+ * MDModulesNotifier because that requires that the same value is
+ * passed to all recipients. Here a distinct value goes to each
+ * recipient, ie. a different view of the communication buffer.
+ *
+ * In order to avoid circular build-time dependencies between the
+ * ObservablesReducer (and its builder) with the modules that use it,
+ * the latter can directly call methods on the former, supplying
+ * anonymous callbacks to be used by the former to contact the
+ * latter. CallbackAfterReduction and CallbackFromBuilder are of this
+ * type.
+ *
+ * A callback type CallBackToRequireReduction is also used instead of
+ * a direct method call on ObservablesReducer to require reduction.
+ * This is implemented by calling a method on the Impl object of a
+ * ObservablesReducer. This extends the interface of
+ * ObservablesReducer in a way that is not directly visible. That
+ * complexity provides two benefits:
+ * - only registered subscribers can require reduction (which helps
+ *   ensure correctness by construction)
+ * - the ObservablesReducer::Impl has a stable address from the heap
+ *   allocation needed for std::unique_ptr to use in forming the
+ *   callback to request reduction.
+ * Alternatives exist for the latter, but create requirements on the
+ * stability of the address of ObservablesReducer, and/or extra
+ * coordination to only pass that address to subscribers once it is
+ * stable.
+ *
+ * It is the subscribers' responsibility to coordinate so that all
+ * subscribers on all ranks agree on the need to communicate, e.g. by
+ * orchestrating communication based on the current step number or a
+ * previous message.
+ *
+ */
+class ObservablesReducerBuilder
+{
+public:
+    //! Constructor
+    ObservablesReducerBuilder();
+    //! Destructor
+    ~ObservablesReducerBuilder();
+    //! Move constructor
+    ObservablesReducerBuilder(ObservablesReducerBuilder&& other) noexcept;
+    //! Move assignment operator
+    ObservablesReducerBuilder& operator=(ObservablesReducerBuilder&& other) noexcept;
+
+    /*! \brief Convenience type for the callback subscribers to
+     * provide when they require reduction. */
+    using CallbackAfterReduction = std::function<void(Step)>;
+    /*! \brief Convenience type for the callback subscribers
+     * call to require reduction.
+     *
+     * When called, the status it returns can be used for checking the
+     * internal expectations of the subscriber on whether reduction
+     * has already occured this step, or not. */
+    using CallbackToRequireReduction = std::function<ObservablesReducerStatus(ReductionRequirement)>;
+    /*! \brief Convenience type for the callback from the builder to
+     * notify the subscribers of the callback they will own and later
+     * use to require reduction and the view of the communication
+     * buffer they will later use. */
+    using CallbackFromBuilder = std::function<void(CallbackToRequireReduction&&, ArrayRef<double>)>;
+
+    /*! \brief Add a subscriber to the \c ObservablesReducer that will
+     * later be built in \c build()
+     *
+     * Takes ownership of both callbacks supplied by the subscribing
+     * module. This approach ensures that time is not spent in the MD
+     * loop constructing std::function objects, because constructing
+     * one of those requires 1-2 heap allocations (depending on the
+     * size of the lambda capture).
+     *
+     * Must not be called after build() */
+    void addSubscriber(int                      sizeRequired,
+                       CallbackFromBuilder&&    callbackFromBuilder,
+                       CallbackAfterReduction&& callbackAfterReduction);
+
+    /*! \brief Build a \c ObservablesReducer to which any subscribers
+     * have been added
+     *
+     * Must be called only once. Notifies each subscriber (via the
+     * CallbackFromBuilder that it supplied) of the view of the
+     * reduction buffer that they will use and the
+     * CallbackToRequireReduction that they will use. */
+    ObservablesReducer build();
+
+private:
+    class Impl;
+    //! Impl object
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \libinternal \brief
+ * Manage reduction of observables for registered subscribers
+ *
+ * Modules can require that the \c ObservablesReducer object to which
+ * they have subscribed do communication this step.  After reduction
+ * is complete, notifications are made to the callbacks that modules
+ * previously supplied to the ObservablesReducerBuilder. Then the
+ * reduction buffer is zeroed. Thus the subscribers may not depend on
+ * the values in their buffer view after the notification callback
+ * returns, so they should do any necessary processing during that
+ * callback.
+ *
+ * Modules are free to request reduction whenever they wish, and have
+ * no obligations to do anything at any time. In particular, they
+ * do not have to set values for their reduction buffer except when
+ * they are requesting reduction.
+ *
+ * The \c ObservablesReducerBuilder object is responsible for
+ * preparing a vector of doubles and notifying the subscribers of the
+ * mutually disjoint views of the buffer that they should use for both
+ * input and output of the reduction. The ObservablesReducer object
+ * that it builds retains no record of the subscribers, because its
+ * responsibility is solely to orchestrate the MPI communication and
+ * callbacks.
+ *
+ * Subscribers automatically use the correct \c ObservablesReducer
+ * object because the callback they received is bound to the correct
+ * one. The only way a module can participate in an \c
+ * ObservablesReducer is to have registered with its builder.
+ *
+ * The owner of an ObservablesReducer must maintain the lifetime of
+ * the \c ObservablesReducer object until all subscribers no longer
+ * need it. After the destruction of an \c ObservablesReducer, if
+ * subscribers access their view of the communication buffer, the
+ * behavior is undefined.
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+class ObservablesReducer
+{
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+
+public:
+    //! Constructor only usable by ObservablesReducerBuilder
+    explicit ObservablesReducer(std::unique_ptr<Impl> impl);
+    // Destructor
+    ~ObservablesReducer();
+    //! Move constructor
+    ObservablesReducer(ObservablesReducer&& other) noexcept;
+    //! Move assignment operator
+    ObservablesReducer& operator=(ObservablesReducer&& other) noexcept;
+
+    /*! \brief Provide view of communication buffer for MPI reduction
+     *
+     * If no subscriber used ReductionRequirement::Soon since the last
+     * call to reductionComplete(), then this method returns an empty
+     * buffer. Otherwise it returns a view over the buffer potentially
+     * filled by all subscribed modules.
+     */
+    ArrayRef<double> communicationBuffer();
+    /*! \brief Called by the runner after MPI communication is complete
+     *
+     * Notifies all subscribers who required reduction since the last
+     * call to reductionComplete() and passes the \c step value so
+     * they can check internally that the simulation state is
+     * consistent.
+     *
+     * After all notifications, zeroes the communication buffer. It is
+     * the responsibility of the subscribers that required reduction
+     * to react suitably to the data available during their
+     * notification. This ensures that modules cannot get arbitrary
+     * but realistic-looking values left behind from previous
+     * communication stages. It also ensures that subsequent
+     * communication stages will not be able to keep reducing values
+     * until they overflow or underflow. This zeroing is most efficient
+     * to do centrally in an object of this class.
+     *
+     * The choice of zero for the sentinel value is not perfect. In
+     * principle, a value of zero is potentially significant to any
+     * subscriber, so could be provided to a subscriber as the result
+     * of an incorrect implementation of ObservablesReducer or
+     * inconsistent use by subscribers. However by construction (which
+     * is tested), the integration tests never produce a zero result
+     * from an reduced value provided by a subscriber. So, if the
+     * coverage there is high then there is good reason to expect that
+     * when a zero value is used by a subscribers it is the result of
+     * a reduction and thus significant, rather than an artefact of
+     * the zeroing of the communication buffer after notifications are
+     * complete.
+     *
+     * The choice of zero ensures that the MPI reduction will produce
+     * a valid numerical result in all cases except when a module that
+     * required reduction set buffer contents that produced a
+     * problematic output after reduction.
+     */
+    void reductionComplete(Step step);
+    /*! \brief Notify the ObservablesReducer that it should make
+     * ready to receive new values to reduce
+     *
+     * Any runner using the ObservablesReducer must call this method
+     * whenever a step completes, so that subscribed modules can use
+     * that information to check whether reduction is happening on the
+     * step that they expect.
+     *
+     * The ObservablesReducer keeps track of whether reduction has
+     * already occured this step, so that when modules request
+     * reduction it can notify them of that status. This permits them
+     * to check their own requirements, e.g. that
+     * ReductionRequirement::Soon will operate this step or next
+     * step.
+     *
+     * For the same reason, it is also necessary to call this method
+     * at a suitable point after uses of an ObservablesReducer before
+     * the regular steps of a runner. */
+    void markAsReadyToReduce();
+    //! The builder needs to be able to make the Impl object
+    friend class ObservablesReducerBuilder;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mdtypes/pull_params.h b/src/include/gromacs/mdtypes/pull_params.h
new file mode 100644 (file)
index 0000000..095b248
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ *
+ * \brief
+ * This file contains datatypes for the mdp options used by the pull code.
+ *
+ * \author Berk Hess
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDTYPES_PULL_PARAMS_H
+#define GMX_MDTYPES_PULL_PARAMS_H
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+/*! \cond INTERNAL */
+
+/*! \brief Struct that defines a pull group */
+struct t_pull_group
+{
+    std::vector<int>  ind;     /**< The global atoms numbers */
+    std::vector<real> weight;  /**< Weights (use all 1 when weight==NULL) */
+    int               pbcatom; /**< The reference atom for pbc (global number) */
+    int               pbcatom_input; /**< The reference atom for pbc (global number) as specified in the input parameters */
+};
+
+/*! Maximum number of pull groups that can be used in a pull coordinate */
+static constexpr int c_pullCoordNgroupMax = 6;
+
+/*! \brief Struct that defines a pull coordinate */
+struct t_pull_coord
+{
+    //! The pull type: umbrella, constraint, ...
+    PullingAlgorithm eType = PullingAlgorithm::Umbrella;
+    //! Name of the module providing   the external potential, only used with eType==epullEXTERNAL
+    std::string externalPotentialProvider;
+    //! The pull geometry
+    PullGroupGeometry eGeom = PullGroupGeometry::Distance;
+    //! Mathematical expression evaluated by the pull code for transformation coordinates.
+    std::string expression;
+    //! The finite difference to use in numerical derivation of mathematical expressions
+    double dx = 1e-9;
+    //! The number of groups, depends on eGeom
+    int ngroup = 0;
+    /*! \brief The pull groups:
+     *
+     *  indices into the group arrays in pull_t and pull_params_t,
+     *   ngroup indices are used
+     */
+    std::array<int, c_pullCoordNgroupMax> group;
+    //! Used to select components for constraint
+    gmx::IVec dim = { 0, 0, 0 };
+    //! The origin for the absolute reference
+    gmx::RVec origin = { 0, 0, 0 };
+    //! The pull vector, direction or position
+    gmx::RVec vec = { 0, 0, 0 };
+    //! Set init based on the initial structure
+    bool bStart = false;
+    //! Initial reference displacement (nm) or (deg)
+    real init = 0.0;
+    //! Rate of motion (nm/ps) or (deg/ps)
+    real rate = 0.0;
+    /*! \brief Force constant
+     *
+     * For umbrella pull type this is (kJ/(mol nm^2) or kJ/(mol rad^2).
+     * For constant force pull type it is kJ/(mol nm) or kJ/(mol rad).
+     */
+    real k = 0.0;
+    //! Force constant for state B
+    real kB = 0.0;
+    //! The index of this coordinate in the list of coordinates
+    int coordIndex = -1;
+};
+
+/*! \brief Struct containing all pull parameters */
+struct pull_params_t
+{
+    //! Number of pull groups
+    int ngroup = 0;
+    //! Number of pull coordinates
+    int ncoord = 0;
+    //! Radius of cylinder for dynamic COM (nm)
+    real cylinder_r = 0.0;
+    //! Absolute tolerance for constraints in (nm)
+    real constr_tol = 0.0;
+    //! Print coordinates of COM for each coord
+    bool bPrintCOM = false;
+    //! Print the reference value for each coord
+    bool bPrintRefValue = false;
+    //! Print cartesian components for each coord with geometry=distance
+    bool bPrintComp = false;
+    //! Use the COM of each group from the previous step as reference
+    bool bSetPbcRefToPrevStepCOM = false;
+    //! Output interval for pull x
+    int nstxout = 0;
+    //! Output interval for pull f
+    int nstfout = 0;
+    //! Write the average coordinate during the output interval
+    bool bXOutAverage = false;
+    //! Write the average force during the output interval
+    bool bFOutAverage = false;
+    //! groups to pull/restrain/etc/
+    std::vector<t_pull_group> group;
+    //! the pull coordinates
+    std::vector<t_pull_coord> coord;
+};
+
+/*! \endcond */
+
+#endif /* GMX_MDTYPES_PULL_PARAMS_H */
diff --git a/src/include/gromacs/mdtypes/pullhistory.h b/src/include/gromacs/mdtypes/pullhistory.h
new file mode 100644 (file)
index 0000000..7be3cb5
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ *
+ * \brief
+ * This file contains datatypes for pull statistics history.
+ *
+ * \author Magnus Lundborg, Berk Hess
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDLIB_PULLHISTORY_H
+#define GMX_MDLIB_PULLHISTORY_H
+
+#include <vector>
+
+//! \cond INTERNAL
+
+//! \brief Contains the sum of coordinate observables to enable calculation of the average of pull data.
+class PullCoordinateHistory
+{
+public:
+    double value;       //!< The sum of the current value of the coordinate, units of nm or rad.
+    double valueRef;    //!< The sum of the reference value of the coordinate, units of nm or rad.
+    double scalarForce; //!< The sum of the scalar force of the coordinate.
+    dvec   dr01;        //!< The sum of the direction vector of group 1 relative to group 0.
+    dvec   dr23;        //!< The sum of the direction vector of group 3 relative to group 2.
+    dvec   dr45;        //!< The sum of the direction vector of group 5 relative to group 4.
+    dvec   dynaX;       //!< The sum of the coordinate of the dynamic groups for geom=cylinder.
+
+    //! Constructor
+    PullCoordinateHistory() : value(0), valueRef(0), scalarForce(0), dr01(), dr23(), dr45(), dynaX()
+    {
+    }
+};
+
+//! \brief Contains the sum of group observables to enable calculation of the average of pull data.
+class PullGroupHistory
+{
+public:
+    dvec x; //!< The sum of the coordinates of the group.
+
+    //! Constructor
+    PullGroupHistory() : x() {}
+};
+
+
+//! \brief Pull statistics history, to allow output of average pull data.
+class PullHistory
+{
+public:
+    int numValuesInXSum; //!< Number of values of the coordinate values in the pull sums.
+    int numValuesInFSum; //!< Number of values in the pull force sums.
+    std::vector<PullCoordinateHistory> pullCoordinateSums; //!< The container of the sums of the values of the pull coordinate, also contains the scalar force.
+    std::vector<PullGroupHistory> pullGroupSums; //!< The container of the sums of the values of the pull group.
+
+    //! Constructor
+    PullHistory() : numValuesInXSum(0), numValuesInFSum(0) {}
+};
+
+//! \endcond
+
+#endif
diff --git a/src/include/gromacs/mdtypes/simulation_workload.h b/src/include/gromacs/mdtypes/simulation_workload.h
new file mode 100644 (file)
index 0000000..1a2106e
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares step, domain-lifetime, and run workload managers.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Szilárd Páll <pall.szilard@gmail.com>
+ * \ingroup module_mdlib
+ * \inlibraryapi
+ */
+#ifndef GMX_MDTYPES_SIMULATION_WORKLOAD_H
+#define GMX_MDTYPES_SIMULATION_WORKLOAD_H
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief Describes work done on this domain by the current rank that may change per-step.
+ *
+ * This work description is based on the SimulationWorkload in the context of the
+ * current particle interactions assigned to this domain as well as other
+ * factors that may change during the lifetime of a domain.
+ *
+ * Note that unlike the other workload descriptors, these flags are also used on
+ * dedicated PME ranks, hence the content is rank-specific (at least when it
+ * comes to flags related to PME).
+ *
+ * Note that the contents of an object of this type is valid for
+ * a single step and it is expected to be set at the beginning each step.
+ *
+ */
+class StepWorkload
+{
+public:
+    //! Whether the state has changed, always set unless TPI is used.
+    bool stateChanged = false;
+    //! Whether the box might have changed
+    bool haveDynamicBox = false;
+    //! Whether neighbor searching needs to be done this step
+    bool doNeighborSearch = false;
+    //! Whether the slow forces need to be computed this step (in addition to the faster forces)
+    bool computeSlowForces = false;
+    //! Whether virial needs to be computed this step
+    bool computeVirial = false;
+    //! Whether energies need to be computed this step this step
+    bool computeEnergy = false;
+    //! Whether (any) forces need to be computed this step, not only energies
+    bool computeForces = false;
+    //! Whether only the MTS combined force buffers are needed and not the separate normal force buffer.
+    bool useOnlyMtsCombinedForceBuffer = false;
+    //! Whether nonbonded forces need to be computed this step
+    bool computeNonbondedForces = false;
+    //! Whether listed forces need to be computed this step
+    bool computeListedForces = false;
+    //! Whether this step DHDL needs to be computed
+    bool computeDhdl = false;
+    /*! \brief Whether coordinate buffer ops are done on the GPU this step
+     * \note This technically belongs to DomainLifetimeWorkload but due
+     * to needing the flag before DomainLifetimeWorkload is built we keep
+     * it here for now.
+     */
+    bool useGpuXBufferOps = false;
+    //! Whether force buffer ops are done on the GPU this step
+    bool useGpuFBufferOps = false;
+    //! Whether PME forces are reduced with other contributions on the GPU this step
+    bool useGpuPmeFReduction = false; // TODO: add this flag to the internal PME GPU data structures too
+    //! Whether GPU coordinates halo exchange is active this step
+    bool useGpuXHalo = false;
+    //! Whether GPU forces halo exchange is active this step
+    bool useGpuFHalo = false;
+    //! Whether GPU PME work is computed on the current rank this step (can be false on PP-only ranks or on fast steps with MTS)
+    bool haveGpuPmeOnThisRank = false;
+    //! Whether to combine the forces for multiple time stepping before the halo exchange
+    bool combineMtsForcesBeforeHaloExchange = false;
+};
+
+/*! \libinternal
+ * \brief Describes work done on this domain on every step of its lifetime,
+ * but which might change after the next domain paritioning.
+ *
+ * This work description is based on the SimulationWorkload in the context of the
+ * current particle interactions assigned to this domain. The latter might change
+ * after the next domain partitioning.
+ *
+ * An object of this type is updated every domain decomposition / neighbour search step
+ * and reflects what work is required during the lifetime of a domain;
+ * e.g. whether there are bonded interactions in this PP task.
+ *
+ */
+class DomainLifetimeWorkload
+{
+public:
+    //! Whether the current nstlist step-range has bonded work to run on a GPU.
+    bool haveGpuBondedWork = false;
+    //! Whether the current nstlist step-range has bonded work to run on the CPU.
+    bool haveCpuBondedWork = false;
+    //! Whether the current nstlist step-range has listed (bonded + restraints) forces work to run on the CPU.
+    bool haveCpuListedForceWork = false;
+    //! Whether the current nstlist step-range has special forces on the CPU.
+    bool haveSpecialForces = false;
+    //! Whether there are currently any local forces to be computed on the CPU
+    bool haveCpuLocalForceWork = false;
+
+    //! Whether the current nstlist step-range Free energy work on the CPU.
+    bool haveFreeEnergyWork = false;
+    //! Whether the CPU force buffer has contributions to local atoms that need to be reduced on the GPU (with DD).
+    // This depends on whether there are CPU-based force tasks
+    // or when DD is active the halo exchange has resulted in contributions
+    // from the non-local part.
+    bool haveLocalForceContribInCpuBuffer = false;
+    //! Whether the CPU force buffer has contributions to nonlocal atoms that need to be reduced on the GPU (with DD).
+    bool haveNonLocalForceContribInCpuBuffer = false;
+};
+
+/*! \libinternal
+ * \brief Manage what computation is required during the simulation.
+ *
+ * Holds information on the type of workload constant for the entire
+ * simulation, and independent of the particle interactions handled
+ * on any specific domain.
+ *
+ * An object of this type is constructed at the beginning of the
+ * simulation and is expected to not change.
+ * Additionally, the initialization is uniform across ranks of a
+ * simulation, even with MPMD decomposition and separate PME ranks.
+ */
+class SimulationWorkload
+{
+public:
+    //! Whether to compute nonbonded pair interactions
+    bool computeNonbonded = false;
+    //! Whether nonbonded pair forces are to be computed at slow MTS steps only
+    bool computeNonbondedAtMtsLevel1 = false;
+    //! Whether total dipole needs to be computed
+    bool computeMuTot = false;
+    //! If we have calculation of short range nonbondeds on CPU
+    bool useCpuNonbonded = false;
+    //! If we have calculation of short range nonbondeds on GPU
+    bool useGpuNonbonded = false;
+    //! If we have calculation of long range PME in GPU
+    bool useCpuPme = false;
+    //! If we have calculation of long range PME in GPU
+    bool useGpuPme = false;
+    //! If PME FFT solving is done on GPU.
+    bool useGpuPmeFft = false;
+    //! If bonded interactions are calculated on GPU.
+    bool useGpuBonded = false;
+    //! If update and constraint solving is performed on GPU.
+    bool useGpuUpdate = false;
+    //! If buffer operations are performed on GPU.
+    bool useGpuBufferOps = false;
+    //! If PP domain decomposition is active.
+    bool havePpDomainDecomposition = false;
+    //! If domain decomposition halo exchange is performed on CPU (in CPU-only runs or with staged GPU communication).
+    bool useCpuHaloExchange = false;
+    //! If domain decomposition halo exchange is performed on GPU.
+    bool useGpuHaloExchange = false;
+    //! If separate PME rank(s) are used.
+    bool haveSeparatePmeRank = false;
+    //! If PP-PME communication is done purely on CPU (in CPU-only runs or with staged GPU communication).
+    bool useCpuPmePpCommunication = false;
+    //! If direct PP-PME communication between GPU is used.
+    bool useGpuPmePpCommunication = false;
+    //! If direct GPU-GPU communication is enabled.
+    bool useGpuDirectCommunication = false;
+    //! If there is an Ewald surface (dipole) term to compute
+    bool haveEwaldSurfaceContribution = false;
+    //! Whether to use multiple time stepping
+    bool useMts = false;
+};
+
+class MdrunScheduleWorkload
+{
+public:
+    //! Workload descriptor for information constant for an entire run
+    SimulationWorkload simulationWork;
+
+    //! Workload descriptor for information constant for an nstlist range of steps
+    DomainLifetimeWorkload domainWork;
+
+    //! Workload descriptor for information that may change per-step
+    StepWorkload stepWork;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDTYPES_SIMULATION_WORKLOAD_H
diff --git a/src/include/gromacs/mdtypes/state.h b/src/include/gromacs/mdtypes/state.h
new file mode 100644 (file)
index 0000000..81c9d13
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \file
+ *
+ * \brief
+ * This file contains the definition of the microstate of the simulated system
+ *
+ * History of observables that needs to be checkpointed should be stored
+ * in ObservablesHistory.
+ * The state of the mdrun machinery that needs to be checkpointed is also
+ * stored elsewhere.
+ *
+ * \author Berk Hess
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+
+#ifndef GMX_MDTYPES_STATE_H
+#define GMX_MDTYPES_STATE_H
+
+#include <array>
+#include <memory>
+#include <vector>
+
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/paddedvector.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/real.h"
+
+struct t_inputrec;
+struct t_lambda;
+enum class FreeEnergyPerturbationType;
+
+namespace gmx
+{
+struct AwhHistory;
+enum class CheckpointDataOperation;
+template<CheckpointDataOperation operation>
+class CheckpointData;
+} // namespace gmx
+
+//! Convenience alias for until all is moved in the gmx namespace
+template<class T>
+using PaddedHostVector = gmx::PaddedHostVector<T>;
+
+/*
+ * The t_state struct should contain all the (possibly) non-static
+ * information required to define the state of the system.
+ * Currently the random seeds for SD and BD are missing.
+ */
+
+/*! \brief Enum for all entries in \p t_state
+ *
+ * These enums are used in flags as (1<<est...).
+ * The order of these enums should not be changed,
+ * since that affects the checkpoint (.cpt) file format.
+ */
+enum class StateEntry : int
+{
+    Lambda,
+    Box,
+    BoxRel,
+    BoxV,
+    PressurePrevious,
+    Nhxi,
+    ThermInt,
+    X,
+    V,
+    SDxNotSupported,
+    Cgp,
+    LDRngNotSupported,
+    LDRngINotSupported,
+    DisreInitF,
+    DisreRm3Tav,
+    OrireInitF,
+    OrireDtav,
+    SVirPrev,
+    Nhvxi,
+    Veta,
+    Vol0,
+    Nhpresxi,
+    Nhpresvxi,
+    FVirPrev,
+    FepState,
+    MCRngNotSupported,
+    MCRngINotSupported,
+    BarosInt,
+    PullComPrevStep,
+    Count
+};
+
+//! \brief The names of the state entries, defined in src/gromacs/fileio/checkpoint.cpp
+const char* enumValueToString(StateEntry enumValue);
+/*! \brief Convert enum to bitmask value.
+ *
+ * Used for setting flags in checkpoint header and verifying which flags are set.
+ */
+template<typename Enum>
+inline int enumValueToBitMask(Enum enumValue)
+{
+    static_assert(static_cast<int>(Enum::Count) <= std::numeric_limits<int>::digits);
+    return 1 << static_cast<int>(enumValue);
+}
+
+/*! \libinternal \brief History information for NMR distance and orientation restraints
+ *
+ * Often this is only used for reporting observables, and thus should not
+ * actually be part of the microstate. But with time-dependent restraining
+ * they are actually part of the (non-Markovian) microstate.
+ * \todo Rename this with a more descriptive name.
+ */
+class history_t
+{
+public:
+    history_t();
+
+    real              disre_initf;  //!< The scaling factor for initializing the time av.
+    std::vector<real> disre_rm3tav; //!< The r^-3 time averaged pair distances
+    real              orire_initf;  //!< The scaling factor for initializing the time av.
+    std::vector<real> orire_Dtav;   //!< The time averaged orientation tensors
+};
+
+/*! \libinternal \brief Struct used for checkpointing only
+ *
+ * This struct would not be required with unlimited precision.
+ * But because of limited precision, the COM motion removal implementation
+ * can cause the kinetic energy in the MD loop to differ by a few bits from
+ * the kinetic energy one would determine from state.v.
+ */
+class ekinstate_t
+{
+public:
+    ekinstate_t();
+
+    bool                bUpToDate;      //!< Test if all data is up to date
+    int                 ekin_n;         //!< The number of tensors
+    tensor*             ekinh;          //!< Half step Ekin, size \p ekin_n
+    tensor*             ekinf;          //!< Full step Ekin, size \p ekin_n
+    tensor*             ekinh_old;      //!< Half step Ekin of the previous step, size \p ekin_n
+    tensor              ekin_total;     //!< Total kinetic energy
+    std::vector<double> ekinscalef_nhc; //!< Nose-Hoover Ekin scaling factors for full step Ekin
+    std::vector<double> ekinscaleh_nhc; //!< Nose-Hoover Ekin scaling factors for half step Ekin
+    std::vector<double> vscale_nhc;     //!< Nose-Hoover velocity scaling factors
+    real                dekindl;        //!< dEkin/dlambda, with free-energy
+    real                mvcos; //!< Cosine(z) component of the momentum, for viscosity calculations
+    /*! \brief Whether KE terms have been read from the checkpoint.
+     *
+     * Only used for managing whether the call to compute_globals
+     * before we enter the MD loop should compute these quantities
+     * fresh, or not. */
+    bool hasReadEkinState;
+
+    /*!
+     * \brief Allows to read and write checkpoint within modular simulator
+     * \tparam operation  Whether we're reading or writing
+     * \param checkpointData  The CheckpointData object
+     */
+    template<gmx::CheckpointDataOperation operation>
+    void doCheckpoint(gmx::CheckpointData<operation> checkpointData);
+};
+
+/*! \brief Free-energy sampling history struct
+ *
+ * \todo Split out into microstate and observables history.
+ */
+struct df_history_t
+{
+    int nlambda; //!< total number of lambda states - for history
+
+    bool  bEquil;   //!< Have we reached equilibration
+    int*  n_at_lam; //!< number of points observed at each lambda
+    real* wl_histo; //!< histogram for WL flatness determination
+    real  wl_delta; //!< current wang-landau delta
+
+    real* sum_weights; //!< weights of the states
+    real* sum_dg; //!< free energies of the states -- not actually used for weighting, but informational
+    real* sum_minvar;   //!< corrections to weights for minimum variance
+    real* sum_variance; //!< variances of the states
+
+    real** accum_p;  //!< accumulated bennett weights for n+1
+    real** accum_m;  //!< accumulated bennett weights for n-1
+    real** accum_p2; //!< accumulated squared bennett weights for n+1
+    real** accum_m2; //!< accumulated squared bennett weights for n-1
+
+    real** Tij;           //!< transition matrix
+    real** Tij_empirical; //!< Empirical transition matrix
+
+    /*! \brief Allows to read and write checkpoint within modular simulator
+     *
+     * \tparam operation  Whether we're reading or writing
+     * \param checkpointData  The CheckpointData object
+     * \param elamstats  How the lambda weights are calculated
+     */
+    template<gmx::CheckpointDataOperation operation>
+    void doCheckpoint(gmx::CheckpointData<operation> checkpointData, LambdaWeightCalculation elamstats);
+};
+
+
+/*! \brief The microstate of the system
+ *
+ * The global state will contain complete data for all used entries.
+ * The local state with domain decomposition will have partial entries
+ * for which \p stateEntryIsAtomProperty() is true. Some entries that
+ * are used in the global state might not be present in the local state.
+ * \todo Move pure observables history to ObservablesHistory.
+ */
+class t_state
+{
+public:
+    t_state();
+
+    // All things public
+    int natoms; //!< Number of atoms, local + non-local; this is the size of \p x, \p v and \p cg_p, when used
+    int ngtc;          //!< The number of temperature coupling groups
+    int nnhpres;       //!< The number of NH-chains for the MTTK barostat (always 1 or 0)
+    int nhchainlength; //!< The NH-chain length for temperature coupling and MTTK barostat
+    int flags; //!< Set of bit-flags telling which entries are present, see enum at the top of the file
+    int fep_state; //!< indicates which of the alchemical states we are in
+    gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, real> lambda; //!< Free-energy lambda vector
+    matrix                                                          box; //!< Matrix of box vectors
+    matrix              box_rel;        //!< Relative box vectors to preserve box shape
+    matrix              boxv;           //!< Box velocities for Parrinello-Rahman P-coupling
+    matrix              pres_prev;      //!< Pressure of the previous step for pcoupl
+    matrix              svir_prev;      //!< Shake virial for previous step for pcoupl
+    matrix              fvir_prev;      //!< Force virial of the previous step for pcoupl
+    std::vector<double> nosehoover_xi;  //!< Nose-Hoover coordinates (ngtc)
+    std::vector<double> nosehoover_vxi; //!< Nose-Hoover velocities (ngtc)
+    std::vector<double> nhpres_xi;      //!< Pressure Nose-Hoover coordinates
+    std::vector<double> nhpres_vxi;     //!< Pressure Nose-Hoover velocities
+    std::vector<double> therm_integral; //!< Work exterted N-H/V-rescale T-coupling (ngtc)
+    double              baros_integral; //!< For Berendsen P-coupling conserved quantity
+    real                veta;           //!< Trotter based isotropic P-coupling
+    real                vol0; //!< Initial volume,required for computing MTTK conserved quantity
+    PaddedHostVector<gmx::RVec> x;    //!< The coordinates (natoms)
+    PaddedHostVector<gmx::RVec> v;    //!< The velocities (natoms)
+    PaddedHostVector<gmx::RVec> cg_p; //!< p vector for conjugate gradient minimization
+
+    ekinstate_t ekinstate; //!< The state of the kinetic energy
+
+    /* History for special algorithms, should be moved to a history struct */
+    history_t                        hist;       //!< Time history for restraints
+    df_history_t*                    dfhist;     //!< Free-energy history for free energy analysis
+    std::shared_ptr<gmx::AwhHistory> awhHistory; //!< Accelerated weight histogram history
+
+    int              ddp_count;       //!< The DD partitioning count for this state
+    int              ddp_count_cg_gl; //!< The DD partitioning count for index_gl
+    std::vector<int> cg_gl;           //!< The global cg number of the local cgs
+
+    std::vector<double> pull_com_prev_step; //!< The COM of the previous step of each pull group
+};
+
+#ifndef DOXYGEN
+/* We don't document the structs below, as they don't belong here.
+ * TODO: Move the next two structs out of state.h.
+ */
+
+struct t_extmass
+{
+    std::vector<double> Qinv; /* inverse mass of thermostat -- computed from inputs, but a good place to store */
+    std::vector<double> QPinv; /* inverse mass of thermostat for barostat -- computed from inputs, but a good place to store */
+    double              Winv; /* Pressure mass inverse -- computed, not input, but a good place to store. Need to make a matrix later */
+    tensor              Winvm; /* inverse pressure mass tensor, computed       */
+};
+
+#endif // DOXYGEN
+
+//! Resizes the T- and P-coupling state variables
+void init_gtc_state(t_state* state, int ngtc, int nnhpres, int nhchainlength);
+
+//! Change the number of atoms represented by this state, allocating memory as needed.
+void state_change_natoms(t_state* state, int natoms);
+
+//! Allocates memory for free-energy history
+void init_dfhist_state(t_state* state, int dfhistNumLambda);
+
+/*! \brief Compares two states, write the differences to stdout */
+void comp_state(const t_state* st1, const t_state* st2, bool bRMSD, real ftol, real abstol);
+
+/*! \brief Allocates an rvec pointer and copy the contents of v to it */
+rvec* makeRvecArray(gmx::ArrayRef<const gmx::RVec> v, gmx::index n);
+
+/*! \brief Determine the relative box components
+ *
+ * Set box_rel e.g. used in mdrun state, used to preserve the box shape
+ * \param[in]    ir      Input record
+ * \param[inout] state   State
+ */
+void set_box_rel(const t_inputrec* ir, t_state* state);
+
+/*! \brief Make sure the relative box shape remains the same
+ *
+ * This function ensures that the relative box dimensions are
+ * preserved, which otherwise might diffuse away due to rounding
+ * errors in pressure coupling or the deform option.
+ *
+ * \param[in]    ir      Input record
+ * \param[in]    box_rel Relative box dimensions
+ * \param[inout] box     The corrected actual box dimensions
+ */
+void preserve_box_shape(const t_inputrec* ir, matrix box_rel, matrix box);
+
+/*! \brief Returns an arrayRef to the positions in \p state when \p state!=null
+ *
+ * When \p state=nullptr, returns an empty arrayRef.
+ *
+ * \note The size returned is the number of atoms, without padding.
+ *
+ * \param[in] state  The state, can be nullptr
+ */
+static inline gmx::ArrayRef<const gmx::RVec> positionsFromStatePointer(const t_state* state)
+{
+    if (state)
+    {
+        return gmx::makeConstArrayRef(state->x).subArray(0, state->natoms);
+    }
+    else
+    {
+        return {};
+    }
+};
+
+/*! \brief Prints the current lambda state to the log file.
+ *
+ * \param[in] fplog  The log file. If fplog == nullptr there will be no output.
+ * \param[in] lambda The array of lambda values.
+ * \param[in] isInitialOutput Whether this output is the initial lambda state or not.
+ */
+void printLambdaStateToLog(FILE* fplog, gmx::ArrayRef<const real> lambda, bool isInitialOutput);
+
+
+/*! \brief Fills fep_state and lambda if needed
+ *
+ * If FEP or simulated tempering is in use,  fills fep_state
+ * and lambda on master rank.
+ *
+ * Reports the initial lambda state to the log file. */
+void initialize_lambdas(FILE*                      fplog,
+                        FreeEnergyPerturbationType freeEnergyPerturbationType,
+                        bool                       haveSimulatedTempering,
+                        const t_lambda&            fep,
+                        gmx::ArrayRef<const real>  simulatedTemperingTemps,
+                        gmx::ArrayRef<real>        ref_t,
+                        bool                       isMaster,
+                        int*                       fep_state,
+                        gmx::ArrayRef<real>        lambda);
+
+#endif
diff --git a/src/include/gromacs/mdtypes/state_propagator_data_gpu.h b/src/include/gromacs/mdtypes/state_propagator_data_gpu.h
new file mode 100644 (file)
index 0000000..ad07df2
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declaration of interfaces for GPU state data propagator object.
+ *
+ * This object stores and manages positions, velocities and forces for
+ * all particles in the system on the GPU.
+ *
+ * \todo Add cycle counters.
+ * \todo Add synchronization points.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_mdtypes
+ */
+#ifndef GMX_MDTYPES_STATE_PROPAGATOR_DATA_GPU_H
+#define GMX_MDTYPES_STATE_PROPAGATOR_DATA_GPU_H
+
+#include <memory>
+#include <tuple>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/simulation_workload.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/classhelpers.h"
+
+#include "locality.h"
+
+class DeviceContext;
+class DeviceStream;
+class GpuEventSynchronizer;
+struct gmx_wallcycle;
+
+namespace gmx
+{
+class DeviceStreamManager;
+
+class StatePropagatorDataGpu
+{
+public:
+    /*! \brief Constructor
+     *
+     * The buffers are reallocated only at the reinit call, the padding is
+     * used there for the coordinates buffer. It is needed for PME and added at
+     * the end of the buffer. It is assumed that if the rank has PME duties on the
+     * GPU, all coordinates are copied to the GPU and hence, for this rank, the
+     * coordinates buffer is not split into local and non-local ranges. For other
+     * ranks, the padding size is zero. This works because only one rank ever does
+     * PME work on the GPU, and if that rank also does PP work that is the only
+     * rank. So all coordinates are always transferred.
+     *
+     * In OpenCL, only pmeStream is used since it is the only stream created in
+     * PME context. The local and non-local streams are only needed when buffer
+     * ops are offloaded. This feature is currently not available in OpenCL and
+     * hence these streams are not set in these builds.
+     *
+     *  \param[in] deviceStreamManager         Object that owns the DeviceContext and DeviceStreams.
+     *  \param[in] transferKind                H2D/D2H transfer call behavior (synchronous or not).
+     *  \param[in] allocationBlockSizeDivisor  Deterines padding size for coordinates buffer.
+     *  \param[in] wcycle                      Wall cycle counter data.
+     */
+    StatePropagatorDataGpu(const DeviceStreamManager& deviceStreamManager,
+                           GpuApiCallBehavior         transferKind,
+                           int                        allocationBlockSizeDivisor,
+                           gmx_wallcycle*             wcycle);
+
+    /*! \brief Constructor to use in PME-only rank and in tests.
+     *
+     *  This constructor should be used if only a coordinate device buffer should be managed
+     *  using a single stream. Any operation on force or velocity buffer as well as copy of
+     *  non-local coordinates will exit with assertion failure. Note, that the pmeStream can
+     *  not be a nullptr and the constructor will exit with an assertion failure.
+     *
+     *  \todo Currently, unsupported copy operations are blocked by assertion that the stream
+     *        not nullptr. This should be improved.
+     *
+     *  \param[in] pmeStream       Device PME stream, nullptr is not allowed.
+     *  \param[in] deviceContext   Device context, nullptr allowed for non-OpenCL builds.
+     *  \param[in] transferKind    H2D/D2H transfer call behavior (synchronous or not).
+     *  \param[in] allocationBlockSizeDivisor Determines padding size for coordinates buffer.
+     *  \param[in] wcycle          Wall cycle counter data.
+     */
+    StatePropagatorDataGpu(const DeviceStream*  pmeStream,
+                           const DeviceContext& deviceContext,
+                           GpuApiCallBehavior   transferKind,
+                           int                  allocationBlockSizeDivisor,
+                           gmx_wallcycle*       wcycle);
+
+    //! Move constructor
+    StatePropagatorDataGpu(StatePropagatorDataGpu&& other) noexcept;
+    //! Move assignment
+    StatePropagatorDataGpu& operator=(StatePropagatorDataGpu&& other) noexcept;
+    //! Destructor
+    ~StatePropagatorDataGpu();
+
+    /*! \brief Set the ranges for local and non-local atoms and reallocates buffers.
+     *
+     * Reallocates coordinate, velocities and force buffers on the device.
+     *
+     * \note
+     * The coordinates buffer is (re)allocated, when required by PME, with a padding,
+     * the size of which is set by the constructor. The padding region clearing kernel
+     * is scheduled in the \p pmeStream_ (unlike the coordinates H2D) as only the PME
+     * task uses this padding area.
+     *
+     * \note
+     * The force buffer is cleared if its size increases, so that previously unused
+     * memory is cleared before forces are accumulated.
+     *
+     *  \param[in] numAtomsLocal  Number of atoms in local domain.
+     *  \param[in] numAtomsAll    Total number of atoms to handle.
+     */
+    void reinit(int numAtomsLocal, int numAtomsAll);
+
+    /*! \brief Returns the range of atoms to be copied based on the copy type (all, local or non-local).
+     *
+     * \todo There are at least three versions of the function with this functionality in the code:
+     *       this one and two more in NBNXM. These should be unified in a shape of a general function
+     *       in DD.
+     *
+     * \param[in]  atomLocality    If all, local or non-local ranges are needed.
+     *
+     * \returns Tuple, containing the index of the first atom in the range and the total number of atoms in the range.
+     */
+    std::tuple<int, int> getAtomRangesFromAtomLocality(AtomLocality atomLocality) const;
+
+
+    /*! \brief Get the positions buffer on the GPU.
+     *
+     *  \returns GPU positions buffer.
+     */
+    DeviceBuffer<RVec> getCoordinates();
+
+    /*! \brief Copy positions to the GPU memory.
+     *
+     * Use \ref getCoordinatesReadyOnDeviceEvent to get the associated event synchronizer or
+     * \ref waitCoordinatesCopiedToDevice to wait for the copy completion.
+     * Note: the event is not marked in OpenCL, because it is not used.
+     *
+     *  \param[in] h_x           Positions in the host memory.
+     *  \param[in] atomLocality  Locality of the particles to copy.
+     */
+    void copyCoordinatesToGpu(gmx::ArrayRef<const gmx::RVec> h_x, AtomLocality atomLocality);
+
+    /*! \brief Get the event synchronizer of the coordinates ready for the consumption on the device.
+     *
+     * Returns the event synchronizer which indicates that the coordinates are ready for the
+     * consumption on the device. Takes into account that the producer may be different.
+     *
+     * If the update is offloaded, and the current step is not a DD/search step, the returned
+     * synchronizer indicates the completion of GPU update-constraint kernels. Otherwise, on search
+     * steps and if update is not offloaded, the coordinates are provided by the H2D copy and the
+     * returned synchronizer indicates that the copy is complete.
+     *
+     *  \param[in] atomLocality              Locality of the particles to wait for.
+     *  \param[in] simulationWork            The simulation lifetime flags.
+     *  \param[in] stepWork                  The step lifetime flags.
+     *  \param[in] gpuCoordinateHaloLaunched Event recorded when GPU coordinate halo has been launched.
+     *
+     *  \returns  The event to synchronize the stream that consumes coordinates on device.
+     */
+    GpuEventSynchronizer* getCoordinatesReadyOnDeviceEvent(AtomLocality              atomLocality,
+                                                           const SimulationWorkload& simulationWork,
+                                                           const StepWorkload&       stepWork,
+                                                           GpuEventSynchronizer* gpuCoordinateHaloLaunched = nullptr);
+
+    /*! \brief Blocking wait until coordinates are copied to the device.
+     *
+     * Synchronizes the stream in which the copy was executed.
+     *
+     *  \param[in] atomLocality  Locality of the particles to wait for.
+     */
+    void waitCoordinatesCopiedToDevice(AtomLocality atomLocality);
+
+    /*! \brief Setter for the event synchronizer for the update is done on th GPU
+     *
+     *  \param[in] xUpdatedOnDeviceEvent  The event to synchronize the stream coordinates wre updated on device.
+     */
+    void setXUpdatedOnDeviceEvent(GpuEventSynchronizer* xUpdatedOnDeviceEvent);
+
+    /*! \brief Copy positions from the GPU memory, with an optional explicit dependency.
+     *
+     *  \param[in] h_x           Positions buffer in the host memory.
+     *  \param[in] atomLocality  Locality of the particles to copy.
+     *  \param[in] dependency    Dependency event for this operation.
+     */
+    void copyCoordinatesFromGpu(gmx::ArrayRef<gmx::RVec> h_x,
+                                AtomLocality             atomLocality,
+                                GpuEventSynchronizer*    dependency = nullptr);
+
+    /*! \brief Wait until coordinates are available on the host.
+     *
+     *  \param[in] atomLocality  Locality of the particles to wait for.
+     */
+    void waitCoordinatesReadyOnHost(AtomLocality atomLocality);
+
+
+    /*! \brief Get the velocities buffer on the GPU.
+     *
+     *  \returns GPU velocities buffer.
+     */
+    DeviceBuffer<RVec> getVelocities();
+
+    /*! \brief Copy velocities to the GPU memory.
+     *
+     * Does not mark any event, because we don't use it anywhere at the moment.
+     *
+     *  \param[in] h_v           Velocities in the host memory.
+     *  \param[in] atomLocality  Locality of the particles to copy.
+     */
+    void copyVelocitiesToGpu(gmx::ArrayRef<const gmx::RVec> h_v, AtomLocality atomLocality);
+
+    /*! \brief Copy velocities from the GPU memory.
+     *
+     *  \param[in] h_v           Velocities buffer in the host memory.
+     *  \param[in] atomLocality  Locality of the particles to copy.
+     */
+    void copyVelocitiesFromGpu(gmx::ArrayRef<gmx::RVec> h_v, AtomLocality atomLocality);
+
+    /*! \brief Wait until velocities are available on the host.
+     *
+     *  \param[in] atomLocality  Locality of the particles to wait for.
+     */
+    void waitVelocitiesReadyOnHost(AtomLocality atomLocality);
+
+
+    /*! \brief Get the force buffer on the GPU.
+     *
+     *  \returns GPU force buffer.
+     */
+    DeviceBuffer<RVec> getForces();
+
+    /*! \brief Copy forces to the GPU memory.
+     *
+     *  \param[in] h_f           Forces in the host memory.
+     *  \param[in] atomLocality  Locality of the particles to copy.
+     */
+    void copyForcesToGpu(gmx::ArrayRef<const gmx::RVec> h_f, AtomLocality atomLocality);
+
+    /*! \brief Clear forces in the GPU memory.
+     *
+     *  \param[in] atomLocality  Locality of the particles to clear.
+     *  \param[in] dependency    Dependency event for this operation.
+     */
+    void clearForcesOnGpu(AtomLocality atomLocality, GpuEventSynchronizer* dependency);
+
+    /*! \brief Get the event synchronizer for the forces ready on device.
+     *
+     *  Returns either of the event synchronizers, depending on the offload scenario
+     *  for the current simulation timestep:
+     *  1. The forces are copied to the device (when GPU buffer ops are off)
+     *  2. The forces are reduced on the device (GPU buffer ops are on)
+     *
+     *  \param[in] stepWork        Step workload flags
+     *  \param[in] simulationWork  Simulation workload flags
+     *
+     *  \returns  The event to synchronize the stream that consumes forces on device.
+     */
+    GpuEventSynchronizer* getLocalForcesReadyOnDeviceEvent(StepWorkload       stepWork,
+                                                           SimulationWorkload simulationWork);
+
+    /*! \brief Getter for the event synchronizer for the forces are reduced on the GPU.
+     *
+     *  \param[in] atomLocality      Locality of the particles to wait for.
+     *  \returns                     The event to mark when forces are reduced on the GPU.
+     */
+    GpuEventSynchronizer* fReducedOnDevice(AtomLocality atomLocality);
+
+    /*! \brief Getter for the event synchronizer for the forces are ready on the GPU.
+     *
+     *  \param[in] atomLocality      Locality of the particles to wait for.
+     *  \returns                     The event to mark when forces are ready on the GPU.
+     */
+    GpuEventSynchronizer* fReadyOnDevice(AtomLocality atomLocality);
+
+    /*! \brief Copy forces from the GPU memory.
+     *
+     *  \param[in] h_f           Forces buffer in the host memory.
+     *  \param[in] atomLocality  Locality of the particles to copy.
+     */
+    void copyForcesFromGpu(gmx::ArrayRef<gmx::RVec> h_f, AtomLocality atomLocality);
+
+    /*! \brief Wait until forces are available on the host.
+     *
+     *  \param[in] atomLocality  Locality of the particles to wait for.
+     */
+    void waitForcesReadyOnHost(AtomLocality atomLocality);
+
+    /*! \brief Getter for the update stream.
+     *
+     *  \todo This is temporary here, until the management of this stream is taken over.
+     *
+     *  \returns The device command stream to use in update-constraints.
+     */
+    const DeviceStream* getUpdateStream();
+
+    /*! \brief Getter for the number of local atoms.
+     *
+     *  \returns The number of local atoms.
+     */
+    int numAtomsLocal() const;
+
+    /*! \brief Getter for the total number of atoms.
+     *
+     *  \returns The total number of atoms.
+     */
+    int numAtomsAll() const;
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl_;
+    GMX_DISALLOW_COPY_AND_ASSIGN(StatePropagatorDataGpu);
+};
+
+} // namespace gmx
+
+#endif // GMX_MDTYPES_STATE_PROPAGATOR_DATA_GPU_H
diff --git a/src/include/gromacs/mdtypes/state_propagator_data_gpu_impl.h b/src/include/gromacs/mdtypes/state_propagator_data_gpu_impl.h
new file mode 100644 (file)
index 0000000..dbe435d
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief Declaration of low-level functions and fields of GPU state propagator object.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_mdtypes
+ */
+#ifndef GMX_MDTYPES_STATE_PROPAGATOR_DATA_GPU_IMPL_H
+#define GMX_MDTYPES_STATE_PROPAGATOR_DATA_GPU_IMPL_H
+
+#include "gmxpre.h"
+
+#include "config.h"
+
+#include <memory>
+
+#include "gromacs/gpu_utils/devicebuffer.h"
+#include "gromacs/gpu_utils/gpueventsynchronizer.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/state_propagator_data_gpu.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+struct gmx_wallcycle;
+
+namespace gmx
+{
+
+class StatePropagatorDataGpu::Impl
+{
+public:
+    Impl();
+
+
+    /*! \brief Constructor
+     *
+     * The buffers are reallocated only at the reinit call, the padding is
+     * used there for the coordinates buffer. It is needed for PME and added at
+     * the end of the buffer. It is assumed that if the rank has PME duties on the
+     * GPU, all coordinates are copied to the GPU and hence, for this rank, the
+     * coordinates buffer is not split into local and non-local ranges. For other
+     * ranks, the padding size is zero. This works because only one rank ever does
+     * PME work on the GPU, and if that rank also does PP work that is the only
+     * rank. So all coordinates are always transferred.
+     *
+     * In OpenCL, only pmeStream is used since it is the only stream created in
+     * PME context. The local and non-local streams are only needed when buffer
+     * ops are offloaded. This feature is currently not available in OpenCL and
+     * hence these streams are not set in these builds.
+     *
+     *  \param[in] deviceStreamManager         Object that owns the DeviceContext and DeviceStreams.
+     *  \param[in] transferKind                H2D/D2H transfer call behavior (synchronous or not).
+     *  \param[in] allocationBlockSizeDivisor  Determines the padding size for coordinates buffer.
+     *  \param[in] wcycle                      Wall cycle counter data.
+     */
+    Impl(const DeviceStreamManager& deviceStreamManager,
+         GpuApiCallBehavior         transferKind,
+         int                        allocationBlockSizeDivisor,
+         gmx_wallcycle*             wcycle);
+
+    /*! \brief Constructor to use in PME-only rank and in tests.
+     *
+     *  This constructor should be used if only a coordinate device buffer should be managed
+     *  using a single stream. Any operation on force or velocity buffer as well as copy of
+     *  non-local coordinates will exit with assertion failure. Note, that the pmeStream can
+     *  not be a nullptr and the constructor will exit with an assertion failure.
+     *
+     *  \todo Currently, unsupported copy operations are blocked by assertion that the stream
+     *        not nullptr. This should be improved.
+     *
+     *  \param[in] pmeStream       Device PME stream, nullptr is not allowed.
+     *  \param[in] deviceContext   Device context, nullptr allowed for non-OpenCL builds.
+     *  \param[in] transferKind    H2D/D2H transfer call behavior (synchronous or not).
+     *  \param[in] allocationBlockSizeDivisor  Determines the padding size for coordinates buffer.
+     *  \param[in] wcycle          Wall cycle counter data.
+     */
+    Impl(const DeviceStream*  pmeStream,
+         const DeviceContext& deviceContext,
+         GpuApiCallBehavior   transferKind,
+         int                  allocationBlockSizeDivisor,
+         gmx_wallcycle*       wcycle);
+
+    ~Impl();
+
+
+    /*! \brief Set the ranges for local and non-local atoms and reallocates buffers.
+     *
+     * Reallocates coordinate, velocities and force buffers on the device.
+     *
+     * \note
+     * The coordinates buffer is (re)allocated, when required by PME, with a padding,
+     * the size of which is set by the constructor. The padding region clearing kernel
+     * is scheduled in the \p pmeStream_ (unlike the coordinates H2D) as only the PME
+     * task uses this padding area.
+     *
+     * \note
+     * The force buffer is cleared if its size increases, so that previously unused
+     * memory is cleared before forces are accumulated.
+     *
+     *  \param[in] numAtomsLocal  Number of atoms in local domain.
+     *  \param[in] numAtomsAll    Total number of atoms to handle.
+     */
+    void reinit(int numAtomsLocal, int numAtomsAll);
+
+    /*! \brief Returns the range of atoms to be copied based on the copy type (all, local or non-local).
+     *
+     * \todo There are at least three versions of the function with this functionality in the code:
+     *       this one and two more in NBNXM. These should be unified in a shape of a general function
+     *       in DD.
+     *
+     * \param[in]  atomLocality    If all, local or non-local ranges are needed.
+     *
+     * \returns Tuple, containing the index of the first atom in the range and the total number of atoms in the range.
+     */
+    std::tuple<int, int> getAtomRangesFromAtomLocality(AtomLocality atomLocality) const;
+
+
+    /*! \brief Get the positions buffer on the GPU.
+     *
+     *  \returns GPU positions buffer.
+     */
+    DeviceBuffer<RVec> getCoordinates();
+
+    /*! \brief Copy positions to the GPU memory.
+     *
+     *  \param[in] h_x           Positions in the host memory.
+     *  \param[in] atomLocality  Locality of the particles to copy.
+     */
+    void copyCoordinatesToGpu(gmx::ArrayRef<const gmx::RVec> h_x, AtomLocality atomLocality);
+
+    /*! \brief Get the event synchronizer of the coordinates ready for the consumption on the device.
+     *
+     * Returns the event synchronizer which indicates that the coordinates are ready for the
+     * consumption on the device. Takes into account that the producer may be different.
+     *
+     * If the update is offloaded, and the current step is not a DD/search step, the returned
+     * synchronizer indicates the completion of GPU update-constraint kernels. Otherwise, on search
+     * steps and if update is not offloaded, the coordinates are provided by the H2D copy and the
+     * returned synchronizer indicates that the copy is complete.
+     *
+     *  \param[in] atomLocality    Locality of the particles to wait for.
+     *  \param[in] simulationWork  The simulation lifetime flags.
+     *  \param[in] stepWork        The step lifetime flags.
+     *  \param[in] gpuCoordinateHaloLaunched Event recorded when GPU coordinate halo has been launched.
+     *
+     *  \returns  The event to synchronize the stream that consumes coordinates on device.
+     */
+    GpuEventSynchronizer* getCoordinatesReadyOnDeviceEvent(AtomLocality              atomLocality,
+                                                           const SimulationWorkload& simulationWork,
+                                                           const StepWorkload&       stepWork,
+                                                           GpuEventSynchronizer* gpuCoordinateHaloLaunched = nullptr);
+
+    /*! \brief Blocking wait until coordinates are copied to the device.
+     *
+     * Synchronizes the stream in which the copy was executed.
+     *
+     *  \param[in] atomLocality  Locality of the particles to wait for.
+     */
+    void waitCoordinatesCopiedToDevice(AtomLocality atomLocality);
+
+    /*! \brief Setter for the event synchronizer for the update is done on th GPU
+     *
+     *  \param[in] xUpdatedOnDeviceEvent  The event to synchronize the stream coordinates wre updated on device.
+     */
+    void setXUpdatedOnDeviceEvent(GpuEventSynchronizer* xUpdatedOnDeviceEvent);
+
+    /*! \brief Copy positions from the GPU memory, with an optional explicit dependency.
+     *
+     *  \param[in] h_x           Positions buffer in the host memory.
+     *  \param[in] atomLocality  Locality of the particles to copy.
+     *  \param[in] dependency    Dependency event for this operation.
+     */
+    void copyCoordinatesFromGpu(gmx::ArrayRef<gmx::RVec> h_x,
+                                AtomLocality             atomLocality,
+                                GpuEventSynchronizer*    dependency = nullptr);
+
+    /*! \brief Wait until coordinates are available on the host.
+     *
+     *  \param[in] atomLocality  Locality of the particles to wait for.
+     */
+    void waitCoordinatesReadyOnHost(AtomLocality atomLocality);
+
+
+    /*! \brief Get the velocities buffer on the GPU.
+     *
+     *  \returns GPU velocities buffer.
+     */
+    DeviceBuffer<RVec> getVelocities();
+
+    /*! \brief Copy velocities to the GPU memory.
+     *
+     *  \param[in] h_v           Velocities in the host memory.
+     *  \param[in] atomLocality  Locality of the particles to copy.
+     */
+    void copyVelocitiesToGpu(gmx::ArrayRef<const gmx::RVec> h_v, AtomLocality atomLocality);
+
+    /*! \brief Copy velocities from the GPU memory.
+     *
+     *  \param[in] h_v           Velocities buffer in the host memory.
+     *  \param[in] atomLocality  Locality of the particles to copy.
+     */
+    void copyVelocitiesFromGpu(gmx::ArrayRef<gmx::RVec> h_v, AtomLocality atomLocality);
+
+    /*! \brief Wait until velocities are available on the host.
+     *
+     *  \param[in] atomLocality  Locality of the particles to wait for.
+     */
+    void waitVelocitiesReadyOnHost(AtomLocality atomLocality);
+
+
+    /*! \brief Get the force buffer on the GPU.
+     *
+     *  \returns GPU force buffer.
+     */
+    DeviceBuffer<RVec> getForces();
+
+    /*! \brief Copy forces to the GPU memory.
+     *
+     *  \param[in] h_f           Forces in the host memory.
+     *  \param[in] atomLocality  Locality of the particles to copy.
+     */
+    void copyForcesToGpu(gmx::ArrayRef<const gmx::RVec> h_f, AtomLocality atomLocality);
+
+    /*! \brief Clear forces in the GPU memory.
+     *
+     *  \param[in] atomLocality  Locality of the particles to clear.
+     *  \param[in] dependency    Dependency event for this operation.
+     */
+    void clearForcesOnGpu(AtomLocality atomLocality, GpuEventSynchronizer* dependency);
+
+    /*! \brief Get the event synchronizer for the forces ready on device.
+     *
+     *  Returns either of the event synchronizers, depending on the offload scenario
+     *  for the current simulation timestep:
+     *  1. The forces are copied to the device (when GPU buffer ops are off)
+     *  2. The forces are reduced on the device (GPU buffer ops are on)
+     *
+     *  \param[in] stepWork        Step workload flags
+     *  \param[in] simulationWork  Simulation workload flags
+     *
+     *  \returns  The event to synchronize the stream that consumes forces on device.
+     */
+    GpuEventSynchronizer* getLocalForcesReadyOnDeviceEvent(StepWorkload       stepWork,
+                                                           SimulationWorkload simulationWork);
+
+    /*! \brief Getter for the event synchronizer for when forces are reduced on the GPU.
+     *
+     *  \param[in] atomLocality      Locality of the particles to wait for.
+     *  \returns                     The event to mark when forces are reduced on the GPU.
+     */
+    GpuEventSynchronizer* fReducedOnDevice(AtomLocality atomLocality);
+
+    /*! \brief Getter for the event synchronizer for the forces are ready for GPU update.
+     *
+     *  \param[in] atomLocality      Locality of the particles to wait for.
+     *  \returns                     The event to mark when forces are ready for GPU update.
+     */
+    GpuEventSynchronizer* fReadyOnDevice(AtomLocality atomLocality);
+
+    /*! \brief Copy forces from the GPU memory.
+     *
+     *  \param[in] h_f           Forces buffer in the host memory.
+     *  \param[in] atomLocality  Locality of the particles to copy.
+     */
+    void copyForcesFromGpu(gmx::ArrayRef<gmx::RVec> h_f, AtomLocality atomLocality);
+
+    /*! \brief Wait until forces are available on the host.
+     *
+     *  \param[in] atomLocality  Locality of the particles to wait for.
+     */
+    void waitForcesReadyOnHost(AtomLocality atomLocality);
+
+    /*! \brief Getter for the update stream.
+     *
+     *  \todo This is temporary here, until the management of this stream is taken over.
+     *
+     *  \returns The device command stream to use in update-constraints.
+     */
+    const DeviceStream* getUpdateStream();
+
+    /*! \brief Getter for the number of local atoms.
+     *
+     *  \returns The number of local atoms.
+     */
+    int numAtomsLocal() const;
+
+    /*! \brief Getter for the total number of atoms.
+     *
+     *  \returns The total number of atoms.
+     */
+    int numAtomsAll() const;
+
+private:
+    //! GPU PME stream.
+    const DeviceStream* pmeStream_;
+    //! GPU NBNXM local stream.
+    const DeviceStream* localStream_;
+    //! GPU NBNXM non-local stream.
+    const DeviceStream* nonLocalStream_;
+    //! GPU Update-constraints stream.
+    const DeviceStream* updateStream_;
+
+    // Streams to use for coordinates H2D and D2H copies (one event for each atom locality)
+    EnumerationArray<AtomLocality, const DeviceStream*> xCopyStreams_ = { { nullptr } };
+    // Streams to use for velocities H2D and D2H copies (one event for each atom locality)
+    EnumerationArray<AtomLocality, const DeviceStream*> vCopyStreams_ = { { nullptr } };
+    // Streams to use for forces H2D and D2H copies (one event for each atom locality)
+    EnumerationArray<AtomLocality, const DeviceStream*> fCopyStreams_ = { { nullptr } };
+    // Streams internal to this module
+    std::unique_ptr<DeviceStream> copyInStream_;
+    std::unique_ptr<DeviceStream> memsetStream_;
+
+    /*! \brief An array of events that indicate H2D copy is complete (one event for each atom locality)
+     *
+     * \todo Reconsider naming. It should be xCopiedToDevice or xH2DCopyComplete, etc.
+     */
+    EnumerationArray<AtomLocality, GpuEventSynchronizer> xReadyOnDevice_;
+    //! A pointer to an event that the coordinates are ready after update-constraints execution
+    GpuEventSynchronizer* xUpdatedOnDeviceEvent_ = nullptr;
+    //! An array of events that indicate D2H copy of coordinates is complete (one event for each atom locality)
+    EnumerationArray<AtomLocality, GpuEventSynchronizer> xReadyOnHost_;
+
+    //! An array of events that indicate D2H copy of velocities is complete (one event for each atom locality)
+    EnumerationArray<AtomLocality, GpuEventSynchronizer> vReadyOnHost_;
+
+    //! An array of events that indicate H2D copy of forces is complete (one event for each atom locality)
+    EnumerationArray<AtomLocality, GpuEventSynchronizer> fReadyOnDevice_;
+    //! An array of events that indicate the forces were reduced on the GPU (one event for each atom locality)
+    EnumerationArray<AtomLocality, GpuEventSynchronizer> fReducedOnDevice_;
+    //! An array of events that indicate D2H copy of forces is complete (one event for each atom locality)
+    EnumerationArray<AtomLocality, GpuEventSynchronizer> fReadyOnHost_;
+
+    //! GPU context (for OpenCL builds)
+    const DeviceContext& deviceContext_;
+    //! Default GPU calls behavior
+    GpuApiCallBehavior transferKind_ = GpuApiCallBehavior::Async;
+    //! Required minimum divisor of the allocation size of the coordinates buffer
+    int allocationBlockSizeDivisor_ = 0;
+
+    //! Number of local atoms
+    int numAtomsLocal_ = -1;
+    //! Total number of atoms
+    int numAtomsAll_ = -1;
+
+    //! Device positions buffer
+    DeviceBuffer<RVec> d_x_;
+    //! Number of particles saved in the positions buffer
+    int d_xSize_ = -1;
+    //! Allocation size for the positions buffer
+    int d_xCapacity_ = -1;
+
+    //! Device velocities buffer
+    DeviceBuffer<RVec> d_v_;
+    //! Number of particles saved in the velocities buffer
+    int d_vSize_ = -1;
+    //! Allocation size for the velocities buffer
+    int d_vCapacity_ = -1;
+
+    //! Device force buffer
+    DeviceBuffer<RVec> d_f_;
+    //! Number of particles saved in the force buffer
+    int d_fSize_ = -1;
+    //! Allocation size for the force buffer
+    int d_fCapacity_ = -1;
+
+    //! \brief Pointer to wallcycle structure.
+    gmx_wallcycle* wcycle_;
+
+    /*! \brief Performs the copy of data from host to device buffer.
+     *
+     * \todo Template on locality.
+     *
+     *  \param[out] d_data         Device-side buffer.
+     *  \param[in]  h_data         Host-side buffer.
+     *  \param[in]  dataSize       Device-side data allocation size.
+     *  \param[in]  atomLocality   If all, local or non-local ranges should be copied.
+     *  \param[in]  deviceStream   GPU stream to execute copy in.
+     */
+    void copyToDevice(DeviceBuffer<RVec>             d_data,
+                      gmx::ArrayRef<const gmx::RVec> h_data,
+                      int                            dataSize,
+                      AtomLocality                   atomLocality,
+                      const DeviceStream&            deviceStream);
+
+    /*! \brief Performs the copy of data from device to host buffer.
+     *
+     *  \param[out] h_data         Host-side buffer.
+     *  \param[in]  d_data         Device-side buffer.
+     *  \param[in]  dataSize       Device-side data allocation size.
+     *  \param[in]  atomLocality   If all, local or non-local ranges should be copied.
+     *  \param[in]  deviceStream   GPU stream to execute copy in.
+     */
+    void copyFromDevice(gmx::ArrayRef<gmx::RVec> h_data,
+                        DeviceBuffer<RVec>       d_data,
+                        int                      dataSize,
+                        AtomLocality             atomLocality,
+                        const DeviceStream&      deviceStream);
+
+    /*! \brief Performs the clearing of data in device buffer.
+     *
+     * \todo Template on locality.
+     *
+     *  \param[out] d_data         Device-side buffer.
+     *  \param[in]  dataSize       Device-side data allocation size.
+     *  \param[in]  atomLocality   If all, local or non-local ranges should be cleared.
+     *  \param[in]  deviceStream   GPU stream to execute copy in.
+     */
+    void clearOnDevice(DeviceBuffer<RVec>  d_data,
+                       int                 dataSize,
+                       AtomLocality        atomLocality,
+                       const DeviceStream& deviceStream) const;
+};
+
+} // namespace gmx
+
+#endif // GMX_MDTYPES_STATE_PROPAGATOR_DATA_GPU_IMPL_H
diff --git a/src/include/gromacs/mdtypes/swaphistory.h b/src/include/gromacs/mdtypes/swaphistory.h
new file mode 100644 (file)
index 0000000..8dabf20
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * This file contains data types containing ioin/water position exchange
+ * data to be stored in the checkpoint file.
+ */
+
+#ifndef GMX_MDLIB_SWAPHISTORY_H
+#define GMX_MDLIB_SWAPHISTORY_H
+
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+enum class Domain : int;
+enum class ChannelHistory : int;
+/* History of an ion type used in position swapping
+ */
+struct swapstateIons_t
+{
+    gmx::EnumerationArray<Compartment, int> nMolReq; // Requested # of molecules per compartment
+    gmx::EnumerationArray<Compartment, int*> nMolReq_p; // Pointer to this data (for checkpoint writing)
+    gmx::EnumerationArray<Compartment, int>  inflow_net;   // Flux determined from the # of swaps
+    gmx::EnumerationArray<Compartment, int*> inflow_net_p; // Pointer to this data
+    gmx::EnumerationArray<Compartment, int*> nMolPast;   // Array with nAverage entries for history
+    gmx::EnumerationArray<Compartment, int*> nMolPast_p; // Pointer points to the first entry only
+
+    // Channel flux detection, this is counting only and has no influence on whether swaps are performed or not:                                                                 */
+    gmx::EnumerationArray<Channel, int>  fluxfromAtoB;   // Flux determined from the split cylinders
+    gmx::EnumerationArray<Channel, int*> fluxfromAtoB_p; // Pointer to this data
+    int                                  nMol; // Number of molecules, size of the following arrays
+    Domain*                              comp_from;     // Ion came from which compartment?
+    ChannelHistory*                      channel_label; // Through which channel did this ion pass?
+};
+
+/* Position swapping state
+ *
+ * To also make multimeric channel proteins whole, we save the last whole configuration
+ * of the channels in the checkpoint file. If we have no checkpoint file, we assume
+ * that the starting configuration has the correct PBC representation after making the
+ * individual molecules whole
+ *
+ * \todo move out of this file to ObservablesHistory
+ *
+ */
+typedef struct swaphistory_t
+{
+    SwapType eSwapCoords; // Swapping along x, y, or z-direction?
+    int      nIonTypes;   // Number of ion types, this is the size of the following arrays
+    int  nAverage; // Use average over this many swap attempt steps when determining the ion counts
+    int  fluxleak; // Ions not going through any channel (bad!)
+    int* fluxleak_p;                         // Pointer to this data
+    bool bFromCpt;                           // Did we start from a checkpoint file?
+    gmx::EnumerationArray<Channel, int> nat; // Size of xc_old_whole, i.e. the number of atoms in each channel
+    gmx::EnumerationArray<Channel, rvec*> xc_old_whole; // Last known whole positions of the two channels (important for multimeric ch.!)
+    gmx::EnumerationArray<Channel, rvec**> xc_old_whole_p; // Pointer to these positions
+    swapstateIons_t*                       ionType;        // History information for one ion type
+} swaphistory_t;
+
+#endif
diff --git a/src/include/gromacs/mdtypes/threaded_force_buffer.h b/src/include/gromacs/mdtypes/threaded_force_buffer.h
new file mode 100644 (file)
index 0000000..d6b948c
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief This file contains the declaration of ThreadForceBuffer and ThreadedForceBuffer.
+ *
+ * These classes provides thread-local force, shift force and energy buffers
+ * for kernels. These kernels can then run completely independently on
+ * multiple threads. Their output can be reduced thread-parallel afterwards.
+ *
+ * Usage:
+ *
+ * At domain decomposition time:
+ * Each thread calls: ThreadForceBuffer.resizeBufferAndClearMask()
+ * Each thread calls: ThreadForceBuffer.addAtomToMask() for all atoms used in the buffer
+ * Each thread calls: ThreadForceBuffer.processMask()
+ * After that ThreadedForceBuffer.setupReduction() is called
+ *
+ * At force computation time:
+ * Each thread calls: ThreadForceBuffer.clearForcesAndEnergies().
+ * Each thread can then accumulate forces and energies into the buffers in ThreadForceBuffer.
+ * After that ThreadedForceBuffer.reduce() is called for thread-parallel reduction.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup mdtypes
+ */
+#ifndef GMX_MDTYPES_THREADED_FORCE_BUFFER_H
+#define GMX_MDTYPES_THREADED_FORCE_BUFFER_H
+
+#include <memory>
+
+#include "gromacs/math/arrayrefwithpadding.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/enerdata.h"
+#include "gromacs/mdtypes/simulation_workload.h"
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/bitmask.h"
+#include "gromacs/utility/classhelpers.h"
+
+namespace gmx
+{
+
+class ForceWithShiftForces;
+class StepWorkload;
+
+/*! \internal
+ * \brief Object that holds force and energies buffers plus a mask for a thread
+ *
+ * \tparam ForceBufferElementType  The type for components of the normal force buffer: rvec or rvec4
+ */
+template<typename ForceBufferElementType>
+class ThreadForceBuffer
+{
+public:
+    /* We reduce the force array in blocks of 2^5 atoms. This is large enough
+     * to not cause overhead and 32*sizeof(rvec) is a multiple of the cache-line
+     * size on all systems.
+     */
+    //! The log2 of the reduction block size
+    static constexpr int s_numReductionBlockBits = 5;
+    //! Force buffer block size in atoms
+    static constexpr int s_reductionBlockSize = (1 << s_numReductionBlockBits);
+
+    /*! \brief Constructor
+     * \param[in] threadIndex  The index of the thread that will fill the buffers in this object
+     * \param[in] useEnergyTerms   Whether the list of energy terms will be used
+     * \param[in] numEnergyGroups  The number of non-bonded energy groups
+     */
+    ThreadForceBuffer(int threadIndex, bool useEnergyTerms, int numEnergyGroups);
+
+    //! Resizes the buffer to \p numAtoms and clears the mask
+    void resizeBufferAndClearMask(int numAtoms);
+
+    //! Adds atom with index \p atomIndex for reduction
+    void addAtomToMask(const int atomIndex)
+    {
+        bitmask_set_bit(&reductionMask_[atomIndex >> s_numReductionBlockBits], threadIndex_);
+    }
+
+    void processMask();
+
+    //! Returns the size of the force buffer in number of atoms
+    index size() const { return numAtoms_; }
+
+    //! Clears all force and energy buffers
+    void clearForcesAndEnergies();
+
+    //! Returns an array reference to the force buffer which is aligned for SIMD access
+    ArrayRef<ForceBufferElementType> forceBuffer()
+    {
+        return ArrayRef<ForceBufferElementType>(
+                reinterpret_cast<ForceBufferElementType*>(forceBuffer_.data()),
+                reinterpret_cast<ForceBufferElementType*>(forceBuffer_.data()) + numAtoms_);
+    }
+
+    /*! \brief Returns an array reference with padding to the force buffer which is aligned for SIMD access
+     *
+     * For RVec there is padding of one real for 4-wide SIMD access.
+     * For both RVec and rvec4 there is padding up to the block size for use in ThreadedForceBuffer.
+     */
+    ArrayRefWithPadding<ForceBufferElementType> forceBufferWithPadding()
+    {
+        return ArrayRefWithPadding<ForceBufferElementType>(
+                reinterpret_cast<ForceBufferElementType*>(forceBuffer_.data()),
+                reinterpret_cast<ForceBufferElementType*>(forceBuffer_.data()) + numAtoms_,
+                reinterpret_cast<ForceBufferElementType*>(forceBuffer_.data() + forceBuffer_.size()));
+    }
+
+    //! Returns a view of the shift force buffer
+    ArrayRef<RVec> shiftForces() { return shiftForces_; }
+
+    //! Returns a view of the energy terms, size F_NRE
+    ArrayRef<real> energyTerms() { return energyTerms_; }
+
+    //! Returns a reference to the energy group pair energies
+    gmx_grppairener_t& groupPairEnergies() { return groupPairEnergies_; }
+
+    //! Returns a reference to the dvdl terms
+    EnumerationArray<FreeEnergyPerturbationCouplingType, real>& dvdl() { return dvdl_; }
+
+    //! Returns a const view to the reduction masks
+    ArrayRef<const gmx_bitmask_t> reductionMask() const { return reductionMask_; }
+
+private:
+    //! Force array buffer, aligned to enable aligned SIMD access
+    std::vector<real, AlignedAllocator<real>> forceBuffer_;
+    //! Mask for marking which parts of f are filled, working array for constructing mask in setupReduction()
+    std::vector<gmx_bitmask_t> reductionMask_;
+    //! Index to touched blocks
+    std::vector<int> usedBlockIndices_;
+    //! The index of our thread
+    int threadIndex_;
+    //! The number of atoms in the buffer
+    int numAtoms_ = 0;
+
+    //! Shift force array, size c_numShiftVectors
+    std::vector<RVec> shiftForces_;
+    //! Energy array, can be empty
+    std::vector<real> energyTerms_;
+    //! Group pair energy data for pairs
+    gmx_grppairener_t groupPairEnergies_;
+    //! Free-energy dV/dl output
+    gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, real> dvdl_;
+
+    // Disallow copy and assign, remove this we we get rid of f_
+    GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(ThreadForceBuffer);
+};
+
+/*! \internal
+ * \brief Class for accumulating and reducing forces and energies on threads in parallel
+ *
+ * \tparam ForceBufferElementType  The type for components of the normal force buffer: rvec or rvec4
+ */
+template<typename ForceBufferElementType>
+class ThreadedForceBuffer
+{
+public:
+    /*! \brief Constructor
+     * \param[in] numThreads       The number of threads that will use the buffers and reduce
+     * \param[in] useEnergyTerms   Whether the list of energy terms will be used
+     * \param[in] numEnergyGroups  The number of non-bonded energy groups
+     */
+    ThreadedForceBuffer(int numThreads, bool useEnergyTerms, int numEnergyGroups);
+
+    //! Returns the number of thread buffers
+    int numThreadBuffers() const { return threadForceBuffers_.size(); }
+
+    //! Returns a reference to the buffer object for the thread with index \p bufferIndex
+    ThreadForceBuffer<ForceBufferElementType>& threadForceBuffer(int bufferIndex)
+    {
+        return *threadForceBuffers_[bufferIndex];
+    }
+
+    //! Sets up the reduction, should be called after generating the masks on each thread
+    void setupReduction();
+
+    /*! \brief Reduces forces and energies, as requested by \p stepWork
+     *
+     * The reduction of all output starts at the output from thread \p reductionBeginIndex,
+     * except for the normal force buffer, which always starts at 0.
+     *
+     * Buffers that will not be used as indicated by the flags in \p stepWork
+     * are allowed to be nullptr or empty.
+     */
+    void reduce(gmx::ForceWithShiftForces* forceWithShiftForces,
+                real*                      ener,
+                gmx_grppairener_t*         grpp,
+                gmx::ArrayRef<real>        dvdl,
+                const gmx::StepWorkload&   stepWork,
+                int                        reductionBeginIndex);
+
+private:
+    //! Whether the energy buffer is used
+    bool useEnergyTerms_;
+    //! Force/energy data per thread, size nthreads, stored in unique_ptr to allow thread local allocation
+    std::vector<std::unique_ptr<ThreadForceBuffer<ForceBufferElementType>>> threadForceBuffers_;
+    //! Indices of blocks that are used, i.e. have force contributions.
+    std::vector<int> usedBlockIndices_;
+    //! Mask array, one element corresponds to a block of reduction_block_size atoms of the force array, bit corresponding to thread indices set if a thread writes to that block
+    std::vector<gmx_bitmask_t> reductionMask_;
+    //! The number of atoms forces are computed for
+    int numAtomsForce_ = 0;
+
+    // Disallow copies to avoid sub-optimal ownership of allocated memory
+    GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(ThreadedForceBuffer);
+};
+
+// Instantiate for RVec
+extern template class ThreadForceBuffer<RVec>;
+extern template class ThreadedForceBuffer<RVec>;
+
+// Instantiate for rvec4
+extern template class ThreadForceBuffer<rvec4>;
+extern template class ThreadedForceBuffer<rvec4>;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/mimic/communicator.h b/src/include/gromacs/mimic/communicator.h
new file mode 100644 (file)
index 0000000..ea7f18b
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_MIMIC_COMMUNICATOR_H
+#define GMX_MIMIC_COMMUNICATOR_H
+
+#include "gromacs/mdlib/constr.h"
+#include "gromacs/topology/topology.h"
+#include "gromacs/utility/futil.h"
+
+namespace gmx
+{
+
+template<class T>
+class ArrayRef;
+
+/**
+ * \inlibraryapi
+ * \internal \brief
+ * Class-wrapper around MiMiC communication library
+ * It uses GROMACS' unit conversion to switch from GROMACS' units to a.u.
+ *
+ * \author Viacheslav Bolnykh <v.bolnykh@hpc-leap.eu>
+ * \ingroup module_mimic
+ */
+class MimicCommunicator
+{
+
+public:
+    /*! \brief
+     * Initializes the communicator
+     */
+    static void init();
+
+    /*! \brief
+     * Sends the data needed for MiMiC initialization
+     *
+     * That includes number of atoms, element numbers, charges, masses,
+     * maximal order of multipoles (0 for point-charge forcefields),
+     * number of molecules, number of atoms per each molecule,
+     * bond constraints data
+     *
+     * @param mtop global topology data
+     * @param coords coordinates of all atoms
+     */
+    static void sendInitData(gmx_mtop_t* mtop, ArrayRef<const RVec> coords);
+
+    /*! \brief
+     * Gets the number of MD steps to perform from MiMiC
+     *
+     * @return nsteps the number of MD steps to perform
+     */
+    static int64_t getStepNumber();
+
+    /*! \brief
+     * Receive and array of updated atomic coordinates from MiMiC
+     *
+     * @param x array of coordinates to fill
+     * @param natoms number of atoms in the system
+     */
+    static void getCoords(ArrayRef<RVec> x, int natoms);
+
+    /*! \brief
+     * Send the potential energy value to MiMiC
+     *
+     * @param energy energy value to send
+     */
+    static void sendEnergies(real energy);
+
+    /*! \brief
+     * Send classical forces acting on all atoms in the system
+     * to MiMiC.
+     *
+     * @param forces array of forces to send
+     * @param natoms number of atoms in the system
+     */
+    static void sendForces(ArrayRef<gmx::RVec> forces, int natoms);
+
+    /*! \brief
+     * Finish communications and disconnect from the server
+     */
+    static void finalize();
+};
+
+} // namespace gmx
+
+#endif // GMX_MIMIC_COMMUNICATOR_H
diff --git a/src/include/gromacs/mimic/utilities.h b/src/include/gromacs/mimic/utilities.h
new file mode 100644 (file)
index 0000000..8c7a4d9
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief Provides utility functions for MiMiC QM/MM
+ * \inlibraryapi
+ *
+ * \author Viacheslav Bolnykh <v.bolnykh@hpc-leap.eu>
+ * \ingroup module_mimic
+ */
+#ifndef GMX_MIMIC_MIMICUTILS_H
+#define GMX_MIMIC_MIMICUTILS_H
+
+#include <vector>
+
+struct gmx_mtop_t;
+
+/*! \brief Generates the list of QM atoms
+ *
+ * This function generates vector containing global IDs of QM atoms
+ * based on the information stored in the QM/MM group (egcQMMM)
+ *
+ * \param[in]    mtop   Global topology object
+ * \return              The list of global IDs of QM atoms
+ */
+std::vector<int> genQmmmIndices(const gmx_mtop_t& mtop);
+
+#endif // GMX_MIMIC_MIMICUTILS_H
diff --git a/src/include/gromacs/modularsimulator/andersentemperaturecoupling.h b/src/include/gromacs/modularsimulator/andersentemperaturecoupling.h
new file mode 100644 (file)
index 0000000..6cab2ca
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares Andersen temperature coupling for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ */
+
+#ifndef GMX_MODULARSIMULATOR_ANDERSENTHERMOSTAT_H
+#define GMX_MODULARSIMULATOR_ANDERSENTHERMOSTAT_H
+
+#include "gromacs/utility/arrayref.h"
+
+#include "energydata.h"
+#include "modularsimulatorinterfaces.h"
+#include "propagator.h"
+
+struct t_commrec;
+struct t_mdatoms;
+
+namespace gmx
+{
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element implementing the Andersen thermostat
+ *
+ */
+class AndersenTemperatureCoupling final : public ISimulatorElement
+{
+public:
+    //! Constructor
+    AndersenTemperatureCoupling(double               simulationTimestep,
+                                bool                 doMassive,
+                                int64_t              seed,
+                                ArrayRef<const real> referenceTemperature,
+                                ArrayRef<const real> couplingTime,
+                                StatePropagatorData* statePropagatorData,
+                                const MDAtoms*       mdAtoms,
+                                const t_commrec*     cr);
+
+    /*! \brief Register run function for step / time
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! Sanity check at setup time
+    void elementSetup() override;
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    ObservablesReducer*        observablesReducer);
+
+    //! Returns the frequency at which temperature coupling is performed
+    [[nodiscard]] int frequency() const;
+
+private:
+    //! Update the reference temperature
+    static void updateReferenceTemperature(ArrayRef<const real>                temperatures,
+                                           ReferenceTemperatureChangeAlgorithm algorithm);
+
+    //! Whether we're doing massive Andersen thermostatting
+    const bool doMassive_;
+    //! The rate
+    const real randomizationRate_;
+    //! The frequency at which the thermostat is applied
+    const int couplingFrequency_;
+    //! The random seed
+    const int64_t seed_;
+    //! Coupling temperature per group
+    ArrayRef<const real> referenceTemperature_;
+    //! Coupling time per group
+    ArrayRef<const real> couplingTime_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the micro state
+    StatePropagatorData* statePropagatorData_;
+    //! Atom parameters for this domain.
+    const t_mdatoms* mdAtoms_;
+    //! Handles communication.
+    const t_commrec* cr_;
+
+    //! Apply the thermostat at step
+    void apply(Step step);
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_ANDERSENTHERMOSTAT_H
diff --git a/src/include/gromacs/modularsimulator/checkpointhelper.h b/src/include/gromacs/modularsimulator/checkpointhelper.h
new file mode 100644 (file)
index 0000000..80b184f
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the checkpoint helper for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_CHECKPOINTHELPER_H
+#define GMX_MODULARSIMULATOR_CHECKPOINTHELPER_H
+
+#include <map>
+#include <vector>
+
+#include "gromacs/mdlib/checkpointhandler.h"
+#include "gromacs/mdrunutility/handlerestart.h"
+
+#include "modularsimulatorinterfaces.h"
+
+struct gmx_walltime_accounting;
+struct ObservablesHistory;
+
+namespace gmx
+{
+class KeyValueTreeObject;
+class MDLogger;
+class TrajectoryElement;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Checkpoint helper
+ *
+ * The `CheckpointHelper` is responsible to write checkpoints. In the
+ * longer term, it will also be responsible to read checkpoints, but this
+ * is not yet implemented.
+ *
+ * Writing checkpoints is done just before neighbor-searching (NS) steps,
+ * or after the last step. Checkpointing occurs periodically (by default,
+ * every 15 minutes), and needs two NS steps to take effect - on the first
+ * NS step, the checkpoint helper on master rank signals to all other ranks
+ * that checkpointing is about to occur. At the next NS step, the checkpoint
+ * is written. On the last step, checkpointing happens immediately before the
+ * step (no signalling). To be able to react to last step being signalled,
+ * the CheckpointHelper does also implement the `ISimulatorElement` interface,
+ * but does only register a function if the last step has been called. It
+ * should be placed on top of the simulator loop.
+ *
+ * Checkpointing happens at the end of a simulation step, which gives a
+ * straightforward re-entry point at the top of the simulator loop.
+ *
+ * Checkpoint writing is done by passing sub-objects of a
+ * WriteCheckpointDataHolder object to the clients. Checkpoint reading is
+ * done by passing sub-objects of a ReadCheckpointDataHolder object (passed
+ * in from runner level) do the clients.
+ *
+ * \see ReadCheckpointDataHolder
+ * \see WriteCheckpointDataHolder
+ * \see CheckpointData
+ */
+class CheckpointHelper final : public ILastStepSignallerClient, public ISimulatorElement
+{
+public:
+    //! Constructor
+    CheckpointHelper(std::vector<std::tuple<std::string, ICheckpointHelperClient*>>&& clients,
+                     std::unique_ptr<CheckpointHandler> checkpointHandler,
+                     int                                initStep,
+                     TrajectoryElement*                 trajectoryElement,
+                     FILE*                              fplog,
+                     t_commrec*                         cr,
+                     ObservablesHistory*                observablesHistory,
+                     gmx_walltime_accounting*           walltime_accounting,
+                     t_state*                           state_global,
+                     bool                               writeFinalCheckpoint);
+
+    /*! \brief Run checkpointing
+     *
+     * Sets signal and / or performs checkpointing at neighbor searching steps
+     *
+     * \param step  The step number
+     * \param time  The time
+     */
+    void run(Step step, Time time);
+
+    /*! \brief Register run function for step / time
+     *
+     * Performs checkpointing at the last step. This is part of the element call
+     * list, as the checkpoint helper need to be able to react to the last step
+     * being signalled.
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! No element setup needed
+    void elementSetup() override {}
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+private:
+    //! List of checkpoint clients
+    std::vector<std::tuple<std::string, ICheckpointHelperClient*>> clients_;
+
+    //! The checkpoint handler
+    std::unique_ptr<CheckpointHandler> checkpointHandler_;
+
+    //! The first step of the simulation
+    const Step initStep_;
+    //! The last step of the simulation
+    Step lastStep_;
+    //! Whether a checkpoint is written on the last step
+    const bool writeFinalCheckpoint_;
+
+    //! ILastStepSignallerClient implementation
+    std::optional<SignallerCallback> registerLastStepCallback() override;
+
+    //! The actual checkpoint writing function
+    void writeCheckpoint(Step step, Time time);
+
+    //! Pointer to the trajectory element - to use file pointer
+    TrajectoryElement* trajectoryElement_;
+
+    // Access to ISimulator data
+    //! Handles logging.
+    FILE* fplog_;
+    //! Handles communication.
+    t_commrec* cr_;
+    //! History of simulation observables.
+    ObservablesHistory* observablesHistory_;
+    //! Manages wall time accounting.
+    gmx_walltime_accounting* walltime_accounting_;
+    //! Full simulation state (only non-nullptr on master rank).
+    t_state* state_global_;
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Builder for the checkpoint helper
+ */
+class CheckpointHelperBuilder
+{
+public:
+    //! Constructor
+    CheckpointHelperBuilder(std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder,
+                            StartingBehavior                          startingBehavior,
+                            t_commrec*                                cr);
+
+    //! Register checkpointing client
+    void registerClient(ICheckpointHelperClient* client);
+
+    //! Set CheckpointHandler
+    void setCheckpointHandler(std::unique_ptr<CheckpointHandler> checkpointHandler);
+
+    //! Return CheckpointHelper
+    template<typename... Args>
+    std::unique_ptr<CheckpointHelper> build(Args&&... args);
+
+private:
+    //! Map of checkpoint clients
+    std::map<std::string, ICheckpointHelperClient*> clientsMap_;
+    //! Whether we are resetting from checkpoint
+    const bool resetFromCheckpoint_;
+    //! The input checkpoint data
+    std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder_;
+    //! The checkpoint handler
+    std::unique_ptr<CheckpointHandler> checkpointHandler_;
+    //! Handles communication.
+    t_commrec* cr_;
+    //! Whether the builder accepts registrations.
+    ModularSimulatorBuilderState state_;
+};
+
+template<typename... Args>
+std::unique_ptr<CheckpointHelper> CheckpointHelperBuilder::build(Args&&... args)
+{
+    state_ = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
+    // Make sure that we don't have unused entries in checkpoint
+    if (resetFromCheckpoint_)
+    {
+        for (const auto& key : checkpointDataHolder_->keys())
+        {
+            if (clientsMap_.count(key) == 0)
+            {
+                // We have an entry in checkpointDataHolder_ which has no matching client
+                throw CheckpointError("Checkpoint entry " + key + " was not read. This "
+                                      "likely means that you are not using the same algorithm "
+                                      "that was used to create the checkpoint file.");
+            }
+        }
+    }
+
+    std::vector<std::tuple<std::string, ICheckpointHelperClient*>>&& clients = { clientsMap_.begin(),
+                                                                                 clientsMap_.end() };
+    return std::make_unique<CheckpointHelper>(
+            std::move(clients), std::move(checkpointHandler_), std::forward<Args>(args)...);
+}
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_CHECKPOINTHELPER_H
diff --git a/src/include/gromacs/modularsimulator/compositesimulatorelement.h b/src/include/gromacs/modularsimulator/compositesimulatorelement.h
new file mode 100644 (file)
index 0000000..c238974
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the composite element for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+#ifndef GROMACS_MDTYPES_COMPOSITESIMULATORELEMENT_H
+#define GROMACS_MDTYPES_COMPOSITESIMULATORELEMENT_H
+
+#include <vector>
+
+#include "gromacs/compat/pointers.h"
+
+#include "modularsimulatorinterfaces.h"
+
+namespace gmx
+{
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Composite simulator element
+ *
+ * The composite simulator element takes a call list of elements and implements
+ * the ISimulatorElement interface, making a group of elements effectively
+ * behave as one. This simplifies building algorithms.
+ *
+ * The CompositeSimulatorElement can optionally also own the elements, but does
+ * not require this. The owner of a CompositeSimulatorElement object can hence
+ * decide to either pass the ownership to CompositeSimulatorElement, or keep
+ * the ownership (and guarantee that they remain valid during the life time
+ * of the CompositeSimulatorElement object). CompositeSimulatorElement will only
+ * call the setup and teardown methods on the owned elements, thereby avoiding
+ * to call them more than once. Consequently, the owner of the elements not
+ * owned by CompositeSimulatorElement is responsible to call setup and teardown
+ * methods on these elements.
+ */
+class CompositeSimulatorElement final : public ISimulatorElement
+{
+public:
+    //! Constructor
+    explicit CompositeSimulatorElement(std::vector<compat::not_null<ISimulatorElement*>> elementCallList,
+                                       std::vector<std::unique_ptr<ISimulatorElement>> elements,
+                                       int                                             frequency);
+
+    /*! \brief Register run function for step / time
+     *
+     * Lets every member of the composite simulator register run functions
+     * for the given step.
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    /*! \brief Element setup
+     *
+     * Calls the setup functions of the single elements.
+     */
+    void elementSetup() override;
+
+    /*! \brief Element teardown
+     *
+     * Calls the teardown functions of the single elements.
+     */
+    void elementTeardown() override;
+
+private:
+    //! The call list of elements forming the composite element
+    std::vector<compat::not_null<ISimulatorElement*>> elementCallList_;
+    //! List of elements owned by composite element
+    std::vector<std::unique_ptr<ISimulatorElement>> elementOwnershipList_;
+    //! The frequency at which the composite element is running
+    const int frequency_;
+};
+
+} // namespace gmx
+
+#endif // GROMACS_MDTYPES_COMPOSITESIMULATORELEMENT_H
diff --git a/src/include/gromacs/modularsimulator/computeglobalselement.h b/src/include/gromacs/modularsimulator/computeglobalselement.h
new file mode 100644 (file)
index 0000000..a4fba81
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the global reduction element for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_COMPUTEGLOBALSELEMENT_H
+#define GMX_MODULARSIMULATOR_COMPUTEGLOBALSELEMENT_H
+
+#include "gromacs/mdlib/simulationsignal.h"
+#include "gromacs/mdlib/vcm.h"
+
+#include "energydata.h"
+#include "modularsimulatorinterfaces.h"
+#include "statepropagatordata.h"
+#include "topologyholder.h"
+
+struct gmx_global_stat;
+struct gmx_wallcycle;
+struct t_nrnb;
+
+namespace gmx
+{
+class FreeEnergyPerturbationData;
+class LegacySimulatorData;
+class MDAtoms;
+class MDLogger;
+class ObservablesReducer;
+
+//! \addtogroup module_modularsimulator
+//! \{
+
+//! The different global reduction schemes we know about
+enum class ComputeGlobalsAlgorithm
+{
+    LeapFrog,
+    VelocityVerlet
+};
+
+//! The function type allowing to request a check of the number of bonded interactions
+typedef std::function<void()> CheckBondedInteractionsCallback;
+
+/*! \internal
+ * \brief Encapsulate the calls to `compute_globals`
+ *
+ * This element aims at offering an interface to the legacy
+ * implementation which is compatible with the new simulator approach.
+ *
+ * The element comes in 3 (templated) flavors: the leap-frog case, the first
+ * call during a velocity-verlet integrator, and the second call during a
+ * velocity-verlet integrator. In velocity verlet, the state at the beginning
+ * of the step corresponds to
+ *     positions at time t
+ *     velocities at time t - dt/2
+ * The first velocity propagation (+dt/2) therefore actually corresponds to the
+ * previous step, bringing the state to the full timestep at time t. Most global
+ * reductions are made at this point. The second call is needed to correct the
+ * constraint virial after the second propagation of velocities (+dt/2) and of
+ * the positions (+dt).
+ *
+ * \tparam algorithm  The global reduction scheme
+ */
+template<ComputeGlobalsAlgorithm algorithm>
+class ComputeGlobalsElement final : public ISimulatorElement, public IEnergySignallerClient, public ITrajectorySignallerClient
+{
+public:
+    //! Constructor
+    ComputeGlobalsElement(StatePropagatorData*        statePropagatorData,
+                          EnergyData*                 energyData,
+                          FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                          SimulationSignals*          signals,
+                          int                         nstglobalcomm,
+                          FILE*                       fplog,
+                          const MDLogger&             mdlog,
+                          t_commrec*                  cr,
+                          const t_inputrec*           inputrec,
+                          const MDAtoms*              mdAtoms,
+                          t_nrnb*                     nrnb,
+                          gmx_wallcycle*              wcycle,
+                          t_forcerec*                 fr,
+                          const gmx_mtop_t&           global_top,
+                          Constraints*                constr,
+                          ObservablesReducer*         observablesReducer);
+
+    //! Destructor
+    ~ComputeGlobalsElement() override;
+
+    /*! \brief Element setup - first call to compute_globals
+     *
+     */
+    void elementSetup() override;
+
+    /*! \brief Register run function for step / time
+     *
+     * This registers the call to compute_globals when needed.
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     *
+     * \throws std::bad_any_cast  on internal error in VelocityVerlet algorithm builder.
+     * \throws std::bad_alloc  when out of memory.
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    ObservablesReducer*        observablesReducer);
+
+private:
+    //! IEnergySignallerClient implementation
+    std::optional<SignallerCallback> registerEnergyCallback(EnergySignallerEvent event) override;
+    //! ITrajectorySignallerClient implementation
+    std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
+    //! The compute_globals call
+    void compute(Step step, unsigned int flags, SimulationSignaller* signaller, bool useLastBox, bool isInit = false);
+
+    //! Next step at which energy needs to be reduced
+    Step energyReductionStep_;
+    //! Next step at which virial needs to be reduced
+    Step virialReductionStep_;
+
+    //! For VV only, we need to schedule twice per step. This keeps track of the scheduling stage.
+    Step vvSchedulingStep_;
+
+    //! Whether center of mass motion stopping is enabled
+    const bool doStopCM_;
+    //! Number of steps after which center of mass motion is removed
+    int nstcomm_;
+    //! Compute globals communication period
+    int nstglobalcomm_;
+    //! The last (planned) step (only used for LF)
+    const Step lastStep_;
+    //! The initial step (only used for VV)
+    const Step initStep_;
+    //! A dummy signaller (used for setup and VV)
+    std::unique_ptr<SimulationSignaller> nullSignaller_;
+
+    /*! \brief Check that DD doesn't miss bonded interactions
+     *
+     * Domain decomposition could incorrectly miss a bonded
+     * interaction, but checking for that requires a global
+     * communication stage, which does not otherwise happen in DD
+     * code. So we do that alongside the first global energy reduction
+     * after a new DD is made. These variables handle whether the
+     * check happens, and the result it returns.
+     */
+    //! \{
+    int  totalNumberOfBondedInteractions_;
+    bool shouldCheckNumberOfBondedInteractions_;
+    //! \}
+
+    /*! \brief Signal to ComputeGlobalsElement that it should check for DD errors
+     *
+     * Note that this should really be the responsibility of the DD element.
+     * MDLogger, global and local topology are only needed due to the call to
+     * checkNumberOfBondedInteractions(...).
+     *
+     * The DD element should have a single variable which gets reduced, and then
+     * be responsible for the checking after a global reduction has happened.
+     * This would, however, require a new approach for the compute_globals calls,
+     * which is not yet implemented. So for now, we're leaving this here.
+     */
+    void needToCheckNumberOfBondedInteractions();
+
+    //! Global reduction struct
+    gmx_global_stat* gstat_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the microstate
+    StatePropagatorData* statePropagatorData_;
+    //! Pointer to the energy data (needed for the tensors and mu_tot)
+    EnergyData* energyData_;
+    //! Pointer to the free energy perturbation data
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
+
+    //! Center of mass motion removal
+    t_vcm vcm_;
+    //! Signals
+    SimulationSignals* signals_;
+
+    // Access to ISimulator data
+    //! Handles logging.
+    FILE* fplog_;
+    //! Handles logging.
+    const MDLogger& mdlog_;
+    //! Handles communication.
+    t_commrec* cr_;
+    //! Contains user input mdp options.
+    const t_inputrec* inputrec_;
+    //! Full system topology - only needed for checkNumberOfBondedInteractions.
+    const gmx_mtop_t& top_global_;
+    //! Atom parameters for this domain.
+    const MDAtoms* mdAtoms_;
+    //! Handles constraints.
+    Constraints* constr_;
+    //! Manages flop accounting.
+    t_nrnb* nrnb_;
+    //! Manages wall cycle accounting.
+    gmx_wallcycle* wcycle_;
+    //! Parameters for force calculations.
+    t_forcerec* fr_;
+    //! Coordinates reduction for observables
+    ObservablesReducer* observablesReducer_;
+};
+
+//! \}
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_COMPUTEGLOBALSELEMENT_H
diff --git a/src/include/gromacs/modularsimulator/constraintelement.h b/src/include/gromacs/modularsimulator/constraintelement.h
new file mode 100644 (file)
index 0000000..c8600dd
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the constraint element for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_CONSTRAINTELEMENT_H
+#define GMX_MODULARSIMULATOR_CONSTRAINTELEMENT_H
+
+#include "gromacs/mdlib/constr.h"
+
+#include "modularsimulatorinterfaces.h"
+
+namespace gmx
+{
+class Constraints;
+class EnergyData;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
+class ModularSimulatorAlgorithmBuilderHelper;
+class ObservablesReducer;
+class StatePropagatorData;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Constraints element
+ *
+ * The ConstraintsElement is implemented for the position-and-velocity and the
+ * velocity-only case. It does not change the constraint implementation itself,
+ * but uses the current constraints implementation and the data management
+ * introduced with the modular simulator.
+ *
+ * \tparam variable  The constraining variable
+ */
+template<ConstraintVariable variable>
+class ConstraintsElement final :
+    public ISimulatorElement,
+    public IEnergySignallerClient,
+    public ITrajectorySignallerClient,
+    public ILoggingSignallerClient
+{
+public:
+    //! Constructor
+    ConstraintsElement(Constraints*                constr,
+                       StatePropagatorData*        statePropagatorData,
+                       EnergyData*                 energyData,
+                       FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                       bool                        isMaster,
+                       FILE*                       fplog,
+                       const t_inputrec*           inputrec,
+                       const t_mdatoms*            mdAtoms);
+
+    /*! \brief Register constraining function for step / time
+     *
+     * Under LF, this is expected to be run once, constraining positions and velocities
+     * Under VV, this is expected to be run twice, once contraining velocities only,
+     * a second time constraining positions and velocities.
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    /*! \brief Performs inital constraining
+     *  \todo Should this rather happen at grompp time? Right position of this operation is currently
+     *        depending on the integrator algorithm (after domdec, before compute globals...),
+     *        so doing this earlier would be much more stable!
+     */
+    void elementSetup() override;
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    ObservablesReducer*        observablesReducer);
+
+private:
+    //! The actual constraining computation
+    void apply(Step step, bool calculateVirial, bool writeLog, bool writeEnergy);
+
+    //! IEnergySignallerClient implementation
+    std::optional<SignallerCallback> registerEnergyCallback(EnergySignallerEvent event) override;
+    //! ITrajectorySignallerClient implementation
+    std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
+    //! ILoggingSignallerClient implementation
+    std::optional<SignallerCallback> registerLoggingCallback() override;
+
+    //! The next energy calculation step
+    Step nextVirialCalculationStep_;
+    //! The next energy writing step
+    Step nextEnergyWritingStep_;
+    //! The next logging step
+    Step nextLogWritingStep_;
+
+    //! Whether we're master rank
+    const bool isMasterRank_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the micro state
+    StatePropagatorData* statePropagatorData_;
+    //! Pointer to the energy data
+    EnergyData* energyData_;
+    //! Pointer to the free energy perturbation data
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
+
+    // Access to ISimulator data
+    //! Handles constraints.
+    Constraints* constr_;
+    //! Handles logging.
+    FILE* fplog_;
+    //! Contains user input mdp options.
+    const t_inputrec* inputrec_;
+    //! Atom parameters for this domain.
+    const t_mdatoms* mdAtoms_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_CONSTRAINTELEMENT_H
diff --git a/src/include/gromacs/modularsimulator/domdechelper.h b/src/include/gromacs/modularsimulator/domdechelper.h
new file mode 100644 (file)
index 0000000..1023d39
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the domain decomposition helper for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_DOMDECHELPER_H
+#define GMX_MODULARSIMULATOR_DOMDECHELPER_H
+
+#include "modularsimulatorinterfaces.h"
+
+struct gmx_localtop_t;
+struct gmx_wallcycle;
+struct pull_t;
+struct t_commrec;
+struct t_forcerec;
+struct t_inputrec;
+struct t_nrnb;
+
+namespace gmx
+{
+class Constraints;
+class FreeEnergyPerturbationData;
+class ImdSession;
+class MDAtoms;
+class MDLogger;
+class StatePropagatorData;
+class TopologyHolder;
+class VirtualSitesHandler;
+
+//! \addtogroup module_modularsimulator
+//! \{
+
+/*! \internal
+ * \brief Infrastructure element responsible for domain decomposition
+ *
+ * This encapsulates the function call to domain decomposition, which is
+ * important for performance but outside of the current scope of the modular
+ * simulator project. This relies on legacy data structures for the state
+ * and the topology.
+ *
+ * This element does not implement the ISimulatorElement interface, as
+ * the Simulator is calling it explicitly between task queue population
+ * steps. This allows elements to be aware of any changes before
+ * deciding what functionality they need to run.
+ */
+class DomDecHelper final : public INeighborSearchSignallerClient
+{
+public:
+    //! Constructor
+    DomDecHelper(bool                          isVerbose,
+                 int                           verbosePrintInterval,
+                 StatePropagatorData*          statePropagatorData,
+                 TopologyHolder*               topologyHolder,
+                 int                           nstglobalcomm,
+                 FILE*                         fplog,
+                 t_commrec*                    cr,
+                 const MDLogger&               mdlog,
+                 Constraints*                  constr,
+                 const t_inputrec*             inputrec,
+                 MDAtoms*                      mdAtoms,
+                 t_nrnb*                       nrnb,
+                 gmx_wallcycle*                wcycle,
+                 t_forcerec*                   fr,
+                 VirtualSitesHandler*          vsite,
+                 ImdSession*                   imdSession,
+                 pull_t*                       pull_work,
+                 std::vector<DomDecCallback>&& domdecCallbacks);
+
+    /*! \brief Run domain decomposition
+     *
+     * Does domain decomposition partitioning at neighbor searching steps
+     *
+     * \param step  The step number
+     * \param time  The time
+     */
+    void run(Step step, Time time);
+
+    /*! \brief The initial domain decomposition partitioning
+     *
+     */
+    void setup();
+
+private:
+    //! INeighborSearchSignallerClient implementation
+    std::optional<SignallerCallback> registerNSCallback() override;
+
+    //! The next NS step
+    Step nextNSStep_;
+    //! Whether we're being verbose
+    const bool isVerbose_;
+    //! If we're being verbose, how often?
+    const int verbosePrintInterval_;
+    //! The global communication frequency
+    const int nstglobalcomm_;
+
+    //! List of callbacks to inform clients that DD happened
+    std::vector<DomDecCallback> domdecCallbacks_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the micro state
+    StatePropagatorData* statePropagatorData_;
+    //! Pointer to the topology
+    TopologyHolder* topologyHolder_;
+
+    //! Helper function unifying the DD partitioning calls in setup() and run()
+    void partitionSystem(bool           verbose,
+                         bool           isMasterState,
+                         int            nstglobalcomm,
+                         gmx_wallcycle* wcycle,
+                         t_state*       localState,
+                         t_state*       globalState);
+
+    // Access to ISimulator data
+    //! Handles logging.
+    FILE* fplog_;
+    //! Handles communication.
+    t_commrec* cr_;
+    //! Handles logging.
+    const MDLogger& mdlog_;
+    //! Handles constraints.
+    Constraints* constr_;
+    //! Contains user input mdp options.
+    const t_inputrec* inputrec_;
+    //! Atom parameters for this domain.
+    MDAtoms* mdAtoms_;
+    //! Manages flop accounting.
+    t_nrnb* nrnb_;
+    //! Manages wall cycle accounting.
+    gmx_wallcycle* wcycle_;
+    //! Parameters for force calculations.
+    t_forcerec* fr_;
+    //! Handles virtual sites.
+    VirtualSitesHandler* vsite_;
+    //! The Interactive Molecular Dynamics session.
+    ImdSession* imdSession_;
+    //! The pull work object.
+    pull_t* pull_work_;
+};
+
+/*! \internal
+ * \brief Builder for DomDecHelper
+ *
+ * This builder allows clients to register to the DomDecHelper in order to get
+ * informed whenever system re-partitioning is performed.
+ */
+class DomDecHelperBuilder
+{
+public:
+    //! Register DomDecHelper client
+    void registerClient(IDomDecHelperClient* client);
+
+    //! Return DomDecHelper instance
+    template<typename... Args>
+    std::unique_ptr<DomDecHelper> build(Args&&... args)
+    {
+        state_ = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
+        std::vector<DomDecCallback> callbacks;
+        for (const auto& client : clients_)
+        {
+            callbacks.emplace_back(client->registerDomDecCallback());
+        }
+        return std::make_unique<DomDecHelper>(std::forward<Args>(args)..., std::move(callbacks));
+    }
+
+private:
+    //! List of clients to be updated after system partition
+    std::vector<IDomDecHelperClient*> clients_;
+    //! The state of the builder
+    ModularSimulatorBuilderState state_ = ModularSimulatorBuilderState::AcceptingClientRegistrations;
+};
+
+//! \}
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_DOMDECHELPER_H
diff --git a/src/include/gromacs/modularsimulator/energydata.h b/src/include/gromacs/modularsimulator/energydata.h
new file mode 100644 (file)
index 0000000..e9e7f27
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the energy element for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_ENERGYELEMENT_MICROSTATE_H
+#define GMX_ENERGYELEMENT_MICROSTATE_H
+
+#include "gromacs/mdtypes/state.h"
+
+#include "modularsimulatorinterfaces.h"
+
+class gmx_ekindata_t;
+struct gmx_enerdata_t;
+struct gmx_mtop_t;
+struct ObservablesHistory;
+struct pull_t;
+struct t_fcdata;
+struct t_inputrec;
+struct SimulationGroups;
+
+namespace gmx
+{
+enum class StartingBehavior;
+class Constraints;
+class EnergyOutput;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
+class MDAtoms;
+class ModularSimulatorAlgorithmBuilderHelper;
+class ObservablesReducer;
+class ParrinelloRahmanBarostat;
+class StatePropagatorData;
+class VelocityScalingTemperatureCoupling;
+struct MDModulesNotifiers;
+
+//! Function type for elements contributing energy
+using EnergyContribution = std::function<real(Step, Time)>;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Data class managing energies
+ *
+ * The EnergyData owns the EnergyObject,
+ * the tensors for the different virials and the pressure as well as
+ * the total dipole vector. It has a member class which is part of the
+ * simulator loop and and is responsible
+ * for saving energy data and writing it to trajectory.
+ *
+ * The EnergyData offers an interface to add virial contributions,
+ * but also allows access to the raw pointers to tensor data, the
+ * dipole vector, and the legacy energy data structures.
+ *
+ * The EnergyData owns an object of type EnergyData::Element,
+ * which takes part in the simulation loop, allowing to record
+ * and output energies during the simulation.
+ */
+class EnergyData final
+{
+public:
+    //! Constructor
+    EnergyData(StatePropagatorData*        statePropagatorData,
+               FreeEnergyPerturbationData* freeEnergyPerturbationData,
+               const gmx_mtop_t&           globalTopology,
+               const t_inputrec*           inputrec,
+               const MDAtoms*              mdAtoms,
+               gmx_enerdata_t*             enerd,
+               gmx_ekindata_t*             ekind,
+               const Constraints*          constr,
+               FILE*                       fplog,
+               t_fcdata*                   fcd,
+               const MDModulesNotifiers&   mdModulesNotifiers,
+               bool                        isMasterRank,
+               ObservablesHistory*         observablesHistory,
+               StartingBehavior            startingBehavior,
+               bool                        simulationsShareState,
+               pull_t*                     pullWork);
+
+    /*! \brief Final output
+     *
+     * Prints the averages to log. This is called from ModularSimulatorAlgorithm.
+     *
+     * \see ModularSimulatorAlgorithm::teardown
+     */
+    void teardown();
+
+    /*! \brief Add contribution to force virial
+     *
+     * This automatically resets the tensor if the step is higher
+     * than the current step, starting the tensor calculation for
+     * a new step at zero. Otherwise, it adds the new contribution
+     * to the existing virial.
+     */
+    void addToForceVirial(const tensor virial, Step step);
+
+    /*! \brief Add contribution to constraint virial
+     *
+     * This automatically resets the tensor if the step is higher
+     * than the current step, starting the tensor calculation for
+     * a new step at zero. Otherwise, it adds the new contribution
+     * to the existing virial.
+     */
+    void addToConstraintVirial(const tensor virial, Step step);
+
+    /*! \brief Get pointer to force virial tensor
+     *
+     * Allows access to the raw pointer to the tensor.
+     */
+    rvec* forceVirial(Step step);
+
+    /*! \brief Get pointer to constraint virial tensor
+     *
+     * Allows access to the raw pointer to the tensor.
+     */
+    rvec* constraintVirial(Step step);
+
+    /*! \brief Get pointer to total virial tensor
+     *
+     * Allows access to the raw pointer to the tensor.
+     */
+    rvec* totalVirial(Step step);
+
+    /*! \brief Get pointer to pressure tensor
+     *
+     * Allows access to the raw pointer to the tensor.
+     */
+    rvec* pressure(Step step);
+
+    /*! \brief Get pointer to mu_tot
+     *
+     * Allows access to the raw pointer to the dipole vector.
+     */
+    real* muTot();
+
+    /*! \brief Get pointer to energy structure
+     *
+     */
+    gmx_enerdata_t* enerdata();
+
+    /*! \brief Get const pointer to energy structure
+     *
+     */
+    const gmx_enerdata_t* enerdata() const;
+
+    /*! \brief Get pointer to kinetic energy structure
+     *
+     */
+    gmx_ekindata_t* ekindata();
+
+    /*! \brief Get pointer to needToSumEkinhOld
+     *
+     */
+    bool* needToSumEkinhOld();
+
+    /*! \brief Whether kinetic energy was read from checkpoint
+     *
+     * This is needed by the compute globals element
+     * TODO: Remove this when moving global reduction to client system (#3421)
+     */
+    [[nodiscard]] bool hasReadEkinFromCheckpoint() const;
+
+    /*! \brief Add conserved energy contribution
+     *
+     * This allows other elements to register callbacks for contributions to
+     * the conserved energy term.
+     */
+    void addConservedEnergyContribution(EnergyContribution&& energyContribution);
+
+    /*! \brief set Parrinello-Rahman barostat
+     *
+     * This allows to set a pointer to the Parrinello-Rahman barostat used to
+     * print the box velocities.
+     */
+    void setParrinelloRahmanBoxVelocities(std::function<const rvec*()>&& parrinelloRahmanBoxVelocities);
+
+    /*! \brief Initialize energy history
+     *
+     * Kept as a static function to allow usage from legacy code
+     * \todo Make member function once legacy use is not needed anymore
+     */
+    static void initializeEnergyHistory(StartingBehavior    startingBehavior,
+                                        ObservablesHistory* observablesHistory,
+                                        EnergyOutput*       energyOutput);
+
+    /*! \brief Request (local) kinetic energy update
+     */
+    void updateKineticEnergy();
+
+    //! The element taking part in the simulator loop
+    class Element;
+    //! Get pointer to element (whose lifetime is managed by this)
+    Element* element();
+
+private:
+    /*! \brief Setup (needs file pointer)
+     *
+     * Initializes the EnergyOutput object, and does some logging output.
+     *
+     * \param mdoutf  File pointer
+     */
+    void setup(gmx_mdoutf* mdoutf);
+
+    /*! \brief Save data at energy steps
+     *
+     * \param step  The current step
+     * \param time  The current time
+     * \param isEnergyCalculationStep  Whether the current step is an energy calculation step
+     * \param isFreeEnergyCalculationStep  Whether the current step is a free energy calculation step
+     */
+    void doStep(Step step, Time time, bool isEnergyCalculationStep, bool isFreeEnergyCalculationStep);
+
+    /*! \brief Write to energy trajectory
+     *
+     * This is only called by master - writes energy to trajectory and to log.
+     */
+    void write(gmx_mdoutf* outf, Step step, Time time, bool writeTrajectory, bool writeLog);
+
+    /*
+     * Data owned by EnergyData
+     */
+    //! The element
+    std::unique_ptr<Element> element_;
+    //! The energy output object
+    std::unique_ptr<EnergyOutput> energyOutput_;
+    //! Helper object to checkpoint kinetic energy data
+    ekinstate_t ekinstate_;
+
+    //! Whether this is the master rank
+    const bool isMasterRank_;
+
+    //! The force virial tensor
+    tensor forceVirial_;
+    //! The constraint virial tensor
+    tensor shakeVirial_;
+    //! The total virial tensor
+    tensor totalVirial_;
+    //! The pressure tensor
+    tensor pressure_;
+    //! The total dipole moment
+    rvec muTot_;
+
+    //! The step number of the current force virial tensor
+    Step forceVirialStep_;
+    //! The step number of the current constraint virial tensor
+    Step shakeVirialStep_;
+    //! The step number of the current total virial tensor
+    Step totalVirialStep_;
+    //! The step number of the current pressure tensor
+    Step pressureStep_;
+
+    //! Whether ekinh_old needs to be summed up (set by compute globals)
+    bool needToSumEkinhOld_;
+    //! Whether we have read ekin from checkpoint
+    bool hasReadEkinFromCheckpoint_;
+
+    //! Describes how the simulation (re)starts
+    const StartingBehavior startingBehavior_;
+
+    /*
+     * Pointers to Simulator data
+     */
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the state propagator data
+    StatePropagatorData* statePropagatorData_;
+    //! Pointer to the free energy perturbation data
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
+
+    //! Callbacks contributing to the conserved energy term
+    std::vector<EnergyContribution> conservedEnergyContributions_;
+    //! Callback to the Parrinello-Rahman box velocities
+    std::function<const rvec*()> parrinelloRahmanBoxVelocities_;
+
+    //! Contains user input mdp options.
+    const t_inputrec* inputrec_;
+    //! Full system topology.
+    const gmx_mtop_t& top_global_;
+    //! Atom parameters for this domain.
+    const MDAtoms* mdAtoms_;
+    //! Energy data structure
+    gmx_enerdata_t* enerd_;
+    //! Kinetic energy data
+    gmx_ekindata_t* ekind_;
+    //! Handles constraints.
+    const Constraints* constr_;
+    //! Handles logging.
+    FILE* fplog_;
+    //! Helper struct for force calculations.
+    t_fcdata* fcd_;
+    //! Notifiers to MD modules
+    const MDModulesNotifiers& mdModulesNotifiers_;
+    //! Global topology groups
+    const SimulationGroups* groups_;
+    //! History of simulation observables.
+    ObservablesHistory* observablesHistory_;
+    //! Whether simulations share the state
+    bool simulationsShareState_;
+    //! The pull work object.
+    pull_t* pullWork_;
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element for EnergyData
+ *
+ * This member class allows EnergyData to take part in the simulator
+ * loop.
+ *
+ * It subscribes to the trajectory signaller, the energy signaller,
+ * and the logging signaller to know when an energy calculation is
+ * needed and when a non-recording step is enough. The simulator
+ * builder is responsible to place the element in a location at
+ * which a valid energy state is available. The EnergyData::Element is
+ * also a subscriber to the trajectory writer element, as it is
+ * responsible to write energy data to trajectory.
+ */
+class EnergyData::Element final :
+    public ISimulatorElement,
+    public ITrajectoryWriterClient,
+    public ITrajectorySignallerClient,
+    public IEnergySignallerClient,
+    public ICheckpointHelperClient
+{
+public:
+    //! Constructor
+    Element(EnergyData* energyData, bool isMasterRank);
+
+    /*! \brief Register run function for step / time
+     *
+     * This needs to be called when the energies are at a full time step.
+     * Positioning this element is the responsibility of the programmer.
+     *
+     * This is also the place at which the current state becomes the previous
+     * state.
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! No element setup needed
+    void elementSetup() override {}
+
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    ObservablesReducer*        observablesReducer);
+
+private:
+    EnergyData* energyData_;
+
+    /*! \brief Setup (needs file pointer)
+     *
+     * ITrajectoryWriterClient implementation.
+     *
+     * Initializes the EnergyOutput object, and does some logging output.
+     *
+     * \param mdoutf  File pointer
+     */
+    void trajectoryWriterSetup(gmx_mdoutf* mdoutf) override;
+    //! No trajectory writer teardown needed
+    void trajectoryWriterTeardown(gmx_mdoutf gmx_unused* outf) override {}
+
+    //! ITrajectoryWriterClient implementation.
+    std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
+    //! ITrajectorySignallerClient implementation
+    std::optional<ITrajectoryWriterCallback> registerTrajectoryWriterCallback(TrajectoryEvent event) override;
+    //! IEnergySignallerClient implementation
+    std::optional<SignallerCallback> registerEnergyCallback(EnergySignallerEvent event) override;
+
+
+    //! CheckpointHelper identifier
+    const std::string identifier_ = "EnergyElement";
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+
+    //! Whether this is the master rank
+    const bool isMasterRank_;
+    //! The next communicated energy writing step
+    Step energyWritingStep_;
+    //! The next communicated energy calculation step
+    Step energyCalculationStep_;
+    //! The next communicated free energy calculation step
+    Step freeEnergyCalculationStep_;
+};
+
+} // namespace gmx
+
+#endif // GMX_ENERGYELEMENT_MICROSTATE_H
diff --git a/src/include/gromacs/modularsimulator/expandedensembleelement.h b/src/include/gromacs/modularsimulator/expandedensembleelement.h
new file mode 100644 (file)
index 0000000..bf08567
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the expanded ensemble element for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_EXPANDEDENSEMBLEELEMENT_H
+#define GMX_MODULARSIMULATOR_EXPANDEDENSEMBLEELEMENT_H
+
+#include "freeenergyperturbationdata.h"
+
+struct df_history_t;
+struct t_inputrec;
+
+namespace gmx
+{
+enum class CheckpointDataOperation;
+class EnergyData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
+class MDAtoms;
+class ModularSimulatorAlgorithmBuilderHelper;
+class ObservablesReducer;
+class StatePropagatorData;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief The expanded ensemble element
+ *
+ * This element periodically attempts Monte Carlo moves in lambda
+ * space and sets the new lambda state in FreeEnergyPerturbationData::Element.
+ */
+class ExpandedEnsembleElement final : public ISimulatorElement, public ICheckpointHelperClient, public ILoggingSignallerClient
+{
+public:
+    //! Constructor
+    explicit ExpandedEnsembleElement(bool                              isMasterRank,
+                                     Step                              initialStep,
+                                     int                               frequency,
+                                     const EnergyData*                 energyData,
+                                     const FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                     FILE*                             fplog,
+                                     const t_inputrec*                 inputrec);
+
+    //! Attempt lambda MC step and write log
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+    //! Set up FEP history object
+    void elementSetup() override;
+    //! No teardown needed
+    void elementTeardown() override{};
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    ObservablesReducer*        observablesReducer);
+
+private:
+    //! Use expanded ensemble to determine new FEP state or write log
+    void apply(Step step, bool doLambdaStep, bool doLog);
+
+    //! Helper object allowing to set new FEP state
+    FepStateSetting* fepStateSetting_;
+
+    //! Whether this runs on master
+    const bool isMasterRank_;
+    //! The initial Step
+    const Step initialStep_;
+    //! The frequency of lambda MC steps
+    const int frequency_;
+
+    //! ILoggingSignallerClient implementation
+    std::optional<SignallerCallback> registerLoggingCallback() override;
+    //! The next logging step
+    Step nextLogWritingStep_;
+
+    //! The free energy sampling history
+    std::unique_ptr<df_history_t> dfhist_;
+
+    //! CheckpointHelper identifier
+    const std::string identifier_ = "ExpandedEnsembleElement";
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+    //! Whether this object was restored from checkpoint
+    bool restoredFromCheckpoint_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the energy data
+    const EnergyData* energyData_;
+    //! Pointer to the free energy perturbation data
+    const FreeEnergyPerturbationData* freeEnergyPerturbationData_;
+
+    // Access to ISimulator data
+    //! Handles logging.
+    FILE* fplog_;
+    //! Contains user input mdp options.
+    const t_inputrec* inputrec_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_EXPANDEDENSEMBLEELEMENT_H
diff --git a/src/include/gromacs/modularsimulator/firstorderpressurecoupling.h b/src/include/gromacs/modularsimulator/firstorderpressurecoupling.h
new file mode 100644 (file)
index 0000000..489c8a4
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the element performing first-order pressure coupling for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_FIRSTORDERPRESSURECOUPLING_H
+#define GMX_MODULARSIMULATOR_FIRSTORDERPRESSURECOUPLING_H
+
+#include "modularsimulatorinterfaces.h"
+
+struct t_inputrec;
+struct t_nrnb;
+
+enum class PressureCoupling;
+
+namespace gmx
+{
+class EnergyData;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
+class MDAtoms;
+class ModularSimulatorAlgorithmBuilderHelper;
+class ObservablesReducer;
+class StatePropagatorData;
+
+/*! \internal
+ * \brief Element performing first-order pressure coupling
+ *
+ * This element implements the first-order pressure coupling algorithms
+ * (Berendesen, c-rescale).
+ */
+class FirstOrderPressureCoupling final : public ISimulatorElement, public ICheckpointHelperClient
+{
+public:
+    //! Constructor
+    FirstOrderPressureCoupling(int                               couplingFrequency,
+                               int                               couplingOffset,
+                               real                              couplingTimeStep,
+                               StatePropagatorData*              statePropagatorData,
+                               EnergyData*                       energyData,
+                               FILE*                             fplog,
+                               const t_inputrec*                 inputrec,
+                               const MDAtoms*                    mdAtoms,
+                               t_nrnb*                           nrnb,
+                               ReportPreviousStepConservedEnergy reportPreviousStepConservedEnergy);
+
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& function) override;
+    //! Setup - initialize relative box matrix
+    void elementSetup() override;
+    //! No teardown needed
+    void elementTeardown() override{};
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     * \param offset  The step offset at which the barostat is applied
+     * \param reportPreviousStepConservedEnergy  Report the previous or the current step conserved energy
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement*
+    getElementPointerImpl(LegacySimulatorData*                    legacySimulatorData,
+                          ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                          StatePropagatorData*                    statePropagatorData,
+                          EnergyData*                             energyData,
+                          FreeEnergyPerturbationData gmx_unused* freeEnergyPerturbationData,
+                          GlobalCommunicationHelper gmx_unused* globalCommunicationHelper,
+                          ObservablesReducer*                   observablesReducer,
+                          int                                   offset,
+                          ReportPreviousStepConservedEnergy     reportPreviousStepConservedEnergy);
+
+private:
+    //! Calculate the scaling matrix
+    template<PressureCoupling pressureCouplingType>
+    void calculateScalingMatrix(Step step);
+    //! Scale the box and coordinates according to the current scaling matrix
+    template<PressureCoupling pressureCouplingType>
+    void scaleBoxAndCoordinates();
+    //! Helper function returning the conserved energy contribution
+    real conservedEnergyContribution(Step step);
+
+    //! The pressure coupling type required
+    const PressureCoupling pressureCouplingType_;
+    //! The coupling time step (simulation time step x coupling frequency)
+    const real couplingTimeStep_;
+    //! The frequency at which pressure coupling is applied
+    const int couplingFrequency_;
+    //! The offset at which pressure coupling is applied
+    const int couplingOffset_;
+
+    //! The current box scaling matrix
+    tensor boxScalingMatrix_;
+    //! Relative box shape
+    tensor boxRel_;
+    //! Contribution to the conserved energy
+    double conservedEnergyContribution_;
+    //! Contribution to the conserved energy
+    double previousStepConservedEnergyContribution_;
+    //! Current step of the conserved energy contribution
+    Step conservedEnergyContributionStep_;
+    //! Whether we're reporting current step or previous step conserved energy
+    const ReportPreviousStepConservedEnergy reportPreviousStepConservedEnergy_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the micro state
+    StatePropagatorData* statePropagatorData_;
+    //! Pointer to the energy data
+    EnergyData* energyData_;
+
+    // Access to ISimulator data
+    //! Handles logging.
+    FILE* fplog_;
+    //! Contains user input mdp options.
+    const t_inputrec* inputrec_;
+    //! Atom parameters for this domain.
+    const MDAtoms* mdAtoms_;
+    //! Manages flop accounting.
+    t_nrnb* nrnb_;
+
+    //! CheckpointHelper identifier
+    const std::string identifier_;
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_FIRSTORDERPRESSURECOUPLING_H
diff --git a/src/include/gromacs/modularsimulator/forceelement.h b/src/include/gromacs/modularsimulator/forceelement.h
new file mode 100644 (file)
index 0000000..b9fd205
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the force element for the modular simulator
+ *
+ * This element calculates the forces, with or without shells or
+ * flexible constraints.
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_FORCEELEMENT_H
+#define GMX_MODULARSIMULATOR_FORCEELEMENT_H
+
+#include <array>
+
+#include "gromacs/domdec/dlbtiming.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/real.h"
+
+#include "modularsimulatorinterfaces.h"
+#include "topologyholder.h"
+
+struct gmx_enfrot;
+struct gmx_shellfc_t;
+struct gmx_wallcycle;
+class CpuPpLongRangeNonbondeds;
+struct pull_t;
+struct t_nrnb;
+
+namespace gmx
+{
+class Awh;
+class EnergyData;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
+class ImdSession;
+class LegacySimulatorData;
+class MDAtoms;
+class MdrunScheduleWorkload;
+class ModularSimulatorAlgorithmBuilderHelper;
+class ObservablesReducer;
+class StatePropagatorData;
+class VirtualSitesHandler;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Force element
+ *
+ * The force element manages the call to either
+ * do_force(...) or relax_shell_flexcon(...)
+ */
+class ForceElement final :
+    public ISimulatorElement,
+    public ITopologyHolderClient,
+    public INeighborSearchSignallerClient,
+    public IEnergySignallerClient,
+    public IDomDecHelperClient
+{
+public:
+    //! Constructor
+    ForceElement(StatePropagatorData*        statePropagatorData,
+                 EnergyData*                 energyData,
+                 FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                 bool                        isVerbose,
+                 bool                        isDynamicBox,
+                 FILE*                       fplog,
+                 const t_commrec*            cr,
+                 const t_inputrec*           inputrec,
+                 const MDAtoms*              mdAtoms,
+                 t_nrnb*                     nrnb,
+                 t_forcerec*                 fr,
+                 gmx_wallcycle*              wcycle,
+                 MdrunScheduleWorkload*      runScheduleWork,
+                 VirtualSitesHandler*        vsite,
+                 ImdSession*                 imdSession,
+                 pull_t*                     pull_work,
+                 Constraints*                constr,
+                 const gmx_mtop_t&           globalTopology,
+                 gmx_enfrot*                 enforcedRotation);
+
+    /*! \brief Register force calculation for step / time
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! Check that we got the local topology
+    void elementSetup() override;
+    //! Print some final output
+    void elementTeardown() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    ObservablesReducer*        observablesReducer);
+
+    //! Callback on domain decomposition repartitioning
+    DomDecCallback registerDomDecCallback() override;
+
+private:
+    //! ITopologyHolderClient implementation
+    void setTopology(const gmx_localtop_t* top) override;
+    //! INeighborSearchSignallerClient implementation
+    std::optional<SignallerCallback> registerNSCallback() override;
+    //! IEnergySignallerClient implementation
+    std::optional<SignallerCallback> registerEnergyCallback(EnergySignallerEvent event) override;
+    //! The actual do_force call
+    template<bool doShellFC>
+    void run(Step step, Time time, unsigned int flags);
+
+    //! The shell / FC helper struct
+    gmx_shellfc_t* shellfc_;
+    //! Whether shells or flexible constraints are present
+    const bool doShellFC_;
+
+    //! The next NS step
+    Step nextNSStep_;
+    //! The next energy calculation step
+    Step nextEnergyCalculationStep_;
+    //! The next energy calculation step
+    Step nextVirialCalculationStep_;
+    //! The next free energy calculation step
+    Step nextFreeEnergyCalculationStep_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the micro state
+    StatePropagatorData* statePropagatorData_;
+    //! Pointer to the energy data
+    EnergyData* energyData_;
+    //! Pointer to the free energy perturbation data
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
+
+    //! The local topology - updated by Topology via Client system
+    const gmx_localtop_t* localTopology_;
+
+    //! Whether we're having a dynamic box
+    const bool isDynamicBox_;
+    //! Whether we're being verbose
+    const bool isVerbose_;
+    //! The number of shell relaxation steps we did
+    Step nShellRelaxationSteps_;
+
+    //! DD / DLB helper object
+    const DDBalanceRegionHandler ddBalanceRegionHandler_;
+    //! Long range force calculator
+    std::unique_ptr<CpuPpLongRangeNonbondeds> longRangeNonbondeds_;
+
+    /* \brief The FEP lambda vector
+     *
+     * Used if FEP is off, since do_force
+     * requires lambda to be allocated anyway
+     */
+    gmx::EnumerationArray<FreeEnergyPerturbationType, real> lambda_;
+
+    // Access to ISimulator data
+    //! Handles logging.
+    FILE* fplog_;
+    //! Handles communication.
+    const t_commrec* cr_;
+    //! Contains user input mdp options.
+    const t_inputrec* inputrec_;
+    //! Atom parameters for this domain.
+    const MDAtoms* mdAtoms_;
+    //! Manages flop accounting.
+    t_nrnb* nrnb_;
+    //! Manages wall cycle accounting.
+    gmx_wallcycle* wcycle_;
+    //! Parameters for force calculations.
+    t_forcerec* fr_;
+    //! Handles virtual sites.
+    VirtualSitesHandler* vsite_;
+    //! The Interactive Molecular Dynamics session.
+    ImdSession* imdSession_;
+    //! The pull work object.
+    pull_t* pull_work_;
+    //! Schedule of work for each MD step for this task.
+    MdrunScheduleWorkload* runScheduleWork_;
+    //! Handles constraints.
+    Constraints* constr_;
+    //! Handles enforced rotation.
+    gmx_enfrot* enforcedRotation_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_FORCEELEMENT_H
diff --git a/src/include/gromacs/modularsimulator/freeenergyperturbationdata.h b/src/include/gromacs/modularsimulator/freeenergyperturbationdata.h
new file mode 100644 (file)
index 0000000..0dfb820
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the free energy perturbation element for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_FREEENERGYPERTURBATIONELEMENT_H
+#define GMX_MODULARSIMULATOR_FREEENERGYPERTURBATIONELEMENT_H
+
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/real.h"
+
+#include "modularsimulatorinterfaces.h"
+
+struct t_inputrec;
+struct t_trxframe;
+
+namespace gmx
+{
+enum class CheckpointDataOperation;
+class EnergyData;
+class FepStateSetting;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
+class MDAtoms;
+class ModularSimulatorAlgorithmBuilderHelper;
+class StatePropagatorData;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief The free energy perturbation data
+ *
+ * The lambda vector and the current FEP state are held by the
+ * FreeEnergyPerturbationData, offering access to its values via getter
+ * functions. The FreeEnergyPerturbationData::Element is responsible for
+ * lambda update (if applicable) and checkpointing.
+ */
+class FreeEnergyPerturbationData final
+{
+public:
+    //! Constructor
+    FreeEnergyPerturbationData(FILE* fplog, const t_inputrec& inputrec, MDAtoms* mdAtoms);
+
+    //! Get a view of the current lambda vector
+    ArrayRef<real> lambdaView();
+    //! Get a const view of the current lambda vector
+    [[nodiscard]] ArrayRef<const real> constLambdaView() const;
+    //! Get the current FEP state
+    [[nodiscard]] int currentFEPState() const;
+
+    /*! \brief Enable setting of the FEP state by an external object
+     *
+     * Currently, this can only be called once, usually during setup time.
+     * Having more than one object setting the FEP state would require additional bookkeeping.
+     *
+     * \return Pointer to an object allowing to set new FEP state
+     */
+    [[nodiscard]] FepStateSetting* enableExternalFepStateSetting() const;
+
+    //! The element taking part in the simulator loop
+    class Element;
+    //! Get pointer to element (whose lifetime is managed by this)
+    Element* element();
+
+    //! Read everything that can be stored in t_trxframe from a checkpoint file
+    static void readCheckpointToTrxFrame(t_trxframe*                       trxFrame,
+                                         std::optional<ReadCheckpointData> readCheckpointData);
+    //! CheckpointHelper identifier
+    static const std::string& checkpointID();
+
+private:
+    //! Update the lambda values
+    void updateLambdas(Step step);
+    //! Update the lambda values
+    void setLambdaState(Step step, int newState);
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+    //! Update MDAtoms
+    void updateMDAtoms();
+
+    //! The element
+    std::unique_ptr<Element> element_;
+
+    //! The lambda vector
+    gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, real> lambda_;
+    //! The current free energy state
+    int currentFEPState_;
+
+    //! Handles logging.
+    FILE* fplog_;
+    //! Contains user input mdp options.
+    const t_inputrec& inputrec_;
+    //! Atom parameters for this domain.
+    MDAtoms* mdAtoms_;
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Allows external clients to specify how to change the FEP state
+ */
+class FepStateSetting
+{
+public:
+    //! Signal (during task scheduling) that a signal stepping step will happen
+    void signalSettingStep(Step step);
+    //! Set new state at specific step (called during simulation run)
+    void setNewState(int state, Step step);
+
+    // Allow private member access
+    friend class FreeEnergyPerturbationData::Element;
+
+private:
+    //! The next external lambda setting step
+    Step nextFepStateSettingStep = -1;
+    //! The new FEP state set externally
+    int newFepState = -1;
+    //! The step at which the new FEP state gets used
+    Step newFepStateStep = -1;
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief The free energy perturbation data element
+ *
+ * The FreeEnergyPerturbationData::Element does update the lambda
+ * values during the simulation run if lambda is non-static. It does
+ * implement the checkpointing client interface to save its current
+ * state for restart.
+ */
+class FreeEnergyPerturbationData::Element final :
+    public ISimulatorElement,
+    public ICheckpointHelperClient,
+    public IDomDecHelperClient
+{
+public:
+    //! Constructor
+    explicit Element(FreeEnergyPerturbationData* freeEnergyPerturbationElement, double deltaLambda);
+
+    //! Update lambda and mdatoms
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! Update the MdAtoms object
+    void elementSetup() override;
+
+    //! No teardown needed
+    void elementTeardown() override{};
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    //! Callback on domain decomposition repartitioning
+    DomDecCallback registerDomDecCallback() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper);
+
+    //! Enable setting of the FEP state by an external object
+    FepStateSetting* enableExternalFepStateSetting();
+
+private:
+    //! The free energy data
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
+    //! Whether lambda values change continuously
+    const bool doSlowGrowth_;
+
+    //! Information about external lambda setting, set only if external lambda setting is enabled
+    std::optional<FepStateSetting> externalFepStateSetting_;
+
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_FREEENERGYPERTURBATIONELEMENT_H
diff --git a/src/include/gromacs/modularsimulator/modularsimulator.h b/src/include/gromacs/modularsimulator/modularsimulator.h
new file mode 100644 (file)
index 0000000..82d6068
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Provides the modular simulator.
+ *
+ * Defines the ModularSimulator class. Provides checkUseModularSimulator() utility function
+ * to determine whether the ModularSimulator should be used.
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is currently the only part of the modular simulator module which is exposed.
+ * Mdrunner creates an object of type ModularSimulator (via SimulatorBuilder), and calls its
+ * run() method. Mdrunner also calls checkUseModularSimulator(...), which in turns calls a
+ * static method of ModularSimulator. This could easily become a free function if this requires
+ * more exposure than otherwise necessary.
+ */
+#ifndef GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H
+#define GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H
+
+#include "gromacs/mdrun/isimulator.h"
+
+struct CheckpointHeaderContents;
+struct t_fcdata;
+struct t_trxframe;
+
+namespace gmx
+{
+class ModularSimulatorAlgorithmBuilder;
+class ReadCheckpointDataHolder;
+
+/*! \libinternal
+ * \ingroup module_modularsimulator
+ * \brief The modular simulator
+ *
+ * Based on the input given, this simulator builds independent elements and
+ * signallers and stores them in a respective vector. The run function
+ * runs the simulation by, in turn, building a task list from the elements
+ * for a predefined number of steps, then running the task list, and repeating
+ * until the stop criterion is fulfilled.
+ */
+class ModularSimulator final : public ISimulator
+{
+public:
+    //! Run the simulator
+    void run() override;
+
+    //! Check for disabled functionality
+    static bool isInputCompatible(bool                             exitOnFailure,
+                                  const t_inputrec*                inputrec,
+                                  bool                             doRerun,
+                                  const gmx_mtop_t&                globalTopology,
+                                  const gmx_multisim_t*            ms,
+                                  const ReplicaExchangeParameters& replExParams,
+                                  const t_fcdata*                  fcd,
+                                  bool                             doEssentialDynamics,
+                                  bool                             doMembed);
+
+    //! Read everything that can be stored in t_trxframe from a checkpoint file
+    static void readCheckpointToTrxFrame(t_trxframe*                     fr,
+                                         ReadCheckpointDataHolder*       readCheckpointDataHolder,
+                                         const CheckpointHeaderContents& checkpointHeaderContents);
+
+    // Only builder can construct
+    friend class SimulatorBuilder;
+
+private:
+    //! Constructor
+    ModularSimulator(std::unique_ptr<LegacySimulatorData>      legacySimulatorData,
+                     std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder);
+
+    //! Populate algorithm builder with elements
+    void addIntegrationElements(ModularSimulatorAlgorithmBuilder* builder);
+
+    //! Check for disabled functionality (during construction time)
+    void checkInputForDisabledFunctionality();
+
+    //! Pointer to legacy simulator data (TODO: Can we avoid using unique_ptr? #3628)
+    std::unique_ptr<LegacySimulatorData> legacySimulatorData_;
+    //! Input checkpoint data
+    std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder_;
+};
+
+/*!
+ * \brief Whether or not to use the ModularSimulator
+ *
+ * GMX_DISABLE_MODULAR_SIMULATOR environment variable allows to disable modular simulator for
+ * all uses.
+ *
+ * See ModularSimulator::isInputCompatible() for function signature.
+ *
+ * \ingroup module_modularsimulator
+ */
+template<typename... Ts>
+auto checkUseModularSimulator(Ts&&... args)
+        -> decltype(ModularSimulator::isInputCompatible(std::forward<Ts>(args)...))
+{
+    return ModularSimulator::isInputCompatible(std::forward<Ts>(args)...)
+           && getenv("GMX_DISABLE_MODULAR_SIMULATOR") == nullptr;
+}
+
+} // namespace gmx
+
+#endif // GROMACS_MODULARSIMULATOR_MODULARSIMULATOR_H
diff --git a/src/include/gromacs/modularsimulator/modularsimulatorinterfaces.h b/src/include/gromacs/modularsimulator/modularsimulatorinterfaces.h
new file mode 100644 (file)
index 0000000..1f0cc1f
--- /dev/null
@@ -0,0 +1,597 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \defgroup module_modularsimulator The modular simulator
+ * \ingroup group_mdrun
+ * \brief
+ * The modular simulator improves extensibility, adds Monte Carlo capabilities,
+ * promotes data locality and communication via interfaces, supports
+ * multi-stepping integrators, and paves the way for some task parallelism.
+ *
+ * For more information, see page_modularsimulator
+ * \todo Can we link to `docs/doxygen/lib/modularsimulator.md`?
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ */
+/*! \internal \file
+ * \brief
+ * Declares the main interfaces used by the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+#ifndef GMX_MODULARSIMULATOR_MODULARSIMULATORINTERFACES_H
+#define GMX_MODULARSIMULATOR_MODULARSIMULATORINTERFACES_H
+
+#include <functional>
+#include <memory>
+#include <optional>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/checkpointdata.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/exceptions.h"
+
+struct gmx_localtop_t;
+struct gmx_mdoutf;
+struct t_commrec;
+class t_state;
+
+namespace gmx
+{
+template<typename T>
+class ArrayRef;
+class EnergySignaller;
+class LastStepSignaller;
+class LoggingSignaller;
+class NeighborSearchSignaller;
+enum class ReferenceTemperatureChangeAlgorithm;
+enum class ScaleVelocities;
+template<class Signaller>
+class SignallerBuilder;
+class TrajectorySignaller;
+
+//! \addtogroup module_modularsimulator
+//! \{
+
+// This will make signatures more legible
+//! Step number
+using Step = int64_t;
+//! Simulation time
+using Time = double;
+
+//! The function type that can be scheduled to be run during the simulator run
+typedef std::function<void()> SimulatorRunFunction;
+
+//! The function type that allows to register run functions
+typedef std::function<void(SimulatorRunFunction)> RegisterRunFunction;
+//! The function type scheduling run functions for a step / time using a RegisterRunFunction reference
+typedef std::function<void(Step, Time, const RegisterRunFunction&)> SchedulingFunction;
+
+/*! \internal
+ * \brief The general interface for elements of the modular simulator
+ *
+ * Setup and teardown are run once at the beginning of the simulation
+ * (after all elements were set up, before the first step) and at the
+ * end of the simulation (after the last step, before elements are
+ * destructed), respectively. `registerRun` is periodically called
+ * during the run, at which point elements can decide to register one
+ * or more run functions to be run at a specific time / step. Registering
+ * more than one function is especially valuable for collective elements
+ * consisting of more than one single element.
+ */
+class ISimulatorElement
+{
+public:
+    /*! \brief Query whether element wants to run at step / time
+     *
+     * Element can register one or more functions to be run at that step through
+     * the registration pointer.
+     */
+    virtual void scheduleTask(Step, Time, const RegisterRunFunction&) = 0;
+    //! Method guaranteed to be called after construction, before simulator run
+    virtual void elementSetup() = 0;
+    //! Method guaranteed to be called after simulator run, before deconstruction
+    virtual void elementTeardown() = 0;
+    //! Standard virtual destructor
+    virtual ~ISimulatorElement() = default;
+};
+
+/*! \internal
+ * \brief The general Signaller interface
+ *
+ * Signallers are run at the beginning of Simulator steps, informing
+ * their clients about the upcoming step. This allows clients to
+ * decide if they need to be activated at this time step, and what
+ * functions they will have to run. Examples for signallers
+ * include the neighbor-search signaller (informs its clients when a
+ * neighbor-searching step is about to happen) or the logging
+ * signaller (informs its clients when a logging step is about to
+ * happen).
+ *
+ * We expect all signallers to accept registration from clients, but
+ * different signallers might handle that differently, so we don't
+ * include this in the interface.
+ */
+class ISignaller
+{
+public:
+    //! Function run before every step of scheduling
+    virtual void signal(Step, Time) = 0;
+    //! Method guaranteed to be called after construction, before simulator run
+    virtual void setup() = 0;
+    //! Standard virtual destructor
+    virtual ~ISignaller() = default;
+};
+
+//! The function type that can be registered to signallers for callback
+typedef std::function<void(Step, Time)> SignallerCallback;
+
+/*! \internal
+ * \brief Interface for clients of the NeighborSearchSignaller
+ *
+ * Defining registerNSCallback allows clients to register an arbitrary callback
+ * for notification by the signaller.
+ */
+class INeighborSearchSignallerClient
+{
+public:
+    //! \cond
+    // (doxygen doesn't like these...)
+    //! Allow builder of NeighborSearchSignaller to ask for callback registration
+    friend class SignallerBuilder<NeighborSearchSignaller>;
+    //! \endcond
+    //! Standard virtual destructor
+    virtual ~INeighborSearchSignallerClient() = default;
+
+protected:
+    //! Return callback to NeighborSearchSignaller
+    virtual std::optional<SignallerCallback> registerNSCallback() = 0;
+};
+
+/*! \internal
+ * \brief Interface for clients of the LastStepSignaller
+ *
+ * Defining registerLastStepCallback allows clients to register an arbitrary callback
+ * for notification by the signaller.
+ */
+class ILastStepSignallerClient
+{
+public:
+    //! \cond
+    // (doxygen doesn't like these...)
+    //! Allow builder of LastStepSignaller to ask for callback registration
+    friend class SignallerBuilder<LastStepSignaller>;
+    //! \endcond
+    //! Standard virtual destructor
+    virtual ~ILastStepSignallerClient() = default;
+
+protected:
+    //! Return callback to LastStepSignaller
+    virtual std::optional<SignallerCallback> registerLastStepCallback() = 0;
+};
+
+/*! \internal
+ * \brief Interface for clients of the LoggingSignaller
+ *
+ * Defining registerLoggingCallback allows clients to register an arbitrary callback
+ * for notification by the signaller.
+ */
+class ILoggingSignallerClient
+{
+public:
+    //! \cond
+    // (doxygen doesn't like these...)
+    //! Allow builder of LoggingSignaller to ask for callback registration
+    friend class SignallerBuilder<LoggingSignaller>;
+    //! \endcond
+    //! Standard virtual destructor
+    virtual ~ILoggingSignallerClient() = default;
+
+protected:
+    //! Return callback to LoggingSignaller
+    virtual std::optional<SignallerCallback> registerLoggingCallback() = 0;
+};
+
+//! The energy events signalled by the EnergySignaller
+enum class EnergySignallerEvent
+{
+    EnergyCalculationStep,
+    VirialCalculationStep,
+    FreeEnergyCalculationStep
+};
+
+/*! \internal
+ * \brief Interface for clients of the EnergySignaller
+ *
+ * Defining registerEnergyCallback allows clients to register an arbitrary callback
+ * for notification by the signaller for every EnergySignallerEvent separately.
+ */
+class IEnergySignallerClient
+{
+public:
+    //! \cond
+    // (doxygen doesn't like these...)
+    //! Allow builder of EnergySignaller to ask for callback registration
+    friend class SignallerBuilder<EnergySignaller>;
+    //! \endcond
+    //! Standard virtual destructor
+    virtual ~IEnergySignallerClient() = default;
+
+protected:
+    //! Return callback to EnergySignaller
+    virtual std::optional<SignallerCallback> registerEnergyCallback(EnergySignallerEvent) = 0;
+};
+
+//! The trajectory writing events
+enum class TrajectoryEvent
+{
+    StateWritingStep,
+    EnergyWritingStep
+};
+
+/*! \internal
+ * \brief Interface for signaller clients of the TrajectoryElement
+ *
+ * Defining registerTrajectorySignallerCallback allows clients to register an arbitrary
+ * callback for notification by the signaller for every TrajectoryEvent separately.
+ */
+class ITrajectorySignallerClient
+{
+public:
+    //! \cond
+    // (doxygen doesn't like these...)
+    //! Allow builder of TrajectorySignaller to ask for callback registration
+    friend class SignallerBuilder<TrajectorySignaller>;
+    //! \endcond
+    //! Standard virtual destructor
+    virtual ~ITrajectorySignallerClient() = default;
+
+protected:
+    //! Return callback to TrajectoryElement
+    virtual std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent) = 0;
+};
+
+/* Trajectory writing clients are handed a pointer to the output file handler,
+ * allowing them to write their own trajectory contribution.
+ *
+ * As trajectory writing and log writing cannot currently be separated for the
+ * energy, clients also get informed whether this is a trajectory-writing step
+ * and / or a log-writing step.
+ */
+//! Function type for trajectory writing clients
+typedef std::function<void(gmx_mdoutf*, Step, Time, bool, bool)> ITrajectoryWriterCallback;
+
+/*! \internal
+ * \brief Interface for writer clients of the TrajectoryElement
+ *
+ * Defining registerTrajectoryWriterCallback allows clients to register an arbitrary
+ * callback called by the TrajectoryElement when trajectory writing happens.
+ *
+ * Setup and teardown methods allow clients to perform tasks with a valid output pointer.
+ */
+class ITrajectoryWriterClient
+{
+public:
+    //! \cond
+    // (doxygen doesn't like these...)
+    //! Allow TrajectoryElement to ask for callback registration
+    friend class TrajectoryElement;
+    //! \endcond
+    //! Standard virtual destructor
+    virtual ~ITrajectoryWriterClient() = default;
+
+protected:
+    //! Setup method with valid output pointer.
+    virtual void trajectoryWriterSetup(gmx_mdoutf* outf) = 0;
+    //! Teardown method with valid output pointer.
+    virtual void trajectoryWriterTeardown(gmx_mdoutf* outf) = 0;
+
+    //! Return callback to TrajectoryElement
+    virtual std::optional<ITrajectoryWriterCallback> registerTrajectoryWriterCallback(TrajectoryEvent) = 0;
+};
+
+/*! \internal
+ * \brief Client requiring read access to the local topology
+ *
+ */
+class ITopologyHolderClient
+{
+public:
+    //! \cond
+    // (doxygen doesn't like these...)
+    //! Allow TopologyHolder to set new topology
+    friend class TopologyHolder;
+    //! \endcond
+    //! Standard virtual destructor
+    virtual ~ITopologyHolderClient() = default;
+
+protected:
+    //! Pass pointer to new local topology
+    virtual void setTopology(const gmx_localtop_t*) = 0;
+};
+
+/*! \internal
+ * \brief Client that needs to store data during checkpointing
+ *
+ * Clients receive a CheckpointData object for reading and writing.
+ * Note that `ReadCheckpointData` is a typedef for
+ * `CheckpointData<CheckpointDataOperation::Read>`, and
+ * `WriteCheckpointData` is a typedef for
+ * `CheckpointData<CheckpointDataOperation::Write>`. This allows clients
+ * to write a single templated function, e.g.
+ *     template<CheckpointDataOperation operation>
+ *     void doCheckpointData(CheckpointData<operation>* checkpointData,
+ *                           const t_commrec* cr)
+ *     {
+ *         checkpointData->scalar("important value", &value_);
+ *     }
+ * for both checkpoint reading and writing. This function can then be
+ * dispatched from the interface functions,
+ *     void writeCheckpoint(WriteCheckpointData checkpointData, const t_commrec* cr)
+ *     {
+ *         doCheckpointData<CheckpointDataOperation::Write>(&checkpointData, cr);
+ *     }
+ *     void readCheckpoint(ReadCheckpointData checkpointData, const t_commrec* cr)
+ *     {
+ *         doCheckpointData<CheckpointDataOperation::Read>(&checkpointData, cr);
+ *     }
+ * This reduces code duplication and ensures that reading and writing
+ * operations will not get out of sync.
+ */
+class ICheckpointHelperClient
+{
+public:
+    //! Standard virtual destructor
+    virtual ~ICheckpointHelperClient() = default;
+
+    //! Write checkpoint (CheckpointData object only passed on master rank)
+    virtual void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData,
+                                     const t_commrec*                   cr) = 0;
+    //! Read checkpoint (CheckpointData object only passed on master rank)
+    virtual void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData,
+                                        const t_commrec*                  cr) = 0;
+    //! Get unique client id
+    [[nodiscard]] virtual const std::string& clientID() = 0;
+};
+
+/*! \brief
+ * Exception class signalling that a requested element was not found.
+ *
+ * \internal
+ */
+class ElementNotFoundError final : public ModularSimulatorError
+{
+public:
+    //! \copydoc FileIOError::FileIOError()
+    explicit ElementNotFoundError(const ExceptionInitializer& details) :
+        ModularSimulatorError(details)
+    {
+    }
+};
+
+/*! \brief
+ * Exception class signalling that elements were not connected properly.
+ *
+ * \internal
+ */
+class MissingElementConnectionError final : public ModularSimulatorError
+{
+public:
+    //! \copydoc FileIOError::FileIOError()
+    explicit MissingElementConnectionError(const ExceptionInitializer& details) :
+        ModularSimulatorError(details)
+    {
+    }
+};
+
+/*! \brief
+ * Exception class signalling that the ModularSimulatorAlgorithm was set up
+ * in an incompatible way.
+ *
+ * \internal
+ */
+class SimulationAlgorithmSetupError final : public ModularSimulatorError
+{
+public:
+    //! \copydoc FileIOError::FileIOError()
+    explicit SimulationAlgorithmSetupError(const ExceptionInitializer& details) :
+        ModularSimulatorError(details)
+    {
+    }
+};
+
+/*! \brief
+ * Exception class signalling an error in reading or writing modular checkpoints.
+ *
+ * \internal
+ */
+class CheckpointError final : public ModularSimulatorError
+{
+public:
+    //! \copydoc FileIOError::FileIOError()
+    explicit CheckpointError(const ExceptionInitializer& details) : ModularSimulatorError(details)
+    {
+    }
+};
+
+//! Enum allowing builders to store whether they can accept client registrations
+enum class ModularSimulatorBuilderState
+{
+    AcceptingClientRegistrations,
+    NotAcceptingClientRegistrations
+};
+
+//! Generic callback to the propagator
+typedef std::function<void(Step)> PropagatorCallback;
+
+/*! \internal
+ * \brief Strong type used to name propagators
+ */
+struct PropagatorTag
+{
+    //! Explicit constructor
+    explicit PropagatorTag(std::string_view name) : name_(name) {}
+    //! Can be used as string
+    operator const std::string&() const { return name_; }
+    //! Equality operator
+    bool operator==(const PropagatorTag& other) const { return name_ == other.name_; }
+    //! Inequality operator
+    bool operator!=(const PropagatorTag& other) const { return name_ != other.name_; }
+
+private:
+    //! The name of the propagator
+    const std::string name_;
+};
+
+/*! \internal
+ * \brief Strong type used to denote propagation time steps
+ */
+struct TimeStep
+{
+    //! Explicit constructor
+    explicit TimeStep(real timeStep) : timeStep_(timeStep) {}
+    //! Can be used as underlying type
+    operator const real&() const { return timeStep_; }
+
+private:
+    //! The time step
+    real timeStep_;
+};
+
+/*! \internal
+ * \brief Strong type used to denote scheduling offsets
+ */
+struct Offset
+{
+    //! Explicit constructor
+    explicit Offset(int offset) : offset_(offset) {}
+    //! Can be used as underlying type
+    operator const int&() const { return offset_; }
+
+private:
+    //! The offset
+    int offset_;
+};
+
+/*! \internal
+ * \brief Information needed to connect a propagator to a temperature and / or pressure coupling element
+ */
+struct PropagatorConnection
+{
+    //! The tag of the creating propagator
+    PropagatorTag tag;
+
+    //! Whether the propagator offers start velocity scaling
+    bool hasStartVelocityScaling() const
+    {
+        return setNumVelocityScalingVariables && getVelocityScalingCallback && getViewOnStartVelocityScaling;
+    }
+    //! Whether the propagator offers end velocity scaling
+    bool hasEndVelocityScaling() const
+    {
+        return setNumVelocityScalingVariables && getVelocityScalingCallback && getViewOnEndVelocityScaling;
+    }
+    //! Whether the propagator offers position scaling
+    bool hasPositionScaling() const
+    {
+        return setNumPositionScalingVariables && getPositionScalingCallback && getViewOnPositionScaling;
+    }
+    //! Whether the propagator offers Parrinello-Rahman scaling
+    bool hasParrinelloRahmanScaling() const
+    {
+        return getPRScalingCallback && getViewOnPRScalingMatrix;
+    }
+
+    //! Function object for setting velocity scaling variables
+    std::function<void(int, ScaleVelocities)> setNumVelocityScalingVariables;
+    //! Function object for setting velocity scaling variables
+    std::function<void(int)> setNumPositionScalingVariables;
+    //! Function object for receiving view on velocity scaling (before step)
+    std::function<ArrayRef<real>()> getViewOnStartVelocityScaling;
+    //! Function object for receiving view on velocity scaling (after step)
+    std::function<ArrayRef<real>()> getViewOnEndVelocityScaling;
+    //! Function object for receiving view on position scaling
+    std::function<ArrayRef<real>()> getViewOnPositionScaling;
+    //! Function object to request callback allowing to signal a velocity scaling step
+    std::function<PropagatorCallback()> getVelocityScalingCallback;
+    //! Function object to request callback allowing to signal a position scaling step
+    std::function<PropagatorCallback()> getPositionScalingCallback;
+    //! Function object for receiving view on pressure scaling matrix
+    std::function<ArrayRef<rvec>()> getViewOnPRScalingMatrix;
+    //! Function object to request callback allowing to signal a Parrinello-Rahman scaling step
+    std::function<PropagatorCallback()> getPRScalingCallback;
+};
+
+//! Enum describing whether an element is reporting conserved energy from the previous step
+enum class ReportPreviousStepConservedEnergy
+{
+    Yes,
+    No,
+    Count
+};
+
+//! Callback used by the DomDecHelper object to inform clients about system re-partitioning
+typedef std::function<void()> DomDecCallback;
+
+/*! \internal
+ * \brief Client interface of the DomDecHelper class
+ *
+ * Classes implementing this interface will register with the DomDecHelper
+ * builder object.
+ * Before the simulation, the DomDecHelper builder will call the clients'
+ * registerDomDecCallback() function and build a list of callbacks to be
+ * passed to the DomDecHelper. After every time the DomDecHelper object
+ * performed system partitioning, it will use the callbacks to inform the
+ * clients that a re-partitioning has happened.
+ */
+class IDomDecHelperClient
+{
+public:
+    //! Standard virtual destructor
+    virtual ~IDomDecHelperClient() = default;
+    //! Register function to be informed about system re-partitioning
+    virtual DomDecCallback registerDomDecCallback() = 0;
+};
+
+//! Callback updating the reference temperature
+using ReferenceTemperatureCallback =
+        std::function<void(ArrayRef<const real>, ReferenceTemperatureChangeAlgorithm algorithm)>;
+
+//! /}
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_MODULARSIMULATORINTERFACES_H
diff --git a/src/include/gromacs/modularsimulator/mttk.h b/src/include/gromacs/modularsimulator/mttk.h
new file mode 100644 (file)
index 0000000..fa03a8d
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares classes related to MTTK pressure coupling
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_MTTK_H
+#define GMX_MODULARSIMULATOR_MTTK_H
+
+#include <queue>
+
+#include "modularsimulatorinterfaces.h"
+#include "statepropagatordata.h"
+
+namespace gmx
+{
+class EnergyData;
+class MttkPropagatorConnection;
+enum class ScheduleOnInitStep;
+
+/*! \internal
+ * \brief Struct collecting the propagator tags and offsets used by the MTTK pressure coupling
+ *
+ * MTTK scales the positions before and after the propagation step, and the
+ * velocities before and after each propagation half step.
+ */
+struct MttkPropagatorConnectionDetails
+{
+    //! The tag of the position scaling before the propagation step
+    PropagatorTag propagatorTagPrePosition;
+    //! The tag of the position scaling after the propagation step
+    PropagatorTag propagatorTagPostPosition;
+    //! The offset at which position scaling is applied
+    Offset positionOffset;
+    //! The tag of the velocity scaling before the first propagation half step
+    PropagatorTag propagatorTagPreVelocity1;
+    //! The tag of the velocity scaling after the first propagation half step
+    PropagatorTag propagatorTagPostVelocity1;
+    //! The offset at which the first velocity half step scaling is applied
+    Offset velocityOffset1;
+    //! The tag of the velocity scaling before the second propagation half step
+    PropagatorTag propagatorTagPreVelocity2;
+    //! The tag of the velocity scaling after the second propagation half step
+    PropagatorTag propagatorTagPostVelocity2;
+    //! The offset at which the second velocity half step scaling is applied
+    Offset velocityOffset2;
+};
+
+/*! \internal
+ * \brief Class holding the extra dof and parameters used by the MTTK algorithm
+ *
+ * As the Trotter update is split in several sub-steps (i.e. is updated
+ * by several element instances), the MTTK degrees of freedom must be
+ * stored centrally rather than by the single elements.
+ *
+ * This class manages these extra degrees of freedom. It controls access, keeps
+ * track of the current time stamp of the dofs, calculates the energy
+ * related to the dof at the requested times, and writes the data needed
+ * for restarts to checkpoint. As this is not implementing the
+ * ISimulatorElement interface, it is not part of the simulator loop, but
+ * relies on callbacks to perform its duties.
+ */
+class MttkData final : public ICheckpointHelperClient
+{
+public:
+    //! Constructor
+    MttkData(real                       referenceTemperature,
+             real                       referencePressure,
+             real                       couplingTimeStep,
+             real                       couplingTime,
+             real                       initialVolume,
+             real                       numDegreesOfFreedom,
+             real                       simulationTimeStep,
+             const tensor               compressibility,
+             const StatePropagatorData* statePropagatorData,
+             MttkPropagatorConnection*  mttkPropagatorConnection);
+
+    //! Explicit copy constructor (interface has a standard destructor)
+    MttkData(const MttkData& other);
+
+    //! The current kinetic energy of the MTTK degree of freedom
+    [[nodiscard]] real kineticEnergy() const;
+    /*! \brief Scale the MTTK dof velocity
+     *
+     * \param scalingFactor  The factor by which the velocity is scaled
+     * \param scalingAtFullCouplingTimeStep  Whether the calling object is at full timestep
+     *                                       (determines whether the integral is calculated)
+     */
+    void scale(real scalingFactor, bool scalingAtFullCouplingTimeStep);
+
+    //! The current MTTK dof velocity
+    [[nodiscard]] real etaVelocity() const;
+    //! Set a new MTTK velocity
+    void setEtaVelocity(real etaVelocity, real etaVelocityTimeIncrement);
+
+    //! Get the inverse mass of the MTTK degree of freedom
+    [[nodiscard]] real invEtaMass() const;
+    //! Get the reference pressure
+    real referencePressure() const;
+    //! Pointer to the box velocities
+    [[nodiscard]] rvec* boxVelocities();
+
+    //! Inform the propagators that scaling is needed
+    void propagatorCallback(Step step) const;
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    //! Build object and store in builder helper object
+    static void build(LegacySimulatorData*                    legacySimulatorData,
+                      ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                      StatePropagatorData*                    statePropagatorData,
+                      EnergyData*                             energyData,
+                      const MttkPropagatorConnectionDetails&  mttkPropagatorConnectionDetails);
+
+    //! Calculate the current value of the MTTK conserved energy if it is needed
+    void calculateIntegralIfNeeded();
+
+    //! Identifier used to store objects
+    static std::string dataID();
+
+private:
+    //! Return the current value of the MTTK dof contribution to the conserved energy
+    double temperatureCouplingIntegral(Time time) const;
+    //! Update the position and velocity scaling factors
+    void updateScalingFactors();
+    //! Update the reference temperature
+    void updateReferenceTemperature(real temperature, ReferenceTemperatureChangeAlgorithm algorithm);
+    //! Calculate the MTTK conserved energy
+    void calculateIntegral(real volume);
+
+    //! The coupling time step
+    const real couplingTimeStep_;
+    //! The velocity of the MTTK dof
+    real etaVelocity_;
+    //! The inverse mass of the MTTK dof
+    real invMass_;
+    //! The current time stamp of the MTTK dof velocity
+    Time etaVelocityTime_;
+    //! The current value of the MTTK dof contribution to the conserved energy
+    double temperatureCouplingIntegral_;
+    //! The current time stamp of the conserved energy contribution
+    Time integralTime_;
+    //! The reference pressure
+    const real referencePressure_;
+    //! The current box velocities (used for reporting only)
+    tensor boxVelocity_;
+    //! The number of degrees of freedom in the first temperature group
+    const real numDegreesOfFreedom_;
+    //! The simulation time step - by how much the propagators are advancing the positions
+    const real simulationTimeStep_;
+    //! The reference temperature the mass is based on
+    real referenceTemperature_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the micro state data (access to the current box)
+    const StatePropagatorData* statePropagatorData_;
+    //! Pointer to the propagator connection object
+    MttkPropagatorConnection* mttkPropagatorConnection_;
+
+    //! CheckpointHelper identifier
+    const std::string identifier_ = "MttkData";
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+};
+
+/*! \internal
+ * \brief Object holding the callbacks and scaling views for the connection
+ *        of MTTKElement objects to propagators
+ *
+ * As the Trotter update is split in several sub-steps (i.e. is updated
+ * by several element instances), the connection to propagators must be
+ * stored centrally rather than by the single elements.
+ */
+class MttkPropagatorConnection
+{
+public:
+    //! Build object and store in builder helper object
+    static void build(ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                      const PropagatorTag&                    propagatorTagPrePosition,
+                      const PropagatorTag&                    propagatorTagPostPosition,
+                      int                                     positionOffset,
+                      const PropagatorTag&                    propagatorTagPreVelocity1,
+                      const PropagatorTag&                    propagatorTagPostVelocity1,
+                      int                                     velocityOffset1,
+                      const PropagatorTag&                    propagatorTagPreVelocity2,
+                      const PropagatorTag&                    propagatorTagPostVelocity2,
+                      int                                     velocityOffset2);
+
+    //! Call the propagator callbacks
+    void propagatorCallback(Step step) const;
+
+    //! Set position scaling
+    void setPositionScaling(real preStepScaling, real postStepScaling);
+    //! Set velocity scaling
+    void setVelocityScaling(real preStepScaling, real postStepScaling);
+
+    //! Identifier used to store objects
+    static std::string dataID();
+
+private:
+    //! Connect to pre-step velocity scaling
+    void connectWithPropagatorVelocityPreStepScaling(const PropagatorConnection& connectionData,
+                                                     const PropagatorTag&        propagatorTag,
+                                                     int                         offset);
+    //! Connect to post-step velocity scaling
+    void connectWithPropagatorVelocityPostStepScaling(const PropagatorConnection& connectionData,
+                                                      const PropagatorTag&        propagatorTag,
+                                                      int                         offset);
+    //! Connect to pre-step position scaling
+    void connectWithPropagatorPositionPreStepScaling(const PropagatorConnection& connectionData,
+                                                     const PropagatorTag&        propagatorTag,
+                                                     int                         offset);
+    //! Connect to post-step position scaling
+    void connectWithPropagatorPositionPostStepScaling(const PropagatorConnection& connectionData,
+                                                      const PropagatorTag&        propagatorTag,
+                                                      int                         offset);
+
+    //! View on the scaling factors of the position propagators (before step)
+    std::vector<ArrayRef<real>> startPositionScalingFactors_;
+    //! View on the scaling factors of the position propagators (after step)
+    std::vector<ArrayRef<real>> endPositionScalingFactors_;
+    //! View on the scaling factors of the velocity propagators (before step)
+    std::vector<ArrayRef<real>> startVelocityScalingFactors_;
+    //! View on the scaling factors of the velocity propagators (after step)
+    std::vector<ArrayRef<real>> endVelocityScalingFactors_;
+    //! Callbacks to let propagators know that we will update scaling factor, plus their offset
+    std::vector<std::tuple<PropagatorCallback, int>> propagatorCallbacks_;
+};
+
+/*! \internal
+ * \brief Element propagating the MTTK degree of freedom
+ *
+ * This roughly follows Tuckerman et al. 2006 (DOI: 10.1088/0305-4470/39/19/S18)
+ *
+ * This currently only implements the isotropic version of MTTK and does
+ * not work with constraints. It also adopts some conventions chosen by
+ * the legacy GROMACS implementation, such as using the number of degrees
+ * of freedom and the kinetic energy scaling of the first temperature group
+ * to calculate the current pressure. (It does use the full system kinetic
+ * energy, however!)
+ */
+class MttkElement final : public ISimulatorElement
+{
+public:
+    //! Constructor
+    MttkElement(int                        nstcouple,
+                int                        offset,
+                real                       propagationTimeStep,
+                ScheduleOnInitStep         scheduleOnInitStep,
+                Step                       initStep,
+                const StatePropagatorData* statePropagatorData,
+                EnergyData*                energyData,
+                MttkData*                  mttkData,
+                PbcType                    pbcType,
+                int                        numWalls,
+                real                       numDegreesOfFreedom);
+
+    /*! \brief Register run function for step / time
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! No element setup needed
+    void elementSetup() override {}
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer  Pointer to the \c ObservablesReducer object
+     * \param offset  The step offset at which the thermostat is applied
+     * \param scheduleOnInitStep  Whether the element is scheduled on the initial step
+     * \param mttkPropagatorConnectionDetails  Reference to the \c MttkPropagatorConnectionDetails object containing propagator tags and offsets
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement*
+    getElementPointerImpl(LegacySimulatorData*                    legacySimulatorData,
+                          ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                          StatePropagatorData*                    statePropagatorData,
+                          EnergyData*                             energyData,
+                          FreeEnergyPerturbationData*             freeEnergyPerturbationData,
+                          GlobalCommunicationHelper*              globalCommunicationHelper,
+                          ObservablesReducer*                     observablesReducer,
+                          Offset                                  offset,
+                          ScheduleOnInitStep                      scheduleOnInitStep,
+                          const MttkPropagatorConnectionDetails&  mttkPropagatorConnectionDetails);
+
+private:
+    //! Propagation of the MTTK dof velocity
+    void propagateEtaVelocity(Step step);
+
+    //! The periodic boundary condition type
+    const PbcType pbcType_;
+    //! The number of walls
+    const int numWalls_;
+    //! The number of degrees of freedom in the first temperature group
+    const real numDegreesOfFreedom_;
+
+    //! The period at which the thermostat is applied
+    const int nstcouple_;
+    //! If != 0, offset the step at which the thermostat is applied
+    const int offset_;
+    //! The propagation time step - by how much we propagate the MTTK dof
+    const real propagationTimeStep_;
+    //! Whether we're scheduling on the first step
+    const ScheduleOnInitStep scheduleOnInitStep_;
+    //! The initial step number
+    const Step initialStep_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the micro state
+    const StatePropagatorData* statePropagatorData_;
+    //! Pointer to the energy data (for ekindata)
+    EnergyData* energyData_;
+    //! Pointer to the MTTK data
+    MttkData* mttkData_;
+};
+
+/*! \internal
+ * \brief This element scales the box based on the MTTK dof
+ */
+class MttkBoxScaling final : public ISimulatorElement
+{
+public:
+    //! Constructor
+    MttkBoxScaling(real simulationTimeStep, StatePropagatorData* statePropagatorData, MttkData* mttkData);
+
+    /*! \brief Register run function for step / time
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! Sanity check at setup time
+    void elementSetup() override {}
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     * \param mttkPropagatorConnectionDetails  Reference to the \c MttkPropagatorConnectionDetails object containing propagator tags and offsets
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement*
+    getElementPointerImpl(LegacySimulatorData*                    legacySimulatorData,
+                          ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                          StatePropagatorData*                    statePropagatorData,
+                          EnergyData*                             energyData,
+                          FreeEnergyPerturbationData*             freeEnergyPerturbationData,
+                          GlobalCommunicationHelper*              globalCommunicationHelper,
+                          ObservablesReducer*                     observablesReducer,
+                          const MttkPropagatorConnectionDetails&  mttkPropagatorConnectionDetails);
+
+private:
+    //! Scale the box
+    void scaleBox();
+    //! The simulation time step
+    const real simulationTimeStep_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the micro state
+    StatePropagatorData* statePropagatorData_;
+    //! Pointer to the MTTK data (nullptr if this is not connected to barostat)
+    MttkData* mttkData_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_MTTK_H
diff --git a/src/include/gromacs/modularsimulator/nosehooverchains.h b/src/include/gromacs/modularsimulator/nosehooverchains.h
new file mode 100644 (file)
index 0000000..0abf32d
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares classes related to Nose-Hoover chains for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_NOSEHOOVERCHAINS_H
+#define GMX_MODULARSIMULATOR_NOSEHOOVERCHAINS_H
+
+#include <queue>
+
+#include "modularsimulatorinterfaces.h"
+
+struct t_grp_tcstat;
+
+namespace gmx
+{
+class EnergyData;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
+class ModularSimulatorAlgorithmBuilderHelper;
+struct MttkPropagatorConnectionDetails;
+class MttkData;
+class NoseHooverGroup;
+class ObservablesReducer;
+class StatePropagatorData;
+enum class UseFullStepKE;
+
+//! Whether the element does schedule on the initial step
+enum class ScheduleOnInitStep
+{
+    Yes,  //!< Schedule on first step
+    No,   //!< Do not schedule on first step
+    Count //!< Number of enum entries
+};
+
+//! The usages of Nose-Hoover chains
+enum class NhcUsage
+{
+    System,   //!< Couple system to temperature bath
+    Barostat, //!< Couple barostat to temperature bath
+    Count     //!< Number of enum entries
+};
+
+/*! \internal
+ * \brief Class holding data used by the Nose-Hoover chains
+ *
+ * As the Trotter update is split in several sub-steps (i.e. is updated
+ * by several element instances), the NHC degrees of freedom must be
+ * stored centrally rather than by the single elements.
+ *
+ * This class manages these extra degrees of freedom. It controls access
+ * (making sure that only one element has write access at a time), keeps
+ * track of the current time stamp of the dofs, calculates the energy
+ * related to the dof at the requested times, and writes the data needed
+ * for restarts to checkpoint. As this is not implementing the
+ * ISimulatorElement interface, it is not part of the simulator loop, but
+ * relies on callbacks to perform its duties.
+ */
+class NoseHooverChainsData final : public ICheckpointHelperClient
+{
+public:
+    //! Constructor
+    NoseHooverChainsData(int                  numTemperatureGroups,
+                         real                 couplingTimeStep,
+                         int                  chainLength,
+                         ArrayRef<const real> referenceTemperature,
+                         ArrayRef<const real> couplingTime,
+                         ArrayRef<const real> numDegreesOfFreedom,
+                         NhcUsage             nhcUsage);
+
+    //! Explicit copy constructor (interface has a standard destructor)
+    NoseHooverChainsData(const NoseHooverChainsData& other);
+
+    /*! \brief Propagate the NHC degrees of freedom for a temperature group and
+     *        return its current velocity scaling value
+     *
+     * \param temperatureGroup  The temperature group to be propagated
+     * \param propagationTimeStep  The time step by which the DOF are propagated
+     * \param currentKineticEnergy  The current kinetic energy of the temperature group
+     * \return  The current velocity scaling value for the temperature group
+     */
+    real applyNhc(int temperatureGroup, double propagationTimeStep, real currentKineticEnergy);
+
+    //! The number of temperature groups
+    int numTemperatureGroups() const;
+    //! Whether the NHC degrees of freedom are at a full coupling time step
+    bool isAtFullCouplingTimeStep() const;
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    //! Build object and store in builder helper object
+    static void build(NhcUsage                                nhcUsage,
+                      LegacySimulatorData*                    legacySimulatorData,
+                      ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                      EnergyData*                             energyData);
+
+    //! Identifier used to store objects
+    static std::string dataID(NhcUsage nhcUsage);
+
+private:
+    //! Return the value of the coupling integral at a specific time
+    double temperatureCouplingIntegral(Time time) const;
+    //! Update the reference temperature
+    void updateReferenceTemperature(ArrayRef<const real>                temperatures,
+                                    ReferenceTemperatureChangeAlgorithm algorithm);
+
+    //! CheckpointHelper identifier
+    const std::string identifier_;
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+
+    //! List of temperature groups with Nose-Hoover chains
+    std::vector<NoseHooverGroup> noseHooverGroups_;
+
+    //! The number of temperature groups
+    const int numTemperatureGroups_;
+};
+
+/*! \internal
+ * \brief Element propagating the Nose-Hoover chains
+ *
+ * This propagates the Nose-Hoover chain degrees of freedom, and
+ * transmits the scaling factor to a connected propagator.
+ */
+class NoseHooverChainsElement final : public ISimulatorElement
+{
+public:
+    //! Constructor
+    NoseHooverChainsElement(int                   nstcouple,
+                            int                   offset,
+                            NhcUsage              nhcUsage,
+                            UseFullStepKE         useFullStepKE,
+                            double                propagationTimeStep,
+                            ScheduleOnInitStep    scheduleOnInitStep,
+                            Step                  initStep,
+                            EnergyData*           energyData,
+                            NoseHooverChainsData* noseHooverChainData,
+                            MttkData*             mttkData);
+
+    /*! \brief Register run function for step / time
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! Sanity check at setup time
+    void elementSetup() override;
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    //! Connect this to propagator
+    void connectWithPropagator(const PropagatorConnection& connectionData,
+                               const PropagatorTag&        propagatorTag);
+
+    /*! \brief Factory method implementation (no propagator connection)
+     *
+     * This signature is used to connect a Nose-Hoover chain to a barostat
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     * \param nhcUsage  What the NHC is connected to - system or barostat
+     * \param offset  The step offset at which the thermostat is applied
+     * \param useFullStepKE  Whether full step or half step KE is used
+     * \param scheduleOnInitStep  Whether the element is scheduled on the initial step
+     * \param mttkPropagatorConnectionDetails  Connection information for the MTTK barostat
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement*
+    getElementPointerImpl(LegacySimulatorData*                    legacySimulatorData,
+                          ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                          StatePropagatorData*                    statePropagatorData,
+                          EnergyData*                             energyData,
+                          FreeEnergyPerturbationData*             freeEnergyPerturbationData,
+                          GlobalCommunicationHelper*              globalCommunicationHelper,
+                          ObservablesReducer*                     observablesReducer,
+                          NhcUsage                                nhcUsage,
+                          Offset                                  offset,
+                          UseFullStepKE                           useFullStepKE,
+                          ScheduleOnInitStep                      scheduleOnInitStep,
+                          const MttkPropagatorConnectionDetails&  mttkPropagatorConnectionDetails);
+
+    /*! \brief Factory method implementation (including propagator connection)
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     * \param nhcUsage  What the NHC is connected to - system or barostat
+     * \param offset  The step offset at which the thermostat is applied
+     * \param useFullStepKE  Whether full step or half step KE is used
+     * \param scheduleOnInitStep  Whether the element is scheduled on the initial step
+     * \param propagatorTag  Tag of the propagator to connect to
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    ObservablesReducer*        observablesReducer,
+                                                    NhcUsage                   nhcUsage,
+                                                    Offset                     offset,
+                                                    UseFullStepKE              useFullStepKE,
+                                                    ScheduleOnInitStep         scheduleOnInitStep,
+                                                    const PropagatorTag&       propagatorTag);
+
+private:
+    //! Propagate the NHC degrees of freedom
+    void propagateNhc();
+    //! Helper function returning the appropriate kinetic energy
+    real currentKineticEnergy(const t_grp_tcstat& tcstat);
+
+    //! View on the scaling factor of the propagator (pre-step velocities)
+    ArrayRef<real> lambdaStartVelocities_;
+    //! Callback to let propagator know that we will update lambda
+    PropagatorCallback propagatorCallback_;
+
+    //! The period at which the thermostat is applied
+    const int nsttcouple_;
+    //! If != 0, offset the step at which the thermostat is applied
+    const int offset_;
+    //! The propagation time step - by how much we propagate the NHC dof
+    const double propagationTimeStep_;
+    //! Whether this NHC is acting on the system or a barostat
+    const NhcUsage nhcUsage_;
+    //! Whether we're using full step kinetic energy
+    const UseFullStepKE useFullStepKE_;
+    //! Whether we're scheduling on the first step
+    const ScheduleOnInitStep scheduleOnInitStep_;
+    //! The initial step number
+    const Step initialStep_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the energy data (for ekindata)
+    EnergyData* energyData_;
+    //! Pointer to the NHC data
+    NoseHooverChainsData* noseHooverChainData_;
+    //! Pointer to the MTTK data (nullptr if this is not connected to barostat)
+    MttkData* mttkData_;
+};
+
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_NOSEHOOVERCHAINS_H
diff --git a/src/include/gromacs/modularsimulator/parrinellorahmanbarostat.h b/src/include/gromacs/modularsimulator/parrinellorahmanbarostat.h
new file mode 100644 (file)
index 0000000..0e08fb5
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the Parrinello-Rahman barostat for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_PARRINELLORAHMANBAROSTAT_H
+#define GMX_MODULARSIMULATOR_PARRINELLORAHMANBAROSTAT_H
+
+#include "gromacs/math/vectypes.h"
+
+#include "modularsimulatorinterfaces.h"
+#include "propagator.h"
+
+struct t_inputrec;
+struct t_commrec;
+
+namespace gmx
+{
+enum class CheckpointDataOperation;
+class EnergyData;
+class LegacySimulatorData;
+class MDAtoms;
+class ObservablesReducer;
+class StatePropagatorData;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element implementing the Parrinello-Rahman barostat
+ *
+ * This element
+ *   * integrates the Parrinello-Rahman box velocity equations,
+ *   * takes a callback to the propagator to update the velocity
+ *     scaling factor, and
+ *   * scales the box and the positions of the system.
+ */
+class ParrinelloRahmanBarostat final : public ISimulatorElement, public ICheckpointHelperClient, public IEnergySignallerClient
+{
+public:
+    //! Constructor
+    ParrinelloRahmanBarostat(int                  nstpcouple,
+                             int                  offset,
+                             real                 couplingTimeStep,
+                             Step                 initStep,
+                             StatePropagatorData* statePropagatorData,
+                             EnergyData*          energyData,
+                             FILE*                fplog,
+                             const t_inputrec*    inputrec,
+                             const MDAtoms*       mdAtoms);
+
+    /*! \brief Register run function for step / time
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! Fix relative box shape
+    void elementSetup() override;
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    //! Getter for the box velocities
+    [[nodiscard]] const rvec* boxVelocities() const;
+
+    //! Connect this to propagator
+    void connectWithMatchingPropagator(const PropagatorConnection& connectionData,
+                                       const PropagatorTag&        propagatorTag);
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     * \param propagatorTag  Tag of the propagator to connect to
+     * \param offset  The step offset at which the barostat is applied
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement*
+    getElementPointerImpl(LegacySimulatorData*                    legacySimulatorData,
+                          ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                          StatePropagatorData*                    statePropagatorData,
+                          EnergyData*                             energyData,
+                          FreeEnergyPerturbationData gmx_unused* freeEnergyPerturbationData,
+                          GlobalCommunicationHelper gmx_unused* globalCommunicationHelper,
+                          ObservablesReducer*                   observablesReducer,
+                          Offset                                offset,
+                          const PropagatorTag&                  propagatorTag);
+
+private:
+    //! The frequency at which the barostat is applied
+    const int nstpcouple_;
+    //! If != 0, offset the step at which the barostat is applied
+    const int offset_;
+    //! The coupling time step - simulation time step x nstcouple_
+    const real couplingTimeStep_;
+    //! The first step of the simulation
+    const Step initStep_;
+
+    //! View on the velocity scaling tensor (owned by the propagator)
+    ArrayRef<rvec> scalingTensor_;
+    //! Callback to let propagator know that we updated lambda
+    PropagatorCallback propagatorCallback_;
+
+    //! Relative change in box before - after barostatting
+    matrix mu_;
+    //! Relative box shape
+    tensor boxRel_;
+    //! Box velocity
+    tensor boxVelocity_;
+
+    //! Current conserved energy contribution
+    real conservedEnergyContribution_;
+    //! Step of current conserved energy contribution
+    Step conservedEnergyContributionStep_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the micro state
+    StatePropagatorData* statePropagatorData_;
+    //! Pointer to the energy data
+    EnergyData* energyData_;
+
+    //! Integrate the PR box vector equations of motion - does not alter state
+    void integrateBoxVelocityEquations(Step step);
+    //! Scale box and positions
+    void scaleBoxAndPositions();
+
+    //! CheckpointHelper identifier
+    const std::string identifier_ = "ParrinelloRahmanBarostat";
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+
+    //! IEnergySignallerClient implementation
+    std::optional<SignallerCallback> registerEnergyCallback(EnergySignallerEvent event) override;
+    //! The next communicated energy calculation step
+    Step nextEnergyCalculationStep_;
+
+    //! Contribution to the conserved energy
+    [[nodiscard]] real conservedEnergyContribution() const;
+
+    // Access to ISimulator data
+    //! Handles logging.
+    FILE* fplog_;
+    //! Contains user input mdp options.
+    const t_inputrec* inputrec_;
+    //! Atom parameters for this domain.
+    const MDAtoms* mdAtoms_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_PARRINELLORAHMANBAROSTAT_H
diff --git a/src/include/gromacs/modularsimulator/pmeloadbalancehelper.h b/src/include/gromacs/modularsimulator/pmeloadbalancehelper.h
new file mode 100644 (file)
index 0000000..29e5f99
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the PME load balancing helper for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_PMELOADBALANCEHELPER_H
+#define GMX_MODULARSIMULATOR_PMELOADBALANCEHELPER_H
+
+#include "modularsimulatorinterfaces.h"
+
+struct gmx_wallcycle;
+struct pme_load_balancing_t;
+struct t_commrec;
+struct t_forcerec;
+struct t_inputrec;
+
+namespace gmx
+{
+class MDLogger;
+struct MdrunOptions;
+class StatePropagatorData;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Infrastructure element responsible for PME load balancing
+ *
+ * This encapsulates the function call to PME load balancing, which is
+ * important for performance but outside of the current scope of the modular
+ * simulator project. This relies on legacy data structures for the state.
+ *
+ * This element does not implement the ISimulatorElement interface, as
+ * the Simulator is calling it explicitly between task queue population
+ * steps. This allows elements to be aware of any changes before
+ * deciding what functionality they need to run.
+ */
+class PmeLoadBalanceHelper final : public INeighborSearchSignallerClient
+{
+public:
+    //! Constructor
+    PmeLoadBalanceHelper(bool                 isVerbose,
+                         StatePropagatorData* statePropagatorData,
+                         FILE*                fplog,
+                         t_commrec*           cr,
+                         const MDLogger&      mdlog,
+                         const t_inputrec*    inputrec,
+                         gmx_wallcycle*       wcycle,
+                         t_forcerec*          fr);
+
+    //! Initialize the load balancing object
+    void setup();
+    //! Do load balancing
+    void run(Step step, Time time);
+    //! Final printout and deconstruction of the load balancing object
+    void teardown();
+    //! Whether PME load balancing printing is active \todo Check this!
+    bool pmePrinting();
+
+    //! Whether we're doing PME load balancing
+    static bool doPmeLoadBalancing(const MdrunOptions& mdrunOptions,
+                                   const t_inputrec*   inputrec,
+                                   const t_forcerec*   fr);
+
+    //! Direct access to the load balancing object - used by reset counter
+    const pme_load_balancing_t* loadBalancingObject();
+
+private:
+    //! The PME load balancing object - used by reset counter
+    pme_load_balancing_t* pme_loadbal_;
+
+    //! INeighborSearchSignallerClient implementation
+    std::optional<SignallerCallback> registerNSCallback() override;
+
+    //! The next NS step
+    Step nextNSStep_;
+    //! Whether we're being verbose
+    const bool isVerbose_;
+    //! Whether PME load balancing printing is active \todo Check this!
+    bool bPMETunePrinting_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the micro state
+    StatePropagatorData* statePropagatorData_;
+
+    // Access to ISimulator data
+    //! Handles logging.
+    FILE* fplog_;
+    //! Handles communication.
+    t_commrec* cr_;
+    //! Handles logging.
+    const MDLogger& mdlog_;
+    //! Contains user input mdp options.
+    const t_inputrec* inputrec_;
+    //! Manages wall cycle accounting.
+    gmx_wallcycle* wcycle_;
+    //! Parameters for force calculations.
+    t_forcerec* fr_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_PMELOADBALANCEHELPER_H
diff --git a/src/include/gromacs/modularsimulator/propagator.h b/src/include/gromacs/modularsimulator/propagator.h
new file mode 100644 (file)
index 0000000..02c32ed
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the propagator element for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_PROPAGATOR_H
+#define GMX_MODULARSIMULATOR_PROPAGATOR_H
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+#include "modularsimulatorinterfaces.h"
+
+struct gmx_wallcycle;
+
+namespace gmx
+{
+class EnergyData;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
+class MDAtoms;
+class ModularSimulatorAlgorithmBuilderHelper;
+class ObservablesReducer;
+class StatePropagatorData;
+
+//! \addtogroup module_modularsimulator
+//! \{
+
+//! Which velocities the thermostat scales
+enum class ScaleVelocities
+{
+    PreStepOnly,
+    PreStepAndPostStep
+};
+
+//! The different integration types we know about
+enum class IntegrationStage
+{
+    PositionsOnly,                        //!< Moves the position vector by the given time step
+    VelocitiesOnly,                       //!< Moves the velocity vector by the given time step
+    LeapFrog,                             //!< Manual fusion of the previous two propagators
+    VelocityVerletPositionsAndVelocities, //!< Manual position (full dt) and velocity (half dt) fusion
+    ScaleVelocities,                      //!< Only scale velocities, don't propagate
+    ScalePositions,                       //!< Only scale positions, don't propagate
+    Count                                 //!< The number of enum entries
+};
+
+//! Sets the number of different position scaling values
+enum class NumPositionScalingValues
+{
+    None,     //!< No position scaling (either this step or ever)
+    Single,   //!< Single scaling value (either one group or all values =1)
+    Multiple, //!< Multiple scaling values, need to use T-group indices
+    Count     //!< The number of enum entries
+};
+
+//! Sets the number of different velocity scaling values
+enum class NumVelocityScalingValues
+{
+    None,     //!< No velocity scaling (either this step or ever)
+    Single,   //!< Single T-scaling value (either one group or all values =1)
+    Multiple, //!< Multiple T-scaling values, need to use T-group indices
+    Count
+};
+
+//! Sets the type of Parrinello-Rahman pressure scaling
+enum class ParrinelloRahmanVelocityScaling
+{
+    No,       //!< Do not apply velocity scaling (not a PR-coupling run or step)
+    Diagonal, //!< Apply velocity scaling using a diagonal matrix
+    Full,     //!< Apply velocity scaling using a full matrix
+    Count
+};
+
+/*! \internal
+ * \brief Propagator element
+ *
+ * The propagator element can, through templating, cover the different
+ * propagation types used in NVE MD. The combination of templating, static
+ * functions, and having only the inner-most operations in the static
+ * functions allows to have performance comparable to fused update elements
+ * while keeping easily re-orderable single instructions.
+ *
+ * \tparam integrationStep  The integration types
+ */
+template<IntegrationStage integrationStage>
+class Propagator final : public ISimulatorElement
+{
+public:
+    //! Constructor
+    Propagator(double               timestep,
+               StatePropagatorData* statePropagatorData,
+               const MDAtoms*       mdAtoms,
+               gmx_wallcycle*       wcycle);
+
+    /*! \brief Register run function for step / time
+     *
+     * This function will determine the required flavor of the run function to be registered
+     * for the current step. In case of the pure scaling integrator stage, it might also skip
+     * the function registration if no scaling is needed.
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! No element setup needed
+    void elementSetup() override {}
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    //! Set the number of velocity scaling variables
+    void setNumVelocityScalingVariables(int numVelocityScalingVariables, ScaleVelocities scaleVelocities);
+    //! Set the number of position scaling variables
+    void setNumPositionScalingVariables(int numPositionScalingVariables);
+    //! Get view on the scaling vector applied to start of step velocities
+    ArrayRef<real> viewOnStartVelocityScaling();
+    //! Get view on the scaling vector applied to end of step velocities
+    ArrayRef<real> viewOnEndVelocityScaling();
+    //! Get view on the scaling vector applied to the positions
+    ArrayRef<real> viewOnPositionScaling();
+    //! Get velocity scaling callback
+    PropagatorCallback velocityScalingCallback();
+    //! Get position scaling callback
+    PropagatorCallback positionScalingCallback();
+
+    //! Get view on the full PR scaling matrix
+    ArrayRef<rvec> viewOnPRScalingMatrix();
+    //! Get PR scaling callback
+    PropagatorCallback prScalingCallback();
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     * \param propagatorTag  The name of the propagator to simplify connection
+     * \param timestep  The time step the propagator uses
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    ObservablesReducer*        observablesReducer,
+                                                    const PropagatorTag&       propagatorTag,
+                                                    TimeStep                   timestep);
+
+    /*! \brief Factory method implementation
+     *
+     * Version without time step for pure scaling elements
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     * \param propagatorTag  The name of the propagator to simplify connection
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    ObservablesReducer*        observablesReducer,
+                                                    const PropagatorTag&       propagatorTag);
+
+private:
+    //! The actual propagation
+    template<NumVelocityScalingValues        numStartVelocityScalingValues,
+             ParrinelloRahmanVelocityScaling parrinelloRahmanVelocityScaling,
+             NumVelocityScalingValues        numEndVelocityScalingValues,
+             NumPositionScalingValues        numPositionScalingValues>
+    void run();
+
+    //! The time step
+    const real timestep_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the micro state
+    StatePropagatorData* statePropagatorData_;
+
+    //! Whether we're doing single-value velocity scaling (velocities at start of step)
+    bool doSingleStartVelocityScaling_;
+    //! Whether we're doing group-wise velocity scaling (velocities at start of step)
+    bool doGroupStartVelocityScaling_;
+    //! Whether we're doing single-value velocity scaling (velocities at end of step)
+    bool doSingleEndVelocityScaling_;
+    //! Wether we're doing group-wise velocity scaling (velocities at end of step)
+    bool doGroupEndVelocityScaling_;
+    //! Whether we're doing single-value position scaling
+    bool doSinglePositionScaling_;
+    //! Whether we're doing group-wise position scaling
+    bool doGroupPositionScaling_;
+    //! The vector of velocity scaling values
+    std::vector<real> startVelocityScaling_;
+    //! The vector of velocity scaling values
+    std::vector<real> endVelocityScaling_;
+    //! The vector of position scaling values
+    std::vector<real> positionScaling_;
+    //! The next velocity scaling step
+    Step scalingStepVelocity_;
+    //! The next position scaling step
+    Step scalingStepPosition_;
+
+    //! The diagonal of the PR scaling matrix
+    rvec diagPR_;
+    //! The full PR scaling matrix
+    matrix matrixPR_;
+    //! The next PR scaling step
+    Step scalingStepPR_;
+
+    // Access to ISimulator data
+    //! Atom parameters for this domain.
+    const MDAtoms* mdAtoms_;
+    //! Manages wall cycle accounting.
+    gmx_wallcycle* wcycle_;
+};
+
+//! \}
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_PROPAGATOR_H
diff --git a/src/include/gromacs/modularsimulator/pullelement.h b/src/include/gromacs/modularsimulator/pullelement.h
new file mode 100644 (file)
index 0000000..fd49e9e
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the pull element for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_PULLELEMENT_H
+#define GMX_MODULARSIMULATOR_PULLELEMENT_H
+
+#include "modularsimulatorinterfaces.h"
+
+struct gmx_mtop_t;
+struct pull_t;
+struct t_inputrec;
+
+namespace gmx
+{
+class EnergyData;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
+class MDAtoms;
+class ModularSimulatorAlgorithmBuilderHelper;
+class ObservablesReducer;
+class StatePropagatorData;
+
+/*! \internal
+ * \brief Element calling pull functionality
+ */
+class PullElement : public ISimulatorElement, public ICheckpointHelperClient
+{
+public:
+    //! Constructor
+    PullElement(bool                 setPbcRefToPrevStepCOM,
+                PbcType              pbcType,
+                StatePropagatorData* statePropagatorData,
+                pull_t*              pullWork,
+                const t_commrec*     commrec,
+                const MDAtoms*       mdAtoms);
+    //! Update annealing temperature
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+    //! Set initial annealing temperature
+    void elementSetup() override;
+    //! No teardown needed
+    void elementTeardown() override {}
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper  Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    ObservablesReducer*        observablesReducer);
+
+private:
+    //! Schedule post step functionality
+    void schedulePostStep(Step step, Time time, const RegisterRunFunction& registerRunFunction);
+
+    //! Whether to use the COM of each group from the previous step as reference
+    const bool setPbcRefToPrevStepCOM_;
+    //! The PBC type
+    const PbcType pbcType_;
+
+    //! CheckpointHelper identifier
+    const std::string identifier_ = "PullElement";
+    //! Whether this object was restored from checkpoint
+    bool restoredFromCheckpoint_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the micro state
+    StatePropagatorData* statePropagatorData_;
+
+    // Access to LegacySimulatorData
+    //! The pull work object.
+    pull_t* pullWork_;
+    //! Handles communication.
+    const t_commrec* commrec_;
+    //! Atom parameters for this domain.
+    const MDAtoms* mdAtoms_;
+};
+} // namespace gmx
+
+
+#endif // GMX_MODULARSIMULATOR_PULLELEMENT_H
diff --git a/src/include/gromacs/modularsimulator/referencetemperaturemanager.h b/src/include/gromacs/modularsimulator/referencetemperaturemanager.h
new file mode 100644 (file)
index 0000000..422565f
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the a helper struct managing reference temperature changes
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_REFERENCETEMPERATUREMANAGER_H
+#define GMX_MODULARSIMULATOR_REFERENCETEMPERATUREMANAGER_H
+
+#include <functional>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+#include "modularsimulatorinterfaces.h"
+
+struct t_inputrec;
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief The algorithm changing the reference temperature
+ *
+ * In the legacy implementation, reference temperature changes by
+ * different algorithms are not handled identically. This enum is
+ * used to inform clients what algorithm caused the temperature
+ * change, allowing them to customize their response.
+ */
+enum class ReferenceTemperatureChangeAlgorithm
+{
+};
+
+/*! \internal
+ * \brief Object managing reference temperature changes
+ *
+ * The ReferenceTemperatureManager allows to change the reference
+ * temperatures of the temperature groups. Elements can register a callback
+ * if they need to be informed about changes.
+ *
+ * The ReferenceTemperatureManager updates the inputrec. Elements
+ * might, however, have a copy of the reference temperature they
+ * need updated, or perform another action upon change of the
+ * reference temperature (e.g. velocity scaling or recalculating
+ * a temperature coupling integral).
+ */
+class ReferenceTemperatureManager final
+{
+public:
+    //! Constructor
+    ReferenceTemperatureManager(t_inputrec* inputrec);
+    //! Register a callback for reference temperature update
+    void registerUpdateCallback(ReferenceTemperatureCallback referenceTemperatureCallback);
+    //! Set reference temperatures (one per temperature group)
+    void setReferenceTemperature(ArrayRef<const real>                newReferenceTemperatures,
+                                 ReferenceTemperatureChangeAlgorithm algorithm);
+
+private:
+    //! List of callbacks
+    std::vector<ReferenceTemperatureCallback> callbacks_;
+    //! Pointer to the input record
+    t_inputrec* inputrec_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_REFERENCETEMPERATUREMANAGER_H
diff --git a/src/include/gromacs/modularsimulator/signallers.h b/src/include/gromacs/modularsimulator/signallers.h
new file mode 100644 (file)
index 0000000..b9c50c7
--- /dev/null
@@ -0,0 +1,559 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the signallers for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_SIGNALLERS_H
+#define GMX_MODULARSIMULATOR_SIGNALLERS_H
+
+#include <vector>
+
+#include "gromacs/compat/pointers.h"
+
+#include "modularsimulatorinterfaces.h"
+
+namespace gmx
+{
+class StopHandler;
+class TrajectoryElement;
+enum class StartingBehavior;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Builder for signallers
+ *
+ * This builder allows clients to register, and then builds the signaller
+ * passing on the list of clients.
+ *
+ * \tparam Signaller  The signaller to be built
+ */
+template<typename Signaller>
+class SignallerBuilder final
+{
+public:
+    //! Allows clients to register to the signaller
+    void registerSignallerClient(typename Signaller::Client* client);
+
+    //! Build the signaller
+    template<typename... Args>
+    std::unique_ptr<Signaller> build(Args&&... args);
+
+private:
+    //! List of signaller clients
+    std::vector<typename Signaller::Client*> signallerClients_;
+    //! The state of the builder
+    ModularSimulatorBuilderState state_ = ModularSimulatorBuilderState::AcceptingClientRegistrations;
+
+    //! Helper function to get the callbacks from the clients
+    template<typename... Args>
+    std::vector<SignallerCallback> buildCallbackVector(Args&&... args);
+
+    /*! \brief Get a callback from a single client
+     *
+     * This is in a separate function, as the exact call depends on the
+     * specific signaller / client.
+     */
+    template<typename... Args>
+    std::optional<SignallerCallback> getSignallerCallback(typename Signaller::Client* client,
+                                                          Args&&... args);
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element signalling a neighbor search step
+ *
+ * This element informs its clients via callbacks
+ * when a neighbor-searching step is happening.
+ */
+class NeighborSearchSignaller final : public ISignaller
+{
+public:
+    /*! \brief Run the signaller at a specific step / time
+     *
+     * Informs callbacks if step % nstlist_ == 0
+     *
+     * \param step  The current time step
+     * \param time  The current time
+     */
+    void signal(Step step, Time time) override;
+
+    //! Do nothing at setup time
+    void setup() override{};
+
+    //! Allow builder to construct
+    friend class SignallerBuilder<NeighborSearchSignaller>;
+    //! Define client type
+    typedef INeighborSearchSignallerClient Client;
+
+private:
+    /*! \brief Constructor
+     *
+     * \param callbacks  A vector of pointers to callbacks
+     * \param nstlist    The frequency at which neighbor search is performed
+     * \param initStep   The first step of the simulation
+     * \param initTime   The start time of the simulation
+     */
+    NeighborSearchSignaller(std::vector<SignallerCallback> callbacks, Step nstlist, Step initStep, Time initTime);
+
+    //! Client callbacks
+    std::vector<SignallerCallback> callbacks_;
+
+    //! The NS frequency
+    const Step nstlist_;
+    //! The initial step of the simulation
+    const Step initStep_;
+    //! The initial time of the simulation
+    const Time initTime_;
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element signalling the last step
+ *
+ * This element informs its clients via callbacks
+ * when the last step is happening.
+ */
+class LastStepSignaller final : public ISignaller, public INeighborSearchSignallerClient
+{
+public:
+    /*! \brief Run the signaller at a specific step / time
+     *
+     * Informs callbacks if this is the last step
+     *
+     * \param step  The current time step
+     * \param time  The current time
+     */
+    void signal(Step step, Time time) override;
+
+    //! Check that necessary registration was done
+    void setup() override;
+
+    //! Allow builder to construct
+    friend class SignallerBuilder<LastStepSignaller>;
+    //! Define client type
+    typedef ILastStepSignallerClient Client;
+
+private:
+    /*! \brief Constructor
+     *
+     * \param callbacks    A vector of pointers to callbacks
+     * \param nsteps       The total number of steps for the simulation
+     * \param initStep     The first step of the simulation
+     * \param stopHandler  A pointer to the stop handler (LastStepSignaller takes ownership)
+     */
+    LastStepSignaller(std::vector<SignallerCallback> callbacks, Step nsteps, Step initStep, StopHandler* stopHandler);
+
+    //! Client callbacks
+    std::vector<SignallerCallback> callbacks_;
+
+    //! The last step of the simulation
+    const Step stopStep_;
+    //! Whether we signalled last step due to stop condition
+    bool signalledStopCondition_;
+    //! A pointer to the stop handler communicating signal and time-related stops
+    StopHandler* stopHandler_;
+
+    //! INeighborSearchSignallerClient implementation
+    std::optional<SignallerCallback> registerNSCallback() override;
+    //! The next NS step (notified by NS signaller)
+    Step nextNSStep_;
+    //! Whether we registered to the NS signaller
+    bool nsStepRegistrationDone_;
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element signalling a logging step
+ *
+ * This element informs its clients via callbacks
+ * when a logging step is happening.
+ */
+class LoggingSignaller final : public ISignaller, public ILastStepSignallerClient
+{
+public:
+    /*! \brief Run the signaller at a specific step / time
+     *
+     * Informs callbacks if step % nstlog_ == 0
+     *
+     * \param step  The current time step
+     * \param time  The current time
+     */
+    void signal(Step step, Time time) override;
+
+    //! Check that necessary registration was done
+    void setup() override;
+
+    //! Allow builder to construct
+    friend class SignallerBuilder<LoggingSignaller>;
+    //! Define client type
+    typedef ILoggingSignallerClient Client;
+
+private:
+    /*! \brief Constructor
+     *
+     * \param callbacks         A vector of pointers to callbacks
+     * \param nstlog            The logging frequency
+     * \param initStep          The first step of the simulation
+     * \param startingBehavior  Whether this is a new simulation or restarting from checkpoint
+     */
+    LoggingSignaller(std::vector<SignallerCallback> callbacks,
+                     Step                           nstlog,
+                     Step                           initStep,
+                     StartingBehavior               startingBehavior);
+
+    //! Client callbacks
+    std::vector<SignallerCallback> callbacks_;
+
+    //! The logging frequency
+    const Step nstlog_;
+    //! The initial step of the simulation
+    const Step initStep_;
+    //! How we are starting the simulation
+    const StartingBehavior startingBehavior_;
+
+    //! ILastStepSignallerClient implementation
+    std::optional<SignallerCallback> registerLastStepCallback() override;
+    //! The last step (notified by signaller)
+    Step lastStep_;
+    //! Whether we registered to the last step signaller
+    bool lastStepRegistrationDone_;
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element signalling trajectory writing
+ *
+ * During signalling phase, it checks whether the current step is a writing
+ * step for either the energy or the state (position, velocity, forces)
+ * trajectory. It then notifies the signaller clients of the upcoming step.
+ *
+ * The TrajectorySignaller works in close collaboration with the TrajectoryElement
+ * which does the actual trajectory writing during the simulation step.
+ */
+class TrajectorySignaller final : public ISignaller, public ILastStepSignallerClient
+{
+public:
+    /*! \brief Prepare signaller
+     *
+     * Check that necessary registration was done
+     */
+    void setup() override;
+
+    /*! \brief Run the signaller at a specific step / time
+     *
+     * Informs clients when energy or state will be written.
+     *
+     * \param step           The current time step
+     * \param time           The current time
+     */
+    void signal(Step step, Time time) override;
+
+    //! Allow builder to construct
+    friend class SignallerBuilder<TrajectorySignaller>;
+    //! Define client type
+    typedef ITrajectorySignallerClient Client;
+
+private:
+    //! Constructor
+    TrajectorySignaller(std::vector<SignallerCallback> signalEnergyCallbacks,
+                        std::vector<SignallerCallback> signalStateCallbacks,
+                        int                            nstxout,
+                        int                            nstvout,
+                        int                            nstfout,
+                        int                            nstxoutCompressed,
+                        int                            tngBoxOut,
+                        int                            tngLambdaOut,
+                        int                            tngBoxOutCompressed,
+                        int                            tngLambdaOutCompressed,
+                        int                            nstenergy);
+
+    //! Output frequencies
+    //! {
+    const int nstxout_;
+    const int nstvout_;
+    const int nstfout_;
+    const int nstxoutCompressed_;
+    const int tngBoxOut_;
+    const int tngLambdaOut_;
+    const int tngBoxOutCompressed_;
+    const int tngLambdaOutCompressed_;
+    const int nstenergy_;
+    //! }
+
+    //! Callbacks to signal events
+    //! {
+    std::vector<SignallerCallback> signalEnergyCallbacks_;
+    std::vector<SignallerCallback> signalStateCallbacks_;
+    //! }
+
+    /*
+     * Last step client
+     */
+    Step lastStep_;
+    bool lastStepRegistrationDone_;
+    //! ILastStepSignallerClient implementation
+    std::optional<SignallerCallback> registerLastStepCallback() override;
+};
+
+//! When we calculate virial
+enum class EnergySignallerVirialMode
+{
+    Off,           //!< No specific virial calculation - calculate when energy is calculated
+    OnStep,        //!< Calculate on virial frequency steps
+    OnStepAndNext, //!< Calculate on virial frequency steps and on step after
+    Count          //!< The number of entries
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element signalling energy related special steps
+ *
+ * This element informs its clients via callbacks
+ * of the following events:
+ *   - energy calculation step
+ *   - virial calculation step
+ *   - free energy calculation step
+ */
+class EnergySignaller final : public ISignaller, public ITrajectorySignallerClient, public ILoggingSignallerClient
+{
+public:
+    /*! \brief Run the signaller at a specific step / time
+     *
+     * Informs callbacks of energy / virial / free energy special steps
+     *
+     * \param step  The current time step
+     * \param time  The current time
+     */
+    void signal(Step step, Time time) override;
+
+    //! Check that necessary registration was done
+    void setup() override;
+
+    //! Allow builder to construct
+    friend class SignallerBuilder<EnergySignaller>;
+    //! Define client type
+    typedef IEnergySignallerClient Client;
+
+private:
+    /*! \brief Constructor
+     *
+     * \param calculateEnergyCallbacks      A vector of pointers to callbacks (energy steps)
+     * \param calculateVirialCallbacks      A vector of pointers to callbacks (virial steps)
+     * \param calculateFreeEnergyCallbacks  A vector of pointers to callbacks (free energy steps)
+     * \param nstcalcenergy                 The energy calculation frequency
+     * \param nstcalcfreeenergy             The free energy calculation frequency
+     * \param nstcalcvirial                 The free energy calculation frequency
+     * \param virialMode                    Indicates which steps will calculate virial
+     */
+    EnergySignaller(std::vector<SignallerCallback> calculateEnergyCallbacks,
+                    std::vector<SignallerCallback> calculateVirialCallbacks,
+                    std::vector<SignallerCallback> calculateFreeEnergyCallbacks,
+                    int                            nstcalcenergy,
+                    int                            nstcalcfreeenergy,
+                    int                            nstcalcvirial,
+                    EnergySignallerVirialMode      virialMode);
+
+    //! Client callbacks
+    //! {
+    std::vector<SignallerCallback> calculateEnergyCallbacks_;
+    std::vector<SignallerCallback> calculateVirialCallbacks_;
+    std::vector<SignallerCallback> calculateFreeEnergyCallbacks_;
+    //! }
+
+    //! The energy calculation frequency
+    const int nstcalcenergy_;
+    //! The free energy calculation frequency
+    const int nstcalcfreeenergy_;
+    //! The virial calculation frequency
+    const int nstcalcvirial_;
+    //! The virial calculation mode
+    const EnergySignallerVirialMode virialMode_;
+
+    //! ITrajectorySignallerClient implementation
+    std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
+    //! The energy writing step (notified by signaller)
+    Step energyWritingStep_;
+    //! Whether we registered to the trajectory signaller
+    bool trajectoryRegistrationDone_;
+
+    //! ILoggingSignallerClient implementation
+    std::optional<SignallerCallback> registerLoggingCallback() override;
+    //! The next logging step (notified by signaller)
+    Step loggingStep_;
+    //! Whether we registered to the logging signaller
+    bool loggingRegistrationDone_;
+};
+
+//! Allows clients to register to the signaller
+template<class Signaller>
+void SignallerBuilder<Signaller>::registerSignallerClient(typename Signaller::Client* client)
+{
+    if (client)
+    {
+        if (state_ == ModularSimulatorBuilderState::NotAcceptingClientRegistrations)
+        {
+            throw SimulationAlgorithmSetupError(
+                    "Tried to register to signaller after it was built.");
+        }
+        signallerClients_.emplace_back(client);
+    }
+}
+
+/*! \brief Build the signaller
+ *
+ * General version - for NeighborSearchSignaller, LastStepSignaller, LoggingSignaller
+ */
+template<class Signaller>
+template<typename... Args>
+std::unique_ptr<Signaller> SignallerBuilder<Signaller>::build(Args&&... args)
+{
+    state_         = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
+    auto callbacks = buildCallbackVector();
+    // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
+    return std::unique_ptr<Signaller>(new Signaller(std::move(callbacks), std::forward<Args>(args)...));
+}
+
+/*! \brief Build the signaller
+ *
+ * Specialized version - TrajectorySignaller has a different build process
+ */
+template<>
+template<typename... Args>
+std::unique_ptr<TrajectorySignaller> SignallerBuilder<TrajectorySignaller>::build(Args&&... args)
+{
+    state_                     = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
+    auto signalEnergyCallbacks = buildCallbackVector(TrajectoryEvent::EnergyWritingStep);
+    auto signalStateCallbacks  = buildCallbackVector(TrajectoryEvent::StateWritingStep);
+    // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
+    return std::unique_ptr<TrajectorySignaller>(new TrajectorySignaller(
+            std::move(signalEnergyCallbacks), std::move(signalStateCallbacks), std::forward<Args>(args)...));
+}
+
+/*! \brief Build the signaller
+ *
+ * Specialized version - EnergySignaller has a significantly different build process
+ */
+template<>
+template<typename... Args>
+std::unique_ptr<EnergySignaller> SignallerBuilder<EnergySignaller>::build(Args&&... args)
+{
+    state_                        = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
+    auto calculateEnergyCallbacks = buildCallbackVector(EnergySignallerEvent::EnergyCalculationStep);
+    auto calculateVirialCallbacks = buildCallbackVector(EnergySignallerEvent::VirialCalculationStep);
+    auto calculateFreeEnergyCallbacks =
+            buildCallbackVector(EnergySignallerEvent::FreeEnergyCalculationStep);
+    // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
+    return std::unique_ptr<EnergySignaller>(new EnergySignaller(std::move(calculateEnergyCallbacks),
+                                                                std::move(calculateVirialCallbacks),
+                                                                std::move(calculateFreeEnergyCallbacks),
+                                                                std::forward<Args>(args)...));
+}
+
+//! Helper function to get the callbacks from the clients
+template<typename Signaller>
+template<typename... Args>
+std::vector<SignallerCallback> SignallerBuilder<Signaller>::buildCallbackVector(Args&&... args)
+{
+    std::vector<SignallerCallback> callbacks;
+    // Allow clients to register their callbacks
+    for (auto& client : signallerClients_)
+    {
+        if (auto callback = getSignallerCallback(client, std::forward<Args>(args)...)) // don't register nullptr
+        {
+            callbacks.emplace_back(std::move(*callback));
+        }
+    }
+    return callbacks;
+}
+
+//! Get a callback from a single client - NeighborSearchSignaller
+template<>
+template<typename... Args>
+std::optional<SignallerCallback> SignallerBuilder<NeighborSearchSignaller>::getSignallerCallback(
+        typename NeighborSearchSignaller::Client* client,
+        Args&&... args)
+{
+    return client->registerNSCallback(std::forward<Args>(args)...);
+}
+
+//! Get a callback from a single client - LastStepSignaller
+template<>
+template<typename... Args>
+std::optional<SignallerCallback>
+SignallerBuilder<LastStepSignaller>::getSignallerCallback(typename LastStepSignaller::Client* client,
+                                                          Args&&... args)
+{
+    return client->registerLastStepCallback(std::forward<Args>(args)...);
+}
+
+//! Get a callback from a single client - LoggingSignaller
+template<>
+template<typename... Args>
+std::optional<SignallerCallback>
+SignallerBuilder<LoggingSignaller>::getSignallerCallback(typename LoggingSignaller::Client* client,
+                                                         Args&&... args)
+{
+    return client->registerLoggingCallback(std::forward<Args>(args)...);
+}
+
+//! Get a callback from a single client - TrajectorySignaller
+template<>
+template<typename... Args>
+std::optional<SignallerCallback>
+SignallerBuilder<TrajectorySignaller>::getSignallerCallback(typename TrajectorySignaller::Client* client,
+                                                            Args&&... args)
+{
+    return client->registerTrajectorySignallerCallback(std::forward<Args>(args)...);
+}
+
+//! Get a callback from a single client - EnergySignaller
+template<>
+template<typename... Args>
+std::optional<SignallerCallback>
+SignallerBuilder<EnergySignaller>::getSignallerCallback(typename EnergySignaller::Client* client,
+                                                        Args&&... args)
+{
+    return client->registerEnergyCallback(std::forward<Args>(args)...);
+}
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_SIGNALLERS_H
diff --git a/src/include/gromacs/modularsimulator/simulatoralgorithm.h b/src/include/gromacs/modularsimulator/simulatoralgorithm.h
new file mode 100644 (file)
index 0000000..1c6daaa
--- /dev/null
@@ -0,0 +1,776 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Provides the modular simulator algorithm.
+ *
+ * Defines the ModularSimulatorAlgorithm class and its builder.
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module.
+ * Moving forward, the ModularSimulatorAlgorithmBuilder could be exposed to allow users to
+ * create custom algorithm - currently algorithms are only created an used by the ModularSimulator,
+ * meaning that this file is not exposed outside of the modular simulator module.
+ */
+#ifndef GROMACS_MODULARSIMULATOR_SIMULATORALGORITHM_H
+#define GROMACS_MODULARSIMULATOR_SIMULATORALGORITHM_H
+
+#include <any>
+#include <map>
+#include <optional>
+#include <string>
+#include <typeinfo>
+
+#include "gromacs/mdrun/isimulator.h"
+#include "gromacs/mdtypes/observablesreducer.h"
+#include "gromacs/mdtypes/state.h"
+#include "gromacs/utility/exceptions.h"
+
+#include "checkpointhelper.h"
+#include "domdechelper.h"
+#include "freeenergyperturbationdata.h"
+#include "modularsimulatorinterfaces.h"
+#include "pmeloadbalancehelper.h"
+#include "signallers.h"
+#include "topologyholder.h"
+#include "trajectoryelement.h"
+
+namespace gmx
+{
+enum class IntegrationStage;
+class EnergyData;
+class ModularSimulator;
+class ResetHandler;
+template<IntegrationStage integrationStage>
+class Propagator;
+class TopologyHolder;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief The modular simulator
+ *
+ * Based on the input given, this simulator builds independent elements and
+ * signallers and stores them in a respective vector. The run function
+ * runs the simulation by, in turn, building a task list from the elements
+ * for a predefined number of steps, then running the task list, and repeating
+ * until the stop criterion is fulfilled.
+ *
+ * The simulator algorithm is owning all elements involved in the simulation
+ * and is hence controlling their lifetime. This ensures that pointers and
+ * callbacks exchanged between elements remain valid throughout the duration
+ * of the simulation run.
+ */
+class ModularSimulatorAlgorithm final
+{
+public:
+    //! Get next task in queue
+    [[nodiscard]] const SimulatorRunFunction* getNextTask();
+
+    // Only builder can construct
+    friend class ModularSimulatorAlgorithmBuilder;
+
+private:
+    //! Constructor
+    ModularSimulatorAlgorithm(std::string              topologyName,
+                              FILE*                    fplog,
+                              t_commrec*               cr,
+                              const MDLogger&          mdlog,
+                              const MdrunOptions&      mdrunOptions,
+                              const t_inputrec*        inputrec,
+                              t_nrnb*                  nrnb,
+                              gmx_wallcycle*           wcycle,
+                              t_forcerec*              fr,
+                              gmx_walltime_accounting* walltime_accounting);
+
+    //! Calls setup for the simulator and all elements and signallers
+    void setup();
+    //! Calls teardown for the simulator and all elements
+    void teardown();
+    //! Re-populate task queue
+    void updateTaskQueue();
+
+    /*! \brief A function called once before the simulation run
+     *
+     * This function collects calls which need to be made once at the
+     * beginning of the simulation run. These are mainly printing information
+     * to the user and starting the wall time.
+     */
+    void simulatorSetup();
+
+    /*! \brief A function called once after the simulation run
+     *
+     * This function collects calls which need to be made once at the
+     * end of the simulation run.
+     */
+    void simulatorTeardown();
+
+    /*! \brief A function called before every step
+     *
+     * This function collects calls which need to be made before every
+     * simulation step. The need for this function could likely be
+     * eliminated by rewriting the stop and reset handler to fit the
+     * modular simulator approach.
+     */
+    void preStep(Step step, Time time, bool isNeighborSearchingStep);
+
+    /*! \brief A function called after every step
+     *
+     * This function collects calls which need to be made after every
+     * simulation step.
+     */
+    void postStep(Step step, Time time);
+
+    /*! \brief Add run functions to the task queue
+     *
+     * This function calls PME load balancing and domain decomposition first,
+     * and then queries the elements for all steps of the life time of the
+     * current neighbor list for their run functions. When the next step
+     * would be a neighbor-searching step, this function returns, such that
+     * the simulator can run the pre-computed task list before updating the
+     * neighbor list and re-filling the task list.
+     *
+     * TODO: In the current approach, unique pointers to tasks are created
+     *       repeatedly. While preliminary measurements do not seem to indicate
+     *       performance problems, a pool allocator would be an obvious
+     *       optimization possibility, as would reusing the task queue if
+     *       the task queue is periodic around the rebuild point (i.e. the
+     *       task queue is identical between rebuilds).
+     */
+    void populateTaskQueue();
+
+    //! The run queue
+    std::vector<SimulatorRunFunction> taskQueue_;
+    //! The task iterator
+    std::vector<SimulatorRunFunction>::const_iterator taskIterator_;
+
+    /* Note that the Simulator is owning the signallers and elements.
+     * The ownership list and the call list of the elements are kept
+     * separate, to allow to have elements more than once in the call
+     * lists - for example, using velocity verlet, the compute globals
+     * element needs to be scheduled more than once per step. For the
+     * signallers, no distinction between ownership and call list is
+     * made, all signallers are called exactly once per scheduling step.
+     *
+     * Objects being both a signaller and an element are not supported.
+     */
+    //! List of signalers (ownership and calling sequence)
+    std::vector<std::unique_ptr<ISignaller>> signallerList_;
+    //! List of schedulerElements (ownership)
+    std::vector<std::unique_ptr<ISimulatorElement>> elementsOwnershipList_;
+    //! List of schedulerElements (run calling sequence)
+    std::vector<ISimulatorElement*> elementCallList_;
+    //! List of schedulerElements (setup / teardown calling sequence)
+    std::vector<ISimulatorElement*> elementSetupTeardownList_;
+    //! List of pre-step scheduling functions
+    std::vector<SchedulingFunction> preStepScheduling_;
+    //! List of post-step scheduling functions
+    std::vector<SchedulingFunction> postStepScheduling_;
+
+    // Infrastructure elements
+    //! The domain decomposition element
+    std::unique_ptr<DomDecHelper> domDecHelper_;
+    //! The PME load balancing element
+    std::unique_ptr<PmeLoadBalanceHelper> pmeLoadBalanceHelper_;
+    //! The checkpoint helper
+    std::unique_ptr<CheckpointHelper> checkpointHelper_;
+    //! The stop handler
+    std::unique_ptr<StopHandler> stopHandler_;
+    //! The reset handler
+    std::unique_ptr<ResetHandler> resetHandler_;
+    //! Signal vector (used by stop / reset / checkpointing signaller)
+    std::unique_ptr<SimulationSignals> signals_;
+    //! The topology
+    std::unique_ptr<TopologyHolder> topologyHolder_;
+
+    // Data structures
+    //! The state propagator data
+    std::unique_ptr<StatePropagatorData> statePropagatorData_;
+    //! The energy data
+    std::unique_ptr<EnergyData> energyData_;
+    //! The free energy data
+    std::unique_ptr<FreeEnergyPerturbationData> freeEnergyPerturbationData_;
+    //! Arbitrary data with lifetime equal to the simulation (used to share data between elements)
+    std::map<std::string, std::unique_ptr<std::any>> simulationData_;
+
+    //! The current step
+    Step step_;
+    //! Whether the simulation run is finished
+    bool runFinished_;
+
+    /*! \internal
+     * \brief Signal helper
+     *
+     * The simulator needs to know about the last step and the
+     * neighbor searching step, which are determined in signallers.
+     * This object can be registered to the signals and accessed by
+     * the methods of the simulator.
+     */
+    class SignalHelper : public ILastStepSignallerClient, public INeighborSearchSignallerClient
+    {
+    public:
+        //! The last step
+        Step lastStep_ = std::numeric_limits<Step>::max();
+        //! The next NS step
+        Step nextNSStep_ = -1;
+        //! ILastStepSignallerClient implementation
+        std::optional<SignallerCallback> registerLastStepCallback() override;
+        //! INeighborSearchSignallerClient implementation
+        std::optional<SignallerCallback> registerNSCallback() override;
+    };
+    //! The signal helper object
+    std::unique_ptr<SignalHelper> signalHelper_;
+
+    // TODO: This is a hack for stop handler - needs to go once StopHandler
+    //       is adapted to the modular simulator
+    //! Whether this is a neighbor-searching step
+    bool stophandlerIsNSStep_ = false;
+    //! The current step
+    Step stophandlerCurrentStep_ = -1;
+
+    // ISimulator data - used for setup, teardown, pre step and post step
+    //! Name of the topology
+    std::string topologyName_;
+    //! Handles logging.
+    FILE* fplog;
+    //! Handles communication.
+    t_commrec* cr;
+    //! Handles logging.
+    const MDLogger& mdlog;
+    //! Contains command-line options to mdrun.
+    const MdrunOptions& mdrunOptions;
+    //! Contains user input mdp options.
+    const t_inputrec* inputrec;
+    //! Manages flop accounting.
+    t_nrnb* nrnb;
+    //! Manages wall cycle accounting.
+    gmx_wallcycle* wcycle;
+    //! Parameters for force calculations.
+    t_forcerec* fr;
+    //! Manages wall time accounting.
+    gmx_walltime_accounting* walltime_accounting;
+};
+
+/*! \internal
+ * \brief Helper container with data connected to global communication
+ *
+ * This includes data that needs to be shared between elements involved in
+ * global communication. This will become obsolete as soon as global
+ * communication is moved to a client system (#3421 and #3887).
+ */
+class GlobalCommunicationHelper
+{
+public:
+    //! Constructor
+    GlobalCommunicationHelper(int nstglobalcomm, SimulationSignals* simulationSignals);
+
+    //! Get the compute globals communication period
+    [[nodiscard]] int nstglobalcomm() const;
+    //! Get a pointer to the signals vector
+    [[nodiscard]] SimulationSignals* simulationSignals();
+
+private:
+    //! Compute globals communication period
+    const int nstglobalcomm_;
+    //! Signal vector (used by stop / reset / checkpointing signaller)
+    SimulationSignals* simulationSignals_;
+};
+
+class ModularSimulatorAlgorithmBuilder;
+
+/*! \internal
+ * \brief Helper for element addition
+ *
+ * Such an object will be given to each invocation of getElementPointer
+ *
+ * Note: It would be nicer to define this as a member type of
+ * ModularSimulatorAlgorithmBuilder, but this would break forward declaration.
+ * This object is therefore defined as friend class.
+ */
+class ModularSimulatorAlgorithmBuilderHelper
+{
+public:
+    //! Constructor
+    ModularSimulatorAlgorithmBuilderHelper(ModularSimulatorAlgorithmBuilder* builder);
+    //! Store an element to the ModularSimulatorAlgorithmBuilder
+    template<typename Element>
+    Element* storeElement(std::unique_ptr<Element> element);
+    //! Check if an element is stored in the ModularSimulatorAlgorithmBuilder
+    bool elementIsStored(const ISimulatorElement* element) const;
+    /*! \brief Register callback to schedule a pre-step run
+     *
+     * This allows elements to schedule a function call before the integration step.
+     * The function call is guaranteed to happen before any functions scheduled for
+     * the integration step. It is not guaranteed to happen in any specific order
+     * compared to other elements registering a pre-step scheduling function.
+     */
+    void registerPreStepScheduling(SchedulingFunction schedulingFunction);
+    /*! \brief Register callback to schedule a post-step run
+     *
+     * This allows elements to schedule a function call after the integration step.
+     * The function call is guaranteed to happen after all functions scheduled for
+     * the integration step. It is not guaranteed to happen in any specific order
+     * compared to other elements registering a post-step scheduling function.
+     */
+    void registerPostStepScheduling(SchedulingFunction schedulingFunction);
+    /*! \brief Set arbitrary data in the ModularSimulatorAlgorithmBuilder
+     *
+     * Allows to store arbitrary data with lifetime equal to the builder. Functionality is used
+     * by stateful static builder functions.
+     */
+    template<typename ValueType>
+    void storeBuilderData(const std::string& key, const ValueType& value);
+    //! Get previously stored builder data. Returns std::nullopt if key is not found.
+    std::optional<std::any> builderData(const std::string& key) const;
+    //! \copydoc ModularSimulatorAlgorithmBuilder::storeSimulationData()
+    template<typename ValueType>
+    void storeSimulationData(const std::string& key, ValueType&& value);
+    //! \copydoc ModularSimulatorAlgorithmBuilder::simulationData()
+    template<typename ValueType>
+    std::optional<ValueType*> simulationData(const std::string& key);
+    //! Register temperature / pressure control algorithm to be matched with a propagator
+    void registerTemperaturePressureControl(std::function<void(const PropagatorConnection&)> registrationFunction);
+    //! Register a propagator to be used with a temperature / pressure control algorithm
+    void registerPropagator(PropagatorConnection connectionData);
+    //! Register for callback after an update to the reference temperature
+    void registerReferenceTemperatureUpdate(ReferenceTemperatureCallback referenceTemperatureCallback);
+    //! Get a callback to change reference temperature
+    ReferenceTemperatureCallback changeReferenceTemperatureCallback();
+
+private:
+    //! Pointer to the associated ModularSimulatorAlgorithmBuilder
+    ModularSimulatorAlgorithmBuilder* builder_;
+};
+
+/*!\internal
+ * \brief Builder for ModularSimulatorAlgorithm objects
+ *
+ * This builds a ModularSimulatorAlgorithm.
+ *
+ * Users can add elements and define their call order by calling the templated
+ * add<Element> function. Note that only elements that have a static
+ * getElementPointerImpl factory method can be built in that way.
+ *
+ * Note that each ModularSimulatorAlgorithmBuilder can only be used to build
+ * one ModularSimulatorAlgorithm object, i.e. build() can only be called once.
+ * During the call to build, all elements and other infrastructure objects will
+ * be moved to the built ModularSimulatorAlgorithm object, such that further use
+ * of the builder would not make sense.
+ * Any access to the build or add<> methods after the first call to
+ * build() will result in an exception being thrown.
+ */
+class ModularSimulatorAlgorithmBuilder final
+{
+public:
+    //! Constructor
+    ModularSimulatorAlgorithmBuilder(compat::not_null<LegacySimulatorData*>    legacySimulatorData,
+                                     std::unique_ptr<ReadCheckpointDataHolder> checkpointDataHolder);
+    //! Build algorithm
+    ModularSimulatorAlgorithm build();
+
+    /*! \brief  Add element to the modular simulator algorithm builder
+     *
+     * This function has a general implementation, which will call the getElementPointer(...)
+     * factory function.
+     *
+     * \tparam Element  The element type
+     * \tparam Args     A variable number of argument types
+     * \param args      A variable number of arguments
+     */
+    template<typename Element, typename... Args>
+    void add(Args&&... args);
+
+    //! Allow access from helper
+    friend class ModularSimulatorAlgorithmBuilderHelper;
+
+private:
+    //! The state of the builder
+    bool algorithmHasBeenBuilt_ = false;
+
+    // Data structures
+    //! The state propagator data
+    std::unique_ptr<StatePropagatorData> statePropagatorData_;
+    //! The energy data
+    std::unique_ptr<EnergyData> energyData_;
+    //! The free energy data
+    std::unique_ptr<FreeEnergyPerturbationData> freeEnergyPerturbationData_;
+    //! Arbitrary data with lifetime equal to the builder (used by stateful static builder functions)
+    std::map<std::string, std::any> builderData_;
+    //! Arbitrary data with lifetime equal to the simulation (used to share data between elements)
+    std::map<std::string, std::unique_ptr<std::any>> simulationData_;
+
+    //! Pointer to the LegacySimulatorData object
+    compat::not_null<LegacySimulatorData*> legacySimulatorData_;
+
+    // Helper objects
+    //! Signal vector (used by stop / reset / checkpointing signaller)
+    std::unique_ptr<SimulationSignals> signals_;
+    //! Helper object passed to element factory functions
+    ModularSimulatorAlgorithmBuilderHelper elementAdditionHelper_;
+    //! Container for minor aspects of global computation data
+    GlobalCommunicationHelper globalCommunicationHelper_;
+    //! Coordinates reduction for observables
+    ObservablesReducer observablesReducer_;
+
+    /*! \brief Set arbitrary data in the ModularSimulatorAlgorithm
+     *
+     * Allows to store arbitrary data with lifetime equal to the simulator algorithm.
+     * Functionality allows elements to share arbitrary data.
+     */
+    template<typename ValueType>
+    void storeSimulationData(const std::string& key, ValueType&& value);
+
+    /*! \brief Get previously stored simulation data.
+     *
+     * Returns std::nullopt if key is not found.
+     */
+    template<typename ValueType>
+    std::optional<ValueType*> simulationData(const std::string& key);
+
+    /*! \brief  Register an element to all applicable signallers and infrastructure elements
+     *
+     * \tparam Element  Type of the Element
+     * \param element   Pointer to the element
+     */
+    template<typename Element>
+    void registerWithInfrastructureAndSignallers(Element* element);
+
+    /*! \brief Take ownership of element
+     *
+     * This function returns a non-owning pointer to the new location of that
+     * element, allowing further usage (e.g. adding the element to the call list).
+     * This will also add the element to the setup / teardown list, and register
+     * it with all applicable signallers and infrastructure objects.
+     * Note that simply adding an element using this function will not call it
+     * during the simulation - it needs to be added to the call list separately.
+     * Also note that generally, users will want to add elements to the call list,
+     * but it might not be practical to do this in the same order.
+     *
+     * \tparam Element  Type of the Element
+     * \param element  A unique pointer to the element
+     * \return  A non-owning (raw) pointer to the element for further usage
+     */
+    template<typename Element>
+    Element* addElementToSimulatorAlgorithm(std::unique_ptr<Element> element);
+
+    /*! \brief Register existing element to infrastructure
+     *
+     * This function adds existing elements to the setup / teardown list, and
+     * registers them with all applicable signallers and infrastructure objects.
+     * This is only permissible for elements owned directly by the builder or
+     * indirectly through data objects. Before registering the element, the function
+     * checks that the element is owned by the builder or a known object.
+     *
+     * \tparam Element  Type of the Element
+     * \param element   A non-owning (raw) pointer to the element
+     */
+    template<typename Element>
+    void registerExistingElement(Element* element);
+
+    /*! \brief Check if element is owned by *this
+     *
+     * \param element  Pointer to the element
+     * \return  Bool indicating whether element is owned by *this
+     */
+    [[nodiscard]] bool elementExists(const ISimulatorElement* element) const;
+
+    //! Vector to store elements, allowing the SimulatorAlgorithm to control their lifetime
+    std::vector<std::unique_ptr<ISimulatorElement>> elements_;
+    /*! \brief List defining in which order elements are called every step
+     *
+     * Elements may be referenced more than once if they should be called repeatedly
+     */
+    std::vector<ISimulatorElement*> callList_;
+    /*! \brief  List defining in which order elements are set up and torn down
+     *
+     * Elements should only appear once in this list
+     */
+    std::vector<ISimulatorElement*> setupAndTeardownList_;
+    //! List of pre-step scheduling functions
+    std::vector<SchedulingFunction> preStepScheduling_;
+    //! List of post-step scheduling functions
+    std::vector<SchedulingFunction> postStepScheduling_;
+
+    //! Builder for the NeighborSearchSignaller
+    SignallerBuilder<NeighborSearchSignaller> neighborSearchSignallerBuilder_;
+    //! Builder for the LastStepSignaller
+    SignallerBuilder<LastStepSignaller> lastStepSignallerBuilder_;
+    //! Builder for the LoggingSignaller
+    SignallerBuilder<LoggingSignaller> loggingSignallerBuilder_;
+    //! Builder for the EnergySignaller
+    SignallerBuilder<EnergySignaller> energySignallerBuilder_;
+    //! Builder for the TrajectorySignaller
+    SignallerBuilder<TrajectorySignaller> trajectorySignallerBuilder_;
+    //! Builder for the TrajectoryElementBuilder
+    TrajectoryElementBuilder trajectoryElementBuilder_;
+    //! Builder for the TopologyHolder
+    TopologyHolder::Builder topologyHolderBuilder_;
+    //! Builder for the CheckpointHelper
+    CheckpointHelperBuilder checkpointHelperBuilder_;
+    //! Builder for the DomDecHelper
+    DomDecHelperBuilder domDecHelperBuilder_;
+
+    /*! \brief List of clients for the CheckpointHelper
+     *
+     * \todo Replace this by proper builder (#3422)
+     */
+    std::vector<ICheckpointHelperClient*> checkpointClients_;
+
+    //! List of data to connect propagators to thermostats / barostats
+    std::vector<PropagatorConnection> propagatorConnections_;
+    //! List of temperature / pressure control registration functions
+    std::vector<std::function<void(const PropagatorConnection&)>> pressureTemperatureControlRegistrationFunctions_;
+};
+
+/*! \internal
+ * \brief Factory function for elements that can be added via ModularSimulatorAlgorithmBuilder:
+ *        Get a pointer to an object of type \c Element to add to the call list
+ *
+ * This allows elements to be built via the templated ModularSimulatorAlgorithmBuilder::add<Element>
+ * method. Elements buildable throught this factor function are required to implement a static
+ * function with minimal signature
+ *
+ *     static ISimulatorElement* getElementPointerImpl(
+ *             LegacySimulatorData*                    legacySimulatorData,
+ *             ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+ *             StatePropagatorData*                    statePropagatorData,
+ *             EnergyData*                             energyData,
+ *             FreeEnergyPerturbationData*             freeEnergyPerturbationData,
+ *             GlobalCommunicationHelper*              globalCommunicationHelper,
+ *             ObservablesReducer*                     observablesReducer)
+ *
+ * This function may also accept additional parameters which are passed using the variadic
+ * template parameter pack forwarded in getElementPointer.
+ *
+ * This function returns a pointer to an object of the Element type. Note that the caller will
+ * check whether the returned object has previously been stored using the `storeElement`
+ * function, and throw an exception if the element is not found.
+ * The function can check whether a previously stored pointer is valid using
+ * the `checkElementExistence` function. Most implementing functions will simply want
+ * to create an object, store it using `storeElement`, and then use the return value of
+ * `storeElement` as a return value to the caller. However, this setup allows the function
+ * to store a created element (using a static pointer inside the function) and return it
+ * in case that the factory function is called repeatedly. This allows to create an element
+ * once, but have it called multiple times during the simulation run.
+ *
+ * \see ModularSimulatorAlgorithmBuilder::add
+ *      Function using this functionality
+ * \see ComputeGlobalsElement<ComputeGlobalsAlgorithm::VelocityVerlet>::getElementPointerImpl
+ *      Implementation using the single object / multiple call sites functionality
+ *
+ * \tparam Element The type of the element
+ * \tparam Args  Variable number of argument types allowing specific implementations to have
+ *               additional arguments
+ *
+ * \param legacySimulatorData  Pointer allowing access to simulator level data
+ * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+ * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+ * \param energyData  Pointer to the \c EnergyData object
+ * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+ * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+ * \param observablesReducer          Pointer to the \c ObservablesReducer object
+ * \param args  Variable number of additional parameters to be forwarded
+ *
+ * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+ */
+template<typename Element, typename... Args>
+ISimulatorElement* getElementPointer(LegacySimulatorData*                    legacySimulatorData,
+                                     ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                     StatePropagatorData*                    statePropagatorData,
+                                     EnergyData*                             energyData,
+                                     FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                     GlobalCommunicationHelper*  globalCommunicationHelper,
+                                     ObservablesReducer*         observablesReducer,
+                                     Args&&... args)
+{
+    return Element::getElementPointerImpl(legacySimulatorData,
+                                          builderHelper,
+                                          statePropagatorData,
+                                          energyData,
+                                          freeEnergyPerturbationData,
+                                          globalCommunicationHelper,
+                                          observablesReducer,
+                                          std::forward<Args>(args)...);
+}
+
+template<typename Element, typename... Args>
+void ModularSimulatorAlgorithmBuilder::add(Args&&... args)
+{
+    if (algorithmHasBeenBuilt_)
+    {
+        GMX_THROW(SimulationAlgorithmSetupError(
+                "Tried to add an element after ModularSimulationAlgorithm was built."));
+    }
+
+    // Get element from factory method
+    auto* element = static_cast<Element*>(getElementPointer<Element>(legacySimulatorData_,
+                                                                     &elementAdditionHelper_,
+                                                                     statePropagatorData_.get(),
+                                                                     energyData_.get(),
+                                                                     freeEnergyPerturbationData_.get(),
+                                                                     &globalCommunicationHelper_,
+                                                                     &observablesReducer_,
+                                                                     std::forward<Args>(args)...));
+
+    // Make sure returned element pointer is owned by *this
+    // Ensuring this makes sure we can control the life time
+    if (!elementExists(element))
+    {
+        GMX_THROW(ElementNotFoundError("Tried to append non-existing element to call list."));
+    }
+    // Add to call list
+    callList_.emplace_back(element);
+}
+
+//! Returns a pointer casted to type Base if the Element is derived from Base
+template<typename Base, typename Element>
+static std::enable_if_t<std::is_base_of<Base, Element>::value, Base*> castOrNull(Element* element)
+{
+    return static_cast<Base*>(element);
+}
+
+//! Returns a nullptr of type Base if Element is not derived from Base
+template<typename Base, typename Element>
+static std::enable_if_t<!std::is_base_of<Base, Element>::value, Base*> castOrNull(Element gmx_unused* element)
+{
+    return nullptr;
+}
+
+template<typename Element>
+void ModularSimulatorAlgorithmBuilder::registerWithInfrastructureAndSignallers(Element* element)
+{
+    // Register element to all applicable signallers
+    neighborSearchSignallerBuilder_.registerSignallerClient(
+            castOrNull<INeighborSearchSignallerClient, Element>(element));
+    lastStepSignallerBuilder_.registerSignallerClient(castOrNull<ILastStepSignallerClient, Element>(element));
+    loggingSignallerBuilder_.registerSignallerClient(castOrNull<ILoggingSignallerClient, Element>(element));
+    energySignallerBuilder_.registerSignallerClient(castOrNull<IEnergySignallerClient, Element>(element));
+    trajectorySignallerBuilder_.registerSignallerClient(
+            castOrNull<ITrajectorySignallerClient, Element>(element));
+    // Register element to trajectory element (if applicable)
+    trajectoryElementBuilder_.registerWriterClient(castOrNull<ITrajectoryWriterClient, Element>(element));
+    // Register element to topology holder (if applicable)
+    topologyHolderBuilder_.registerClient(castOrNull<ITopologyHolderClient, Element>(element));
+    // Register element to checkpoint client (if applicable)
+    checkpointHelperBuilder_.registerClient(castOrNull<ICheckpointHelperClient, Element>(element));
+    // Register element to DomDecHelper builder (if applicable)
+    domDecHelperBuilder_.registerClient(castOrNull<IDomDecHelperClient, Element>(element));
+}
+
+template<typename Element>
+Element* ModularSimulatorAlgorithmBuilder::addElementToSimulatorAlgorithm(std::unique_ptr<Element> element)
+{
+    // Store element
+    elements_.emplace_back(std::move(element));
+    // Get non-owning pointer for further use
+    Element* elementPtr = static_cast<Element*>(elements_.back().get());
+    // Register element to infrastructure
+    registerExistingElement(elementPtr);
+
+    return elementPtr;
+}
+
+template<typename Element>
+void ModularSimulatorAlgorithmBuilder::registerExistingElement(Element* element)
+{
+    // Make sure the element pointer is owned by *this
+    // Ensuring this makes sure we can control the life time
+    if (!elementExists(element))
+    {
+        GMX_THROW(
+                ElementNotFoundError("Tried to register non-existing element to infrastructure."));
+    }
+
+    // Add to setup / teardown list
+    setupAndTeardownList_.emplace_back(element);
+    // Register element to all applicable signallers
+    registerWithInfrastructureAndSignallers(element);
+}
+
+template<typename Element>
+Element* ModularSimulatorAlgorithmBuilderHelper::storeElement(std::unique_ptr<Element> element)
+{
+    return builder_->addElementToSimulatorAlgorithm(std::move(element));
+}
+
+template<typename ValueType>
+void ModularSimulatorAlgorithmBuilderHelper::storeBuilderData(const std::string& key, const ValueType& value)
+{
+    builder_->builderData_[key] = std::any(value);
+}
+
+template<typename ValueType>
+void ModularSimulatorAlgorithmBuilderHelper::storeSimulationData(const std::string& key, ValueType&& value)
+{
+    builder_->storeSimulationData(key, std::forward<ValueType>(value));
+}
+
+template<typename ValueType>
+std::optional<ValueType*> ModularSimulatorAlgorithmBuilderHelper::simulationData(const std::string& key)
+{
+    return builder_->simulationData<ValueType>(key);
+}
+
+template<typename ValueType>
+void ModularSimulatorAlgorithmBuilder::storeSimulationData(const std::string& key, ValueType&& value)
+{
+    GMX_RELEASE_ASSERT(simulationData_.count(key) == 0,
+                       formatString("Key %s was already stored in simulation data.", key.c_str()).c_str());
+    simulationData_[key] = std::make_unique<std::any>(std::forward<ValueType>(value));
+    auto* ptrToData      = simulationData<ValueType>(key).value();
+    registerWithInfrastructureAndSignallers(ptrToData);
+}
+
+template<typename ValueType>
+std::optional<ValueType*> ModularSimulatorAlgorithmBuilder::simulationData(const std::string& key)
+{
+    const auto iter = simulationData_.find(key);
+    if (iter == simulationData_.end())
+    {
+        return std::nullopt;
+    }
+    ValueType* data = std::any_cast<ValueType>(iter->second.get());
+    GMX_RELEASE_ASSERT(data != nullptr,
+                       formatString("Object stored in simulation data under key %s does not have "
+                                    "the expected type.",
+                                    key.c_str())
+                               .c_str());
+    return data;
+}
+
+
+} // namespace gmx
+
+#endif // GROMACS_MODULARSIMULATOR_SIMULATORALGORITHM_H
diff --git a/src/include/gromacs/modularsimulator/statepropagatordata.h b/src/include/gromacs/modularsimulator/statepropagatordata.h
new file mode 100644 (file)
index 0000000..734a070
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the state for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_STATEPROPAGATORDATA_H
+#define GMX_MODULARSIMULATOR_STATEPROPAGATORDATA_H
+
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/paddedvector.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/checkpointdata.h"
+#include "gromacs/mdtypes/forcebuffers.h"
+#include "gromacs/utility/keyvaluetree.h"
+
+#include "modularsimulatorinterfaces.h"
+#include "topologyholder.h"
+
+struct gmx_mdoutf;
+enum class PbcType : int;
+struct t_commrec;
+struct t_inputrec;
+class t_state;
+struct t_mdatoms;
+struct t_trxframe;
+
+namespace gmx
+{
+enum class CheckpointDataOperation;
+enum class ConstraintVariable;
+class EnergyData;
+class FreeEnergyPerturbationData;
+class GlobalCommunicationHelper;
+class LegacySimulatorData;
+class ModularSimulatorAlgorithmBuilderHelper;
+class ObservablesReducer;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief StatePropagatorData and associated data
+ *
+ * The `StatePropagatorData` contains a little more than the pure
+ * statistical-physical micro state, namely the positions,
+ * velocities, forces, and box matrix, as well as a backup of
+ * the positions and box of the last time step. While it takes
+ * part in the simulator loop via its member class `Element`
+ * to be able to backup positions /
+ * boxes and save the current state if needed, it's main purpose
+ * is to offer access to its data via getter methods. All elements
+ * reading or writing to this data need a pointer to the
+ * `StatePropagatorData` and need to request their data explicitly. This
+ * will later simplify the understanding of data dependencies
+ * between elements.
+ *
+ * Note that the `StatePropagatorData` can be converted to and from the
+ * legacy `t_state` object. This is useful when dealing with
+ * functionality which has not yet been adapted to use the new
+ * data approach - of the elements currently implemented, only
+ * domain decomposition, PME load balancing, and the initial
+ * constraining are using this.
+ */
+class StatePropagatorData final
+{
+public:
+    //! Constructor
+    StatePropagatorData(int                numAtoms,
+                        FILE*              fplog,
+                        const t_commrec*   cr,
+                        t_state*           globalState,
+                        t_state*           localState,
+                        bool               useGPU,
+                        bool               canMoleculesBeDistributedOverPBC,
+                        bool               writeFinalConfiguration,
+                        const std::string& finalConfigurationFilename,
+                        const t_inputrec*  inputrec,
+                        const t_mdatoms*   mdatoms,
+                        const gmx_mtop_t&  globalTop);
+
+    //! Destructor (allows forward declaration of internal type)
+    ~StatePropagatorData();
+
+    // Allow access to state
+    //! Get write access to position vector
+    ArrayRefWithPadding<RVec> positionsView();
+    //! Get read access to position vector
+    ArrayRefWithPadding<const RVec> constPositionsView() const;
+    //! Get write access to previous position vector
+    ArrayRefWithPadding<RVec> previousPositionsView();
+    //! Get read access to previous position vector
+    ArrayRefWithPadding<const RVec> constPreviousPositionsView() const;
+    //! Get write access to velocity vector
+    ArrayRefWithPadding<RVec> velocitiesView();
+    //! Get read access to velocity vector
+    ArrayRefWithPadding<const RVec> constVelocitiesView() const;
+    //! Get write access to force vector
+    ForceBuffersView& forcesView();
+    //! Get read access to force vector
+    const ForceBuffersView& constForcesView() const;
+    //! Get pointer to box
+    rvec* box();
+    //! Get const pointer to box
+    const rvec* constBox() const;
+    //! Get pointer to previous box
+    rvec* previousBox();
+    //! Get const pointer to previous box
+    const rvec* constPreviousBox() const;
+    //! Get the local number of atoms
+    int localNumAtoms() const;
+    //! Get the total number of atoms
+    int totalNumAtoms() const;
+
+    //! The element taking part in the simulator loop
+    class Element;
+    //! Get pointer to element (whose lifetime is managed by this)
+    Element* element();
+    //! Initial set up for the associated element
+    void setup();
+
+    //! Update the reference temperature
+    void updateReferenceTemperature(ArrayRef<const real>                temperatures,
+                                    ReferenceTemperatureChangeAlgorithm algorithm);
+    //! Helper class handling reference temperature change
+    class ReferenceTemperatureHelper;
+
+    //! Read everything that can be stored in t_trxframe from a checkpoint file
+    static void readCheckpointToTrxFrame(t_trxframe* trxFrame, ReadCheckpointData readCheckpointData);
+    //! CheckpointHelper identifier
+    static const std::string& checkpointID();
+
+    //! \cond
+    // (doxygen doesn't like these)
+    // Classes which need access to legacy state
+    friend class DomDecHelper;
+    //! \endcond
+
+private:
+    //! Default constructor - only used internally
+    StatePropagatorData() = default;
+    //! The total number of atoms in the system
+    int totalNumAtoms_;
+    //! The local number of atoms
+    int localNAtoms_;
+    //! The position vector
+    PaddedHostVector<RVec> x_;
+    //! The position vector of the previous step
+    PaddedHostVector<RVec> previousX_;
+    //! The velocity vector
+    PaddedHostVector<RVec> v_;
+    //! The force vector
+    ForceBuffers f_;
+    //! The box matrix
+    matrix box_;
+    //! The box matrix of the previous step
+    matrix previousBox_;
+    //! The DD partitioning count
+    int ddpCount_;
+    //! The DD partitioning count for index_gl
+    int ddpCountCgGl_;
+    //! The global cg number of the local cgs
+    std::vector<int> cgGl_;
+
+    //! The global position vector
+    PaddedHostVector<RVec> xGlobal_;
+    //! The global position vector of the previous step
+    PaddedHostVector<RVec> previousXGlobal_;
+    //! The global velocity vector
+    PaddedHostVector<RVec> vGlobal_;
+    //! The global force vector
+    PaddedHostVector<RVec> fGlobal_;
+
+    //! The element
+    std::unique_ptr<Element> element_;
+    //! Instance of reference temperature helper
+    std::unique_ptr<ReferenceTemperatureHelper> referenceTemperatureHelper_;
+
+    //! Move x_ to previousX_
+    void copyPosition();
+    //! OMP helper to move x_ to previousX_
+    void copyPosition(int start, int end);
+
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+
+    // Access to legacy state
+    //! Give ownership of local state resources in legacy format
+    t_state* localState();
+    //! Take ownership of local state resources in legacy format
+    void setLocalState(t_state* state);
+    /*! \brief Deep copy the local state into the provided copy and
+     * return it
+     *
+     * In order to minimize reallocations, this function takes as a sink
+     * a local state object owned by the caller, copies the current local
+     * state into it, and returns the same object via a move.
+     */
+    std::unique_ptr<t_state> copyLocalState(std::unique_ptr<t_state> copy);
+    //! Get a pointer to the global state
+    t_state* globalState();
+    //! Get a force pointer
+    ForceBuffers* forcePointer();
+
+    //! Whether we're doing VV and need to reset velocities after the first half step
+    bool vvResetVelocities_;
+    //! Velocities backup for VV
+    PaddedHostVector<RVec> velocityBackup_;
+    //! Function resetting the velocities
+    void resetVelocities();
+
+    //! Whether planned total number of steps was reached (used for final output only)
+    bool isRegularSimulationEnd_;
+    //! The signalled last step (used for final output only)
+    Step lastStep_;
+
+    // Access to ISimulator data
+    //! Full simulation state (only non-nullptr on master rank).
+    t_state* globalState_;
+    //! Local simulation state
+    t_state* localState_;
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element for StatePropagatorData
+ *
+ * The `StatePropagatorData::Element` takes part in the simulator run, as it might
+ * have to save a valid state at the right moment during the
+ * integration. Placing the StatePropagatorData::Element correctly is the
+ * duty of the simulator builder - this might be automatized later
+ * if we have enough meta-data of the variables (i.e., if
+ * `StatePropagatorData` knows at which time the variables currently are,
+ * and can decide when a valid state (full-time step of all
+ * variables) is reached. The `StatePropagatorData::Element` is also a client of
+ * both the trajectory signaller and writer - it will save a
+ * state for later writeout during the simulator step if it
+ * knows that trajectory writing will occur later in the step,
+ * and it knows how to write to file given a file pointer by
+ * the `TrajectoryElement`. It is also responsible to store
+ * the state for checkpointing.
+ *
+ */
+class StatePropagatorData::Element final :
+    public ISimulatorElement,
+    public ITrajectoryWriterClient,
+    public ITrajectorySignallerClient,
+    public ICheckpointHelperClient,
+    public ILastStepSignallerClient
+{
+public:
+    //! Constructor
+    Element(StatePropagatorData* statePropagatorData,
+            FILE*                fplog,
+            const t_commrec*     cr,
+            int                  nstxout,
+            int                  nstvout,
+            int                  nstfout,
+            int                  nstxout_compressed,
+            bool                 canMoleculesBeDistributedOverPBC,
+            bool                 writeFinalConfiguration,
+            std::string          finalConfigurationFilename,
+            const t_inputrec*    inputrec,
+            const gmx_mtop_t&    globalTop);
+
+    /*! \brief Register run function for step / time
+     *
+     * This needs to be called during the integration part of the simulator,
+     * at the moment at which the state is at a full time step. Positioning
+     * this element is the responsibility of the programmer writing the
+     * integration algorithm! If the current step is a trajectory writing
+     * step, StatePropagatorData will save a backup for later writeout.
+     *
+     * This is also the place at which the current state becomes the previous
+     * state.
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    /*! \brief Backup starting velocities
+     *
+     * This is only needed for vv, where the first (velocity) half step is only
+     * used to compute the constraint virial, but the velocities need to be reset
+     * after.
+     * TODO: There must be a more elegant solution to this!
+     */
+    void elementSetup() override;
+
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    //! Set free energy data
+    void setFreeEnergyPerturbationData(FreeEnergyPerturbationData* freeEnergyPerturbationData);
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement* getElementPointerImpl(LegacySimulatorData* legacySimulatorData,
+                                                    ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                                                    StatePropagatorData*        statePropagatorData,
+                                                    EnergyData*                 energyData,
+                                                    FreeEnergyPerturbationData* freeEnergyPerturbationData,
+                                                    GlobalCommunicationHelper* globalCommunicationHelper,
+                                                    ObservablesReducer*        observablesReducer);
+
+private:
+    //! Pointer to the associated StatePropagatorData
+    StatePropagatorData* statePropagatorData_;
+
+    //! The position writeout frequency
+    const int nstxout_;
+    //! The velocity writeout frequency
+    const int nstvout_;
+    //! The force writeout frequency
+    const int nstfout_;
+    //! The compressed position writeout frequency
+    const int nstxout_compressed_;
+
+    //! Pointer to keep a backup of the state for later writeout
+    std::unique_ptr<t_state> localStateBackup_;
+    /*! \brief Whether the contents of localStateBackup_ are logically valid
+     *
+     * This ensures that we don't make a second backup without consuming the
+     * first. */
+    bool localStateBackupValid_ = false;
+    //! Step at which next writeout occurs
+    Step writeOutStep_;
+    //! Backup current state
+    void saveState();
+
+    //! ITrajectorySignallerClient implementation
+    std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
+
+    //! ITrajectoryWriterClient implementation
+    std::optional<ITrajectoryWriterCallback> registerTrajectoryWriterCallback(TrajectoryEvent event) override;
+
+    //! ILastStepSignallerClient implementation (used for final output only)
+    std::optional<SignallerCallback> registerLastStepCallback() override;
+
+    //! Callback writing the state to file
+    void write(gmx_mdoutf* outf, Step step, Time time);
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the free energy perturbation data (for trajectory writing only)
+    FreeEnergyPerturbationData* freeEnergyPerturbationData_;
+
+    //! No trajectory writer setup needed
+    void trajectoryWriterSetup(gmx_mdoutf gmx_unused* outf) override {}
+    //! Trajectory writer teardown - write final coordinates
+    void trajectoryWriterTeardown(gmx_mdoutf* outf) override;
+    //! A dummy CheckpointData - remove when we stop using the legacy trajectory writing function
+    WriteCheckpointDataHolder dummyCheckpointDataHolder_;
+
+    //! Whether planned total number of steps was reached (used for final output only)
+    bool isRegularSimulationEnd_;
+    //! The signalled last step (used for final output only)
+    Step lastStep_;
+    //! Whether system can have molecules distributed over PBC boundaries (used for final output only)
+    const bool canMoleculesBeDistributedOverPBC_;
+    //! Whether system has molecules self-interacting through PBC (used for final output only)
+    const bool systemHasPeriodicMolecules_;
+    //! The PBC type (used for final output only)
+    const PbcType pbcType_;
+    //! The (planned) last step - determines whether final configuration is written (used for final output only)
+    const Step lastPlannedStep_;
+    //! Whether final configuration was chosen in mdrun options (used for final output only)
+    const bool writeFinalConfiguration_;
+    //! The filename of the final configuration file (used for final output only)
+    const std::string finalConfigurationFilename_;
+
+    // Access to ISimulator data
+    //! Handles logging.
+    FILE* fplog_;
+    //! Handles communication.
+    const t_commrec* cr_;
+    //! Full system topology.
+    const gmx_mtop_t& top_global_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_STATEPROPAGATORDATA_H
diff --git a/src/include/gromacs/modularsimulator/topologyholder.h b/src/include/gromacs/modularsimulator/topologyholder.h
new file mode 100644 (file)
index 0000000..3c334c2
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the topology class for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+
+#ifndef GMX_MODULARSIMULATOR_TOPOLOGYHOLDER_H
+#define GMX_MODULARSIMULATOR_TOPOLOGYHOLDER_H
+
+#include <vector>
+
+#include "gromacs/compat/pointers.h"
+
+#include "modularsimulatorinterfaces.h"
+
+struct gmx_localtop_t;
+struct gmx_mtop_t;
+struct t_commrec;
+struct t_forcerec;
+struct t_inputrec;
+
+namespace gmx
+{
+class Constraints;
+class MDAtoms;
+class VirtualSitesHandler;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Object holding the topology
+ *
+ * Clients can register to get an updated local topology whenever there
+ * is a change (infrequent, only due to domdec currently).
+ */
+class TopologyHolder final : public IDomDecHelperClient
+{
+public:
+    //! Constructor
+    TopologyHolder(std::vector<ITopologyHolderClient*> clients,
+                   const gmx_mtop_t&                   globalTopology,
+                   gmx_localtop_t*                     localTopology,
+                   const t_commrec*                    cr,
+                   const t_inputrec*                   inputrec,
+                   t_forcerec*                         fr,
+                   MDAtoms*                            mdAtoms,
+                   Constraints*                        constr,
+                   VirtualSitesHandler*                vsite);
+
+    //! Get global topology
+    const gmx_mtop_t& globalTopology() const;
+
+    //! Callback on domain decomposition repartitioning
+    DomDecCallback registerDomDecCallback() override;
+
+    //! Allow domdec to access local topology directly
+    friend class DomDecHelper;
+
+    //! The builder
+    class Builder;
+
+private:
+    //! Constant reference to the global topology
+    const gmx_mtop_t& globalTopology_;
+    //! Pointer to the currently valid local topology
+    gmx_localtop_t* localTopology_;
+
+    //! List of clients to be updated if local topology changes
+    std::vector<ITopologyHolderClient*> clients_;
+
+    //! Update local topology
+    void updateLocalTopology();
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Builder for the topology holder
+ */
+class TopologyHolder::Builder
+{
+public:
+    //! Register topology client
+    void registerClient(ITopologyHolderClient* client);
+
+    //! Build TopologyHolder
+    template<typename... Args>
+    std::unique_ptr<TopologyHolder> build(Args&&... args);
+
+private:
+    //! List of clients to be updated if local topology changes
+    std::vector<ITopologyHolderClient*> clients_;
+    //! The state of the builder
+    ModularSimulatorBuilderState state_ = ModularSimulatorBuilderState::AcceptingClientRegistrations;
+};
+
+template<typename... Args>
+std::unique_ptr<TopologyHolder> TopologyHolder::Builder::build(Args&&... args)
+{
+    state_ = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
+    return std::make_unique<TopologyHolder>(std::move(clients_), std::forward<Args>(args)...);
+}
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_TOPOLOGYHOLDER_H
diff --git a/src/include/gromacs/modularsimulator/trajectoryelement.h b/src/include/gromacs/modularsimulator/trajectoryelement.h
new file mode 100644 (file)
index 0000000..4f8d487
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares the trajectory element for the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_TRAJECTORYELEMENT_H
+#define GMX_MODULARSIMULATOR_TRAJECTORYELEMENT_H
+
+#include <vector>
+
+#include "gromacs/compat/pointers.h"
+
+#include "modularsimulatorinterfaces.h"
+
+struct gmx_mtop_t;
+struct gmx_output_env_t;
+struct gmx_wallcycle;
+struct t_commrec;
+struct t_filenm;
+struct t_inputrec;
+
+namespace gmx
+{
+class IMDOutputProvider;
+struct MDModulesNotifiers;
+struct MdrunOptions;
+enum class StartingBehavior;
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Trajectory element signals and handles trajectory writing
+ *
+ * The trajectory element is both a signaller and a simulator element.
+ *
+ * During signalling phase, it checks whether the current step is a writing
+ * step for either the energy or the state (position, velocity, forces)
+ * trajectory. It then notifies the signaller clients of the upcoming step.
+ *
+ * For the simulator run, the element registers a run function at trajectory
+ * writing steps. Trajectory writing is done using a client system - the
+ * element only prepares the output struct, and passes it to the clients who
+ * write their part of the trajectory.
+ */
+class TrajectoryElement final : public ISimulatorElement, public ILoggingSignallerClient, public ITrajectorySignallerClient
+{
+public:
+    friend class TrajectoryElementBuilder;
+    //! Get the box writeout frequency for TNG
+    [[nodiscard]] int tngBoxOut() const;
+    //! Get the lambda writeout frequency for TNG
+    [[nodiscard]] int tngLambdaOut() const;
+    //! Get the compressed box writeout frequency for TNG
+    [[nodiscard]] int tngBoxOutCompressed() const;
+    //! Get the compressed lambda writeout frequency for TNG
+    [[nodiscard]] int tngLambdaOutCompressed() const;
+
+    /*! \brief Prepare trajectory writer
+     *
+     * During setup, the trajectory writer will query the writer clients for
+     * their callbacks. It will also call the setup methods of the different
+     * clients. To be run before the main simulator run, but after all clients
+     * were registered.
+     */
+    void elementSetup() override;
+
+    /*! \brief Register run function for step / time
+     *
+     * Registers a trajectory writing function if the current step / time is
+     * either a state or energy writing step, as defined by the signaller
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    /*! \brief Teardown trajectory writer
+     *
+     * During teardown, the trajectory writer will call the teardown
+     * methods of the clients and perform some additional clean-up.
+     * To be run after the main simulator run.
+     */
+    void elementTeardown() override;
+
+    //! \cond
+    // (doxygen doesn't like these...)
+    //! Allow CheckpointHelper to use outf_ (TODO: Can we improve this?)
+    friend class CheckpointHelper;
+    //! \endcond
+
+private:
+    //! Constructor
+    TrajectoryElement(std::vector<ITrajectoryWriterClient*> writerClients,
+                      FILE*                                 fplog,
+                      int                                   nfile,
+                      const t_filenm                        fnm[],
+                      const MdrunOptions&                   mdrunOptions,
+                      const t_commrec*                      cr,
+                      IMDOutputProvider*                    outputProvider,
+                      const MDModulesNotifiers&             mdModulesNotifiers,
+                      const t_inputrec*                     inputrec,
+                      const gmx_mtop_t&                     top_global,
+                      const gmx_output_env_t*               oenv,
+                      gmx_wallcycle*                        wcycle,
+                      StartingBehavior                      startingBehavior,
+                      bool                                  simulationsSharingState);
+
+    //! The next energy writing step
+    Step writeEnergyStep_;
+    //! The next state writing step
+    Step writeStateStep_;
+    //! The next communicated log writing step
+    Step writeLogStep_;
+
+    //! The output object
+    gmx_mdoutf* outf_;
+
+    //! ILoggingSignallerClient implementation
+    std::optional<SignallerCallback> registerLoggingCallback() override;
+    //! ITrajectorySignallerClient implementation
+    std::optional<SignallerCallback> registerTrajectorySignallerCallback(TrajectoryEvent event) override;
+
+    /*
+     * Trajectory writing
+     */
+    //! The trajectory writing clients
+    std::vector<ITrajectoryWriterClient*> writerClients_;
+
+    //! Callbacks to write trajectory
+    //! {
+    std::vector<ITrajectoryWriterCallback> runStateCallbacks_;
+    std::vector<ITrajectoryWriterCallback> runEnergyCallbacks_;
+    //! }
+
+    //! The writing function - calls the clients to get their contributions
+    void write(Step step, Time time, bool writeState, bool writeEnergy, bool writeLog);
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Build the `TrajectoryElement`
+ *
+ * This builder allows clients to register with the trajectory element
+ * as writer clients. The builder then builds the trajectory element.
+ */
+class TrajectoryElementBuilder final
+{
+public:
+    //! Allows clients to register as trajectory writers
+    void registerWriterClient(ITrajectoryWriterClient* client);
+
+    //! Build the TrajectoryElement
+    template<typename... Args>
+    std::unique_ptr<TrajectoryElement> build(Args&&... args);
+
+private:
+    //! List of writer clients
+    std::vector<ITrajectoryWriterClient*> writerClients_;
+    //! The state of the builder
+    ModularSimulatorBuilderState state_ = ModularSimulatorBuilderState::AcceptingClientRegistrations;
+};
+
+template<typename... Args>
+std::unique_ptr<TrajectoryElement> TrajectoryElementBuilder::build(Args&&... args)
+{
+    state_ = ModularSimulatorBuilderState::NotAcceptingClientRegistrations;
+    // NOLINTNEXTLINE(modernize-make-unique): make_unique does not work with private constructor
+    return std::unique_ptr<TrajectoryElement>(
+            new TrajectoryElement(std::move(writerClients_), std::forward<Args>(args)...));
+}
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_TRAJECTORYELEMENT_H
diff --git a/src/include/gromacs/modularsimulator/trotterhelperfunctions.h b/src/include/gromacs/modularsimulator/trotterhelperfunctions.h
new file mode 100644 (file)
index 0000000..12b54b2
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Defines helper functions used by the Trotter decomposition
+ * algorithms (Nose-Hoover chains, MTTK)
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+#ifndef GMX_MODULARSIMULATOR_TROTTERHELPERFUNCTIONS_H
+#define GMX_MODULARSIMULATOR_TROTTERHELPERFUNCTIONS_H
+
+#include "modularsimulatorinterfaces.h"
+
+namespace gmx
+{
+
+/*! \brief Check whether two times are nearly equal
+ *
+ * Times are considered close if their absolute difference is smaller
+ * than c_timePrecision.
+ *
+ * \param time           The test time
+ * \param referenceTime  The reference time
+ * \return bool          Whether the absolute difference is < c_timePrecision
+ */
+inline bool timesClose(Time time, Time referenceTime)
+{
+    /* Expected time precision
+     * Times are typically incremented in the order of 1e-3 ps (1 fs), so
+     * 1e-6 should be sufficiently tight.
+     */
+    constexpr real c_timePrecision = 1e-6;
+
+    return (time - referenceTime) * (time - referenceTime) < c_timePrecision * c_timePrecision;
+}
+
+} // namespace gmx
+#endif // GMX_MODULARSIMULATOR_TROTTERHELPERFUNCTIONS_H
diff --git a/src/include/gromacs/modularsimulator/velocityscalingtemperaturecoupling.h b/src/include/gromacs/modularsimulator/velocityscalingtemperaturecoupling.h
new file mode 100644 (file)
index 0000000..6054ae7
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declares a velocity-scaling temperature coupling element for
+ * the modular simulator
+ *
+ * \author Pascal Merz <pascal.merz@me.com>
+ * \ingroup module_modularsimulator
+ *
+ * This header is only used within the modular simulator module
+ */
+
+#ifndef GMX_MODULARSIMULATOR_VELOCITYSCALINGTEMPERATURECOUPLING_H
+#define GMX_MODULARSIMULATOR_VELOCITYSCALINGTEMPERATURECOUPLING_H
+
+#include "gromacs/utility/arrayref.h"
+
+#include "energydata.h"
+#include "modularsimulatorinterfaces.h"
+#include "propagator.h"
+
+struct t_commrec;
+
+namespace gmx
+{
+class ITemperatureCouplingImpl;
+class LegacySimulatorData;
+class ObservablesReducer;
+struct TemperatureCouplingData;
+
+//! Enum describing whether the thermostat is using full or half step kinetic energy
+enum class UseFullStepKE
+{
+    Yes,
+    No,
+    Count
+};
+
+/*! \internal
+ * \ingroup module_modularsimulator
+ * \brief Element implementing the a velocity-scaling thermostat
+ *
+ * This element takes a callback to the propagator and updates the velocity
+ * scaling factor according to the internal temperature coupling implementation.
+ *
+ * Note that the concrete implementation is handled by the concrete
+ * implementations of the ITemperatureCouplingImpl interface, while the element
+ * handles the scheduling and interfacing with other elements.
+ */
+class VelocityScalingTemperatureCoupling final :
+    public ISimulatorElement,
+    public ICheckpointHelperClient,
+    public IEnergySignallerClient
+{
+public:
+    //! Constructor
+    VelocityScalingTemperatureCoupling(int                               nstcouple,
+                                       int                               offset,
+                                       UseFullStepKE                     useFullStepKE,
+                                       ReportPreviousStepConservedEnergy reportPreviousConservedEnergy,
+                                       int64_t                           seed,
+                                       int                               numTemperatureGroups,
+                                       double                            couplingTimeStep,
+                                       const real*                       referenceTemperature,
+                                       const real*                       couplingTime,
+                                       const real*                       numDegreesOfFreedom,
+                                       EnergyData*                       energyData,
+                                       TemperatureCoupling               couplingType);
+
+    /*! \brief Register run function for step / time
+     *
+     * \param step                 The step number
+     * \param time                 The time
+     * \param registerRunFunction  Function allowing to register a run function
+     */
+    void scheduleTask(Step step, Time time, const RegisterRunFunction& registerRunFunction) override;
+
+    //! Sanity check at setup time
+    void elementSetup() override;
+    //! No element teardown needed
+    void elementTeardown() override {}
+
+    //! Connect this to propagator
+    void connectWithMatchingPropagator(const PropagatorConnection& connectionData,
+                                       const PropagatorTag&        propagatorTag);
+
+    //! ICheckpointHelperClient write checkpoint implementation
+    void saveCheckpointState(std::optional<WriteCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient read checkpoint implementation
+    void restoreCheckpointState(std::optional<ReadCheckpointData> checkpointData, const t_commrec* cr) override;
+    //! ICheckpointHelperClient key implementation
+    const std::string& clientID() override;
+
+    /*! \brief Factory method implementation
+     *
+     * \param legacySimulatorData  Pointer allowing access to simulator level data
+     * \param builderHelper  ModularSimulatorAlgorithmBuilder helper object
+     * \param statePropagatorData  Pointer to the \c StatePropagatorData object
+     * \param energyData  Pointer to the \c EnergyData object
+     * \param freeEnergyPerturbationData  Pointer to the \c FreeEnergyPerturbationData object
+     * \param globalCommunicationHelper   Pointer to the \c GlobalCommunicationHelper object
+     * \param observablesReducer          Pointer to the \c ObservablesReducer object
+     * \param propagatorTag  Tag of the propagator to connect to
+     * \param offset  The step offset at which the thermostat is applied
+     * \param useFullStepKE  Whether full step or half step KE is used
+     * \param reportPreviousStepConservedEnergy  Report the previous or the current step conserved energy
+     *
+     * \return  Pointer to the element to be added. Element needs to have been stored using \c storeElement
+     */
+    static ISimulatorElement*
+    getElementPointerImpl(LegacySimulatorData*                    legacySimulatorData,
+                          ModularSimulatorAlgorithmBuilderHelper* builderHelper,
+                          StatePropagatorData*                    statePropagatorData,
+                          EnergyData*                             energyData,
+                          FreeEnergyPerturbationData*             freeEnergyPerturbationData,
+                          GlobalCommunicationHelper*              globalCommunicationHelper,
+                          ObservablesReducer*                     observablesReducer,
+                          Offset                                  offset,
+                          UseFullStepKE                           useFullStepKE,
+                          ReportPreviousStepConservedEnergy       reportPreviousStepConservedEnergy,
+                          const PropagatorTag&                    propagatorTag);
+
+private:
+    //! Update the reference temperature
+    void updateReferenceTemperature(ArrayRef<const real>                temperatures,
+                                    ReferenceTemperatureChangeAlgorithm algorithm);
+
+    //! The frequency at which the thermostat is applied
+    const int nstcouple_;
+    //! If != 0, offset the step at which the thermostat is applied
+    const int offset_;
+    //! Whether we're using full step kinetic energy
+    const UseFullStepKE useFullStepKE_;
+    //! Whether we are reporting the conserved energy from the previous step
+    const ReportPreviousStepConservedEnergy reportPreviousConservedEnergy_;
+
+    //! The number of temperature groups
+    const int numTemperatureGroups_;
+    //! The coupling time step - simulation time step x nstcouple_
+    const double couplingTimeStep_;
+    //! Coupling temperature per group
+    std::vector<real> referenceTemperature_;
+    //! Coupling time per group
+    const std::vector<real> couplingTime_;
+    //! Number of degrees of freedom per group
+    const std::vector<real> numDegreesOfFreedom_;
+    //! Work exerted by thermostat per group
+    std::vector<double> temperatureCouplingIntegral_;
+
+    //! Current conserved energy contribution
+    real conservedEnergyContribution_;
+    //! Step of current conserved energy contribution
+    Step conservedEnergyContributionStep_;
+
+    // TODO: Clarify relationship to data objects and find a more robust alternative to raw pointers (#3583)
+    //! Pointer to the energy data (for ekindata)
+    EnergyData* energyData_;
+
+    //! Callback to let propagator know that we updated lambda
+    PropagatorCallback propagatorCallback_;
+
+    //! Set new lambda value (at T-coupling steps)
+    void setLambda(Step step);
+    //! Contribution to the conserved energy
+    [[nodiscard]] real conservedEnergyContribution() const;
+
+    //! The temperature coupling implementation
+    std::unique_ptr<ITemperatureCouplingImpl> temperatureCouplingImpl_;
+
+    //! CheckpointHelper identifier
+    const std::string identifier_ = "VelocityScalingTemperatureCoupling";
+    //! Helper function to read from / write to CheckpointData
+    template<CheckpointDataOperation operation>
+    void doCheckpointData(CheckpointData<operation>* checkpointData);
+
+    //! IEnergySignallerClient implementation
+    std::optional<SignallerCallback> registerEnergyCallback(EnergySignallerEvent event) override;
+    //! The next communicated energy calculation step
+    Step nextEnergyCalculationStep_;
+};
+
+} // namespace gmx
+
+#endif // GMX_MODULARSIMULATOR_VELOCITYSCALINGTEMPERATURECOUPLING_H
diff --git a/src/include/gromacs/nbnxm/atomdata.h b/src/include/gromacs/nbnxm/atomdata.h
new file mode 100644 (file)
index 0000000..50c4d5f
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief
+ *  Functionality for per-atom data in the nbnxm module
+ *
+ *  \author Berk Hess <hess@kth.se>
+ *  \ingroup module_nbnxm
+ *  \inlibraryapi
+ */
+
+
+#ifndef GMX_NBNXN_ATOMDATA_H
+#define GMX_NBNXN_ATOMDATA_H
+
+#include <cstdio>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/locality.h"
+#include "gromacs/utility/bitmask.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+class MDLogger;
+}
+
+struct NbnxmGpu;
+struct nbnxn_atomdata_t;
+struct nonbonded_verlet_t;
+
+class GpuEventSynchronizer;
+
+namespace Nbnxm
+{
+class GridSet;
+enum class KernelType;
+} // namespace Nbnxm
+
+//! Convenience type for vector with aligned memory
+template<typename T>
+using AlignedVector = std::vector<T, gmx::AlignedAllocator<T>>;
+
+enum
+{
+    nbatXYZ,
+    nbatXYZQ,
+    nbatX4,
+    nbatX8
+};
+
+//! Stride for coordinate/force arrays with xyz coordinate storage
+static constexpr int STRIDE_XYZ = 3;
+//! Stride for coordinate/force arrays with xyzq coordinate storage
+static constexpr int STRIDE_XYZQ = 4;
+//! Size of packs of x, y or z with SIMD 4-grouped packed coordinates/forces
+static constexpr int c_packX4 = 4;
+//! Size of packs of x, y or z with SIMD 8-grouped packed coordinates/forces
+static constexpr int c_packX8 = 8;
+//! Stridefor a pack of 4 coordinates/forces
+static constexpr int STRIDE_P4 = DIM * c_packX4;
+//! Stridefor a pack of 8 coordinates/forces
+static constexpr int STRIDE_P8 = DIM * c_packX8;
+
+//! Returns the index in a coordinate array corresponding to atom a
+template<int packSize>
+static inline int atom_to_x_index(int a)
+{
+    return DIM * (a & ~(packSize - 1)) + (a & (packSize - 1));
+}
+
+/*! \internal
+ * \brief Struct that holds force and energy output buffers */
+struct nbnxn_atomdata_output_t
+{
+    /*! \brief Constructor
+     *
+     * \param[in] kernelType              Type of non-bonded kernel
+     * \param[in] numEnergyGroups         The number of energy groups
+     * \param[in] simdEnergyBufferStride  Stride for entries in the energy buffers for SIMD kernels
+     * \param[in] pinningPolicy           Sets the pinning policy for all buffers used on the GPU
+     */
+    nbnxn_atomdata_output_t(Nbnxm::KernelType  kernelType,
+                            int                numEnergyGroups,
+                            int                simdEnergyBufferStride,
+                            gmx::PinningPolicy pinningPolicy);
+
+    //! f, size natoms*fstride
+    gmx::HostVector<real> f;
+    //! Shift force array, size c_numShiftVectors*DIM
+    gmx::HostVector<real> fshift;
+    //! Temporary Van der Waals group energy storage
+    gmx::HostVector<real> Vvdw;
+    //! Temporary Coulomb group energy storage
+    gmx::HostVector<real> Vc;
+    //! Temporary SIMD Van der Waals group energy storage
+    AlignedVector<real> VSvdw;
+    //! Temporary SIMD Coulomb group energy storage
+    AlignedVector<real> VSc;
+};
+
+/*! \brief Block size in atoms for the non-bonded thread force-buffer reduction.
+ *
+ * Should be a multiple of all cell and x86 SIMD sizes (i.e. 2, 4 and 8).
+ * Should be small to reduce the reduction and zeroing cost,
+ * but too small will result in overhead.
+ * Currently the block size is NBNXN_BUFFERFLAG_SIZE*3*sizeof(real)=192 bytes.
+ */
+#if GMX_DOUBLE
+#    define NBNXN_BUFFERFLAG_SIZE 8
+#else
+#    define NBNXN_BUFFERFLAG_SIZE 16
+#endif
+
+/*! \brief We store the reduction flags as gmx_bitmask_t.
+ * This limits the number of flags to BITMASK_SIZE.
+ */
+#define NBNXN_BUFFERFLAG_MAX_THREADS (BITMASK_SIZE)
+
+
+//! LJ combination rules
+enum class LJCombinationRule : int
+{
+    //! Geometric
+    Geometric,
+    //! Lorentz-Berthelot
+    LorentzBerthelot,
+    //! No rule
+    None,
+    //! Size of the enum
+    Count
+};
+
+//! String corresponding to LJ combination rule
+const char* enumValueToString(LJCombinationRule enumValue);
+
+/*! \internal
+ * \brief Struct that stores atom related data for the nbnxn module
+ *
+ * Note: performance would improve slightly when all std::vector containers
+ *       in this struct would not initialize during resize().
+ */
+struct nbnxn_atomdata_t
+{ //NOLINT(clang-analyzer-optin.performance.Padding)
+    /*! \internal
+     * \brief The actual atom data parameter values */
+    struct Params
+    {
+        /*! \brief Constructor
+         *
+         * \param[in] pinningPolicy  Sets the pinning policy for all data that might be transfered to a GPU
+         */
+        Params(gmx::PinningPolicy pinningPolicy);
+
+        //! The number of different atom types
+        int numTypes;
+        //! Lennard-Jone 6*C6 and 12*C12 parameters, size numTypes*2*2
+        gmx::HostVector<real> nbfp;
+        //! Combination rule, see enum defined above
+        LJCombinationRule ljCombinationRule;
+        //! LJ parameters per atom type, size numTypes*2
+        gmx::HostVector<real> nbfp_comb;
+        //! As nbfp, but with a stride for the present SIMD architecture
+        AlignedVector<real> nbfp_aligned;
+        //! Atom types per atom
+        gmx::HostVector<int> type;
+        //! LJ parameters per atom for fast SIMD loading
+        gmx::HostVector<real> lj_comb;
+        //! Charges per atom, not set with format nbatXYZQ
+        gmx::HostVector<real> q;
+        //! The number of energy groups
+        int nenergrp;
+        //! 2log(nenergrp)
+        int neg_2log;
+        //! The energy groups, one int entry per cluster, only set when needed
+        gmx::HostVector<int> energrp;
+    };
+
+    /*! \internal
+     * \brief Diagonal and topology exclusion helper data for all SIMD kernels. */
+    struct SimdMasks
+    {
+        SimdMasks();
+
+        //! Helper data for setting up diagonal exclusion masks in the SIMD 4xN kernels
+        AlignedVector<real> diagonal_4xn_j_minus_i;
+        //! Helper data for setting up diaginal exclusion masks in the SIMD 2xNN kernels
+        AlignedVector<real> diagonal_2xnn_j_minus_i;
+        //! Filters for topology exclusion masks for the SIMD kernels
+        AlignedVector<uint32_t> exclusion_filter;
+        //! Filters for topology exclusion masks for double SIMD kernels without SIMD int32 logical support
+        AlignedVector<uint64_t> exclusion_filter64;
+        //! Array of masks needed for exclusions
+        AlignedVector<real> interaction_array;
+    };
+
+    /*! \brief Constructor
+     *
+     * \param[in] pinningPolicy      Sets the pinning policy for all data that might be transferred
+     *                               to a GPU
+     * \param[in] mdlog              The logger
+     * \param[in] kernelType         Nonbonded NxN kernel type
+     * \param[in] enbnxninitcombrule LJ combination rule
+     * \param[in] ntype              Number of atom types
+     * \param[in] nbfp               Non-bonded force parameters
+     * \param[in] n_energygroups     Number of energy groups
+     * \param[in] nout               Number of output data structures
+     */
+    nbnxn_atomdata_t(gmx::PinningPolicy        pinningPolicy,
+                     const gmx::MDLogger&      mdlog,
+                     Nbnxm::KernelType         kernelType,
+                     int                       enbnxninitcombrule,
+                     int                       ntype,
+                     gmx::ArrayRef<const real> nbfp,
+                     int                       n_energygroups,
+                     int                       nout);
+
+    //! Returns a const reference to the parameters
+    const Params& params() const { return params_; }
+
+    //! Returns a non-const reference to the parameters
+    Params& paramsDeprecated() { return params_; }
+
+    //! Returns the current total number of atoms stored
+    int numAtoms() const { return numAtoms_; }
+
+    //! Return the coordinate buffer, and q with xFormat==nbatXYZQ
+    gmx::ArrayRef<const real> x() const { return x_; }
+
+    //! Return the coordinate buffer, and q with xFormat==nbatXYZQ
+    gmx::ArrayRef<real> x() { return x_; }
+
+    //! Resizes the coordinate buffer and sets the number of atoms
+    void resizeCoordinateBuffer(int numAtoms);
+
+    //! Resizes the force buffers for the current number of atoms
+    void resizeForceBuffers();
+
+private:
+    //! The LJ and charge parameters
+    Params params_;
+    //! The total number of atoms currently stored
+    int numAtoms_;
+
+public:
+    //! Number of local atoms
+    int natoms_local;
+    //! The format of x (and q), enum
+    int XFormat;
+    //! The format of f, enum
+    int FFormat;
+    //! Do we need to update shift_vec every step?
+    bool bDynamicBox;
+    //! Shift vectors, copied from t_forcerec
+    gmx::HostVector<gmx::RVec> shift_vec;
+    //! stride for a coordinate in x (usually 3 or 4)
+    int xstride;
+    //! stride for a coordinate in f (usually 3 or 4)
+    int fstride;
+
+private:
+    //! x and possibly q, size natoms*xstride
+    gmx::HostVector<real> x_;
+
+public:
+    //! Masks for handling exclusions in the SIMD kernels
+    const SimdMasks simdMasks;
+
+    //! Output data structures, 1 per thread
+    std::vector<nbnxn_atomdata_output_t> out;
+
+    //! Reduction related data
+    //! \{
+    //! Use the flags or operate on all atoms
+    bool bUseBufferFlags;
+    //! Flags for buffer zeroing+reduc.
+    std::vector<gmx_bitmask_t> buffer_flags;
+    //! \}
+};
+
+/*! \brief Copy na rvec elements from x to xnb using nbatFormat, start dest a0,
+ * and fills up to na_round with coordinates that are far away.
+ */
+void copy_rvec_to_nbat_real(const int* a, int na, int na_round, const rvec* x, int nbatFormat, real* xnb, int a0);
+
+//! Describes the combination rule in use by this force field
+enum
+{
+    enbnxninitcombruleDETECT,
+    enbnxninitcombruleGEOM,
+    enbnxninitcombruleLB,
+    enbnxninitcombruleNONE
+};
+
+//! Sets the atomdata after pair search
+void nbnxn_atomdata_set(nbnxn_atomdata_t*            nbat,
+                        const Nbnxm::GridSet&        gridSet,
+                        gmx::ArrayRef<const int>     atomTypes,
+                        gmx::ArrayRef<const real>    atomCharges,
+                        gmx::ArrayRef<const int64_t> atomInfo);
+
+//! Copy the shift vectors to nbat
+void nbnxn_atomdata_copy_shiftvec(bool dynamic_box, gmx::ArrayRef<gmx::RVec> shift_vec, nbnxn_atomdata_t* nbat);
+
+/*! \brief Transform coordinates to xbat layout
+ *
+ * Creates a copy of the coordinates buffer using short-range ordering.
+ *
+ * \param[in] gridSet      The grids data.
+ * \param[in] locality     If the transformation should be applied to local or non local coordinates.
+ * \param[in] coordinates  Coordinates in plain rvec format.
+ * \param[in,out] nbat     Data in NBNXM format, used for mapping formats and to locate the output buffer.
+ */
+void nbnxn_atomdata_copy_x_to_nbat_x(const Nbnxm::GridSet& gridSet,
+                                     gmx::AtomLocality     locality,
+                                     const rvec*           coordinates,
+                                     nbnxn_atomdata_t*     nbat);
+
+/*! \brief Transform coordinates to xbat layout on GPU
+ *
+ * Creates a GPU copy of the coordinates buffer using short-range ordering.
+ * As input, uses coordinates in plain rvec format in GPU memory.
+ *
+ * \param[in]     gridSet    The grids data.
+ * \param[in]     locality   If the transformation should be applied to local or non local coordinates.
+ * \param[in,out] gpu_nbv    The NBNXM GPU data structure.
+ * \param[in]     d_x        Coordinates to be copied (in plain rvec format).
+ * \param[in]     xReadyOnDevice   Event synchronizer indicating that the coordinates are ready in the device memory.
+ */
+void nbnxn_atomdata_x_to_nbat_x_gpu(const Nbnxm::GridSet&   gridSet,
+                                    gmx::AtomLocality       locality,
+                                    NbnxmGpu*               gpu_nbv,
+                                    DeviceBuffer<gmx::RVec> d_x,
+                                    GpuEventSynchronizer*   xReadyOnDevice);
+
+/*! \brief Add the computed forces to \p f, an internal reduction might be performed as well
+ *
+ * \param[in]  nbat        Atom data in NBNXM format.
+ * \param[in]  locality    If the reduction should be performed on local or non-local atoms.
+ * \param[in]  gridSet     The grids data.
+ * \param[out] totalForce  Buffer to accumulate resulting force
+ */
+void reduceForces(nbnxn_atomdata_t* nbat, gmx::AtomLocality locality, const Nbnxm::GridSet& gridSet, rvec* totalForce);
+
+//! Add the fshift force stored in nbat to fshift
+void nbnxn_atomdata_add_nbat_fshift_to_fshift(const nbnxn_atomdata_t& nbat, gmx::ArrayRef<gmx::RVec> fshift);
+
+//! Get the atom start index and number of atoms for a given locality
+void nbnxn_get_atom_range(gmx::AtomLocality     atomLocality,
+                          const Nbnxm::GridSet& gridSet,
+                          int*                  atomStart,
+                          int*                  nAtoms);
+#endif
diff --git a/src/include/gromacs/nbnxm/benchmark/bench_coords.h b/src/include/gromacs/nbnxm/benchmark/bench_coords.h
new file mode 100644 (file)
index 0000000..3410bcc
--- /dev/null
@@ -0,0 +1,1553 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ * \brief
+ * This file defines a box with 1000 SPC/E water molecules for use in benchmarks
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#include <vector>
+
+#include "gromacs/math/vec.h"
+
+//! A cubic simulation box matching coordinates1000 defined below
+static const matrix box1000 = { { 3.107360, 0.0, 0.0 }, { 0.0, 3.107360, 0.0 }, { 0.0, 0.0, 3.107360 } };
+
+//! Coordinates of 1000 SPC/E molecules equilibrated at 300 K, 1 bar, LJ cut-off 1 nm
+static const std::vector<gmx::RVec> coordinates1000 = {
+    { 3.058441, 1.962230, 1.989683 },   { 2.975840, 1.980085, 1.936221 },
+    { 3.122664, 2.038002, 1.978114 },   { 2.511969, 0.591980, 0.611700 },
+    { 2.600885, 0.614626, 0.651463 },   { 2.439189, 0.625060, 0.671774 },
+    { 3.037115, 1.103423, 0.510358 },   { 3.075596, 1.113280, 0.602129 },
+    { 3.094188, 1.041190, 0.456788 },   { 0.625031, 2.325809, 2.735173 },
+    { 0.572419, 2.273776, 2.802438 },   { 0.697119, 2.267854, 2.697168 },
+    { 1.631749, 0.888913, 1.089352 },   { 1.722932, 0.929772, 1.085316 },
+    { 1.639399, 0.792924, 1.116324 },   { 0.135367, 0.404389, 0.014781 },
+    { 0.123013, 0.353549, -0.070440 },  { 0.065358, 0.475464, 0.021644 },
+    { 2.133195, 1.942149, 1.063163 },   { 2.103670, 1.932198, 1.158185 },
+    { 2.057370, 1.977206, 1.008194 },   { 0.974812, 1.273100, 2.779850 },
+    { 0.977014, 1.306597, 2.874047 },   { 0.914936, 1.331539, 2.725080 },
+    { 3.039257, 2.275091, 2.297507 },   { 3.077036, 2.367559, 2.292780 },
+    { 2.939914, 2.279080, 2.286772 },   { 3.066429, 1.655573, 0.946745 },
+    { 2.988511, 1.717413, 0.956967 },   { 3.146125, 1.694443, 0.992980 },
+    { 2.402943, 1.159655, 1.041159 },   { 2.457788, 1.113902, 0.971169 },
+    { 2.463494, 1.202601, 1.108161 },   { 0.198771, 2.872936, 2.409695 },
+    { 0.230047, 2.782781, 2.439593 },   { 0.105151, 2.887969, 2.441464 },
+    { 0.368974, 0.573758, 2.697072 },   { 0.309036, 0.494602, 2.708979 },
+    { 0.372528, 0.598619, 2.600277 },   { 0.780393, 2.245371, 0.386869 },
+    { 0.696219, 2.230132, 0.335076 },   { 0.803298, 2.342700, 0.385368 },
+    { 1.288811, 0.315362, 0.649111 },   { 1.337870, 0.256502, 0.713366 },
+    { 1.213238, 0.264277, 0.608136 },   { 2.939084, 1.486800, 0.541567 },
+    { 2.999762, 1.475771, 0.462849 },   { 2.860697, 1.543197, 0.515590 },
+    { 1.700513, 1.987843, 0.690324 },   { 1.683709, 2.077665, 0.649708 },
+    { 1.624542, 1.963847, 0.750761 },   { 2.002822, 0.880025, 2.162986 },
+    { 1.904598, 0.881033, 2.144248 },   { 2.021988, 0.936233, 2.243442 },
+    { 1.419121, 1.568915, 3.023597 },   { 1.499351, 1.625145, 3.043628 },
+    { 1.448466, 1.476567, 2.998884 },   { 1.659223, 2.063895, 0.003451 },
+    { 1.721702, 2.003487, 0.052920 },   { 1.585788, 2.009680, -0.037391 },
+    { 0.932668, 0.985297, 2.066137 },   { 0.841309, 1.019847, 2.087582 },
+    { 0.925293, 0.909652, 2.001149 },   { 2.544425, 2.069147, 0.358217 },
+    { 2.569305, 2.158265, 0.396151 },   { 2.491877, 2.017600, 0.425905 },
+    { 2.643055, 0.627143, 1.977899 },   { 2.728804, 0.579992, 1.957311 },
+    { 2.593018, 0.643929, 1.892961 },   { 2.525198, 2.511291, 0.851458 },
+    { 2.548718, 2.560892, 0.935043 },   { 2.585276, 2.431939, 0.841761 },
+    { 1.593863, 1.272151, 1.144316 },   { 1.641930, 1.220443, 1.073494 },
+    { 1.595331, 1.220620, 1.230004 },   { 0.241304, 1.026247, 1.527110 },
+    { 0.314388, 1.092000, 1.545424 },   { 0.153979, 1.074500, 1.520328 },
+    { 0.670527, 2.411080, 0.740280 },   { 0.743415, 2.366956, 0.792629 },
+    { 0.616596, 2.341413, 0.692972 },   { 0.506075, 0.784227, 0.136559 },
+    { 0.468598, 0.695519, 0.109607 },   { 0.557918, 0.823051, 0.060368 },
+    { 2.820775, 2.815071, 1.889172 },   { 2.815770, 2.751934, 1.811786 },
+    { 2.765318, 2.896000, 1.869805 },   { 0.987299, 2.128997, 0.524538 },
+    { 1.040313, 2.207735, 0.556000 },   { 0.919566, 2.159089, 0.457407 },
+    { 0.229879, 0.835755, 1.940027 },   { 0.167288, 0.879689, 2.004464 },
+    { 0.180058, 0.767698, 1.886303 },   { 0.378771, 0.998110, 0.493601 },
+    { 0.339751, 0.957631, 0.576299 },   { 0.466309, 0.954303, 0.473154 },
+    { 2.123415, 1.896470, 2.510045 },   { 2.077036, 1.955502, 2.443983 },
+    { 2.089467, 1.802874, 2.500702 },   { 1.588236, 2.328677, 1.511384 },
+    { 1.576339, 2.284743, 1.600425 },   { 1.526922, 2.407382, 1.504588 },
+    { 0.580119, 2.671499, 1.778415 },   { 0.516760, 2.699374, 1.706245 },
+    { 0.573680, 2.735504, 1.854978 },   { 0.236329, 1.658599, 0.482922 },
+    { 0.173161, 1.716011, 0.535014 },   { 0.216351, 1.562272, 0.500868 },
+    { 2.866907, 2.172310, 0.976553 },   { 2.911961, 2.101637, 0.922006 },
+    { 2.932579, 2.244494, 0.998384 },   { 2.765681, 2.315028, 2.427439 },
+    { 2.832893, 2.283105, 2.494248 },   { 2.712988, 2.237172, 2.393353 },
+    { 0.705927, 2.118901, 1.945963 },   { 0.721405, 2.171561, 1.862373 },
+    { 0.770568, 2.148338, 2.016355 },   { 1.781326, 0.167158, 0.154133 },
+    { 1.802628, 0.096981, 0.222114 },   { 1.746708, 0.123639, 0.071021 },
+    { 0.126723, 1.501790, 2.733301 },   { 0.156993, 1.595659, 2.716802 },
+    { 0.203411, 1.439118, 2.719470 },   { 0.870069, 0.129573, 0.216301 },
+    { 0.862810, 0.197557, 0.143326 },   { 0.959244, 0.084607, 0.211220 },
+    { 1.070536, 0.743136, 0.182829 },   { 1.013762, 0.666505, 0.152757 },
+    { 1.166809, 0.716182, 0.180608 },   { 0.440991, 3.021278, 2.594643 },
+    { 0.439645, 3.047789, 2.498230 },   { 0.532720, 2.990050, 2.619351 },
+    { 2.742220, 2.873495, 2.785948 },   { 2.707374, 2.780469, 2.774456 },
+    { 2.683663, 2.937420, 2.736102 },   { 0.713098, 1.865958, 0.712284 },
+    { 0.624328, 1.911507, 0.705575 },   { 0.712267, 1.802542, 0.789600 },
+    { 0.314657, 0.447043, 0.721110 },   { 0.350251, 0.534830, 0.689071 },
+    { 0.357799, 0.372848, 0.669789 },   { 2.969682, 2.658620, 2.862492 },
+    { 2.984106, 2.745657, 2.909572 },   { 2.871682, 2.639454, 2.857144 },
+    { 1.198561, 2.913779, 2.614820 },   { 1.150109, 2.838385, 2.659182 },
+    { 1.132291, 2.976540, 2.573961 },   { 2.641875, 1.268461, 1.491413 },
+    { 2.633864, 1.233835, 1.584884 },   { 2.682652, 1.359754, 1.493064 },
+    { 0.568633, 0.009039, 1.190356 },   { 0.651953, 0.059866, 1.212135 },
+    { 0.490931, 0.071934, 1.187777 },   { 2.645675, 1.030926, 1.996264 },
+    { 2.656484, 1.127034, 2.021689 },   { 2.689366, 0.973100, 2.065165 },
+    { 0.077218, 2.967854, 2.133322 },   { 0.044197, 3.062070, 2.127588 },
+    { 0.131490, 2.956560, 2.216550 },   { 0.408365, 1.237271, 1.580670 },
+    { 0.474287, 1.284063, 1.639532 },   { 0.359054, 1.304681, 1.525676 },
+    { 0.226492, 1.945573, 0.076363 },   { 0.261322, 1.853437, 0.059106 },
+    { 0.171768, 1.975692, -0.001728 },  { 0.468126, 2.450245, 2.524287 },
+    { 0.486194, 2.415823, 2.616421 },   { 0.526489, 2.402303, 2.458749 },
+    { 1.434546, 2.964952, 2.504450 },   { 1.477884, 3.052850, 2.524343 },
+    { 1.336295, 2.971528, 2.521873 },   { 1.464730, 0.522293, 0.985985 },
+    { 1.526258, 0.561016, 0.917320 },   { 1.461514, 0.422887, 0.975592 },
+    { 1.630397, 2.791382, 2.590973 },   { 1.601935, 2.722126, 2.657257 },
+    { 1.554965, 2.854999, 2.574763 },   { 1.972387, 0.963775, 2.902179 },
+    { 2.029530, 1.043059, 2.923363 },   { 1.969130, 0.950549, 2.803111 },
+    { 2.337723, 0.321846, 0.882555 },   { 2.314325, 0.235026, 0.926315 },
+    { 2.435069, 0.340610, 0.895658 },   { 0.734798, 1.529859, 3.053034 },
+    { 0.713380, 1.615060, 3.100805 },   { 0.672452, 1.457908, 3.083628 },
+    { 0.032173, 0.527923, 2.239826 },   { 0.027814, 0.477786, 2.326239 },
+    { -0.054207, 0.517106, 2.190617 },  { 0.022851, 1.802543, 0.630916 },
+    { -0.015108, 1.813262, 0.539024 },  { -0.037014, 1.847556, 0.697172 },
+    { 1.010410, 0.509648, 2.039333 },   { 1.003466, 0.551298, 2.129981 },
+    { 1.093583, 0.454356, 2.034335 },   { 2.898570, 2.091456, 1.807118 },
+    { 2.854734, 2.046376, 1.729360 },   { 2.829335, 2.138791, 1.861577 },
+    { 1.961915, 1.375020, 2.801540 },   { 1.948970, 1.298617, 2.738333 },
+    { 1.941740, 1.460950, 2.754540 },   { 0.290337, 0.199516, 2.503690 },
+    { 0.291054, 0.257287, 2.422069 },   { 0.327554, 0.250724, 2.581102 },
+    { 2.077703, 0.143438, 0.468608 },   { 2.065651, 0.217401, 0.402395 },
+    { 2.171157, 0.145407, 0.504141 },   { 0.990673, 2.712908, 0.856622 },
+    { 0.895723, 2.743789, 0.851057 },   { 1.037275, 2.761569, 0.930516 },
+    { 0.078677, 1.380516, 1.009080 },   { 0.029933, 1.464352, 1.033485 },
+    { 0.162256, 1.403821, 0.959367 },   { 1.836054, 2.642598, 1.704376 },
+    { 1.795981, 2.597778, 1.624468 },   { 1.913014, 2.588291, 1.737959 },
+    { 0.257482, 0.992506, 1.247093 },   { 0.281524, 1.086213, 1.221773 },
+    { 0.267620, 0.981034, 1.345914 },   { 1.986510, 2.050284, 0.848145 },
+    { 1.991489, 2.078718, 0.752402 },   { 1.906061, 1.992431, 0.861596 },
+    { 0.384118, 3.016757, 0.633530 },   { 0.365374, 2.938621, 0.574004 },
+    { 0.379431, 2.987730, 0.729109 },   { 1.061282, 2.803301, 1.113124 },
+    { 1.140678, 2.799514, 1.173803 },   { 0.982344, 2.762423, 1.158925 },
+    { 0.893588, 0.623562, 1.159613 },   { 0.935464, 0.532797, 1.156770 },
+    { 0.961163, 0.692300, 1.132990 },   { 0.064641, 1.597101, 1.338713 },
+    { 0.089583, 1.650727, 1.258077 },   { 0.000262, 1.525261, 1.312362 },
+    { 1.744692, 2.999075, 2.729173 },   { 1.711603, 2.931172, 2.663642 },
+    { 1.732436, 2.964397, 2.822163 },   { 1.010117, 1.010223, 2.690790 },
+    { 1.003234, 1.103514, 2.726136 },   { 1.005616, 0.945370, 2.766776 },
+    { 2.119029, 2.091893, 2.142070 },   { 2.053933, 2.055051, 2.208442 },
+    { 2.069727, 2.144103, 2.072475 },   { 0.476711, 0.278280, 2.980274 },
+    { 0.398589, 0.239525, 2.931336 },   { 0.546481, 0.207756, 2.992862 },
+    { 1.017548, 1.173820, 0.262341 },   { 0.930974, 1.150575, 0.306663 },
+    { 1.038587, 1.270192, 0.278769 },   { 3.006009, 2.653507, 0.115338 },
+    { 3.070976, 2.729519, 0.116520 },   { 2.913460, 2.688961, 0.102011 },
+    { 0.321159, 1.479760, 1.402806 },   { 0.401871, 1.536967, 1.417403 },
+    { 0.240644, 1.537977, 1.391482 },   { 1.629104, 2.275881, 0.958820 },
+    { 1.558865, 2.324456, 1.010849 },   { 1.624176, 2.178136, 0.979354 },
+    { 3.066126, 2.858316, 1.644953 },   { 3.084059, 2.953924, 1.668140 },
+    { 3.146678, 2.803116, 1.666501 },   { 1.051189, 2.076017, 1.920453 },
+    { 1.125807, 2.103292, 1.859722 },   { 0.969998, 2.130757, 1.900170 },
+    { 1.516509, 0.100229, 0.350171 },   { 1.560545, 0.020237, 0.309401 },
+    { 1.431952, 0.120519, 0.300791 },   { 0.493363, 2.189335, 1.269427 },
+    { 0.521529, 2.285282, 1.270284 },   { 0.404068, 2.180412, 1.313548 },
+    { 2.807439, 1.983481, 1.563125 },   { 2.768178, 2.068799, 1.528782 },
+    { 2.841332, 1.929104, 1.486351 },   { 2.853076, 0.830733, 1.400601 },
+    { 2.899426, 0.745984, 1.374732 },   { 2.909799, 0.908954, 1.374833 },
+    { 2.959505, 0.005017, 0.423243 },   { 2.864585, 0.013417, 0.453569 },
+    { 3.007911, 0.090678, 0.441104 },   { 2.807480, 1.919990, 2.908606 },
+    { 2.827273, 1.955044, 2.817067 },   { 2.804816, 1.820055, 2.906175 },
+    { 0.082000, 0.549511, 1.184398 },   { 0.156887, 0.615066, 1.174679 },
+    { 0.106704, 0.463752, 1.139285 },   { 0.629631, 0.003336, 1.945927 },
+    { 0.544066, -0.015054, 1.897548 },  { 0.670653, 0.087312, 1.910356 },
+    { 1.183012, 0.907457, 0.474085 },   { 1.250390, 0.969782, 0.434387 },
+    { 1.156401, 0.839314, 0.405907 },   { 3.032287, 0.465330, 0.292632 },
+    { 3.062801, 0.530277, 0.222984 },   { 3.089603, 0.474409, 0.374072 },
+    { 2.243416, 0.013336, 2.312642 },   { 2.250096, 0.111913, 2.328068 },
+    { 2.159889, -0.021479, 2.355200 },  { 1.164546, 0.173416, 1.168458 },
+    { 1.104884, 0.099010, 1.138385 },   { 1.206309, 0.149320, 1.256066 },
+    { 2.607532, 1.124091, 1.724250 },   { 2.622452, 1.052768, 1.655763 },
+    { 2.656772, 1.100643, 1.808069 },   { 2.551467, 1.338625, 1.208755 },
+    { 2.583643, 1.301105, 1.295686 },   { 2.619725, 1.402104, 1.172543 },
+    { 2.136262, 1.322655, 0.114168 },   { 2.208042, 1.328919, 0.183511 },
+    { 2.092568, 1.411940, 0.103260 },   { 0.941991, 1.920726, 2.868290 },
+    { 0.939600, 1.894556, 2.964776 },   { 0.851093, 1.908966, 2.828299 },
+    { 0.004144, 1.775721, 2.412374 },   { -0.041340, 1.699791, 2.365836 },
+    { -0.012548, 1.860487, 2.362015 },  { 0.867448, 0.363037, 0.498578 },
+    { 0.800322, 0.437082, 0.501957 },   { 0.953402, 0.397657, 0.460984 },
+    { 0.890184, 0.676760, 1.539212 },   { 0.935641, 0.621324, 1.608930 },
+    { 0.824962, 0.738580, 1.583080 },   { 2.539177, 1.282032, 3.012743 },
+    { 2.501141, 1.363299, 2.968595 },   { 2.610318, 1.309461, 3.077447 },
+    { 1.604518, 1.894267, 1.505941 },   { 1.686555, 1.935396, 1.545670 },
+    { 1.617543, 1.795488, 1.497399 },   { 2.913814, 2.506912, 1.298808 },
+    { 2.839748, 2.479654, 1.237398 },   { 2.888555, 2.485341, 1.393130 },
+    { 2.479043, 2.811393, 0.464821 },   { 2.461245, 2.785192, 0.369970 },
+    { 2.480291, 2.729592, 0.522328 },   { 0.000418, 2.908271, 0.684488 },
+    { 0.052322, 2.991863, 0.666647 },   { 0.048874, 2.852730, 0.752070 },
+    { 2.420592, 0.017713, 1.710805 },   { 2.390744, 0.098744, 1.761235 },
+    { 2.369678, 0.011235, 1.624981 },   { 2.926354, 2.206255, 0.118647 },
+    { 3.026107, 2.212509, 0.115431 },   { 2.891067, 2.188804, 0.026722 },
+    { 3.083248, 0.397414, 1.498830 },   { 3.094998, 0.418032, 1.595973 },
+    { 3.110143, 0.302695, 1.481366 },   { 1.151128, 1.570859, 1.066268 },
+    { 1.137265, 1.624490, 0.983012 },   { 1.236225, 1.518892, 1.058662 },
+    { 2.100275, 0.308399, 0.082371 },   { 2.063669, 0.215716, 0.074010 },
+    { 2.068801, 0.349058, 0.168139 },   { 2.089025, 0.726449, 2.710428 },
+    { 2.158632, 0.687899, 2.649858 },   { 2.125094, 0.731738, 2.803546 },
+    { 1.069548, 0.219822, 2.643919 },   { 1.155804, 0.212609, 2.593841 },
+    { 1.069971, 0.303144, 2.699212 },   { 0.215623, 1.771402, 2.733978 },
+    { 0.116525, 1.780547, 2.724178 },   { 0.259711, 1.857516, 2.708670 },
+    { 0.076321, 2.814589, 0.017922 },   { 0.153935, 2.798606, -0.043074 },
+    { -0.004197, 2.838785, -0.036221 }, { 0.827608, 0.266569, 2.523361 },
+    { 0.924521, 0.255060, 2.545165 },   { 0.782806, 0.317778, 2.596643 },
+    { 1.774364, 1.200772, 0.922419 },   { 1.847724, 1.268283, 0.914643 },
+    { 1.699914, 1.225138, 0.860262 },   { 1.843738, 0.863281, 1.426679 },
+    { 1.835180, 0.960574, 1.448143 },   { 1.909114, 0.820935, 1.489391 },
+    { 2.190590, 2.093015, 0.489172 },   { 2.114286, 2.129196, 0.542731 },
+    { 2.168165, 2.097687, 0.391831 },   { 2.856985, 2.708571, 2.421630 },
+    { 2.826059, 2.637500, 2.358445 },   { 2.940505, 2.678308, 2.467548 },
+    { 0.701585, 1.132874, 1.532032 },   { 0.713505, 1.046958, 1.581796 },
+    { 0.636137, 1.190763, 1.580667 },   { 1.112152, 1.761410, 2.730896 },
+    { 1.032952, 1.800466, 2.777823 },   { 1.183341, 1.740039, 2.797795 },
+    { 2.362848, 0.400039, 0.467593 },   { 2.413963, 0.451503, 0.536431 },
+    { 2.364750, 0.449557, 0.380735 },   { 1.923021, 2.539071, 1.210065 },
+    { 2.005782, 2.500150, 1.250510 },   { 1.911843, 2.504236, 1.116997 },
+    { 2.843027, 2.693089, 1.659378 },   { 2.914984, 2.761898, 1.668726 },
+    { 2.770508, 2.728503, 1.600329 },   { 0.128262, 0.810566, 1.667570 },
+    { 0.116881, 0.757626, 1.583499 },   { 0.142354, 0.906881, 1.644662 },
+    { 1.002980, 0.859088, 2.952428 },   { 0.921072, 0.801826, 2.948964 },
+    { 0.978573, 0.950206, 2.985621 },   { 0.665071, 1.176651, 0.745782 },
+    { 0.572750, 1.214473, 0.752595 },   { 0.662264, 1.078374, 0.764052 },
+    { 1.604419, 2.998470, 1.830186 },   { 1.578196, 3.085323, 1.872245 },
+    { 1.699378, 3.003726, 1.799281 },   { 0.766206, 2.635236, 2.266948 },
+    { 0.669570, 2.646575, 2.290034 },   { 0.821969, 2.650297, 2.348579 },
+    { 0.413167, 1.864376, 1.358930 },   { 0.436077, 1.767159, 1.354041 },
+    { 0.368949, 1.883842, 1.446484 },   { 2.520023, 2.005269, 2.712810 },
+    { 2.491840, 2.040054, 2.802229 },   { 2.615839, 2.028833, 2.696563 },
+    { 0.653971, 0.523083, 2.191540 },   { 0.655081, 0.426122, 2.215980 },
+    { 0.648901, 0.532338, 2.092098 },   { 2.064821, 2.995576, 1.899338 },
+    { 2.000715, 2.961996, 1.968351 },   { 2.148348, 3.027455, 1.944135 },
+    { 0.974837, 3.040805, 0.756885 },   { 0.947137, 2.957808, 0.708467 },
+    { 0.934946, 3.120620, 0.711736 },   { 2.444642, 2.518796, 2.408258 },
+    { 2.440402, 2.457240, 2.486953 },   { 2.370887, 2.586034, 2.414519 },
+    { 2.321767, 1.107241, 2.328455 },   { 2.385756, 1.168742, 2.374533 },
+    { 2.229778, 1.121331, 2.365052 },   { 1.605816, 0.704012, 0.201441 },
+    { 1.679042, 0.692486, 0.134321 },   { 1.642942, 0.745578, 0.284471 },
+    { 1.552828, 1.036090, 1.319262 },   { 1.570254, 0.975950, 1.241290 },
+    { 1.462357, 1.016354, 1.357017 },   { 3.106174, 0.095841, 1.468391 },
+    { 3.169836, 0.054846, 1.403072 },   { 3.016178, 0.105234, 1.425817 },
+    { 1.471106, 2.572710, 1.544631 },   { 1.503995, 2.624378, 1.465582 },
+    { 1.425647, 2.634814, 1.608479 },   { 2.479549, 1.837065, 1.139527 },
+    { 2.433902, 1.811477, 1.224742 },   { 2.578245, 1.842147, 1.154804 },
+    { 2.285594, 0.997748, 2.811664 },   { 2.337996, 0.950786, 2.882718 },
+    { 2.230949, 1.070507, 2.853138 },   { 2.069438, 2.790248, 2.236690 },
+    { 2.128149, 2.745234, 2.169408 },   { 2.071366, 2.738737, 2.322380 },
+    { 2.405399, 0.905125, 1.347911 },   { 2.447368, 0.898610, 1.438443 },
+    { 2.346844, 0.986097, 1.344063 },   { 2.196427, 0.438752, 2.136470 },
+    { 2.192188, 0.376343, 2.214491 },   { 2.290922, 0.445799, 2.104517 },
+    { 2.889807, 0.531760, 1.366584 },   { 2.900943, 0.497848, 1.273171 },
+    { 2.958704, 0.489830, 1.425704 },   { 1.841082, 3.042295, 1.732903 },
+    { 1.861475, 3.126049, 1.682215 },   { 1.919077, 3.018546, 1.790806 },
+    { 1.326290, 2.104904, 0.154356 },   { 1.358661, 2.045197, 0.080959 },
+    { 1.312102, 2.050989, 0.237373 },   { 3.049543, 1.131760, 2.722415 },
+    { 2.996461, 1.169327, 2.646448 },   { 3.132666, 1.088896, 2.687015 },
+    { 1.908184, 0.043121, 0.967115 },   { 1.972116, 0.062905, 1.041420 },
+    { 1.957530, 0.041801, 0.880148 },   { 2.401901, 0.513585, 1.491280 },
+    { 2.427890, 0.425841, 1.450961 },   { 2.344707, 0.564272, 1.426784 },
+    { 2.448317, 1.127002, 0.327588 },   { 2.418437, 1.074801, 0.407477 },
+    { 2.405026, 1.217141, 0.328569 },   { 0.576997, 2.600784, 2.855002 },
+    { 0.517158, 2.664093, 2.805898 },   { 0.581336, 2.513933, 2.805624 },
+    { 2.875255, 1.238762, 1.338781 },   { 2.892585, 1.147209, 1.302481 },
+    { 2.795469, 1.236189, 1.399010 },   { 1.623443, 1.734577, 3.063192 },
+    { 1.708509, 1.682883, 3.053623 },   { 1.635767, 1.806728, 3.131327 },
+    { 2.710919, 0.916881, 2.856473 },   { 2.766298, 0.905953, 2.773928 },
+    { 2.730606, 1.005542, 2.898326 },   { 0.413553, 0.398427, 1.586956 },
+    { 0.433660, 0.488295, 1.547974 },   { 0.453389, 0.327300, 1.529042 },
+    { 1.497303, 1.793333, 2.176276 },   { 1.451349, 1.745062, 2.250829 },
+    { 1.487507, 1.740990, 2.091634 },   { 1.518297, 0.712931, 1.713533 },
+    { 1.511776, 0.670016, 1.803621 },   { 1.431340, 0.702497, 1.665266 },
+    { 1.551914, 1.629915, 1.465708 },   { 1.627901, 1.578363, 1.505312 },
+    { 1.466468, 1.602561, 1.509874 },   { 0.817285, 0.341999, 1.337646 },
+    { 0.812267, 0.250486, 1.297643 },   { 0.849200, 0.406874, 1.268563 },
+    { 0.483208, 1.585241, 2.340546 },   { 0.455791, 1.506492, 2.285348 },
+    { 0.401919, 1.636533, 2.368138 },   { 0.072505, 1.709287, 2.006497 },
+    { 0.160460, 1.755185, 1.993951 },   { -0.001476, 1.775956, 1.997443 },
+    { 2.582320, 0.685754, 2.244482 },   { 2.650091, 0.637094, 2.299612 },
+    { 2.587629, 0.654280, 2.149712 },   { 1.501209, 2.306496, 0.101764 },
+    { 1.573483, 2.238069, 0.092060 },   { 1.424723, 2.267188, 0.152802 },
+    { 2.481658, 1.039356, 2.585797 },   { 2.460124, 0.963544, 2.524242 },
+    { 2.416564, 1.040181, 2.661705 },   { 0.007444, 0.131285, 3.075162 },
+    { 0.021151, 0.050319, 3.132229 },   { -0.077919, 0.121895, 3.023929 },
+    { 2.384391, 2.087250, 2.954037 },   { 2.285605, 2.102203, 2.958238 },
+    { 2.419802, 2.072319, 3.046358 },   { 2.522700, 1.616950, 0.785956 },
+    { 2.585893, 1.539614, 0.791042 },   { 2.445543, 1.593127, 0.726971 },
+    { 0.874549, 0.343416, 0.840602 },   { 0.833294, 0.425210, 0.800506 },
+    { 0.845368, 0.262750, 0.789208 },   { 1.590202, 0.346154, 1.432877 },
+    { 1.615997, 0.442727, 1.435750 },   { 1.615345, 0.302353, 1.519186 },
+    { 0.200720, 2.166011, 1.669131 },   { 0.203419, 2.079517, 1.619016 },
+    { 0.270766, 2.165386, 1.740497 },   { 2.718568, 1.424756, 0.815413 },
+    { 2.672368, 1.357349, 0.757778 },   { 2.816407, 1.424830, 0.794736 },
+    { 0.046991, 1.844195, 1.458743 },   { 0.084611, 1.875306, 1.546017 },
+    { 0.077274, 1.750582, 1.440864 },   { 2.572122, 0.480250, 0.179410 },
+    { 2.648033, 0.542352, 0.159896 },   { 2.607804, 0.388536, 0.197167 },
+    { 2.983852, 2.126827, 0.529426 },   { 2.919768, 2.105563, 0.455662 },
+    { 3.074677, 2.143297, 0.490962 },   { 2.481990, 1.585232, 2.950477 },
+    { 2.481605, 1.594815, 2.850938 },   { 2.550736, 1.646421, 2.989590 },
+    { 1.937349, 2.315709, 2.488461 },   { 1.866975, 2.245603, 2.476944 },
+    { 1.894483, 2.401770, 2.515955 },   { 2.198232, 2.608651, 1.779312 },
+    { 2.176557, 2.685274, 1.718822 },   { 2.121877, 2.544080, 1.779987 },
+    { 2.767491, 1.663955, 0.469798 },   { 2.721891, 1.697642, 0.552174 },
+    { 2.698811, 1.638682, 0.401649 },   { 0.406640, 1.422145, 1.928232 },
+    { 0.397242, 1.448410, 2.024263 },   { 0.347679, 1.343646, 1.909219 },
+    { 1.896401, 2.791342, 1.136750 },   { 1.924382, 2.698789, 1.162263 },
+    { 1.819455, 2.786541, 1.073062 },   { 1.769318, 1.881876, 2.175327 },
+    { 1.682986, 1.831440, 2.177071 },   { 1.835406, 1.832880, 2.118478 },
+    { 2.282848, 0.667216, 1.278688 },   { 2.328695, 0.755760, 1.286297 },
+    { 2.184749, 0.681493, 1.265545 },   { 0.082448, 2.196028, 2.545991 },
+    { 0.127614, 2.276112, 2.506664 },   { 0.023481, 2.153839, 2.477121 },
+    { 1.173007, 2.545388, 1.465317 },   { 1.261847, 2.555552, 1.510086 },
+    { 1.100823, 2.576852, 1.526958 },   { 0.971281, 0.476352, 1.707317 },
+    { 0.987809, 0.486035, 1.805465 },   { 1.034678, 0.408994, 1.669323 },
+    { 1.729149, 0.592677, 2.083464 },   { 1.809754, 0.606881, 2.026009 },
+    { 1.710765, 0.494708, 2.091470 },   { 0.139730, 0.213097, 1.878174 },
+    { 0.232551, 0.235783, 1.848687 },   { 0.142086, 0.178372, 1.971921 },
+    { 3.067644, 0.529023, 2.833107 },   { 3.055445, 0.610258, 2.890134 },
+    { 2.990385, 0.466966, 2.846527 },   { 1.629882, 2.760517, 1.057234 },
+    { 1.570178, 2.835637, 1.085385 },   { 1.576395, 2.676331, 1.050029 },
+    { 0.046460, 2.403684, 1.395527 },   { 0.000167, 2.351222, 1.466974 },
+    { -0.017278, 2.469978, 1.356251 },  { 0.979941, 2.502884, 1.685655 },
+    { 0.890348, 2.484811, 1.726232 },   { 1.050479, 2.455007, 1.737925 },
+    { 0.380089, 0.272073, 1.817507 },   { 0.405127, 0.180000, 1.787580 },
+    { 0.383922, 0.334644, 1.739596 },   { 1.475603, 1.663110, 1.902010 },
+    { 1.562825, 1.673352, 1.854183 },   { 1.453838, 1.565942, 1.911209 },
+    { 1.235365, 1.009114, 1.796560 },   { 1.195125, 0.917615, 1.799501 },
+    { 1.169665, 1.075583, 1.832132 },   { 3.102696, 0.787487, 2.857110 },
+    { 3.043825, 0.822299, 2.784156 },   { 3.198383, 0.805056, 2.833971 },
+    { 2.096222, 2.284527, 2.693411 },   { 2.058902, 2.270396, 2.601719 },
+    { 2.088818, 2.381204, 2.717880 },   { 2.356806, 0.691401, 2.545193 },
+    { 2.386469, 0.605803, 2.502849 },   { 2.396691, 0.768628, 2.495743 },
+    { 1.891409, 0.166304, 1.584899 },   { 1.959147, 0.204771, 1.522195 },
+    { 1.859632, 0.237563, 1.647447 },   { 2.518043, 2.459156, 3.101591 },
+    { 2.428358, 2.472460, 3.059407 },   { 2.508114, 2.457102, 3.201075 },
+    { 0.320428, 1.244524, 0.006551 },   { 0.286081, 1.304244, 0.079034 },
+    { 0.246629, 1.224461, -0.057879 },  { 2.048229, 0.089071, 0.726703 },
+    { 2.014095, 0.080462, 0.633104 },   { 2.088850, 0.179593, 0.739177 },
+    { 2.034245, 2.982632, 1.329181 },   { 2.031245, 2.911409, 1.259050 },
+    { 1.953036, 2.975792, 1.387133 },   { 0.538485, 2.084337, 1.023997 },
+    { 0.627754, 2.089723, 0.979252 },   { 0.548110, 2.108365, 1.120589 },
+    { 2.212275, 0.056864, 2.952055 },   { 2.213052, 0.078465, 2.854419 },
+    { 2.274182, 0.118766, 3.000384 },   { 1.700132, 0.240342, 0.949202 },
+    { 1.694106, 0.245036, 1.048910 },   { 1.789110, 0.203045, 0.922903 },
+    { 1.557351, 1.014863, 0.118569 },   { 1.498333, 0.953216, 0.066447 },
+    { 1.652146, 1.003249, 0.088924 },   { 1.370467, 1.107947, 0.345641 },
+    { 1.433625, 1.162976, 0.400256 },   { 1.402511, 1.104544, 0.250976 },
+    { 0.051230, 0.959064, 0.306681 },   { 0.137569, 0.966428, 0.256769 },
+    { -0.023371, 0.993105, 0.249444 },  { 1.061472, 0.435716, 0.312109 },
+    { 1.001351, 0.476028, 0.243114 },   { 1.147227, 0.407409, 0.269158 },
+    { 1.159078, 1.784237, 1.666312 },   { 1.243340, 1.831701, 1.691750 },
+    { 1.103225, 1.769421, 1.747927 },   { 2.346733, 2.132205, 2.314271 },
+    { 2.250042, 2.143686, 2.291489 },   { 2.363438, 2.166195, 2.406822 },
+    { 1.831914, 0.624811, 2.621164 },   { 1.918548, 0.648796, 2.664973 },
+    { 1.841155, 0.633610, 2.521981 },   { 2.313396, 0.217101, 1.823930 },
+    { 2.375730, 0.273528, 1.878065 },   { 2.246887, 0.275793, 1.777758 },
+    { 0.682445, 0.914361, 0.800937 },   { 0.644296, 0.833146, 0.845083 },
+    { 0.733038, 0.887148, 0.719084 },   { 1.290598, 2.697603, 1.716289 },
+    { 1.300953, 2.723287, 1.812378 },   { 1.220298, 2.754714, 1.673908 },
+    { 2.699916, 0.577677, 1.036050 },   { 2.781543, 0.527541, 1.064746 },
+    { 2.676811, 0.646124, 1.105196 },   { 2.379672, 1.923759, 2.151954 },
+    { 2.363378, 2.011183, 2.197688 },   { 2.414151, 1.940385, 2.059570 },
+    { 2.918502, 2.910934, 1.384671 },   { 2.835120, 2.891595, 1.436376 },
+    { 2.996449, 2.914455, 1.447216 },   { 3.076292, 0.830083, 0.821021 },
+    { 3.048771, 0.918554, 0.858645 },   { 3.145879, 0.844013, 0.750568 },
+    { 0.218858, 2.069650, 1.317117 },   { 0.146551, 2.023580, 1.368588 },
+    { 0.283616, 2.001745, 1.282544 },   { 0.020619, 0.874374, 2.443902 },
+    { -0.012722, 0.782671, 2.465787 },  { 0.091966, 0.900318, 2.508991 },
+    { 2.521324, 1.753594, 2.320911 },   { 2.464712, 1.785120, 2.244745 },
+    { 2.490042, 1.796457, 2.405670 },   { 2.057530, 3.102252, 2.695123 },
+    { 1.972974, 3.145551, 2.726356 },   { 2.053180, 3.088109, 2.596224 },
+    { 2.032116, 0.595488, 1.461278 },   { 2.063105, 0.673032, 1.516293 },
+    { 2.016531, 0.625497, 1.367169 },   { 1.491030, 0.686757, 2.193506 },
+    { 1.571887, 0.657781, 2.142296 },   { 1.456372, 0.772463, 2.155382 },
+    { 1.455156, 0.193460, 2.956994 },   { 1.455369, 0.291350, 2.977428 },
+    { 1.407622, 0.177574, 2.870460 },   { 3.024817, 1.123669, 2.374621 },
+    { 3.033344, 1.024199, 2.380361 },   { 2.959151, 1.155498, 2.442993 },
+    { 2.168128, 2.952341, 0.763788 },   { 2.136058, 3.040271, 0.728577 },
+    { 2.161865, 2.883290, 0.691728 },   { 0.030132, 2.537830, 0.817716 },
+    { 0.028067, 2.528831, 0.718143 },   { -0.043010, 2.599343, 0.847151 },
+    { 1.305384, 0.363666, 0.213584 },   { 1.332245, 0.277124, 0.171289 },
+    { 1.357434, 0.377131, 0.297902 },   { 2.632164, 0.136342, 0.656899 },
+    { 2.641994, 0.192049, 0.574437 },   { 2.718966, 0.090940, 0.677000 },
+    { 0.466417, 1.949597, 0.605141 },   { 0.461019, 1.914586, 0.511626 },
+    { 0.392703, 1.909972, 0.659877 },   { 0.398048, 0.367850, 2.287718 },
+    { 0.479261, 0.318780, 2.319286 },   { 0.398678, 0.460705, 2.324835 },
+    { 1.741551, 1.439929, 1.553944 },   { 1.803090, 1.434375, 1.475318 },
+    { 1.675412, 1.365036, 1.549864 },   { 1.050770, 2.232698, 1.500930 },
+    { 1.090450, 2.316498, 1.538386 },   { 1.118122, 2.186993, 1.442837 },
+    { 1.717596, 2.258860, 0.597207 },   { 1.699232, 2.267105, 0.499254 },
+    { 1.691849, 2.343831, 0.643217 },   { 2.212399, 2.773996, 0.555699 },
+    { 2.267986, 2.695320, 0.582536 },   { 2.270206, 2.841640, 0.510064 },
+    { 0.833825, 0.137371, 0.654410 },   { 0.765034, 0.067362, 0.635268 },
+    { 0.827607, 0.209855, 0.585798 },   { 1.484024, 2.431415, 1.172574 },
+    { 1.387432, 2.405719, 1.169462 },   { 1.492573, 2.524800, 1.207303 },
+    { 1.092009, 1.304918, 0.645060 },   { 1.037350, 1.229798, 0.608053 },
+    { 1.147515, 1.271401, 0.721189 },   { 2.926792, 1.120965, 0.145719 },
+    { 2.857706, 1.056880, 0.179192 },   { 2.991043, 1.072650, 0.086242 },
+    { 0.854119, 2.974997, 1.408124 },   { 0.869941, 2.893842, 1.351878 },
+    { 0.813268, 2.947735, 1.495233 },   { 0.395941, 1.628557, 1.722443 },
+    { 0.405298, 1.574434, 1.806008 },   { 0.467522, 1.602497, 1.657659 },
+    { 0.691627, 3.086008, 2.208135 },   { 0.612388, 3.045434, 2.253686 },
+    { 0.675996, 3.087823, 2.109380 },   { 1.902204, 2.035316, 0.346277 },
+    { 1.910733, 2.004423, 0.441003 },   { 1.831359, 2.105646, 0.340402 },
+    { 1.645593, 0.569489, 3.018336 },   { 1.729209, 0.620777, 3.037777 },
+    { 1.619165, 0.516928, 3.099199 },   { 2.037092, 0.324540, 1.440475 },
+    { 2.007342, 0.418695, 1.456280 },   { 2.112294, 0.323963, 1.374562 },
+    { 1.709332, 0.025492, 1.460518 },   { 1.782128, 0.081685, 1.499798 },
+    { 1.731019, -0.071218, 1.473815 },  { 1.822994, 1.386993, 1.285320 },
+    { 1.852887, 1.448187, 1.212095 },   { 1.740161, 1.338800, 1.256753 },
+    { 2.640290, 2.907177, 2.258028 },   { 2.588012, 2.895568, 2.173575 },
+    { 2.737965, 2.909498, 2.236716 },   { 2.830221, 1.201105, 2.554194 },
+    { 2.789919, 1.238195, 2.470528 },   { 2.775225, 1.229030, 2.632907 },
+    { 0.312595, 1.269209, 1.183354 },   { 0.399686, 1.257023, 1.230964 },
+    { 0.276338, 1.360314, 1.202979 },   { 1.219074, 1.304704, 1.406725 },
+    { 1.187584, 1.394406, 1.437740 },   { 1.171917, 1.233162, 1.458280 },
+    { 0.088165, 1.132162, 2.948754 },   { 0.041748, 1.067558, 3.009349 },
+    { 0.029670, 1.152032, 2.870119 },   { 2.146272, 1.433617, 2.390542 },
+    { 2.138419, 1.336790, 2.414267 },   { 2.230608, 1.448556, 2.338924 },
+    { 1.126390, 2.619047, 0.229842 },   { 1.067299, 2.650958, 0.155748 },
+    { 1.172580, 2.697254, 0.271677 },   { 0.772610, 0.713337, 2.611108 },
+    { 0.748713, 0.722431, 2.514432 },   { 0.849110, 0.774120, 2.632397 },
+    { 2.447981, 2.453893, 0.248781 },   { 2.367628, 2.396337, 0.233596 },
+    { 2.503037, 2.415403, 0.322858 },   { 0.263845, 2.493355, 0.144281 },
+    { 0.335474, 2.526864, 0.083072 },   { 0.263751, 2.547567, 0.228310 },
+    { 2.711082, 1.281642, 2.333081 },   { 2.719674, 1.381229, 2.336037 },
+    { 2.715094, 1.250643, 2.238091 },   { 2.676279, 0.650773, 2.902170 },
+    { 2.580775, 0.630422, 2.880608 },   { 2.692490, 0.748955, 2.892301 },
+    { 2.877642, 0.476823, 2.154497 },   { 2.817917, 0.409468, 2.110950 },
+    { 2.850465, 0.488665, 2.250002 },   { 1.876275, 1.779603, 0.923386 },
+    { 1.813814, 1.729332, 0.863624 },   { 1.843092, 1.775125, 1.017613 },
+    { 1.999578, 2.412161, 1.792502 },   { 2.001027, 2.333447, 1.854162 },
+    { 1.945209, 2.390079, 1.711530 },   { 1.345610, 0.547217, 1.984605 },
+    { 1.382190, 0.579678, 2.071830 },   { 1.346384, 0.447229, 1.983274 },
+    { 0.367515, 3.057362, 1.507738 },   { 0.302379, 3.058892, 1.431877 },
+    { 0.427117, 3.137462, 1.502119 },   { 3.011322, 0.923958, 3.084858 },
+    { 3.042668, 0.880246, 3.000557 },   { 2.945836, 0.864150, 3.131061 },
+    { 2.392270, 1.829070, 2.562705 },   { 2.428474, 1.901010, 2.621983 },
+    { 2.295803, 1.847105, 2.543500 },   { 1.842649, 0.637906, 1.017004 },
+    { 1.801396, 0.602764, 0.932961 },   { 1.785666, 0.612644, 1.095201 },
+    { 0.617497, 0.921076, 3.039116 },   { 0.658644, 0.998296, 3.087530 },
+    { 0.598462, 0.947354, 2.944527 },   { 3.000423, 0.591953, 0.667713 },
+    { 3.020428, 0.676659, 0.716955 },   { 3.082706, 0.560794, 0.620188 },
+    { 1.169441, 1.042230, 2.178946 },   { 1.245754, 0.989978, 2.140921 },
+    { 1.083286, 1.006290, 2.143090 },   { 2.036610, 2.690183, 0.260508 },
+    { 2.009603, 2.593929, 0.262906 },   { 2.055502, 2.721831, 0.353467 },
+    { 0.678618, 0.253853, 2.315954 },   { 0.675382, 0.155406, 2.298699 },
+    { 0.739093, 0.272140, 2.393467 },   { 1.005798, 2.653579, 3.072247 },
+    { 0.962132, 2.738055, 3.041310 },   { 1.102456, 2.654701, 3.046633 },
+    { 1.212983, 0.709212, 2.939614 },   { 1.130614, 0.765585, 2.945729 },
+    { 1.252068, 0.717137, 2.847911 },   { 1.489914, 2.604836, 0.398980 },
+    { 1.495513, 2.508495, 0.372768 },   { 1.561823, 2.625570, 0.465306 },
+    { 0.270020, 3.000161, 0.139979 },   { 0.272046, 2.929216, 0.069533 },
+    { 0.175190, 3.025624, 0.158928 },   { 0.116325, 2.146527, 0.405128 },
+    { 0.197633, 2.090927, 0.387876 },   { 0.062843, 2.154374, 0.320996 },
+    { 2.194390, 2.363083, 1.040172 },   { 2.203799, 2.458153, 1.069720 },
+    { 2.099466, 2.333825, 1.051723 },   { 2.614382, 2.672006, 2.545183 },
+    { 2.702055, 2.682635, 2.498273 },   { 2.547538, 2.632268, 2.482312 },
+    { 1.172490, 1.978854, 0.953816 },   { 1.153736, 1.956987, 0.858056 },
+    { 1.208445, 1.897823, 1.000089 },   { 0.312017, 1.290869, 2.693676 },
+    { 0.400078, 1.306245, 2.738496 },   { 0.308832, 1.197685, 2.657527 },
+    { 1.098510, 0.153188, 0.479180 },   { 1.082395, 0.060457, 0.445397 },
+    { 1.013706, 0.205890, 0.473643 },   { 1.784232, 1.875739, 2.847711 },
+    { 1.880468, 1.867252, 2.821892 },   { 1.756731, 1.795507, 2.900688 },
+    { 0.350440, 0.678473, 0.534561 },   { 0.304420, 0.661506, 0.447417 },
+    { 0.444709, 0.645498, 0.529451 },   { 1.169279, 2.747161, 2.285225 },
+    { 1.268213, 2.761560, 2.283046 },   { 1.142212, 2.689916, 2.207827 },
+    { 0.570823, 2.828304, 0.432263 },   { 0.477361, 2.806398, 0.460280 },
+    { 0.630151, 2.749729, 0.449758 },   { 2.444623, 1.342698, 1.778805 },
+    { 2.396114, 1.374737, 1.697438 },   { 2.502313, 1.264669, 1.754654 },
+    { 1.238327, 1.673449, 0.435189 },   { 1.219243, 1.771146, 0.425637 },
+    { 1.289703, 1.641577, 0.355536 },   { 0.153594, 2.293098, 1.181422 },
+    { 0.090310, 2.342888, 1.240718 },   { 0.168866, 2.201468, 1.218445 },
+    { 0.632666, 0.899307, 0.356243 },   { 0.723101, 0.856637, 0.355391 },
+    { 0.581983, 0.871642, 0.274598 },   { 1.171387, 1.540110, 0.022861 },
+    { 1.256436, 1.556098, -0.027249 },  { 1.158655, 1.612352, 0.090824 },
+    { 1.775234, 2.639426, 1.417896 },   { 1.845711, 2.597737, 1.360493 },
+    { 1.690109, 2.646981, 1.365967 },   { 2.286208, 0.014645, 1.462383 },
+    { 2.206073, 0.018088, 1.402662 },   { 2.347516, 0.090501, 1.440309 },
+    { 1.855339, 2.566208, 2.578398 },   { 1.790876, 2.631256, 2.618564 },
+    { 1.937501, 2.561317, 2.635191 },   { 1.365327, 2.886261, 0.999166 },
+    { 1.387151, 2.932093, 1.085324 },   { 1.306889, 2.945537, 0.943747 },
+    { 1.928061, 0.624805, 1.853047 },   { 2.014539, 0.628321, 1.903139 },
+    { 1.890842, 0.717205, 1.844269 },   { 1.819535, 1.163727, 2.634753 },
+    { 1.795091, 1.160180, 2.731654 },   { 1.776597, 1.087246, 2.586722 },
+    { 0.437791, 0.196657, 0.571311 },   { 0.395955, 0.125509, 0.627771 },
+    { 0.517613, 0.159163, 0.524167 },   { 0.990439, 3.084440, 1.049646 },
+    { 0.979541, 3.092549, 0.950573 },   { 0.982817, 2.988350, 1.076266 },
+    { 0.135630, 1.424594, 2.366009 },   { 0.186875, 1.452547, 2.447204 },
+    { 0.038302, 1.419049, 2.388291 },   { 0.774298, 0.700782, 3.014136 },
+    { 0.720206, 0.631836, 2.965965 },   { 0.718205, 0.782026, 3.030041 },
+    { 0.336274, 0.174819, 1.183050 },   { 0.314277, 0.220780, 1.097005 },
+    { 0.375410, 0.240907, 1.247087 },   { 2.624106, 1.198353, 2.749908 },
+    { 2.560306, 1.194010, 2.826790 },   { 2.597145, 1.131696, 2.680410 },
+    { 1.093957, 0.845871, 2.479553 },   { 1.188103, 0.816901, 2.462315 },
+    { 1.093910, 0.922684, 2.543582 },   { 1.144358, 2.465872, 2.785667 },
+    { 1.125932, 2.423311, 2.697072 },   { 1.162905, 2.394834, 2.853561 },
+    { 0.649380, 2.048907, 1.653051 },   { 0.724093, 2.114135, 1.665825 },
+    { 0.641274, 2.026088, 1.556027 },   { 2.643681, 3.013983, 1.808290 },
+    { 2.566518, 3.046667, 1.753722 },   { 2.715972, 3.083077, 1.808537 },
+    { 1.812965, 0.376719, 1.762352 },   { 1.801808, 0.339809, 1.854619 },
+    { 1.843189, 0.471878, 1.767946 },   { 0.234547, 1.903456, 0.762488 },
+    { 0.157170, 1.852394, 0.724997 },   { 0.206290, 1.997433, 0.781722 },
+    { 1.299412, 0.664482, 1.563116 },   { 1.311728, 0.569449, 1.534531 },
+    { 1.233073, 0.709042, 1.503004 },   { 0.471100, 2.414620, 0.417294 },
+    { 0.391412, 2.471872, 0.436584 },   { 0.481394, 2.346350, 0.489635 },
+    { 1.105199, 0.810713, 1.415766 },   { 1.028862, 0.771144, 1.466826 },
+    { 1.075099, 0.833553, 1.323179 },   { 0.357194, 1.129205, 2.166708 },
+    { 0.381063, 1.108356, 2.071863 },   { 0.258268, 1.120957, 2.178772 },
+    { 2.141714, 2.142842, 1.486301 },   { 2.214417, 2.177650, 1.427118 },
+    { 2.173165, 2.142784, 1.581227 },   { 1.702656, 2.224459, 0.331437 },
+    { 1.603028, 2.232063, 0.335497 },   { 1.732962, 2.233784, 0.236598 },
+    { 1.658323, 0.033785, 3.067776 },   { 1.664949, -0.057469, 3.027418 },
+    { 1.645833, 0.101485, 2.995245 },   { 1.569246, 2.399663, 2.180838 },
+    { 1.632507, 2.472664, 2.206702 },   { 1.618676, 2.312866, 2.176035 },
+    { 1.419905, 1.952344, 3.057437 },   { 1.445836, 1.856069, 3.049776 },
+    { 1.336568, 1.968723, 3.004648 },   { 0.928720, 2.693943, 2.063425 },
+    { 1.010503, 2.637688, 2.075549 },   { 0.868058, 2.681946, 2.142014 },
+    { 1.634044, 1.676942, 0.314676 },   { 1.598932, 1.702735, 0.404686 },
+    { 1.693576, 1.597106, 0.323743 },   { 0.830401, 2.033807, 0.958468 },
+    { 0.877584, 2.009187, 0.873806 },   { 0.842339, 1.960779, 1.025731 },
+    { 1.967128, 2.547359, 2.082619 },   { 1.876900, 2.575384, 2.115383 },
+    { 1.998137, 2.610913, 2.011912 },   { 1.914760, 2.240130, 2.014567 },
+    { 1.821780, 2.210964, 2.037023 },   { 1.919160, 2.340032, 2.014097 },
+    { 1.204308, 0.740440, 1.830255 },   { 1.256133, 0.732237, 1.745127 },
+    { 1.232956, 0.668707, 1.893766 },   { 2.380670, 2.109764, 1.022198 },
+    { 2.326708, 2.025893, 1.029529 },   { 2.321764, 2.184785, 0.992169 },
+    { 2.366020, 1.001282, 0.597718 },   { 2.285764, 1.010658, 0.656634 },
+    { 2.447119, 0.985657, 0.654100 },   { 0.691093, 0.752276, 2.340281 },
+    { 0.672345, 0.665438, 2.294371 },   { 0.638224, 0.825117, 2.296702 },
+    { 0.414470, 0.458922, 2.024676 },   { 0.426859, 0.408179, 1.939402 },
+    { 0.398400, 0.394716, 2.099639 },   { 0.167761, 2.702213, 0.308317 },
+    { 0.164799, 2.726732, 0.211415 },   { 0.075331, 2.705407, 0.346351 },
+    { 1.577121, 1.524739, 1.021991 },   { 1.589557, 1.439621, 1.072984 },
+    { 1.597070, 1.602521, 1.081588 },   { 1.464515, 1.174865, 1.762501 },
+    { 1.478245, 1.202376, 1.667345 },   { 1.399154, 1.099275, 1.766248 },
+    { 2.156436, 2.877467, 1.024294 },   { 2.161692, 2.918408, 0.933211 },
+    { 2.067570, 2.833082, 1.035814 },   { 2.271721, 2.343209, 1.882211 },
+    { 2.257455, 2.432253, 1.838994 },   { 2.291592, 2.356109, 1.979364 },
+    { 0.807058, 1.819083, 1.176660 },   { 0.777268, 1.875932, 1.253347 },
+    { 0.730406, 1.763369, 1.144717 },   { 0.521151, 2.636134, 0.772735 },
+    { 0.427182, 2.604330, 0.760148 },   { 0.582650, 2.557415, 0.777345 },
+    { 2.865336, 0.074459, 1.821400 },   { 2.952667, 0.047840, 1.780599 },
+    { 2.830128, 0.155624, 1.774788 },   { 1.032809, 2.928064, 1.935884 },
+    { 1.018040, 2.848521, 1.994662 },   { 1.035481, 2.898836, 1.840289 },
+    { 0.603364, 2.018313, 2.974977 },   { 0.686166, 2.053333, 3.018767 },
+    { 0.559972, 1.950936, 3.034789 },   { 2.496931, 1.651761, 1.499709 },
+    { 2.426104, 1.589820, 1.533574 },   { 2.465513, 1.695407, 1.415400 },
+    { 1.992895, 1.432951, 0.877265 },   { 2.091252, 1.415249, 0.880807 },
+    { 1.970437, 1.479332, 0.791565 },   { 1.349627, 0.944871, 0.891822 },
+    { 1.294930, 0.866245, 0.920567 },   { 1.430409, 0.952441, 0.950277 },
+    { 0.688705, 1.806392, 1.795933 },   { 0.675348, 1.802183, 1.894948 },
+    { 0.661166, 1.896480, 1.762382 },   { 2.765604, 1.268705, 0.455509 },
+    { 2.832068, 1.330161, 0.498002 },   { 2.804218, 1.176637, 0.449819 },
+    { 1.087520, 0.313048, 1.525021 },   { 1.171839, 0.329995, 1.474000 },
+    { 1.010104, 0.311057, 1.461754 },   { 2.264949, 1.629745, 1.089892 },
+    { 2.266355, 1.729527, 1.083429 },   { 2.294268, 1.590285, 1.002810 },
+    { 1.678565, 0.632669, 1.470373 },   { 1.624608, 0.654483, 1.551692 },
+    { 1.740148, 0.708734, 1.449838 },   { 2.043401, 0.809955, 1.606486 },
+    { 1.981133, 0.814050, 1.684626 },   { 2.114947, 0.878990, 1.617221 },
+    { 1.188958, 2.411149, 1.184279 },   { 1.117315, 2.345728, 1.160041 },
+    { 1.172674, 2.445693, 1.276699 },   { 2.032803, 1.248583, 1.710235 },
+    { 2.108155, 1.185674, 1.691144 },   { 2.065892, 1.323986, 1.766976 },
+    { 0.653339, 1.578073, 0.294654 },   { 0.578773, 1.637336, 0.325114 },
+    { 0.691682, 1.613835, 0.209501 },   { 0.009546, 0.004266, 0.204101 },
+    { -0.040074, -0.018504, 0.287883 }, { 0.072281, 0.080040, 0.222063 },
+    { 0.301904, 1.858297, 1.612515 },   { 0.355925, 1.926715, 1.661512 },
+    { 0.316285, 1.768475, 1.654050 },   { 0.210458, 0.733536, 2.307309 },
+    { 0.180804, 0.654013, 2.254425 },   { 0.130306, 0.783770, 2.339748 },
+    { 0.468404, 1.026549, 2.487707 },   { 0.469169, 0.997854, 2.391915 },
+    { 0.528829, 1.105369, 2.499377 },   { 1.253251, 2.170940, 2.427798 },
+    { 1.218924, 2.125540, 2.345576 },   { 1.292201, 2.102762, 2.489723 },
+    { 1.591250, 2.288296, 1.789703 },   { 1.535539, 2.354383, 1.839990 },
+    { 1.577205, 2.196951, 1.827900 },   { 2.946891, 1.547805, 0.043940 },
+    { 2.976856, 1.626139, 0.098401 },   { 3.024835, 1.510346, -0.006275 },
+    { 1.371900, 2.628764, 0.835498 },   { 1.372210, 2.716646, 0.883213 },
+    { 1.304040, 2.568374, 0.877306 },   { 0.280736, 1.464804, 0.831929 },
+    { 0.228581, 1.472811, 0.746984 },   { 0.347573, 1.539004, 0.837130 },
+    { 0.464336, 1.777354, 0.380566 },   { 0.389430, 1.725765, 0.422129 },
+    { 0.427185, 1.855411, 0.330296 },   { 1.262352, 0.703325, 0.972513 },
+    { 1.191673, 0.644211, 0.933656 },   { 1.344104, 0.649143, 0.992027 },
+    { 1.863906, 2.737826, 0.051729 },   { 1.794894, 2.699801, 0.113303 },
+    { 1.945526, 2.762192, 0.104117 },   { 1.934621, 0.339861, 2.346475 },
+    { 1.953277, 0.266858, 2.280729 },   { 1.887904, 0.301739, 2.426251 },
+    { 2.302869, 1.427632, 1.252785 },   { 2.397274, 1.397143, 1.240212 },
+    { 2.280683, 1.496666, 1.183922 },   { 1.499814, 2.033362, 1.994311 },
+    { 1.554854, 1.968437, 1.941819 },   { 1.450097, 1.984557, 2.066049 },
+    { 2.083899, 1.448550, 1.427712 },   { 2.155069, 1.430052, 1.359942 },
+    { 1.994861, 1.452375, 1.382351 },   { 1.166293, 0.405665, 2.411606 },
+    { 1.219526, 0.442955, 2.487604 },   { 1.089623, 0.466645, 2.391527 },
+    { 1.884977, 2.484835, 0.910720 },   { 1.800940, 2.508832, 0.862120 },
+    { 1.960852, 2.539311, 0.875009 },   { 1.112780, 1.256626, 2.318798 },
+    { 1.124735, 1.166291, 2.277606 },   { 1.015258, 1.277965, 2.324623 },
+    { 1.261958, 0.079632, 1.408891 },   { 1.210228, -0.005830, 1.413406 },
+    { 1.296352, 0.102628, 1.499931 },   { 2.585863, 2.434800, 1.911444 },
+    { 2.534322, 2.378065, 1.975667 },   { 2.671546, 2.463898, 1.954008 },
+    { 1.730110, 2.631838, 2.128379 },   { 1.705843, 2.703561, 2.193702 },
+    { 1.699136, 2.658108, 2.036998 },   { 0.786013, 0.466280, 2.703562 },
+    { 0.791829, 0.555341, 2.658458 },   { 0.709772, 0.466601, 2.768271 },
+    { 2.124892, 1.358270, 0.553264 },   { 2.034419, 1.400658, 0.549024 },
+    { 2.119175, 1.272546, 0.604438 },   { 1.937050, 2.839942, 0.507969 },
+    { 1.875834, 2.818483, 0.584074 },   { 2.028851, 2.805971, 0.528431 },
+    { 0.694033, 3.048894, 0.117965 },   { 0.742816, 3.121531, 0.166381 },
+    { 0.609109, 3.027780, 0.166360 },   { 1.831965, 2.250876, 0.092662 },
+    { 1.865896, 2.301798, 0.013570 },   { 1.773638, 2.175866, 0.061491 },
+    { 0.423245, 1.234308, 0.866709 },   { 0.360637, 1.303337, 0.830443 },
+    { 0.397879, 1.212525, 0.960954 },   { 0.694900, 2.540850, 0.372187 },
+    { 0.602805, 2.502432, 0.365669 },   { 0.720059, 2.582155, 0.284660 },
+    { 1.613218, 1.068730, 0.571353 },   { 1.538921, 1.001994, 0.566242 },
+    { 1.695532, 1.030236, 0.529608 },   { 0.351434, 0.972531, 0.811903 },
+    { 0.260612, 0.979713, 0.853132 },   { 0.394644, 1.062703, 0.810529 },
+    { 0.654466, 0.668950, 0.939867 },   { 0.702533, 0.633043, 0.859865 },
+    { 0.710634, 0.655200, 1.021452 },   { 0.603028, 1.706677, 1.011611 },
+    { 0.580653, 1.621617, 0.964026 },   { 0.523907, 1.767799, 1.009608 },
+    { 1.984519, 2.241066, 0.609025 },   { 2.023379, 2.331592, 0.626204 },
+    { 1.884845, 2.247855, 0.604676 },   { 2.409340, 0.321441, 3.077053 },
+    { 2.478278, 0.391544, 3.095303 },   { 2.325876, 0.364808, 3.043095 },
+    { 0.107482, 0.226784, 2.149938 },   { 0.116209, 0.319603, 2.186110 },
+    { 0.058157, 0.169871, 2.215724 },   { 2.516283, 2.299608, 1.651831 },
+    { 2.498891, 2.206877, 1.684975 },   { 2.522873, 2.362117, 1.729607 },
+    { 0.166350, 0.223961, 0.204554 },   { 0.252719, 0.174404, 0.195355 },
+    { 0.155959, 0.287342, 0.127907 },   { 1.790683, 2.102293, 2.508842 },
+    { 1.772161, 2.115965, 2.606156 },   { 1.727524, 2.033989, 2.472162 },
+    { 0.804515, 2.449580, 1.058470 },   { 0.834701, 2.389681, 1.132638 },
+    { 0.826442, 2.407366, 0.970509 },   { 0.893496, 1.408646, 1.094836 },
+    { 0.884226, 1.374665, 1.001245 },   { 0.977331, 1.462610, 1.102556 },
+    { 1.324336, 0.283014, 1.983930 },   { 1.249350, 0.246218, 1.928948 },
+    { 1.311952, 0.256118, 2.079446 },   { 2.246377, 2.623759, 1.150493 },
+    { 2.254787, 2.651518, 1.246195 },   { 2.233979, 2.704806, 1.093243 },
+    { 2.622015, 2.142252, 1.121850 },   { 2.689193, 2.164431, 1.051174 },
+    { 2.532526, 2.128120, 1.079520 },   { 2.868567, 0.744064, 0.453349 },
+    { 2.910325, 0.673469, 0.510556 },   { 2.806360, 0.701325, 0.387747 },
+    { 3.007500, 2.824869, 0.443635 },   { 3.007007, 2.870315, 0.532711 },
+    { 2.943592, 2.871184, 0.382230 },   { 0.618477, 0.645863, 0.479989 },
+    { 0.594221, 0.568779, 0.421086 },   { 0.609509, 0.731187, 0.428613 },
+    { 0.315716, 2.761151, 1.152915 },   { 0.333548, 2.715008, 1.239822 },
+    { 0.223414, 2.799613, 1.153918 },   { 2.418530, 2.718375, 0.233422 },
+    { 2.423642, 2.619487, 0.219456 },   { 2.380935, 2.761708, 0.151514 },
+    { 1.098775, 2.225525, 0.111034 },   { 1.066019, 2.283329, 0.185771 },
+    { 1.184183, 2.181157, 0.138180 },   { 0.073228, 2.893589, 1.127852 },
+    { -0.000546, 2.826243, 1.132538 },  { 0.059832, 2.952201, 1.047945 },
+    { 2.174281, 0.457878, 2.973554 },   { 2.121013, 0.408744, 3.042462 },
+    { 2.172823, 0.555783, 2.993862 },   { 1.661179, 0.921490, 2.630975 },
+    { 1.665185, 0.927948, 2.730685 },   { 1.665678, 0.825419, 2.603585 },
+    { 2.933824, 1.488535, 1.260173 },   { 2.909874, 1.400289, 1.300658 },
+    { 2.862083, 1.516509, 1.196371 },   { 0.456309, 2.283995, 2.966447 },
+    { 0.466757, 2.196379, 3.013504 },   { 0.361327, 2.294986, 2.937162 },
+    { 0.384923, 0.707170, 1.649992 },   { 0.418731, 0.700554, 1.556113 },
+    { 0.288802, 0.734733, 1.648941 },   { 1.510475, 2.724715, 1.326010 },
+    { 1.413124, 2.746983, 1.331212 },   { 1.562660, 2.807736, 1.306410 },
+    { 2.250746, 0.876319, 0.084095 },   { 2.321093, 0.946839, 0.075252 },
+    { 2.215587, 0.875860, 0.177709 },   { 0.977551, 0.759542, 0.731438 },
+    { 1.009700, 0.844792, 0.772656 },   { 1.044178, 0.686987, 0.748662 },
+    { 1.871166, 1.469743, 1.990566 },   { 1.831271, 1.463875, 2.082075 },
+    { 1.849894, 1.386424, 1.939522 },   { 2.976639, 2.909585, 2.965670 },
+    { 2.896178, 2.903513, 2.906601 },   { 2.955625, 2.966962, 3.044830 },
+    { 0.219237, 0.614174, 0.309084 },   { 0.158141, 0.621804, 0.230286 },
+    { 0.312225, 0.595369, 0.277468 },   { 0.981300, 0.603983, 2.324795 },
+    { 0.882565, 0.588395, 2.321892 },   { 1.001037, 0.681675, 2.384581 },
+    { 0.755745, 1.121905, 0.406068 },   { 0.691338, 1.191344, 0.438159 },
+    { 0.709373, 1.033485, 0.400449 },   { 1.727780, 0.415657, 0.225759 },
+    { 1.724835, 0.315869, 0.219966 },   { 1.637306, 0.450294, 0.250552 },
+    { 2.250400, 1.714328, 1.807288 },   { 2.201134, 1.794153, 1.841942 },
+    { 2.313622, 1.681184, 1.877320 },   { 1.251501, 3.006716, 0.790040 },
+    { 1.153176, 3.024619, 0.786643 },   { 1.270862, 2.918652, 0.746798 },
+    { 1.408456, 0.117617, 0.777739 },   { 1.355175, 0.033350, 0.785506 },
+    { 1.406323, 0.166559, 0.864918 },   { 0.191205, 1.398354, 0.571156 },
+    { 0.243639, 1.328404, 0.522600 },   { 0.109777, 1.356822, 0.611709 },
+    { 0.168897, 1.033560, 0.985840 },   { 0.226152, 0.997522, 1.059481 },
+    { 0.076618, 1.050336, 1.020529 },   { 0.899943, 2.296334, 1.263741 },
+    { 0.941794, 2.238530, 1.193690 },   { 0.936440, 2.271641, 1.353508 },
+    { 3.025877, 1.802499, 0.139230 },   { 3.014528, 1.810825, 0.238234 },
+    { 3.120663, 1.822856, 0.114713 },   { 2.165556, 2.121814, 1.756587 },
+    { 2.133566, 2.054018, 1.822771 },   { 2.183626, 2.208257, 1.803504 },
+    { 2.014080, 0.353870, 1.050143 },   { 1.947732, 0.401211, 1.108082 },
+    { 2.045383, 0.270858, 1.096285 },   { 1.032910, 0.439954, 2.784389 },
+    { 1.039158, 0.468637, 2.879983 },   { 0.940927, 0.458674, 2.749913 },
+    { 2.768091, 0.500326, 2.411384 },   { 2.832257, 0.519231, 2.485716 },
+    { 2.693746, 0.442723, 2.445366 },   { 2.006408, 0.715128, 1.227328 },
+    { 1.975360, 0.677657, 1.139967 },   { 1.940802, 0.783386, 1.259525 },
+    { 2.487491, 2.094551, 1.399436 },   { 2.575522, 2.109672, 1.354471 },
+    { 2.418647, 2.154795, 1.359050 },   { 1.382277, 0.933263, 2.057272 },
+    { 1.464198, 0.988093, 2.040457 },   { 1.331486, 0.922156, 1.971851 },
+    { 0.814603, 1.428325, 2.639752 },   { 0.800255, 1.459433, 2.545803 },
+    { 0.763166, 1.486824, 2.702459 },   { 1.261287, 0.211828, 2.224595 },
+    { 1.241577, 0.276306, 2.298447 },   { 1.224565, 0.121861, 2.248203 },
+    { 1.619485, 2.689898, 0.188250 },   { 1.588224, 2.651442, 0.275106 },
+    { 1.626511, 2.789316, 0.196419 },   { 0.418282, 2.348942, 1.783532 },
+    { 0.446726, 2.334551, 1.688749 },   { 0.340773, 2.412083, 1.785854 },
+    { 0.074372, 0.645818, 1.465995 },   { 0.111170, 0.656995, 1.373686 },
+    { 0.051180, 0.549745, 1.481229 },   { 3.103577, 0.430002, 1.792040 },
+    { 3.127296, 0.345212, 1.839453 },   { 3.185557, 0.486255, 1.781320 },
+    { 0.656926, 1.071854, 1.113254 },   { 0.740429, 1.082080, 1.059192 },
+    { 0.603347, 0.995283, 1.077673 },   { 1.082612, 2.167030, 1.119109 },
+    { 1.112438, 2.118508, 1.036914 },   { 1.121778, 2.122971, 1.199885 },
+    { 1.685950, 2.668299, 0.595540 },   { 1.654574, 2.595554, 0.656563 },
+    { 1.701733, 2.751685, 0.648433 },   { 1.877991, 1.451300, 0.501414 },
+    { 1.860443, 1.441725, 0.403432 },   { 1.791928, 1.469157, 0.549104 },
+    { 1.193680, 2.367192, 1.806688 },   { 1.230655, 2.274677, 1.798090 },
+    { 1.267246, 2.430350, 1.831166 },   { 1.262868, 3.023321, 2.111573 },
+    { 1.218133, 3.027728, 2.200900 },   { 1.198796, 2.986934, 2.043964 },
+    { 2.363530, 1.819884, 0.872829 },   { 2.421597, 1.755189, 0.823403 },
+    { 2.406757, 1.843780, 0.959779 },   { 2.828612, 2.346733, 0.601532 },
+    { 2.871731, 2.429490, 0.565585 },   { 2.896460, 2.273475, 0.606992 },
+    { 1.479757, 1.908335, 0.805231 },   { 1.432019, 1.821825, 0.820629 },
+    { 1.493505, 1.954847, 0.892682 },   { 2.447036, 2.469183, 1.388989 },
+    { 2.484687, 2.415866, 1.464750 },   { 2.421583, 2.560194, 1.421687 },
+    { 0.390387, 2.304647, 0.871009 },   { 0.343645, 2.374860, 0.924723 },
+    { 0.452494, 2.253251, 0.930179 },   { 1.331260, 0.646907, 0.168446 },
+    { 1.335624, 0.547157, 0.173994 },   { 1.421037, 0.685440, 0.189784 },
+    { 2.828654, 2.172696, 2.960263 },   { 2.739741, 2.218000, 2.953789 },
+    { 2.815353, 2.073702, 2.965085 },   { 1.031085, 0.986172, 0.843095 },
+    { 1.020895, 1.030502, 0.754039 },   { 0.995655, 1.046452, 0.914586 },
+    { 1.724838, 0.391131, 2.659596 },   { 1.768809, 0.480924, 2.657695 },
+    { 1.633320, 0.399657, 2.698991 },   { 2.588704, 2.692304, 1.035174 },
+    { 2.627025, 2.632110, 1.105232 },   { 2.527773, 2.758929, 1.078168 },
+    { 2.699075, 0.688808, 0.769271 },   { 2.672432, 0.666009, 0.862921 },
+    { 2.792721, 0.657848, 0.752781 },   { 0.703772, 1.603296, 2.794425 },
+    { 0.701323, 1.570932, 2.889011 },   { 0.695426, 1.702938, 2.793061 },
+    { 0.199272, 0.353715, 0.976004 },   { 0.232097, 0.405137, 0.896767 },
+    { 0.126936, 0.290899, 0.947342 },   { 2.546014, 0.024049, 2.369379 },
+    { 2.611304, -0.035563, 2.322648 },  { 2.454932, 0.010881, 2.330253 },
+    { 0.022823, 1.201578, 0.748523 },   { 0.089773, 1.186154, 0.821185 },
+    { -0.020197, 1.290949, 0.761256 },  { 0.132073, 0.862795, 0.586868 },
+    { 0.119996, 0.867413, 0.487708 },   { 0.214518, 0.810293, 0.607998 },
+    { 2.092458, 0.359644, 0.786490 },   { 2.040730, 0.360242, 0.872069 },
+    { 2.190376, 0.358194, 0.806735 },   { 0.421056, 2.015500, 1.793066 },
+    { 0.440920, 2.024396, 1.890669 },   { 0.503559, 2.035681, 1.740283 },
+    { 0.385087, 0.729295, 0.920073 },   { 0.370596, 0.804357, 0.855608 },
+    { 0.481900, 0.704266, 0.920879 },   { 0.660259, 0.556115, 1.874230 },
+    { 0.674878, 0.507989, 1.787800 },   { 0.637897, 0.651811, 1.855729 },
+    { 1.502447, 0.450094, 2.831928 },   { 1.420294, 0.484380, 2.786374 },
+    { 1.548974, 0.525641, 2.878058 },   { 0.029169, 0.014635, 1.731543 },
+    { 0.026633, 0.054837, 1.640016 },   { 0.060547, 0.083210, 1.797215 },
+    { 1.945071, 1.895363, 0.577451 },   { 1.862520, 1.917282, 0.629461 },
+    { 2.002888, 1.833848, 0.631051 },   { 2.310390, 0.609079, 0.805817 },
+    { 2.224057, 0.654864, 0.827042 },   { 2.298969, 0.510214, 0.815575 },
+    { 0.263679, 0.742484, 1.174274 },   { 0.318844, 0.741293, 1.090875 },
+    { 0.258625, 0.835913, 1.209567 },   { 2.214362, 2.795311, 3.028770 },
+    { 2.276577, 2.856483, 2.979910 },   { 2.193373, 2.833914, 3.118600 },
+    { 2.862483, 0.087396, 2.967400 },   { 2.775054, 0.134739, 2.956690 },
+    { 2.852752, 0.014295, 3.034940 },   { 1.911967, 1.618076, 2.687047 },
+    { 1.939990, 1.699615, 2.737705 },   { 1.934376, 1.629614, 2.590276 },
+    { 3.089770, 0.624647, 0.091657 },   { 3.155308, 0.685544, 0.046978 },
+    { 2.997098, 0.648104, 0.062301 },   { 0.073286, 1.189907, 2.156408 },
+    { 0.052831, 1.175998, 2.253300 },   { 0.036866, 1.278274, 2.127000 },
+    { 1.751887, 1.440179, 0.260930 },   { 1.798183, 1.408672, 0.178081 },
+    { 1.701865, 1.364040, 0.302169 },   { 0.173609, 2.179533, 0.758562 },
+    { 0.240866, 2.219671, 0.820734 },   { 0.176312, 2.227662, 0.670948 },
+    { 0.142457, 1.714121, 2.988510 },   { 0.117949, 1.619062, 3.007567 },
+    { 0.171225, 1.722647, 2.893118 },   { 1.307838, 1.001512, 1.411174 },
+    { 1.271228, 0.912830, 1.439374 },   { 1.232705, 1.066537, 1.399913 },
+    { 2.169500, 1.727567, 2.182432 },   { 2.214494, 1.641325, 2.205624 },
+    { 2.236708, 1.801605, 2.183625 },   { 1.262072, 0.462936, 1.175858 },
+    { 1.169844, 0.436656, 1.147514 },   { 1.323387, 0.459730, 1.096926 },
+    { 1.423067, 0.881474, 0.640908 },   { 1.410777, 0.897237, 0.738890 },
+    { 1.333599, 0.874747, 0.596747 },   { 1.354799, 1.797455, 1.110787 },
+    { 1.284755, 1.765934, 1.174821 },   { 1.443784, 1.760881, 1.138063 },
+    { 2.129596, 0.681632, 2.008767 },   { 2.164107, 0.608978, 2.068185 },
+    { 2.111495, 0.763493, 2.063275 },   { 2.448219, 0.769518, 0.357171 },
+    { 2.453654, 0.740682, 0.452769 },   { 2.364799, 0.732847, 0.315983 },
+    { 0.882298, 0.543308, 0.139417 },   { 0.841711, 0.611831, 0.078942 },
+    { 0.885374, 0.455061, 0.092482 },   { 0.800295, 1.861557, 2.511300 },
+    { 0.895248, 1.830847, 2.504910 },   { 0.776153, 1.875477, 2.607338 },
+    { 0.764847, 2.617533, 0.120170 },   { 0.750496, 2.712191, 0.091292 },
+    { 0.847962, 2.581897, 0.077486 },   { 2.224452, 2.284036, 0.208382 },
+    { 2.184583, 2.300793, 0.118217 },   { 2.231256, 2.185503, 0.224032 },
+    { 0.079359, 0.002159, 0.962802 },   { 0.088958, 0.015986, 0.864229 },
+    { 0.027587, 0.078089, 1.002228 },   { 2.203527, 0.302158, 2.750022 },
+    { 2.118676, 0.267117, 2.710368 },   { 2.181990, 0.363004, 2.826402 },
+    { 2.170179, 2.922779, 0.174290 },   { 2.139623, 2.835094, 0.211408 },
+    { 2.214502, 2.975912, 0.246487 },   { 0.890672, 0.080190, 2.804656 },
+    { 0.938414, 0.126680, 2.730094 },   { 0.956701, 0.052889, 2.874619 },
+    { 2.895087, 1.762880, 1.373604 },   { 2.900573, 1.663669, 1.362331 },
+    { 2.987003, 1.800097, 1.386500 },   { 1.853496, 2.074764, 1.508729 },
+    { 1.948479, 2.059296, 1.481547 },   { 1.801303, 2.107900, 1.430129 },
+    { 2.582175, 2.315870, 2.888700 },   { 2.529120, 2.231314, 2.882752 },
+    { 2.555198, 2.366456, 2.970634 },   { 2.750479, 0.538398, 1.610168 },
+    { 2.766143, 0.560952, 1.514012 },   { 2.664507, 0.579511, 1.640473 },
+    { 1.377553, 1.621616, 0.224361 },   { 1.362069, 1.531830, 0.183145 },
+    { 1.471939, 1.627547, 0.256858 },   { 0.701605, 1.997351, 0.173637 },
+    { 0.647573, 2.049848, 0.239400 },   { 0.768452, 2.057483, 0.129870 },
+    { 0.732393, 2.885926, 1.675034 },   { 0.681248, 2.960743, 1.717302 },
+    { 0.678998, 2.801575, 1.680869 },   { 1.333051, 1.944054, 2.636380 },
+    { 1.270422, 1.992525, 2.697439 },   { 1.304905, 1.848392, 2.628856 },
+    { 0.350047, 1.397444, 2.193652 },   { 0.381158, 1.302587, 2.187811 },
+    { 0.269866, 1.402622, 2.253185 },   { 2.655774, 2.801154, 1.477291 },
+    { 2.607340, 2.769915, 1.559012 },   { 2.612048, 2.883978, 1.442246 },
+    { 0.830586, 2.382898, 2.380660 },   { 0.751782, 2.323648, 2.363947 },
+    { 0.821711, 2.466316, 2.326230 },   { 0.386076, 2.773589, 2.748805 },
+    { 0.415232, 2.861381, 2.710824 },   { 0.309400, 2.737978, 2.695395 },
+    { 0.288028, 1.761089, 2.267276 },   { 0.195585, 1.734450, 2.294565 },
+    { 0.306586, 1.727296, 2.175006 },   { 2.845222, 3.049689, 0.710560 },
+    { 2.938507, 3.015530, 0.699111 },   { 2.796410, 2.992019, 0.776069 },
+    { 2.095486, 2.468157, 1.419021 },   { 2.014126, 2.429148, 1.462136 },
+    { 2.161124, 2.395254, 1.399606 },   { 0.990751, 0.380958, 1.088061 },
+    { 0.962957, 0.355990, 0.995302 },   { 1.050039, 0.309627, 1.125433 },
+    { 1.490762, 3.006773, 1.217981 },   { 1.443078, 3.092111, 1.239049 },
+    { 1.589527, 3.021852, 1.222217 },   { 1.054051, 1.680650, 2.482261 },
+    { 0.992890, 1.601710, 2.487543 },   { 1.097855, 1.694879, 2.571023 },
+    { 1.617761, 2.709729, 1.856625 },   { 1.685809, 2.664639, 1.798863 },
+    { 1.625149, 2.808851, 1.845661 },   { 0.831548, 0.104590, 1.240112 },
+    { 0.854610, 0.030748, 1.303480 },   { 0.870357, 0.084640, 1.150135 },
+    { 2.460575, 2.913002, 2.033870 },   { 2.387432, 2.978945, 2.016501 },
+    { 2.544679, 2.944073, 1.989585 },   { 0.968554, 1.072304, 0.587917 },
+    { 1.021416, 0.999670, 0.543986 },   { 0.886242, 1.090771, 0.534217 },
+    { 1.747197, 0.995358, 2.249228 },   { 1.834103, 1.034204, 2.279860 },
+    { 1.689613, 0.975781, 2.328606 },   { 2.113166, 1.198713, 2.946735 },
+    { 2.164920, 1.232009, 3.025557 },   { 2.082168, 1.276428, 2.891968 },
+    { 0.386328, 0.003613, 1.788966 },   { 0.370117, -0.024061, 1.694249 },
+    { 0.331931, -0.053732, 1.850224 },  { 1.113078, 2.990990, 0.400315 },
+    { 1.033021, 2.934960, 0.421561 },   { 1.196169, 2.937301, 0.414924 },
+    { 3.004892, 0.215602, 0.988293 },   { 2.941095, 0.159722, 1.041278 },
+    { 2.959599, 0.248333, 0.905364 },   { 1.180028, 0.462930, 3.027119 },
+    { 1.194383, 0.448931, 3.125088 },   { 1.205635, 0.556462, 3.002706 },
+    { 1.274036, 0.501724, 2.672057 },   { 1.301730, 0.596901, 2.685261 },
+    { 1.191003, 0.483553, 2.724738 },   { 2.947187, 2.426754, 0.273837 },
+    { 2.910177, 2.352895, 0.217490 },   { 2.962008, 2.507751, 0.217092 },
+    { 0.414034, 1.604272, 2.862308 },   { 0.369433, 1.665968, 2.797468 },
+    { 0.511529, 1.598227, 2.840903 },   { 0.857555, 1.198856, 1.337218 },
+    { 0.815665, 1.254240, 1.265260 },   { 0.790251, 1.181020, 1.408996 },
+    { 1.402725, 1.861893, 1.683336 },   { 1.478004, 1.884592, 1.621549 },
+    { 1.438488, 1.812987, 1.762893 },   { 1.143436, 2.070677, 2.177391 },
+    { 1.126093, 1.974251, 2.197423 },   { 1.140420, 2.085246, 2.078504 },
+    { 1.140117, 2.529814, 2.107822 },   { 1.195065, 2.512471, 2.026092 },
+    { 1.179853, 2.481181, 2.185642 },   { 1.977009, 0.352605, 0.334132 },
+    { 1.906708, 0.390859, 0.274179 },   { 1.962216, 0.384824, 0.427636 },
+    { 2.130115, 1.470625, 1.830714 },   { 2.130574, 1.426674, 1.920537 },
+    { 2.139711, 1.569528, 1.841947 },   { 0.730700, 0.203115, 1.799955 },
+    { 0.694439, 0.296295, 1.801601 },   { 0.830605, 0.206164, 1.803080 },
+    { 1.903507, 2.950766, 2.105392 },   { 1.961982, 2.880427, 2.145802 },
+    { 1.809765, 2.939068, 2.138189 },   { 1.362461, 0.776317, 2.440580 },
+    { 1.373552, 0.711877, 2.364919 },   { 1.401240, 0.864848, 2.414921 },
+    { 1.393736, 0.230352, 1.027233 },   { 1.474875, 0.215232, 1.083694 },
+    { 1.312059, 0.203810, 1.078462 },   { 0.883267, 2.290109, 0.853754 },
+    { 0.964497, 2.279289, 0.796442 },   { 0.862588, 2.203355, 0.898988 },
+    { 1.860047, 2.351311, 1.532016 },   { 1.892853, 2.257751, 1.518966 },
+    { 1.760116, 2.352393, 1.528474 },   { 0.907781, 1.468871, 0.302742 },
+    { 0.943288, 1.506453, 0.388339 },   { 0.808771, 1.482381, 0.298937 },
+    { 0.634389, 0.172523, 0.350440 },   { 0.731006, 0.157177, 0.329712 },
+    { 0.579360, 0.100624, 0.307988 },   { 0.948849, 0.786484, 1.870334 },
+    { 1.046390, 0.765851, 1.862583 },   { 0.902713, 0.711892, 1.918370 },
+    { 0.928507, 1.428757, 1.800533 },   { 0.964649, 1.337409, 1.819219 },
+    { 0.981629, 1.471275, 1.727250 },   { 0.520126, 0.449796, 0.329923 },
+    { 0.480298, 0.444788, 0.238333 },   { 0.545807, 0.358005, 0.360171 },
+    { 0.173019, 1.766946, 1.124286 },   { 0.119940, 1.845672, 1.155670 },
+    { 0.266447, 1.795893, 1.103473 },   { 0.294915, 2.483116, 1.059922 },
+    { 0.237058, 2.412428, 1.100614 },   { 0.279515, 2.569983, 1.107008 },
+    { 1.967528, 1.701598, 0.384068 },   { 1.937630, 1.618064, 0.430200 },
+    { 1.953844, 1.779905, 0.444737 },   { 2.602165, 1.306857, 2.049074 },
+    { 2.675314, 1.367643, 2.018182 },   { 2.530699, 1.302540, 1.979261 },
+    { 0.466325, 2.360293, 2.055629 },   { 0.431952, 2.347197, 1.962640 },
+    { 0.560285, 2.394324, 2.051953 },   { 2.050154, 1.584414, 0.148513 },
+    { 2.040061, 1.624411, 0.239608 },   { 2.068395, 1.656922, 0.082106 },
+    { 1.556378, 1.957320, 2.428697 },   { 1.476038, 1.941088, 2.485985 },
+    { 1.529396, 1.956406, 2.332410 },   { 0.682812, 1.205529, 2.526461 },
+    { 0.723788, 1.287438, 2.566610 },   { 0.754192, 1.137731, 2.508900 },
+    { 1.535311, 0.251227, 2.208550 },   { 1.556668, 0.315254, 2.282337 },
+    { 1.437096, 0.253450, 2.189872 },   { 2.668479, 2.280105, 0.800170 },
+    { 2.710946, 2.318387, 0.718127 },   { 2.739255, 2.259010, 0.867592 },
+    { 1.542641, 3.084046, 2.220568 },   { 1.544509, 3.183810, 2.213954 },
+    { 1.458575, 3.049445, 2.178907 },   { 1.457762, 1.374257, 2.244142 },
+    { 1.499569, 1.291727, 2.206182 },   { 1.369735, 1.390552, 2.199582 },
+    { 2.519020, 1.910788, 1.891775 },   { 2.505169, 1.980275, 1.821208 },
+    { 2.518774, 1.820060, 1.849722 },   { 1.153670, 2.497971, 0.920991 },
+    { 1.071685, 2.549851, 0.896764 },   { 1.150536, 2.474089, 1.018047 },
+    { 2.586281, 3.065417, 2.640789 },   { 2.568059, 3.047493, 2.544111 },
+    { 2.503036, 3.049598, 2.693891 },   { 2.306781, 1.384101, 0.311448 },
+    { 2.360460, 1.468263, 0.305517 },   { 2.263250, 1.378589, 0.401307 },
+    { 2.141848, 2.022213, 0.235023 },   { 2.188715, 1.934828, 0.222090 },
+    { 2.046005, 2.005531, 0.258168 },   { 0.201078, 1.356753, 0.228828 },
+    { 0.104294, 1.337330, 0.244812 },   { 0.255501, 1.318727, 0.303609 },
+    { 0.457343, 0.726277, 2.048767 },   { 0.460321, 0.628162, 2.067859 },
+    { 0.365421, 0.751595, 2.018612 },   { 2.860924, 1.577827, 1.715642 },
+    { 2.807984, 1.661584, 1.729132 },   { 2.834381, 1.510187, 1.784347 },
+    { 3.020626, 2.542969, 0.500236 },   { 2.999224, 2.640062, 0.489514 },
+    { 3.013950, 2.497365, 0.411491 },   { 2.449280, 0.880812, 2.387606 },
+    { 2.481825, 0.817972, 2.316952 },   { 2.399173, 0.956029, 2.344807 },
+    { 0.839472, 2.578596, 0.606320 },   { 0.789949, 2.578031, 0.519445 },
+    { 0.782640, 2.537078, 0.677358 },   { 1.243451, 2.095390, 1.335077 },
+    { 1.341417, 2.115228, 1.338089 },   { 1.228127, 1.999442, 1.358721 },
+    { 0.679730, 2.838689, 2.009180 },   { 0.764800, 2.786702, 2.001406 },
+    { 0.699085, 2.936157, 1.997983 },   { 0.738842, 1.537655, 0.650196 },
+    { 0.761227, 1.584947, 0.564976 },   { 0.790684, 1.578252, 0.725458 },
+    { 2.835650, 0.699534, 0.020790 },   { 2.777710, 0.692381, 0.101980 },
+    { 2.780032, 0.685783, -0.061170 },  { 1.845371, 0.433166, 1.258526 },
+    { 1.842422, 0.520378, 1.307367 },   { 1.791587, 0.365195, 1.308398 },
+    { 3.094758, 1.053543, 1.958825 },   { 3.137912, 1.085383, 2.043228 },
+    { 3.044658, 0.968902, 1.976876 },   { 1.893173, 3.056022, 0.347448 },
+    { 1.915403, 2.979650, 0.408054 },   { 1.943785, 3.137299, 0.376303 },
+    { 0.227549, 2.726489, 1.743416 },   { 0.256351, 2.780895, 1.822222 },
+    { 0.292572, 2.739273, 1.668526 },   { 1.149302, 2.387237, 0.343532 },
+    { 1.166208, 2.474303, 0.297339 },   { 1.104012, 2.404185, 0.431062 },
+    { 0.998224, 1.657693, 1.298714 },   { 1.056918, 1.616397, 1.229074 },
+    { 0.929567, 1.715405, 1.254493 },   { 1.580309, 1.703216, 2.655682 },
+    { 1.649980, 1.643679, 2.695698 },   { 1.618097, 1.794935, 2.643051 },
+    { 2.081265, 2.616518, 0.762181 },   { 2.163416, 2.561470, 0.777047 },
+    { 2.033529, 2.583809, 0.680625 },   { 1.576754, 0.079976, 2.599135 },
+    { 1.624151, 0.042940, 2.679022 },   { 1.635391, 0.071299, 2.518597 },
+    { 1.820339, 2.087323, 1.807897 },   { 1.862842, 2.081803, 1.717547 },
+    { 1.876202, 2.144950, 1.867550 },   { 1.288347, 2.784119, 0.352047 },
+    { 1.348111, 2.713677, 0.313756 },   { 1.267249, 2.761808, 0.447216 },
+    { 2.053319, 2.806163, 2.812877 },   { 2.012843, 2.896631, 2.799563 },
+    { 2.079198, 2.795015, 2.908825 },   { 3.103936, 1.939383, 3.010709 },
+    { 3.140987, 1.850879, 2.982526 },   { 3.004767, 1.932147, 3.021340 },
+    { 1.747635, 1.880408, 0.182510 },   { 1.711340, 1.800658, 0.230703 },
+    { 1.811848, 1.928874, 0.241905 },   { 0.357312, 1.249510, 0.396746 },
+    { 0.434813, 1.308092, 0.420447 },   { 0.370178, 1.159100, 0.437496 },
+    { 1.162175, 1.003437, 0.132788 },   { 1.128159, 0.910792, 0.148908 },
+    { 1.118065, 1.066691, 0.196452 },   { 1.720024, 2.350807, 2.796146 },
+    { 1.816624, 2.332488, 2.777905 },   { 1.666584, 2.268039, 2.779012 },
+    { 1.819804, 2.881982, 1.499877 },   { 1.801703, 2.787241, 1.473482 },
+    { 1.853012, 2.884553, 1.594167 },   { 1.641302, 0.985935, 1.700102 },
+    { 1.578200, 1.054423, 1.736539 },   { 1.590234, 0.903900, 1.674370 },
+    { 1.407558, 2.105852, 0.622137 },   { 1.439669, 2.037089, 0.687256 },
+    { 1.376226, 2.186770, 0.671844 },   { 0.742039, 2.449685, 1.807591 },
+    { 0.664816, 2.508741, 1.784159 },   { 0.750599, 2.443898, 1.907056 },
+    { 1.055383, 2.338027, 2.575278 },   { 0.980287, 2.357568, 2.512201 },
+    { 1.137644, 2.315308, 2.523153 },   { 0.383084, 1.881983, 0.997049 },
+    { 0.439661, 1.963523, 1.009305 },   { 0.342804, 1.882808, 0.905524 },
+    { 1.624121, 1.460397, 0.585692 },   { 1.610918, 1.374878, 0.635814 },
+    { 1.536897, 1.490047, 0.546797 },   { 3.073791, 2.284139, 1.733887 },
+    { 3.159350, 2.238214, 1.710000 },   { 3.006778, 2.216259, 1.763913 },
+    { 2.276815, 2.539086, 2.980504 },   { 2.243982, 2.631874, 2.998179 },
+    { 2.215148, 2.473147, 3.023507 },   { 0.287152, 0.812484, 2.829435 },
+    { 0.364868, 0.874562, 2.839761 },   { 0.320299, 0.721354, 2.805011 },
+    { 2.947925, 2.745224, 1.175244 },   { 2.937136, 2.657265, 1.221578 },
+    { 2.932797, 2.819397, 1.240584 },   { 2.432782, 0.445198, 2.008265 },
+    { 2.528164, 0.421749, 2.027040 },   { 2.426765, 0.541315, 1.981331 },
+    { 2.135631, 2.846586, 1.674159 },   { 2.112821, 2.924476, 1.615739 },
+    { 2.142838, 2.877115, 1.769111 },   { 1.992697, 1.542239, 1.123538 },
+    { 1.994674, 1.495622, 1.035090 },   { 2.085254, 1.571023, 1.148125 },
+    { 3.007302, 1.002020, 1.263169 },   { 3.005399, 1.045505, 1.173139 },
+    { 3.102123, 0.991726, 1.293219 },   { 3.042117, 1.830594, 2.657699 },
+    { 3.091667, 1.816789, 2.571943 },   { 2.974763, 1.757611, 2.669402 },
+    { 1.465297, 1.281842, 2.950841 },   { 1.411305, 1.229250, 3.016560 },
+    { 1.408774, 1.303931, 2.871359 },   { 2.612090, 0.891093, 1.547955 },
+    { 2.695821, 0.887163, 1.493423 },   { 2.616686, 0.823002, 1.621047 },
+    { 0.812006, 2.707695, 1.208838 },   { 0.735158, 2.770254, 1.195395 },
+    { 0.808964, 2.635761, 1.139439 },   { 0.318130, 2.780547, 0.516632 },
+    { 0.265551, 2.753112, 0.436116 },   { 0.291856, 2.723669, 0.594571 },
+    { 1.554664, 1.776211, 0.557025 },   { 1.585392, 1.801147, 0.648862 },
+    { 1.479411, 1.836105, 0.529645 },   { 1.313118, 1.325875, 1.106502 },
+    { 1.406876, 1.305721, 1.134843 },   { 1.255307, 1.336157, 1.187448 },
+    { 0.563695, 1.495837, 0.858004 },   { 0.647750, 1.477497, 0.807031 },
+    { 0.516021, 1.409745, 0.875762 },   { 0.189938, 2.083492, 2.220667 },
+    { 0.179488, 2.169607, 2.170918 },   { 0.228533, 2.014250, 2.159708 },
+    { 1.219512, 1.960655, 0.459886 },   { 1.126082, 1.995943, 0.454832 },
+    { 1.269108, 2.007934, 0.532721 },   { 2.373176, 2.965076, 2.821447 },
+    { 2.364244, 2.875424, 2.778057 },   { 2.284076, 3.010455, 2.822850 },
+    { 0.688102, 2.932363, 2.635048 },   { 0.749140, 3.002946, 2.599097 },
+    { 0.736401, 2.878551, 2.704123 },   { 3.043730, 1.315443, 0.326532 },
+    { 3.042042, 1.258753, 0.408893 },   { 2.996829, 1.267695, 0.252232 },
+    { 1.964500, 2.004276, 2.335286 },   { 1.904133, 1.953017, 2.274227 },
+    { 1.911536, 2.040095, 2.412175 },   { 0.450798, 1.904655, 2.606229 },
+    { 0.475742, 1.954537, 2.523226 },   { 0.376490, 1.952627, 2.652888 },
+    { 2.112788, 1.128108, 0.683552 },   { 2.130045, 1.121204, 0.781809 },
+    { 2.060726, 1.048319, 0.653164 },   { 0.770271, 0.564206, 0.707533 },
+    { 0.849217, 0.624301, 0.695042 },   { 0.701429, 0.584713, 0.637961 },
+    { 1.907848, 1.027415, 1.090399 },   { 1.926364, 1.073308, 1.177296 },
+    { 1.854230, 1.087899, 1.031520 },   { 2.006840, 0.879452, 0.615825 },
+    { 1.950347, 0.812869, 0.567089 },   { 2.048772, 0.835710, 0.695376 },
+    { 1.044237, 2.729692, 2.739900 },   { 0.957432, 2.739064, 2.788657 },
+    { 1.087096, 2.642769, 2.764545 },   { 2.024470, 0.868120, 2.448789 },
+    { 1.977539, 0.792888, 2.402554 },   { 2.038713, 0.844474, 2.544904 },
+    { 2.346306, 0.847486, 1.854141 },   { 2.428596, 0.887883, 1.894097 },
+    { 2.273015, 0.846975, 1.922172 },   { 2.451466, 1.066926, 0.052602 },
+    { 2.454208, 1.089762, 0.149921 },   { 2.461859, 1.150367, -0.001524 },
+    { 1.586928, 0.171622, 1.225782 },   { 1.643190, 0.092301, 1.249084 },
+    { 1.585595, 0.235339, 1.302842 },   { 2.158334, 2.595841, 2.438041 },
+    { 2.126742, 2.586688, 2.532477 },   { 2.120427, 2.521555, 2.382862 },
+    { 0.268769, 2.384647, 1.524956 },   { 0.178764, 2.396688, 1.483075 },
+    { 0.264809, 2.310261, 1.591672 },   { 2.964697, 0.609294, 2.578384 },
+    { 3.014589, 0.539796, 2.526607 },   { 2.991249, 0.604578, 2.674679 },
+    { 2.804129, 2.035185, 2.658896 },   { 2.831947, 2.130885, 2.667122 },
+    { 2.859608, 1.990245, 2.588878 },   { 2.269440, 0.004664, 2.015026 },
+    { 2.292795, 0.086003, 1.961748 },   { 2.266981, 0.028052, 2.112221 },
+    { 0.978350, 1.814540, 1.887908 },   { 1.016409, 1.901488, 1.919396 },
+    { 0.900506, 1.832099, 1.827642 },   { 1.043390, 1.175341, 1.906444 },
+    { 1.099585, 1.238650, 1.959679 },   { 0.975010, 1.133260, 1.966055 },
+    { 2.016893, 0.274093, 2.004319 },   { 2.071563, 0.340099, 2.055840 },
+    { 2.024768, 0.293409, 1.906518 },   { 0.200210, 1.231970, 1.834713 },
+    { 0.114839, 1.184264, 1.855594 },   { 0.188948, 1.285302, 1.750875 },
+    { 2.729975, 1.523368, 1.494243 },   { 2.786119, 1.553298, 1.571393 },
+    { 2.644035, 1.574497, 1.493741 },   { 0.654823, 2.344026, 0.041906 },
+    { 0.585582, 2.332462, -0.029311 },  { 0.740286, 2.375393, 0.000528 },
+    { 3.021982, 2.593565, 2.621302 },   { 2.989764, 2.499790, 2.634267 },
+    { 3.017212, 2.642770, 2.708228 },   { 2.487491, 2.587543, 0.588152 },
+    { 2.540926, 2.508060, 0.559391 },   { 2.491371, 2.596063, 0.687713 },
+    { 0.858188, 2.209867, 1.713786 },   { 0.822392, 2.294693, 1.752813 },
+    { 0.919970, 2.231431, 1.638169 },   { 2.596792, 1.253523, 0.653627 },
+    { 2.639966, 1.236539, 0.565041 },   { 2.592499, 1.168156, 0.705531 },
+    { 1.557444, 1.249902, 0.754581 },   { 1.590664, 1.171004, 0.702893 },
+    { 1.458703, 1.241710, 0.768116 },   { 0.474497, 2.224022, 0.619046 },
+    { 0.446543, 2.260459, 0.707877 },   { 0.480869, 2.124369, 0.624402 },
+    { 0.761041, 1.590794, 2.432924 },   { 0.791242, 1.685785, 2.440954 },
+    { 0.663172, 1.588543, 2.412513 },   { 0.836284, 1.482214, 2.190881 },
+    { 0.850232, 1.384275, 2.205489 },   { 0.787473, 1.520964, 2.269085 },
+    { 2.454650, 0.391488, 2.726185 },   { 2.367849, 0.341863, 2.724489 },
+    { 2.440428, 0.482185, 2.765830 },   { 2.354491, 0.123748, 1.086280 },
+    { 2.373650, 0.056337, 1.014945 },   { 2.440302, 0.152735, 1.128663 },
+    { 2.992918, 1.513766, 2.387696 },   { 2.906224, 1.543682, 2.347829 },
+    { 2.987283, 1.518885, 2.487406 },   { 1.227082, 2.282968, 2.976912 },
+    { 1.164560, 2.261041, 3.051813 },   { 1.316651, 2.307128, 3.014247 },
+    { 1.518608, 0.153309, 1.856854 },   { 1.447277, 0.199556, 1.909514 },
+    { 1.606102, 0.199449, 1.871548 },   { 2.587957, 0.288841, 2.468338 },
+    { 2.578525, 0.296796, 2.567574 },   { 2.606517, 0.193603, 2.444146 },
+    { 0.144688, 1.415405, 1.610001 },   { 0.207284, 1.450562, 1.540389 },
+    { 0.085255, 1.489291, 1.641759 },   { 2.748672, 1.594337, 2.271072 },
+    { 2.673511, 1.622680, 2.330631 },   { 2.718247, 1.596183, 2.175830 },
+    { 0.410163, 0.619010, 2.435827 },   { 0.345484, 0.681869, 2.392637 },
+    { 0.503603, 0.646896, 2.413665 },   { 0.534691, 0.343823, 1.325729 },
+    { 0.632028, 0.338918, 1.348121 },   { 0.500601, 0.435795, 1.345198 },
+    { 2.461049, 0.820135, 0.965838 },   { 2.536165, 0.820911, 1.031846 },
+    { 2.421632, 0.728344, 0.961273 },   { 0.531212, 2.220300, 0.236689 },
+    { 0.502904, 2.296693, 0.294679 },   { 0.574186, 2.255658, 0.153605 },
+    { 1.655506, 2.833838, 2.317040 },   { 1.668580, 2.830497, 2.416125 },
+    { 1.602969, 2.915483, 2.293083 },   { 1.288110, 2.656172, 2.952423 },
+    { 1.277223, 2.634069, 2.855505 },   { 1.368445, 2.608857, 2.988584 },
+    { 2.348524, 2.179906, 2.595979 },   { 2.372646, 2.102011, 2.653862 },
+    { 2.259984, 2.216541, 2.624589 },   { 1.695517, 0.551448, 0.805462 },
+    { 1.699774, 0.609314, 0.724017 },   { 1.704642, 0.455524, 0.778714 },
+    { 2.106596, 1.916632, 1.923212 },   { 2.155546, 1.961234, 1.998142 },
+    { 2.032239, 1.861051, 1.960386 },   { 2.447050, 2.038526, 1.664823 },
+    { 2.350761, 2.065299, 1.668234 },   { 2.484148, 2.058980, 1.574239 },
+    { 2.449344, 1.569436, 1.950703 },   { 2.437787, 1.493630, 1.886517 },
+    { 2.546473, 1.580996, 1.971496 },   { 2.112171, 0.408380, 1.794677 },
+    { 2.158942, 0.496119, 1.805372 },   { 2.078496, 0.399767, 1.700912 },
+    { 2.692511, 0.417154, 0.512240 },   { 2.679476, 0.365314, 0.427725 },
+    { 2.621964, 0.487674, 0.519306 },   { 0.435488, 2.780530, 1.586002 },
+    { 0.420283, 2.877527, 1.567019 },   { 0.435612, 2.729368, 1.500080 },
+    { 1.115003, 1.717126, 0.837236 },   { 1.203147, 1.677245, 0.811938 },
+    { 1.068973, 1.751023, 0.755185 },   { 1.856343, 0.616824, 2.343989 },
+    { 1.796548, 0.622851, 2.264063 },   { 1.904818, 0.529366, 2.342835 },
+    { 2.604397, 0.261200, 2.905175 },   { 2.528620, 0.244919, 2.968362 },
+    { 2.569499, 0.303156, 2.821379 },   { 2.983071, 1.836011, 0.408545 },
+    { 2.941255, 1.922312, 0.380198 },   { 2.911503, 1.771495, 0.435298 },
+    { 0.284846, 1.004775, 0.159867 },   { 0.340818, 1.016814, 0.241856 },
+    { 0.297717, 1.083015, 0.098934 },   { 0.192126, 0.793038, 0.011385 },
+    { 0.230558, 0.870973, 0.060874 },   { 0.241567, 0.780506, -0.074630 },
+    { 2.563812, 1.647993, 2.692657 },   { 2.622485, 1.725166, 2.717190 },
+    { 2.503455, 1.674583, 2.617490 },   { 2.529260, 2.046808, 0.776094 },
+    { 2.471748, 2.053308, 0.857642 },   { 2.544468, 2.138278, 0.738651 },
+    { 1.521366, 2.552599, 3.064763 },   { 1.526632, 2.463583, 3.110023 },
+    { 1.534993, 2.625151, 3.132221 },   { 0.511749, 0.923074, 2.208753 },
+    { 0.447660, 0.998092, 2.192473 },   { 0.496390, 0.850876, 2.141288 },
+    { 2.117688, 1.781807, 0.783134 },   { 2.208797, 1.813138, 0.809922 },
+    { 2.055770, 1.788578, 0.861366 },   { 1.652780, 2.093001, 2.754505 },
+    { 1.554431, 2.085575, 2.771006 },   { 1.698800, 2.011709, 2.790196 },
+    { 1.737177, 1.179170, 2.904903 },   { 1.737840, 1.107564, 2.974704 },
+    { 1.642971, 1.207781, 2.887392 },   { 2.608930, 0.790629, 1.194068 },
+    { 2.528547, 0.828235, 1.240158 },   { 2.684912, 0.785017, 1.258839 },
+    { 1.698150, 0.265894, 0.685113 },   { 1.679547, 0.256862, 0.782952 },
+    { 1.680976, 0.178467, 0.639707 },   { 1.062092, 1.101304, 1.495332 },
+    { 1.047507, 1.009579, 1.532398 },   { 0.976651, 1.135380, 1.456105 },
+    { 2.481560, 2.004994, 0.118148 },   { 2.554321, 1.938467, 0.101419 },
+    { 2.482028, 2.031356, 0.214610 },   { 1.923369, 2.539567, 0.556425 },
+    { 1.838167, 2.585092, 0.530578 },   { 1.973136, 2.512668, 0.473965 },
+    { 2.710513, 1.598229, 2.008108 },   { 2.724888, 1.696756, 2.017366 },
+    { 2.797808, 1.553485, 1.988678 },   { 2.942739, 0.227199, 0.186899 },
+    { 3.020717, 0.170557, 0.160232 },   { 2.975626, 0.309638, 0.232967 },
+    { 1.274750, 0.014932, 2.831151 },   { 1.277539, -0.019282, 2.737227 },
+    { 1.311231, -0.054613, 2.893059 },  { 0.138548, 2.393702, 0.543185 },
+    { 0.060579, 2.445383, 0.507829 },   { 0.146433, 2.307200, 0.493635 },
+    { 0.194759, 3.076992, 1.308021 },   { 0.227728, 3.149223, 1.247228 },
+    { 0.123684, 3.024177, 1.261557 },   { 1.133502, 0.030873, 0.173195 },
+    { 1.148695, -0.014224, 0.261146 },  { 1.211249, 0.090184, 0.152275 },
+    { 2.722824, 2.558867, 2.789907 },   { 2.702571, 2.600501, 2.701270 },
+    { 2.696187, 2.462493, 2.788332 },   { 1.307419, 2.352714, 2.242314 },
+    { 1.405996, 2.369378, 2.240105 },   { 1.287675, 2.277863, 2.305619 },
+    { 2.808640, 0.090952, 2.094578 },   { 2.837316, 0.064602, 2.002473 },
+    { 2.873024, 0.053768, 2.161452 },   { 2.610557, 2.119816, 2.296930 },
+    { 2.516683, 2.088381, 2.311053 },   { 2.673361, 2.042581, 2.306444 },
+    { 2.067765, 0.663952, 0.224417 },   { 2.149722, 0.619553, 0.188201 },
+    { 2.062731, 0.649171, 0.323191 },   { 2.410587, 1.939136, 0.539274 },
+    { 2.329640, 1.983980, 0.501371 },   { 2.433478, 1.980572, 0.627359 },
+    { 0.266830, 2.589002, 0.694311 },   { 0.229373, 2.506770, 0.651476 },
+    { 0.213318, 2.611497, 0.775739 },   { 2.665478, 0.247924, 0.257674 },
+    { 2.758689, 0.235464, 0.223669 },   { 2.619919, 0.159088, 0.263375 },
+    { 1.391624, 1.635214, 0.824559 },   { 1.443687, 1.587036, 0.895045 },
+    { 1.373363, 1.573131, 0.748321 },   { 0.915468, 0.823852, 0.413096 },
+    { 0.943532, 0.769916, 0.492490 },   { 0.954097, 0.783595, 0.330107 },
+    { 2.422248, 2.909883, 1.103557 },   { 2.418386, 2.919563, 1.203012 },
+    { 2.330025, 2.917723, 1.065696 },   { 1.408309, 2.936097, 0.154872 },
+    { 1.501433, 2.955528, 0.185701 },   { 1.357769, 2.890516, 0.228139 },
+    { 1.601230, 0.015841, 0.607280 },   { 1.583071, 0.049227, 0.514783 },
+    { 1.529356, 0.047873, 0.668990 },   { 2.387127, 2.249286, 0.604177 },
+    { 2.339117, 2.308859, 0.668568 },   { 2.320551, 2.194928, 0.553061 },
+    { 0.006317, 0.397187, 2.468096 },   { 0.086350, 0.369685, 2.521373 },
+    { -0.067122, 0.330651, 2.481501 },  { 2.414588, 0.649050, 2.818040 },
+    { 2.413447, 0.726390, 2.881421 },   { 2.379346, 0.678284, 2.729139 },
+    { 1.620984, 0.646636, 0.554546 },   { 1.555752, 0.571955, 0.541602 },
+    { 1.571288, 0.732489, 0.567183 },   { 1.388431, 1.496548, 2.555467 },
+    { 1.346007, 1.491565, 2.645885 },   { 1.459663, 1.566733, 2.555890 },
+    { 0.135346, 0.027221, 0.692454 },   { 0.110535, 0.085136, 0.614799 },
+    { 0.229755, -0.004019, 0.681917 },  { 0.356113, 1.759265, 2.006375 },
+    { 0.428524, 1.828103, 2.010617 },   { 0.357217, 1.715211, 1.916609 },
+    { 2.784840, 1.337893, 0.048133 },   { 2.833571, 1.258874, 0.085296 },
+    { 2.850191, 1.410781, 0.027721 },   { 2.732356, 2.248891, 1.483486 },
+    { 2.798089, 2.308929, 1.529034 },   { 2.645377, 2.251207, 1.532774 },
+    { 1.609259, 2.461869, 0.761605 },   { 1.515557, 2.495474, 0.771124 },
+    { 1.626281, 2.390800, 0.829865 },   { 3.034595, 2.376378, 1.030368 },
+    { 3.108631, 2.326860, 1.075828 },   { 3.072930, 2.450813, 0.975689 },
+    { 3.001063, 2.956280, 2.449605 },   { 2.932654, 2.883470, 2.445274 },
+    { 3.020729, 2.977897, 2.545239 },   { 0.973131, 1.398826, 3.027256 },
+    { 1.048479, 1.453925, 3.063128 },   { 0.886059, 1.441609, 3.051508 },
+    { 1.075531, 2.444457, 0.635589 },   { 1.139902, 2.479480, 0.703631 },
+    { 0.990821, 2.497460, 0.639456 },   { 1.866464, 1.096888, 1.554833 },
+    { 1.941039, 1.123887, 1.615740 },   { 1.789922, 1.062351, 1.609132 },
+    { 1.591678, 1.105215, 2.043568 },   { 1.653395, 1.145689, 1.976092 },
+    { 1.645280, 1.060640, 2.115260 },   { 2.619927, 0.227980, 1.174340 },
+    { 2.633047, 0.294780, 1.101090 },   { 2.665581, 0.142407, 1.149987 },
+    { 1.916667, 0.415190, 0.590627 },   { 1.825708, 0.378396, 0.609931 },
+    { 1.983344, 0.373288, 0.652258 },   { 1.200849, 1.841071, 1.389818 },
+    { 1.114525, 1.790768, 1.394038 },   { 1.256145, 1.819190, 1.470215 },
+    { 2.221784, 1.766977, 3.004150 },   { 2.225336, 1.794523, 3.100216 },
+    { 2.288916, 1.694712, 2.987690 },   { 1.120165, 3.008868, 2.322689 },
+    { 1.029789, 3.031427, 2.359063 },   { 1.133909, 2.909893, 2.326577 },
+    { 0.405986, 1.997015, 2.356493 },   { 0.366114, 1.907130, 2.338302 },
+    { 0.345189, 2.068190, 2.321310 },   { 2.326232, 0.162329, 0.556844 },
+    { 2.317494, 0.256924, 0.525610 },   { 2.412060, 0.151587, 0.607026 },
+    { 1.349474, 2.909042, 3.002070 },   { 1.354029, 2.915883, 3.101732 },
+    { 1.313151, 2.819469, 2.976431 },   { 1.677748, 2.890286, 2.981716 },
+    { 1.584536, 2.854118, 2.979874 },   { 1.739825, 2.820638, 3.017710 },
+    { 2.146479, 1.045232, 0.950271 },   { 2.062776, 1.035080, 1.004035 },
+    { 2.225747, 1.042585, 1.011178 },   { 0.038713, 0.203819, 0.461963 },
+    { 0.080467, 0.204316, 0.371099 },   { 0.022739, 0.297910, 0.491824 },
+    { 1.344154, 0.934719, 3.048288 },   { 1.298501, 0.975815, 3.127199 },
+    { 1.292224, 0.854988, 3.017525 },   { 1.876736, 0.670754, 0.506472 },
+    { 1.922832, 0.582605, 0.516712 },   { 1.777974, 0.657982, 0.515576 },
+    { 1.547024, 1.257100, 1.494394 },   { 1.462871, 1.279845, 1.445394 },
+    { 1.595454, 1.184510, 1.445555 },   { 0.837046, 1.662106, 0.882764 },
+    { 0.927946, 1.694579, 0.908893 },   { 0.770410, 1.690515, 0.951702 },
+    { 2.905724, 1.433934, 1.956678 },   { 2.901017, 1.350620, 1.901572 },
+    { 2.982411, 1.427385, 2.020522 },   { 1.082721, 1.574084, 1.538980 },
+    { 1.118780, 1.653944, 1.587168 },   { 1.041923, 1.603015, 1.452386 },
+    { 0.973517, 1.950813, 0.715403 },   { 0.983211, 2.013477, 0.638077 },
+    { 0.883009, 1.908370, 0.712770 },   { 2.047444, 1.846347, 2.788275 },
+    { 2.105859, 1.796484, 2.852317 },   { 2.098174, 1.864406, 2.704012 },
+    { 1.472286, 0.407694, 0.463299 },   { 1.403985, 0.383358, 0.532166 },
+    { 1.548848, 0.343549, 0.468160 },   { 1.523722, 2.012941, 1.058531 },
+    { 1.538378, 2.080087, 1.131171 },   { 1.463127, 1.940637, 1.091702 },
+    { 2.094622, 0.765399, 0.870291 },   { 2.123104, 0.847060, 0.920494 },
+    { 2.004127, 0.736782, 0.901781 },   { 1.360037, 1.461451, 1.615762 },
+    { 1.375715, 1.426828, 1.708258 },   { 1.267388, 1.498526, 1.609306 },
+    { 1.754323, 2.881247, 0.755875 },   { 1.740556, 2.861699, 0.852975 },
+    { 1.710649, 2.968212, 0.732860 },   { 2.597326, 2.339246, 0.463067 },
+    { 2.688913, 2.333021, 0.502729 },   { 2.530638, 2.300986, 0.527011 },
+    { 2.404012, 2.888762, 1.379749 },   { 2.377619, 2.981254, 1.407111 },
+    { 2.362718, 2.822465, 1.442195 },   { 2.682210, 0.752891, 0.246149 },
+    { 2.588032, 0.751670, 0.279748 },   { 2.719633, 0.845183, 0.255187 },
+    { 1.147989, 0.504369, 0.806579 },   { 1.188565, 0.445977, 0.736266 },
+    { 1.068069, 0.458769, 0.845737 },   { 0.882417, 2.859277, 0.450074 },
+    { 0.828696, 2.842740, 0.532782 },   { 0.820924, 2.879784, 0.373929 },
+    { 2.310841, 1.485608, 2.189112 },   { 2.390981, 1.454864, 2.240419 },
+    { 2.338739, 1.513314, 2.097166 },   { 0.048620, 1.439459, 2.985076 },
+    { 0.078551, 1.439376, 2.889661 },   { 0.058812, 1.347612, 3.023289 },
+    { 2.685128, 3.096664, 1.076209 },   { 2.634155, 3.017488, 1.109867 },
+    { 2.636042, 3.137695, 0.999352 },   { 2.955404, 1.555187, 2.648529 },
+    { 2.871594, 1.525579, 2.694345 },   { 3.034353, 1.532617, 2.705606 },
+    { 0.064303, 2.480490, 2.970512 },   { 0.081989, 2.494919, 3.067872 },
+    { -0.018484, 2.529915, 2.943987 },  { 2.482995, 0.044725, 0.867955 },
+    { 2.455494, -0.036642, 0.816739 },  { 2.538862, 0.103589, 0.809526 },
+    { 2.017835, 2.999311, 2.452181 },   { 2.056715, 2.908900, 2.434452 },
+    { 1.920387, 2.998675, 2.429744 },   { 0.869423, 2.167004, 3.082747 },
+    { 0.954671, 2.177259, 3.134008 },   { 0.864372, 2.237510, 3.012012 },
+    { 1.589117, 0.399666, 2.413510 },   { 1.674099, 0.378754, 2.461891 },
+    { 1.570287, 0.497660, 2.420041 },   { 0.440864, 0.611366, 1.385214 },
+    { 0.366708, 0.650546, 1.330755 },   { 0.527868, 0.625182, 1.337891 },
+    { 2.713192, 1.529373, 1.085268 },   { 2.693629, 1.627372, 1.088945 },
+    { 2.710758, 1.498439, 0.990204 },   { 2.066839, 1.149465, 2.402028 },
+    { 1.978751, 1.170214, 2.359483 },   { 2.063437, 1.058285, 2.442951 },
+    { 0.718140, 1.556839, 1.676953 },   { 0.794749, 1.522176, 1.731078 },
+    { 0.701074, 1.652454, 1.700755 },   { 1.851286, 1.748633, 1.205296 },
+    { 1.906319, 1.667628, 1.185058 },   { 1.903282, 1.810358, 1.264343 },
+    { 0.690798, 2.820113, 0.892873 },   { 0.624258, 2.764246, 0.843363 },
+    { 0.659978, 2.832952, 0.987135 },   { 0.700392, 2.971775, 0.643684 },
+    { 0.629248, 2.966450, 0.573611 },   { 0.667850, 2.928206, 0.727605 },
+    { 0.224944, 2.412756, 2.411288 },   { 0.179940, 2.480028, 2.352558 },
+    { 0.321229, 2.437351, 2.422438 },   { 0.931960, 1.088532, 1.085458 },
+    { 0.948257, 1.069581, 1.182284 },   { 0.943490, 1.186361, 1.068238 },
+    { 0.665315, 1.482798, 1.205282 },   { 0.623395, 1.392477, 1.196078 },
+    { 0.754802, 1.482720, 1.160649 },   { 2.090545, 2.389004, 2.282603 },
+    { 2.040173, 2.355573, 2.362259 },   { 2.025895, 2.417626, 2.211884 },
+    { 0.586392, 2.277950, 2.330457 },   { 0.544270, 2.318000, 2.249084 },
+    { 0.583852, 2.178214, 2.323650 },   { 0.397428, 2.598797, 1.378318 },
+    { 0.467494, 2.548450, 1.327762 },   { 0.332320, 2.534269, 1.418282 },
+    { 0.769337, 0.909264, 1.677104 },   { 0.851592, 0.904461, 1.733770 },
+    { 0.689510, 0.924296, 1.735428 },   { 2.809384, 0.362243, 1.792651 },
+    { 2.906450, 0.384960, 1.800543 },   { 2.770939, 0.407692, 1.712300 },
+    { 2.165909, 0.965344, 0.397730 },   { 2.089549, 0.933174, 0.453714 },
+    { 2.248087, 0.972319, 0.454283 },   { 3.029223, 1.963090, 2.255361 },
+    { 3.063521, 2.056860, 2.260912 },   { 3.032822, 1.931843, 2.160436 },
+    { 2.290786, 0.279773, 2.425314 },   { 2.272727, 0.248542, 2.518580 },
+    { 2.389205, 0.277242, 2.407783 },   { 2.887936, 0.900624, 2.678907 },
+    { 2.848443, 0.883810, 2.588588 },   { 2.939101, 0.986531, 2.677450 },
+    { 2.724532, 1.007735, 0.316482 },   { 2.638163, 1.056986, 0.305776 },
+    { 2.737244, 0.983237, 0.412598 },   { 2.046399, 1.071255, 1.952165 },
+    { 2.071263, 1.164937, 1.976772 },   { 2.077369, 1.008654, 2.023734 },
+    { 0.170790, 2.227231, 2.943018 },   { 0.126575, 2.314910, 2.961923 },
+    { 0.105750, 2.152970, 2.958991 },   { 1.361693, 2.771602, 1.982177 },
+    { 1.454761, 2.773405, 1.945637 },   { 1.342410, 2.858072, 2.028557 },
+    { 2.982726, 0.757058, 1.738590 },   { 3.078254, 0.782233, 1.723078 },
+    { 2.946703, 0.711262, 1.657319 },   { 2.418788, 0.252780, 1.386757 },
+    { 2.352642, 0.300740, 1.329098 },   { 2.499443, 0.229291, 1.332506 },
+    { 1.608286, 2.595250, 2.794006 },   { 1.645863, 2.503744, 2.779360 },
+    { 1.562406, 2.598627, 2.882796 },   { 1.508130, 0.999554, 2.425027 },
+    { 1.547857, 0.994937, 2.516681 },   { 1.431312, 1.063578, 2.425178 },
+    { 0.403062, 0.456581, 0.079987 },   { 0.443234, 0.406243, 0.003487 },
+    { 0.303384, 0.455265, 0.072083 },   { 2.772199, 1.911678, 2.353951 },
+    { 2.729955, 1.826940, 2.321781 },   { 2.870697, 1.908343, 2.337011 },
+    { 0.267947, 2.608428, 2.130318 },   { 0.325039, 2.689867, 2.140717 },
+    { 0.324975, 2.531033, 2.102786 },   { 0.271482, 2.897455, 1.935251 },
+    { 0.186640, 2.909345, 1.986831 },   { 0.341018, 2.856649, 1.994408 },
+    { 0.420612, 1.093519, 1.906373 },   { 0.340253, 1.144399, 1.875491 },
+    { 0.424876, 1.005758, 1.858624 },   { 0.317469, 2.961369, 0.908774 },
+    { 0.344220, 2.899485, 0.982631 },   { 0.236408, 3.013052, 0.936305 },
+    { 1.331593, 1.232370, 2.476098 },   { 1.246807, 1.247953, 2.425416 },
+    { 1.387810, 1.315023, 2.473236 },   { 1.653708, 2.990313, 0.221008 },
+    { 1.729954, 2.996775, 0.285388 },   { 1.680305, 3.031054, 0.133642 },
+    { 0.590628, 1.387522, 0.466964 },   { 0.630802, 1.407555, 0.556321 },
+    { 0.620713, 1.456152, 0.400747 },   { 2.905997, 0.221663, 2.459906 },
+    { 2.819336, 0.267701, 2.440662 },   { 2.896627, 0.166170, 2.542566 },
+    { 3.093628, 2.977978, 2.705256 },   { 3.167925, 3.044386, 2.696902 },
+    { 3.071771, 2.964390, 2.801887 },   { 2.165292, 1.291783, 2.041764 },
+    { 2.234435, 1.220242, 2.031709 },   { 2.185992, 1.347037, 2.122501 },
+    { 2.252015, 0.402395, 1.221932 },   { 2.285112, 0.496702, 1.225218 },
+    { 2.179675, 0.394827, 1.153306 },   { 2.104356, 0.137080, 1.189404 },
+    { 2.195319, 0.128762, 1.148705 },   { 2.079462, 0.050753, 1.233312 },
+    { 2.148437, 0.707200, 3.015197 },   { 2.198806, 0.754869, 3.087243 },
+    { 2.051558, 0.731376, 3.020663 },   { 1.850478, 1.598817, 3.013910 },
+    { 1.874091, 1.567314, 3.105834 },   { 1.850247, 1.521043, 2.951052 },
+    { 1.984243, 1.101537, 0.205597 },   { 2.056039, 1.050969, 0.253432 },
+    { 2.016996, 1.193515, 0.183984 },   { 0.819823, 2.184803, 2.627983 },
+    { 0.910067, 2.222214, 2.649346 },   { 0.818973, 2.151619, 2.533653 },
+    { 0.680880, 0.100328, 2.990090 },   { 0.748260, 0.091873, 2.916685 },
+    { 0.673872, 0.013431, 3.039078 },   { 2.347672, 1.066007, 2.070294 },
+    { 2.435627, 1.018893, 2.063654 },   { 2.327837, 1.086215, 2.166201 },
+    { 1.111822, 1.814359, 2.252410 },   { 1.077790, 1.776269, 2.338381 },
+    { 1.095407, 1.749261, 2.178296 },   { 2.873311, 1.875343, 0.918630 },
+    { 2.944104, 1.911954, 0.858231 },   { 2.784531, 1.880520, 0.872899 },
+    { 2.101428, 2.550539, 2.746625 },   { 2.078013, 2.646820, 2.760104 },
+    { 2.189270, 2.531164, 2.790308 },   { 2.827466, 0.083838, 2.684289 },
+    { 2.743385, 0.029836, 2.680525 },   { 2.855318, 0.095556, 2.779614 },
+    { 2.643409, 2.459431, 1.183392 },   { 2.631575, 2.376139, 1.129333 },
+    { 2.581379, 2.457376, 1.261802 },   { 0.210477, 1.616716, 0.215712 },
+    { 0.227327, 1.627602, 0.313679 },   { 0.191301, 1.520642, 0.195659 },
+    { 2.595851, 3.085082, 0.245145 },   { 2.533216, 3.108680, 0.170849 },
+    { 2.632559, 2.993318, 0.229920 },   { 0.948067, 1.831565, 0.021233 },
+    { 0.912134, 1.907261, 0.075814 },   { 0.938805, 1.746188, 0.072468 },
+    { 1.953344, 2.247947, 1.017599 },   { 1.909073, 2.312925, 0.955810 },
+    { 1.960324, 2.158811, 0.972811 },   { 1.222649, 2.766274, 0.625820 },
+    { 1.132619, 2.728772, 0.647918 },   { 1.292364, 2.720128, 0.680687 },
+    { 1.697084, 1.491664, 2.240765 },   { 1.608414, 1.453049, 2.215338 },
+    { 1.683516, 1.566071, 2.306182 },   { 1.267412, 1.311498, 2.741722 },
+    { 1.282870, 1.251771, 2.663022 },   { 1.169226, 1.326331, 2.753535 },
+    { 0.703434, 0.631065, 1.342521 },   { 0.757236, 0.648969, 1.424891 },
+    { 0.755720, 0.658720, 1.261889 },   { 0.536418, 0.871703, 1.821272 },
+    { 0.506921, 0.828597, 1.906547 },   { 0.501439, 0.819320, 1.743603 },
+    { 2.033586, 1.894688, 1.361921 },   { 2.053905, 1.813031, 1.415951 },
+    { 2.102318, 1.964908, 1.380502 },   { 1.353145, 0.749155, 2.700183 },
+    { 1.340584, 0.768015, 2.602784 },   { 1.423395, 0.809983, 2.737126 },
+    { 1.757646, 3.040187, 1.185496 },   { 1.805834, 2.953766, 1.199967 },
+    { 1.789142, 3.082058, 1.100321 },   { 2.692199, 2.835351, 0.825364 },
+    { 2.663609, 2.776601, 0.901068 },   { 2.611627, 2.876881, 0.783133 },
+    { 2.753498, 2.840923, 0.324355 },   { 2.736860, 2.782748, 0.244738 },
+    { 2.680602, 2.827267, 0.391435 },   { 0.196329, 2.621545, 2.569990 },
+    { 0.219355, 2.524588, 2.578307 },   { 0.108105, 2.638733, 2.613821 },
+    { 2.345007, 1.825788, 1.374116 },   { 2.272672, 1.793851, 1.435333 },
+    { 2.379912, 1.913637, 1.406738 },   { 0.596710, 0.509863, 2.871939 },
+    { 0.526640, 0.561688, 2.822905 },   { 0.556514, 0.426024, 2.908751 },
+    { 2.937274, 0.823236, 1.996167 },   { 2.937670, 0.813596, 1.896634 },
+    { 2.911096, 0.736251, 2.037979 },   { 0.579374, 1.320692, 0.011389 },
+    { 0.634136, 1.240102, 0.033891 },   { 0.484056, 1.293558, -0.001960 },
+    { 2.278243, 2.412097, 0.787639 },   { 2.368768, 2.449098, 0.808522 },
+    { 2.240378, 2.368551, 0.869309 },   { 0.391627, 1.711143, 0.025375 },
+    { 0.332230, 1.678111, 0.098730 },   { 0.390472, 1.645887, -0.050389 },
+    { 0.153753, 2.467941, 1.820145 },   { 0.065625, 2.429276, 1.792973 },
+    { 0.150971, 2.567416, 1.810297 },   { 2.843451, 0.422126, 2.906952 },
+    { 2.775335, 0.354829, 2.935786 },   { 2.799691, 0.511419, 2.896375 },
+    { 1.970457, 1.649264, 2.404565 },   { 2.013029, 1.699928, 2.329594 },
+    { 1.985128, 1.551262, 2.391131 },   { 1.342607, 1.643091, 2.334386 },
+    { 1.344545, 1.563513, 2.394914 },   { 1.248079, 1.673291, 2.322043 },
+    { 0.748739, 1.133236, 0.071356 },   { 0.842917, 1.127034, 0.038310 },
+    { 0.747734, 1.124634, 0.170981 },   { 1.378131, 1.410352, 1.877753 },
+    { 1.319542, 1.407747, 1.958750 },   { 1.424860, 1.322530, 1.867574 },
+    { 2.379939, 1.693036, 0.436722 },   { 2.413363, 1.778852, 0.475690 },
+    { 2.340060, 1.636823, 0.509177 },   { 0.560335, 1.301357, 2.831908 },
+    { 0.656147, 1.325504, 2.816512 },   { 0.537359, 1.315835, 2.928149 },
+    { 2.308699, 2.450235, 2.131759 },   { 2.320931, 2.549287, 2.125504 },
+    { 2.229118, 2.430013, 2.188837 },   { 1.398964, 0.129485, 0.101708 },
+    { 1.406581, 0.030972, 0.117107 },   { 1.430994, 0.151393, 0.009545 },
+    { 2.738661, 1.810796, 1.150217 },   { 2.785875, 1.826236, 1.063428 },
+    { 2.803245, 1.821952, 1.225745 },   { 1.834855, 1.243843, 2.347878 },
+    { 1.784425, 1.296402, 2.279364 },   { 1.812100, 1.277635, 2.439203 },
+    { 2.675870, 1.821318, 0.707845 },   { 2.624071, 1.899672, 0.742160 },
+    { 2.626939, 1.736877, 0.729653 },   { 2.769887, 2.202680, 2.007300 },
+    { 2.822160, 2.284730, 2.030436 },   { 2.675095, 2.214055, 2.037049 },
+    { 1.998308, 2.423041, 0.279446 },   { 1.928540, 2.367313, 0.234427 },
+    { 2.088992, 2.387066, 0.257485 },   { 2.612892, 0.331665, 0.870959 },
+    { 2.614312, 0.422865, 0.911954 },   { 2.693229, 0.320122, 0.812541 },
+    { 1.438447, 2.473494, 1.915666 },   { 1.459389, 2.449059, 2.010347 },
+    { 1.449352, 2.572151, 1.903508 },   { 0.869607, 0.994845, 2.380191 },
+    { 0.960289, 0.969060, 2.413537 },   { 0.816006, 0.912361, 2.362208 },
+    { 2.262026, 1.572083, 0.674023 },   { 2.207234, 1.647739, 0.709715 },
+    { 2.201615, 1.503071, 0.634175 },   { 0.029213, 2.539434, 2.193714 },
+    { 0.114704, 2.573874, 2.154917 },   { -0.041214, 2.610128, 2.187201 },
+    { 2.804363, 0.979878, 0.585511 },   { 2.878881, 1.037113, 0.551286 },
+    { 2.815526, 0.886603, 0.551230 },   { 1.145669, 0.191337, 1.809638 },
+    { 1.136214, 0.202391, 1.710702 },   { 1.055125, 0.181165, 1.850847 },
+    { 1.799933, 2.204886, 1.265283 },   { 1.721729, 2.267174, 1.263213 },
+    { 1.858550, 2.222334, 1.186166 },   { 2.768473, 1.887750, 2.008596 },
+    { 2.685808, 1.884735, 1.952404 },   { 2.781906, 1.980614, 2.043174 },
+    { 0.566042, 0.942291, 2.780672 },   { 0.542564, 1.003858, 2.705451 },
+    { 0.618895, 0.865111, 2.745320 },   { 1.440484, 2.308258, 0.440554 },
+    { 1.345608, 2.311138, 0.409085 },   { 1.451129, 2.234898, 0.507674 },
+    { 2.099174, 2.113051, 2.937382 },   { 2.064708, 2.019650, 2.927986 },
+    { 2.084658, 2.162707, 2.851804 },   { 0.640344, 1.990762, 1.382271 },
+    { 0.613247, 2.082472, 1.353029 },   { 0.567632, 1.925826, 1.359992 },
+    { 1.005877, 1.535225, 0.537525 },   { 1.087595, 1.584022, 0.506849 },
+    { 1.032672, 1.459432, 0.597003 },   { 1.001243, 2.787001, 1.696773 },
+    { 0.992437, 2.688644, 1.712534 },   { 0.910309, 2.828442, 1.693079 },
+    { 0.987802, 1.110389, 3.055721 },   { 1.065605, 1.081580, 3.111549 },
+    { 0.999141, 1.206273, 3.029689 },   { 0.559374, 1.591763, 1.450101 },
+    { 0.607457, 1.568462, 1.534631 },   { 0.618799, 1.573391, 1.371800 },
+    { 1.478474, 0.101704, 1.588815 },   { 1.486885, 0.115724, 1.687469 },
+    { 1.567854, 0.077935, 1.550783 },   { 1.197401, 2.030470, 2.891763 },
+    { 1.204490, 2.130070, 2.886325 },   { 1.101431, 2.004405, 2.902264 },
+    { 2.593369, 2.916082, 3.009380 },   { 2.501069, 2.936721, 2.976903 },
+    { 2.647562, 2.878580, 2.934170 },   { 1.874216, 0.910326, 1.799684 },
+    { 1.928068, 0.986814, 1.835033 },   { 1.784798, 0.943563, 1.769689 },
+    { 1.120926, 0.186051, 2.975514 },   { 1.169400, 0.130500, 2.907954 },
+    { 1.157365, 0.279169, 2.974435 },   { 1.743099, 0.329782, 2.034890 },
+    { 1.692066, 0.296574, 2.114217 },   { 1.836555, 0.294352, 2.038158 },
+    { 1.657390, 1.879589, 1.841187 },   { 1.717444, 1.958116, 1.826119 },
+    { 1.702586, 1.796398, 1.808989 },   { 1.300228, 2.270635, 0.841357 },
+    { 1.295869, 2.196579, 0.908414 },   { 1.228832, 2.337792, 0.861172 },
+    { 1.948942, 0.107632, 2.195505 },   { 1.962856, 0.009021, 2.186429 },
+    { 1.980313, 0.153514, 2.112374 },   { 0.592938, 2.832478, 1.152267 },
+    { 0.595325, 2.932350, 1.156722 },   { 0.497900, 2.801368, 1.152355 },
+    { 3.028118, 1.151776, 1.025723 },   { 3.094740, 1.221061, 1.053310 },
+    { 2.957975, 1.193980, 0.968288 },   { 1.833976, 0.982234, 0.013196 },
+    { 1.883561, 1.039306, 0.078648 },   { 1.885634, 0.976953, -0.072265 },
+    { 1.798332, 1.244152, 1.851843 },   { 1.884876, 1.234745, 1.802633 },
+    { 1.723244, 1.251207, 1.786177 },   { 2.873589, 1.201986, 1.817110 },
+    { 2.927328, 1.201648, 1.732778 },   { 2.906900, 1.130124, 1.878153 },
+    { 0.577414, 0.140788, 1.536999 },   { 0.611632, 0.171898, 1.625663 },
+    { 0.652564, 0.138931, 1.471052 },   { 0.479143, 3.039990, 0.297688 },
+    { 0.403226, 3.001126, 0.245475 },   { 0.521000, 2.968592, 0.353815 },
+    { 2.581915, 1.009475, 0.768424 },   { 2.672051, 0.990541, 0.729474 },
+    { 2.554187, 0.933238, 0.826898 },   { 2.285805, 1.798154, 0.177273 },
+    { 2.371693, 1.842169, 0.151083 },   { 2.293527, 1.762829, 0.270507 },
+    { 0.216147, 3.103959, 2.689932 },   { 0.206578, 3.176040, 2.621282 },
+    { 0.297853, 3.049780, 2.670213 },   { 0.686871, 1.890136, 2.775596 },
+    { 0.662080, 1.936036, 2.860911 },   { 0.605159, 1.878210, 2.719196 },
+    { 1.069007, 2.995313, 3.029030 },   { 1.073078, 3.066635, 3.099005 },
+    { 1.160823, 2.978560, 2.993125 },   { 2.694476, 1.837937, 0.085521 },
+    { 2.644472, 1.755175, 0.111019 },   { 2.791382, 1.826384, 0.107334 },
+    { 1.563852, 0.853418, 2.911395 },   { 1.602637, 0.770872, 2.952407 },
+    { 1.488071, 0.886218, 2.967798 },   { 0.460347, 2.801415, 2.155280 },
+    { 0.542405, 2.813267, 2.099369 },   { 0.486605, 2.790770, 2.251182 },
+    { 1.067116, 0.828584, 1.118327 },   { 1.017197, 0.895557, 1.063347 },
+    { 1.147577, 0.797335, 1.067835 },   { 2.248599, 1.379134, 0.873790 },
+    { 2.293116, 1.393911, 0.785473 },   { 2.293247, 1.303346, 0.921359 },
+    { 0.023727, 1.461944, 2.107485 },   { 0.039466, 1.452465, 2.205783 },
+    { 0.056943, 1.551156, 2.076858 },   { 0.890338, 1.285968, 0.873042 },
+    { 0.805316, 1.245775, 0.839044 },   { 0.933413, 1.339340, 0.800269 },
+    { 2.648945, 1.437668, 0.287452 },   { 2.699407, 1.401270, 0.209165 },
+    { 2.675734, 1.388361, 0.370224 },   { 0.390508, 3.056364, 2.334304 },
+    { 0.374435, 3.116324, 2.255905 },   { 0.317691, 2.988021, 2.339484 },
+    { 2.941599, 2.289971, 2.678662 },   { 3.039241, 2.269131, 2.673033 },
+    { 2.912219, 2.286466, 2.774185 },   { 1.199353, 2.918159, 1.497088 },
+    { 1.269711, 2.966859, 1.548839 },   { 1.118937, 2.904431, 1.554922 },
+    { 2.948856, 0.470312, 1.096935 },   { 2.985099, 0.380752, 1.071136 },
+    { 3.024561, 0.531828, 1.118947 },   { 2.489230, 1.274888, 2.457981 },
+    { 2.575071, 1.260972, 2.408608 },   { 2.489590, 1.220024, 2.541587 },
+    { 1.644334, 1.202727, 0.331251 },   { 1.631770, 1.123611, 0.271394 },
+    { 1.635932, 1.174006, 0.426668 },   { 0.669309, 2.003121, 2.315451 },
+    { 0.730467, 1.954706, 2.378027 },   { 0.574977, 1.974480, 2.332215 },
+    { 2.284317, 1.482399, 1.602583 },   { 2.203780, 1.434499, 1.567663 },
+    { 2.260654, 1.530045, 1.687259 },   { 0.527517, 2.701255, 2.395590 },
+    { 0.572355, 2.743476, 2.474374 },   { 0.501162, 2.607549, 2.418497 },
+    { 2.691990, 1.782137, 1.668582 },   { 2.622890, 1.751930, 1.602910 },
+    { 2.728574, 1.870738, 1.640094 },   { 1.221627, 1.403014, 2.134535 },
+    { 1.177639, 1.339731, 2.198256 },   { 1.151914, 1.458799, 2.089499 },
+    { 1.276239, 1.209683, 0.830184 },   { 1.274310, 1.109869, 0.824392 },
+    { 1.270617, 1.237626, 0.926036 },   { 0.816251, 2.909087, 3.046489 },
+    { 0.901923, 2.953749, 3.020687 },   { 0.771765, 2.961669, 3.118989 },
+    { 0.533906, 1.223529, 1.303916 },   { 0.569896, 1.156711, 1.238800 },
+    { 0.578958, 1.211478, 1.392376 },   { 1.683346, 2.144673, 2.139148 },
+    { 1.741238, 2.072530, 2.177146 },   { 1.613641, 2.103882, 2.080179 },
+    { 2.875850, 0.044009, 1.250278 },   { 2.911148, -0.036725, 1.297565 },
+    { 2.805482, 0.016229, 1.184882 },   { 1.784021, 0.926724, 0.326634 },
+    { 1.855064, 0.984494, 0.286440 },   { 1.826414, 0.857633, 0.385195 },
+    { 1.704426, 1.630143, 0.792940 },   { 1.672037, 1.613072, 0.699884 },
+    { 1.668404, 1.559794, 0.854205 },   { 0.766134, 2.421196, 2.059355 },
+    { 0.809315, 2.345348, 2.108166 },   { 0.752692, 2.497881, 2.122115 },
+    { 0.871077, 2.182524, 2.154052 },   { 0.810919, 2.153015, 2.228283 },
+    { 0.962738, 2.145297, 2.168630 },   { 1.865873, 1.349151, 0.032304 },
+    { 1.827492, 1.323421, -0.056380 },  { 1.964725, 1.334066, 0.031487 },
+    { 2.222735, 1.008374, 1.670202 },   { 2.286023, 1.057572, 1.610417 },
+    { 2.274593, 0.951644, 1.734173 },   { 1.293692, 2.083151, 1.808644 },
+    { 1.358039, 2.082571, 1.885189 },   { 1.324981, 2.019323, 1.738309 },
+    { 2.875127, 0.361920, 0.728630 },   { 2.927991, 0.446531, 0.735430 },
+    { 2.818555, 0.364492, 0.646210 },   { 0.560226, 1.918473, 2.025234 },
+    { 0.614082, 1.999690, 2.002796 },   { 0.606294, 1.866823, 2.097414 },
+    { 0.116601, 2.287058, 2.040205 },   { 0.135431, 2.341154, 1.958235 },
+    { 0.072215, 2.344766, 2.108759 },   { 1.731207, 1.627822, 1.774748 },
+    { 1.743176, 1.560404, 1.701867 },   { 1.758793, 1.587726, 1.862106 },
+    { 2.955634, 2.730964, 0.905002 },   { 2.874554, 2.771880, 0.863146 },
+    { 2.949333, 2.738789, 1.004496 },   { 1.501594, 2.091653, 1.371834 },
+    { 1.538751, 2.009640, 1.415345 },   { 1.538425, 2.172932, 1.416971 },
+    { 2.833318, 2.065374, 0.326849 },   { 2.735733, 2.046780, 0.338315 },
+    { 2.849821, 2.101427, 0.235046 },   { 1.050436, 1.606848, 2.061210 },
+    { 1.034856, 1.671504, 1.986532 },   { 0.963136, 1.567431, 2.089933 },
+    { 2.852918, 2.103729, 1.290104 },   { 2.826100, 2.168810, 1.361134 },
+    { 2.787516, 2.107990, 1.214576 },   { 2.693916, 0.326078, 2.033253 },
+    { 2.731292, 0.332854, 1.940748 },   { 2.723277, 0.240009, 2.074847 },
+    { 0.212220, 0.175849, 2.885870 },   { 0.212176, 0.100251, 2.820410 },
+    { 0.153710, 0.152683, 2.963587 },   { 1.629055, 0.636815, 1.189020 },
+    { 1.552820, 0.586142, 1.148765 },   { 1.622687, 0.632959, 1.288743 },
+    { 1.339579, 0.227037, 2.582705 },   { 1.429690, 0.183681, 2.583158 },
+    { 1.350244, 0.326169, 2.590398 },   { 0.877764, 3.073416, 2.428342 },
+    { 0.876731, 3.168795, 2.458370 },   { 0.830020, 3.065500, 2.340833 },
+    { 0.012970, 1.608793, 1.734893 },   { -0.086547, 1.600180, 1.730197 },
+    { 0.041121, 1.618498, 1.830357 },   { 2.089223, 2.356806, 3.093349 },
+    { 2.080570, 2.263474, 3.058502 },   { 2.007413, 2.409067, 3.069351 },
+    { 2.470659, 0.854329, 2.975580 },   { 2.544938, 0.892503, 2.920576 },
+    { 2.460200, 0.907998, 3.059307 },   { 2.511084, 0.660490, 1.700075 },
+    { 2.478100, 0.600190, 1.627439 },   { 2.435588, 0.717009, 1.733330 },
+    { 3.043270, 2.024359, 0.777765 },   { 3.006095, 2.041482, 0.686524 },
+    { 3.131200, 2.071035, 0.787233 },   { 2.406380, 2.833836, 2.437090 },
+    { 2.498902, 2.800394, 2.455016 },   { 2.410718, 2.912515, 2.375521 },
+    { 2.734020, 1.466940, 2.755877 },   { 2.700045, 1.377356, 2.784519 },
+    { 2.659075, 1.519847, 2.716075 },   { 0.855259, 2.409014, 2.948572 },
+    { 0.811121, 2.434356, 2.862493 },   { 0.919369, 2.480852, 2.975574 },
+    { 1.958775, 1.734897, 2.027947 },   { 1.918280, 1.644707, 2.012915 },
+    { 2.045688, 1.724766, 2.076356 },   { 2.982304, 2.766292, 2.104076 },
+    { 3.060064, 2.827760, 2.117309 },   { 2.923938, 2.801635, 2.030972 },
+    { 0.495257, 2.600207, 0.002865 },   { 0.574514, 2.584488, 0.061783 },
+    { 0.523212, 2.594076, -0.092952 },  { 0.620427, 1.316333, 1.773186 },
+    { 0.550082, 1.344964, 1.838239 },   { 0.654246, 1.396176, 1.723373 },
+    { 1.851595, 2.477959, 3.036031 },   { 1.794908, 2.470350, 2.954003 },
+    { 1.842133, 2.569694, 3.074699 },   { 2.162743, 1.730778, 1.542545 },
+    { 2.114802, 1.643183, 1.547900 },   { 2.217777, 1.743755, 1.625025 },
+    { 2.529501, 2.719916, 1.696654 },   { 2.503803, 2.782004, 1.770713 },
+    { 2.570790, 2.637380, 1.735165 },   { 2.307523, 3.038538, 0.375298 },
+    { 2.404434, 3.016431, 0.364365 },   { 2.297896, 3.116554, 0.437110 },
+    { 2.247696, 2.677393, 1.478840 },   { 2.181267, 2.606604, 1.454839 },
+    { 2.208496, 2.737719, 1.548296 },   { 2.405993, 0.100579, 0.117782 },
+    { 2.319176, 0.078530, 0.162242 },   { 2.399747, 0.190952, 0.075429 },
+    { 2.845756, 2.478670, 2.035563 },   { 2.813879, 2.492950, 2.129265 },
+    { 2.929733, 2.530863, 2.020603 },   { 2.412509, 2.437882, 2.677793 },
+    { 2.409318, 2.340578, 2.654949 },   { 2.447902, 2.449183, 2.770635 },
+    { 2.743492, 0.946496, 2.446978 },   { 2.763913, 0.946953, 2.349087 },
+    { 2.644497, 0.941931, 2.460358 },   { 0.252801, 2.064296, 2.729654 },
+    { 0.205969, 2.116614, 2.658453 },   { 0.233709, 2.105034, 2.818962 },
+    { 3.063191, 1.922158, 1.214172 },   { 2.986959, 1.986712, 1.218799 },
+    { 3.100113, 1.907543, 1.305950 },   { 2.880231, 3.025379, 0.083855 },
+    { 2.795332, 3.002985, 0.131716 },   { 2.945707, 3.064380, 0.148600 },
+    { 2.779395, 1.653453, 2.946437 },   { 2.784444, 1.581315, 2.877367 },
+    { 2.816726, 1.619581, 3.032803 },   { 2.054265, 0.038926, 0.074381 },
+    { 2.065626, -0.041658, 0.132494 },  { 2.100034, 0.023588, -0.013197 },
+    { 2.355414, 2.720144, 2.698744 },   { 2.344450, 2.753016, 2.604939 },
+    { 2.357313, 2.620162, 2.699021 },   { 0.361217, 2.018670, 0.301548 },
+    { 0.303415, 2.006261, 0.220894 },   { 0.429552, 2.089293, 0.283039 },
+    { 0.332342, 0.090876, 2.109455 },   { 0.263449, 0.156985, 2.139177 },
+    { 0.390810, 0.132649, 2.039909 },   { 3.034524, 0.068387, 2.292232 },
+    { 2.993148, 0.137518, 2.351469 },   { 3.043613, -0.017534, 2.342580 },
+    { 0.237543, 0.583210, 1.838098 },   { 0.311681, 0.597860, 1.772608 },
+    { 0.276038, 0.559019, 1.927165 },   { 2.503589, 2.263025, 2.095976 },
+    { 2.420729, 2.315682, 2.114991 },   { 2.540302, 2.226586, 2.181559 },
+    { 2.262877, 2.698771, 2.033787 },   { 2.333674, 2.768746, 2.024233 },
+    { 2.248342, 2.653808, 1.945656 },   { 0.964111, 2.685023, 2.452276 },
+    { 0.990834, 2.666914, 2.546923 },   { 1.040843, 2.726435, 2.403315 },
+    { 0.794322, 2.734946, 2.845067 },   { 0.803437, 2.799933, 2.920523 },
+    { 0.710474, 2.681741, 2.856841 },   { 2.896233, 2.435937, 1.571472 },
+    { 2.949992, 2.393141, 1.644124 },   { 2.879950, 2.531950, 1.594195 },
+    { 0.707918, 0.439981, 1.637015 },   { 0.805572, 0.456561, 1.650751 },
+    { 0.687875, 0.438135, 1.539061 },   { 2.429562, 2.935991, 0.705919 },
+    { 2.331154, 2.931808, 0.723196 },   { 2.451272, 2.883147, 0.623845 },
+    { 0.890410, 0.316803, 3.083332 },   { 0.973293, 0.305226, 3.028592 },
+    { 0.829674, 0.238704, 3.068783 },   { 0.544859, 2.440687, 1.202416 },
+    { 0.638042, 2.463712, 1.174367 },   { 0.480239, 2.473483, 1.133506 },
+    { 2.821353, 0.977632, 2.194345 },   { 2.890551, 1.047090, 2.214022 },
+    { 2.854392, 0.917286, 2.121772 },   { 0.710748, 1.170876, 2.004677 },
+    { 0.643095, 1.116736, 1.954758 },   { 0.730699, 1.254545, 1.953672 },
+    { 1.323921, 1.479145, 0.621735 },   { 1.286777, 1.537103, 0.549201 },
+    { 1.253285, 1.415737, 0.653197 },   { 2.698551, 2.717451, 0.063376 },
+    { 2.639217, 2.637232, 0.056723 },   { 2.661341, 2.791201, 0.007017 },
+    { 0.840228, 1.225083, 2.251827 },   { 0.854567, 1.147753, 2.313588 },
+    { 0.765601, 1.203942, 2.188709 },   { 1.592481, 1.743693, 1.226737 },
+    { 1.565173, 1.689101, 1.305946 },   { 1.691904, 1.754410, 1.226317 },
+    { 1.258602, 2.759789, 1.297767 },   { 1.231534, 2.833672, 1.359482 },
+    { 1.236972, 2.671656, 1.339776 },   { 1.283405, 3.025043, 1.772125 },
+    { 1.383297, 3.021171, 1.774669 },   { 1.252723, 3.114943, 1.803376 },
+    { 2.541767, 1.632310, 0.149237 },   { 2.449338, 1.608351, 0.119526 },
+    { 2.572917, 1.566396, 0.217683 },   { 1.328386, 0.424321, 1.433167 },
+    { 1.399835, 0.355967, 1.418246 },   { 1.300864, 0.463486, 1.345368 },
+    { 0.236768, 0.950959, 2.590976 },   { 0.330331, 0.962132, 2.557492 },
+    { 0.238410, 0.905038, 2.679794 },   { 0.313513, 2.796867, 3.012214 },
+    { 0.323708, 2.795983, 2.912740 },   { 0.375929, 2.730056, 3.052716 },
+    { 0.112414, 2.245971, 0.123348 },   { 0.177492, 2.170582, 0.114330 },
+    { 0.162248, 2.331534, 0.137334 },   { 2.701271, 2.514977, 2.263225 },
+    { 2.611569, 2.555932, 2.279852 },   { 2.714556, 2.437557, 2.325108 },
+    { 1.770655, 0.024603, 2.384955 },   { 1.699905, 0.005690, 2.316861 },
+    { 1.847949, 0.070276, 2.340914 },   { 0.009882, 1.194905, 1.519823 },
+    { -0.077604, 1.217861, 1.477170 },  { 0.058135, 1.279012, 1.544271 },
+    { 2.362329, 1.189368, 1.500885 },   { 2.448073, 1.222733, 1.461709 },
+    { 2.288210, 1.252204, 1.477265 },   { 1.943393, 1.163287, 1.318490 },
+    { 1.904684, 1.254202, 1.303121 },   { 1.923693, 1.134053, 1.412070 },
+    { 0.095808, 0.472131, 0.518367 },   { 0.157246, 0.463382, 0.596782 },
+    { 0.143525, 0.518364, 0.443630 },   { 0.157283, 0.392476, 2.732502 },
+    { 0.071212, 0.434916, 2.760620 },   { 0.167245, 0.303987, 2.778005 },
+    { 2.308432, 0.518593, 0.204206 },   { 2.407431, 0.531838, 0.199337 },
+    { 2.285893, 0.425193, 0.176483 },   { 0.944487, 0.067090, 1.962743 },
+    { 0.979830, -0.021319, 1.932171 },  { 0.921300, 0.061935, 2.059881 },
+    { 0.672079, 1.749103, 0.099972 },   { 0.679855, 1.844222, 0.129836 },
+    { 0.575510, 1.724947, 0.090434 },   { 1.825839, 0.707532, 0.059138 },
+    { 1.820204, 0.806882, 0.049239 },   { 1.897469, 0.684609, 0.125044 },
+    { 2.985048, 1.441246, 0.798920 },   { 3.015628, 1.520955, 0.850991 },
+    { 2.984317, 1.463406, 0.701409 },   { 2.308448, 2.258307, 1.299501 },
+    { 2.290653, 2.254337, 1.201177 },   { 2.344603, 2.348475, 1.323219 }
+};
diff --git a/src/include/gromacs/nbnxm/benchmark/bench_setup.h b/src/include/gromacs/nbnxm/benchmark/bench_setup.h
new file mode 100644 (file)
index 0000000..d7b0f29
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief
+ * This file declares functions for setting up kernel benchmarks
+ *
+ * \author Berk Hess <hess@kth.se>
+ *
+ * \inlibraryapi
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXN_BENCH_SETUP_H
+#define GMX_NBNXN_BENCH_SETUP_H
+
+#include <string>
+
+#include "gromacs/utility/real.h"
+
+namespace Nbnxm
+{
+
+//! Enum for selecting the SIMD kernel type for benchmarks
+enum class BenchMarkKernels : int
+{
+    SimdAuto,
+    SimdNo,
+    Simd4XM,
+    Simd2XMM,
+    Count
+};
+
+//! Enum for selecting the combination rule for kernel benchmarks
+enum class BenchMarkCombRule : int
+{
+    RuleGeom,
+    RuleLB,
+    RuleNone,
+    Count
+};
+
+//! Enum for selecting coulomb type for kernel benchmarks
+enum class BenchMarkCoulomb : int
+{
+    Pme,
+    ReactionField,
+    Count
+};
+
+/*! \internal \brief
+ * The options for the kernel benchmarks
+ */
+struct KernelBenchOptions
+{
+    //! Whether to use a GPU, currently GPUs are not supported
+    bool useGpu = false;
+    //! The number of OpenMP threads to use
+    int numThreads = 1;
+    //! The SIMD type for the kernel
+    BenchMarkKernels nbnxmSimd = BenchMarkKernels::SimdAuto;
+    //! The LJ combination rule
+    BenchMarkCombRule ljCombinationRule = BenchMarkCombRule::RuleGeom;
+    //! Use i-cluster half-LJ optimization for clusters with <= half LJ
+    bool useHalfLJOptimization = false;
+    //! The pairlist and interaction cut-off
+    real pairlistCutoff = 1.0;
+    //! The Coulomb Ewald coefficient
+    real ewaldcoeff_q = 0;
+    //! Whether to compute energies (shift forces for virial are always computed on CPU)
+    bool computeVirialAndEnergy = false;
+    //! The Coulomb interaction function
+    BenchMarkCoulomb coulombType = BenchMarkCoulomb::Pme;
+    //! Whether to use tabulated PME grid correction instead of analytical, not applicable with simd=no
+    bool useTabulatedEwaldCorr = false;
+    //! Whether to run all combinations of Coulomb type, combination rule and SIMD
+    bool doAll = false;
+    //! Number of iterations to run before running each kernel benchmark, currently always 1
+    int numPreIterations = 1;
+    //! The number of iterations for each kernel
+    int numIterations = 100;
+    //! The number of (untimed) iterations to run at startup to warm up the CPU
+    int numWarmupIterations = 0;
+    //! Print cycles/pair instead of pairs/cycle
+    bool cyclesPerPair = false;
+    //! Report in micro seconds instead of cycles
+    bool reportTime = false;
+    //! Also report into a csv file
+    std::string outputFile;
+};
+
+/*! \brief
+ * Sets up and runs one or more Nbnxm kernel benchmarks
+ *
+ * The simulated system is a box of 1000 SPC/E water molecules scaled
+ * by the factor \p sizeFactor, which has to be a power of 2.
+ * One or more benchmarks are run, as specified by \p options.
+ * Benchmark settings and timings are printed to stdout.
+ *
+ * \param[in] sizeFactor How much should the system size be increased.
+ * \param[in] options How the benchmark will be run.
+ */
+void bench(int sizeFactor, const KernelBenchOptions& options);
+
+} // namespace Nbnxm
+
+#endif
diff --git a/src/include/gromacs/nbnxm/benchmark/bench_system.h b/src/include/gromacs/nbnxm/benchmark/bench_system.h
new file mode 100644 (file)
index 0000000..56b49a8
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*!\file
+ * \brief
+ * This file declares functions for setting up a benchmark system
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXN_BENCH_SYSTEM_H
+#define GMX_NBNXN_BENCH_SYSTEM_H
+
+#include <string>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/forcerec.h"
+#include "gromacs/utility/listoflists.h"
+#include "gromacs/utility/smalloc.h"
+
+namespace gmx
+{
+
+//! Description of the system used for benchmarking.
+struct BenchmarkSystem
+{
+    /*! \brief Constructor
+     *
+     * Generates a benchmark system of size \p multiplicationFactor
+     * times the base size by stacking cubic boxes of 1000 water molecules
+     * with 3000 atoms total.
+     *
+     * \param[in] multiplicationFactor  Should be a power of 2, is checked
+     * \param[in] outputFile            The name of the csv file to write benchmark results
+     */
+    BenchmarkSystem(int multiplicationFactor, const std::string& outputFile);
+
+    //! Number of different atom types in test system.
+    int numAtomTypes;
+    //! Storage for parameters for short range interactions.
+    std::vector<real> nonbondedParameters;
+    //! Storage for atom type parameters.
+    std::vector<int> atomTypes;
+    //! Storage for atom partial charges.
+    std::vector<real> charges;
+    //! Atom info where all atoms are marked to have Van der Waals interactions
+    std::vector<int64_t> atomInfoAllVdw;
+    //! Atom info where only oxygen atoms are marked to have Van der Waals interactions
+    std::vector<int64_t> atomInfoOxygenVdw;
+    //! Information about exclusions.
+    ListOfLists<int> excls;
+    //! Storage for atom positions.
+    std::vector<gmx::RVec> coordinates;
+    //! System simulation box.
+    matrix box;
+    //! Forcerec with only the entries used in the benchmark set
+    t_forcerec forceRec;
+    //! csv output file
+    FILE* csv;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/nbnxm/boundingboxes.h b/src/include/gromacs/nbnxm/boundingboxes.h
new file mode 100644 (file)
index 0000000..986dde3
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \file
+ * \internal
+ *
+ * \brief Declares constants and helper functions used when handling
+ * bounding boxes for clusters of particles.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_BOUNDINGBOXES_H
+#define GMX_NBNXM_BOUNDINGBOXES_H
+
+#include "gromacs/simd/simd.h"
+
+namespace Nbnxm
+{
+
+/*! \brief The number of bounds along one dimension of a bounding box */
+static constexpr int c_numBoundingBoxBounds1D = 2;
+
+} // namespace Nbnxm
+
+#ifndef DOXYGEN
+
+/*! \brief Bounding box calculations are (currently) always in single precision, so
+ * we only need to check for single precision support here.
+ * This uses less (cache-)memory and SIMD is faster, at least on x86.
+ */
+#    if GMX_SIMD4_HAVE_FLOAT
+#        define NBNXN_SEARCH_BB_SIMD4 1
+#    else
+#        define NBNXN_SEARCH_BB_SIMD4 0
+#    endif
+
+
+#    if NBNXN_SEARCH_BB_SIMD4
+/* Always use 4-wide SIMD for bounding box calculations */
+
+#        if !GMX_DOUBLE
+/* Single precision BBs + coordinates, we can also load coordinates with SIMD */
+#            define NBNXN_SEARCH_SIMD4_FLOAT_X_BB 1
+#        else
+#            define NBNXN_SEARCH_SIMD4_FLOAT_X_BB 0
+#        endif
+
+/* Store bounding boxes corners as quadruplets: xxxxyyyyzzzz
+ *
+ * The packed bounding box coordinate stride is always set to 4.
+ * With AVX we could use 8, but that turns out not to be faster.
+ */
+#        define NBNXN_BBXXXX 1
+
+//! The number of bounding boxes in a pack, also the size of a pack along one dimension
+static constexpr int c_packedBoundingBoxesDimSize = GMX_SIMD4_WIDTH;
+
+//! Total number of corners (floats) in a pack of bounding boxes
+static constexpr int c_packedBoundingBoxesSize =
+        c_packedBoundingBoxesDimSize * DIM * Nbnxm::c_numBoundingBoxBounds1D;
+
+//! Returns the starting index of the bounding box pack that contains the given cluster
+static constexpr inline int packedBoundingBoxesIndex(int clusterIndex)
+{
+    return (clusterIndex / c_packedBoundingBoxesDimSize) * c_packedBoundingBoxesSize;
+}
+
+#    else /* NBNXN_SEARCH_BB_SIMD4 */
+
+#        define NBNXN_SEARCH_SIMD4_FLOAT_X_BB 0
+#        define NBNXN_BBXXXX 0
+
+#    endif /* NBNXN_SEARCH_BB_SIMD4 */
+
+#endif // !DOXYGEN
+
+#endif
diff --git a/src/include/gromacs/nbnxm/clusterdistancekerneltype.h b/src/include/gromacs/nbnxm/clusterdistancekerneltype.h
new file mode 100644 (file)
index 0000000..80da088
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the ClusterDistanceKernelType enum
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_CLUSTERDISTANCEKERNELTYPE_H
+#define GMX_NBNXM_CLUSTERDISTANCEKERNELTYPE_H
+
+#include "gromacs/nbnxm/atomdata.h"
+#include "gromacs/simd/simd.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "pairlistparams.h"
+
+//! The types of kernel for calculating the distance between pairs of atom clusters
+enum class ClusterDistanceKernelType : int
+{
+    CpuPlainC,    //!< Plain-C for CPU list
+    CpuSimd_4xM,  //!< SIMD for CPU list for j-cluster size matching the SIMD width
+    CpuSimd_2xMM, //!< SIMD for CPU list for j-cluster size matching half the SIMD width
+    Gpu           //!< For GPU list, can be either plain-C or SIMD
+};
+
+//! Return the cluster distance kernel type given the pairlist type and atomdata
+static inline ClusterDistanceKernelType getClusterDistanceKernelType(const PairlistType pairlistType,
+                                                                     const nbnxn_atomdata_t& atomdata)
+{
+    if (pairlistType == PairlistType::HierarchicalNxN)
+    {
+        return ClusterDistanceKernelType::Gpu;
+    }
+    else if (atomdata.XFormat == nbatXYZ)
+    {
+        return ClusterDistanceKernelType::CpuPlainC;
+    }
+    else if (pairlistType == PairlistType::Simple4x2)
+    {
+#if GMX_SIMD && GMX_SIMD_REAL_WIDTH == 2
+        return ClusterDistanceKernelType::CpuSimd_4xM;
+#else
+        GMX_RELEASE_ASSERT(false, "Expect 2-wide SIMD with 4x2 list and nbat SIMD layout");
+#endif
+    }
+    else if (pairlistType == PairlistType::Simple4x4)
+    {
+#if GMX_SIMD && GMX_SIMD_REAL_WIDTH == 4
+        return ClusterDistanceKernelType::CpuSimd_4xM;
+#elif GMX_SIMD && GMX_SIMD_REAL_WIDTH == 8
+        return ClusterDistanceKernelType::CpuSimd_2xMM;
+#else
+        GMX_RELEASE_ASSERT(false, "Expect 4-wide or 8-wide SIMD with 4x4 list and nbat SIMD layout");
+#endif
+    }
+    else
+    {
+        GMX_ASSERT(pairlistType == PairlistType::Simple4x8, "Unhandled pairlist type");
+#if GMX_SIMD && GMX_SIMD_REAL_WIDTH == 8
+        return ClusterDistanceKernelType::CpuSimd_4xM;
+#elif GMX_SIMD && GMX_SIMD_REAL_WIDTH == 16
+        return ClusterDistanceKernelType::CpuSimd_2xMM;
+#else
+        GMX_RELEASE_ASSERT(false,
+                           "Expect 8-wide or 16-wide SIMD with 4x4 list and nbat SIMD layout");
+#endif
+    }
+
+    GMX_RELEASE_ASSERT(false, "We should have returned before getting here");
+    return ClusterDistanceKernelType::CpuPlainC;
+}
+
+#endif
diff --git a/src/include/gromacs/nbnxm/cuda/nbnxm_cuda.h b/src/include/gromacs/nbnxm/cuda/nbnxm_cuda.h
new file mode 100644 (file)
index 0000000..f222cdd
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares nbnxn cuda cache and texture helper functions
+ *
+ * \ingroup module_nbnxm
+ */
+#ifndef GMX_NBNXN_CUDA_NBNXN_CUDA_H
+#define GMX_NBNXN_CUDA_NBNXN_CUDA_H
+
+namespace Nbnxm
+{
+
+//! Set up the cache configuration for the non-bonded kernels.
+void cuda_set_cacheconfig();
+
+} // namespace Nbnxm
+
+#endif
diff --git a/src/include/gromacs/nbnxm/cuda/nbnxm_cuda_kernels.cuh b/src/include/gromacs/nbnxm/cuda/nbnxm_cuda_kernels.cuh
new file mode 100644 (file)
index 0000000..6bee7c3
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#include "gromacs/gpu_utils/cuda_arch_utils.cuh"
+
+/*! \internal \file
+ *  This header has the sole purpose of generating kernels for the combinations of
+ *  supported electrostatics types (cut-off, reaction-field, analytical and
+ *  tabulated Ewald) and VDW types (cut-off + V shift, LJ-Ewald with
+ *  geometric or Lorentz-Berthelot combination rule, F switch, V switch).
+ *
+ *  The Ewald kernels have twin-range cut-off versions with rcoul != rvdw which
+ *  require an extra distance check to enable  PP-PME load balancing
+ *  (otherwise, by default rcoul == rvdw).
+ *
+ *  NOTE: No include fence as it is meant to be included multiple times.
+ *
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \author Berk Hess <hess@kth.se>
+ *  \ingroup module_nbnxm
+ */
+
+/* Analytical plain cut-off electrostatics kernels
+ */
+#define EL_CUTOFF
+
+/* cut-off + V shift LJ */
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecCut_VdwLJ##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef NB_KERNEL_FUNC_NAME
+/* cut-off + V shift LJ w geometric combination rules */
+#define LJ_COMB_GEOM
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecCut_VdwLJCombGeom##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_COMB_GEOM
+#undef NB_KERNEL_FUNC_NAME
+/* cut-off + V shift LJ w LB combination rules */
+#define LJ_COMB_LB
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecCut_VdwLJCombLB##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_COMB_LB
+#undef NB_KERNEL_FUNC_NAME
+/* LJ-Ewald w geometric combination rules */
+#define LJ_EWALD_COMB_GEOM
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecCut_VdwLJEwCombGeom##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_EWALD_COMB_GEOM
+#undef NB_KERNEL_FUNC_NAME
+/* LJ-Ewald w LB combination rules */
+#define LJ_EWALD_COMB_LB
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecCut_VdwLJEwCombLB##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_EWALD_COMB_LB
+#undef NB_KERNEL_FUNC_NAME
+/* F switch LJ */
+#define LJ_FORCE_SWITCH
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecCut_VdwLJFsw##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_FORCE_SWITCH
+#undef NB_KERNEL_FUNC_NAME
+/* V switch LJ */
+#define LJ_POT_SWITCH
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecCut_VdwLJPsw##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_POT_SWITCH
+#undef NB_KERNEL_FUNC_NAME
+
+#undef EL_CUTOFF
+
+
+/* Analytical reaction-field kernels
+ */
+#define EL_RF
+
+/* cut-off + V shift LJ */
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecRF_VdwLJ##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef NB_KERNEL_FUNC_NAME
+/* cut-off + V shift LJ w geometric combination rules */
+#define LJ_COMB_GEOM
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecRF_VdwLJCombGeom##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_COMB_GEOM
+#undef NB_KERNEL_FUNC_NAME
+/* cut-off + V shift LJ w LB combination rules */
+#define LJ_COMB_LB
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecRF_VdwLJCombLB##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_COMB_LB
+#undef NB_KERNEL_FUNC_NAME
+/* LJ-Ewald w geometric combination rules */
+#define LJ_EWALD_COMB_GEOM
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecRF_VdwLJEwCombGeom##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_EWALD_COMB_GEOM
+#undef NB_KERNEL_FUNC_NAME
+/* LJ-Ewald w LB combination rules */
+#define LJ_EWALD_COMB_LB
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecRF_VdwLJEwCombLB##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_EWALD_COMB_LB
+#undef NB_KERNEL_FUNC_NAME
+/* F switch LJ */
+#define LJ_FORCE_SWITCH
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecRF_VdwLJFsw##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_FORCE_SWITCH
+#undef NB_KERNEL_FUNC_NAME
+/* V switch LJ */
+#define LJ_POT_SWITCH
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecRF_VdwLJPsw##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_POT_SWITCH
+#undef NB_KERNEL_FUNC_NAME
+
+#undef EL_RF
+
+
+/* Analytical Ewald interaction kernels
+ */
+#define EL_EWALD_ANA
+
+/* cut-off + V shift LJ */
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEw_VdwLJ##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef NB_KERNEL_FUNC_NAME
+/* cut-off + V shift LJ w geometric combination rules */
+#define LJ_COMB_GEOM
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEw_VdwLJCombGeom##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_COMB_GEOM
+#undef NB_KERNEL_FUNC_NAME
+/* cut-off + V shift LJ w LB combination rules */
+#define LJ_COMB_LB
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEw_VdwLJCombLB##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_COMB_LB
+#undef NB_KERNEL_FUNC_NAME
+/* LJ-Ewald w geometric combination rules */
+#define LJ_EWALD_COMB_GEOM
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEw_VdwLJEwCombGeom##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_EWALD_COMB_GEOM
+#undef NB_KERNEL_FUNC_NAME
+/* LJ-Ewald w LB combination rules */
+#define LJ_EWALD_COMB_LB
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEw_VdwLJEwCombLB##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_EWALD_COMB_LB
+#undef NB_KERNEL_FUNC_NAME
+/* F switch LJ */
+#define LJ_FORCE_SWITCH
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEw_VdwLJFsw##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_FORCE_SWITCH
+#undef NB_KERNEL_FUNC_NAME
+/* V switch LJ */
+#define LJ_POT_SWITCH
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEw_VdwLJPsw##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_POT_SWITCH
+#undef NB_KERNEL_FUNC_NAME
+
+#undef EL_EWALD_ANA
+
+
+/* Analytical Ewald interaction kernels with twin-range cut-off
+ */
+#define EL_EWALD_ANA
+#define VDW_CUTOFF_CHECK
+
+/* cut-off + V shift LJ */
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwTwinCut_VdwLJ##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef NB_KERNEL_FUNC_NAME
+/* cut-off + V shift LJ w geometric combination rules */
+#define LJ_COMB_GEOM
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwTwinCut_VdwLJCombGeom##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_COMB_GEOM
+#undef NB_KERNEL_FUNC_NAME
+/* cut-off + V shift LJ w LB combination rules */
+#define LJ_COMB_LB
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwTwinCut_VdwLJCombLB##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_COMB_LB
+#undef NB_KERNEL_FUNC_NAME
+/* LJ-Ewald w geometric combination rules */
+#define LJ_EWALD_COMB_GEOM
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwTwinCut_VdwLJEwCombGeom##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_EWALD_COMB_GEOM
+#undef NB_KERNEL_FUNC_NAME
+/* LJ-Ewald w LB combination rules */
+#define LJ_EWALD_COMB_LB
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwTwinCut_VdwLJEwCombLB##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_EWALD_COMB_LB
+#undef NB_KERNEL_FUNC_NAME
+/* F switch LJ */
+#define LJ_FORCE_SWITCH
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwTwinCut_VdwLJFsw##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_FORCE_SWITCH
+#undef NB_KERNEL_FUNC_NAME
+/* V switch LJ */
+#define LJ_POT_SWITCH
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwTwinCut_VdwLJPsw##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_POT_SWITCH
+#undef NB_KERNEL_FUNC_NAME
+
+#undef EL_EWALD_ANA
+#undef VDW_CUTOFF_CHECK
+
+
+/* Tabulated Ewald interaction kernels */
+#define EL_EWALD_TAB
+
+/* cut-off + V shift LJ */
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTab_VdwLJ##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef NB_KERNEL_FUNC_NAME
+/* cut-off + V shift LJ w geometric combination rules */
+#define LJ_COMB_GEOM
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTab_VdwLJCombGeom##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_COMB_GEOM
+#undef NB_KERNEL_FUNC_NAME
+/* cut-off + V shift LJ w LB combination rules */
+#define LJ_COMB_LB
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTab_VdwLJCombLB##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_COMB_LB
+#undef NB_KERNEL_FUNC_NAME
+/* LJ-Ewald w geometric combination rules */
+#define LJ_EWALD_COMB_GEOM
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTab_VdwLJEwCombGeom##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_EWALD_COMB_GEOM
+#undef NB_KERNEL_FUNC_NAME
+/* LJ-Ewald w LB combination rules */
+#define LJ_EWALD_COMB_LB
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTab_VdwLJEwCombLB##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_EWALD_COMB_LB
+#undef NB_KERNEL_FUNC_NAME
+/* F switch LJ */
+#define LJ_FORCE_SWITCH
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTab_VdwLJFsw##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_FORCE_SWITCH
+#undef NB_KERNEL_FUNC_NAME
+/* V switch LJ */
+#define LJ_POT_SWITCH
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTab_VdwLJPsw##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_POT_SWITCH
+#undef NB_KERNEL_FUNC_NAME
+
+#undef EL_EWALD_TAB
+
+
+/* Tabulated Ewald interaction kernels with twin-range cut-off */
+#define EL_EWALD_TAB
+#define VDW_CUTOFF_CHECK
+
+/* cut-off + V shift LJ */
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTabTwinCut_VdwLJ##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef NB_KERNEL_FUNC_NAME
+/* cut-off + V shift LJ w geometric combination rules */
+#define LJ_COMB_GEOM
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTabTwinCut_VdwLJCombGeom##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_COMB_GEOM
+#undef NB_KERNEL_FUNC_NAME
+/* cut-off + V shift LJ w LB combination rules */
+#define LJ_COMB_LB
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTabTwinCut_VdwLJCombLB##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_COMB_LB
+#undef NB_KERNEL_FUNC_NAME
+/* LJ-Ewald w geometric combination rules */
+#define LJ_EWALD_COMB_GEOM
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTabTwinCut_VdwLJEwCombGeom##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_EWALD_COMB_GEOM
+#undef NB_KERNEL_FUNC_NAME
+/* LJ-Ewald w LB combination rules */
+#define LJ_EWALD_COMB_LB
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTabTwinCut_VdwLJEwCombLB##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_EWALD_COMB_LB
+#undef NB_KERNEL_FUNC_NAME
+/* F switch LJ */
+#define LJ_FORCE_SWITCH
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTabTwinCut_VdwLJFsw##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_FORCE_SWITCH
+#undef NB_KERNEL_FUNC_NAME
+/* V switch LJ */
+#define LJ_POT_SWITCH
+#define NB_KERNEL_FUNC_NAME(x, ...) x##_ElecEwQSTabTwinCut_VdwLJPsw##__VA_ARGS__
+#include "nbnxm_cuda_kernel.cuh"
+#undef LJ_POT_SWITCH
+#undef NB_KERNEL_FUNC_NAME
+
+#undef EL_EWALD_TAB
+#undef VDW_CUTOFF_CHECK
diff --git a/src/include/gromacs/nbnxm/cuda/nbnxm_cuda_types.h b/src/include/gromacs/nbnxm/cuda/nbnxm_cuda_types.h
new file mode 100644 (file)
index 0000000..2d818af
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2012, The GROMACS development team.
+ * Copyright (c) 2013-2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief
+ *  Data types used internally in the nbnxn_cuda module.
+ *
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \ingroup module_nbnxm
+ */
+
+#ifndef NBNXM_CUDA_TYPES_H
+#define NBNXM_CUDA_TYPES_H
+
+#include "gromacs/gpu_utils/cuda_arch_utils.cuh"
+#include "gromacs/gpu_utils/cudautils.cuh"
+#include "gromacs/gpu_utils/devicebuffer.h"
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gpueventsynchronizer.h"
+#include "gromacs/gpu_utils/gputraits.cuh"
+#include "gromacs/mdtypes/interaction_const.h"
+#include "gromacs/nbnxm/gpu_types_common.h"
+#include "gromacs/nbnxm/nbnxm.h"
+#include "gromacs/nbnxm/pairlist.h"
+#include "gromacs/timing/gpu_timing.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+/* TODO: consider moving this to kernel_utils */
+/* Convenience defines */
+/*! \brief cluster size = number of atoms per cluster. */
+static constexpr int c_clSize = c_nbnxnGpuClusterSize;
+
+/*! \internal
+ * \brief Main data structure for CUDA nonbonded force calculations.
+ */
+struct NbnxmGpu
+{
+    /*! \brief GPU device context.
+     *
+     * \todo Make it constant reference, once NbnxmGpu is a proper class.
+     */
+    const DeviceContext* deviceContext_;
+    /*! \brief true if doing both local/non-local NB work on GPU */
+    bool bUseTwoStreams = false;
+    //! true indicates that the nonlocal_done event was marked
+    bool bNonLocalStreamDoneMarked = false;
+
+    /*! \brief atom data */
+    NBAtomDataGpu* atdat = nullptr;
+    /*! \brief array of atom indices */
+    int* atomIndices = nullptr;
+    /*! \brief size of atom indices */
+    int atomIndicesSize = 0;
+    /*! \brief size of atom indices allocated in device buffer */
+    int atomIndicesSize_alloc = 0;
+    /*! \brief x buf ops num of atoms */
+    int* cxy_na = nullptr;
+    /*! \brief number of elements in cxy_na */
+    int ncxy_na = 0;
+    /*! \brief number of elements allocated allocated in device buffer */
+    int ncxy_na_alloc = 0;
+    /*! \brief x buf ops cell index mapping */
+    int* cxy_ind = nullptr;
+    /*! \brief number of elements in cxy_ind */
+    int ncxy_ind = 0;
+    /*! \brief number of elements allocated allocated in device buffer */
+    int ncxy_ind_alloc = 0;
+    /*! \brief parameters required for the non-bonded calc. */
+    NBParamGpu* nbparam = nullptr;
+    /*! \brief pair-list data structures (local and non-local) */
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, Nbnxm::gpu_plist*> plist = { { nullptr } };
+    /*! \brief staging area where fshift/energies get downloaded */
+    NBStagingData nbst;
+    /*! \brief local and non-local GPU streams */
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, const DeviceStream*> deviceStreams;
+
+    /*! \brief Event triggered when the non-local non-bonded
+     * kernel is done (and the local transfer can proceed) */
+    GpuEventSynchronizer nonlocal_done;
+    /*! \brief Event triggered when the tasks issued in the local
+     * stream that need to precede the non-local force or buffer
+     * operation calculations are done (e.g. f buffer 0-ing, local
+     * x/q H2D, buffer op initialization in local stream that is
+     * required also by nonlocal stream ) */
+    GpuEventSynchronizer misc_ops_and_local_H2D_done;
+
+    /*! \brief True if there is work for the current domain in the
+     * respective locality.
+     *
+     * This includes local/nonlocal GPU work, either bonded or
+     * nonbonded, scheduled to be executed in the current
+     * domain. As long as bonded work is not split up into
+     * local/nonlocal, if there is bonded GPU work, both flags
+     * will be true. */
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, bool> haveWork = { { false } };
+
+    /* NOTE: With current CUDA versions (<=5.0) timing doesn't work with multiple
+     * concurrent streams, so we won't time if both l/nl work is done on GPUs.
+     * Timer init/uninit is still done even with timing off so only the condition
+     * setting bDoTime needs to be change if this CUDA "feature" gets fixed. */
+    /*! \brief True if event-based timing is enabled. */
+    bool bDoTime = false;
+    /*! \brief CUDA event-based timers. */
+    Nbnxm::GpuTimers* timers = nullptr;
+    /*! \brief Timing data. TODO: deprecate this and query timers for accumulated data instead */
+    gmx_wallclock_gpu_nbnxn_t* timings = nullptr;
+};
+
+#endif /* NBNXN_CUDA_TYPES_H */
diff --git a/src/include/gromacs/nbnxm/freeenergydispatch.h b/src/include/gromacs/nbnxm/freeenergydispatch.h
new file mode 100644 (file)
index 0000000..dc09a72
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief
+ * Declares the free-energy kernel dispatch class
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+#ifndef GMX_NBNXM_FREEENERGYDISPATCH_H
+#define GMX_NBNXM_FREEENERGYDISPATCH_H
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/enerdata.h"
+#include "gromacs/mdtypes/threaded_force_buffer.h"
+#include "gromacs/utility/arrayref.h"
+
+struct gmx_enerdata_t;
+struct gmx_wallcycle;
+struct interaction_const_t;
+class PairlistSets;
+struct t_lambda;
+struct t_nrnb;
+
+namespace gmx
+{
+template<typename>
+class ArrayRefWithPadding;
+class ForceWithShiftForces;
+class StepWorkload;
+} // namespace gmx
+
+/*! \libinternal
+ *  \brief Temporary data and methods for handling dispatching of the nbnxm free-energy kernels
+ */
+class FreeEnergyDispatch
+{
+public:
+    //! Constructor
+    FreeEnergyDispatch(int numEnergyGroups);
+
+    //! Sets up the threaded force buffer and the reduction, should be called after constructing the pair lists
+    void setupFepThreadedForceBuffer(int numAtomsForce, const PairlistSets& pairlistSets);
+
+    //! Dispatches the non-bonded free-energy kernels, thread parallel and reduces the output
+    void dispatchFreeEnergyKernels(const PairlistSets&                       pairlistSets,
+                                   const gmx::ArrayRefWithPadding<const gmx::RVec>& coords,
+                                   gmx::ForceWithShiftForces*                forceWithShiftForces,
+                                   bool                                      useSimd,
+                                   int                                       ntype,
+                                   real                                      rlist,
+                                   const interaction_const_t&                ic,
+                                   gmx::ArrayRef<const gmx::RVec>            shiftvec,
+                                   gmx::ArrayRef<const real>                 nbfp,
+                                   gmx::ArrayRef<const real>                 nbfp_grid,
+                                   gmx::ArrayRef<const real>                 chargeA,
+                                   gmx::ArrayRef<const real>                 chargeB,
+                                   gmx::ArrayRef<const int>                  typeA,
+                                   gmx::ArrayRef<const int>                  typeB,
+                                   t_lambda*                                 fepvals,
+                                   gmx::ArrayRef<const real>                 lambda,
+                                   gmx_enerdata_t*                           enerd,
+                                   const gmx::StepWorkload&                  stepWork,
+                                   t_nrnb*                                   nrnb,
+                                   gmx_wallcycle*                            wcycle);
+
+private:
+    //! Temporary array for storing foreign lambda group pair energies
+    gmx_grppairener_t foreignGroupPairEnergies_;
+
+    //! Threaded force buffer for nonbonded FEP
+    gmx::ThreadedForceBuffer<gmx::RVec> threadedForceBuffer_;
+    //! Threaded buffer for nonbonded FEP foreign energies and dVdl, no forces, so numAtoms = 0
+    gmx::ThreadedForceBuffer<gmx::RVec> threadedForeignEnergyBuffer_;
+};
+
+#endif // GMX_NBNXN_FREEENERGYDISPATCH_H
diff --git a/src/include/gromacs/nbnxm/gpu_common.h b/src/include/gromacs/nbnxm/gpu_common.h
new file mode 100644 (file)
index 0000000..96e2e48
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Common functions for the different NBNXN GPU implementations.
+ *
+ * \author Szilard Pall <pall.szilard@gmail.com>
+ *
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_GPU_COMMON_H
+#define GMX_NBNXM_GPU_COMMON_H
+
+#include "config.h"
+
+#include <string>
+
+#if GMX_GPU_CUDA
+#    include "cuda/nbnxm_cuda_types.h"
+#endif
+
+#if GMX_GPU_OPENCL
+#    include "opencl/nbnxm_ocl_types.h"
+#endif
+
+#if GMX_GPU_SYCL
+#    include "sycl/nbnxm_sycl_types.h"
+#    include "gromacs/gpu_utils/syclutils.h"
+#endif
+
+#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/mdtypes/simulation_workload.h"
+#include "gromacs/nbnxm/nbnxm.h"
+#include "gromacs/pbcutil/ishift.h"
+#include "gromacs/timing/gpu_timing.h"
+#include "gromacs/timing/wallcycle.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "gpu_common_utils.h"
+#include "nbnxm_gpu.h"
+
+namespace gmx
+{
+class ListedForcesGpu;
+}
+
+namespace Nbnxm
+{
+
+/*! \brief Count pruning kernel time if either kernel has been triggered
+ *
+ *  We do the accounting for either of the two pruning kernel flavors:
+ *   - 1st pass prune: ran during the current step (prior to the force kernel);
+ *   - rolling prune:  ran at the end of the previous step (prior to the current step H2D xq);
+ *
+ * Note that the resetting of Nbnxm::GpuTimers::didPrune and Nbnxm::GpuTimers::didRollingPrune
+ * should happen after calling this function.
+ *
+ * \param[in] timers   structs with GPU timer objects
+ * \param[inout] timings  GPU task timing data
+ * \param[in] iloc        interaction locality
+ */
+static void countPruneKernelTime(Nbnxm::GpuTimers*          timers,
+                                 gmx_wallclock_gpu_nbnxn_t* timings,
+                                 const InteractionLocality  iloc)
+{
+    GpuTimers::Interaction& iTimers = timers->interaction[iloc];
+
+    // We might have not done any pruning (e.g. if we skipped with empty domains).
+    if (!iTimers.didPrune && !iTimers.didRollingPrune)
+    {
+        return;
+    }
+
+    if (iTimers.didPrune)
+    {
+        timings->pruneTime.c++;
+        timings->pruneTime.t += iTimers.prune_k.getLastRangeTime();
+    }
+
+    if (iTimers.didRollingPrune)
+    {
+        timings->dynamicPruneTime.c++;
+        timings->dynamicPruneTime.t += iTimers.rollingPrune_k.getLastRangeTime();
+    }
+}
+
+/*! \brief Reduce data staged internally in the nbnxn module.
+ *
+ * Shift forces and electrostatic/LJ energies copied from the GPU into
+ * a module-internal staging area are immediately reduced (CPU-side buffers passed)
+ * after having waited for the transfers' completion.
+ *
+ * Note that this function should always be called after the transfers into the
+ * staging buffers has completed.
+ *
+ * \param[in]  nbst           Nonbonded staging data
+ * \param[in]  iLocality      Interaction locality specifier
+ * \param[in]  reduceEnergies True if energy reduction should be done
+ * \param[in]  reduceFshift   True if shift force reduction should be done
+ * \param[out] e_lj           Variable to accumulate LJ energy into
+ * \param[out] e_el           Variable to accumulate electrostatic energy into
+ * \param[out] fshift         Pointer to the array of shift forces to accumulate into
+ */
+static inline void gpu_reduce_staged_outputs(const NBStagingData&      nbst,
+                                             const InteractionLocality iLocality,
+                                             const bool                reduceEnergies,
+                                             const bool                reduceFshift,
+                                             real*                     e_lj,
+                                             real*                     e_el,
+                                             rvec*                     fshift)
+{
+    /* add up energies and shift forces (only once at local F wait) */
+    if (iLocality == InteractionLocality::Local)
+    {
+        if (reduceEnergies)
+        {
+            *e_lj += *nbst.eLJ;
+            *e_el += *nbst.eElec;
+        }
+
+        if (reduceFshift)
+        {
+            for (int i = 0; i < gmx::c_numShiftVectors; i++)
+            {
+                rvec_inc(fshift[i], nbst.fShift[i]);
+            }
+        }
+    }
+}
+
+/*! \brief Do the per-step timing accounting of the nonbonded tasks.
+ *
+ *  Does timing accumulation and call-count increments for the nonbonded kernels.
+ *  Note that this function should be called after the current step's nonbonded
+ *  nonbonded tasks have completed with the exception of the rolling pruning kernels
+ *  that are accounted for during the following step.
+ *
+ * NOTE: if timing with multiple GPUs (streams) becomes possible, the
+ *      counters could end up being inconsistent due to not being incremented
+ *      on some of the node when this is skipped on empty local domains!
+ *
+ * \tparam     GpuPairlist       Pair list type
+ * \param[out] timings           Pointer to the NB GPU timings data
+ * \param[in]  timers            Pointer to GPU timers data
+ * \param[in]  plist             Pointer to the pair list data
+ * \param[in]  atomLocality      Atom locality specifier
+ * \param[in]  stepWork          Force schedule flags
+ * \param[in]  doTiming          True if timing is enabled.
+ *
+ */
+template<typename GpuPairlist>
+static inline void gpu_accumulate_timings(gmx_wallclock_gpu_nbnxn_t* timings,
+                                          Nbnxm::GpuTimers*          timers,
+                                          const GpuPairlist*         plist,
+                                          AtomLocality               atomLocality,
+                                          const gmx::StepWorkload&   stepWork,
+                                          bool                       doTiming)
+{
+    /* timing data accumulation */
+    if (!doTiming)
+    {
+        return;
+    }
+
+    /* determine interaction locality from atom locality */
+    const InteractionLocality iLocality        = atomToInteractionLocality(atomLocality);
+    const bool                didEnergyKernels = stepWork.computeEnergy;
+
+    /* only increase counter once (at local F wait) */
+    if (iLocality == InteractionLocality::Local)
+    {
+        timings->nb_c++;
+        timings->ktime[plist->haveFreshList ? 1 : 0][didEnergyKernels ? 1 : 0].c += 1;
+    }
+
+    /* kernel timings */
+    timings->ktime[plist->haveFreshList ? 1 : 0][didEnergyKernels ? 1 : 0].t +=
+            timers->interaction[iLocality].nb_k.getLastRangeTime();
+
+    /* X/q H2D and F D2H timings */
+    timings->nb_h2d_t += timers->xf[atomLocality].nb_h2d.getLastRangeTime();
+    timings->nb_d2h_t += timers->xf[atomLocality].nb_d2h.getLastRangeTime();
+
+    /* Count the pruning kernel times for both cases:1st pass (at search step)
+       and rolling pruning (if called at the previous step).
+       We do the accounting here as this is the only sync point where we
+       know (without checking or additional sync-ing) that prune tasks in
+       in the current stream have completed (having just blocking-waited
+       for the force D2H). */
+    countPruneKernelTime(timers, timings, iLocality);
+
+    /* only count atdat at pair-search steps (add only once, at local F wait) */
+    if (stepWork.doNeighborSearch && atomLocality == AtomLocality::Local)
+    {
+        /* atdat transfer timing */
+        timings->pl_h2d_c++;
+        timings->pl_h2d_t += timers->atdat.getLastRangeTime();
+    }
+
+    /* only count pair-list H2D when actually performed */
+    if (timers->interaction[iLocality].didPairlistH2D)
+    {
+        timings->pl_h2d_t += timers->interaction[iLocality].pl_h2d.getLastRangeTime();
+
+        /* Clear the timing flag for the next step */
+        timers->interaction[iLocality].didPairlistH2D = false;
+    }
+}
+
+/*! \brief Attempts to complete nonbonded GPU task.
+ *
+ * See documentation in nbnxm_gpu.h for details.
+ *
+ * \todo Move into shared source file, perhaps including
+ * cuda_runtime.h if needed for any remaining CUDA-specific
+ * objects.
+ */
+//NOLINTNEXTLINE(misc-definitions-in-headers)
+bool gpu_try_finish_task(NbnxmGpu*                nb,
+                         const gmx::StepWorkload& stepWork,
+                         const AtomLocality       aloc,
+                         real*                    e_lj,
+                         real*                    e_el,
+                         gmx::ArrayRef<gmx::RVec> shiftForces,
+                         GpuTaskCompletion        completionKind,
+                         gmx_wallcycle*           wcycle)
+{
+    GMX_ASSERT(nb, "Need a valid nbnxn_gpu object");
+
+    /* determine interaction locality from atom locality */
+    const InteractionLocality iLocality = atomToInteractionLocality(aloc);
+
+
+    // Transfers are launched and therefore need to be waited on if:
+    // - buffer ops is not offloaded
+    // - energies or virials are needed (on the local stream)
+    //
+    // (Note that useGpuFBufferOps and computeVirial are mutually exclusive
+    // in current code as virial steps do CPU reduction.)
+    const bool haveResultToWaitFor =
+            (!stepWork.useGpuFBufferOps
+             || (aloc == AtomLocality::Local && (stepWork.computeEnergy || stepWork.computeVirial)));
+
+    //  We skip when during the non-local phase there was actually no work to do.
+    //  This is consistent with nbnxn_gpu_launch_kernel but it also considers possible
+    //  bonded GPU work.
+    if ((iLocality == InteractionLocality::Local) || haveGpuShortRangeWork(nb, iLocality))
+    {
+        // Query the state of the GPU stream and return early if we're not done
+        if (completionKind == GpuTaskCompletion::Check)
+        {
+            // To get the wcycle call count right, when in GpuTaskCompletion::Check mode,
+            // we start without counting and only when the task finished we issue a
+            // start/stop to increment.
+            // GpuTaskCompletion::Wait mode the timing is expected to be done in the caller.
+            wallcycle_start_nocount(wcycle, WallCycleCounter::WaitGpuNbL);
+
+            if (!haveStreamTasksCompleted(*nb->deviceStreams[iLocality]))
+            {
+                wallcycle_stop(wcycle, WallCycleCounter::WaitGpuNbL);
+
+                // Early return to skip the steps below that we have to do only
+                // after the NB task completed
+                return false;
+            }
+
+            wallcycle_increment_event_count(wcycle, WallCycleCounter::WaitGpuNbL);
+        }
+        else if (haveResultToWaitFor)
+        {
+            nb->deviceStreams[iLocality]->synchronize();
+        }
+
+        // TODO: this needs to be moved later because conditional wait could brake timing
+        // with a future OpenCL implementation, but with CUDA timing is anyway disabled
+        // in all cases where we skip the wait.
+        gpu_accumulate_timings(nb->timings, nb->timers, nb->plist[iLocality], aloc, stepWork, nb->bDoTime);
+
+        if (stepWork.computeEnergy || stepWork.computeVirial)
+        {
+            gpu_reduce_staged_outputs(nb->nbst,
+                                      iLocality,
+                                      stepWork.computeEnergy,
+                                      stepWork.computeVirial,
+                                      e_lj,
+                                      e_el,
+                                      as_rvec_array(shiftForces.data()));
+        }
+    }
+
+    /* Reset both pruning flags. */
+    if (nb->bDoTime)
+    {
+        nb->timers->interaction[iLocality].didPrune =
+                nb->timers->interaction[iLocality].didRollingPrune = false;
+    }
+
+    /* Turn off initial list pruning (doesn't hurt if this is not pair-search step). */
+    nb->plist[iLocality]->haveFreshList = false;
+
+    return true;
+}
+
+/*! \brief
+ * Wait for the asynchronously launched nonbonded tasks and data
+ * transfers to finish.
+ *
+ * Also does timing accounting and reduction of the internal staging buffers.
+ * As this is called at the end of the step, it also resets the pair list and
+ * pruning flags.
+ *
+ * \param[in] nb The nonbonded data GPU structure
+ * \param[in]  stepWork     Force schedule flags
+ * \param[in] aloc Atom locality identifier
+ * \param[out] e_lj Pointer to the LJ energy output to accumulate into
+ * \param[out] e_el Pointer to the electrostatics energy output to accumulate into
+ * \param[out] shiftForces Shift forces buffer to accumulate into
+ * \param[out] wcycle Pointer to wallcycle data structure
+ * \return            The number of cycles the gpu wait took
+ */
+//NOLINTNEXTLINE(misc-definitions-in-headers) TODO: move into source file
+float gpu_wait_finish_task(NbnxmGpu*                nb,
+                           const gmx::StepWorkload& stepWork,
+                           AtomLocality             aloc,
+                           real*                    e_lj,
+                           real*                    e_el,
+                           gmx::ArrayRef<gmx::RVec> shiftForces,
+                           gmx_wallcycle*           wcycle)
+{
+    auto cycleCounter = (atomToInteractionLocality(aloc) == InteractionLocality::Local)
+                                ? WallCycleCounter::WaitGpuNbL
+                                : WallCycleCounter::WaitGpuNbNL;
+
+    wallcycle_start(wcycle, cycleCounter);
+    gpu_try_finish_task(nb, stepWork, aloc, e_lj, e_el, shiftForces, GpuTaskCompletion::Wait, wcycle);
+    float waitTime = wallcycle_stop(wcycle, cycleCounter);
+
+    return waitTime;
+}
+
+} // namespace Nbnxm
+
+#endif
diff --git a/src/include/gromacs/nbnxm/gpu_common_utils.h b/src/include/gromacs/nbnxm/gpu_common_utils.h
new file mode 100644 (file)
index 0000000..2ede311
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Implements common util routines for different NBNXN GPU implementations
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_GPU_COMMON_UTILS_H
+#define GMX_NBNXM_GPU_COMMON_UTILS_H
+
+#include "gromacs/listed_forces/listed_forces_gpu.h"
+#include "gromacs/mdtypes/locality.h"
+#include "gromacs/nbnxm/gpu_types_common.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/range.h"
+
+namespace Nbnxm
+{
+
+/*! \brief An early return condition for empty NB GPU workloads
+ *
+ * This is currently used for non-local kernels/transfers only.
+ * Skipping the local kernel is more complicated, since the
+ * local part of the force array also depends on the non-local kernel.
+ * The skip of the local kernel is taken care of separately.
+ */
+static inline bool canSkipNonbondedWork(const NbnxmGpu& nb, InteractionLocality iloc)
+{
+    assert(nb.plist[iloc]);
+    return (iloc == InteractionLocality::NonLocal && nb.plist[iloc]->nsci == 0);
+}
+
+/*! \brief Calculate atom range and return start index and length.
+ *
+ * \param[in] atomData Atom descriptor data structure
+ * \param[in] atomLocality Atom locality specifier
+ * \returns Range of indexes for selected locality.
+ */
+static inline gmx::Range<int> getGpuAtomRange(const NBAtomDataGpu* atomData, const AtomLocality atomLocality)
+{
+    assert(atomData);
+
+    /* calculate the atom data index range based on locality */
+    if (atomLocality == AtomLocality::Local)
+    {
+        return gmx::Range<int>(0, atomData->numAtomsLocal);
+    }
+    else if (atomLocality == AtomLocality::NonLocal)
+    {
+        return gmx::Range<int>(atomData->numAtomsLocal, atomData->numAtoms);
+    }
+    else
+    {
+        GMX_THROW(gmx::InconsistentInputError(
+                "Only Local and NonLocal atom locities can be used to get atom ranges in NBNXM."));
+    }
+}
+
+} // namespace Nbnxm
+
+#endif
diff --git a/src/include/gromacs/nbnxm/gpu_data_mgmt.h b/src/include/gromacs/nbnxm/gpu_data_mgmt.h
new file mode 100644 (file)
index 0000000..5d5e795
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declare interface for GPU data transfer for NBNXN module
+ *
+ *  \author Szilard Pall <pall.szilard@gmail.com>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \ingroup module_nbnxm
+ *  \inlibraryapi
+ */
+
+#ifndef GMX_NBNXN_GPU_DATA_MGMT_H
+#define GMX_NBNXN_GPU_DATA_MGMT_H
+
+#include <memory>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gpu_macros.h"
+#include "gromacs/mdtypes/locality.h"
+
+#include "nbnxm.h"
+
+struct NbnxmGpu;
+struct DeviceInformation;
+struct gmx_wallclock_gpu_nbnxn_t;
+struct nbnxn_atomdata_t;
+struct NbnxnPairlistGpu;
+struct PairlistParams;
+struct interaction_const_t;
+
+class DeviceStream;
+
+namespace gmx
+{
+class DeviceStreamManager;
+}
+
+namespace Nbnxm
+{
+
+/** Initializes the data structures related to GPU nonbonded calculations. */
+GPU_FUNC_QUALIFIER
+NbnxmGpu* gpu_init(const gmx::DeviceStreamManager gmx_unused& deviceStreamManager,
+                   const interaction_const_t gmx_unused* ic,
+                   const PairlistParams gmx_unused& listParams,
+                   const nbnxn_atomdata_t gmx_unused* nbat,
+                   /* true if both local and non-local are done on GPU */
+                   bool gmx_unused bLocalAndNonlocal) GPU_FUNC_TERM_WITH_RETURN(nullptr);
+
+/** Initializes pair-list data for GPU, called at every pair search step. */
+GPU_FUNC_QUALIFIER
+void gpu_init_pairlist(NbnxmGpu gmx_unused*          nb,
+                       const struct NbnxnPairlistGpu gmx_unused* h_nblist,
+                       gmx::InteractionLocality gmx_unused       iloc) GPU_FUNC_TERM;
+
+/** Initializes atom-data on the GPU, called at every pair search step. */
+GPU_FUNC_QUALIFIER
+void gpu_init_atomdata(NbnxmGpu gmx_unused* nb, const nbnxn_atomdata_t gmx_unused* nbat) GPU_FUNC_TERM;
+
+/*! \brief Re-generate the GPU Ewald force table, resets rlist, and update the
+ *  electrostatic type switching to twin cut-off (or back) if needed.
+ */
+GPU_FUNC_QUALIFIER
+void gpu_pme_loadbal_update_param(const struct nonbonded_verlet_t gmx_unused* nbv,
+                                  const interaction_const_t gmx_unused& ic) GPU_FUNC_TERM;
+
+/** Uploads shift vector to the GPU if the box is dynamic (otherwise just returns). */
+GPU_FUNC_QUALIFIER
+void gpu_upload_shiftvec(NbnxmGpu gmx_unused* nb, const nbnxn_atomdata_t gmx_unused* nbatom) GPU_FUNC_TERM;
+
+/** Clears GPU outputs: nonbonded force, shift force and energy. */
+GPU_FUNC_QUALIFIER
+void gpu_clear_outputs(NbnxmGpu gmx_unused* nb, bool gmx_unused computeVirial) GPU_FUNC_TERM;
+
+/** Frees all GPU resources used for the nonbonded calculations. */
+GPU_FUNC_QUALIFIER
+void gpu_free(NbnxmGpu gmx_unused* nb) GPU_FUNC_TERM;
+
+/** Returns the GPU timings structure or NULL if GPU is not used or timing is off. */
+GPU_FUNC_QUALIFIER
+struct gmx_wallclock_gpu_nbnxn_t* gpu_get_timings(NbnxmGpu gmx_unused* nb)
+        GPU_FUNC_TERM_WITH_RETURN(nullptr);
+
+/** Resets nonbonded GPU timings. */
+GPU_FUNC_QUALIFIER
+void gpu_reset_timings(struct nonbonded_verlet_t gmx_unused* nbv) GPU_FUNC_TERM;
+
+/** Calculates the minimum size of proximity lists to improve SM load balance
+ *  with GPU non-bonded kernels. */
+GPU_FUNC_QUALIFIER
+int gpu_min_ci_balanced(NbnxmGpu gmx_unused* nb) GPU_FUNC_TERM_WITH_RETURN(-1);
+
+/** Returns if analytical Ewald GPU kernels are used. */
+GPU_FUNC_QUALIFIER
+bool gpu_is_kernel_ewald_analytical(const NbnxmGpu gmx_unused* nb) GPU_FUNC_TERM_WITH_RETURN(FALSE);
+
+/** Returns an opaque pointer to the GPU coordinate+charge array
+ *  Note: CUDA only.
+ */
+CUDA_FUNC_QUALIFIER
+void* gpu_get_xq(NbnxmGpu gmx_unused* nb) CUDA_FUNC_TERM_WITH_RETURN(nullptr);
+
+/** Returns forces device buffer.
+ */
+GPU_FUNC_QUALIFIER
+DeviceBuffer<gmx::RVec> gpu_get_f(NbnxmGpu gmx_unused* nb)
+        GPU_FUNC_TERM_WITH_RETURN(DeviceBuffer<gmx::RVec>{});
+
+/** Returns an opaque pointer to the GPU shift force array
+ *  Note: CUDA only.
+ */
+CUDA_FUNC_QUALIFIER
+DeviceBuffer<gmx::RVec> gpu_get_fshift(NbnxmGpu gmx_unused* nb)
+        CUDA_FUNC_TERM_WITH_RETURN(DeviceBuffer<gmx::RVec>{});
+
+} // namespace Nbnxm
+
+#endif
diff --git a/src/include/gromacs/nbnxm/gpu_jit_support.h b/src/include/gromacs/nbnxm/gpu_jit_support.h
new file mode 100644 (file)
index 0000000..183fcad
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *  \brief Declares functions that support JIT compilation (e.g. for OpenCL)
+ *
+ *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_GPU_JIT_SUPPORT_H
+#define GMX_NBNXM_GPU_JIT_SUPPORT_H
+
+#include "gromacs/utility/basedefinitions.h"
+
+struct NbnxmGpu;
+
+/*! \brief Handles any JIT compilation of nbnxn kernels for the selected device */
+OPENCL_FUNC_QUALIFIER void nbnxn_gpu_compile_kernels(NbnxmGpu gmx_unused* nb) OPENCL_FUNC_TERM;
+
+#endif
diff --git a/src/include/gromacs/nbnxm/gpu_types_common.h b/src/include/gromacs/nbnxm/gpu_types_common.h
new file mode 100644 (file)
index 0000000..74e2a02
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Implements common internal types for different NBNXN GPU implementations
+ *
+ * \author Szilárd Páll <pall.szilard@gmail.com>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_MDLIB_NBNXN_GPU_COMMON_TYPES_H
+#define GMX_MDLIB_NBNXN_GPU_COMMON_TYPES_H
+
+#include "config.h"
+
+#include "gromacs/mdtypes/interaction_const.h"
+#include "gromacs/mdtypes/locality.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+#include "nbnxm.h"
+#include "pairlist.h"
+
+#if GMX_GPU_OPENCL
+#    include "gromacs/gpu_utils/gpuregiontimer_ocl.h"
+#endif
+
+#if GMX_GPU_CUDA
+#    include "gromacs/gpu_utils/gpuregiontimer.cuh"
+#endif
+
+#if GMX_GPU_SYCL
+#    include "gromacs/gpu_utils/gpuregiontimer_sycl.h"
+#endif
+
+/*! \brief Macro definining default for the prune kernel's j4 processing concurrency.
+ *
+ *  The GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY macro allows compile-time override with the default value of 4.
+ */
+#ifndef GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY
+#    define GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY 4
+#endif
+//! Default for the prune kernel's j4 processing concurrency.
+static constexpr int c_pruneKernelJ4Concurrency = GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY;
+
+/*! \internal
+ * \brief Staging area for temporary data downloaded from the GPU.
+ *
+ * Since SYCL buffers already have host-side storage, this is a bit redundant.
+ * But it allows prefetching of the data from GPU, and brings GPU backends closer together.
+ */
+struct NBStagingData
+{
+    //! LJ energy
+    float* eLJ = nullptr;
+    //! electrostatic energy
+    float* eElec = nullptr;
+    //! shift forces
+    Float3* fShift = nullptr;
+};
+
+/** \internal
+ * \brief Nonbonded atom data - both inputs and outputs.
+ */
+struct NBAtomDataGpu
+{
+    //! number of atoms
+    int numAtoms;
+    //! number of local atoms
+    int numAtomsLocal;
+    //! allocation size for the atom data (xq, f)
+    int numAtomsAlloc;
+
+    //! atom coordinates + charges, size \ref numAtoms
+    DeviceBuffer<Float4> xq;
+    //! force output array, size \ref numAtoms
+    DeviceBuffer<Float3> f;
+
+    //! LJ energy output, size 1
+    DeviceBuffer<float> eLJ;
+    //! Electrostatics energy input, size 1
+    DeviceBuffer<float> eElec;
+
+    //! shift forces
+    DeviceBuffer<Float3> fShift;
+
+    //! number of atom types
+    int numTypes;
+    //! atom type indices, size \ref numAtoms
+    DeviceBuffer<int> atomTypes;
+    //! sqrt(c6),sqrt(c12) size \ref numAtoms
+    DeviceBuffer<Float2> ljComb;
+
+    //! shifts
+    DeviceBuffer<Float3> shiftVec;
+    //! true if the shift vector has been uploaded
+    bool shiftVecUploaded;
+};
+
+/** \internal
+ * \brief Parameters required for the GPU nonbonded calculations.
+ */
+struct NBParamGpu
+{
+
+    //! type of electrostatics
+    enum Nbnxm::ElecType elecType;
+    //! type of VdW impl.
+    enum Nbnxm::VdwType vdwType;
+
+    //! charge multiplication factor
+    float epsfac;
+    //! Reaction-field/plain cutoff electrostatics const.
+    float c_rf;
+    //! Reaction-field electrostatics constant
+    float two_k_rf;
+    //! Ewald/PME parameter
+    float ewald_beta;
+    //! Ewald/PME correction term subtracted from the direct-space potential
+    float sh_ewald;
+    //! LJ-Ewald/PME correction term added to the correction potential
+    float sh_lj_ewald;
+    //! LJ-Ewald/PME coefficient
+    float ewaldcoeff_lj;
+
+    //! Coulomb cut-off squared
+    float rcoulomb_sq;
+
+    //! VdW cut-off squared
+    float rvdw_sq;
+    //! VdW switched cut-off
+    float rvdw_switch;
+    //! Full, outer pair-list cut-off squared
+    float rlistOuter_sq;
+    //! Inner, dynamic pruned pair-list cut-off squared
+    float rlistInner_sq;
+    //! True if we use dynamic pair-list pruning
+    bool useDynamicPruning;
+
+    //! VdW shift dispersion constants
+    shift_consts_t dispersion_shift;
+    //! VdW shift repulsion constants
+    shift_consts_t repulsion_shift;
+    //! VdW switch constants
+    switch_consts_t vdw_switch;
+
+    /* LJ non-bonded parameters - accessed through texture memory */
+    //! nonbonded parameter table with 6*C6/12*C12 pairs per atom type-pair, ntype^2 elements
+    DeviceBuffer<Float2> nbfp{};
+    //! texture object bound to nbfp
+    DeviceTexture nbfp_texobj;
+    //! nonbonded parameter table per atom type, ntype elements
+    DeviceBuffer<Float2> nbfp_comb{};
+    //! texture object bound to nbfp_comb
+    DeviceTexture nbfp_comb_texobj;
+
+    /* Ewald Coulomb force table data - accessed through texture memory */
+    //! table scale/spacing
+    float coulomb_tab_scale;
+    //! pointer to the table in the device memory
+    DeviceBuffer<float> coulomb_tab{};
+    //! texture object bound to coulomb_tab
+    DeviceTexture coulomb_tab_texobj;
+};
+
+namespace Nbnxm
+{
+
+using gmx::AtomLocality;
+using gmx::InteractionLocality;
+
+/*! \internal
+ * \brief GPU region timers used for timing GPU kernels and H2D/D2H transfers.
+ *
+ * The two-sized arrays hold the local and non-local values and should always
+ * be indexed with eintLocal/eintNonlocal.
+ */
+struct GpuTimers
+{
+    /*! \internal
+     * \brief Timers for local or non-local coordinate/force transfers
+     */
+    struct XFTransfers
+    {
+        //! timer for x/q H2D transfers (l/nl, every step)
+        GpuRegionTimer nb_h2d;
+        //! timer for f D2H transfer (l/nl, every step)
+        GpuRegionTimer nb_d2h;
+    };
+
+    /*! \internal
+     * \brief Timers for local or non-local interaction related operations
+     */
+    struct Interaction
+    {
+        //! timer for pair-list H2D transfers (l/nl, every PS step)
+        GpuRegionTimer pl_h2d;
+        //! true when a pair-list transfer has been done at this step
+        bool didPairlistH2D = false;
+        //! timer for non-bonded kernels (l/nl, every step)
+        GpuRegionTimer nb_k;
+        //! timer for the 1st pass list pruning kernel (l/nl, every PS step)
+        GpuRegionTimer prune_k;
+        //! true when we timed pruning and the timings need to be accounted for
+        bool didPrune = false;
+        //! timer for rolling pruning kernels (l/nl, frequency depends on chunk size)
+        GpuRegionTimer rollingPrune_k;
+        //! true when we timed rolling pruning (at the previous step) and the timings need to be accounted for
+        bool didRollingPrune = false;
+    };
+
+    //! timer for atom data transfer (every PS step)
+    GpuRegionTimer atdat;
+    //! timers for coordinate/force transfers (every step)
+    gmx::EnumerationArray<AtomLocality, XFTransfers> xf;
+    //! timers for interaction related transfers
+    gmx::EnumerationArray<InteractionLocality, Nbnxm::GpuTimers::Interaction> interaction;
+};
+
+/*! \internal
+ * \brief GPU pair list structure */
+struct gpu_plist
+{
+    //! number of atoms per cluster
+    int na_c;
+
+    //! size of sci, # of i clusters in the list
+    int nsci;
+    //! allocation size of sci
+    int sci_nalloc;
+    //! list of i-cluster ("super-clusters")
+    DeviceBuffer<nbnxn_sci_t> sci;
+
+    //! total # of 4*j clusters
+    int ncj4;
+    //! allocation size of cj4
+    int cj4_nalloc;
+    //! 4*j cluster list, contains j cluster number and index into the i cluster list
+    DeviceBuffer<nbnxn_cj4_t> cj4;
+    //! # of 4*j clusters * # of warps
+    int nimask;
+    //! allocation size of imask
+    int imask_nalloc;
+    //! imask for 2 warps for each 4*j cluster group
+    DeviceBuffer<unsigned int> imask;
+    //! atom interaction bits
+    DeviceBuffer<nbnxn_excl_t> excl;
+    //! count for excl
+    int nexcl;
+    //! allocation size of excl
+    int excl_nalloc;
+
+    /* parameter+variables for normal and rolling pruning */
+    //! true after search, indicates that initial pruning with outer pruning is needed
+    bool haveFreshList;
+    //! the number of parts/steps over which one cycle of rolling pruning takes places
+    int rollingPruningNumParts;
+    //! the next part to which the rolling pruning needs to be applied
+    int rollingPruningPart;
+};
+
+} // namespace Nbnxm
+
+#endif
diff --git a/src/include/gromacs/nbnxm/grid.h b/src/include/gromacs/nbnxm/grid.h
new file mode 100644 (file)
index 0000000..19e440f
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the Grid class.
+ *
+ * This class provides functionality for setting up and accessing atoms
+ * on a grid for one domain decomposition zone. This grid is used for
+ * generating cluster pair lists for computing non-bonded pair interactions.
+ * The grid consists of a regular array of columns along dimensions x and y.
+ * Along z the number of cells and their boundaries vary between the columns.
+ * Each cell can hold one or more clusters of atoms, depending on the grid
+ * geometry, which is set by the pair-list type.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_GRID_H
+#define GMX_NBNXM_GRID_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/range.h"
+
+struct nbnxn_atomdata_t;
+enum class PairlistType;
+
+namespace gmx
+{
+class UpdateGroupsCog;
+} // namespace gmx
+
+namespace Nbnxm
+{
+
+struct GridSetData;
+struct GridWork;
+
+/*! \internal
+ * \brief Bounding box for a nbnxm atom cluster
+ *
+ * \note Should be aligned in memory to enable 4-wide SIMD operations.
+ */
+struct BoundingBox
+{
+    /*! \internal
+     * \brief Corner for the bounding box, padded with one element to enable 4-wide SIMD operations
+     */
+    struct Corner
+    {
+        //! Returns a corner with the minimum coordinates along each dimension
+        static Corner min(const Corner& c1, const Corner& c2)
+        {
+            Corner cMin;
+
+            cMin.x = std::min(c1.x, c2.x);
+            cMin.y = std::min(c1.y, c2.y);
+            cMin.z = std::min(c1.z, c2.z);
+            /* This value of the padding is irrelevant, as long as it
+             * is initialized. We use min to allow auto-vectorization.
+             */
+            cMin.padding = std::min(c1.padding, c2.padding);
+
+            return cMin;
+        }
+
+        //! Returns a corner with the maximum coordinates along each dimension
+        static Corner max(const Corner& c1, const Corner& c2)
+        {
+            Corner cMax;
+
+            cMax.x       = std::max(c1.x, c2.x);
+            cMax.y       = std::max(c1.y, c2.y);
+            cMax.z       = std::max(c1.z, c2.z);
+            cMax.padding = std::max(c1.padding, c2.padding);
+
+            return cMax;
+        }
+
+        //! Returns a pointer for SIMD loading of a Corner object
+        const float* ptr() const { return &x; }
+
+        //! Returns a pointer for SIMD storing of a Corner object
+        float* ptr() { return &x; }
+
+        //! x coordinate
+        float x;
+        //! y coordinate
+        float y;
+        //! z coordinate
+        float z;
+        //! padding, unused, but should be set to avoid operations on unitialized data
+        float padding;
+    };
+
+    //! lower, along x and y and z, corner
+    Corner lower;
+    //! upper, along x and y and z, corner
+    Corner upper;
+};
+
+/*! \internal
+ * \brief Bounding box for one dimension of a grid cell
+ */
+struct BoundingBox1D
+{
+    //! lower bound
+    float lower;
+    //! upper bound
+    float upper;
+};
+
+} // namespace Nbnxm
+
+namespace Nbnxm
+{
+
+/*! \internal
+ * \brief A pair-search grid object for one domain decomposition zone
+ *
+ * This is a rectangular 3D grid covering a potentially non-rectangular
+ * volume which is either the whole unit cell or the local zone or part
+ * of a non-local zone when using domain decomposition. The grid cells
+ * are even spaced along x/y and irregular along z. Each cell is sub-divided
+ * into atom clusters. With a CPU geometry, each cell contains 1 or 2 clusters.
+ * With a GPU geometry, each cell contains up to 8 clusters. The geometry is
+ * set by the pairlist type which is the only argument of the constructor.
+ *
+ * When multiple grids are used, i.e. with domain decomposition, we want
+ * to avoid the overhead of multiple coordinate arrays or extra indexing.
+ * Therefore each grid stores a cell offset, so a contiguous cell index
+ * can be used to index atom arrays. All methods returning atom indices
+ * return indices which index into a full atom array.
+ *
+ * Note that when atom groups, instead of individual atoms, are assigned
+ * to grid cells, individual atoms can be geometrically outside the cell
+ * and grid that they have been assigned to (as determined by the center
+ * or geometry of the atom group they belong to).
+ */
+class Grid
+{
+public:
+    /*! \internal
+     * \brief The cluster and cell geometry of a grid
+     */
+    struct Geometry
+    {
+        //! Constructs the cluster/cell geometry given the type of pairlist
+        Geometry(PairlistType pairlistType);
+
+        //! Is this grid simple (CPU) or hierarchical (GPU)
+        bool isSimple;
+        //! Number of atoms per cluster
+        int numAtomsICluster;
+        //! Number of atoms for list j-clusters
+        int numAtomsJCluster;
+        //! Number of atoms per cell
+        int numAtomsPerCell;
+        //! 2log of na_c
+        int numAtomsICluster2Log;
+    };
+
+    //! The physical dimensions of a grid \internal
+    struct Dimensions
+    {
+        //! The lower corner of the (local) grid
+        rvec lowerCorner;
+        //! The upper corner of the (local) grid
+        rvec upperCorner;
+        //! The physical grid size: upperCorner - lowerCorner
+        rvec gridSize;
+        //! An estimate for the atom number density of the region targeted by the grid
+        real atomDensity;
+        //! The maximum distance an atom can be outside of a cell and outside of the grid
+        real maxAtomGroupRadius;
+        //! Size of cell along dimension x and y
+        real cellSize[DIM - 1];
+        //! 1/size of a cell along dimensions x and y
+        real invCellSize[DIM - 1];
+        //! The number of grid cells along dimensions x and y
+        int numCells[DIM - 1];
+    };
+
+    //! Constructs a grid given the type of pairlist
+    Grid(PairlistType pairlistType, const bool& haveFep);
+
+    //! Returns the geometry of the grid cells
+    const Geometry& geometry() const { return geometry_; }
+
+    //! Returns the dimensions of the grid
+    const Dimensions& dimensions() const { return dimensions_; }
+
+    //! Returns the total number of grid columns
+    int numColumns() const { return dimensions_.numCells[XX] * dimensions_.numCells[YY]; }
+
+    //! Returns the total number of grid cells
+    int numCells() const { return numCellsTotal_; }
+
+    //! Returns the cell offset of (the first cell of) this grid in the list of cells combined over all grids
+    int cellOffset() const { return cellOffset_; }
+
+    //! Returns the maximum number of grid cells in a column
+    int numCellsColumnMax() const { return numCellsColumnMax_; }
+
+    //! Returns the start of the source atom range mapped to this grid
+    int srcAtomBegin() const { return srcAtomBegin_; }
+
+    //! Returns the end of the source atom range mapped to this grid
+    int srcAtomEnd() const { return srcAtomEnd_; }
+
+    //! Returns the first cell index in the grid, starting at 0 in this grid
+    int firstCellInColumn(int columnIndex) const { return cxy_ind_[columnIndex]; }
+
+    //! Returns the number of cells in the column
+    int numCellsInColumn(int columnIndex) const
+    {
+        return cxy_ind_[columnIndex + 1LL] - cxy_ind_[columnIndex];
+    }
+
+    //! Returns the index of the first atom in the column
+    int firstAtomInColumn(int columnIndex) const
+    {
+        return (cellOffset_ + cxy_ind_[columnIndex]) * geometry_.numAtomsPerCell;
+    }
+
+    //! Returns the number of real atoms in the column
+    int numAtomsInColumn(int columnIndex) const { return cxy_na_[columnIndex]; }
+
+    /*! \brief Returns a view of the number of non-filler, atoms for each grid column
+     *
+     * \todo Needs a useful name. */
+    gmx::ArrayRef<const int> cxy_na() const { return cxy_na_; }
+    /*! \brief Returns a view of the grid-local cell index for each grid column
+     *
+     * \todo Needs a useful name. */
+    gmx::ArrayRef<const int> cxy_ind() const { return cxy_ind_; }
+
+    //! Returns the number of real atoms in the column
+    int numAtomsPerCell() const { return geometry_.numAtomsPerCell; }
+
+    //! Returns the number of atoms in the column including padding
+    int paddedNumAtomsInColumn(int columnIndex) const
+    {
+        return numCellsInColumn(columnIndex) * geometry_.numAtomsPerCell;
+    }
+
+    //! Returns the end of the atom index range on the grid, including padding
+    int atomIndexEnd() const { return (cellOffset_ + numCellsTotal_) * geometry_.numAtomsPerCell; }
+
+    //! Returns whether any atom in the cluster is perturbed
+    bool clusterIsPerturbed(int clusterIndex) const { return fep_[clusterIndex] != 0U; }
+
+    //! Returns whether the given atom in the cluster is perturbed
+    bool atomIsPerturbed(int clusterIndex, int atomIndexInCluster) const
+    {
+        return (fep_[clusterIndex] & (1 << atomIndexInCluster)) != 0U;
+    }
+
+    //! Returns the free-energy perturbation bits for the cluster
+    unsigned int fepBits(int clusterIndex) const { return fep_[clusterIndex]; }
+
+    //! Returns the i-bounding boxes for all clusters on the grid
+    gmx::ArrayRef<const BoundingBox> iBoundingBoxes() const { return bb_; }
+
+    //! Returns the j-bounding boxes for all clusters on the grid
+    gmx::ArrayRef<const BoundingBox> jBoundingBoxes() const { return bbj_; }
+
+    //! Returns the packed bounding boxes for all clusters on the grid, empty with a CPU list
+    gmx::ArrayRef<const float> packedBoundingBoxes() const { return pbb_; }
+
+    //! Returns the bounding boxes along z for all cells on the grid
+    gmx::ArrayRef<const BoundingBox1D> zBoundingBoxes() const { return bbcz_; }
+
+    //! Returns the flags for all clusters on the grid
+    gmx::ArrayRef<const int> clusterFlags() const { return flags_; }
+
+    //! Returns the number of clusters for all cells on the grid, empty with a CPU geometry
+    gmx::ArrayRef<const int> numClustersPerCell() const { return numClusters_; }
+
+    //! Returns the cluster index for an atom
+    int atomToCluster(int atomIndex) const { return (atomIndex >> geometry_.numAtomsICluster2Log); }
+
+    //! Returns the total number of clusters on the grid
+    int numClusters() const
+    {
+        if (geometry_.isSimple)
+        {
+            return numCellsTotal_;
+        }
+        else
+        {
+            return numClustersTotal_;
+        }
+    }
+
+    //! Sets the grid dimensions
+    void setDimensions(int                ddZone,
+                       int                numAtoms,
+                       gmx::RVec          lowerCorner,
+                       gmx::RVec          upperCorner,
+                       real               atomDensity,
+                       real               maxAtomGroupRadius,
+                       bool               haveFep,
+                       gmx::PinningPolicy pinningPolicy);
+
+    //! Sets the cell indices using indices in \p gridSetData and \p gridWork
+    void setCellIndices(int                            ddZone,
+                        int                            cellOffset,
+                        GridSetData*                   gridSetData,
+                        gmx::ArrayRef<GridWork>        gridWork,
+                        gmx::Range<int>                atomRange,
+                        gmx::ArrayRef<const int64_t>   atomInfo,
+                        gmx::ArrayRef<const gmx::RVec> x,
+                        int                            numAtomsMoved,
+                        nbnxn_atomdata_t*              nbat);
+
+    //! Determine in which grid columns atoms should go, store cells and atom counts in \p cell and \p cxy_na
+    static void calcColumnIndices(const Grid::Dimensions&        gridDims,
+                                  const gmx::UpdateGroupsCog*    updateGroupsCog,
+                                  gmx::Range<int>                atomRange,
+                                  gmx::ArrayRef<const gmx::RVec> x,
+                                  int                            dd_zone,
+                                  const int*                     move,
+                                  int                            thread,
+                                  int                            nthread,
+                                  gmx::ArrayRef<int>             cell,
+                                  gmx::ArrayRef<int>             cxy_na);
+
+private:
+    /*! \brief Fill a pair search cell with atoms
+     *
+     * Potentially sorts atoms and sets the interaction flags.
+     */
+    void fillCell(GridSetData*                   gridSetData,
+                  nbnxn_atomdata_t*              nbat,
+                  int                            atomStart,
+                  int                            atomEnd,
+                  gmx::ArrayRef<const int64_t>   atomInfo,
+                  gmx::ArrayRef<const gmx::RVec> x,
+                  BoundingBox gmx_unused* bb_work_aligned);
+
+    //! Spatially sort the atoms within the given column range, for CPU geometry
+    void sortColumnsCpuGeometry(GridSetData*                   gridSetData,
+                                int                            dd_zone,
+                                gmx::ArrayRef<const int64_t>   atomInfo,
+                                gmx::ArrayRef<const gmx::RVec> x,
+                                nbnxn_atomdata_t*              nbat,
+                                gmx::Range<int>                columnRange,
+                                gmx::ArrayRef<int>             sort_work);
+
+    //! Spatially sort the atoms within the given column range, for GPU geometry
+    void sortColumnsGpuGeometry(GridSetData*                   gridSetData,
+                                int                            dd_zone,
+                                gmx::ArrayRef<const int64_t>   atomInfo,
+                                gmx::ArrayRef<const gmx::RVec> x,
+                                nbnxn_atomdata_t*              nbat,
+                                gmx::Range<int>                columnRange,
+                                gmx::ArrayRef<int>             sort_work);
+
+    /* Data members */
+    //! The geometry of the grid clusters and cells
+    Geometry geometry_;
+    //! The physical dimensions of the grid
+    Dimensions dimensions_;
+
+    //! The total number of cells in this grid
+    int numCellsTotal_;
+    //! Index in nbs->cell corresponding to cell 0
+    int cellOffset_;
+    //! The maximum number of cells in a column
+    int numCellsColumnMax_;
+
+    //! The start of the source atom range mapped to this grid
+    int srcAtomBegin_;
+    //! The end of the source atom range mapped to this grid
+    int srcAtomEnd_;
+
+    /* Grid data */
+    /*! \brief The number of, non-filler, atoms for each grid column.
+     *
+     * \todo Needs a useful name. */
+    gmx::HostVector<int> cxy_na_;
+    /*! \brief The grid-local cell index for each grid column
+     *
+     * \todo Needs a useful name. */
+    gmx::HostVector<int> cxy_ind_;
+
+    //! The number of cluster for each cell
+    std::vector<int> numClusters_;
+
+    /* Bounding boxes */
+    //! Bounding boxes in z for the cells
+    std::vector<BoundingBox1D> bbcz_;
+    //! 3D bounding boxes for the sub cells
+    std::vector<BoundingBox, gmx::AlignedAllocator<BoundingBox>> bb_;
+    //! 3D j-bounding boxes for the case where the i- and j-cluster sizes are different
+    std::vector<BoundingBox, gmx::AlignedAllocator<BoundingBox>> bbjStorage_;
+    //! 3D j-bounding boxes
+    gmx::ArrayRef<BoundingBox> bbj_;
+    //! 3D bounding boxes in packed xxxx format per cell
+    std::vector<float, gmx::AlignedAllocator<float>> pbb_;
+
+    //! Tells whether we have perturbed interactions, authorative source is in GridSet (never modified)
+    const bool& haveFep_;
+
+    /* Bit-flag information */
+    //! Flags for properties of clusters in each cell
+    std::vector<int> flags_;
+    //! Signal bits for atoms in each cell that tell whether an atom is perturbed
+    std::vector<unsigned int> fep_;
+
+    /* Statistics */
+    //! Total number of clusters, used for printing
+    int numClustersTotal_;
+};
+
+} // namespace Nbnxm
+
+#endif
diff --git a/src/include/gromacs/nbnxm/gridset.h b/src/include/gromacs/nbnxm/gridset.h
new file mode 100644 (file)
index 0000000..be9b523
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the GridSet class.
+ *
+ * This class holds the grids for the local and non-local domain decomposition
+ * zones, as well as the cell and atom data that covers all grids.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_GRIDSET_H
+#define GMX_NBNXM_GRIDSET_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/math/vec.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/range.h"
+
+#include "grid.h"
+#include "gridsetdata.h"
+
+struct gmx_domdec_zones_t;
+struct nbnxn_atomdata_t;
+enum class PairlistType;
+enum class PbcType : int;
+
+namespace gmx
+{
+class UpdateGroupsCog;
+}
+
+namespace Nbnxm
+{
+
+/*! \internal
+ * \brief Holds a set of search grids for the local + non-local DD zones
+ *
+ * The are three different possible setups:
+ * - a single grid, this is the standard case without domain decomposition
+ * - one grid for each domain decomposition zone
+ * - with test particle insertion there are two grids, one for the system
+ *   to insert in and one for the molecule that is inserted
+ */
+class GridSet
+{
+public:
+    /*! \internal
+     * \brief Description of the domain setup: PBC and the connections between domains
+     */
+    struct DomainSetup
+    {
+        //! Constructor, without DD \p numDDCells and \p ddZones should be nullptr
+        DomainSetup(PbcType                   pbcType,
+                    bool                      doTestParticleInsertion,
+                    const ivec*               numDDCells,
+                    const gmx_domdec_zones_t* ddZones);
+
+        //! The type of PBC
+        PbcType pbcType;
+        //! Tells whether we are doing test-particle insertion
+        bool doTestParticleInsertion;
+        //! Are there multiple domains?
+        bool haveMultipleDomains;
+        //! Are there multiple domains along each dimension?
+        std::array<bool, DIM> haveMultipleDomainsPerDim;
+        //! The domain decomposition zone setup
+        const gmx_domdec_zones_t* zones;
+    };
+
+    //! Constructs a grid set for 1 or multiple DD zones, when numDDCells!=nullptr
+    GridSet(PbcType                   pbcType,
+            bool                      doTestParticleInsertion,
+            const ivec*               numDDCells,
+            const gmx_domdec_zones_t* ddZones,
+            PairlistType              pairlistType,
+            bool                      haveFep,
+            int                       numThreads,
+            gmx::PinningPolicy        pinningPolicy);
+
+    //! Puts the atoms on the grid with index \p gridIndex and copies the coordinates to \p nbat
+    void putOnGrid(const matrix                   box,
+                   int                            gridIndex,
+                   const rvec                     lowerCorner,
+                   const rvec                     upperCorner,
+                   const gmx::UpdateGroupsCog*    updateGroupsCog,
+                   gmx::Range<int>                atomRange,
+                   real                           atomDensity,
+                   gmx::ArrayRef<const int64_t>   atomInfo,
+                   gmx::ArrayRef<const gmx::RVec> x,
+                   int                            numAtomsMoved,
+                   const int*                     move,
+                   nbnxn_atomdata_t*              nbat);
+
+    //! Returns the domain setup
+    DomainSetup domainSetup() const { return domainSetup_; }
+
+    //! Returns the total number of atoms in the grid set, including padding
+    int numGridAtomsTotal() const { return grids_.back().atomIndexEnd(); }
+
+    //! Returns the number of local real atoms, i.e. without padded atoms
+    int numRealAtomsLocal() const { return numRealAtomsLocal_; }
+
+    //! Returns the number of total real atoms, i.e. without padded atoms
+    int numRealAtomsTotal() const { return numRealAtomsTotal_; }
+
+    //! Returns the atom order on the grid for the local atoms
+    gmx::ArrayRef<const int> getLocalAtomorder() const
+    {
+        /* Return the atom order for the home cell (index 0) */
+        const int numIndices = grids_[0].atomIndexEnd() - grids_[0].firstAtomInColumn(0);
+
+        return gmx::constArrayRefFromArray(atomIndices().data(), numIndices);
+    }
+
+    //! Sets the order of the local atoms to the order grid atom ordering
+    void setLocalAtomOrder();
+
+    //! Returns the list of grids
+    gmx::ArrayRef<const Grid> grids() const { return grids_; }
+
+    //! Returns the grid atom indices covering all grids
+    gmx::ArrayRef<const int> cells() const { return gridSetData_.cells; }
+
+    //! Returns the grid atom indices covering all grids
+    gmx::ArrayRef<const int> atomIndices() const { return gridSetData_.atomIndices; }
+
+    //! Returns whether we have perturbed non-bonded interactions
+    bool haveFep() const { return haveFep_; }
+
+    //! Returns the unit cell in \p box
+    void getBox(matrix box) const { copy_mat(box_, box); }
+
+    //! Returns the maximum number of columns across all grids
+    int numColumnsMax() const { return numColumnsMax_; }
+
+    //! Sets the maximum number of columns across all grids
+    void setNumColumnsMax(int numColumnsMax) { numColumnsMax_ = numColumnsMax; }
+
+private:
+    /* Data members */
+    //! The domain setup
+    DomainSetup domainSetup_;
+    //! The search grids
+    std::vector<Grid> grids_;
+    //! The cell and atom index data which runs over all grids
+    GridSetData gridSetData_;
+    //! Tells whether we have perturbed non-bonded interactions
+    bool haveFep_;
+    //! The periodic unit-cell
+    matrix box_;
+    //! The number of local real atoms, i.e. without padded atoms, local atoms: 0 to numAtomsLocal_
+    int numRealAtomsLocal_;
+    //! The total number of real atoms, i.e. without padded atoms
+    int numRealAtomsTotal_;
+    //! Working data for constructing a single grid, one entry per thread
+    std::vector<GridWork> gridWork_;
+    //! Maximum number of columns across all grids
+    int numColumnsMax_;
+};
+
+} // namespace Nbnxm
+
+#endif
diff --git a/src/include/gromacs/nbnxm/gridsetdata.h b/src/include/gromacs/nbnxm/gridsetdata.h
new file mode 100644 (file)
index 0000000..9d7301d
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the GridSetData struct which holds grid data that is shared over all grids
+ *
+ * Also declares a struct for work data that is shared over grids.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_GRIDSETDATA_H
+#define GMX_NBNXM_GRIDSETDATA_H
+
+#include <vector>
+
+#include "gromacs/gpu_utils/hostallocator.h"
+
+namespace Nbnxm
+{
+
+/*! \internal
+ * \brief Struct that holds grid data that is shared over all grids
+ *
+ * To enable a single coordinate and force array, a single cell range
+ * is needed which covers all grids.
+ */
+struct GridSetData
+{
+    //! The cell indices for all atoms
+    gmx::HostVector<int> cells;
+    //! The atom indices for all atoms stored in cell order
+    gmx::HostVector<int> atomIndices;
+};
+
+/*! \internal
+ * \brief Working arrays for constructing a grid
+ */
+struct GridWork
+{
+    //! Number of atoms for each grid column
+    std::vector<int> numAtomsPerColumn;
+    //! Buffer for sorting integers
+    std::vector<int> sortBuffer;
+};
+
+} // namespace Nbnxm
+
+#endif
diff --git a/src/include/gromacs/nbnxm/kernel_common.h b/src/include/gromacs/nbnxm/kernel_common.h
new file mode 100644 (file)
index 0000000..93d4b8c
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the nbnxm pair interaction kernel function types and kind counts, also declares utility functions used in nbnxm_kernel.cpp.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBXNM_KERNEL_COMMON_H
+#define GMX_NBXNM_KERNEL_COMMON_H
+
+#include "gromacs/math/vectypes.h"
+/* nbnxn_atomdata_t and nbnxn_pairlist_t could be forward declared, but that requires modifications in all SIMD kernel files */
+#include "gromacs/nbnxm/atomdata.h"
+#include "gromacs/utility/real.h"
+
+#include "pairlist.h"
+
+struct interaction_const_t;
+enum class CoulombInteractionType : int;
+enum class VanDerWaalsType : int;
+enum class InteractionModifiers : int;
+enum class LongRangeVdW : int;
+
+namespace Nbnxm
+{
+enum class EwaldExclusionType : int;
+}
+
+// TODO: Consider using one nbk_func type now ener and noener are identical
+
+/*! \brief Pair-interaction kernel type that also calculates energies.
+ */
+typedef void(nbk_func_ener)(const NbnxnPairlistCpu*    nbl,
+                            const nbnxn_atomdata_t*    nbat,
+                            const interaction_const_t* ic,
+                            const rvec*                shift_vec,
+                            nbnxn_atomdata_output_t*   out);
+
+/*! \brief Pointer to \p nbk_func_ener.
+ */
+typedef nbk_func_ener* p_nbk_func_ener;
+
+/*! \brief Pair-interaction kernel type that does not calculates energies.
+ */
+typedef void(nbk_func_noener)(const NbnxnPairlistCpu*    nbl,
+                              const nbnxn_atomdata_t*    nbat,
+                              const interaction_const_t* ic,
+                              const rvec*                shift_vec,
+                              nbnxn_atomdata_output_t*   out);
+
+/*! \brief Pointer to \p nbk_func_noener.
+ */
+typedef nbk_func_noener* p_nbk_func_noener;
+
+/*! \brief Kinds of electrostatic treatments in SIMD Verlet kernels
+ */
+enum class CoulombKernelType : int
+{
+    ReactionField,
+    Table,
+    TableTwin,
+    Ewald,
+    EwaldTwin,
+    Count
+};
+
+//! \brief Lookup function for Coulomb kernel type
+CoulombKernelType getCoulombKernelType(Nbnxm::EwaldExclusionType ewaldExclusionType,
+                                       CoulombInteractionType    coulombInteractionType,
+                                       bool                      haveEqualCoulombVwdRadii);
+
+/*! \brief Kinds of Van der Waals treatments in SIMD Verlet kernels
+ *
+ * The \p LJCUT_COMB refers to the LJ combination rule for the short range.
+ * The \p EWALDCOMB refers to the combination rule for the grid part.
+ * \p vdwktNR is the number of VdW treatments for the SIMD kernels.
+ * \p vdwktNR_ref is the number of VdW treatments for the C reference kernels.
+ * These two numbers differ, because currently only the reference kernels
+ * support LB combination rules for the LJ-Ewald grid part.
+ */
+enum
+{
+    vdwktLJCUT_COMBGEOM,
+    vdwktLJCUT_COMBLB,
+    vdwktLJCUT_COMBNONE,
+    vdwktLJFORCESWITCH,
+    vdwktLJPOTSWITCH,
+    vdwktLJEWALDCOMBGEOM,
+    vdwktLJEWALDCOMBLB,
+    vdwktNR = vdwktLJEWALDCOMBLB,
+    vdwktNR_ref
+};
+
+//! \brief Lookup function for Vdw kernel type
+int getVdwKernelType(Nbnxm::KernelType    kernelType,
+                     LJCombinationRule    ljCombinationRule,
+                     VanDerWaalsType      vanDerWaalsType,
+                     InteractionModifiers interactionModifiers,
+                     LongRangeVdW         longRangeVdW);
+
+/*! \brief Clears the force buffer.
+ *
+ * Either the whole buffer is cleared or only the parts used
+ * by thread/task \p outputIndex when nbat->bUseBufferFlags is set.
+ *
+ * \param[in,out] nbat         The Nbnxm atom data
+ * \param[in]     outputIndex  The index of the output object to clear
+ */
+void clearForceBuffer(nbnxn_atomdata_t* nbat, int outputIndex);
+
+/*! \brief Clears the shift forces.
+ */
+void clear_fshift(real* fshift);
+
+/*! \brief Reduces the collected energy terms over the pair-lists/threads.
+ */
+void reduce_energies_over_lists(const nbnxn_atomdata_t* nbat, int nlist, real* Vvdw, real* Vc);
+
+#endif
diff --git a/src/include/gromacs/nbnxm/kernel_file_generator/kernel_simd_template.h.pre b/src/include/gromacs/nbnxm/kernel_file_generator/kernel_simd_template.h.pre
new file mode 100644 (file)
index 0000000..0032667
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#include "gromacs/nbnxm/kernel_common.h"
+
+/* Declare all the different kernel functions.
+ */
+{0}
+
+#ifdef INCLUDE_KERNELFUNCTION_TABLES
+
+/* Declare and define the kernel function pointer lookup tables.
+ * The minor index of the array goes over both the LJ combination rules,
+ * which is only supported by plain cut-off, and the LJ switch/PME functions.
+ */
+static p_nbk_func_noener nbnxm_kernel_noener_simd_{1}[static_cast<int>(CoulombKernelType::Count)][vdwktNR] =
+{2}
+static p_nbk_func_ener nbnxm_kernel_ener_simd_{1}[static_cast<int>(CoulombKernelType::Count)][vdwktNR] =
+{3}
+static p_nbk_func_ener nbnxm_kernel_energrp_simd_{1}[static_cast<int>(CoulombKernelType::Count)][vdwktNR] =
+{4}
+
+#endif /* INCLUDE_KERNELFUNCTION_TABLES */
diff --git a/src/include/gromacs/nbnxm/kernels_reference/kernel_gpu_ref.h b/src/include/gromacs/nbnxm/kernels_reference/kernel_gpu_ref.h
new file mode 100644 (file)
index 0000000..83c59b6
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief
+ * Declares GPU reference kernel
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_KERNELS_REFERENCE_KERNEL_GPU_REF_H
+#define GMX_NBNXM_KERNELS_REFERENCE_KERNEL_GPU_REF_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+struct NbnxnPairlistGpu;
+struct nbnxn_atomdata_t;
+struct interaction_const_t;
+struct t_forcerec;
+
+namespace gmx
+{
+class StepWorkload;
+}
+
+//! Reference (slow) kernel for nb n vs n GPU type pair lists
+void nbnxn_kernel_gpu_ref(const NbnxnPairlistGpu*        nbl,
+                          const nbnxn_atomdata_t*        nbat,
+                          const interaction_const_t*     iconst,
+                          gmx::ArrayRef<const gmx::RVec> shiftvec,
+                          const gmx::StepWorkload&       stepWork,
+                          int                            clearF,
+                          gmx::ArrayRef<real>            f,
+                          real*                          fshift,
+                          real*                          Vc,
+                          real*                          Vvdw);
+
+#endif
diff --git a/src/include/gromacs/nbnxm/kernels_reference/kernel_ref.h b/src/include/gromacs/nbnxm/kernels_reference/kernel_ref.h
new file mode 100644 (file)
index 0000000..2d9a150
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 The GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief
+ * Declares CPU reference kernels
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+#ifndef GMX_NBNXM_KERNELS_REFERENCE_KERNEL_REF_H
+#define GMX_NBNXM_KERNELS_REFERENCE_KERNEL_REF_H
+
+#include "gromacs/nbnxm/kernel_common.h"
+
+//! All the different CPU reference kernel functions.
+//! \{
+nbk_func_noener nbnxn_kernel_ElecRF_VdwLJ_F_ref;
+nbk_func_noener nbnxn_kernel_ElecRF_VdwLJFsw_F_ref;
+nbk_func_noener nbnxn_kernel_ElecRF_VdwLJPsw_F_ref;
+nbk_func_noener nbnxn_kernel_ElecRF_VdwLJEwCombGeom_F_ref;
+nbk_func_noener nbnxn_kernel_ElecRF_VdwLJEwCombLB_F_ref;
+nbk_func_noener nbnxn_kernel_ElecQSTab_VdwLJ_F_ref;
+nbk_func_noener nbnxn_kernel_ElecQSTab_VdwLJFsw_F_ref;
+nbk_func_noener nbnxn_kernel_ElecQSTab_VdwLJPsw_F_ref;
+nbk_func_noener nbnxn_kernel_ElecQSTab_VdwLJEwCombGeom_F_ref;
+nbk_func_noener nbnxn_kernel_ElecQSTab_VdwLJEwCombLB_F_ref;
+nbk_func_noener nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_F_ref;
+nbk_func_noener nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_F_ref;
+nbk_func_noener nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_F_ref;
+nbk_func_noener nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_F_ref;
+nbk_func_noener nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_F_ref;
+
+nbk_func_ener nbnxn_kernel_ElecRF_VdwLJ_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecRF_VdwLJFsw_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecRF_VdwLJPsw_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecRF_VdwLJEwCombGeom_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecRF_VdwLJEwCombLB_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTab_VdwLJ_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTab_VdwLJFsw_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTab_VdwLJPsw_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTab_VdwLJEwCombGeom_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTab_VdwLJEwCombLB_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_VF_ref;
+
+nbk_func_ener nbnxn_kernel_ElecRF_VdwLJ_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecRF_VdwLJFsw_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecRF_VdwLJPsw_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecRF_VdwLJEwCombGeom_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecRF_VdwLJEwCombLB_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTab_VdwLJ_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTab_VdwLJFsw_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTab_VdwLJPsw_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTab_VdwLJEwCombGeom_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTab_VdwLJEwCombLB_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF_ref;
+nbk_func_ener nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_VgrpF_ref;
+//! \}
+
+#ifdef INCLUDE_KERNELFUNCTION_TABLES
+
+/*! \brief Declare and define the kernel function pointer lookup tables.
+ *
+ * The minor index of the array goes over both the LJ combination rules,
+ * which is only supported by plain cut-off, and the LJ switch/PME functions.
+ * For the C reference kernels, unlike the SIMD kernels, there is not much
+ * advantage in using combination rules, so we (re-)use the same kernel.
+ */
+//! \{
+static const p_nbk_func_noener nbnxn_kernel_noener_ref[static_cast<int>(CoulombKernelType::Count)][vdwktNR_ref] = {
+    { nbnxn_kernel_ElecRF_VdwLJ_F_ref,
+      nbnxn_kernel_ElecRF_VdwLJ_F_ref,
+      nbnxn_kernel_ElecRF_VdwLJ_F_ref,
+      nbnxn_kernel_ElecRF_VdwLJFsw_F_ref,
+      nbnxn_kernel_ElecRF_VdwLJPsw_F_ref,
+      nbnxn_kernel_ElecRF_VdwLJEwCombGeom_F_ref,
+      nbnxn_kernel_ElecRF_VdwLJEwCombLB_F_ref },
+    { nbnxn_kernel_ElecQSTab_VdwLJ_F_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJ_F_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJ_F_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJFsw_F_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJPsw_F_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJEwCombGeom_F_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJEwCombLB_F_ref },
+    { nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_F_ref },
+    { nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_F_ref },
+    { nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_F_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_F_ref }
+};
+
+static const p_nbk_func_ener nbnxn_kernel_ener_ref[static_cast<int>(CoulombKernelType::Count)][vdwktNR_ref] = {
+    { nbnxn_kernel_ElecRF_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecRF_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecRF_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecRF_VdwLJFsw_VF_ref,
+      nbnxn_kernel_ElecRF_VdwLJPsw_VF_ref,
+      nbnxn_kernel_ElecRF_VdwLJEwCombGeom_VF_ref,
+      nbnxn_kernel_ElecRF_VdwLJEwCombLB_VF_ref },
+    { nbnxn_kernel_ElecQSTab_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJFsw_VF_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJPsw_VF_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJEwCombGeom_VF_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJEwCombLB_VF_ref },
+    { nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_VF_ref },
+    { nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_VF_ref },
+    { nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_VF_ref }
+};
+
+static const p_nbk_func_ener nbnxn_kernel_energrp_ref[static_cast<int>(CoulombKernelType::Count)][vdwktNR_ref] = {
+    { nbnxn_kernel_ElecRF_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecRF_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecRF_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecRF_VdwLJFsw_VgrpF_ref,
+      nbnxn_kernel_ElecRF_VdwLJPsw_VgrpF_ref,
+      nbnxn_kernel_ElecRF_VdwLJEwCombGeom_VgrpF_ref,
+      nbnxn_kernel_ElecRF_VdwLJEwCombLB_VgrpF_ref },
+    { nbnxn_kernel_ElecQSTab_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJFsw_VgrpF_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJPsw_VgrpF_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJEwCombGeom_VgrpF_ref,
+      nbnxn_kernel_ElecQSTab_VdwLJEwCombLB_VgrpF_ref },
+    { nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_VgrpF_ref },
+    { nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_VgrpF_ref },
+    { nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJFsw_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJPsw_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF_ref,
+      nbnxn_kernel_ElecQSTabTwinCut_VdwLJEwCombLB_VgrpF_ref }
+};
+//! \}
+
+#endif /* INCLUDE_KERNELFUNCTION_TABLES */
+
+#endif
diff --git a/src/include/gromacs/nbnxm/kernels_reference/kernel_ref_includes.h b/src/include/gromacs/nbnxm/kernels_reference/kernel_ref_includes.h
new file mode 100644 (file)
index 0000000..8b825ba
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+
+/* This file includes all required (non-)energy flavors of the kernel
+ * outer and inner loops, given a Coulomb and VdW treatment.
+ */
+
+/* Include the force only kernels */
+#include "kernel_ref_outer.h"
+
+/* Include the force+energy kernels */
+#define CALC_ENERGIES
+#include "kernel_ref_outer.h"
+#undef CALC_ENERGIES
+
+/* Include the force+energygroups kernels */
+#define CALC_ENERGIES
+#define ENERGY_GROUPS
+#include "kernel_ref_outer.h"
+#undef ENERGY_GROUPS
+#undef CALC_ENERGIES
diff --git a/src/include/gromacs/nbnxm/kernels_reference/kernel_ref_inner.h b/src/include/gromacs/nbnxm/kernels_reference/kernel_ref_inner.h
new file mode 100644 (file)
index 0000000..1de0703
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/* When calculating RF or Ewald interactions we calculate the electrostatic
+ * forces and energies on excluded atom pairs here in the non-bonded loops.
+ */
+#if defined CHECK_EXCLS && (defined CALC_COULOMB || defined LJ_EWALD)
+#    define EXCL_FORCES
+#endif
+
+{
+    const int cj = l_cj[cjind].cj;
+
+#ifdef ENERGY_GROUPS
+    const int egp_cj = nbatParams.energrp[cj];
+#endif
+    for (int i = 0; i < UNROLLI; i++)
+    {
+        const int ai = ci * UNROLLI + i;
+
+        const int type_i_off = type[ai] * ntype2;
+
+        for (int j = 0; j < UNROLLJ; j++)
+        {
+            real FrLJ6 = 0, FrLJ12 = 0, frLJ = 0;
+
+            /* A multiply mask used to zero an interaction
+             * when either the distance cutoff is exceeded, or
+             * (if appropriate) the i and j indices are
+             * unsuitable for this kind of inner loop. */
+#ifdef CHECK_EXCLS
+            /* A multiply mask used to zero an interaction
+             * when that interaction should be excluded
+             * (e.g. because of bonding). */
+            const real interact = static_cast<real>((l_cj[cjind].excl >> (i * UNROLLI + j)) & 1);
+#    ifndef EXCL_FORCES
+            real skipmask = interact;
+#    else
+            real skipmask = (cj == ci_sh && j <= i) ? 0.0 : 1.0;
+#    endif
+#else
+            constexpr real interact = 1.0;
+            real           skipmask = interact;
+#endif
+
+            real gmx_unused VLJ = 0;
+
+            const int aj = cj * UNROLLJ + j;
+
+            const real dx = xi[i * XI_STRIDE + XX] - x[aj * X_STRIDE + XX];
+            const real dy = xi[i * XI_STRIDE + YY] - x[aj * X_STRIDE + YY];
+            const real dz = xi[i * XI_STRIDE + ZZ] - x[aj * X_STRIDE + ZZ];
+
+            real rsq = dx * dx + dy * dy + dz * dz;
+
+            /* Prepare to enforce the cut-off. */
+            skipmask = (rsq >= rcut2) ? 0 : skipmask;
+            /* 9 flops for r^2 + cut-off check */
+
+            // Ensure the distances do not fall below the limit where r^-12 overflows.
+            // This should never happen for normal interactions.
+            rsq = std::max(rsq, c_nbnxnMinDistanceSquared);
+
+#ifdef COUNT_PAIRS
+            npair++;
+#endif
+
+            real rinv = gmx::invsqrt(rsq);
+            /* 5 flops for invsqrt */
+
+            /* Partially enforce the cut-off (and perhaps
+             * exclusions) to avoid possible overflow of
+             * rinvsix when computing LJ, and/or overflowing
+             * the Coulomb table during lookup. */
+            rinv = rinv * skipmask;
+
+            const real rinvsq = rinv * rinv;
+
+#ifdef HALF_LJ
+            if (i < UNROLLI / 2)
+#endif
+            {
+                const real c6  = nbfp[type_i_off + type[aj] * 2];
+                const real c12 = nbfp[type_i_off + type[aj] * 2 + 1];
+
+#if defined LJ_CUT || defined LJ_FORCE_SWITCH || defined LJ_POT_SWITCH
+                real rinvsix = interact * rinvsq * rinvsq * rinvsq;
+                FrLJ6        = c6 * rinvsix;
+                FrLJ12       = c12 * rinvsix * rinvsix;
+                frLJ         = FrLJ12 - FrLJ6;
+                /* 7 flops for r^-2 + LJ force */
+#    if defined CALC_ENERGIES || defined LJ_POT_SWITCH
+                VLJ = (FrLJ12 + c12 * ic->repulsion_shift.cpot) / 12
+                      - (FrLJ6 + c6 * ic->dispersion_shift.cpot) / 6;
+                /* 7 flops for LJ energy */
+#    endif
+#endif
+
+#if defined LJ_FORCE_SWITCH || defined LJ_POT_SWITCH
+                /* Force or potential switching from ic->rvdw_switch */
+                real r   = rsq * rinv;
+                real rsw = r - ic->rvdw_switch;
+                rsw      = (rsw >= 0.0 ? rsw : 0.0);
+#endif
+#ifdef LJ_FORCE_SWITCH
+                frLJ += -c6 * (ic->dispersion_shift.c2 + ic->dispersion_shift.c3 * rsw) * rsw * rsw * r
+                        + c12 * (ic->repulsion_shift.c2 + ic->repulsion_shift.c3 * rsw) * rsw * rsw * r;
+#    if defined CALC_ENERGIES
+                VLJ += -c6 * (-ic->dispersion_shift.c2 / 3 - ic->dispersion_shift.c3 / 4 * rsw)
+                               * rsw * rsw * rsw
+                       + c12 * (-ic->repulsion_shift.c2 / 3 - ic->repulsion_shift.c3 / 4 * rsw)
+                                 * rsw * rsw * rsw;
+#    endif
+#endif
+
+#if defined CALC_ENERGIES || defined LJ_POT_SWITCH
+                /* Masking should be done after force switching,
+                 * but before potential switching.
+                 */
+                /* Need to zero the interaction if there should be exclusion. */
+                VLJ = VLJ * interact;
+#endif
+
+#ifdef LJ_POT_SWITCH
+                {
+                    const real sw  = 1.0 + (swV3 + (swV4 + swV5 * rsw) * rsw) * rsw * rsw * rsw;
+                    const real dsw = (swF2 + (swF3 + swF4 * rsw) * rsw) * rsw * rsw;
+
+                    frLJ = frLJ * sw - r * VLJ * dsw;
+                    VLJ *= sw;
+                }
+#endif
+
+#ifdef LJ_EWALD
+                {
+#    ifdef LJ_EWALD_COMB_GEOM
+                    const real c6grid = ljc[type[ai] * 2] * ljc[type[aj] * 2];
+#    elif defined LJ_EWALD_COMB_LB
+                    real c6grid = NAN;
+                    {
+                        /* These sigma and epsilon are scaled to give 6*C6 */
+                        const real sigma   = ljc[type[ai] * 2] + ljc[type[aj] * 2];
+                        const real epsilon = ljc[type[ai] * 2 + 1] * ljc[type[aj] * 2 + 1];
+
+                        const real sigma2 = sigma * sigma;
+                        c6grid            = epsilon * sigma2 * sigma2 * sigma2;
+                    }
+#    else
+#        error "No LJ Ewald combination rule defined"
+#    endif
+
+#    ifdef CHECK_EXCLS
+                    /* Recalculate rinvsix without exclusion mask */
+                    const real rinvsix_nm = rinvsq * rinvsq * rinvsq;
+#    else
+                    const real rinvsix_nm = rinvsix;
+#    endif
+                    const real cr2 = lje_coeff2 * rsq;
+#    if GMX_DOUBLE
+                    const real expmcr2 = exp(-cr2);
+#    else
+                    const real expmcr2    = expf(-cr2);
+#    endif
+                    const real poly = 1 + cr2 + 0.5 * cr2 * cr2;
+
+                    /* Subtract the grid force from the total LJ force */
+                    frLJ += c6grid * (rinvsix_nm - expmcr2 * (rinvsix_nm * poly + lje_coeff6_6));
+#    ifdef CALC_ENERGIES
+                    /* Shift should only be applied to real LJ pairs */
+                    const real sh_mask = lje_vc * interact;
+
+                    VLJ += c6grid / 6 * (rinvsix_nm * (1 - expmcr2 * poly) + sh_mask);
+#    endif
+                }
+#endif /* LJ_EWALD */
+
+#ifdef VDW_CUTOFF_CHECK
+                /* Mask for VdW cut-off shorter than Coulomb cut-off */
+                {
+                    real skipmask_rvdw = (rsq < rvdw2) ? 1.0 : 0.0;
+                    frLJ *= skipmask_rvdw;
+#    ifdef CALC_ENERGIES
+                    VLJ *= skipmask_rvdw;
+#    endif
+                }
+#else
+#    if defined CALC_ENERGIES
+                /* Need to zero the interaction if r >= rcut */
+                VLJ = VLJ * skipmask;
+                /* 1 more flop for LJ energy */
+#    endif
+#endif /* VDW_CUTOFF_CHECK */
+
+
+#ifdef CALC_ENERGIES
+#    ifdef ENERGY_GROUPS
+                Vvdw[egp_sh_i[i] + ((egp_cj >> (nbatParams.neg_2log * j)) & egp_mask)] += VLJ;
+#    else
+                Vvdw_ci += VLJ;
+                /* 1 flop for LJ energy addition */
+#    endif
+#endif
+            }
+
+#ifdef CALC_COULOMB
+            /* Enforce the cut-off and perhaps exclusions. In
+             * those cases, rinv is zero because of skipmask,
+             * but fcoul and vcoul will later be non-zero (in
+             * both RF and table cases) because of the
+             * contributions that do not depend on rinv. These
+             * contributions cannot be allowed to accumulate
+             * to the force and potential, and the easiest way
+             * to do this is to zero the charges in
+             * advance. */
+            const real qq = skipmask * qi[i] * q[aj];
+
+#    ifdef CALC_COUL_RF
+            real fcoul = qq * (interact * rinv * rinvsq - k_rf2);
+            /* 4 flops for RF force */
+#        ifdef CALC_ENERGIES
+            real vcoul = qq * (interact * rinv + reactionFieldCoefficient * rsq - reactionFieldShift);
+            /* 4 flops for RF energy */
+#        endif
+#    endif
+
+#    ifdef CALC_COUL_TAB
+            const real rs   = rsq * rinv * tab_coul_scale;
+            const int  ri   = int(rs);
+            const real frac = rs - static_cast<real>(ri);
+#        if !GMX_DOUBLE
+            /* fexcl = F_i + frac * (F_(i+1)-F_i) */
+            const real fexcl = tab_coul_FDV0[ri * 4] + frac * tab_coul_FDV0[ri * 4 + 1];
+#        else
+            /* fexcl = (1-frac) * F_i + frac * F_(i+1) */
+            const real fexcl = (1 - frac) * tab_coul_F[ri] + frac * tab_coul_F[ri + 1];
+#        endif
+            real fcoul = interact * rinvsq - fexcl;
+            /* 7 flops for float 1/r-table force */
+#        ifdef CALC_ENERGIES
+#            if !GMX_DOUBLE
+            real vcoul =
+                    qq
+                    * (interact * (rinv - ic->sh_ewald)
+                       - (tab_coul_FDV0[ri * 4 + 2] - halfsp * frac * (tab_coul_FDV0[ri * 4] + fexcl)));
+            /* 7 flops for float 1/r-table energy (8 with excls) */
+#            else
+            real vcoul = qq
+                         * (interact * (rinv - ic->sh_ewald)
+                            - (tab_coul_V[ri] - halfsp * frac * (tab_coul_F[ri] + fexcl)));
+#            endif
+#        endif
+            fcoul *= qq * rinv;
+#    endif
+
+#    ifdef CALC_ENERGIES
+#        ifdef ENERGY_GROUPS
+            Vc[egp_sh_i[i] + ((egp_cj >> (nbatParams.neg_2log * j)) & egp_mask)] += vcoul;
+#        else
+            Vc_ci += vcoul;
+            /* 1 flop for Coulomb energy addition */
+#        endif
+#    endif
+#endif
+
+#ifdef CALC_COULOMB
+            static constexpr bool sc_halfLJ =
+#    ifdef HALF_LJ
+                    true;
+#    else
+                    false;
+#    endif
+            /* 2 flops for scalar LJ+Coulomb force if !HALF_LJ || (i < UNROLLI / 2) */
+            const real fscal = (!sc_halfLJ || (i < UNROLLI / 2)) ? frLJ * rinvsq + fcoul : fcoul;
+#else
+            const real fscal = frLJ * rinvsq;
+#endif
+            const real fx = fscal * dx;
+            const real fy = fscal * dy;
+            const real fz = fscal * dz;
+
+            /* Increment i-atom force */
+            fi[i * FI_STRIDE + XX] += fx;
+            fi[i * FI_STRIDE + YY] += fy;
+            fi[i * FI_STRIDE + ZZ] += fz;
+            /* Decrement j-atom force */
+            f[aj * F_STRIDE + XX] -= fx;
+            f[aj * F_STRIDE + YY] -= fy;
+            f[aj * F_STRIDE + ZZ] -= fz;
+            /* 9 flops for force addition */
+        }
+    }
+}
+
+#undef interact
+#undef EXCL_FORCES
diff --git a/src/include/gromacs/nbnxm/kernels_reference/kernel_ref_outer.h b/src/include/gromacs/nbnxm/kernels_reference/kernel_ref_outer.h
new file mode 100644 (file)
index 0000000..a74d579
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#define UNROLLI 4
+#define UNROLLJ 4
+
+static_assert(UNROLLI == c_nbnxnCpuIClusterSize, "UNROLLI should match the i-cluster size");
+
+/* We could use nbat->xstride and nbat->fstride, but macros might be faster */
+#define X_STRIDE 3
+#define F_STRIDE 3
+/* Local i-atom buffer strides */
+#define XI_STRIDE 3
+#define FI_STRIDE 3
+
+
+/* All functionality defines are set here, except for:
+ * CALC_ENERGIES, ENERGY_GROUPS which are defined before.
+ * CHECK_EXCLS, which is set just before including the inner loop contents.
+ */
+
+/* We always calculate shift forces, because it's cheap anyhow */
+#define CALC_SHIFTFORCES
+
+#ifdef CALC_COUL_RF
+#    define NBK_FUNC_NAME2(ljt, feg) nbnxn_kernel##_ElecRF##ljt##feg##_ref
+#endif
+#ifdef CALC_COUL_TAB
+#    ifndef VDW_CUTOFF_CHECK
+#        define NBK_FUNC_NAME2(ljt, feg) nbnxn_kernel##_ElecQSTab##ljt##feg##_ref
+#    else
+#        define NBK_FUNC_NAME2(ljt, feg) nbnxn_kernel##_ElecQSTabTwinCut##ljt##feg##_ref
+#    endif
+#endif
+
+#if defined LJ_CUT && !defined LJ_EWALD
+#    define NBK_FUNC_NAME(feg) NBK_FUNC_NAME2(_VdwLJ, feg)
+#elif defined LJ_FORCE_SWITCH
+#    define NBK_FUNC_NAME(feg) NBK_FUNC_NAME2(_VdwLJFsw, feg)
+#elif defined LJ_POT_SWITCH
+#    define NBK_FUNC_NAME(feg) NBK_FUNC_NAME2(_VdwLJPsw, feg)
+#elif defined LJ_EWALD
+#    ifdef LJ_EWALD_COMB_GEOM
+#        define NBK_FUNC_NAME(feg) NBK_FUNC_NAME2(_VdwLJEwCombGeom, feg)
+#    else
+#        define NBK_FUNC_NAME(feg) NBK_FUNC_NAME2(_VdwLJEwCombLB, feg)
+#    endif
+#else
+#    error "No VdW type defined"
+#endif
+
+void
+#ifndef CALC_ENERGIES
+        NBK_FUNC_NAME(_F) // NOLINT(misc-definitions-in-headers)
+#else
+#    ifndef ENERGY_GROUPS
+        NBK_FUNC_NAME(_VF) // NOLINT(misc-definitions-in-headers)
+#    else
+        NBK_FUNC_NAME(_VgrpF) // NOLINT(misc-definitions-in-headers)
+#    endif
+#endif
+#undef NBK_FUNC_NAME
+#undef NBK_FUNC_NAME2
+        (const NbnxnPairlistCpu*    nbl,
+         const nbnxn_atomdata_t*    nbat,
+         const interaction_const_t* ic,
+         const rvec*                shift_vec,
+         nbnxn_atomdata_output_t*   out)
+{
+    /* Unpack pointers for output */
+    real* f = out->f.data();
+#ifdef CALC_SHIFTFORCES
+    real* fshift = out->fshift.data();
+#endif
+#ifdef CALC_ENERGIES
+    real* Vvdw = out->Vvdw.data();
+    real* Vc   = out->Vc.data();
+#endif
+
+    real xi[UNROLLI * XI_STRIDE];
+    real fi[UNROLLI * FI_STRIDE];
+    real qi[UNROLLI];
+
+#ifdef COUNT_PAIRS
+    int npair = 0;
+#endif
+
+#ifdef LJ_POT_SWITCH
+    const real swV3 = ic->vdw_switch.c3;
+    const real swV4 = ic->vdw_switch.c4;
+    const real swV5 = ic->vdw_switch.c5;
+    const real swF2 = 3 * ic->vdw_switch.c3;
+    const real swF3 = 4 * ic->vdw_switch.c4;
+    const real swF4 = 5 * ic->vdw_switch.c5;
+#endif
+
+    const nbnxn_atomdata_t::Params& nbatParams = nbat->params();
+
+#ifdef LJ_EWALD
+    const real lje_coeff2   = ic->ewaldcoeff_lj * ic->ewaldcoeff_lj;
+    const real lje_coeff6_6 = lje_coeff2 * lje_coeff2 * lje_coeff2 / 6.0;
+#    ifdef CALC_ENERGIES
+    const real lje_vc = ic->sh_lj_ewald;
+#    endif
+
+    const real* ljc = nbatParams.nbfp_comb.data();
+#endif
+
+#ifdef CALC_COUL_RF
+    const real k_rf2 = 2 * ic->reactionFieldCoefficient;
+#    ifdef CALC_ENERGIES
+    const real reactionFieldCoefficient = ic->reactionFieldCoefficient;
+    const real reactionFieldShift       = ic->reactionFieldShift;
+#    endif
+#endif
+#ifdef CALC_COUL_TAB
+    const real tab_coul_scale = ic->coulombEwaldTables->scale;
+#    ifdef CALC_ENERGIES
+    const real halfsp = 0.5 / tab_coul_scale;
+#    endif
+
+#    if !GMX_DOUBLE
+    const real* tab_coul_FDV0 = ic->coulombEwaldTables->tableFDV0.data();
+#    else
+    const real* tab_coul_F = ic->coulombEwaldTables->tableF.data();
+#        ifdef CALC_ENERGIES
+    const real* tab_coul_V = ic->coulombEwaldTables->tableV.data();
+#        endif
+#    endif
+#endif
+
+#ifdef ENERGY_GROUPS
+    const int egp_mask = (1 << nbatParams.neg_2log) - 1;
+#endif
+
+
+    const real rcut2 = ic->rcoulomb * ic->rcoulomb;
+#ifdef VDW_CUTOFF_CHECK
+    const real rvdw2 = ic->rvdw * ic->rvdw;
+#endif
+
+    const int   ntype2   = nbatParams.numTypes * 2;
+    const real* nbfp     = nbatParams.nbfp.data();
+    const real* q        = nbatParams.q.data();
+    const int*  type     = nbatParams.type.data();
+    const real  facel    = ic->epsfac;
+    const real* shiftvec = shift_vec[0];
+    const real* x        = nbat->x().data();
+
+    const nbnxn_cj_t* l_cj = nbl->cj.data();
+
+    for (const nbnxn_ci_t& ciEntry : nbl->ci)
+    {
+        const int ish = (ciEntry.shift & NBNXN_CI_SHIFT);
+        /* x, f and fshift are assumed to be stored with stride 3 */
+        const int ishf   = ish * DIM;
+        const int cjind0 = ciEntry.cj_ind_start;
+        const int cjind1 = ciEntry.cj_ind_end;
+        /* Currently only works super-cells equal to sub-cells */
+        const int ci    = ciEntry.ci;
+        const int ci_sh = (ish == gmx::c_centralShiftIndex ? ci : -1);
+
+        /* We have 5 LJ/C combinations, but use only three inner loops,
+         * as the other combinations are unlikely and/or not much faster:
+         * inner half-LJ + C for half-LJ + C / no-LJ + C
+         * inner LJ + C      for full-LJ + C
+         * inner LJ          for full-LJ + no-C / half-LJ + no-C
+         */
+        const bool do_LJ   = ((ciEntry.shift & NBNXN_CI_DO_LJ(0)) != 0);
+        const bool do_coul = ((ciEntry.shift & NBNXN_CI_DO_COUL(0)) != 0);
+        const bool half_LJ = (((ciEntry.shift & NBNXN_CI_HALF_LJ(0)) != 0) || !do_LJ) && do_coul;
+#ifdef CALC_ENERGIES
+
+#    ifdef LJ_EWALD
+        const bool do_self = true;
+#    else
+        const bool do_self = do_coul;
+#    endif
+
+#    ifndef ENERGY_GROUPS
+        real Vvdw_ci = 0;
+        real Vc_ci   = 0;
+#    else
+        int        egp_sh_i[UNROLLI];
+        for (int i = 0; i < UNROLLI; i++)
+        {
+            egp_sh_i[i] = ((nbatParams.energrp[ci] >> (i * nbatParams.neg_2log)) & egp_mask)
+                          * nbatParams.nenergrp;
+        }
+#    endif
+#endif
+
+        for (int i = 0; i < UNROLLI; i++)
+        {
+            for (int d = 0; d < DIM; d++)
+            {
+                xi[i * XI_STRIDE + d] = x[(ci * UNROLLI + i) * X_STRIDE + d] + shiftvec[ishf + d];
+                fi[i * FI_STRIDE + d] = 0;
+            }
+
+            qi[i] = facel * q[ci * UNROLLI + i];
+        }
+
+#ifdef CALC_ENERGIES
+        if (do_self)
+        {
+#    ifdef CALC_COUL_RF
+            const real Vc_sub_self = 0.5 * reactionFieldShift;
+#    endif
+#    ifdef CALC_COUL_TAB
+#        if GMX_DOUBLE
+            const real Vc_sub_self = 0.5 * tab_coul_V[0];
+#        else
+            const real Vc_sub_self = 0.5 * tab_coul_FDV0[2];
+#        endif
+#    endif
+
+            if (l_cj[ciEntry.cj_ind_start].cj == ci_sh)
+            {
+                for (int i = 0; i < UNROLLI; i++)
+                {
+#    ifdef ENERGY_GROUPS
+                    const int egp_ind =
+                            egp_sh_i[i] + ((nbatParams.energrp[ci] >> (i * nbatParams.neg_2log)) & egp_mask);
+#    else
+                    const int egp_ind = 0;
+#    endif
+                    /* Coulomb self interaction */
+                    Vc[egp_ind] -= qi[i] * q[ci * UNROLLI + i] * Vc_sub_self;
+
+#    ifdef LJ_EWALD
+                    /* LJ Ewald self interaction */
+                    Vvdw[egp_ind] +=
+                            0.5
+                            * nbatParams.nbfp[nbatParams.type[ci * UNROLLI + i] * (nbatParams.numTypes + 1) * 2]
+                            / 6 * lje_coeff6_6;
+#    endif
+                }
+            }
+        }
+#endif /* CALC_ENERGIES */
+
+        int cjind = cjind0;
+        while (cjind < cjind1 && nbl->cj[cjind].excl != 0xffff)
+        {
+#define CHECK_EXCLS
+            if (half_LJ)
+            {
+#define CALC_COULOMB
+#define HALF_LJ
+#include "kernel_ref_inner.h"
+#undef HALF_LJ
+#undef CALC_COULOMB
+            }
+            else if (do_coul)
+            {
+#define CALC_COULOMB
+#include "kernel_ref_inner.h"
+#undef CALC_COULOMB
+            }
+            else
+            {
+#include "kernel_ref_inner.h"
+            }
+#undef CHECK_EXCLS
+            cjind++;
+        }
+
+        for (; (cjind < cjind1); cjind++)
+        {
+            if (half_LJ)
+            {
+#define CALC_COULOMB
+#define HALF_LJ
+#include "kernel_ref_inner.h"
+#undef HALF_LJ
+#undef CALC_COULOMB
+            }
+            else if (do_coul)
+            {
+#define CALC_COULOMB
+#include "kernel_ref_inner.h"
+#undef CALC_COULOMB
+            }
+            else
+            {
+#include "kernel_ref_inner.h"
+            }
+        }
+
+        /* Add accumulated i-forces to the force array */
+        for (int i = 0; i < UNROLLI; i++)
+        {
+            for (int d = 0; d < DIM; d++)
+            {
+                f[(ci * UNROLLI + i) * F_STRIDE + d] += fi[i * FI_STRIDE + d];
+            }
+        }
+#ifdef CALC_SHIFTFORCES
+        if (fshift != nullptr)
+        {
+            /* Add i forces to shifted force list */
+            for (int i = 0; i < UNROLLI; i++)
+            {
+                for (int d = 0; d < DIM; d++)
+                {
+                    fshift[ishf + d] += fi[i * FI_STRIDE + d];
+                }
+            }
+        }
+#endif
+
+#ifdef CALC_ENERGIES
+#    ifndef ENERGY_GROUPS
+        *Vvdw += Vvdw_ci;
+        *Vc += Vc_ci;
+#    endif
+#endif
+    }
+
+#ifdef COUNT_PAIRS
+    printf("atom pairs %d\n", npair);
+#endif
+}
+
+#undef CALC_SHIFTFORCES
+
+#undef X_STRIDE
+#undef F_STRIDE
+#undef XI_STRIDE
+#undef FI_STRIDE
+
+#undef UNROLLI
+#undef UNROLLJ
diff --git a/src/include/gromacs/nbnxm/kernels_reference/kernel_ref_prune.h b/src/include/gromacs/nbnxm/kernels_reference/kernel_ref_prune.h
new file mode 100644 (file)
index 0000000..d29cc42
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the C reference pruning only kernel.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct nbnxn_atomdata_t;
+struct NbnxnPairlistCpu;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+/*! \brief Prune a single NbnxnPairlistCpu entry with distance \p rlistInner
+ *
+ * Reads a cluster pairlist \p nbl->ciOuter, \p nbl->cjOuter and writes
+ * all cluster pairs within \p rlistInner to \p nbl->ci, \p nbl->cj.
+ */
+void nbnxn_kernel_prune_ref(NbnxnPairlistCpu*              nbl,
+                            const nbnxn_atomdata_t*        nbat,
+                            gmx::ArrayRef<const gmx::RVec> shiftvec,
+                            real                           rlistInner);
diff --git a/src/include/gromacs/nbnxm/kernels_simd_2xmm/kernel_common.h b/src/include/gromacs/nbnxm/kernels_simd_2xmm/kernel_common.h
new file mode 100644 (file)
index 0000000..fff9328
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#include "gromacs/pbcutil/ishift.h"
+#include "gromacs/simd/simd.h"
+#include "gromacs/simd/simd_math.h"
+#include "gromacs/simd/vector_operations.h"
+#ifdef CALC_COUL_EWALD
+#    include "gromacs/math/utilities.h"
+#endif
+
+#include "config.h"
+
+#include <cstdint>
+
+#if !GMX_SIMD_HAVE_HSIMD_UTIL_REAL
+#    error "Half-simd utility operations are required for the 2xNN kernels"
+#endif
+
+#ifndef GMX_SIMD_J_UNROLL_SIZE
+#    error "Need to define GMX_SIMD_J_UNROLL_SIZE before including the 2xnn kernel common header file"
+#endif
+
+#define UNROLLI 4
+#define UNROLLJ (GMX_SIMD_REAL_WIDTH / GMX_SIMD_J_UNROLL_SIZE)
+
+static_assert(UNROLLI == c_nbnxnCpuIClusterSize, "UNROLLI should match the i-cluster size");
+
+/* The stride of all the atom data arrays is equal to half the SIMD width */
+#define STRIDE UNROLLJ
+
+#if !defined GMX_NBNXN_SIMD_2XNN && !defined GMX_NBNXN_SIMD_4XN
+#    error "Must define an NBNxN kernel flavour before including NBNxN kernel utility functions"
+#endif
+
+// We use the FDV0 tables for width==4 (when we can load it in one go), or if we don't have any unaligned loads
+#if GMX_SIMD_REAL_WIDTH == 4 || !GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_REAL
+#    define TAB_FDV0
+#endif
+
+#if defined UNROLLJ
+/* As add_ener_grp, but for two groups of UNROLLJ/2 stored in
+ * a single SIMD register.
+ */
+static inline void add_ener_grp_halves(gmx::SimdReal e_S, real* v0, real* v1, const int* offset_jj)
+{
+    for (int jj = 0; jj < (UNROLLJ / 2); jj++)
+    {
+        incrDualHsimd(v0 + offset_jj[jj] + jj * GMX_SIMD_REAL_WIDTH / 2,
+                      v1 + offset_jj[jj] + jj * GMX_SIMD_REAL_WIDTH / 2,
+                      e_S);
+    }
+}
+#endif
+
+#if GMX_SIMD_HAVE_INT32_LOGICAL
+typedef gmx::SimdInt32 SimdBitMask;
+#else
+typedef gmx::SimdReal SimdBitMask;
+#endif
+
+
+static inline void gmx_simdcall gmx_load_simd_2xnn_interactions(int            excl,
+                                                                SimdBitMask    filter_S0,
+                                                                SimdBitMask    filter_S2,
+                                                                gmx::SimdBool* interact_S0,
+                                                                gmx::SimdBool* interact_S2)
+{
+    using namespace gmx;
+#if GMX_SIMD_HAVE_INT32_LOGICAL
+    SimdInt32 mask_pr_S(excl);
+    *interact_S0 = cvtIB2B(testBits(mask_pr_S & filter_S0));
+    *interact_S2 = cvtIB2B(testBits(mask_pr_S & filter_S2));
+#elif GMX_SIMD_HAVE_LOGICAL
+    union
+    {
+#    if GMX_DOUBLE
+        std::int64_t i;
+#    else
+        std::int32_t i;
+#    endif
+        real         r;
+    } conv;
+
+    conv.i = excl;
+    SimdReal mask_pr_S(conv.r);
+
+    *interact_S0 = testBits(mask_pr_S & filter_S0);
+    *interact_S2 = testBits(mask_pr_S & filter_S2);
+#endif
+}
+
+/* All functionality defines are set here, except for:
+ * CALC_ENERGIES, ENERGY_GROUPS which are defined before.
+ * CHECK_EXCLS, which is set just before including the inner loop contents.
+ * The combination rule defines, LJ_COMB_GEOM or LJ_COMB_LB are currently
+ * set before calling the kernel function. We might want to move that
+ * to inside the n-loop and have a different combination rule for different
+ * ci's, as no combination rule gives a 50% performance hit for LJ.
+ */
+
+/* We always calculate shift forces, because it's cheap anyhow */
+#define CALC_SHIFTFORCES
+
+/* Assumes all LJ parameters are identical */
+/* #define FIX_LJ_C */
diff --git a/src/include/gromacs/nbnxm/kernels_simd_2xmm/kernel_inner.h b/src/include/gromacs/nbnxm/kernels_simd_2xmm/kernel_inner.h
new file mode 100644 (file)
index 0000000..6d55bbe
--- /dev/null
@@ -0,0 +1,904 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/* This is the innermost loop contents for the 4 x N atom simd kernel.
+ * This flavor of the kernel duplicates the data for N j-particles in
+ * 2xN wide simd registers to do operate on 2 i-particles at once.
+ * This leads to 4/2=2 sets of most instructions. Therefore we call
+ * this kernel 2x(N+N) = 2xnn
+ *
+ * This 2xnn kernel is basically the 4xn equivalent with half the registers
+ * and instructions removed.
+ *
+ * An alternative would be to load to different cluster of N j-particles
+ * into simd registers, giving a 4x(N+N) kernel. This doubles the amount
+ * of instructions, which could lead to better scheduling. But we actually
+ * observed worse scheduling for the AVX-256 4x8 normal analytical PME
+ * kernel, which has a lower pair throughput than 2x(4+4) with gcc 4.7.
+ * It could be worth trying this option, but it takes some more effort.
+ * This 2xnn kernel is basically the 4xn equivalent with
+ */
+
+
+/* When calculating RF or Ewald interactions we calculate the electrostatic/LJ
+ * forces on excluded atom pairs here in the non-bonded loops.
+ * But when energies and/or virial is required we calculate them
+ * separately to as then it is easier to separate the energy and virial
+ * contributions.
+ */
+#if defined CHECK_EXCLS && (defined CALC_COULOMB || defined LJ_EWALD_GEOM)
+#    define EXCL_FORCES
+#endif
+
+{
+#ifdef ENERGY_GROUPS
+    /* Energy group indices for two atoms packed into one int */
+    int egp_jj[UNROLLJ / 2];
+#endif
+
+#ifdef CHECK_EXCLS
+    /* Interaction (non-exclusion) mask of all 1's or 0's */
+    SimdBool interact_S0;
+    SimdBool interact_S2;
+#endif
+
+    SimdReal jx_S, jy_S, jz_S;
+    SimdReal dx_S0, dy_S0, dz_S0;
+    SimdReal dx_S2, dy_S2, dz_S2;
+    SimdReal tx_S0, ty_S0, tz_S0;
+    SimdReal tx_S2, ty_S2, tz_S2;
+    SimdReal rsq_S0, rinv_S0, rinvsq_S0;
+    SimdReal rsq_S2, rinv_S2, rinvsq_S2;
+    /* wco: within cut-off, mask of all 1's or 0's */
+    SimdBool wco_S0;
+    SimdBool wco_S2;
+#ifdef VDW_CUTOFF_CHECK
+    SimdBool wco_vdw_S0;
+#    ifndef HALF_LJ
+    SimdBool wco_vdw_S2;
+#    endif
+#endif
+
+#if (defined CALC_COULOMB && defined CALC_COUL_TAB) || defined LJ_FORCE_SWITCH || defined LJ_POT_SWITCH
+    SimdReal                                                        r_S0;
+#    if (defined CALC_COULOMB && defined CALC_COUL_TAB) || !defined HALF_LJ
+    SimdReal                                                        r_S2;
+#    endif
+#endif
+
+#if defined LJ_FORCE_SWITCH || defined LJ_POT_SWITCH
+    SimdReal rsw_S0, rsw2_S0;
+#    ifndef HALF_LJ
+    SimdReal rsw_S2, rsw2_S2;
+#    endif
+#endif
+
+#ifdef CALC_COULOMB
+#    ifdef CHECK_EXCLS
+    /* 1/r masked with the interaction mask */
+    SimdReal rinv_ex_S0;
+    SimdReal rinv_ex_S2;
+#    endif
+    SimdReal jq_S;
+    SimdReal qq_S0;
+    SimdReal qq_S2;
+#    ifdef CALC_COUL_TAB
+    /* The force (PME mesh force) we need to subtract from 1/r^2 */
+    SimdReal fsub_S0;
+    SimdReal fsub_S2;
+#    endif
+#    ifdef CALC_COUL_EWALD
+    SimdReal brsq_S0, brsq_S2;
+    SimdReal ewcorr_S0, ewcorr_S2;
+#    endif
+
+    /* frcoul = (1/r - fsub)*r */
+    SimdReal frcoul_S0;
+    SimdReal frcoul_S2;
+#    ifdef CALC_COUL_TAB
+    /* For tables: r, rs=r/sp, rf=floor(rs), frac=rs-rf */
+    SimdReal rs_S0, rf_S0, frac_S0;
+    SimdReal rs_S2, rf_S2, frac_S2;
+    /* Table index: rs truncated to an int */
+    SimdInt32 ti_S0, ti_S2;
+    /* Linear force table values */
+    SimdReal ctab0_S0, ctab1_S0;
+    SimdReal ctab0_S2, ctab1_S2;
+#        ifdef CALC_ENERGIES
+    /* Quadratic energy table value */
+    SimdReal ctabv_S0, dum_S0;
+    SimdReal ctabv_S2, dum_S2;
+#        endif
+#    endif
+#    if defined CALC_ENERGIES && (defined CALC_COUL_EWALD || defined CALC_COUL_TAB)
+    /* The potential (PME mesh) we need to subtract from 1/r */
+    SimdReal vc_sub_S0;
+    SimdReal vc_sub_S2;
+#    endif
+#    ifdef CALC_ENERGIES
+    /* Electrostatic potential */
+    SimdReal vcoul_S0;
+    SimdReal vcoul_S2;
+#    endif
+#endif
+    /* The force times 1/r */
+    SimdReal fscal_S0;
+    SimdReal fscal_S2;
+
+#ifdef CALC_LJ
+#    ifdef LJ_COMB_LB
+    /* LJ sigma_j/2 and sqrt(epsilon_j) */
+    SimdReal hsig_j_S, seps_j_S;
+    /* LJ sigma_ij and epsilon_ij */
+    SimdReal sig_S0, eps_S0;
+#        ifndef HALF_LJ
+    SimdReal sig_S2, eps_S2;
+#        endif
+#        ifdef CALC_ENERGIES
+    SimdReal sig2_S0, sig6_S0;
+#            ifndef HALF_LJ
+    SimdReal sig2_S2, sig6_S2;
+#            endif
+#        endif /* LJ_COMB_LB */
+#    endif     /* CALC_LJ */
+
+#    ifdef LJ_COMB_GEOM
+    SimdReal c6s_j_S, c12s_j_S;
+#    endif
+
+    /* Intermediate variables for LJ calculation */
+#    ifndef LJ_COMB_LB
+    SimdReal rinvsix_S0;
+#        ifndef HALF_LJ
+    SimdReal rinvsix_S2;
+#        endif
+#    endif
+#    ifdef LJ_COMB_LB
+    SimdReal sir_S0, sir2_S0, sir6_S0;
+#        ifndef HALF_LJ
+    SimdReal sir_S2, sir2_S2, sir6_S2;
+#        endif
+#    endif
+
+    SimdReal FrLJ6_S0, FrLJ12_S0, frLJ_S0;
+#    ifndef HALF_LJ
+    SimdReal FrLJ6_S2, FrLJ12_S2, frLJ_S2;
+#    endif
+#endif /* CALC_LJ */
+
+    /* j-cluster index */
+    const int cj = l_cj[cjind].cj;
+
+    /* Atom indices (of the first atom in the cluster) */
+    const int aj = cj * UNROLLJ;
+#if defined CALC_LJ && (defined LJ_COMB_GEOM || defined LJ_COMB_LB || defined LJ_EWALD_GEOM)
+    /* Index for loading LJ parameters, complicated when interleaving */
+    const int aj2 = aj * 2;
+#endif
+    const int ajx = aj * DIM;
+    const int ajy = ajx + STRIDE;
+    const int ajz = ajy + STRIDE;
+
+#ifdef CHECK_EXCLS
+    gmx_load_simd_2xnn_interactions(
+            static_cast<int>(l_cj[cjind].excl), filter_S0, filter_S2, &interact_S0, &interact_S2);
+#endif /* CHECK_EXCLS */
+
+    /* load j atom coordinates */
+    jx_S = loadDuplicateHsimd(x + ajx);
+    jy_S = loadDuplicateHsimd(x + ajy);
+    jz_S = loadDuplicateHsimd(x + ajz);
+
+    /* Calculate distance */
+    dx_S0 = ix_S0 - jx_S;
+    dy_S0 = iy_S0 - jy_S;
+    dz_S0 = iz_S0 - jz_S;
+    dx_S2 = ix_S2 - jx_S;
+    dy_S2 = iy_S2 - jy_S;
+    dz_S2 = iz_S2 - jz_S;
+
+    /* rsq = dx*dx+dy*dy+dz*dz */
+    rsq_S0 = norm2(dx_S0, dy_S0, dz_S0);
+    rsq_S2 = norm2(dx_S2, dy_S2, dz_S2);
+
+    /* Do the cut-off check */
+    wco_S0 = (rsq_S0 < rc2_S);
+    wco_S2 = (rsq_S2 < rc2_S);
+
+#ifdef CHECK_EXCLS
+#    ifdef EXCL_FORCES
+    /* Only remove the (sub-)diagonal to avoid double counting */
+#        if UNROLLJ == UNROLLI
+    if (cj == ci_sh)
+    {
+        wco_S0 = wco_S0 && diagonal_mask_S0;
+        wco_S2 = wco_S2 && diagonal_mask_S2;
+    }
+#        else
+#            if UNROLLJ == 2 * UNROLLI
+    if (cj * 2 == ci_sh)
+    {
+        wco_S0 = wco_S0 && diagonal_mask0_S0;
+        wco_S2 = wco_S2 && diagonal_mask0_S2;
+    }
+    else if (cj * 2 + 1 == ci_sh)
+    {
+        wco_S0 = wco_S0 && diagonal_mask1_S0;
+        wco_S2 = wco_S2 && diagonal_mask1_S2;
+    }
+#            else
+#                error "only UNROLLJ == UNROLLI*(1 or 2) currently supported in 2xnn kernels"
+#            endif
+#        endif
+#    else /* EXCL_FORCES */
+    /* No exclusion forces: remove all excluded atom pairs from the list */
+    wco_S0 = wco_S0 && interact_S0;
+    wco_S2 = wco_S2 && interact_S2;
+#    endif
+#endif
+
+#ifdef COUNT_PAIRS
+    {
+        int                              i, j;
+        alignas(GMX_SIMD_ALIGNMENT) real tmp[GMX_SIMD_REAL_WIDTH];
+
+        for (i = 0; i < UNROLLI; i += 2)
+        {
+            store(tmp, rc2_S - (i == 0 ? rsq_S0 : rsq_S2));
+            for (j = 0; j < 2 * UNROLLJ; j++)
+            {
+                if (tmp[j] >= 0)
+                {
+                    npair++;
+                }
+            }
+        }
+    }
+#endif
+
+    // Ensure the distances do not fall below the limit where r^-12 overflows.
+    // This should never happen for normal interactions.
+    rsq_S0 = max(rsq_S0, minRsq_S);
+    rsq_S2 = max(rsq_S2, minRsq_S);
+
+    /* Calculate 1/r */
+    rinv_S0 = invsqrt(rsq_S0);
+    rinv_S2 = invsqrt(rsq_S2);
+
+#ifdef CALC_COULOMB
+    /* Load parameters for j atom */
+    jq_S  = loadDuplicateHsimd(q + aj);
+    qq_S0 = iq_S0 * jq_S;
+    qq_S2 = iq_S2 * jq_S;
+#endif
+
+#ifdef CALC_LJ
+#    if !defined LJ_COMB_GEOM && !defined LJ_COMB_LB && !defined FIX_LJ_C
+    SimdReal                                                     c6_S0, c12_S0;
+    gatherLoadTransposeHsimd<c_simdBestPairAlignment>(nbfp0, nbfp1, type + aj, &c6_S0, &c12_S0);
+#        ifndef HALF_LJ
+    SimdReal c6_S2, c12_S2;
+    gatherLoadTransposeHsimd<c_simdBestPairAlignment>(nbfp2, nbfp3, type + aj, &c6_S2, &c12_S2);
+#        endif
+#    endif /* not defined any LJ rule */
+
+#    ifdef LJ_COMB_GEOM
+    c6s_j_S        = loadDuplicateHsimd(ljc + aj2);
+    c12s_j_S       = loadDuplicateHsimd(ljc + aj2 + STRIDE);
+    SimdReal c6_S0 = c6s_S0 * c6s_j_S;
+#        ifndef HALF_LJ
+    SimdReal c6_S2 = c6s_S2 * c6s_j_S;
+#        endif
+    SimdReal c12_S0 = c12s_S0 * c12s_j_S;
+#        ifndef HALF_LJ
+    SimdReal c12_S2 = c12s_S2 * c12s_j_S;
+#        endif
+#    endif /* LJ_COMB_GEOM */
+
+#    ifdef LJ_COMB_LB
+    hsig_j_S = loadDuplicateHsimd(ljc + aj2);
+    seps_j_S = loadDuplicateHsimd(ljc + aj2 + STRIDE);
+
+    sig_S0 = hsig_i_S0 + hsig_j_S;
+    eps_S0 = seps_i_S0 * seps_j_S;
+#        ifndef HALF_LJ
+    sig_S2 = hsig_i_S2 + hsig_j_S;
+    eps_S2 = seps_i_S2 * seps_j_S;
+#        endif
+#    endif /* LJ_COMB_LB */
+
+#endif /* CALC_LJ */
+
+    /* Set rinv to zero for r beyond the cut-off */
+    rinv_S0 = selectByMask(rinv_S0, wco_S0);
+    rinv_S2 = selectByMask(rinv_S2, wco_S2);
+
+    rinvsq_S0 = rinv_S0 * rinv_S0;
+    rinvsq_S2 = rinv_S2 * rinv_S2;
+
+#ifdef CALC_COULOMB
+    /* Note that here we calculate force*r, not the usual force/r.
+     * This allows avoiding masking the reaction-field contribution,
+     * as frcoul is later multiplied by rinvsq which has been
+     * masked with the cut-off check.
+     */
+
+#    ifdef EXCL_FORCES
+    /* Only add 1/r for non-excluded atom pairs */
+    rinv_ex_S0 = selectByMask(rinv_S0, interact_S0);
+    rinv_ex_S2 = selectByMask(rinv_S2, interact_S2);
+#    else
+    /* No exclusion forces, we always need 1/r */
+#        define rinv_ex_S0 rinv_S0
+#        define rinv_ex_S2 rinv_S2
+#    endif
+
+#    ifdef CALC_COUL_RF
+    /* Electrostatic interactions */
+    frcoul_S0 = qq_S0 * fma(rsq_S0, mrc_3_S, rinv_ex_S0);
+    frcoul_S2 = qq_S2 * fma(rsq_S2, mrc_3_S, rinv_ex_S2);
+
+#        ifdef CALC_ENERGIES
+    vcoul_S0 = qq_S0 * (rinv_ex_S0 + fma(rsq_S0, hrc_3_S, moh_rc_S));
+    vcoul_S2 = qq_S2 * (rinv_ex_S2 + fma(rsq_S2, hrc_3_S, moh_rc_S));
+#        endif
+#    endif
+
+#    ifdef CALC_COUL_EWALD
+    /* We need to mask (or limit) rsq for the cut-off,
+     * as large distances can cause an overflow in gmx_pmecorrF/V.
+     */
+    brsq_S0   = beta2_S * selectByMask(rsq_S0, wco_S0);
+    brsq_S2   = beta2_S * selectByMask(rsq_S2, wco_S2);
+    ewcorr_S0 = beta_S * pmeForceCorrection(brsq_S0);
+    ewcorr_S2 = beta_S * pmeForceCorrection(brsq_S2);
+    frcoul_S0 = qq_S0 * fma(ewcorr_S0, brsq_S0, rinv_ex_S0);
+    frcoul_S2 = qq_S2 * fma(ewcorr_S2, brsq_S2, rinv_ex_S2);
+
+#        ifdef CALC_ENERGIES
+    vc_sub_S0 = beta_S * pmePotentialCorrection(brsq_S0);
+    vc_sub_S2 = beta_S * pmePotentialCorrection(brsq_S2);
+#        endif
+
+#    endif /* CALC_COUL_EWALD */
+
+#    ifdef CALC_COUL_TAB
+    /* Electrostatic interactions */
+    r_S0 = rsq_S0 * rinv_S0;
+    r_S2 = rsq_S2 * rinv_S2;
+    /* Convert r to scaled table units */
+    rs_S0 = r_S0 * invtsp_S;
+    rs_S2 = r_S2 * invtsp_S;
+    /* Truncate scaled r to an int */
+    ti_S0 = cvttR2I(rs_S0);
+    ti_S2 = cvttR2I(rs_S2);
+
+    rf_S0 = trunc(rs_S0);
+    rf_S2 = trunc(rs_S2);
+
+    frac_S0 = rs_S0 - rf_S0;
+    frac_S2 = rs_S2 - rf_S2;
+
+    /* Load and interpolate table forces and possibly energies.
+     * Force and energy can be combined in one table, stride 4: FDV0
+     * or in two separate tables with stride 1: F and V
+     * Currently single precision uses FDV0, double F and V.
+     */
+#        ifndef CALC_ENERGIES
+#            ifdef TAB_FDV0
+    gatherLoadBySimdIntTranspose<4>(tab_coul_F, ti_S0, &ctab0_S0, &ctab1_S0);
+    gatherLoadBySimdIntTranspose<4>(tab_coul_F, ti_S2, &ctab0_S2, &ctab1_S2);
+#            else
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_F, ti_S0, &ctab0_S0, &ctab1_S0);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_F, ti_S2, &ctab0_S2, &ctab1_S2);
+    ctab1_S0  = ctab1_S0 - ctab0_S0;
+    ctab1_S2  = ctab1_S2 - ctab0_S2;
+#            endif
+#        else
+#            ifdef TAB_FDV0
+    gatherLoadBySimdIntTranspose<4>(tab_coul_F, ti_S0, &ctab0_S0, &ctab1_S0, &ctabv_S0, &dum_S0);
+    gatherLoadBySimdIntTranspose<4>(tab_coul_F, ti_S2, &ctab0_S2, &ctab1_S2, &ctabv_S2, &dum_S2);
+#            else
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_F, ti_S0, &ctab0_S0, &ctab1_S0);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_V, ti_S0, &ctabv_S0, &dum_S0);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_F, ti_S2, &ctab0_S2, &ctab1_S2);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_V, ti_S2, &ctabv_S2, &dum_S2);
+    ctab1_S0 = ctab1_S0 - ctab0_S0;
+    ctab1_S2 = ctab1_S2 - ctab0_S2;
+#            endif
+#        endif
+    fsub_S0   = fma(frac_S0, ctab1_S0, ctab0_S0);
+    fsub_S2   = fma(frac_S2, ctab1_S2, ctab0_S2);
+    frcoul_S0 = qq_S0 * fnma(fsub_S0, r_S0, rinv_ex_S0);
+    frcoul_S2 = qq_S2 * fnma(fsub_S2, r_S2, rinv_ex_S2);
+
+#        ifdef CALC_ENERGIES
+    vc_sub_S0 = fma((mhalfsp_S * frac_S0), (ctab0_S0 + fsub_S0), ctabv_S0);
+    vc_sub_S2 = fma((mhalfsp_S * frac_S2), (ctab0_S2 + fsub_S2), ctabv_S2);
+#        endif
+#    endif /* CALC_COUL_TAB */
+
+#    if defined CALC_ENERGIES && (defined CALC_COUL_EWALD || defined CALC_COUL_TAB)
+#        ifndef NO_SHIFT_EWALD
+    /* Add Ewald potential shift to vc_sub for convenience */
+#            ifdef CHECK_EXCLS
+    vc_sub_S0 = vc_sub_S0 + selectByMask(sh_ewald_S, interact_S0);
+    vc_sub_S2 = vc_sub_S2 + selectByMask(sh_ewald_S, interact_S2);
+#            else
+    vc_sub_S0 = vc_sub_S0 + sh_ewald_S;
+    vc_sub_S2 = vc_sub_S2 + sh_ewald_S;
+#            endif
+#        endif
+
+    vcoul_S0 = qq_S0 * (rinv_ex_S0 - vc_sub_S0);
+    vcoul_S2 = qq_S2 * (rinv_ex_S2 - vc_sub_S2);
+
+#    endif
+
+#    ifdef CALC_ENERGIES
+    /* Mask energy for cut-off and diagonal */
+    vcoul_S0 = selectByMask(vcoul_S0, wco_S0);
+    vcoul_S2 = selectByMask(vcoul_S2, wco_S2);
+#    endif
+
+#endif /* CALC_COULOMB */
+
+#ifdef CALC_LJ
+    /* Lennard-Jones interaction */
+
+#    ifdef VDW_CUTOFF_CHECK
+    wco_vdw_S0 = (rsq_S0 < rcvdw2_S);
+#        ifndef HALF_LJ
+    wco_vdw_S2 = (rsq_S2 < rcvdw2_S);
+#        endif
+#    else
+    /* Same cut-off for Coulomb and VdW, reuse the registers */
+#        define wco_vdw_S0 wco_S0
+#        define wco_vdw_S2 wco_S2
+#    endif
+
+#    ifndef LJ_COMB_LB
+    rinvsix_S0 = rinvsq_S0 * rinvsq_S0 * rinvsq_S0;
+#        ifdef EXCL_FORCES
+    rinvsix_S0 = selectByMask(rinvsix_S0, interact_S0);
+#        endif
+#        ifndef HALF_LJ
+    rinvsix_S2 = rinvsq_S2 * rinvsq_S2 * rinvsq_S2;
+#            ifdef EXCL_FORCES
+    rinvsix_S2 = selectByMask(rinvsix_S2, interact_S2);
+#            endif
+#        endif
+
+#        if defined LJ_CUT || defined LJ_POT_SWITCH
+    /* We have plain LJ or LJ-PME with simple C6/6 C12/12 coefficients */
+    FrLJ6_S0 = c6_S0 * rinvsix_S0;
+#            ifndef HALF_LJ
+    FrLJ6_S2 = c6_S2 * rinvsix_S2;
+#            endif
+    FrLJ12_S0 = c12_S0 * rinvsix_S0 * rinvsix_S0;
+#            ifndef HALF_LJ
+    FrLJ12_S2 = c12_S2 * rinvsix_S2 * rinvsix_S2;
+#            endif
+#        endif
+
+#        if defined LJ_FORCE_SWITCH || defined LJ_POT_SWITCH
+    /* We switch the LJ force */
+    r_S0    = rsq_S0 * rinv_S0;
+    rsw_S0  = max(r_S0 - rswitch_S, zero_S);
+    rsw2_S0 = rsw_S0 * rsw_S0;
+#            ifndef HALF_LJ
+    r_S2    = rsq_S2 * rinv_S2;
+    rsw_S2  = max(r_S2 - rswitch_S, zero_S);
+    rsw2_S2 = rsw_S2 * rsw_S2;
+#            endif
+#        endif
+
+#        ifdef LJ_FORCE_SWITCH
+
+#            define add_fr_switch(fr, rsw, rsw2_r, c2, c3) fma(fma(c3, rsw, c2), rsw2_r, fr)
+    SimdReal rsw2_r_S0 = rsw2_S0 * r_S0;
+    FrLJ6_S0           = c6_S0 * add_fr_switch(rinvsix_S0, rsw_S0, rsw2_r_S0, p6_fc2_S, p6_fc3_S);
+#            ifndef HALF_LJ
+    SimdReal rsw2_r_S2 = rsw2_S2 * r_S2;
+    FrLJ6_S2           = c6_S2 * add_fr_switch(rinvsix_S2, rsw_S2, rsw2_r_S2, p6_fc2_S, p6_fc3_S);
+#            endif
+    FrLJ12_S0 = c12_S0 * add_fr_switch(rinvsix_S0 * rinvsix_S0, rsw_S0, rsw2_r_S0, p12_fc2_S, p12_fc3_S);
+#            ifndef HALF_LJ
+    FrLJ12_S2 = c12_S2 * add_fr_switch(rinvsix_S2 * rinvsix_S2, rsw_S2, rsw2_r_S2, p12_fc2_S, p12_fc3_S);
+#            endif
+#            undef add_fr_switch
+#        endif /* LJ_FORCE_SWITCH */
+
+#    endif /* not LJ_COMB_LB */
+
+#    ifdef LJ_COMB_LB
+    sir_S0 = sig_S0 * rinv_S0;
+#        ifndef HALF_LJ
+    sir_S2 = sig_S2 * rinv_S2;
+#        endif
+    sir2_S0 = sir_S0 * sir_S0;
+#        ifndef HALF_LJ
+    sir2_S2 = sir_S2 * sir_S2;
+#        endif
+    sir6_S0 = sir2_S0 * sir2_S0 * sir2_S0;
+#        ifdef EXCL_FORCES
+    sir6_S0 = selectByMask(sir6_S0, interact_S0);
+#        endif
+#        ifndef HALF_LJ
+    sir6_S2 = sir2_S2 * sir2_S2 * sir2_S2;
+#            ifdef EXCL_FORCES
+    sir6_S2 = selectByMask(sir6_S2, interact_S2);
+#            endif
+#        endif
+#        ifdef VDW_CUTOFF_CHECK
+    sir6_S0 = selectByMask(sir6_S0, wco_vdw_S0);
+#            ifndef HALF_LJ
+    sir6_S2 = selectByMask(sir6_S2, wco_vdw_S2);
+#            endif
+#        endif
+    FrLJ6_S0 = eps_S0 * sir6_S0;
+#        ifndef HALF_LJ
+    FrLJ6_S2 = eps_S2 * sir6_S2;
+#        endif
+    FrLJ12_S0 = FrLJ6_S0 * sir6_S0;
+#        ifndef HALF_LJ
+    FrLJ12_S2 = FrLJ6_S2 * sir6_S2;
+#        endif
+#        if defined CALC_ENERGIES
+    /* We need C6 and C12 to calculate the LJ potential shift */
+    sig2_S0 = sig_S0 * sig_S0;
+#            ifndef HALF_LJ
+    sig2_S2 = sig_S2 * sig_S2;
+#            endif
+    sig6_S0 = sig2_S0 * sig2_S0 * sig2_S0;
+#            ifndef HALF_LJ
+    sig6_S2 = sig2_S2 * sig2_S2 * sig2_S2;
+#            endif
+    SimdReal c6_S0 = eps_S0 * sig6_S0;
+#            ifndef HALF_LJ
+    SimdReal c6_S2 = eps_S2 * sig6_S2;
+#            endif
+    SimdReal c12_S0 = c6_S0 * sig6_S0;
+#            ifndef HALF_LJ
+    SimdReal c12_S2 = c6_S2 * sig6_S2;
+#            endif
+#        endif
+#    endif /* LJ_COMB_LB */
+
+    /* Determine the total scalar LJ force*r */
+    frLJ_S0 = FrLJ12_S0 - FrLJ6_S0;
+#    ifndef HALF_LJ
+    frLJ_S2 = FrLJ12_S2 - FrLJ6_S2;
+#    endif
+
+#    if (defined LJ_CUT || defined LJ_FORCE_SWITCH) && defined CALC_ENERGIES
+
+#        ifdef LJ_CUT
+    /* Calculate the LJ energies, with constant potential shift */
+    SimdReal VLJ6_S0 = sixth_S * fma(c6_S0, p6_cpot_S, FrLJ6_S0);
+#            ifndef HALF_LJ
+    SimdReal VLJ6_S2 = sixth_S * fma(c6_S2, p6_cpot_S, FrLJ6_S2);
+#            endif
+    SimdReal VLJ12_S0 = twelveth_S * fma(c12_S0, p12_cpot_S, FrLJ12_S0);
+#            ifndef HALF_LJ
+    SimdReal VLJ12_S2 = twelveth_S * fma(c12_S2, p12_cpot_S, FrLJ12_S2);
+#            endif
+#        endif /* LJ_CUT */
+
+#        ifdef LJ_FORCE_SWITCH
+#            define v_fswitch_pr(rsw, rsw2, c0, c3, c4) fma(fma(c4, rsw, c3), (rsw2) * (rsw), c0)
+
+    SimdReal VLJ6_S0 =
+            c6_S0 * fma(sixth_S, rinvsix_S0, v_fswitch_pr(rsw_S0, rsw2_S0, p6_6cpot_S, p6_vc3_S, p6_vc4_S));
+#            ifndef HALF_LJ
+    SimdReal VLJ6_S2 =
+            c6_S2 * fma(sixth_S, rinvsix_S2, v_fswitch_pr(rsw_S2, rsw2_S2, p6_6cpot_S, p6_vc3_S, p6_vc4_S));
+#            endif
+    SimdReal VLJ12_S0 = c12_S0
+                        * fma(twelveth_S,
+                              rinvsix_S0 * rinvsix_S0,
+                              v_fswitch_pr(rsw_S0, rsw2_S0, p12_12cpot_S, p12_vc3_S, p12_vc4_S));
+#            ifndef HALF_LJ
+    SimdReal VLJ12_S2 = c12_S2
+                        * fma(twelveth_S,
+                              rinvsix_S2 * rinvsix_S2,
+                              v_fswitch_pr(rsw_S2, rsw2_S2, p12_12cpot_S, p12_vc3_S, p12_vc4_S));
+#            endif
+#            undef v_fswitch_pr
+#        endif /* LJ_FORCE_SWITCH */
+
+    /* Add up the repulsion and dispersion */
+    SimdReal VLJ_S0 = VLJ12_S0 - VLJ6_S0;
+#        ifndef HALF_LJ
+    SimdReal VLJ_S2 = VLJ12_S2 - VLJ6_S2;
+#        endif
+
+#    endif /* (LJ_CUT || LJ_FORCE_SWITCH) && CALC_ENERGIES */
+
+#    ifdef LJ_POT_SWITCH
+    /* We always need the potential, since it is needed for the force */
+    SimdReal VLJ_S0 = fnma(sixth_S, FrLJ6_S0, twelveth_S * FrLJ12_S0);
+#        ifndef HALF_LJ
+    SimdReal VLJ_S2 = fnma(sixth_S, FrLJ6_S2, twelveth_S * FrLJ12_S2);
+#        endif
+
+    {
+        SimdReal sw_S0, dsw_S0;
+#        ifndef HALF_LJ
+        SimdReal sw_S2, dsw_S2;
+#        endif
+
+#        define switch_pr(rsw, rsw2, c3, c4, c5) \
+            fma(fma(fma(c5, rsw, c4), rsw, c3), (rsw2) * (rsw), one_S)
+#        define dswitch_pr(rsw, rsw2, c2, c3, c4) fma(fma(c4, rsw, c3), rsw, c2) * (rsw2)
+
+        sw_S0  = switch_pr(rsw_S0, rsw2_S0, swV3_S, swV4_S, swV5_S);
+        dsw_S0 = dswitch_pr(rsw_S0, rsw2_S0, swF2_S, swF3_S, swF4_S);
+#        ifndef HALF_LJ
+        sw_S2  = switch_pr(rsw_S2, rsw2_S2, swV3_S, swV4_S, swV5_S);
+        dsw_S2 = dswitch_pr(rsw_S2, rsw2_S2, swF2_S, swF3_S, swF4_S);
+#        endif
+        frLJ_S0 = fnma(dsw_S0 * VLJ_S0, r_S0, sw_S0 * frLJ_S0);
+#        ifndef HALF_LJ
+        frLJ_S2 = fnma(dsw_S2 * VLJ_S2, r_S2, sw_S2 * frLJ_S2);
+#        endif
+#        ifdef CALC_ENERGIES
+        VLJ_S0 = sw_S0 * VLJ_S0;
+#            ifndef HALF_LJ
+        VLJ_S2 = sw_S2 * VLJ_S2;
+#            endif
+#        endif
+
+#        undef switch_pr
+#        undef dswitch_pr
+    }
+#    endif /* LJ_POT_SWITCH */
+
+#    if defined CALC_ENERGIES && defined CHECK_EXCLS
+    /* The potential shift should be removed for excluded pairs */
+    VLJ_S0 = selectByMask(VLJ_S0, interact_S0);
+#        ifndef HALF_LJ
+    VLJ_S2 = selectByMask(VLJ_S2, interact_S2);
+#        endif
+#    endif
+
+#    ifdef LJ_EWALD_GEOM
+    {
+        SimdReal c6s_j_S;
+        SimdReal c6grid_S0, rinvsix_nm_S0, cr2_S0, expmcr2_S0, poly_S0;
+#        ifndef HALF_LJ
+        SimdReal c6grid_S2, rinvsix_nm_S2, cr2_S2, expmcr2_S2, poly_S2;
+#        endif
+#        ifdef CALC_ENERGIES
+        SimdReal sh_mask_S0;
+#            ifndef HALF_LJ
+        SimdReal sh_mask_S2;
+#            endif
+#        endif
+
+        /* Determine C6 for the grid using the geometric combination rule */
+        c6s_j_S   = loadDuplicateHsimd(ljc + aj2);
+        c6grid_S0 = c6s_S0 * c6s_j_S;
+#        ifndef HALF_LJ
+        c6grid_S2 = c6s_S2 * c6s_j_S;
+#        endif
+
+#        ifdef CHECK_EXCLS
+        /* Recalculate rinvsix without exclusion mask (compiler might optimize) */
+        rinvsix_nm_S0 = rinvsq_S0 * rinvsq_S0 * rinvsq_S0;
+#            ifndef HALF_LJ
+        rinvsix_nm_S2 = rinvsq_S2 * rinvsq_S2 * rinvsq_S2;
+#            endif
+#        else
+        /* We didn't use a mask, so we can copy */
+        rinvsix_nm_S0 = rinvsix_S0;
+#            ifndef HALF_LJ
+        rinvsix_nm_S2 = rinvsix_S2;
+#            endif
+#        endif
+
+        /* Mask for the cut-off to avoid overflow of cr2^2 */
+        cr2_S0 = lje_c2_S * selectByMask(rsq_S0, wco_vdw_S0);
+#        ifndef HALF_LJ
+        cr2_S2 = lje_c2_S * selectByMask(rsq_S2, wco_vdw_S2);
+#        endif
+        // Unsafe version of our exp() should be fine, since these arguments should never
+        // be smaller than -127 for any reasonable choice of cutoff or ewald coefficients.
+        expmcr2_S0 = exp<MathOptimization::Unsafe>(-cr2_S0);
+#        ifndef HALF_LJ
+        expmcr2_S2 = exp<MathOptimization::Unsafe>(-cr2_S2);
+#        endif
+
+        /* 1 + cr2 + 1/2*cr2^2 */
+        poly_S0 = fma(fma(half_S, cr2_S0, one_S), cr2_S0, one_S);
+#        ifndef HALF_LJ
+        poly_S2 = fma(fma(half_S, cr2_S2, one_S), cr2_S2, one_S);
+#        endif
+
+        /* We calculate LJ F*r = (6*C6)*(r^-6 - F_mesh/6), we use:
+         * r^-6*cexp*(1 + cr2 + cr2^2/2 + cr2^3/6) = cexp*(r^-6*poly + c^6/6)
+         */
+        frLJ_S0 = fma(c6grid_S0,
+                      fnma(expmcr2_S0, fma(rinvsix_nm_S0, poly_S0, lje_c6_6_S), rinvsix_nm_S0),
+                      frLJ_S0);
+#        ifndef HALF_LJ
+        frLJ_S2 = fma(c6grid_S2,
+                      fnma(expmcr2_S2, fma(rinvsix_nm_S2, poly_S2, lje_c6_6_S), rinvsix_nm_S2),
+                      frLJ_S2);
+#        endif
+
+#        ifdef CALC_ENERGIES
+#            ifdef CHECK_EXCLS
+        sh_mask_S0 = selectByMask(lje_vc_S, interact_S0);
+#                ifndef HALF_LJ
+        sh_mask_S2 = selectByMask(lje_vc_S, interact_S2);
+#                endif
+#            else
+        sh_mask_S0 = lje_vc_S;
+#                ifndef HALF_LJ
+        sh_mask_S2 = lje_vc_S;
+#                endif
+#            endif
+
+        VLJ_S0 = fma(sixth_S * c6grid_S0,
+                     fma(rinvsix_nm_S0, fnma(expmcr2_S0, poly_S0, one_S), sh_mask_S0),
+                     VLJ_S0);
+#            ifndef HALF_LJ
+        VLJ_S2 = fma(sixth_S * c6grid_S2,
+                     fma(rinvsix_nm_S2, fnma(expmcr2_S2, poly_S2, one_S), sh_mask_S2),
+                     VLJ_S2);
+#            endif
+#        endif /* CALC_ENERGIES */
+    }
+#    endif /* LJ_EWALD_GEOM */
+
+#    if defined VDW_CUTOFF_CHECK
+    /* frLJ is multiplied later by rinvsq, which is masked for the Coulomb
+     * cut-off, but if the VdW cut-off is shorter, we need to mask with that.
+     */
+    frLJ_S0 = selectByMask(frLJ_S0, wco_vdw_S0);
+#        ifndef HALF_LJ
+    frLJ_S2 = selectByMask(frLJ_S2, wco_vdw_S2);
+#        endif
+#    endif
+
+#    ifdef CALC_ENERGIES
+    /* The potential shift should be removed for pairs beyond cut-off */
+    VLJ_S0 = selectByMask(VLJ_S0, wco_vdw_S0);
+#        ifndef HALF_LJ
+    VLJ_S2 = selectByMask(VLJ_S2, wco_vdw_S2);
+#        endif
+#    endif
+
+#endif /* CALC_LJ */
+
+#ifdef CALC_ENERGIES
+#    ifdef ENERGY_GROUPS
+    /* Extract the group pair index per j pair.
+     * Energy groups are stored per i-cluster, so things get
+     * complicated when the i- and j-cluster size don't match.
+     */
+    {
+#        if UNROLLJ == 2
+        const int egps_j = nbatParams.energrp[cj >> 1];
+        egp_jj[0]        = ((egps_j >> ((cj & 1) * egps_jshift)) & egps_jmask) * egps_jstride;
+#        else
+        /* We assume UNROLLI <= UNROLLJ */
+        for (int jdi = 0; jdi < UNROLLJ / UNROLLI; jdi++)
+        {
+            const int egps_j = nbatParams.energrp[cj * (UNROLLJ / UNROLLI) + jdi];
+            for (int jj = 0; jj < (UNROLLI / 2); jj++)
+            {
+                egp_jj[jdi * (UNROLLI / 2) + jj] =
+                        ((egps_j >> (jj * egps_jshift)) & egps_jmask) * egps_jstride;
+            }
+        }
+#        endif
+    }
+#    endif
+
+#    ifdef CALC_COULOMB
+#        ifndef ENERGY_GROUPS
+    vctot_S = vctot_S + vcoul_S0 + vcoul_S2;
+#        else
+    add_ener_grp_halves(vcoul_S0, vctp[0], vctp[1], egp_jj);
+    add_ener_grp_halves(vcoul_S2, vctp[2], vctp[3], egp_jj);
+#        endif
+#    endif
+
+#    ifdef CALC_LJ
+#        ifndef ENERGY_GROUPS
+    Vvdwtot_S = Vvdwtot_S + VLJ_S0
+#            ifndef HALF_LJ
+                + VLJ_S2
+#            endif
+            ;
+#        else
+    add_ener_grp_halves(VLJ_S0, vvdwtp[0], vvdwtp[1], egp_jj);
+#            ifndef HALF_LJ
+    add_ener_grp_halves(VLJ_S2, vvdwtp[2], vvdwtp[3], egp_jj);
+#            endif
+#        endif
+#    endif /* CALC_LJ */
+#endif     /* CALC_ENERGIES */
+
+#ifdef CALC_LJ
+#    ifdef CALC_COULOMB
+    fscal_S0 = rinvsq_S0 * (frcoul_S0 + frLJ_S0);
+#    else
+    fscal_S0 = rinvsq_S0 * frLJ_S0;
+#    endif
+#else
+    fscal_S0 = rinvsq_S0 * frcoul_S0;
+#endif /* CALC_LJ */
+#if defined CALC_LJ && !defined HALF_LJ
+#    ifdef CALC_COULOMB
+    fscal_S2 = rinvsq_S2 * (frcoul_S2 + frLJ_S2);
+#    else
+    fscal_S2 = rinvsq_S2 * frLJ_S2;
+#    endif
+#else
+    /* Atom 2 and 3 don't have LJ, so only add Coulomb forces */
+    fscal_S2 = rinvsq_S2 * frcoul_S2;
+#endif
+
+    /* Calculate temporary vectorial force */
+    tx_S0 = fscal_S0 * dx_S0;
+    tx_S2 = fscal_S2 * dx_S2;
+    ty_S0 = fscal_S0 * dy_S0;
+    ty_S2 = fscal_S2 * dy_S2;
+    tz_S0 = fscal_S0 * dz_S0;
+    tz_S2 = fscal_S2 * dz_S2;
+
+    /* Increment i atom force */
+    fix_S0 = fix_S0 + tx_S0;
+    fix_S2 = fix_S2 + tx_S2;
+    fiy_S0 = fiy_S0 + ty_S0;
+    fiy_S2 = fiy_S2 + ty_S2;
+    fiz_S0 = fiz_S0 + tz_S0;
+    fiz_S2 = fiz_S2 + tz_S2;
+
+    /* Decrement j atom force */
+    decr3Hsimd(f + aj * DIM, tx_S0 + tx_S2, ty_S0 + ty_S2, tz_S0 + tz_S2);
+}
+
+#undef rinv_ex_S0
+#undef rinv_ex_S2
+
+#undef wco_vdw_S0
+#undef wco_vdw_S2
+
+#undef EXCL_FORCES
diff --git a/src/include/gromacs/nbnxm/kernels_simd_2xmm/kernel_outer.h b/src/include/gromacs/nbnxm/kernels_simd_2xmm/kernel_outer.h
new file mode 100644 (file)
index 0000000..620a6d5
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+
+{
+    using namespace gmx;
+
+    /* Unpack pointers for output */
+    real* f      = out->f.data();
+    real* fshift = out->fshift.data();
+#ifdef CALC_ENERGIES
+#    ifdef ENERGY_GROUPS
+    real* Vvdw = out->VSvdw.data();
+    real* Vc   = out->VSc.data();
+#    else
+    real*       Vvdw       = out->Vvdw.data();
+    real*       Vc         = out->Vc.data();
+#    endif
+#endif
+
+    SimdReal shX_S;
+    SimdReal shY_S;
+    SimdReal shZ_S;
+    SimdReal ix_S0, iy_S0, iz_S0;
+    SimdReal ix_S2, iy_S2, iz_S2;
+    SimdReal fix_S0, fiy_S0, fiz_S0;
+    SimdReal fix_S2, fiy_S2, fiz_S2;
+
+    SimdReal diagonal_jmi_S;
+#if UNROLLI == UNROLLJ
+    SimdBool diagonal_mask_S0, diagonal_mask_S2;
+#else
+    SimdBool                         diagonal_mask0_S0, diagonal_mask0_S2;
+    SimdBool                         diagonal_mask1_S0, diagonal_mask1_S2;
+#endif
+
+    SimdBitMask filter_S0, filter_S2;
+
+    SimdReal zero_S(0.0);
+
+    SimdReal one_S(1.0);
+    SimdReal iq_S0 = setZero();
+    SimdReal iq_S2 = setZero();
+
+#ifdef CALC_COUL_RF
+    SimdReal mrc_3_S;
+#    ifdef CALC_ENERGIES
+    SimdReal hrc_3_S, moh_rc_S;
+#    endif
+#endif
+
+#ifdef CALC_COUL_TAB
+    /* Coulomb table variables */
+    SimdReal invtsp_S;
+
+#    ifdef CALC_ENERGIES
+    SimdReal mhalfsp_S;
+#    endif
+#endif
+
+#ifdef CALC_COUL_EWALD
+    SimdReal beta2_S, beta_S;
+#endif
+
+#if defined CALC_ENERGIES && (defined CALC_COUL_EWALD || defined CALC_COUL_TAB)
+    SimdReal sh_ewald_S;
+#endif
+
+#if defined LJ_CUT && defined CALC_ENERGIES
+    SimdReal p6_cpot_S, p12_cpot_S;
+#endif
+#ifdef LJ_POT_SWITCH
+    SimdReal rswitch_S;
+    SimdReal swV3_S, swV4_S, swV5_S;
+    SimdReal swF2_S, swF3_S, swF4_S;
+#endif
+#ifdef LJ_FORCE_SWITCH
+    SimdReal rswitch_S;
+    SimdReal p6_fc2_S, p6_fc3_S;
+    SimdReal p12_fc2_S, p12_fc3_S;
+#    ifdef CALC_ENERGIES
+    SimdReal p6_vc3_S, p6_vc4_S;
+    SimdReal p12_vc3_S, p12_vc4_S;
+    SimdReal p6_6cpot_S, p12_12cpot_S;
+#    endif
+#endif
+#ifdef LJ_EWALD_GEOM
+    SimdReal half_S, lje_c2_S, lje_c6_6_S;
+#endif
+
+#ifdef LJ_COMB_LB
+    SimdReal hsig_i_S0, seps_i_S0;
+    SimdReal hsig_i_S2, seps_i_S2;
+#else
+#    ifdef FIX_LJ_C
+    alignas(GMX_SIMD_ALIGNMENT) real pvdw_c6[2 * UNROLLI * UNROLLJ];
+    real*                            pvdw_c12          = pvdw_c6 + UNROLLI * UNROLLJ;
+#    endif
+#endif /* LJ_COMB_LB */
+
+    SimdReal minRsq_S;
+    SimdReal rc2_S;
+#ifdef VDW_CUTOFF_CHECK
+    SimdReal rcvdw2_S;
+#endif
+
+#ifdef COUNT_PAIRS
+    int npair = 0;
+#endif
+
+    const nbnxn_atomdata_t::Params& nbatParams = nbat->params();
+
+#if defined LJ_COMB_GEOM || defined LJ_COMB_LB || defined LJ_EWALD_GEOM
+    const real* gmx_restrict ljc = nbatParams.lj_comb.data();
+#endif
+#if !(defined LJ_COMB_GEOM || defined LJ_COMB_LB || defined FIX_LJ_C)
+    /* No combination rule used */
+    const real* gmx_restrict nbfp_ptr = nbatParams.nbfp_aligned.data();
+    const int* gmx_restrict  type     = nbatParams.type.data();
+#endif
+
+    /* Load j-i for the first i */
+    diagonal_jmi_S = load<SimdReal>(nbat->simdMasks.diagonal_2xnn_j_minus_i.data());
+    /* Generate all the diagonal masks as comparison results */
+#if UNROLLI == UNROLLJ
+    diagonal_mask_S0 = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S   = diagonal_jmi_S - one_S;
+    diagonal_jmi_S   = diagonal_jmi_S - one_S;
+    diagonal_mask_S2 = (zero_S < diagonal_jmi_S);
+#else
+#    if 2 * UNROLLI == UNROLLJ
+    diagonal_mask0_S0                                  = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S                                     = diagonal_jmi_S - one_S;
+    diagonal_jmi_S                                     = diagonal_jmi_S - one_S;
+    diagonal_mask0_S2                                  = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S                                     = diagonal_jmi_S - one_S;
+    diagonal_jmi_S                                     = diagonal_jmi_S - one_S;
+    diagonal_mask1_S0                                  = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S                                     = diagonal_jmi_S - one_S;
+    diagonal_jmi_S                                     = diagonal_jmi_S - one_S;
+    diagonal_mask1_S2                                  = (zero_S < diagonal_jmi_S);
+#    endif
+#endif
+
+    /* Load masks for topology exclusion masking. filter_stride is
+       static const, so the conditional will be optimized away. */
+#if GMX_DOUBLE && !GMX_SIMD_HAVE_INT32_LOGICAL
+    const std::uint64_t* gmx_restrict exclusion_filter = nbat->simdMasks.exclusion_filter64.data();
+#else
+    const std::uint32_t* gmx_restrict exclusion_filter = nbat->simdMasks.exclusion_filter.data();
+#endif
+
+    /* Here we cast the exclusion filters from unsigned * to int * or real *.
+     * Since we only check bits, the actual value they represent does not
+     * matter, as long as both filter and mask data are treated the same way.
+     */
+#if GMX_SIMD_HAVE_INT32_LOGICAL
+    filter_S0 = load<SimdBitMask>(reinterpret_cast<const int*>(exclusion_filter + 0 * UNROLLJ));
+    filter_S2 = load<SimdBitMask>(reinterpret_cast<const int*>(exclusion_filter + 2 * UNROLLJ));
+#else
+    filter_S0 = load<SimdBitMask>(reinterpret_cast<const real*>(exclusion_filter + 0 * UNROLLJ));
+    filter_S2 = load<SimdBitMask>(reinterpret_cast<const real*>(exclusion_filter + 2 * UNROLLJ));
+#endif
+
+#ifdef CALC_COUL_RF
+    /* Reaction-field constants */
+    mrc_3_S = SimdReal(-2 * ic->reactionFieldCoefficient);
+#    ifdef CALC_ENERGIES
+    hrc_3_S  = SimdReal(ic->reactionFieldCoefficient);
+    moh_rc_S = SimdReal(-ic->reactionFieldShift);
+#    endif
+#endif
+
+#ifdef CALC_COUL_TAB
+
+    invtsp_S = SimdReal(ic->coulombEwaldTables->scale);
+#    ifdef CALC_ENERGIES
+    mhalfsp_S = SimdReal(-0.5_real / ic->coulombEwaldTables->scale);
+#    endif
+
+#    ifdef TAB_FDV0
+    const real* tab_coul_F = ic->coulombEwaldTables->tableFDV0.data();
+#    else
+    const real* tab_coul_F = ic->coulombEwaldTables->tableF.data();
+#        ifdef CALC_ENERGIES
+    const real* tab_coul_V = ic->coulombEwaldTables->tableV.data();
+#        endif
+#    endif
+#endif /* CALC_COUL_TAB */
+
+#ifdef CALC_COUL_EWALD
+    beta2_S = SimdReal(ic->ewaldcoeff_q * ic->ewaldcoeff_q);
+    beta_S  = SimdReal(ic->ewaldcoeff_q);
+#endif
+
+#if (defined CALC_COUL_TAB || defined CALC_COUL_EWALD) && defined CALC_ENERGIES
+    sh_ewald_S = SimdReal(ic->sh_ewald);
+#endif
+
+    /* LJ function constants */
+#if defined CALC_ENERGIES || defined LJ_POT_SWITCH
+    SimdReal sixth_S    = SimdReal(1.0 / 6.0);
+    SimdReal twelveth_S = SimdReal(1.0 / 12.0);
+#endif
+
+#if defined LJ_CUT && defined CALC_ENERGIES
+    /* We shift the potential by cpot, which can be zero */
+    p6_cpot_S  = SimdReal(ic->dispersion_shift.cpot);
+    p12_cpot_S = SimdReal(ic->repulsion_shift.cpot);
+#endif
+#ifdef LJ_POT_SWITCH
+    rswitch_S = SimdReal(ic->rvdw_switch);
+    swV3_S    = SimdReal(ic->vdw_switch.c3);
+    swV4_S    = SimdReal(ic->vdw_switch.c4);
+    swV5_S    = SimdReal(ic->vdw_switch.c5);
+    swF2_S    = SimdReal(3 * ic->vdw_switch.c3);
+    swF3_S    = SimdReal(4 * ic->vdw_switch.c4);
+    swF4_S    = SimdReal(5 * ic->vdw_switch.c5);
+#endif
+#ifdef LJ_FORCE_SWITCH
+    rswitch_S = SimdReal(ic->rvdw_switch);
+    p6_fc2_S  = SimdReal(ic->dispersion_shift.c2);
+    p6_fc3_S  = SimdReal(ic->dispersion_shift.c3);
+    p12_fc2_S = SimdReal(ic->repulsion_shift.c2);
+    p12_fc3_S = SimdReal(ic->repulsion_shift.c3);
+#    ifdef CALC_ENERGIES
+    {
+        SimdReal mthird_S  = SimdReal(-1.0 / 3.0);
+        SimdReal mfourth_S = SimdReal(-1.0 / 4.0);
+
+        p6_vc3_S     = mthird_S * p6_fc2_S;
+        p6_vc4_S     = mfourth_S * p6_fc3_S;
+        p6_6cpot_S   = SimdReal(ic->dispersion_shift.cpot / 6);
+        p12_vc3_S    = mthird_S * p12_fc2_S;
+        p12_vc4_S    = mfourth_S * p12_fc3_S;
+        p12_12cpot_S = SimdReal(ic->repulsion_shift.cpot / 12);
+    }
+#    endif
+#endif
+#ifdef LJ_EWALD_GEOM
+    half_S                      = SimdReal(0.5);
+    const real lj_ewaldcoeff2   = ic->ewaldcoeff_lj * ic->ewaldcoeff_lj;
+    const real lj_ewaldcoeff6_6 = lj_ewaldcoeff2 * lj_ewaldcoeff2 * lj_ewaldcoeff2 / 6;
+    lje_c2_S                    = SimdReal(lj_ewaldcoeff2);
+    lje_c6_6_S                  = SimdReal(lj_ewaldcoeff6_6);
+#    ifdef CALC_ENERGIES
+    /* Determine the grid potential at the cut-off */
+    SimdReal lje_vc_S = SimdReal(ic->sh_lj_ewald);
+#    endif
+#endif
+
+    /* The kernel either supports rcoulomb = rvdw or rcoulomb >= rvdw */
+    rc2_S = SimdReal(ic->rcoulomb * ic->rcoulomb);
+#ifdef VDW_CUTOFF_CHECK
+    rcvdw2_S = SimdReal(ic->rvdw * ic->rvdw);
+#endif
+
+    minRsq_S = SimdReal(c_nbnxnMinDistanceSquared);
+
+    const real* gmx_restrict q        = nbatParams.q.data();
+    const real               facel    = ic->epsfac;
+    const real* gmx_restrict shiftvec = shift_vec[0];
+    const real* gmx_restrict x        = nbat->x().data();
+
+#ifdef FIX_LJ_C
+
+    for (jp = 0; jp < UNROLLJ; jp++)
+    {
+        pvdw_c6[0 * UNROLLJ + jp] = nbat->nbfp[0 * 2];
+        pvdw_c6[1 * UNROLLJ + jp] = nbat->nbfp[0 * 2];
+        pvdw_c6[2 * UNROLLJ + jp] = nbat->nbfp[0 * 2];
+        pvdw_c6[3 * UNROLLJ + jp] = nbat->nbfp[0 * 2];
+
+        pvdw_c12[0 * UNROLLJ + jp] = nbat->nbfp[0 * 2 + 1];
+        pvdw_c12[1 * UNROLLJ + jp] = nbat->nbfp[0 * 2 + 1];
+        pvdw_c12[2 * UNROLLJ + jp] = nbat->nbfp[0 * 2 + 1];
+        pvdw_c12[3 * UNROLLJ + jp] = nbat->nbfp[0 * 2 + 1];
+    }
+    SimdReal c6_S0 = load<SimdReal>(pvdw_c6 + 0 * UNROLLJ);
+    SimdReal c6_S1 = load<SimdReal>(pvdw_c6 + 1 * UNROLLJ);
+    SimdReal c6_S2 = load<SimdReal>(pvdw_c6 + 2 * UNROLLJ);
+    SimdReal c6_S3 = load<SimdReal>(pvdw_c6 + 3 * UNROLLJ);
+
+    SimdReal c12_S0 = load<SimdReal>(pvdw_c12 + 0 * UNROLLJ);
+    SimdReal c12_S1 = load<SimdReal>(pvdw_c12 + 1 * UNROLLJ);
+    SimdReal c12_S2 = load<SimdReal>(pvdw_c12 + 2 * UNROLLJ);
+    SimdReal c12_S3 = load<SimdReal>(pvdw_c12 + 3 * UNROLLJ);
+#endif /* FIX_LJ_C */
+
+#ifdef ENERGY_GROUPS
+    const int egps_ishift  = nbatParams.neg_2log;
+    const int egps_imask   = (1 << egps_ishift) - 1;
+    const int egps_jshift  = 2 * nbatParams.neg_2log;
+    const int egps_jmask   = (1 << egps_jshift) - 1;
+    const int egps_jstride = (UNROLLJ >> 1) * UNROLLJ;
+    /* Major division is over i-particle energy groups, determine the stride */
+    const int Vstride_i = nbatParams.nenergrp * (1 << nbatParams.neg_2log) * egps_jstride;
+#endif
+
+    const nbnxn_cj_t* l_cj = nbl->cj.data();
+
+    for (const nbnxn_ci_t& ciEntry : nbl->ci)
+    {
+        const int ish    = (ciEntry.shift & NBNXN_CI_SHIFT);
+        const int ish3   = ish * 3;
+        const int cjind0 = ciEntry.cj_ind_start;
+        const int cjind1 = ciEntry.cj_ind_end;
+        const int ci     = ciEntry.ci;
+        const int ci_sh  = (ish == gmx::c_centralShiftIndex ? ci : -1);
+
+        shX_S = SimdReal(shiftvec[ish3]);
+        shY_S = SimdReal(shiftvec[ish3 + 1]);
+        shZ_S = SimdReal(shiftvec[ish3 + 2]);
+
+#if UNROLLJ <= 4
+        int sci  = ci * STRIDE;
+        int scix = sci * DIM;
+#    if defined LJ_COMB_LB || defined LJ_COMB_GEOM || defined LJ_EWALD_GEOM
+        int sci2 = sci * 2;
+#    endif
+#else
+        int sci  = (ci >> 1) * STRIDE;
+        int scix = sci * DIM + (ci & 1) * (STRIDE >> 1);
+#    if defined LJ_COMB_LB || defined LJ_COMB_GEOM || defined LJ_EWALD_GEOM
+        int sci2 = sci * 2 + (ci & 1) * (STRIDE >> 1);
+#    endif
+        sci += (ci & 1) * (STRIDE >> 1);
+#endif
+
+        /* We have 5 LJ/C combinations, but use only three inner loops,
+         * as the other combinations are unlikely and/or not much faster:
+         * inner half-LJ + C for half-LJ + C / no-LJ + C
+         * inner LJ + C      for full-LJ + C
+         * inner LJ          for full-LJ + no-C / half-LJ + no-C
+         */
+        const bool do_LJ   = ((ciEntry.shift & NBNXN_CI_DO_LJ(0)) != 0);
+        const bool do_coul = ((ciEntry.shift & NBNXN_CI_DO_COUL(0)) != 0);
+        const bool half_LJ = (((ciEntry.shift & NBNXN_CI_HALF_LJ(0)) != 0) || !do_LJ) && do_coul;
+
+#ifdef ENERGY_GROUPS
+        const int egps_i = nbatParams.energrp[ci];
+        real*     vvdwtp[UNROLLI];
+        real*     vctp[UNROLLI];
+        {
+            for (int ia = 0; ia < UNROLLI; ia++)
+            {
+                int egp_ia = (egps_i >> (ia * egps_ishift)) & egps_imask;
+                vvdwtp[ia] = Vvdw + egp_ia * Vstride_i;
+                vctp[ia]   = Vc + egp_ia * Vstride_i;
+            }
+        }
+#endif
+
+#ifdef CALC_ENERGIES
+#    ifdef LJ_EWALD_GEOM
+        gmx_bool do_self = TRUE;
+#    else
+        gmx_bool do_self = do_coul;
+#    endif
+#    if UNROLLJ == 4
+        if (do_self && l_cj[ciEntry.cj_ind_start].cj == ci_sh)
+#    endif
+#    if UNROLLJ == 8
+            if (do_self && l_cj[ciEntry.cj_ind_start].cj == (ci_sh >> 1))
+#    endif
+            {
+                if (do_coul)
+                {
+#    ifdef CALC_COUL_RF
+                    const real Vc_sub_self = 0.5 * ic->reactionFieldShift;
+#    endif
+#    ifdef CALC_COUL_TAB
+#        ifdef TAB_FDV0
+                    const real Vc_sub_self = 0.5 * tab_coul_F[2];
+#        else
+                    const real Vc_sub_self = 0.5 * tab_coul_V[0];
+#        endif
+#    endif
+#    ifdef CALC_COUL_EWALD
+                    /* beta/sqrt(pi) */
+                    const real Vc_sub_self = 0.5 * ic->ewaldcoeff_q * M_2_SQRTPI;
+#    endif
+
+                    for (int ia = 0; ia < UNROLLI; ia++)
+                    {
+                        const real qi = q[sci + ia];
+#    ifdef ENERGY_GROUPS
+                        vctp[ia][((egps_i >> (ia * egps_ishift)) & egps_imask) * egps_jstride]
+#    else
+                    Vc[0]
+#    endif
+                                -= facel * qi * qi * Vc_sub_self;
+                    }
+                }
+
+#    ifdef LJ_EWALD_GEOM
+                {
+                    for (int ia = 0; ia < UNROLLI; ia++)
+                    {
+                        const real c6_i =
+                                nbatParams.nbfp[nbatParams.type[sci + ia] * (nbatParams.numTypes + 1) * 2]
+                                / 6;
+#        ifdef ENERGY_GROUPS
+                        vvdwtp[ia][((egps_i >> (ia * egps_ishift)) & egps_imask) * egps_jstride]
+#        else
+                        Vvdw[0]
+#        endif
+                                += 0.5 * c6_i * lj_ewaldcoeff6_6;
+                    }
+                }
+#    endif /* LJ_EWALD */
+            }
+#endif
+
+        /* Load i atom data */
+        int sciy = scix + STRIDE;
+        int sciz = sciy + STRIDE;
+        ix_S0    = loadU1DualHsimd(x + scix);
+        ix_S2    = loadU1DualHsimd(x + scix + 2);
+        iy_S0    = loadU1DualHsimd(x + sciy);
+        iy_S2    = loadU1DualHsimd(x + sciy + 2);
+        iz_S0    = loadU1DualHsimd(x + sciz);
+        iz_S2    = loadU1DualHsimd(x + sciz + 2);
+        ix_S0    = ix_S0 + shX_S;
+        ix_S2    = ix_S2 + shX_S;
+        iy_S0    = iy_S0 + shY_S;
+        iy_S2    = iy_S2 + shY_S;
+        iz_S0    = iz_S0 + shZ_S;
+        iz_S2    = iz_S2 + shZ_S;
+
+        if (do_coul)
+        {
+            SimdReal facel_S;
+
+            facel_S = SimdReal(facel);
+
+            iq_S0 = loadU1DualHsimd(q + sci);
+            iq_S2 = loadU1DualHsimd(q + sci + 2);
+            iq_S0 = facel_S * iq_S0;
+            iq_S2 = facel_S * iq_S2;
+        }
+
+#ifdef LJ_COMB_LB
+        hsig_i_S0 = loadU1DualHsimd(ljc + sci2);
+        hsig_i_S2 = loadU1DualHsimd(ljc + sci2 + 2);
+        seps_i_S0 = loadU1DualHsimd(ljc + sci2 + STRIDE);
+        seps_i_S2 = loadU1DualHsimd(ljc + sci2 + STRIDE + 2);
+#else
+#    ifdef LJ_COMB_GEOM
+        SimdReal c6s_S0, c12s_S0;
+        SimdReal c6s_S2, c12s_S2;
+
+        c6s_S0 = loadU1DualHsimd(ljc + sci2);
+
+        if (!half_LJ)
+        {
+            c6s_S2 = loadU1DualHsimd(ljc + sci2 + 2);
+        }
+        c12s_S0 = loadU1DualHsimd(ljc + sci2 + STRIDE);
+        if (!half_LJ)
+        {
+            c12s_S2 = loadU1DualHsimd(ljc + sci2 + STRIDE + 2);
+        }
+#    elif !defined LJ_COMB_LB && !defined FIX_LJ_C
+        const int   numTypes = nbatParams.numTypes;
+        const real* nbfp0    = nbfp_ptr + type[sci] * numTypes * c_simdBestPairAlignment;
+        const real* nbfp1    = nbfp_ptr + type[sci + 1] * numTypes * c_simdBestPairAlignment;
+        const real *nbfp2 = nullptr, *nbfp3 = nullptr;
+        if (!half_LJ)
+        {
+            nbfp2 = nbfp_ptr + type[sci + 2] * numTypes * c_simdBestPairAlignment;
+            nbfp3 = nbfp_ptr + type[sci + 3] * numTypes * c_simdBestPairAlignment;
+        }
+#    endif
+#endif
+#ifdef LJ_EWALD_GEOM
+        /* We need the geometrically combined C6 for the PME grid correction */
+        SimdReal c6s_S0, c6s_S2;
+        c6s_S0 = loadU1DualHsimd(ljc + sci2);
+        if (!half_LJ)
+        {
+            c6s_S2 = loadU1DualHsimd(ljc + sci2 + 2);
+        }
+#endif
+
+        /* Zero the potential energy for this list */
+#ifdef CALC_ENERGIES
+        SimdReal Vvdwtot_S = setZero();
+        SimdReal vctot_S   = setZero();
+#endif
+
+        /* Clear i atom forces */
+        fix_S0 = setZero();
+        fix_S2 = setZero();
+        fiy_S0 = setZero();
+        fiy_S2 = setZero();
+        fiz_S0 = setZero();
+        fiz_S2 = setZero();
+
+        int cjind = cjind0;
+
+        /* Currently all kernels use (at least half) LJ */
+#define CALC_LJ
+        if (half_LJ)
+        {
+            /* Coulomb: all i-atoms, LJ: first half i-atoms */
+#define CALC_COULOMB
+#define HALF_LJ
+#define CHECK_EXCLS
+            while (cjind < cjind1 && nbl->cj[cjind].excl != NBNXN_INTERACTION_MASK_ALL)
+            {
+#include "kernel_inner.h"
+                cjind++;
+            }
+#undef CHECK_EXCLS
+            for (; (cjind < cjind1); cjind++)
+            {
+#include "kernel_inner.h"
+            }
+#undef HALF_LJ
+#undef CALC_COULOMB
+        }
+        else if (do_coul)
+        {
+            /* Coulomb: all i-atoms, LJ: all i-atoms */
+#define CALC_COULOMB
+#define CHECK_EXCLS
+            while (cjind < cjind1 && nbl->cj[cjind].excl != NBNXN_INTERACTION_MASK_ALL)
+            {
+#include "kernel_inner.h"
+                cjind++;
+            }
+#undef CHECK_EXCLS
+            for (; (cjind < cjind1); cjind++)
+            {
+#include "kernel_inner.h"
+            }
+#undef CALC_COULOMB
+        }
+        else
+        {
+            /* Coulomb: none, LJ: all i-atoms */
+#define CHECK_EXCLS
+            while (cjind < cjind1 && nbl->cj[cjind].excl != NBNXN_INTERACTION_MASK_ALL)
+            {
+#include "kernel_inner.h"
+                cjind++;
+            }
+#undef CHECK_EXCLS
+            for (; (cjind < cjind1); cjind++)
+            {
+#include "kernel_inner.h"
+            }
+        }
+#undef CALC_LJ
+        /* Add accumulated i-forces to the force array */
+        real fShiftX = reduceIncr4ReturnSumHsimd(f + scix, fix_S0, fix_S2);
+        real fShiftY = reduceIncr4ReturnSumHsimd(f + sciy, fiy_S0, fiy_S2);
+        real fShiftZ = reduceIncr4ReturnSumHsimd(f + sciz, fiz_S0, fiz_S2);
+
+#ifdef CALC_SHIFTFORCES
+        fshift[ish3 + 0] += fShiftX;
+        fshift[ish3 + 1] += fShiftY;
+        fshift[ish3 + 2] += fShiftZ;
+#endif
+
+#ifdef CALC_ENERGIES
+        if (do_coul)
+        {
+            *Vc += reduce(vctot_S);
+        }
+        *Vvdw += reduce(Vvdwtot_S);
+#endif
+
+        /* Outer loop uses 6 flops/iteration */
+    }
+
+#ifdef COUNT_PAIRS
+    printf("atom pairs %d\n", npair);
+#endif
+}
diff --git a/src/include/gromacs/nbnxm/kernels_simd_2xmm/kernel_prune.h b/src/include/gromacs/nbnxm/kernels_simd_2xmm/kernel_prune.h
new file mode 100644 (file)
index 0000000..9abafd8
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the SIMD 2xNN pruning only kernel.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct nbnxn_atomdata_t;
+struct NbnxnPairlistCpu;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/*! \brief Prune a single NbnxnPairlistCpu entry with distance \p rlistInner
+ *
+ * Reads a cluster pairlist \p nbl->ciOuter, \p nbl->cjOuter and writes
+ * all cluster pairs within \p rlistInner to \p nbl->ci, \p nbl->cj.
+ */
+void nbnxn_kernel_prune_2xnn(NbnxnPairlistCpu*              nbl,
+                             const nbnxn_atomdata_t*        nbat,
+                             gmx::ArrayRef<const gmx::RVec> shift_vec,
+                             real                           rlistInner);
diff --git a/src/include/gromacs/nbnxm/kernels_simd_2xmm/kernels.h b/src/include/gromacs/nbnxm/kernels_simd_2xmm/kernels.h
new file mode 100644 (file)
index 0000000..4bb063d
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2019 by the GROMACS development team.
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*
+ * Note: this file was generated by the Verlet kernel generator for
+ * kernel type 2xmm.
+ */
+
+
+#include "gromacs/nbnxm/kernel_common.h"
+
+/* Declare all the different kernel functions.
+ */
+nbk_func_noener nbnxm_kernel_ElecRF_VdwLJCombGeom_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecRF_VdwLJCombLB_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecRF_VdwLJ_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecRF_VdwLJFSw_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecRF_VdwLJPSw_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecRF_VdwLJEwCombGeom_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecQSTab_VdwLJCombGeom_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecQSTab_VdwLJCombLB_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecQSTab_VdwLJ_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecQSTab_VdwLJFSw_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecQSTab_VdwLJPSw_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecQSTab_VdwLJEwCombGeom_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombGeom_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombLB_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecQSTabTwinCut_VdwLJ_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecQSTabTwinCut_VdwLJFSw_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecQSTabTwinCut_VdwLJPSw_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecEw_VdwLJCombGeom_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecEw_VdwLJCombLB_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecEw_VdwLJ_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecEw_VdwLJFSw_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecEw_VdwLJPSw_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecEw_VdwLJEwCombGeom_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecEwTwinCut_VdwLJCombGeom_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecEwTwinCut_VdwLJCombLB_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecEwTwinCut_VdwLJ_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecEwTwinCut_VdwLJFSw_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecEwTwinCut_VdwLJPSw_F_2xmm;
+nbk_func_noener nbnxm_kernel_ElecEwTwinCut_VdwLJEwCombGeom_F_2xmm;
+
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJCombGeom_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJCombLB_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJ_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJFSw_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJPSw_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJEwCombGeom_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJCombGeom_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJCombLB_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJ_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJFSw_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJPSw_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJEwCombGeom_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombGeom_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombLB_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJ_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJFSw_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJPSw_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJCombGeom_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJCombLB_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJ_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJFSw_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJPSw_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJEwCombGeom_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJCombGeom_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJCombLB_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJ_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJFSw_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJPSw_VF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VF_2xmm;
+
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJCombGeom_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJCombLB_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJ_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJFSw_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJPSw_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJEwCombGeom_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJCombGeom_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJCombLB_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJ_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJFSw_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJPSw_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJEwCombGeom_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombGeom_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombLB_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJFSw_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJPSw_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJCombGeom_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJCombLB_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJ_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJFSw_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJPSw_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJEwCombGeom_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJCombGeom_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJCombLB_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJ_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJFSw_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJPSw_VgrpF_2xmm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VgrpF_2xmm;
+
+
+#ifdef INCLUDE_KERNELFUNCTION_TABLES
+
+/* Declare and define the kernel function pointer lookup tables.
+ * The minor index of the array goes over both the LJ combination rules,
+ * which is only supported by plain cut-off, and the LJ switch/PME functions.
+ */
+static const p_nbk_func_noener nbnxm_kernel_noener_simd_2xmm[static_cast<int>(CoulombKernelType::Count)][vdwktNR] = {
+    {
+            nbnxm_kernel_ElecRF_VdwLJCombGeom_F_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJCombLB_F_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJ_F_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJFSw_F_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJPSw_F_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJEwCombGeom_F_2xmm,
+    },
+    {
+            nbnxm_kernel_ElecQSTab_VdwLJCombGeom_F_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJCombLB_F_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJ_F_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJFSw_F_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJPSw_F_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJEwCombGeom_F_2xmm,
+    },
+    {
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombGeom_F_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombLB_F_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJ_F_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJFSw_F_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJPSw_F_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_F_2xmm,
+    },
+    {
+            nbnxm_kernel_ElecEw_VdwLJCombGeom_F_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJCombLB_F_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJ_F_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJFSw_F_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJPSw_F_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJEwCombGeom_F_2xmm,
+    },
+    {
+            nbnxm_kernel_ElecEwTwinCut_VdwLJCombGeom_F_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJCombLB_F_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJ_F_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJFSw_F_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJPSw_F_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJEwCombGeom_F_2xmm,
+    },
+};
+
+static const p_nbk_func_ener nbnxm_kernel_ener_simd_2xmm[static_cast<int>(CoulombKernelType::Count)][vdwktNR] = {
+    {
+            nbnxm_kernel_ElecRF_VdwLJCombGeom_VF_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJCombLB_VF_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJ_VF_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJFSw_VF_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJPSw_VF_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJEwCombGeom_VF_2xmm,
+    },
+    {
+            nbnxm_kernel_ElecQSTab_VdwLJCombGeom_VF_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJCombLB_VF_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJ_VF_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJFSw_VF_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJPSw_VF_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJEwCombGeom_VF_2xmm,
+    },
+    {
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombGeom_VF_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombLB_VF_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJ_VF_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJFSw_VF_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJPSw_VF_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VF_2xmm,
+    },
+    {
+            nbnxm_kernel_ElecEw_VdwLJCombGeom_VF_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJCombLB_VF_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJ_VF_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJFSw_VF_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJPSw_VF_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJEwCombGeom_VF_2xmm,
+    },
+    {
+            nbnxm_kernel_ElecEwTwinCut_VdwLJCombGeom_VF_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJCombLB_VF_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJ_VF_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJFSw_VF_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJPSw_VF_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VF_2xmm,
+    },
+};
+
+static const p_nbk_func_ener nbnxm_kernel_energrp_simd_2xmm[static_cast<int>(CoulombKernelType::Count)][vdwktNR] = {
+    {
+            nbnxm_kernel_ElecRF_VdwLJCombGeom_VgrpF_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJCombLB_VgrpF_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJ_VgrpF_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJFSw_VgrpF_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJPSw_VgrpF_2xmm,
+            nbnxm_kernel_ElecRF_VdwLJEwCombGeom_VgrpF_2xmm,
+    },
+    {
+            nbnxm_kernel_ElecQSTab_VdwLJCombGeom_VgrpF_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJCombLB_VgrpF_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJ_VgrpF_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJFSw_VgrpF_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJPSw_VgrpF_2xmm,
+            nbnxm_kernel_ElecQSTab_VdwLJEwCombGeom_VgrpF_2xmm,
+    },
+    {
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombGeom_VgrpF_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombLB_VgrpF_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJFSw_VgrpF_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJPSw_VgrpF_2xmm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF_2xmm,
+    },
+    {
+            nbnxm_kernel_ElecEw_VdwLJCombGeom_VgrpF_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJCombLB_VgrpF_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJ_VgrpF_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJFSw_VgrpF_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJPSw_VgrpF_2xmm,
+            nbnxm_kernel_ElecEw_VdwLJEwCombGeom_VgrpF_2xmm,
+    },
+    {
+            nbnxm_kernel_ElecEwTwinCut_VdwLJCombGeom_VgrpF_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJCombLB_VgrpF_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJ_VgrpF_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJFSw_VgrpF_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJPSw_VgrpF_2xmm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VgrpF_2xmm,
+    },
+};
+
+
+#endif /* INCLUDE_KERNELFUNCTION_TABLES */
diff --git a/src/include/gromacs/nbnxm/kernels_simd_4xm/kernel_common.h b/src/include/gromacs/nbnxm/kernels_simd_4xm/kernel_common.h
new file mode 100644 (file)
index 0000000..c4af95d
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#include "gromacs/pbcutil/ishift.h"
+#include "gromacs/simd/simd.h"
+#include "gromacs/simd/simd_math.h"
+#include "gromacs/simd/vector_operations.h"
+#include "gromacs/utility/basedefinitions.h"
+#ifdef CALC_COUL_EWALD
+#    include "gromacs/math/utilities.h"
+#endif
+
+#include "config.h"
+
+#ifndef GMX_SIMD_J_UNROLL_SIZE
+#    error "Need to define GMX_SIMD_J_UNROLL_SIZE before including the 4xn kernel common header file"
+#endif
+
+#define UNROLLI 4
+#define UNROLLJ (GMX_SIMD_REAL_WIDTH / GMX_SIMD_J_UNROLL_SIZE)
+
+static_assert(UNROLLI == c_nbnxnCpuIClusterSize, "UNROLLI should match the i-cluster size");
+
+/* The stride of all the atom data arrays is max(UNROLLI,unrollj) */
+#if GMX_SIMD_REAL_WIDTH >= UNROLLI
+#    define STRIDE (GMX_SIMD_REAL_WIDTH / GMX_SIMD_J_UNROLL_SIZE)
+#else
+#    define STRIDE (UNROLLI)
+#endif
+
+
+#if !defined GMX_NBNXN_SIMD_2XNN && !defined GMX_NBNXN_SIMD_4XN
+#    error "Must define an NBNxN kernel flavour before including NBNxN kernel utility functions"
+#endif
+
+// We use the FDV0 tables for width==4 (when we can load it in one go), or if we don't have any unaligned loads
+#if GMX_SIMD_REAL_WIDTH == 4 || !GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_REAL
+#    define TAB_FDV0
+#endif
+
+
+#ifdef UNROLLJ
+/* Add energy register to possibly multiple terms in the energy array */
+static inline void add_ener_grp(gmx::SimdReal e_S, real* v, const int* offset_jj)
+{
+    using namespace gmx;
+
+    /* We need to balance the number of store operations with
+     * the rapidly increases number of combinations of energy groups.
+     * We add to a temporary buffer for 1 i-group vs 2 j-groups.
+     */
+    for (int jj = 0; jj < (UNROLLJ / 2); jj++)
+    {
+        SimdReal v_S;
+
+        v_S = load<SimdReal>(v + offset_jj[jj] + jj * GMX_SIMD_REAL_WIDTH);
+        store(v + offset_jj[jj] + jj * GMX_SIMD_REAL_WIDTH, v_S + e_S);
+    }
+}
+#endif
+
+#if GMX_SIMD_HAVE_INT32_LOGICAL
+typedef gmx::SimdInt32 SimdBitMask;
+#else
+typedef gmx::SimdReal SimdBitMask;
+#endif
+
+static inline void gmx_simdcall gmx_load_simd_4xn_interactions(int                    excl,
+                                                               SimdBitMask gmx_unused filter_S0,
+                                                               SimdBitMask gmx_unused filter_S1,
+                                                               SimdBitMask gmx_unused filter_S2,
+                                                               SimdBitMask gmx_unused filter_S3,
+                                                               const real gmx_unused* simd_interaction_array,
+                                                               gmx::SimdBool* interact_S0,
+                                                               gmx::SimdBool* interact_S1,
+                                                               gmx::SimdBool* interact_S2,
+                                                               gmx::SimdBool* interact_S3)
+{
+    using namespace gmx;
+#if GMX_SIMD_HAVE_INT32_LOGICAL
+    /* Load integer interaction mask */
+    SimdInt32 mask_pr_S(excl);
+    *interact_S0 = cvtIB2B(testBits(mask_pr_S & filter_S0));
+    *interact_S1 = cvtIB2B(testBits(mask_pr_S & filter_S1));
+    *interact_S2 = cvtIB2B(testBits(mask_pr_S & filter_S2));
+    *interact_S3 = cvtIB2B(testBits(mask_pr_S & filter_S3));
+#elif GMX_SIMD_HAVE_LOGICAL
+    union
+    {
+#    if GMX_DOUBLE
+        std::int64_t i;
+#    else
+        std::int32_t i;
+#    endif
+        real         r;
+    } conv;
+
+    conv.i = excl;
+    SimdReal mask_pr_S(conv.r);
+
+    *interact_S0 = testBits(mask_pr_S & filter_S0);
+    *interact_S1 = testBits(mask_pr_S & filter_S1);
+    *interact_S2 = testBits(mask_pr_S & filter_S2);
+    *interact_S3 = testBits(mask_pr_S & filter_S3);
+#else
+    // Neither real or integer bitwise logical operations supported.
+    // Load masks from memory instead.
+    SimdReal zero = setZero();
+    *interact_S0  = (zero < load<SimdReal>(simd_interaction_array
+                                          + GMX_SIMD_REAL_WIDTH * ((excl >> (0 * UNROLLJ)) & 0xF)));
+    *interact_S1  = (zero < load<SimdReal>(simd_interaction_array
+                                          + GMX_SIMD_REAL_WIDTH * ((excl >> (1 * UNROLLJ)) & 0xF)));
+    *interact_S2  = (zero < load<SimdReal>(simd_interaction_array
+                                          + GMX_SIMD_REAL_WIDTH * ((excl >> (2 * UNROLLJ)) & 0xF)));
+    *interact_S3  = (zero < load<SimdReal>(simd_interaction_array
+                                          + GMX_SIMD_REAL_WIDTH * ((excl >> (3 * UNROLLJ)) & 0xF)));
+#endif
+}
+
+/* All functionality defines are set here, except for:
+ * CALC_ENERGIES, ENERGY_GROUPS which are defined before.
+ * CHECK_EXCLS, which is set just before including the inner loop contents.
+ * The combination rule defines, LJ_COMB_GEOM or LJ_COMB_LB are currently
+ * set before calling the kernel function. We might want to move that
+ * to inside the n-loop and have a different combination rule for different
+ * ci's, as no combination rule gives a 50% performance hit for LJ.
+ */
+
+/* We always calculate shift forces, because it's cheap anyhow */
+#define CALC_SHIFTFORCES
+
+/* Assumes all LJ parameters are identical */
+/* #define FIX_LJ_C */
diff --git a/src/include/gromacs/nbnxm/kernels_simd_4xm/kernel_inner.h b/src/include/gromacs/nbnxm/kernels_simd_4xm/kernel_inner.h
new file mode 100644 (file)
index 0000000..bd73f8d
--- /dev/null
@@ -0,0 +1,1224 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/* Doxygen gets confused (buggy) about the block in this file in combination with
+ * the  namespace prefix, and thinks store is documented here.
+ * This will solve itself with the second-generation nbnxn kernels, so for now
+ * we just tell Doxygen to stay out.
+ */
+#ifndef DOXYGEN
+
+/* This is the innermost loop contents for the 4 x N atom simd kernel.
+ * This flavor of the kernel calculates interactions of 4 i-atoms
+ * with N j-atoms stored in N wide simd registers.
+ */
+
+/* When calculating RF or Ewald interactions we calculate the electrostatic/LJ
+ * forces on excluded atom pairs here in the non-bonded loops.
+ * But when energies and/or virial is required we calculate them
+ * separately to as then it is easier to separate the energy and virial
+ * contributions.
+ */
+#    if defined CHECK_EXCLS && (defined CALC_COULOMB || defined LJ_EWALD_GEOM)
+#        define EXCL_FORCES
+#    endif
+
+{
+
+#    ifdef ENERGY_GROUPS
+    /* Energy group indices for two atoms packed into one int */
+    int egp_jj[UNROLLJ / 2];
+#    endif
+
+#    ifdef CHECK_EXCLS
+    /* Interaction (non-exclusion) mask of all 1's or 0's */
+    SimdBool interact_S0;
+    SimdBool interact_S1;
+    SimdBool interact_S2;
+    SimdBool interact_S3;
+#    endif
+
+    SimdReal jx_S, jy_S, jz_S;
+    SimdReal dx_S0, dy_S0, dz_S0;
+    SimdReal dx_S1, dy_S1, dz_S1;
+    SimdReal dx_S2, dy_S2, dz_S2;
+    SimdReal dx_S3, dy_S3, dz_S3;
+    SimdReal tx_S0, ty_S0, tz_S0;
+    SimdReal tx_S1, ty_S1, tz_S1;
+    SimdReal tx_S2, ty_S2, tz_S2;
+    SimdReal tx_S3, ty_S3, tz_S3;
+    SimdReal rsq_S0, rinv_S0, rinvsq_S0;
+    SimdReal rsq_S1, rinv_S1, rinvsq_S1;
+    SimdReal rsq_S2, rinv_S2, rinvsq_S2;
+    SimdReal rsq_S3, rinv_S3, rinvsq_S3;
+
+    /* wco: within cut-off, mask of all 1's or 0's */
+    SimdBool wco_S0;
+    SimdBool wco_S1;
+    SimdBool wco_S2;
+    SimdBool wco_S3;
+
+#    ifdef VDW_CUTOFF_CHECK
+    SimdBool wco_vdw_S0;
+    SimdBool wco_vdw_S1;
+#        ifndef HALF_LJ
+    SimdBool wco_vdw_S2;
+    SimdBool wco_vdw_S3;
+#        endif
+#    endif
+
+#    if (defined CALC_COULOMB && defined CALC_COUL_TAB) || defined LJ_FORCE_SWITCH || defined LJ_POT_SWITCH
+    SimdReal                                                                                  r_S0;
+    SimdReal                                                                                  r_S1;
+#        if (defined CALC_COULOMB && defined CALC_COUL_TAB) || !defined HALF_LJ
+    SimdReal r_S2;
+    SimdReal r_S3;
+#        endif
+#    endif
+
+#    if defined LJ_FORCE_SWITCH || defined LJ_POT_SWITCH
+    SimdReal                               rsw_S0, rsw2_S0;
+    SimdReal                               rsw_S1, rsw2_S1;
+#        ifndef HALF_LJ
+    SimdReal rsw_S2, rsw2_S2;
+    SimdReal rsw_S3, rsw2_S3;
+#        endif
+#    endif
+
+#    ifdef CALC_COULOMB
+#        ifdef CHECK_EXCLS
+    /* 1/r masked with the interaction mask */
+    SimdReal rinv_ex_S0;
+    SimdReal rinv_ex_S1;
+    SimdReal rinv_ex_S2;
+    SimdReal rinv_ex_S3;
+#        endif
+    SimdReal jq_S;
+    SimdReal qq_S0;
+    SimdReal qq_S1;
+    SimdReal qq_S2;
+    SimdReal qq_S3;
+#        ifdef CALC_COUL_TAB
+    /* The force (PME mesh force) we need to subtract from 1/r^2 */
+    SimdReal fsub_S0;
+    SimdReal fsub_S1;
+    SimdReal fsub_S2;
+    SimdReal fsub_S3;
+#        endif
+#        ifdef CALC_COUL_EWALD
+    SimdReal brsq_S0, brsq_S1, brsq_S2, brsq_S3;
+    SimdReal ewcorr_S0, ewcorr_S1, ewcorr_S2, ewcorr_S3;
+#        endif
+
+    /* frcoul = (1/r - fsub)*r */
+    SimdReal frcoul_S0;
+    SimdReal frcoul_S1;
+    SimdReal frcoul_S2;
+    SimdReal frcoul_S3;
+#        ifdef CALC_COUL_TAB
+    /* For tables: r, rs=r/sp, rf=floor(rs), frac=rs-rf */
+    SimdReal rs_S0, rf_S0, frac_S0;
+    SimdReal rs_S1, rf_S1, frac_S1;
+    SimdReal rs_S2, rf_S2, frac_S2;
+    SimdReal rs_S3, rf_S3, frac_S3;
+    /* Table index: rs truncated to an int */
+    SimdInt32 ti_S0, ti_S1, ti_S2, ti_S3;
+    /* Linear force table values */
+    SimdReal ctab0_S0, ctab1_S0;
+    SimdReal ctab0_S1, ctab1_S1;
+    SimdReal ctab0_S2, ctab1_S2;
+    SimdReal ctab0_S3, ctab1_S3;
+#            ifdef CALC_ENERGIES
+    /* Quadratic energy table value */
+    SimdReal ctabv_S0, dum_S0;
+    SimdReal ctabv_S1, dum_S1;
+    SimdReal ctabv_S2, dum_S2;
+    SimdReal ctabv_S3, dum_S3;
+#            endif
+#        endif
+#        if defined CALC_ENERGIES && (defined CALC_COUL_EWALD || defined CALC_COUL_TAB)
+    /* The potential (PME mesh) we need to subtract from 1/r */
+    SimdReal vc_sub_S0;
+    SimdReal vc_sub_S1;
+    SimdReal vc_sub_S2;
+    SimdReal vc_sub_S3;
+#        endif
+#        ifdef CALC_ENERGIES
+    /* Electrostatic potential */
+    SimdReal vcoul_S0;
+    SimdReal vcoul_S1;
+    SimdReal vcoul_S2;
+    SimdReal vcoul_S3;
+#        endif
+#    endif
+    /* The force times 1/r */
+    SimdReal fscal_S0;
+    SimdReal fscal_S1;
+    SimdReal fscal_S2;
+    SimdReal fscal_S3;
+
+#    ifdef CALC_LJ
+#        ifdef LJ_COMB_LB
+    /* LJ sigma_j/2 and sqrt(epsilon_j) */
+    SimdReal hsig_j_S, seps_j_S;
+    /* LJ sigma_ij and epsilon_ij */
+    SimdReal sig_S0, eps_S0;
+    SimdReal sig_S1, eps_S1;
+#            ifndef HALF_LJ
+    SimdReal sig_S2, eps_S2;
+    SimdReal sig_S3, eps_S3;
+#            endif
+#            ifdef CALC_ENERGIES
+    SimdReal sig2_S0, sig6_S0;
+    SimdReal sig2_S1, sig6_S1;
+#                ifndef HALF_LJ
+    SimdReal sig2_S2, sig6_S2;
+    SimdReal sig2_S3, sig6_S3;
+#                endif
+#            endif /* LJ_COMB_LB */
+#        endif     /* CALC_LJ */
+
+#        ifdef LJ_COMB_GEOM
+    SimdReal c6s_j_S, c12s_j_S;
+#        endif
+
+    /* Intermediate variables for LJ calculation */
+#        ifndef LJ_COMB_LB
+    SimdReal rinvsix_S0;
+    SimdReal rinvsix_S1;
+#            ifndef HALF_LJ
+    SimdReal rinvsix_S2;
+    SimdReal rinvsix_S3;
+#            endif
+#        endif
+#        ifdef LJ_COMB_LB
+    SimdReal sir_S0, sir2_S0, sir6_S0;
+    SimdReal sir_S1, sir2_S1, sir6_S1;
+#            ifndef HALF_LJ
+    SimdReal sir_S2, sir2_S2, sir6_S2;
+    SimdReal sir_S3, sir2_S3, sir6_S3;
+#            endif
+#        endif
+
+    SimdReal FrLJ6_S0, FrLJ12_S0, frLJ_S0;
+    SimdReal FrLJ6_S1, FrLJ12_S1, frLJ_S1;
+#        ifndef HALF_LJ
+    SimdReal FrLJ6_S2, FrLJ12_S2, frLJ_S2;
+    SimdReal FrLJ6_S3, FrLJ12_S3, frLJ_S3;
+#        endif
+#    endif /* CALC_LJ */
+
+    /* j-cluster index */
+    const int cj = l_cj[cjind].cj;
+
+    /* Atom indices (of the first atom in the cluster) */
+    const int   aj = cj * UNROLLJ;
+#    if defined CALC_LJ && (defined LJ_COMB_GEOM || defined LJ_COMB_LB || defined LJ_EWALD_GEOM)
+    /* Index for loading LJ parameters, complicated when interleaving */
+#        if UNROLLJ == STRIDE
+    const int aj2 = aj * 2;
+#        else
+    const int aj2 = (cj >> 1) * 2 * STRIDE + (cj & 1) * UNROLLJ;
+#        endif
+#    endif
+#    if UNROLLJ == STRIDE
+    const int ajx = aj * DIM;
+#    else
+    const int ajx = (cj >> 1) * DIM * STRIDE + (cj & 1) * UNROLLJ;
+#    endif
+    const int ajy = ajx + STRIDE;
+    const int ajz = ajy + STRIDE;
+
+#    ifdef CHECK_EXCLS
+    gmx_load_simd_4xn_interactions(static_cast<int>(l_cj[cjind].excl),
+                                   filter_S0,
+                                   filter_S1,
+                                   filter_S2,
+                                   filter_S3,
+                                   nbat->simdMasks.interaction_array.data(),
+                                   &interact_S0,
+                                   &interact_S1,
+                                   &interact_S2,
+                                   &interact_S3);
+#    endif /* CHECK_EXCLS */
+
+    /* load j atom coordinates */
+    jx_S = load<SimdReal>(x + ajx);
+    jy_S = load<SimdReal>(x + ajy);
+    jz_S = load<SimdReal>(x + ajz);
+
+    /* Calculate distance */
+    dx_S0 = ix_S0 - jx_S;
+    dy_S0 = iy_S0 - jy_S;
+    dz_S0 = iz_S0 - jz_S;
+    dx_S1 = ix_S1 - jx_S;
+    dy_S1 = iy_S1 - jy_S;
+    dz_S1 = iz_S1 - jz_S;
+    dx_S2 = ix_S2 - jx_S;
+    dy_S2 = iy_S2 - jy_S;
+    dz_S2 = iz_S2 - jz_S;
+    dx_S3 = ix_S3 - jx_S;
+    dy_S3 = iy_S3 - jy_S;
+    dz_S3 = iz_S3 - jz_S;
+
+    /* rsq = dx*dx+dy*dy+dz*dz */
+    rsq_S0 = norm2(dx_S0, dy_S0, dz_S0);
+    rsq_S1 = norm2(dx_S1, dy_S1, dz_S1);
+    rsq_S2 = norm2(dx_S2, dy_S2, dz_S2);
+    rsq_S3 = norm2(dx_S3, dy_S3, dz_S3);
+
+    /* Do the cut-off check */
+    wco_S0 = (rsq_S0 < rc2_S);
+    wco_S1 = (rsq_S1 < rc2_S);
+    wco_S2 = (rsq_S2 < rc2_S);
+    wco_S3 = (rsq_S3 < rc2_S);
+
+#    ifdef CHECK_EXCLS
+#        ifdef EXCL_FORCES
+    /* Only remove the (sub-)diagonal to avoid double counting */
+#            if UNROLLJ == UNROLLI
+    if (cj == ci_sh)
+    {
+        wco_S0 = wco_S0 && diagonal_mask_S0;
+        wco_S1 = wco_S1 && diagonal_mask_S1;
+        wco_S2 = wco_S2 && diagonal_mask_S2;
+        wco_S3 = wco_S3 && diagonal_mask_S3;
+    }
+#            else
+#                if UNROLLJ < UNROLLI
+    if (cj == ci_sh * 2)
+    {
+        wco_S0 = wco_S0 && diagonal_mask0_S0;
+        wco_S1 = wco_S1 && diagonal_mask0_S1;
+        wco_S2 = wco_S2 && diagonal_mask0_S2;
+        wco_S3 = wco_S3 && diagonal_mask0_S3;
+    }
+    if (cj == ci_sh * 2 + 1)
+    {
+        wco_S0 = wco_S0 && diagonal_mask1_S0;
+        wco_S1 = wco_S1 && diagonal_mask1_S1;
+        wco_S2 = wco_S2 && diagonal_mask1_S2;
+        wco_S3 = wco_S3 && diagonal_mask1_S3;
+    }
+#                else
+    if (cj * 2 == ci_sh)
+    {
+        wco_S0 = wco_S0 && diagonal_mask0_S0;
+        wco_S1 = wco_S1 && diagonal_mask0_S1;
+        wco_S2 = wco_S2 && diagonal_mask0_S2;
+        wco_S3 = wco_S3 && diagonal_mask0_S3;
+    }
+    else if (cj * 2 + 1 == ci_sh)
+    {
+        wco_S0 = wco_S0 && diagonal_mask1_S0;
+        wco_S1 = wco_S1 && diagonal_mask1_S1;
+        wco_S2 = wco_S2 && diagonal_mask1_S2;
+        wco_S3 = wco_S3 && diagonal_mask1_S3;
+    }
+#                endif
+#            endif
+#        else /* EXCL_FORCES */
+    /* No exclusion forces: remove all excluded atom pairs from the list */
+    wco_S0 = wco_S0 && interact_S0;
+    wco_S1 = wco_S1 && interact_S1;
+    wco_S2 = wco_S2 && interact_S2;
+    wco_S3 = wco_S3 && interact_S3;
+#        endif
+#    endif
+
+#    ifdef COUNT_PAIRS
+    {
+        int                              i, j;
+        alignas(GMX_SIMD_ALIGNMENT) real tmp[2 * GMX_SIMD_REAL_WIDTH];
+
+        for (i = 0; i < UNROLLI; i++)
+        {
+            store(tmp, rc2_S - (i == 0 ? rsq_S0 : (i == 1 ? rsq_S1 : (i == 2 ? rsq_S2 : rsq_S3))));
+            for (j = 0; j < UNROLLJ; j++)
+            {
+                if (tmp[j] >= 0)
+                {
+                    npair++;
+                }
+            }
+        }
+    }
+#    endif
+
+    // Ensure the distances do not fall below the limit where r^-12 overflows.
+    // This should never happen for normal interactions.
+    rsq_S0 = max(rsq_S0, minRsq_S);
+    rsq_S1 = max(rsq_S1, minRsq_S);
+    rsq_S2 = max(rsq_S2, minRsq_S);
+    rsq_S3 = max(rsq_S3, minRsq_S);
+
+    /* Calculate 1/r */
+#    if !GMX_DOUBLE
+    rinv_S0 = invsqrt(rsq_S0);
+    rinv_S1 = invsqrt(rsq_S1);
+    rinv_S2 = invsqrt(rsq_S2);
+    rinv_S3 = invsqrt(rsq_S3);
+#    else
+    invsqrtPair(rsq_S0, rsq_S1, &rinv_S0, &rinv_S1);
+    invsqrtPair(rsq_S2, rsq_S3, &rinv_S2, &rinv_S3);
+#    endif
+
+#    ifdef CALC_COULOMB
+    /* Load parameters for j atom */
+    jq_S  = load<SimdReal>(q + aj);
+    qq_S0 = iq_S0 * jq_S;
+    qq_S1 = iq_S1 * jq_S;
+    qq_S2 = iq_S2 * jq_S;
+    qq_S3 = iq_S3 * jq_S;
+#    endif
+
+#    ifdef CALC_LJ
+#        if !defined LJ_COMB_GEOM && !defined LJ_COMB_LB && !defined FIX_LJ_C
+    SimdReal c6_S0, c6_S1, c12_S0, c12_S1;
+    gatherLoadTranspose<c_simdBestPairAlignment>(nbfp0, type + aj, &c6_S0, &c12_S0);
+    gatherLoadTranspose<c_simdBestPairAlignment>(nbfp1, type + aj, &c6_S1, &c12_S1);
+#            ifndef HALF_LJ
+    SimdReal c6_S2, c6_S3, c12_S2, c12_S3;
+    gatherLoadTranspose<c_simdBestPairAlignment>(nbfp2, type + aj, &c6_S2, &c12_S2);
+    gatherLoadTranspose<c_simdBestPairAlignment>(nbfp3, type + aj, &c6_S3, &c12_S3);
+#            endif
+#        endif /* not defined any LJ rule */
+
+#        ifdef LJ_COMB_GEOM
+    c6s_j_S        = load<SimdReal>(ljc + aj2 + 0);
+    c12s_j_S       = load<SimdReal>(ljc + aj2 + STRIDE);
+    SimdReal c6_S0 = c6s_S0 * c6s_j_S;
+    SimdReal c6_S1 = c6s_S1 * c6s_j_S;
+#            ifndef HALF_LJ
+    SimdReal c6_S2 = c6s_S2 * c6s_j_S;
+    SimdReal c6_S3 = c6s_S3 * c6s_j_S;
+#            endif
+    SimdReal c12_S0 = c12s_S0 * c12s_j_S;
+    SimdReal c12_S1 = c12s_S1 * c12s_j_S;
+#            ifndef HALF_LJ
+    SimdReal c12_S2 = c12s_S2 * c12s_j_S;
+    SimdReal c12_S3 = c12s_S3 * c12s_j_S;
+#            endif
+#        endif /* LJ_COMB_GEOM */
+
+#        ifdef LJ_COMB_LB
+    hsig_j_S = load<SimdReal>(ljc + aj2 + 0);
+    seps_j_S = load<SimdReal>(ljc + aj2 + STRIDE);
+
+    sig_S0 = hsig_i_S0 + hsig_j_S;
+    sig_S1 = hsig_i_S1 + hsig_j_S;
+    eps_S0 = seps_i_S0 * seps_j_S;
+    eps_S1 = seps_i_S1 * seps_j_S;
+#            ifndef HALF_LJ
+    sig_S2 = hsig_i_S2 + hsig_j_S;
+    sig_S3 = hsig_i_S3 + hsig_j_S;
+    eps_S2 = seps_i_S2 * seps_j_S;
+    eps_S3 = seps_i_S3 * seps_j_S;
+#            endif
+#        endif /* LJ_COMB_LB */
+
+#    endif /* CALC_LJ */
+
+    /* Set rinv to zero for r beyond the cut-off */
+    rinv_S0 = selectByMask(rinv_S0, wco_S0);
+    rinv_S1 = selectByMask(rinv_S1, wco_S1);
+    rinv_S2 = selectByMask(rinv_S2, wco_S2);
+    rinv_S3 = selectByMask(rinv_S3, wco_S3);
+
+    rinvsq_S0 = rinv_S0 * rinv_S0;
+    rinvsq_S1 = rinv_S1 * rinv_S1;
+    rinvsq_S2 = rinv_S2 * rinv_S2;
+    rinvsq_S3 = rinv_S3 * rinv_S3;
+
+#    ifdef CALC_COULOMB
+    /* Note that here we calculate force*r, not the usual force/r.
+     * This allows avoiding masking the reaction-field contribution,
+     * as frcoul is later multiplied by rinvsq which has been
+     * masked with the cut-off check.
+     */
+
+#        ifdef EXCL_FORCES
+    /* Only add 1/r for non-excluded atom pairs */
+    rinv_ex_S0 = selectByMask(rinv_S0, interact_S0);
+    rinv_ex_S1 = selectByMask(rinv_S1, interact_S1);
+    rinv_ex_S2 = selectByMask(rinv_S2, interact_S2);
+    rinv_ex_S3 = selectByMask(rinv_S3, interact_S3);
+#        else
+    /* No exclusion forces, we always need 1/r */
+#            define rinv_ex_S0 rinv_S0
+#            define rinv_ex_S1 rinv_S1
+#            define rinv_ex_S2 rinv_S2
+#            define rinv_ex_S3 rinv_S3
+#        endif
+
+#        ifdef CALC_COUL_RF
+    /* Electrostatic interactions */
+    frcoul_S0 = qq_S0 * fma(rsq_S0, mrc_3_S, rinv_ex_S0);
+    frcoul_S1 = qq_S1 * fma(rsq_S1, mrc_3_S, rinv_ex_S1);
+    frcoul_S2 = qq_S2 * fma(rsq_S2, mrc_3_S, rinv_ex_S2);
+    frcoul_S3 = qq_S3 * fma(rsq_S3, mrc_3_S, rinv_ex_S3);
+
+#            ifdef CALC_ENERGIES
+    vcoul_S0 = qq_S0 * (rinv_ex_S0 + fma(rsq_S0, hrc_3_S, moh_rc_S));
+    vcoul_S1 = qq_S1 * (rinv_ex_S1 + fma(rsq_S1, hrc_3_S, moh_rc_S));
+    vcoul_S2 = qq_S2 * (rinv_ex_S2 + fma(rsq_S2, hrc_3_S, moh_rc_S));
+    vcoul_S3 = qq_S3 * (rinv_ex_S3 + fma(rsq_S3, hrc_3_S, moh_rc_S));
+#            endif
+#        endif
+
+#        ifdef CALC_COUL_EWALD
+    /* We need to mask (or limit) rsq for the cut-off,
+     * as large distances can cause an overflow in gmx_pmecorrF/V.
+     */
+    brsq_S0   = beta2_S * selectByMask(rsq_S0, wco_S0);
+    brsq_S1   = beta2_S * selectByMask(rsq_S1, wco_S1);
+    brsq_S2   = beta2_S * selectByMask(rsq_S2, wco_S2);
+    brsq_S3   = beta2_S * selectByMask(rsq_S3, wco_S3);
+    ewcorr_S0 = beta_S * pmeForceCorrection(brsq_S0);
+    ewcorr_S1 = beta_S * pmeForceCorrection(brsq_S1);
+    ewcorr_S2 = beta_S * pmeForceCorrection(brsq_S2);
+    ewcorr_S3 = beta_S * pmeForceCorrection(brsq_S3);
+    frcoul_S0 = qq_S0 * fma(ewcorr_S0, brsq_S0, rinv_ex_S0);
+    frcoul_S1 = qq_S1 * fma(ewcorr_S1, brsq_S1, rinv_ex_S1);
+    frcoul_S2 = qq_S2 * fma(ewcorr_S2, brsq_S2, rinv_ex_S2);
+    frcoul_S3 = qq_S3 * fma(ewcorr_S3, brsq_S3, rinv_ex_S3);
+
+#            ifdef CALC_ENERGIES
+    vc_sub_S0 = beta_S * pmePotentialCorrection(brsq_S0);
+    vc_sub_S1 = beta_S * pmePotentialCorrection(brsq_S1);
+    vc_sub_S2 = beta_S * pmePotentialCorrection(brsq_S2);
+    vc_sub_S3 = beta_S * pmePotentialCorrection(brsq_S3);
+#            endif
+
+#        endif /* CALC_COUL_EWALD */
+
+#        ifdef CALC_COUL_TAB
+    /* Electrostatic interactions */
+    r_S0 = rsq_S0 * rinv_S0;
+    r_S1 = rsq_S1 * rinv_S1;
+    r_S2 = rsq_S2 * rinv_S2;
+    r_S3 = rsq_S3 * rinv_S3;
+    /* Convert r to scaled table units */
+    rs_S0 = r_S0 * invtsp_S;
+    rs_S1 = r_S1 * invtsp_S;
+    rs_S2 = r_S2 * invtsp_S;
+    rs_S3 = r_S3 * invtsp_S;
+    /* Truncate scaled r to an int */
+    ti_S0 = cvttR2I(rs_S0);
+    ti_S1 = cvttR2I(rs_S1);
+    ti_S2 = cvttR2I(rs_S2);
+    ti_S3 = cvttR2I(rs_S3);
+
+    rf_S0 = trunc(rs_S0);
+    rf_S1 = trunc(rs_S1);
+    rf_S2 = trunc(rs_S2);
+    rf_S3 = trunc(rs_S3);
+
+    frac_S0 = rs_S0 - rf_S0;
+    frac_S1 = rs_S1 - rf_S1;
+    frac_S2 = rs_S2 - rf_S2;
+    frac_S3 = rs_S3 - rf_S3;
+
+    /* Load and interpolate table forces and possibly energies.
+     * Force and energy can be combined in one table, stride 4: FDV0
+     * or in two separate tables with stride 1: F and V
+     * Currently single precision uses FDV0, double F and V.
+     */
+#            ifndef CALC_ENERGIES
+#                ifdef TAB_FDV0
+    gatherLoadBySimdIntTranspose<4>(tab_coul_F, ti_S0, &ctab0_S0, &ctab1_S0);
+    gatherLoadBySimdIntTranspose<4>(tab_coul_F, ti_S1, &ctab0_S1, &ctab1_S1);
+    gatherLoadBySimdIntTranspose<4>(tab_coul_F, ti_S2, &ctab0_S2, &ctab1_S2);
+    gatherLoadBySimdIntTranspose<4>(tab_coul_F, ti_S3, &ctab0_S3, &ctab1_S3);
+#                else
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_F, ti_S0, &ctab0_S0, &ctab1_S0);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_F, ti_S1, &ctab0_S1, &ctab1_S1);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_F, ti_S2, &ctab0_S2, &ctab1_S2);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_F, ti_S3, &ctab0_S3, &ctab1_S3);
+    ctab1_S0  = ctab1_S0 - ctab0_S0;
+    ctab1_S1  = ctab1_S1 - ctab0_S1;
+    ctab1_S2  = ctab1_S2 - ctab0_S2;
+    ctab1_S3  = ctab1_S3 - ctab0_S3;
+#                endif
+#            else
+#                ifdef TAB_FDV0
+    gatherLoadBySimdIntTranspose<4>(tab_coul_F, ti_S0, &ctab0_S0, &ctab1_S0, &ctabv_S0, &dum_S0);
+    gatherLoadBySimdIntTranspose<4>(tab_coul_F, ti_S1, &ctab0_S1, &ctab1_S1, &ctabv_S1, &dum_S1);
+    gatherLoadBySimdIntTranspose<4>(tab_coul_F, ti_S2, &ctab0_S2, &ctab1_S2, &ctabv_S2, &dum_S2);
+    gatherLoadBySimdIntTranspose<4>(tab_coul_F, ti_S3, &ctab0_S3, &ctab1_S3, &ctabv_S3, &dum_S3);
+#                else
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_F, ti_S0, &ctab0_S0, &ctab1_S0);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_V, ti_S0, &ctabv_S0, &dum_S0);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_F, ti_S1, &ctab0_S1, &ctab1_S1);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_V, ti_S1, &ctabv_S1, &dum_S1);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_F, ti_S2, &ctab0_S2, &ctab1_S2);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_V, ti_S2, &ctabv_S2, &dum_S2);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_F, ti_S3, &ctab0_S3, &ctab1_S3);
+    gatherLoadUBySimdIntTranspose<1>(tab_coul_V, ti_S3, &ctabv_S3, &dum_S3);
+    ctab1_S0 = ctab1_S0 - ctab0_S0;
+    ctab1_S1 = ctab1_S1 - ctab0_S1;
+    ctab1_S2 = ctab1_S2 - ctab0_S2;
+    ctab1_S3 = ctab1_S3 - ctab0_S3;
+#                endif
+#            endif
+    fsub_S0   = fma(frac_S0, ctab1_S0, ctab0_S0);
+    fsub_S1   = fma(frac_S1, ctab1_S1, ctab0_S1);
+    fsub_S2   = fma(frac_S2, ctab1_S2, ctab0_S2);
+    fsub_S3   = fma(frac_S3, ctab1_S3, ctab0_S3);
+    frcoul_S0 = qq_S0 * fnma(fsub_S0, r_S0, rinv_ex_S0);
+    frcoul_S1 = qq_S1 * fnma(fsub_S1, r_S1, rinv_ex_S1);
+    frcoul_S2 = qq_S2 * fnma(fsub_S2, r_S2, rinv_ex_S2);
+    frcoul_S3 = qq_S3 * fnma(fsub_S3, r_S3, rinv_ex_S3);
+
+#            ifdef CALC_ENERGIES
+    vc_sub_S0 = fma((mhalfsp_S * frac_S0), (ctab0_S0 + fsub_S0), ctabv_S0);
+    vc_sub_S1 = fma((mhalfsp_S * frac_S1), (ctab0_S1 + fsub_S1), ctabv_S1);
+    vc_sub_S2 = fma((mhalfsp_S * frac_S2), (ctab0_S2 + fsub_S2), ctabv_S2);
+    vc_sub_S3 = fma((mhalfsp_S * frac_S3), (ctab0_S3 + fsub_S3), ctabv_S3);
+#            endif
+#        endif /* CALC_COUL_TAB */
+
+#        if defined CALC_ENERGIES && (defined CALC_COUL_EWALD || defined CALC_COUL_TAB)
+#            ifndef NO_SHIFT_EWALD
+    /* Add Ewald potential shift to vc_sub for convenience */
+#                ifdef CHECK_EXCLS
+    vc_sub_S0 = vc_sub_S0 + selectByMask(sh_ewald_S, interact_S0);
+    vc_sub_S1 = vc_sub_S1 + selectByMask(sh_ewald_S, interact_S1);
+    vc_sub_S2 = vc_sub_S2 + selectByMask(sh_ewald_S, interact_S2);
+    vc_sub_S3 = vc_sub_S3 + selectByMask(sh_ewald_S, interact_S3);
+#                else
+    vc_sub_S0 = vc_sub_S0 + sh_ewald_S;
+    vc_sub_S1 = vc_sub_S1 + sh_ewald_S;
+    vc_sub_S2 = vc_sub_S2 + sh_ewald_S;
+    vc_sub_S3 = vc_sub_S3 + sh_ewald_S;
+#                endif
+#            endif
+
+    vcoul_S0 = qq_S0 * (rinv_ex_S0 - vc_sub_S0);
+    vcoul_S1 = qq_S1 * (rinv_ex_S1 - vc_sub_S1);
+    vcoul_S2 = qq_S2 * (rinv_ex_S2 - vc_sub_S2);
+    vcoul_S3 = qq_S3 * (rinv_ex_S3 - vc_sub_S3);
+
+#        endif
+
+#        ifdef CALC_ENERGIES
+    /* Mask energy for cut-off and diagonal */
+    vcoul_S0 = selectByMask(vcoul_S0, wco_S0);
+    vcoul_S1 = selectByMask(vcoul_S1, wco_S1);
+    vcoul_S2 = selectByMask(vcoul_S2, wco_S2);
+    vcoul_S3 = selectByMask(vcoul_S3, wco_S3);
+#        endif
+
+#    endif /* CALC_COULOMB */
+
+#    ifdef CALC_LJ
+    /* Lennard-Jones interaction */
+
+#        ifdef VDW_CUTOFF_CHECK
+    wco_vdw_S0 = (rsq_S0 < rcvdw2_S);
+    wco_vdw_S1 = (rsq_S1 < rcvdw2_S);
+#            ifndef HALF_LJ
+    wco_vdw_S2 = (rsq_S2 < rcvdw2_S);
+    wco_vdw_S3 = (rsq_S3 < rcvdw2_S);
+#            endif
+#        else
+    /* Same cut-off for Coulomb and VdW, reuse the registers */
+#            define wco_vdw_S0 wco_S0
+#            define wco_vdw_S1 wco_S1
+#            define wco_vdw_S2 wco_S2
+#            define wco_vdw_S3 wco_S3
+#        endif
+
+#        ifndef LJ_COMB_LB
+    rinvsix_S0 = rinvsq_S0 * rinvsq_S0 * rinvsq_S0;
+    rinvsix_S1 = rinvsq_S1 * rinvsq_S1 * rinvsq_S1;
+#            ifdef EXCL_FORCES
+    rinvsix_S0 = selectByMask(rinvsix_S0, interact_S0);
+    rinvsix_S1 = selectByMask(rinvsix_S1, interact_S1);
+#            endif
+#            ifndef HALF_LJ
+    rinvsix_S2 = rinvsq_S2 * rinvsq_S2 * rinvsq_S2;
+    rinvsix_S3 = rinvsq_S3 * rinvsq_S3 * rinvsq_S3;
+#                ifdef EXCL_FORCES
+    rinvsix_S2 = selectByMask(rinvsix_S2, interact_S2);
+    rinvsix_S3 = selectByMask(rinvsix_S3, interact_S3);
+#                endif
+#            endif
+
+#            if defined LJ_CUT || defined LJ_POT_SWITCH
+    /* We have plain LJ or LJ-PME with simple C6/6 C12/12 coefficients */
+    FrLJ6_S0 = c6_S0 * rinvsix_S0;
+    FrLJ6_S1 = c6_S1 * rinvsix_S1;
+#                ifndef HALF_LJ
+    FrLJ6_S2 = c6_S2 * rinvsix_S2;
+    FrLJ6_S3 = c6_S3 * rinvsix_S3;
+#                endif
+    FrLJ12_S0 = c12_S0 * rinvsix_S0 * rinvsix_S0;
+    FrLJ12_S1 = c12_S1 * rinvsix_S1 * rinvsix_S1;
+#                ifndef HALF_LJ
+    FrLJ12_S2 = c12_S2 * rinvsix_S2 * rinvsix_S2;
+    FrLJ12_S3 = c12_S3 * rinvsix_S3 * rinvsix_S3;
+#                endif
+#            endif
+
+#            if defined LJ_FORCE_SWITCH || defined LJ_POT_SWITCH
+    /* We switch the LJ force */
+    r_S0    = rsq_S0 * rinv_S0;
+    rsw_S0  = max(r_S0 - rswitch_S, zero_S);
+    rsw2_S0 = rsw_S0 * rsw_S0;
+    r_S1    = rsq_S1 * rinv_S1;
+    rsw_S1  = max(r_S1 - rswitch_S, zero_S);
+    rsw2_S1 = rsw_S1 * rsw_S1;
+#                ifndef HALF_LJ
+    r_S2    = rsq_S2 * rinv_S2;
+    rsw_S2  = max(r_S2 - rswitch_S, zero_S);
+    rsw2_S2 = rsw_S2 * rsw_S2;
+    r_S3    = rsq_S3 * rinv_S3;
+    rsw_S3  = max(r_S3 - rswitch_S, zero_S);
+    rsw2_S3 = rsw_S3 * rsw_S3;
+#                endif
+#            endif
+
+#            ifdef LJ_FORCE_SWITCH
+
+#                define gmx_add_fr_switch(fr, rsw, rsw2_r, c2, c3) \
+                    fma(fma(c3, rsw, c2), rsw2_r, (fr))
+    SimdReal rsw2_r_S0 = rsw2_S0 * r_S0;
+    SimdReal rsw2_r_S1 = rsw2_S1 * r_S1;
+    FrLJ6_S0 = c6_S0 * gmx_add_fr_switch(rinvsix_S0, rsw_S0, rsw2_r_S0, p6_fc2_S, p6_fc3_S);
+    FrLJ6_S1 = c6_S1 * gmx_add_fr_switch(rinvsix_S1, rsw_S1, rsw2_r_S1, p6_fc2_S, p6_fc3_S);
+#                ifndef HALF_LJ
+    SimdReal rsw2_r_S2 = rsw2_S2 * r_S2;
+    SimdReal rsw2_r_S3 = rsw2_S3 * r_S3;
+    FrLJ6_S2 = c6_S2 * gmx_add_fr_switch(rinvsix_S2, rsw_S2, rsw2_r_S2, p6_fc2_S, p6_fc3_S);
+    FrLJ6_S3 = c6_S3 * gmx_add_fr_switch(rinvsix_S3, rsw_S3, rsw2_r_S3, p6_fc2_S, p6_fc3_S);
+#                endif
+    FrLJ12_S0 = c12_S0 * gmx_add_fr_switch((rinvsix_S0 * rinvsix_S0), rsw_S0, rsw2_r_S0, p12_fc2_S, p12_fc3_S);
+    FrLJ12_S1 = c12_S1 * gmx_add_fr_switch((rinvsix_S1 * rinvsix_S1), rsw_S1, rsw2_r_S1, p12_fc2_S, p12_fc3_S);
+#                ifndef HALF_LJ
+    FrLJ12_S2 = c12_S2 * gmx_add_fr_switch((rinvsix_S2 * rinvsix_S2), rsw_S2, rsw2_r_S2, p12_fc2_S, p12_fc3_S);
+    FrLJ12_S3 = c12_S3 * gmx_add_fr_switch((rinvsix_S3 * rinvsix_S3), rsw_S3, rsw2_r_S3, p12_fc2_S, p12_fc3_S);
+#                endif
+#                undef gmx_add_fr_switch
+#            endif /* LJ_FORCE_SWITCH */
+
+#        endif /* not LJ_COMB_LB */
+
+#        ifdef LJ_COMB_LB
+    sir_S0 = sig_S0 * rinv_S0;
+    sir_S1 = sig_S1 * rinv_S1;
+#            ifndef HALF_LJ
+    sir_S2 = sig_S2 * rinv_S2;
+    sir_S3 = sig_S3 * rinv_S3;
+#            endif
+    sir2_S0 = sir_S0 * sir_S0;
+    sir2_S1 = sir_S1 * sir_S1;
+#            ifndef HALF_LJ
+    sir2_S2 = sir_S2 * sir_S2;
+    sir2_S3 = sir_S3 * sir_S3;
+#            endif
+    sir6_S0 = sir2_S0 * sir2_S0 * sir2_S0;
+    sir6_S1 = sir2_S1 * sir2_S1 * sir2_S1;
+#            ifdef EXCL_FORCES
+    sir6_S0 = selectByMask(sir6_S0, interact_S0);
+    sir6_S1 = selectByMask(sir6_S1, interact_S1);
+#            endif
+#            ifndef HALF_LJ
+    sir6_S2 = sir2_S2 * sir2_S2 * sir2_S2;
+    sir6_S3 = sir2_S3 * sir2_S3 * sir2_S3;
+#                ifdef EXCL_FORCES
+    sir6_S2 = selectByMask(sir6_S2, interact_S2);
+    sir6_S3 = selectByMask(sir6_S3, interact_S3);
+#                endif
+#            endif
+#            ifdef VDW_CUTOFF_CHECK
+    sir6_S0 = selectByMask(sir6_S0, wco_vdw_S0);
+    sir6_S1 = selectByMask(sir6_S1, wco_vdw_S1);
+#                ifndef HALF_LJ
+    sir6_S2 = selectByMask(sir6_S2, wco_vdw_S2);
+    sir6_S3 = selectByMask(sir6_S3, wco_vdw_S3);
+#                endif
+#            endif
+    FrLJ6_S0 = eps_S0 * sir6_S0;
+    FrLJ6_S1 = eps_S1 * sir6_S1;
+#            ifndef HALF_LJ
+    FrLJ6_S2 = eps_S2 * sir6_S2;
+    FrLJ6_S3 = eps_S3 * sir6_S3;
+#            endif
+    FrLJ12_S0 = FrLJ6_S0 * sir6_S0;
+    FrLJ12_S1 = FrLJ6_S1 * sir6_S1;
+#            ifndef HALF_LJ
+    FrLJ12_S2 = FrLJ6_S2 * sir6_S2;
+    FrLJ12_S3 = FrLJ6_S3 * sir6_S3;
+#            endif
+#            if defined CALC_ENERGIES
+    /* We need C6 and C12 to calculate the LJ potential shift */
+    sig2_S0 = sig_S0 * sig_S0;
+    sig2_S1 = sig_S1 * sig_S1;
+#                ifndef HALF_LJ
+    sig2_S2 = sig_S2 * sig_S2;
+    sig2_S3 = sig_S3 * sig_S3;
+#                endif
+    sig6_S0 = sig2_S0 * sig2_S0 * sig2_S0;
+    sig6_S1 = sig2_S1 * sig2_S1 * sig2_S1;
+#                ifndef HALF_LJ
+    sig6_S2 = sig2_S2 * sig2_S2 * sig2_S2;
+    sig6_S3 = sig2_S3 * sig2_S3 * sig2_S3;
+#                endif
+    SimdReal c6_S0 = eps_S0 * sig6_S0;
+    SimdReal c6_S1 = eps_S1 * sig6_S1;
+#                ifndef HALF_LJ
+    SimdReal c6_S2 = eps_S2 * sig6_S2;
+    SimdReal c6_S3 = eps_S3 * sig6_S3;
+#                endif
+    SimdReal c12_S0 = c6_S0 * sig6_S0;
+    SimdReal c12_S1 = c6_S1 * sig6_S1;
+#                ifndef HALF_LJ
+    SimdReal c12_S2 = c6_S2 * sig6_S2;
+    SimdReal c12_S3 = c6_S3 * sig6_S3;
+#                endif
+#            endif
+#        endif /* LJ_COMB_LB */
+
+    /* Determine the total scalar LJ force*r */
+    frLJ_S0 = FrLJ12_S0 - FrLJ6_S0;
+    frLJ_S1 = FrLJ12_S1 - FrLJ6_S1;
+#        ifndef HALF_LJ
+    frLJ_S2 = FrLJ12_S2 - FrLJ6_S2;
+    frLJ_S3 = FrLJ12_S3 - FrLJ6_S3;
+#        endif
+
+#        if (defined LJ_CUT || defined LJ_FORCE_SWITCH) && defined CALC_ENERGIES
+
+#            ifdef LJ_CUT
+    /* Calculate the LJ energies, with constant potential shift */
+    SimdReal VLJ6_S0 = sixth_S * fma(c6_S0, p6_cpot_S, FrLJ6_S0);
+    SimdReal VLJ6_S1 = sixth_S * fma(c6_S1, p6_cpot_S, FrLJ6_S1);
+#                ifndef HALF_LJ
+    SimdReal VLJ6_S2 = sixth_S * fma(c6_S2, p6_cpot_S, FrLJ6_S2);
+    SimdReal VLJ6_S3 = sixth_S * fma(c6_S3, p6_cpot_S, FrLJ6_S3);
+#                endif
+    SimdReal VLJ12_S0 = twelveth_S * fma(c12_S0, p12_cpot_S, FrLJ12_S0);
+    SimdReal VLJ12_S1 = twelveth_S * fma(c12_S1, p12_cpot_S, FrLJ12_S1);
+#                ifndef HALF_LJ
+    SimdReal VLJ12_S2 = twelveth_S * fma(c12_S2, p12_cpot_S, FrLJ12_S2);
+    SimdReal VLJ12_S3 = twelveth_S * fma(c12_S3, p12_cpot_S, FrLJ12_S3);
+#                endif
+#            endif /* LJ_CUT */
+
+#            ifdef LJ_FORCE_SWITCH
+#                define v_fswitch_r(rsw, rsw2, c0, c3, c4) \
+                    fma(fma((c4), (rsw), (c3)), ((rsw2) * (rsw)), (c0))
+
+    SimdReal VLJ6_S0 =
+            c6_S0 * fma(sixth_S, rinvsix_S0, v_fswitch_r(rsw_S0, rsw2_S0, p6_6cpot_S, p6_vc3_S, p6_vc4_S));
+    SimdReal VLJ6_S1 =
+            c6_S1 * fma(sixth_S, rinvsix_S1, v_fswitch_r(rsw_S1, rsw2_S1, p6_6cpot_S, p6_vc3_S, p6_vc4_S));
+#                ifndef HALF_LJ
+    SimdReal VLJ6_S2 =
+            c6_S2 * fma(sixth_S, rinvsix_S2, v_fswitch_r(rsw_S2, rsw2_S2, p6_6cpot_S, p6_vc3_S, p6_vc4_S));
+    SimdReal VLJ6_S3 =
+            c6_S3 * fma(sixth_S, rinvsix_S3, v_fswitch_r(rsw_S3, rsw2_S3, p6_6cpot_S, p6_vc3_S, p6_vc4_S));
+#                endif
+    SimdReal VLJ12_S0 = c12_S0
+                        * fma(twelveth_S,
+                              rinvsix_S0 * rinvsix_S0,
+                              v_fswitch_r(rsw_S0, rsw2_S0, p12_12cpot_S, p12_vc3_S, p12_vc4_S));
+    SimdReal VLJ12_S1 = c12_S1
+                        * fma(twelveth_S,
+                              rinvsix_S1 * rinvsix_S1,
+                              v_fswitch_r(rsw_S1, rsw2_S1, p12_12cpot_S, p12_vc3_S, p12_vc4_S));
+#                ifndef HALF_LJ
+    SimdReal VLJ12_S2 = c12_S2
+                        * fma(twelveth_S,
+                              rinvsix_S2 * rinvsix_S2,
+                              v_fswitch_r(rsw_S2, rsw2_S2, p12_12cpot_S, p12_vc3_S, p12_vc4_S));
+    SimdReal VLJ12_S3 = c12_S3
+                        * fma(twelveth_S,
+                              rinvsix_S3 * rinvsix_S3,
+                              v_fswitch_r(rsw_S3, rsw2_S3, p12_12cpot_S, p12_vc3_S, p12_vc4_S));
+#                endif
+#                undef v_fswitch_r
+#            endif /* LJ_FORCE_SWITCH */
+
+    /* Add up the repulsion and dispersion */
+    SimdReal VLJ_S0 = VLJ12_S0 - VLJ6_S0;
+    SimdReal VLJ_S1 = VLJ12_S1 - VLJ6_S1;
+#            ifndef HALF_LJ
+    SimdReal VLJ_S2 = VLJ12_S2 - VLJ6_S2;
+    SimdReal VLJ_S3 = VLJ12_S3 - VLJ6_S3;
+#            endif
+
+#        endif /* (LJ_CUT || LJ_FORCE_SWITCH) && CALC_ENERGIES */
+
+#        ifdef LJ_POT_SWITCH
+    /* We always need the potential, since it is needed for the force */
+    SimdReal VLJ_S0 = fnma(sixth_S, FrLJ6_S0, twelveth_S * FrLJ12_S0);
+    SimdReal VLJ_S1 = fnma(sixth_S, FrLJ6_S1, twelveth_S * FrLJ12_S1);
+#            ifndef HALF_LJ
+    SimdReal VLJ_S2 = fnma(sixth_S, FrLJ6_S2, twelveth_S * FrLJ12_S2);
+    SimdReal VLJ_S3 = fnma(sixth_S, FrLJ6_S3, twelveth_S * FrLJ12_S3);
+#            endif
+
+    {
+        SimdReal sw_S0, dsw_S0;
+        SimdReal sw_S1, dsw_S1;
+#            ifndef HALF_LJ
+        SimdReal sw_S2, dsw_S2;
+        SimdReal sw_S3, dsw_S3;
+#            endif
+
+#            define switch_r(rsw, rsw2, c3, c4, c5) \
+                fma(fma(fma(c5, rsw, c4), rsw, c3), ((rsw2) * (rsw)), one_S)
+#            define dswitch_r(rsw, rsw2, c2, c3, c4) (fma(fma(c4, rsw, c3), rsw, c2) * (rsw2))
+
+        sw_S0  = switch_r(rsw_S0, rsw2_S0, swV3_S, swV4_S, swV5_S);
+        dsw_S0 = dswitch_r(rsw_S0, rsw2_S0, swF2_S, swF3_S, swF4_S);
+        sw_S1  = switch_r(rsw_S1, rsw2_S1, swV3_S, swV4_S, swV5_S);
+        dsw_S1 = dswitch_r(rsw_S1, rsw2_S1, swF2_S, swF3_S, swF4_S);
+#            ifndef HALF_LJ
+        sw_S2  = switch_r(rsw_S2, rsw2_S2, swV3_S, swV4_S, swV5_S);
+        dsw_S2 = dswitch_r(rsw_S2, rsw2_S2, swF2_S, swF3_S, swF4_S);
+        sw_S3  = switch_r(rsw_S3, rsw2_S3, swV3_S, swV4_S, swV5_S);
+        dsw_S3 = dswitch_r(rsw_S3, rsw2_S3, swF2_S, swF3_S, swF4_S);
+#            endif
+        frLJ_S0 = fnma(dsw_S0 * VLJ_S0, r_S0, sw_S0 * frLJ_S0);
+        frLJ_S1 = fnma(dsw_S1 * VLJ_S1, r_S1, sw_S1 * frLJ_S1);
+#            ifndef HALF_LJ
+        frLJ_S2 = fnma(dsw_S2 * VLJ_S2, r_S2, sw_S2 * frLJ_S2);
+        frLJ_S3 = fnma(dsw_S3 * VLJ_S3, r_S3, sw_S3 * frLJ_S3);
+#            endif
+#            ifdef CALC_ENERGIES
+        VLJ_S0 = sw_S0 * VLJ_S0;
+        VLJ_S1 = sw_S1 * VLJ_S1;
+#                ifndef HALF_LJ
+        VLJ_S2 = sw_S2 * VLJ_S2;
+        VLJ_S3 = sw_S3 * VLJ_S3;
+#                endif
+#            endif
+
+#            undef switch_r
+#            undef dswitch_r
+    }
+#        endif /* LJ_POT_SWITCH */
+
+#        if defined CALC_ENERGIES && defined CHECK_EXCLS
+    /* The potential shift should be removed for excluded pairs */
+    VLJ_S0 = selectByMask(VLJ_S0, interact_S0);
+    VLJ_S1 = selectByMask(VLJ_S1, interact_S1);
+#            ifndef HALF_LJ
+    VLJ_S2 = selectByMask(VLJ_S2, interact_S2);
+    VLJ_S3 = selectByMask(VLJ_S3, interact_S3);
+#            endif
+#        endif
+
+#        ifdef LJ_EWALD_GEOM
+    {
+        SimdReal c6s_j_S;
+        SimdReal c6grid_S0, rinvsix_nm_S0, cr2_S0, expmcr2_S0, poly_S0;
+        SimdReal c6grid_S1, rinvsix_nm_S1, cr2_S1, expmcr2_S1, poly_S1;
+#            ifndef HALF_LJ
+        SimdReal c6grid_S2, rinvsix_nm_S2, cr2_S2, expmcr2_S2, poly_S2;
+        SimdReal c6grid_S3, rinvsix_nm_S3, cr2_S3, expmcr2_S3, poly_S3;
+#            endif
+#            ifdef CALC_ENERGIES
+        SimdReal sh_mask_S0;
+        SimdReal sh_mask_S1;
+#                ifndef HALF_LJ
+        SimdReal sh_mask_S2;
+        SimdReal sh_mask_S3;
+#                endif
+#            endif
+
+        /* Determine C6 for the grid using the geometric combination rule */
+        c6s_j_S   = load<SimdReal>(ljc + aj2 + 0);
+        c6grid_S0 = c6s_S0 * c6s_j_S;
+        c6grid_S1 = c6s_S1 * c6s_j_S;
+#            ifndef HALF_LJ
+        c6grid_S2 = c6s_S2 * c6s_j_S;
+        c6grid_S3 = c6s_S3 * c6s_j_S;
+#            endif
+
+#            ifdef CHECK_EXCLS
+        /* Recalculate rinvsix without exclusion mask (compiler might optimize) */
+        rinvsix_nm_S0 = rinvsq_S0 * rinvsq_S0 * rinvsq_S0;
+        rinvsix_nm_S1 = rinvsq_S1 * rinvsq_S1 * rinvsq_S1;
+#                ifndef HALF_LJ
+        rinvsix_nm_S2 = rinvsq_S2 * rinvsq_S2 * rinvsq_S2;
+        rinvsix_nm_S3 = rinvsq_S3 * rinvsq_S3 * rinvsq_S3;
+#                endif
+#            else
+        /* We didn't use a mask, so we can copy */
+        rinvsix_nm_S0 = rinvsix_S0;
+        rinvsix_nm_S1 = rinvsix_S1;
+#                ifndef HALF_LJ
+        rinvsix_nm_S2 = rinvsix_S2;
+        rinvsix_nm_S3 = rinvsix_S3;
+#                endif
+#            endif
+
+        /* Mask for the cut-off to avoid overflow of cr2^2 */
+        cr2_S0 = lje_c2_S * selectByMask(rsq_S0, wco_vdw_S0);
+        cr2_S1 = lje_c2_S * selectByMask(rsq_S1, wco_vdw_S1);
+#            ifndef HALF_LJ
+        cr2_S2 = lje_c2_S * selectByMask(rsq_S2, wco_vdw_S2);
+        cr2_S3 = lje_c2_S * selectByMask(rsq_S3, wco_vdw_S3);
+#            endif
+        // Unsafe version of our exp() should be fine, since these arguments should never
+        // be smaller than -127 for any reasonable choice of cutoff or ewald coefficients.
+        expmcr2_S0 = exp<MathOptimization::Unsafe>(-cr2_S0);
+        expmcr2_S1 = exp<MathOptimization::Unsafe>(-cr2_S1);
+#            ifndef HALF_LJ
+        expmcr2_S2 = exp<MathOptimization::Unsafe>(-cr2_S2);
+        expmcr2_S3 = exp<MathOptimization::Unsafe>(-cr2_S3);
+#            endif
+
+        /* 1 + cr2 + 1/2*cr2^2 */
+        poly_S0 = fma(fma(half_S, cr2_S0, one_S), cr2_S0, one_S);
+        poly_S1 = fma(fma(half_S, cr2_S1, one_S), cr2_S1, one_S);
+#            ifndef HALF_LJ
+        poly_S2 = fma(fma(half_S, cr2_S2, one_S), cr2_S2, one_S);
+        poly_S3 = fma(fma(half_S, cr2_S3, one_S), cr2_S3, one_S);
+#            endif
+
+        /* We calculate LJ F*r = (6*C6)*(r^-6 - F_mesh/6), we use:
+         * r^-6*cexp*(1 + cr2 + cr2^2/2 + cr2^3/6) = cexp*(r^-6*poly + c^6/6)
+         */
+        frLJ_S0 = fma(c6grid_S0,
+                      fnma(expmcr2_S0, fma(rinvsix_nm_S0, poly_S0, lje_c6_6_S), rinvsix_nm_S0),
+                      frLJ_S0);
+        frLJ_S1 = fma(c6grid_S1,
+                      fnma(expmcr2_S1, fma(rinvsix_nm_S1, poly_S1, lje_c6_6_S), rinvsix_nm_S1),
+                      frLJ_S1);
+#            ifndef HALF_LJ
+        frLJ_S2 = fma(c6grid_S2,
+                      fnma(expmcr2_S2, fma(rinvsix_nm_S2, poly_S2, lje_c6_6_S), rinvsix_nm_S2),
+                      frLJ_S2);
+        frLJ_S3 = fma(c6grid_S3,
+                      fnma(expmcr2_S3, fma(rinvsix_nm_S3, poly_S3, lje_c6_6_S), rinvsix_nm_S3),
+                      frLJ_S3);
+#            endif
+
+#            ifdef CALC_ENERGIES
+#                ifdef CHECK_EXCLS
+        sh_mask_S0 = selectByMask(lje_vc_S, interact_S0);
+        sh_mask_S1 = selectByMask(lje_vc_S, interact_S1);
+#                    ifndef HALF_LJ
+        sh_mask_S2 = selectByMask(lje_vc_S, interact_S2);
+        sh_mask_S3 = selectByMask(lje_vc_S, interact_S3);
+#                    endif
+#                else
+        sh_mask_S0 = lje_vc_S;
+        sh_mask_S1 = lje_vc_S;
+#                    ifndef HALF_LJ
+        sh_mask_S2 = lje_vc_S;
+        sh_mask_S3 = lje_vc_S;
+#                    endif
+#                endif
+
+        VLJ_S0 = fma(sixth_S * c6grid_S0,
+                     fma(rinvsix_nm_S0, fnma(expmcr2_S0, poly_S0, one_S), sh_mask_S0),
+                     VLJ_S0);
+        VLJ_S1 = fma(sixth_S * c6grid_S1,
+                     fma(rinvsix_nm_S1, fnma(expmcr2_S1, poly_S1, one_S), sh_mask_S1),
+                     VLJ_S1);
+#                ifndef HALF_LJ
+        VLJ_S2 = fma(sixth_S * c6grid_S2,
+                     fma(rinvsix_nm_S2, fnma(expmcr2_S2, poly_S2, one_S), sh_mask_S2),
+                     VLJ_S2);
+        VLJ_S3 = fma(sixth_S * c6grid_S3,
+                     fma(rinvsix_nm_S3, fnma(expmcr2_S3, poly_S3, one_S), sh_mask_S3),
+                     VLJ_S3);
+#                endif
+#            endif /* CALC_ENERGIES */
+    }
+#        endif /* LJ_EWALD_GEOM */
+
+#        if defined VDW_CUTOFF_CHECK
+    /* frLJ is multiplied later by rinvsq, which is masked for the Coulomb
+     * cut-off, but if the VdW cut-off is shorter, we need to mask with that.
+     */
+    frLJ_S0 = selectByMask(frLJ_S0, wco_vdw_S0);
+    frLJ_S1 = selectByMask(frLJ_S1, wco_vdw_S1);
+#            ifndef HALF_LJ
+    frLJ_S2 = selectByMask(frLJ_S2, wco_vdw_S2);
+    frLJ_S3 = selectByMask(frLJ_S3, wco_vdw_S3);
+#            endif
+#        endif
+
+#        ifdef CALC_ENERGIES
+    /* The potential shift should be removed for pairs beyond cut-off */
+    VLJ_S0 = selectByMask(VLJ_S0, wco_vdw_S0);
+    VLJ_S1 = selectByMask(VLJ_S1, wco_vdw_S1);
+#            ifndef HALF_LJ
+    VLJ_S2 = selectByMask(VLJ_S2, wco_vdw_S2);
+    VLJ_S3 = selectByMask(VLJ_S3, wco_vdw_S3);
+#            endif
+#        endif
+
+#    endif /* CALC_LJ */
+
+#    ifdef CALC_ENERGIES
+#        ifdef ENERGY_GROUPS
+    /* Extract the group pair index per j pair.
+     * Energy groups are stored per i-cluster, so things get
+     * complicated when the i- and j-cluster size don't match.
+     */
+    {
+#            if UNROLLJ == 2
+        const int egps_j = nbatParams.energrp[cj >> 1];
+        egp_jj[0]        = ((egps_j >> ((cj & 1) * egps_jshift)) & egps_jmask) * egps_jstride;
+#            else
+        /* We assume UNROLLI <= UNROLLJ */
+        for (int jdi = 0; jdi < UNROLLJ / UNROLLI; jdi++)
+        {
+            const int egps_j = nbatParams.energrp[cj * (UNROLLJ / UNROLLI) + jdi];
+            for (int jj = 0; jj < (UNROLLI / 2); jj++)
+            {
+                egp_jj[jdi * (UNROLLI / 2) + jj] =
+                        ((egps_j >> (jj * egps_jshift)) & egps_jmask) * egps_jstride;
+            }
+        }
+#            endif
+    }
+#        endif
+
+#        ifdef CALC_COULOMB
+#            ifndef ENERGY_GROUPS
+    vctot_S = vctot_S + vcoul_S0 + vcoul_S1 + vcoul_S2 + vcoul_S3;
+#            else
+    add_ener_grp(vcoul_S0, vctp[0], egp_jj);
+    add_ener_grp(vcoul_S1, vctp[1], egp_jj);
+    add_ener_grp(vcoul_S2, vctp[2], egp_jj);
+    add_ener_grp(vcoul_S3, vctp[3], egp_jj);
+#            endif
+#        endif
+
+#        ifdef CALC_LJ
+
+#            ifndef ENERGY_GROUPS
+#                ifndef HALF_LJ
+    Vvdwtot_S = Vvdwtot_S + VLJ_S0 + VLJ_S1 + VLJ_S2 + VLJ_S3;
+#                else
+    Vvdwtot_S = Vvdwtot_S + VLJ_S0 + VLJ_S1;
+#                endif
+#            else
+    add_ener_grp(VLJ_S0, vvdwtp[0], egp_jj);
+    add_ener_grp(VLJ_S1, vvdwtp[1], egp_jj);
+#                ifndef HALF_LJ
+    add_ener_grp(VLJ_S2, vvdwtp[2], egp_jj);
+    add_ener_grp(VLJ_S3, vvdwtp[3], egp_jj);
+#                endif
+#            endif
+#        endif /* CALC_LJ */
+#    endif     /* CALC_ENERGIES */
+
+#    ifdef CALC_LJ
+#        ifdef CALC_COULOMB
+    fscal_S0 = rinvsq_S0 * (frcoul_S0 + frLJ_S0);
+#        else
+    fscal_S0 = rinvsq_S0 * frLJ_S0;
+#        endif
+#        ifdef CALC_COULOMB
+    fscal_S1 = rinvsq_S1 * (frcoul_S1 + frLJ_S1);
+#        else
+    fscal_S1 = rinvsq_S1 * frLJ_S1;
+#        endif
+#    else
+    fscal_S0 = rinvsq_S0 * frcoul_S0;
+    fscal_S1 = rinvsq_S1 * frcoul_S1;
+#    endif /* CALC_LJ */
+#    if defined CALC_LJ && !defined HALF_LJ
+#        ifdef CALC_COULOMB
+    fscal_S2 = rinvsq_S2 * (frcoul_S2 + frLJ_S2);
+    fscal_S3 = rinvsq_S3 * (frcoul_S3 + frLJ_S3);
+#        else
+    fscal_S2 = rinvsq_S2 * frLJ_S2;
+    fscal_S3 = rinvsq_S3 * frLJ_S3;
+#        endif
+#    else
+    /* Atom 2 and 3 don't have LJ, so only add Coulomb forces */
+    fscal_S2 = rinvsq_S2 * frcoul_S2;
+    fscal_S3 = rinvsq_S3 * frcoul_S3;
+#    endif
+
+    /* Calculate temporary vectorial force */
+    tx_S0 = fscal_S0 * dx_S0;
+    tx_S1 = fscal_S1 * dx_S1;
+    tx_S2 = fscal_S2 * dx_S2;
+    tx_S3 = fscal_S3 * dx_S3;
+    ty_S0 = fscal_S0 * dy_S0;
+    ty_S1 = fscal_S1 * dy_S1;
+    ty_S2 = fscal_S2 * dy_S2;
+    ty_S3 = fscal_S3 * dy_S3;
+    tz_S0 = fscal_S0 * dz_S0;
+    tz_S1 = fscal_S1 * dz_S1;
+    tz_S2 = fscal_S2 * dz_S2;
+    tz_S3 = fscal_S3 * dz_S3;
+
+    /* Increment i atom force */
+    fix_S0 = fix_S0 + tx_S0;
+    fix_S1 = fix_S1 + tx_S1;
+    fix_S2 = fix_S2 + tx_S2;
+    fix_S3 = fix_S3 + tx_S3;
+    fiy_S0 = fiy_S0 + ty_S0;
+    fiy_S1 = fiy_S1 + ty_S1;
+    fiy_S2 = fiy_S2 + ty_S2;
+    fiy_S3 = fiy_S3 + ty_S3;
+    fiz_S0 = fiz_S0 + tz_S0;
+    fiz_S1 = fiz_S1 + tz_S1;
+    fiz_S2 = fiz_S2 + tz_S2;
+    fiz_S3 = fiz_S3 + tz_S3;
+
+    /* Decrement j atom force */
+    store(f + ajx, load<SimdReal>(f + ajx) - (tx_S0 + tx_S1 + tx_S2 + tx_S3));
+    store(f + ajy, load<SimdReal>(f + ajy) - (ty_S0 + ty_S1 + ty_S2 + ty_S3));
+    store(f + ajz, load<SimdReal>(f + ajz) - (tz_S0 + tz_S1 + tz_S2 + tz_S3));
+}
+
+#    undef rinv_ex_S0
+#    undef rinv_ex_S1
+#    undef rinv_ex_S2
+#    undef rinv_ex_S3
+
+#    undef wco_vdw_S0
+#    undef wco_vdw_S1
+#    undef wco_vdw_S2
+#    undef wco_vdw_S3
+
+#    undef EXCL_FORCES
+
+#endif // !DOXYGEN
diff --git a/src/include/gromacs/nbnxm/kernels_simd_4xm/kernel_outer.h b/src/include/gromacs/nbnxm/kernels_simd_4xm/kernel_outer.h
new file mode 100644 (file)
index 0000000..dae206b
--- /dev/null
@@ -0,0 +1,659 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+
+{
+    using namespace gmx;
+
+    /* Unpack pointers for output */
+    real* f      = out->f.data();
+    real* fshift = out->fshift.data();
+#ifdef CALC_ENERGIES
+#    ifdef ENERGY_GROUPS
+    real* Vvdw = out->VSvdw.data();
+    real* Vc   = out->VSc.data();
+#    else
+    real*       Vvdw       = out->Vvdw.data();
+    real*       Vc         = out->Vc.data();
+#    endif
+#endif
+
+    SimdReal shX_S;
+    SimdReal shY_S;
+    SimdReal shZ_S;
+    SimdReal ix_S0, iy_S0, iz_S0;
+    SimdReal ix_S1, iy_S1, iz_S1;
+    SimdReal ix_S2, iy_S2, iz_S2;
+    SimdReal ix_S3, iy_S3, iz_S3;
+    SimdReal fix_S0, fiy_S0, fiz_S0;
+    SimdReal fix_S1, fiy_S1, fiz_S1;
+    SimdReal fix_S2, fiy_S2, fiz_S2;
+    SimdReal fix_S3, fiy_S3, fiz_S3;
+
+    SimdReal diagonal_jmi_S;
+#if UNROLLI == UNROLLJ
+    SimdBool diagonal_mask_S0, diagonal_mask_S1, diagonal_mask_S2, diagonal_mask_S3;
+#else
+    SimdBool diagonal_mask0_S0, diagonal_mask0_S1, diagonal_mask0_S2, diagonal_mask0_S3;
+    SimdBool diagonal_mask1_S0, diagonal_mask1_S1, diagonal_mask1_S2, diagonal_mask1_S3;
+#endif
+
+    SimdBitMask filter_S0, filter_S1, filter_S2, filter_S3;
+
+    SimdReal zero_S(0.0);
+
+    SimdReal one_S(1.0);
+    SimdReal iq_S0 = setZero();
+    SimdReal iq_S1 = setZero();
+    SimdReal iq_S2 = setZero();
+    SimdReal iq_S3 = setZero();
+
+#ifdef CALC_COUL_RF
+    SimdReal mrc_3_S;
+#    ifdef CALC_ENERGIES
+    SimdReal hrc_3_S, moh_rc_S;
+#    endif
+#endif
+
+#ifdef CALC_COUL_TAB
+    /* Coulomb table variables */
+    SimdReal invtsp_S;
+#    ifdef CALC_ENERGIES
+    SimdReal mhalfsp_S;
+#    endif
+#endif
+
+#ifdef CALC_COUL_EWALD
+    SimdReal beta2_S, beta_S;
+#endif
+
+#if defined CALC_ENERGIES && (defined CALC_COUL_EWALD || defined CALC_COUL_TAB)
+    SimdReal sh_ewald_S;
+#endif
+
+#if defined LJ_CUT && defined CALC_ENERGIES
+    SimdReal p6_cpot_S, p12_cpot_S;
+#endif
+#ifdef LJ_POT_SWITCH
+    SimdReal rswitch_S;
+    SimdReal swV3_S, swV4_S, swV5_S;
+    SimdReal swF2_S, swF3_S, swF4_S;
+#endif
+#ifdef LJ_FORCE_SWITCH
+    SimdReal rswitch_S;
+    SimdReal p6_fc2_S, p6_fc3_S;
+    SimdReal p12_fc2_S, p12_fc3_S;
+#    ifdef CALC_ENERGIES
+    SimdReal p6_vc3_S, p6_vc4_S;
+    SimdReal p12_vc3_S, p12_vc4_S;
+    SimdReal p6_6cpot_S, p12_12cpot_S;
+#    endif
+#endif
+#ifdef LJ_EWALD_GEOM
+    SimdReal half_S, lje_c2_S, lje_c6_6_S;
+#endif
+
+#ifdef LJ_COMB_LB
+    SimdReal hsig_i_S0, seps_i_S0;
+    SimdReal hsig_i_S1, seps_i_S1;
+    SimdReal hsig_i_S2, seps_i_S2;
+    SimdReal hsig_i_S3, seps_i_S3;
+#endif /* LJ_COMB_LB */
+
+    SimdReal minRsq_S;
+    SimdReal rc2_S;
+#ifdef VDW_CUTOFF_CHECK
+    SimdReal rcvdw2_S;
+#endif
+
+#ifdef COUNT_PAIRS
+    int npair = 0;
+#endif
+
+    const nbnxn_atomdata_t::Params& nbatParams = nbat->params();
+
+#if defined LJ_COMB_GEOM || defined LJ_COMB_LB || defined LJ_EWALD_GEOM
+    const real* gmx_restrict ljc = nbatParams.lj_comb.data();
+#endif
+#if !(defined LJ_COMB_GEOM || defined LJ_COMB_LB || defined FIX_LJ_C)
+    /* No combination rule used */
+    const real* gmx_restrict nbfp_ptr = nbatParams.nbfp_aligned.data();
+    const int* gmx_restrict  type     = nbatParams.type.data();
+#endif
+
+    /* Load j-i for the first i */
+    diagonal_jmi_S = load<SimdReal>(nbat->simdMasks.diagonal_4xn_j_minus_i.data());
+    /* Generate all the diagonal masks as comparison results */
+#if UNROLLI == UNROLLJ
+    diagonal_mask_S0 = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S   = diagonal_jmi_S - one_S;
+    diagonal_mask_S1 = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S   = diagonal_jmi_S - one_S;
+    diagonal_mask_S2 = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S   = diagonal_jmi_S - one_S;
+    diagonal_mask_S3 = (zero_S < diagonal_jmi_S);
+#else
+#    if UNROLLI == 2 * UNROLLJ || 2 * UNROLLI == UNROLLJ
+    diagonal_mask0_S0 = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S    = diagonal_jmi_S - one_S;
+    diagonal_mask0_S1 = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S    = diagonal_jmi_S - one_S;
+    diagonal_mask0_S2 = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S    = diagonal_jmi_S - one_S;
+    diagonal_mask0_S3 = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S    = diagonal_jmi_S - one_S;
+
+#        if UNROLLI == 2 * UNROLLJ
+    /* Load j-i for the second half of the j-cluster */
+    diagonal_jmi_S = load<SimdReal>(nbat->simdMasks.diagonal_4xn_j_minus_i.data() + UNROLLJ);
+#        endif
+
+    diagonal_mask1_S0                                  = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S                                     = diagonal_jmi_S - one_S;
+    diagonal_mask1_S1                                  = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S                                     = diagonal_jmi_S - one_S;
+    diagonal_mask1_S2                                  = (zero_S < diagonal_jmi_S);
+    diagonal_jmi_S                                     = diagonal_jmi_S - one_S;
+    diagonal_mask1_S3                                  = (zero_S < diagonal_jmi_S);
+#    endif
+#endif
+
+#if GMX_DOUBLE && !GMX_SIMD_HAVE_INT32_LOGICAL
+    const std::uint64_t* gmx_restrict exclusion_filter = nbat->simdMasks.exclusion_filter64.data();
+#else
+    const std::uint32_t* gmx_restrict exclusion_filter = nbat->simdMasks.exclusion_filter.data();
+#endif
+
+    /* Here we cast the exclusion filters from unsigned * to int * or real *.
+     * Since we only check bits, the actual value they represent does not
+     * matter, as long as both filter and mask data are treated the same way.
+     */
+#if GMX_SIMD_HAVE_INT32_LOGICAL
+    filter_S0 = load<SimdBitMask>(reinterpret_cast<const int*>(exclusion_filter + 0 * UNROLLJ));
+    filter_S1 = load<SimdBitMask>(reinterpret_cast<const int*>(exclusion_filter + 1 * UNROLLJ));
+    filter_S2 = load<SimdBitMask>(reinterpret_cast<const int*>(exclusion_filter + 2 * UNROLLJ));
+    filter_S3 = load<SimdBitMask>(reinterpret_cast<const int*>(exclusion_filter + 3 * UNROLLJ));
+#else
+    filter_S0 = load<SimdBitMask>(reinterpret_cast<const real*>(exclusion_filter + 0 * UNROLLJ));
+    filter_S1 = load<SimdBitMask>(reinterpret_cast<const real*>(exclusion_filter + 1 * UNROLLJ));
+    filter_S2 = load<SimdBitMask>(reinterpret_cast<const real*>(exclusion_filter + 2 * UNROLLJ));
+    filter_S3 = load<SimdBitMask>(reinterpret_cast<const real*>(exclusion_filter + 3 * UNROLLJ));
+#endif
+
+#ifdef CALC_COUL_RF
+    /* Reaction-field constants */
+    mrc_3_S = SimdReal(-2 * ic->reactionFieldCoefficient);
+#    ifdef CALC_ENERGIES
+    hrc_3_S  = SimdReal(ic->reactionFieldCoefficient);
+    moh_rc_S = SimdReal(-ic->reactionFieldShift);
+#    endif
+#endif
+
+#ifdef CALC_COUL_TAB
+
+    invtsp_S = SimdReal(ic->coulombEwaldTables->scale);
+#    ifdef CALC_ENERGIES
+    mhalfsp_S = SimdReal(-0.5_real / ic->coulombEwaldTables->scale);
+#    endif
+
+#    ifdef TAB_FDV0
+    const real* tab_coul_F = ic->coulombEwaldTables->tableFDV0.data();
+#    else
+    const real* tab_coul_F = ic->coulombEwaldTables->tableF.data();
+#        ifdef CALC_ENERGIES
+    const real* tab_coul_V = ic->coulombEwaldTables->tableV.data();
+#        endif
+#    endif
+#endif /* CALC_COUL_TAB */
+
+#ifdef CALC_COUL_EWALD
+    beta2_S = SimdReal(ic->ewaldcoeff_q * ic->ewaldcoeff_q);
+    beta_S  = SimdReal(ic->ewaldcoeff_q);
+#endif
+
+#if (defined CALC_COUL_TAB || defined CALC_COUL_EWALD) && defined CALC_ENERGIES
+    sh_ewald_S = SimdReal(ic->sh_ewald);
+#endif
+
+    /* LJ function constants */
+#if defined CALC_ENERGIES || defined LJ_POT_SWITCH
+    SimdReal sixth_S(1.0 / 6.0);
+    SimdReal twelveth_S(1.0 / 12.0);
+#endif
+
+#if defined LJ_CUT && defined CALC_ENERGIES
+    /* We shift the potential by cpot, which can be zero */
+    p6_cpot_S  = SimdReal(ic->dispersion_shift.cpot);
+    p12_cpot_S = SimdReal(ic->repulsion_shift.cpot);
+#endif
+#ifdef LJ_POT_SWITCH
+    rswitch_S = SimdReal(ic->rvdw_switch);
+    swV3_S    = SimdReal(ic->vdw_switch.c3);
+    swV4_S    = SimdReal(ic->vdw_switch.c4);
+    swV5_S    = SimdReal(ic->vdw_switch.c5);
+    swF2_S    = SimdReal(3 * ic->vdw_switch.c3);
+    swF3_S    = SimdReal(4 * ic->vdw_switch.c4);
+    swF4_S    = SimdReal(5 * ic->vdw_switch.c5);
+#endif
+#ifdef LJ_FORCE_SWITCH
+    rswitch_S = SimdReal(ic->rvdw_switch);
+    p6_fc2_S  = SimdReal(ic->dispersion_shift.c2);
+    p6_fc3_S  = SimdReal(ic->dispersion_shift.c3);
+    p12_fc2_S = SimdReal(ic->repulsion_shift.c2);
+    p12_fc3_S = SimdReal(ic->repulsion_shift.c3);
+#    ifdef CALC_ENERGIES
+    {
+        SimdReal mthird_S(-1.0 / 3.0);
+        SimdReal mfourth_S(-1.0 / 4.0);
+
+        p6_vc3_S     = mthird_S * p6_fc2_S;
+        p6_vc4_S     = mfourth_S * p6_fc3_S;
+        p6_6cpot_S   = SimdReal(ic->dispersion_shift.cpot / 6);
+        p12_vc3_S    = mthird_S * p12_fc2_S;
+        p12_vc4_S    = mfourth_S * p12_fc3_S;
+        p12_12cpot_S = SimdReal(ic->repulsion_shift.cpot / 12);
+    }
+#    endif
+#endif
+#ifdef LJ_EWALD_GEOM
+    half_S                      = SimdReal(0.5);
+    const real lj_ewaldcoeff2   = ic->ewaldcoeff_lj * ic->ewaldcoeff_lj;
+    const real lj_ewaldcoeff6_6 = lj_ewaldcoeff2 * lj_ewaldcoeff2 * lj_ewaldcoeff2 / 6;
+    lje_c2_S                    = SimdReal(lj_ewaldcoeff2);
+    lje_c6_6_S                  = SimdReal(lj_ewaldcoeff6_6);
+#    ifdef CALC_ENERGIES
+    /* Determine the grid potential at the cut-off */
+    SimdReal lje_vc_S(ic->sh_lj_ewald);
+#    endif
+#endif
+
+    /* The kernel either supports rcoulomb = rvdw or rcoulomb >= rvdw */
+    rc2_S = SimdReal(ic->rcoulomb * ic->rcoulomb);
+#ifdef VDW_CUTOFF_CHECK
+    rcvdw2_S = SimdReal(ic->rvdw * ic->rvdw);
+#endif
+
+    minRsq_S = SimdReal(c_nbnxnMinDistanceSquared);
+
+    const real* gmx_restrict q        = nbatParams.q.data();
+    const real               facel    = ic->epsfac;
+    const real* gmx_restrict shiftvec = shift_vec[0];
+    const real* gmx_restrict x        = nbat->x().data();
+
+#ifdef FIX_LJ_C
+    alignas(GMX_SIMD_ALIGNMENT) real pvdw_c6[2 * UNROLLI * UNROLLJ];
+    real*                            pvdw_c12 = pvdw_c6 + UNROLLI * UNROLLJ;
+
+    for (int jp = 0; jp < UNROLLJ; jp++)
+    {
+        pvdw_c6[0 * UNROLLJ + jp] = nbat->nbfp[0 * 2];
+        pvdw_c6[1 * UNROLLJ + jp] = nbat->nbfp[0 * 2];
+        pvdw_c6[2 * UNROLLJ + jp] = nbat->nbfp[0 * 2];
+        pvdw_c6[3 * UNROLLJ + jp] = nbat->nbfp[0 * 2];
+
+        pvdw_c12[0 * UNROLLJ + jp] = nbat->nbfp[0 * 2 + 1];
+        pvdw_c12[1 * UNROLLJ + jp] = nbat->nbfp[0 * 2 + 1];
+        pvdw_c12[2 * UNROLLJ + jp] = nbat->nbfp[0 * 2 + 1];
+        pvdw_c12[3 * UNROLLJ + jp] = nbat->nbfp[0 * 2 + 1];
+    }
+    SimdReal c6_S0 = load<SimdReal>(pvdw_c6 + 0 * UNROLLJ);
+    SimdReal c6_S1 = load<SimdReal>(pvdw_c6 + 1 * UNROLLJ);
+    SimdReal c6_S2 = load<SimdReal>(pvdw_c6 + 2 * UNROLLJ);
+    SimdReal c6_S3 = load<SimdReal>(pvdw_c6 + 3 * UNROLLJ);
+
+    SimdReal c12_S0 = load<SimdReal>(pvdw_c12 + 0 * UNROLLJ);
+    SimdReal c12_S1 = load<SimdReal>(pvdw_c12 + 1 * UNROLLJ);
+    SimdReal c12_S2 = load<SimdReal>(pvdw_c12 + 2 * UNROLLJ);
+    SimdReal c12_S3 = load<SimdReal>(pvdw_c12 + 3 * UNROLLJ);
+#endif /* FIX_LJ_C */
+
+#ifdef ENERGY_GROUPS
+    const int egps_ishift  = nbatParams.neg_2log;
+    const int egps_imask   = (1 << egps_ishift) - 1;
+    const int egps_jshift  = 2 * nbatParams.neg_2log;
+    const int egps_jmask   = (1 << egps_jshift) - 1;
+    const int egps_jstride = (UNROLLJ >> 1) * UNROLLJ;
+    /* Major division is over i-particle energy groups, determine the stride */
+    const int Vstride_i = nbatParams.nenergrp * (1 << nbatParams.neg_2log) * egps_jstride;
+#endif
+
+    const nbnxn_cj_t* l_cj = nbl->cj.data();
+
+    for (const nbnxn_ci_t& ciEntry : nbl->ci)
+    {
+        const int ish    = (ciEntry.shift & NBNXN_CI_SHIFT);
+        const int ish3   = ish * 3;
+        const int cjind0 = ciEntry.cj_ind_start;
+        const int cjind1 = ciEntry.cj_ind_end;
+        const int ci     = ciEntry.ci;
+        const int ci_sh  = (ish == gmx::c_centralShiftIndex ? ci : -1);
+
+        shX_S = SimdReal(shiftvec[ish3]);
+        shY_S = SimdReal(shiftvec[ish3 + 1]);
+        shZ_S = SimdReal(shiftvec[ish3 + 2]);
+
+#if UNROLLJ <= 4
+        int sci  = ci * STRIDE;
+        int scix = sci * DIM;
+#    if defined LJ_COMB_LB || defined LJ_COMB_GEOM || defined LJ_EWALD_GEOM
+        int sci2 = sci * 2;
+#    endif
+#else
+        int sci  = (ci >> 1) * STRIDE;
+        int scix = sci * DIM + (ci & 1) * (STRIDE >> 1);
+#    if defined LJ_COMB_LB || defined LJ_COMB_GEOM || defined LJ_EWALD_GEOM
+        int sci2 = sci * 2 + (ci & 1) * (STRIDE >> 1);
+#    endif
+        sci += (ci & 1) * (STRIDE >> 1);
+#endif
+
+        /* We have 5 LJ/C combinations, but use only three inner loops,
+         * as the other combinations are unlikely and/or not much faster:
+         * inner half-LJ + C for half-LJ + C / no-LJ + C
+         * inner LJ + C      for full-LJ + C
+         * inner LJ          for full-LJ + no-C / half-LJ + no-C
+         */
+        const bool do_LJ   = ((ciEntry.shift & NBNXN_CI_DO_LJ(0)) != 0);
+        const bool do_coul = ((ciEntry.shift & NBNXN_CI_DO_COUL(0)) != 0);
+        const bool half_LJ = (((ciEntry.shift & NBNXN_CI_HALF_LJ(0)) != 0) || !do_LJ) && do_coul;
+
+#ifdef ENERGY_GROUPS
+        real*     vvdwtp[UNROLLI];
+        real*     vctp[UNROLLI];
+        const int egps_i = nbatParams.energrp[ci];
+        {
+            for (int ia = 0; ia < UNROLLI; ia++)
+            {
+                int egp_ia = (egps_i >> (ia * egps_ishift)) & egps_imask;
+                vvdwtp[ia] = Vvdw + egp_ia * Vstride_i;
+                vctp[ia]   = Vc + egp_ia * Vstride_i;
+            }
+        }
+#endif
+
+#ifdef CALC_ENERGIES
+#    ifdef LJ_EWALD_GEOM
+        const bool do_self = true;
+#    else
+        const bool do_self = do_coul;
+#    endif
+#    if UNROLLJ == 4
+        if (do_self && l_cj[ciEntry.cj_ind_start].cj == ci_sh)
+#    endif
+#    if UNROLLJ == 2
+            if (do_self && l_cj[ciEntry.cj_ind_start].cj == (ci_sh << 1))
+#    endif
+#    if UNROLLJ == 8
+                if (do_self && l_cj[ciEntry.cj_ind_start].cj == (ci_sh >> 1))
+#    endif
+                {
+                    if (do_coul)
+                    {
+#    ifdef CALC_COUL_RF
+                        const real Vc_sub_self = 0.5 * ic->reactionFieldShift;
+#    endif
+#    ifdef CALC_COUL_TAB
+#        ifdef TAB_FDV0
+                        const real Vc_sub_self = 0.5 * tab_coul_F[2];
+#        else
+                        const real Vc_sub_self = 0.5 * tab_coul_V[0];
+#        endif
+#    endif
+#    ifdef CALC_COUL_EWALD
+                        /* beta/sqrt(pi) */
+                        const real Vc_sub_self = 0.5 * ic->ewaldcoeff_q * M_2_SQRTPI;
+#    endif
+
+                        for (int ia = 0; ia < UNROLLI; ia++)
+                        {
+                            const real qi = q[sci + ia];
+#    ifdef ENERGY_GROUPS
+                            vctp[ia][((egps_i >> (ia * egps_ishift)) & egps_imask) * egps_jstride]
+#    else
+                    Vc[0]
+#    endif
+                                    -= facel * qi * qi * Vc_sub_self;
+                        }
+                    }
+
+#    ifdef LJ_EWALD_GEOM
+                    {
+                        for (int ia = 0; ia < UNROLLI; ia++)
+                        {
+                            real c6_i =
+                                    nbatParams.nbfp[nbatParams.type[sci + ia] * (nbatParams.numTypes + 1) * 2]
+                                    / 6;
+#        ifdef ENERGY_GROUPS
+                            vvdwtp[ia][((egps_i >> (ia * egps_ishift)) & egps_imask) * egps_jstride]
+#        else
+                            Vvdw[0]
+#        endif
+                                    += 0.5 * c6_i * lj_ewaldcoeff6_6;
+                        }
+                    }
+#    endif /* LJ_EWALD_GEOM */
+                }
+#endif
+
+        /* Load i atom data */
+        const int sciy = scix + STRIDE;
+        const int sciz = sciy + STRIDE;
+        ix_S0          = SimdReal(x[scix]) + shX_S;
+        ix_S1          = SimdReal(x[scix + 1]) + shX_S;
+        ix_S2          = SimdReal(x[scix + 2]) + shX_S;
+        ix_S3          = SimdReal(x[scix + 3]) + shX_S;
+        iy_S0          = SimdReal(x[sciy]) + shY_S;
+        iy_S1          = SimdReal(x[sciy + 1]) + shY_S;
+        iy_S2          = SimdReal(x[sciy + 2]) + shY_S;
+        iy_S3          = SimdReal(x[sciy + 3]) + shY_S;
+        iz_S0          = SimdReal(x[sciz]) + shZ_S;
+        iz_S1          = SimdReal(x[sciz + 1]) + shZ_S;
+        iz_S2          = SimdReal(x[sciz + 2]) + shZ_S;
+        iz_S3          = SimdReal(x[sciz + 3]) + shZ_S;
+
+        if (do_coul)
+        {
+            iq_S0 = SimdReal(facel * q[sci]);
+            iq_S1 = SimdReal(facel * q[sci + 1]);
+            iq_S2 = SimdReal(facel * q[sci + 2]);
+            iq_S3 = SimdReal(facel * q[sci + 3]);
+        }
+
+#ifdef LJ_COMB_LB
+        hsig_i_S0 = SimdReal(ljc[sci2 + 0]);
+        hsig_i_S1 = SimdReal(ljc[sci2 + 1]);
+        hsig_i_S2 = SimdReal(ljc[sci2 + 2]);
+        hsig_i_S3 = SimdReal(ljc[sci2 + 3]);
+        seps_i_S0 = SimdReal(ljc[sci2 + STRIDE + 0]);
+        seps_i_S1 = SimdReal(ljc[sci2 + STRIDE + 1]);
+        seps_i_S2 = SimdReal(ljc[sci2 + STRIDE + 2]);
+        seps_i_S3 = SimdReal(ljc[sci2 + STRIDE + 3]);
+#else
+#    ifdef LJ_COMB_GEOM
+        SimdReal c6s_S0 = SimdReal(ljc[sci2 + 0]);
+        SimdReal c6s_S1 = SimdReal(ljc[sci2 + 1]);
+        SimdReal c6s_S2, c6s_S3;
+        if (!half_LJ)
+        {
+            c6s_S2 = SimdReal(ljc[sci2 + 2]);
+            c6s_S3 = SimdReal(ljc[sci2 + 3]);
+        }
+        else
+        {
+            c6s_S2 = setZero();
+            c6s_S3 = setZero();
+        }
+        SimdReal c12s_S0 = SimdReal(ljc[sci2 + STRIDE + 0]);
+        SimdReal c12s_S1 = SimdReal(ljc[sci2 + STRIDE + 1]);
+        SimdReal c12s_S2, c12s_S3;
+        if (!half_LJ)
+        {
+            c12s_S2 = SimdReal(ljc[sci2 + STRIDE + 2]);
+            c12s_S3 = SimdReal(ljc[sci2 + STRIDE + 3]);
+        }
+        else
+        {
+            c12s_S2 = setZero();
+            c12s_S3 = setZero();
+        }
+#    else
+        const int   numTypes = nbatParams.numTypes;
+        const real* nbfp0    = nbfp_ptr + type[sci] * numTypes * c_simdBestPairAlignment;
+        const real* nbfp1    = nbfp_ptr + type[sci + 1] * numTypes * c_simdBestPairAlignment;
+        const real *nbfp2 = nullptr, *nbfp3 = nullptr;
+        if (!half_LJ)
+        {
+            nbfp2 = nbfp_ptr + type[sci + 2] * numTypes * c_simdBestPairAlignment;
+            nbfp3 = nbfp_ptr + type[sci + 3] * numTypes * c_simdBestPairAlignment;
+        }
+#    endif
+#endif
+#ifdef LJ_EWALD_GEOM
+        /* We need the geometrically combined C6 for the PME grid correction */
+        SimdReal c6s_S0 = SimdReal(ljc[sci2 + 0]);
+        SimdReal c6s_S1 = SimdReal(ljc[sci2 + 1]);
+        SimdReal c6s_S2, c6s_S3;
+        if (!half_LJ)
+        {
+            c6s_S2 = SimdReal(ljc[sci2 + 2]);
+            c6s_S3 = SimdReal(ljc[sci2 + 3]);
+        }
+#endif
+
+        /* Zero the potential energy for this list */
+#ifdef CALC_ENERGIES
+        SimdReal Vvdwtot_S = setZero();
+        SimdReal vctot_S   = setZero();
+#endif
+
+        /* Clear i atom forces */
+        fix_S0 = setZero();
+        fix_S1 = setZero();
+        fix_S2 = setZero();
+        fix_S3 = setZero();
+        fiy_S0 = setZero();
+        fiy_S1 = setZero();
+        fiy_S2 = setZero();
+        fiy_S3 = setZero();
+        fiz_S0 = setZero();
+        fiz_S1 = setZero();
+        fiz_S2 = setZero();
+        fiz_S3 = setZero();
+
+        int cjind = cjind0;
+
+        /* Currently all kernels use (at least half) LJ */
+#define CALC_LJ
+        if (half_LJ)
+        {
+            /* Coulomb: all i-atoms, LJ: first half i-atoms */
+#define CALC_COULOMB
+#define HALF_LJ
+#define CHECK_EXCLS
+            while (cjind < cjind1 && nbl->cj[cjind].excl != NBNXN_INTERACTION_MASK_ALL)
+            {
+#include "kernel_inner.h"
+                cjind++;
+            }
+#undef CHECK_EXCLS
+            for (; (cjind < cjind1); cjind++)
+            {
+#include "kernel_inner.h"
+            }
+#undef HALF_LJ
+#undef CALC_COULOMB
+        }
+        else if (do_coul)
+        {
+            /* Coulomb: all i-atoms, LJ: all i-atoms */
+#define CALC_COULOMB
+#define CHECK_EXCLS
+            while (cjind < cjind1 && nbl->cj[cjind].excl != NBNXN_INTERACTION_MASK_ALL)
+            {
+#include "kernel_inner.h"
+                cjind++;
+            }
+#undef CHECK_EXCLS
+            for (; (cjind < cjind1); cjind++)
+            {
+#include "kernel_inner.h"
+            }
+#undef CALC_COULOMB
+        }
+        else
+        {
+            /* Coulomb: none, LJ: all i-atoms */
+#define CHECK_EXCLS
+            while (cjind < cjind1 && nbl->cj[cjind].excl != NBNXN_INTERACTION_MASK_ALL)
+            {
+#include "kernel_inner.h"
+                cjind++;
+            }
+#undef CHECK_EXCLS
+            for (; (cjind < cjind1); cjind++)
+            {
+#include "kernel_inner.h"
+            }
+        }
+#undef CALC_LJ
+        /* Add accumulated i-forces to the force array */
+        real fShiftX = reduceIncr4ReturnSum(f + scix, fix_S0, fix_S1, fix_S2, fix_S3);
+        real fShiftY = reduceIncr4ReturnSum(f + sciy, fiy_S0, fiy_S1, fiy_S2, fiy_S3);
+        real fShiftZ = reduceIncr4ReturnSum(f + sciz, fiz_S0, fiz_S1, fiz_S2, fiz_S3);
+
+
+#ifdef CALC_SHIFTFORCES
+        fshift[ish3 + 0] += fShiftX;
+        fshift[ish3 + 1] += fShiftY;
+        fshift[ish3 + 2] += fShiftZ;
+#endif
+
+#ifdef CALC_ENERGIES
+        if (do_coul)
+        {
+            *Vc += reduce(vctot_S);
+        }
+
+        *Vvdw += reduce(Vvdwtot_S);
+#endif
+
+        /* Outer loop uses 6 flops/iteration */
+    }
+
+#ifdef COUNT_PAIRS
+    printf("atom pairs %d\n", npair);
+#endif
+}
diff --git a/src/include/gromacs/nbnxm/kernels_simd_4xm/kernel_prune.h b/src/include/gromacs/nbnxm/kernels_simd_4xm/kernel_prune.h
new file mode 100644 (file)
index 0000000..025ba4a
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the SIMD 4xN pruning only kernel.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct nbnxn_atomdata_t;
+struct NbnxnPairlistCpu;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+}
+
+/*! \brief Prune a single NbnxnPairlistCpu entry with distance \p rlistInner
+ *
+ * Reads a cluster pairlist \p nbl->ciOuter, \p nbl->cjOuter and writes
+ * all cluster pairs within \p rlistInner to \p nbl->ci, \p nbl->cj.
+ */
+void nbnxn_kernel_prune_4xn(NbnxnPairlistCpu*              nbl,
+                            const nbnxn_atomdata_t*        nbat,
+                            gmx::ArrayRef<const gmx::RVec> shiftvec,
+                            real                           rlistInner);
diff --git a/src/include/gromacs/nbnxm/kernels_simd_4xm/kernels.h b/src/include/gromacs/nbnxm/kernels_simd_4xm/kernels.h
new file mode 100644 (file)
index 0000000..2c8bc96
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2019 by the GROMACS development team.
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*
+ * Note: this file was generated by the Verlet kernel generator for
+ * kernel type 4xm.
+ */
+
+
+#include "gromacs/nbnxm/kernel_common.h"
+
+/* Declare all the different kernel functions.
+ */
+nbk_func_noener nbnxm_kernel_ElecRF_VdwLJCombGeom_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecRF_VdwLJCombLB_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecRF_VdwLJ_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecRF_VdwLJFSw_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecRF_VdwLJPSw_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecRF_VdwLJEwCombGeom_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecQSTab_VdwLJCombGeom_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecQSTab_VdwLJCombLB_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecQSTab_VdwLJ_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecQSTab_VdwLJFSw_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecQSTab_VdwLJPSw_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecQSTab_VdwLJEwCombGeom_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombGeom_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombLB_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecQSTabTwinCut_VdwLJ_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecQSTabTwinCut_VdwLJFSw_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecQSTabTwinCut_VdwLJPSw_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecEw_VdwLJCombGeom_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecEw_VdwLJCombLB_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecEw_VdwLJ_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecEw_VdwLJFSw_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecEw_VdwLJPSw_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecEw_VdwLJEwCombGeom_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecEwTwinCut_VdwLJCombGeom_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecEwTwinCut_VdwLJCombLB_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecEwTwinCut_VdwLJ_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecEwTwinCut_VdwLJFSw_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecEwTwinCut_VdwLJPSw_F_4xm;
+nbk_func_noener nbnxm_kernel_ElecEwTwinCut_VdwLJEwCombGeom_F_4xm;
+
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJCombGeom_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJCombLB_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJ_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJFSw_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJPSw_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJEwCombGeom_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJCombGeom_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJCombLB_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJ_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJFSw_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJPSw_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJEwCombGeom_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombGeom_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombLB_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJ_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJFSw_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJPSw_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJCombGeom_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJCombLB_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJ_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJFSw_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJPSw_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJEwCombGeom_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJCombGeom_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJCombLB_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJ_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJFSw_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJPSw_VF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VF_4xm;
+
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJCombGeom_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJCombLB_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJ_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJFSw_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJPSw_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecRF_VdwLJEwCombGeom_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJCombGeom_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJCombLB_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJ_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJFSw_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJPSw_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTab_VdwLJEwCombGeom_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombGeom_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombLB_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJFSw_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJPSw_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJCombGeom_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJCombLB_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJ_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJFSw_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJPSw_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEw_VdwLJEwCombGeom_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJCombGeom_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJCombLB_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJ_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJFSw_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJPSw_VgrpF_4xm;
+nbk_func_ener nbnxm_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VgrpF_4xm;
+
+
+#ifdef INCLUDE_KERNELFUNCTION_TABLES
+
+/* Declare and define the kernel function pointer lookup tables.
+ * The minor index of the array goes over both the LJ combination rules,
+ * which is only supported by plain cut-off, and the LJ switch/PME functions.
+ */
+static const p_nbk_func_noener nbnxm_kernel_noener_simd_4xm[static_cast<int>(CoulombKernelType::Count)][vdwktNR] = {
+    {
+            nbnxm_kernel_ElecRF_VdwLJCombGeom_F_4xm,
+            nbnxm_kernel_ElecRF_VdwLJCombLB_F_4xm,
+            nbnxm_kernel_ElecRF_VdwLJ_F_4xm,
+            nbnxm_kernel_ElecRF_VdwLJFSw_F_4xm,
+            nbnxm_kernel_ElecRF_VdwLJPSw_F_4xm,
+            nbnxm_kernel_ElecRF_VdwLJEwCombGeom_F_4xm,
+    },
+    {
+            nbnxm_kernel_ElecQSTab_VdwLJCombGeom_F_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJCombLB_F_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJ_F_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJFSw_F_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJPSw_F_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJEwCombGeom_F_4xm,
+    },
+    {
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombGeom_F_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombLB_F_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJ_F_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJFSw_F_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJPSw_F_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_F_4xm,
+    },
+    {
+            nbnxm_kernel_ElecEw_VdwLJCombGeom_F_4xm,
+            nbnxm_kernel_ElecEw_VdwLJCombLB_F_4xm,
+            nbnxm_kernel_ElecEw_VdwLJ_F_4xm,
+            nbnxm_kernel_ElecEw_VdwLJFSw_F_4xm,
+            nbnxm_kernel_ElecEw_VdwLJPSw_F_4xm,
+            nbnxm_kernel_ElecEw_VdwLJEwCombGeom_F_4xm,
+    },
+    {
+            nbnxm_kernel_ElecEwTwinCut_VdwLJCombGeom_F_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJCombLB_F_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJ_F_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJFSw_F_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJPSw_F_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJEwCombGeom_F_4xm,
+    },
+};
+
+static const p_nbk_func_ener nbnxm_kernel_ener_simd_4xm[static_cast<int>(CoulombKernelType::Count)][vdwktNR] = {
+    {
+            nbnxm_kernel_ElecRF_VdwLJCombGeom_VF_4xm,
+            nbnxm_kernel_ElecRF_VdwLJCombLB_VF_4xm,
+            nbnxm_kernel_ElecRF_VdwLJ_VF_4xm,
+            nbnxm_kernel_ElecRF_VdwLJFSw_VF_4xm,
+            nbnxm_kernel_ElecRF_VdwLJPSw_VF_4xm,
+            nbnxm_kernel_ElecRF_VdwLJEwCombGeom_VF_4xm,
+    },
+    {
+            nbnxm_kernel_ElecQSTab_VdwLJCombGeom_VF_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJCombLB_VF_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJ_VF_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJFSw_VF_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJPSw_VF_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJEwCombGeom_VF_4xm,
+    },
+    {
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombGeom_VF_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombLB_VF_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJ_VF_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJFSw_VF_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJPSw_VF_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VF_4xm,
+    },
+    {
+            nbnxm_kernel_ElecEw_VdwLJCombGeom_VF_4xm,
+            nbnxm_kernel_ElecEw_VdwLJCombLB_VF_4xm,
+            nbnxm_kernel_ElecEw_VdwLJ_VF_4xm,
+            nbnxm_kernel_ElecEw_VdwLJFSw_VF_4xm,
+            nbnxm_kernel_ElecEw_VdwLJPSw_VF_4xm,
+            nbnxm_kernel_ElecEw_VdwLJEwCombGeom_VF_4xm,
+    },
+    {
+            nbnxm_kernel_ElecEwTwinCut_VdwLJCombGeom_VF_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJCombLB_VF_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJ_VF_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJFSw_VF_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJPSw_VF_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VF_4xm,
+    },
+};
+
+static const p_nbk_func_ener nbnxm_kernel_energrp_simd_4xm[static_cast<int>(CoulombKernelType::Count)][vdwktNR] = {
+    {
+            nbnxm_kernel_ElecRF_VdwLJCombGeom_VgrpF_4xm,
+            nbnxm_kernel_ElecRF_VdwLJCombLB_VgrpF_4xm,
+            nbnxm_kernel_ElecRF_VdwLJ_VgrpF_4xm,
+            nbnxm_kernel_ElecRF_VdwLJFSw_VgrpF_4xm,
+            nbnxm_kernel_ElecRF_VdwLJPSw_VgrpF_4xm,
+            nbnxm_kernel_ElecRF_VdwLJEwCombGeom_VgrpF_4xm,
+    },
+    {
+            nbnxm_kernel_ElecQSTab_VdwLJCombGeom_VgrpF_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJCombLB_VgrpF_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJ_VgrpF_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJFSw_VgrpF_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJPSw_VgrpF_4xm,
+            nbnxm_kernel_ElecQSTab_VdwLJEwCombGeom_VgrpF_4xm,
+    },
+    {
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombGeom_VgrpF_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJCombLB_VgrpF_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJ_VgrpF_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJFSw_VgrpF_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJPSw_VgrpF_4xm,
+            nbnxm_kernel_ElecQSTabTwinCut_VdwLJEwCombGeom_VgrpF_4xm,
+    },
+    {
+            nbnxm_kernel_ElecEw_VdwLJCombGeom_VgrpF_4xm,
+            nbnxm_kernel_ElecEw_VdwLJCombLB_VgrpF_4xm,
+            nbnxm_kernel_ElecEw_VdwLJ_VgrpF_4xm,
+            nbnxm_kernel_ElecEw_VdwLJFSw_VgrpF_4xm,
+            nbnxm_kernel_ElecEw_VdwLJPSw_VgrpF_4xm,
+            nbnxm_kernel_ElecEw_VdwLJEwCombGeom_VgrpF_4xm,
+    },
+    {
+            nbnxm_kernel_ElecEwTwinCut_VdwLJCombGeom_VgrpF_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJCombLB_VgrpF_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJ_VgrpF_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJFSw_VgrpF_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJPSw_VgrpF_4xm,
+            nbnxm_kernel_ElecEwTwinCut_VdwLJEwCombGeom_VgrpF_4xm,
+    },
+};
+
+
+#endif /* INCLUDE_KERNELFUNCTION_TABLES */
diff --git a/src/include/gromacs/nbnxm/nbnxm.h b/src/include/gromacs/nbnxm/nbnxm.h
new file mode 100644 (file)
index 0000000..b438c9b
--- /dev/null
@@ -0,0 +1,522 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+// FIXME: remove the "__" prefix in front of the group def when we move the
+//        nonbonded code into separate dir.
+
+/*! \libinternal \defgroup __module_nbnxm Short-range non-bonded interaction module
+ * \ingroup group_mdrun
+ *
+ * \brief Computes forces and energies for short-range pair-interactions
+ * based on the Verlet algorithm. The algorithm uses pair-lists generated
+ * at fixed intervals as well as various flavors of pair interaction kernels
+ * implemented for a wide range of CPU and GPU architectures.
+ *
+ * The module includes support for flavors of Coulomb and Lennard-Jones interaction
+ * treatment implemented for a large range of SIMD instruction sets for CPU
+ * architectures as well as in CUDA and OpenCL for GPU architectures.
+ * Additionally there is a reference CPU non-SIMD and a reference CPU
+ * for GPU pair-list setup interaction kernel.
+ *
+ * The implementation of the kernels is based on the cluster non-bonded algorithm
+ * which in the code is referred to as the NxM algorithms ("nbnxm_" prefix);
+ * for details of the algorithm see DOI:10.1016/j.cpc.2013.06.003.
+ *
+ * Algorithmically, the non-bonded computation has two different modes:
+ * A "classical" mode: generate a list every nstlist steps containing at least
+ * all atom pairs up to a distance of rlistOuter and compute pair interactions
+ * for all pairs that are within the interaction cut-off.
+ * A "dynamic pruning" mode: generate an "outer-list" up to cut-off rlistOuter
+ * every nstlist steps and prune the outer-list using a cut-off of rlistInner
+ * every nstlistPrune steps to obtain a, smaller, "inner-list". This
+ * results in fewer interaction computations and allows for a larger nstlist.
+ * On a GPU, this dynamic pruning is performed in a rolling fashion, pruning
+ * only a sub-part of the list each (second) step. This way it can often
+ * overlap with integration and constraints on the CPU.
+ * Currently a simple heuristic determines which mode will be used.
+ *
+ * TODO: add a summary list and brief descriptions of the different submodules:
+ * search, CPU kernels, GPU glue code + kernels.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Szilárd Páll <pall.szilard@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Anca Hamuraru <anca@streamcomputing.eu>
+ * \author Teemu Virolainen <teemu@streamcomputing.eu>
+ * \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
+ *
+ * TODO: add more authors!
+ */
+
+/*! \libinternal
+ * \defgroup module_nbnxm Non-bonded pair interactions
+ * \ingroup group_mdrun
+ * \brief
+ * Implements non-bonded pair interaction functionality for NxM atom clusters.
+ *
+ * This module provides methods to, very efficiently, compute non-bonded
+ * pair interactions on CPUs as well as accelerators. It also provides
+ * a method to construct the NxM atom-cluster pair-list required for
+ * computing these non-bonded iteractions.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief This file contains the public interface of the nbnxm module
+ * that implements the NxM atom cluster non-bonded algorithm to efficiently
+ * compute pair forces.
+ *
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \author Szilárd Páll <pall.szilard@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_nbnxm
+ */
+
+
+#ifndef GMX_NBNXM_NBNXM_H
+#define GMX_NBNXM_NBNXM_H
+
+#include <memory>
+
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/locality.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/real.h"
+
+struct DeviceInformation;
+class FreeEnergyDispatch;
+struct gmx_domdec_zones_t;
+struct gmx_enerdata_t;
+struct gmx_hw_info_t;
+struct gmx_mtop_t;
+struct NbnxmGpu;
+struct gmx_wallcycle;
+struct interaction_const_t;
+enum class LJCombinationRule;
+struct nbnxn_atomdata_t;
+struct nonbonded_verlet_t;
+class PairSearch;
+class PairlistSets;
+struct t_commrec;
+struct t_lambda;
+struct t_nrnb;
+struct t_forcerec;
+struct t_inputrec;
+struct gmx_grppairener_t;
+
+class GpuEventSynchronizer;
+
+namespace gmx
+{
+template<typename>
+class ArrayRefWithPadding;
+class DeviceStreamManager;
+class ForceWithShiftForces;
+class ListedForcesGpu;
+template<typename>
+class ListOfLists;
+class MDLogger;
+template<typename>
+class Range;
+class StepWorkload;
+class UpdateGroupsCog;
+} // namespace gmx
+
+//! Namespace for non-bonded kernels
+namespace Nbnxm
+{
+enum class KernelType;
+
+/*! \brief Nbnxm electrostatic GPU kernel flavors.
+ *
+ *  Types of electrostatics implementations available in the GPU non-bonded
+ *  force kernels. These represent both the electrostatics types implemented
+ *  by the kernels (cut-off, RF, and Ewald - a subset of what's defined in
+ *  enums.h) as well as encode implementation details analytical/tabulated
+ *  and single or twin cut-off (for Ewald kernels).
+ *  Note that the cut-off and RF kernels have only analytical flavor and unlike
+ *  in the CPU kernels, the tabulated kernels are ATM Ewald-only.
+ *
+ *  The row-order of pointers to different electrostatic kernels defined in
+ *  nbnxn_cuda.cu by the nb_*_kfunc_ptr function pointer table
+ *  should match the order of enumerated types below.
+ */
+enum class ElecType : int
+{
+    Cut,          //!< Plain cut-off
+    RF,           //!< Reaction field
+    EwaldTab,     //!< Tabulated Ewald with single cut-off
+    EwaldTabTwin, //!< Tabulated Ewald with twin cut-off
+    EwaldAna,     //!< Analytical Ewald with single cut-off
+    EwaldAnaTwin, //!< Analytical Ewald with twin cut-off
+    Count         //!< Number of valid values
+};
+
+//! Number of possible \ref ElecType values.
+constexpr int c_numElecTypes = static_cast<int>(ElecType::Count);
+
+/*! \brief Nbnxm VdW GPU kernel flavors.
+ *
+ * The enumerates values correspond to the LJ implementations in the GPU non-bonded
+ * kernels.
+ *
+ * The column-order of pointers to different electrostatic kernels defined in
+ * nbnxn_cuda_ocl.cpp/.cu by the nb_*_kfunc_ptr function pointer table
+ * should match the order of enumerated types below.
+ */
+enum class VdwType : int
+{
+    Cut,         //!< Plain cut-off
+    CutCombGeom, //!< Cut-off with geometric combination rules
+    CutCombLB,   //!< Cut-off with Lorentz-Berthelot combination rules
+    FSwitch,     //!< Smooth force switch
+    PSwitch,     //!< Smooth potential switch
+    EwaldGeom,   //!< Ewald with geometric combination rules
+    EwaldLB,     //!< Ewald with Lorentz-Berthelot combination rules
+    Count        //!< Number of valid values
+};
+
+//! Number of possible \ref VdwType values.
+constexpr int c_numVdwTypes = static_cast<int>(VdwType::Count);
+
+/*! \brief Nonbonded NxN kernel types: plain C, CPU SIMD, GPU, GPU emulation */
+enum class KernelType : int
+{
+    NotSet = 0,
+    Cpu4x4_PlainC,
+    Cpu4xN_Simd_4xN,
+    Cpu4xN_Simd_2xNN,
+    Gpu8x8x8,
+    Cpu8x8x8_PlainC,
+    Count
+};
+
+/*! \brief Ewald exclusion types */
+enum class EwaldExclusionType : int
+{
+    NotSet = 0,
+    Table,
+    Analytical,
+    DecidedByGpuModule
+};
+
+/* \brief The non-bonded setup, also affects the pairlist construction kernel */
+struct KernelSetup
+{
+    //! The non-bonded type, also affects the pairlist construction kernel
+    KernelType kernelType = KernelType::NotSet;
+    //! Ewald exclusion computation handling type, currently only used for CPU
+    EwaldExclusionType ewaldExclusionType = EwaldExclusionType::NotSet;
+};
+
+/*! \brief Return a string identifying the kernel type.
+ *
+ * \param [in] kernelType   nonbonded kernel type, takes values from the nbnxn_kernel_type enum
+ * \returns                 a string identifying the kernel corresponding to the type passed as argument
+ */
+const char* lookup_kernel_name(Nbnxm::KernelType kernelType);
+
+} // namespace Nbnxm
+
+/*! \brief Flag to tell the nonbonded kernels whether to clear the force output buffers */
+enum
+{
+    enbvClearFNo,
+    enbvClearFYes
+};
+
+/*! \libinternal
+ *  \brief Top-level non-bonded data structure for the Verlet-type cut-off scheme. */
+struct nonbonded_verlet_t
+{
+public:
+    //! Constructs an object from its components
+    nonbonded_verlet_t(std::unique_ptr<PairlistSets>     pairlistSets,
+                       std::unique_ptr<PairSearch>       pairSearch,
+                       std::unique_ptr<nbnxn_atomdata_t> nbat,
+                       const Nbnxm::KernelSetup&         kernelSetup,
+                       NbnxmGpu*                         gpu_nbv,
+                       gmx_wallcycle*                    wcycle);
+
+    ~nonbonded_verlet_t();
+
+    //! Returns whether a GPU is use for the non-bonded calculations
+    bool useGpu() const { return kernelSetup_.kernelType == Nbnxm::KernelType::Gpu8x8x8; }
+
+    //! Returns whether a GPU is emulated for the non-bonded calculations
+    bool emulateGpu() const
+    {
+        return kernelSetup_.kernelType == Nbnxm::KernelType::Cpu8x8x8_PlainC;
+    }
+
+    //! Return whether the pairlist is of simple, CPU type
+    bool pairlistIsSimple() const { return !useGpu() && !emulateGpu(); }
+
+
+    //! Returns the order of the local atoms on the grid
+    gmx::ArrayRef<const int> getLocalAtomOrder() const;
+
+    //! Sets the order of the local atoms to the order grid atom ordering
+    void setLocalAtomOrder() const;
+
+    //! Returns the index position of the atoms on the search grid
+    gmx::ArrayRef<const int> getGridIndices() const;
+
+    /*! \brief Constructs the pairlist for the given locality
+     *
+     * When there are no non-self exclusions, \p exclusions can be empty.
+     * Otherwise the number of lists in \p exclusions should match the number
+     * of atoms when not using DD, or the total number of atoms in the i-zones
+     * when using DD.
+     *
+     * \param[in] iLocality   The interaction locality: local or non-local
+     * \param[in] exclusions  Lists of exclusions for every atom.
+     * \param[in] step        Used to set the list creation step
+     * \param[in,out] nrnb    Flop accounting struct, can be nullptr
+     */
+    void constructPairlist(gmx::InteractionLocality     iLocality,
+                           const gmx::ListOfLists<int>& exclusions,
+                           int64_t                      step,
+                           t_nrnb*                      nrnb) const;
+
+    //! Updates all the atom properties in Nbnxm
+    void setAtomProperties(gmx::ArrayRef<const int>     atomTypes,
+                           gmx::ArrayRef<const real>    atomCharges,
+                           gmx::ArrayRef<const int64_t> atomInfo) const;
+
+    /*!\brief Convert the coordinates to NBNXM format for the given locality.
+     *
+     * The API function for the transformation of the coordinates from one layout to another.
+     *
+     * \param[in] locality     Whether coordinates for local or non-local atoms should be
+     * transformed. \param[in] coordinates  Coordinates in plain rvec format to be transformed.
+     */
+    void convertCoordinates(gmx::AtomLocality locality, gmx::ArrayRef<const gmx::RVec> coordinates);
+
+    /*!\brief Convert the coordinates to NBNXM format on the GPU for the given locality
+     *
+     * The API function for the transformation of the coordinates from one layout to another in the GPU memory.
+     *
+     * \param[in] locality        Whether coordinates for local or non-local atoms should be transformed.
+     * \param[in] d_x             GPU coordinates buffer in plain rvec format to be transformed.
+     * \param[in] xReadyOnDevice  Event synchronizer indicating that the coordinates are ready in the device memory.
+     */
+    void convertCoordinatesGpu(gmx::AtomLocality       locality,
+                               DeviceBuffer<gmx::RVec> d_x,
+                               GpuEventSynchronizer*   xReadyOnDevice);
+
+    //! Init for GPU version of setup coordinates in Nbnxm
+    void atomdata_init_copy_x_to_nbat_x_gpu() const;
+
+    //! Returns a reference to the pairlist sets
+    const PairlistSets& pairlistSets() const { return *pairlistSets_; }
+
+    //! Returns whether step is a dynamic list pruning step, for CPU lists
+    bool isDynamicPruningStepCpu(int64_t step) const;
+
+    //! Returns whether step is a dynamic list pruning step, for GPU lists
+    bool isDynamicPruningStepGpu(int64_t step) const;
+
+    //! Dispatches the dynamic pruning kernel for the given locality, for CPU lists
+    void dispatchPruneKernelCpu(gmx::InteractionLocality       iLocality,
+                                gmx::ArrayRef<const gmx::RVec> shift_vec) const;
+
+    //! Dispatches the dynamic pruning kernel for GPU lists
+    void dispatchPruneKernelGpu(int64_t step);
+
+    //! \brief Executes the non-bonded kernel of the GPU or launches it on the GPU
+    void dispatchNonbondedKernel(gmx::InteractionLocality       iLocality,
+                                 const interaction_const_t&     ic,
+                                 const gmx::StepWorkload&       stepWork,
+                                 int                            clearF,
+                                 gmx::ArrayRef<const gmx::RVec> shiftvec,
+                                 gmx::ArrayRef<real>            repulsionDispersionSR,
+                                 gmx::ArrayRef<real>            CoulombSR,
+                                 t_nrnb*                        nrnb) const;
+
+    //! Executes the non-bonded free-energy kernels, local + non-local, always runs on the CPU
+    void dispatchFreeEnergyKernels(const gmx::ArrayRefWithPadding<const gmx::RVec>& coords,
+                                   gmx::ForceWithShiftForces*                forceWithShiftForces,
+                                   bool                                      useSimd,
+                                   int                                       ntype,
+                                   real                                      rlist,
+                                   const interaction_const_t&                ic,
+                                   gmx::ArrayRef<const gmx::RVec>            shiftvec,
+                                   gmx::ArrayRef<const real>                 nbfp,
+                                   gmx::ArrayRef<const real>                 nbfp_grid,
+                                   gmx::ArrayRef<const real>                 chargeA,
+                                   gmx::ArrayRef<const real>                 chargeB,
+                                   gmx::ArrayRef<const int>                  typeA,
+                                   gmx::ArrayRef<const int>                  typeB,
+                                   t_lambda*                                 fepvals,
+                                   gmx::ArrayRef<const real>                 lambda,
+                                   gmx_enerdata_t*                           enerd,
+                                   const gmx::StepWorkload&                  stepWork,
+                                   t_nrnb*                                   nrnb);
+
+    /*! \brief Add the forces stored in nbat to f, zeros the forces in nbat
+     * \param [in] locality         Local or non-local
+     * \param [inout] force         Force to be added to
+     */
+    void atomdata_add_nbat_f_to_f(gmx::AtomLocality locality, gmx::ArrayRef<gmx::RVec> force);
+
+    /*! \brief Get the number of atoms for a given locality
+     *
+     * \param [in] locality   Local or non-local
+     * \returns               The number of atoms for given locality
+     */
+    int getNumAtoms(gmx::AtomLocality locality) const;
+
+    //! Return the kernel setup
+    const Nbnxm::KernelSetup& kernelSetup() const { return kernelSetup_; }
+
+    //! Returns the outer radius for the pair list
+    real pairlistInnerRadius() const;
+
+    //! Returns the outer radius for the pair list
+    real pairlistOuterRadius() const;
+
+    //! Changes the pair-list outer and inner radius
+    void changePairlistRadii(real rlistOuter, real rlistInner) const;
+
+    //! Set up internal flags that indicate what type of short-range work there is.
+    void setupGpuShortRangeWork(const gmx::ListedForcesGpu* listedForcesGpu,
+                                gmx::InteractionLocality    iLocality) const;
+
+    void setupFepThreadedForceBuffer(int numAtomsForce);
+
+    // TODO: Make all data members private
+    //! All data related to the pair lists
+    std::unique_ptr<PairlistSets> pairlistSets_;
+    //! Working data for constructing the pairlists
+    std::unique_ptr<PairSearch> pairSearch_;
+    //! Atom data
+    std::unique_ptr<nbnxn_atomdata_t> nbat;
+
+private:
+    //! The non-bonded setup, also affects the pairlist construction kernel
+    Nbnxm::KernelSetup kernelSetup_;
+
+    //! \brief Pointer to wallcycle structure.
+    gmx_wallcycle* wcycle_;
+
+    //! \brief The non-bonded free-energy kernel dispatcher
+    std::unique_ptr<FreeEnergyDispatch> freeEnergyDispatch_;
+
+public:
+    //! GPU Nbnxm data, only used with a physical GPU (TODO: use unique_ptr)
+    NbnxmGpu* gpu_nbv;
+};
+
+namespace Nbnxm
+{
+
+/*! \brief Creates an Nbnxm object */
+std::unique_ptr<nonbonded_verlet_t> init_nb_verlet(const gmx::MDLogger& mdlog,
+                                                   const t_inputrec&    inputrec,
+                                                   const t_forcerec&    forcerec,
+                                                   const t_commrec*     commrec,
+                                                   const gmx_hw_info_t& hardwareInfo,
+                                                   bool                 useGpuForNonbonded,
+                                                   const gmx::DeviceStreamManager* deviceStreamManager,
+                                                   const gmx_mtop_t&               mtop,
+                                                   matrix                          box,
+                                                   gmx_wallcycle*                  wcycle);
+
+} // namespace Nbnxm
+
+/*! \brief Put the atoms on the pair search grid.
+ *
+ * Only atoms with indices wihtin \p atomRange in x are put on the grid.
+ * When \p updateGroupsCog != nullptr, atoms are put on the grid
+ * based on the center of geometry of the group they belong to.
+ * Atoms or COGs of groups should be within the bounding box provided,
+ * this is checked in debug builds when not using update groups.
+ * The atom density is used to determine the grid size when \p gridIndex = 0.
+ * When \p atomDensity <= 0, the density is determined from atomEnd-atomStart
+ * and the bounding box corners.
+ * With domain decomposition, part of the atoms might have migrated,
+ * but have not been removed yet. This count is given by \p numAtomsMoved.
+ * When \p move[i] < 0 particle i has migrated and will not be put on the grid.
+ *
+ * \param[in,out] nb_verlet    The non-bonded object
+ * \param[in]     box          Box used for periodic distance calculations
+ * \param[in]     gridIndex    The index of the grid to spread to, always 0 except with test particle insertion
+ * \param[in]     lowerCorner  Atom groups to be gridded should have coordinates >= this corner
+ * \param[in]     upperCorner  Atom groups to be gridded should have coordinates <= this corner
+ * \param[in]     updateGroupsCog  Centers of geometry for update groups, pass nullptr when not using update groups
+ * \param[in]     atomRange    Range of atoms to grid
+ * \param[in]     atomDensity  An estimate of the atom density, used for peformance optimization and only with \p gridIndex = 0
+ * \param[in]     atomInfo     Atom information flags
+ * \param[in]     x            Coordinates for atoms to grid
+ * \param[in]     numAtomsMoved  The number of atoms that will move to another domain, pass 0 without DD
+ * \param[in]     move         Move flags for atoms, pass nullptr without DD
+ */
+void nbnxn_put_on_grid(nonbonded_verlet_t*            nb_verlet,
+                       const matrix                   box,
+                       int                            gridIndex,
+                       const rvec                     lowerCorner,
+                       const rvec                     upperCorner,
+                       const gmx::UpdateGroupsCog*    updateGroupsCog,
+                       gmx::Range<int>                atomRange,
+                       real                           atomDensity,
+                       gmx::ArrayRef<const int64_t>   atomInfo,
+                       gmx::ArrayRef<const gmx::RVec> x,
+                       int                            numAtomsMoved,
+                       const int*                     move);
+
+/*! \brief As nbnxn_put_on_grid, but for the non-local atoms
+ *
+ * with domain decomposition. Should be called after calling
+ * nbnxn_search_put_on_grid for the local atoms / home zone.
+ */
+void nbnxn_put_on_grid_nonlocal(nonbonded_verlet_t*              nb_verlet,
+                                const struct gmx_domdec_zones_t* zones,
+                                gmx::ArrayRef<const int64_t>     atomInfo,
+                                gmx::ArrayRef<const gmx::RVec>   x);
+
+/*! \brief Check if GROMACS has been built with GPU support.
+ *
+ * \param[in] error Pointer to error string or nullptr.
+ * \todo Move this to NB module once it exists.
+ */
+bool buildSupportsNonbondedOnGpu(std::string* error);
+
+#endif // GMX_NBNXN_NBNXM_H
diff --git a/src/include/gromacs/nbnxm/nbnxm_geometry.h b/src/include/gromacs/nbnxm/nbnxm_geometry.h
new file mode 100644 (file)
index 0000000..68e6097
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief
+ * Declares the geometry-related functionality
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+#ifndef GMX_NBNXM_NBNXM_GEOMETRY_H
+#define GMX_NBNXM_NBNXM_GEOMETRY_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/nbnxm/nbnxm.h"
+#include "gromacs/simd/simd.h"
+#include "gromacs/utility/fatalerror.h"
+
+#include "pairlist.h"
+
+
+/*! \brief Returns the base-2 log of n.
+ * *
+ * Generates a fatal error when n is not an integer power of 2.
+ */
+static inline int get_2log(int n)
+{
+    if (!gmx::isPowerOfTwo(n))
+    {
+        gmx_fatal(FARGS, "nbnxn na_c (%d) is not a power of 2", n);
+    }
+
+    return gmx::log2I(n);
+}
+
+namespace Nbnxm
+{
+
+/*! \brief The nbnxn i-cluster size in atoms for each nbnxn kernel type */
+static constexpr gmx::EnumerationArray<KernelType, int> IClusterSizePerKernelType = {
+    { 0, c_nbnxnCpuIClusterSize, c_nbnxnCpuIClusterSize, c_nbnxnCpuIClusterSize, c_nbnxnGpuClusterSize, c_nbnxnGpuClusterSize }
+};
+
+/*! \brief The nbnxn j-cluster size in atoms for each nbnxn kernel type */
+static constexpr gmx::EnumerationArray<KernelType, int> JClusterSizePerKernelType = {
+    { 0,
+      c_nbnxnCpuIClusterSize,
+#if GMX_SIMD
+      GMX_SIMD_REAL_WIDTH,
+      GMX_SIMD_REAL_WIDTH / 2,
+#else
+      0,
+      0,
+#endif
+      c_nbnxnGpuClusterSize,
+      c_nbnxnGpuClusterSize / 2 }
+};
+
+/*! \brief Returns whether the pair-list corresponding to nb_kernel_type is simple */
+static inline bool kernelTypeUsesSimplePairlist(const KernelType kernelType)
+{
+    return (kernelType == KernelType::Cpu4x4_PlainC || kernelType == KernelType::Cpu4xN_Simd_4xN
+            || kernelType == KernelType::Cpu4xN_Simd_2xNN);
+}
+
+//! Returns whether a SIMD kernel is in use
+static inline bool kernelTypeIsSimd(const KernelType kernelType)
+{
+    return (kernelType == KernelType::Cpu4xN_Simd_4xN || kernelType == KernelType::Cpu4xN_Simd_2xNN);
+}
+
+} // namespace Nbnxm
+
+/*! \brief Returns the effective list radius of the pair-list
+ *
+ * Due to the cluster size the effective pair-list is longer than
+ * that of a simple atom pair-list. This function gives the extra distance.
+ *
+ * NOTE: If the i- and j-cluster sizes are identical and you know
+ *       the physical dimensions of the clusters, use the next function
+ *       for more accurate results
+ */
+real nbnxn_get_rlist_effective_inc(int jClusterSize, real atomDensity);
+
+/*! \brief Returns the effective list radius of the pair-list
+ *
+ * Due to the cluster size the effective pair-list is longer than
+ * that of a simple atom pair-list. This function gives the extra distance.
+ */
+real nbnxn_get_rlist_effective_inc(int clusterSize, const gmx::RVec& averageClusterBoundingBox);
+
+#endif
diff --git a/src/include/gromacs/nbnxm/nbnxm_gpu.h b/src/include/gromacs/nbnxm/nbnxm_gpu.h
new file mode 100644 (file)
index 0000000..2a49837
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declare interface for GPU execution for NBNXN module
+ *
+ *  \author Szilard Pall <pall.szilard@gmail.com>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_NBNXM_GPU_H
+#define GMX_NBNXM_NBNXM_GPU_H
+
+#include "gromacs/gpu_utils/gpu_macros.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/locality.h"
+#include "gromacs/nbnxm/atomdata.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+#include "nbnxm.h"
+
+struct interaction_const_t;
+struct nbnxn_atomdata_t;
+struct gmx_wallcycle;
+enum class GpuTaskCompletion;
+
+namespace gmx
+{
+class ListedForcesGpu;
+class StepWorkload;
+} // namespace gmx
+
+namespace Nbnxm
+{
+
+class Grid;
+
+/*! \brief Returns true if LJ combination rules are used in the non-bonded kernels.
+ *
+ *  \param[in] vdwType  The VdW interaction/implementation type as defined by VdwType
+ *                      enumeration.
+ *
+ * \returns Whether combination rules are used by the run.
+ */
+static inline bool useLjCombRule(const enum VdwType vdwType)
+{
+    return (vdwType == VdwType::CutCombGeom || vdwType == VdwType::CutCombLB);
+}
+
+/*! \brief
+ * Launch asynchronously the xq buffer host to device copy.
+ *
+ * The nonlocal copy is skipped if there is no dependent work to do,
+ * neither non-local nonbonded interactions nor bonded GPU work.
+ *
+ * \param [in]    nb        GPU nonbonded data.
+ * \param [in]    nbdata    Host-side atom data structure.
+ * \param [in]    aloc      Atom locality flag.
+ */
+GPU_FUNC_QUALIFIER
+void gpu_copy_xq_to_gpu(NbnxmGpu gmx_unused*          nb,
+                        const struct nbnxn_atomdata_t gmx_unused* nbdata,
+                        gmx::AtomLocality gmx_unused              aloc) GPU_FUNC_TERM;
+
+/*! \brief
+ * Launch asynchronously the nonbonded force calculations.
+ *
+ *  Also launches the initial pruning of a fresh list after search.
+ *
+ *  The local and non-local interaction calculations are launched in two
+ *  separate streams. If there is no work (i.e. empty pair list), the
+ *  force kernel launch is omitted.
+ *
+ */
+GPU_FUNC_QUALIFIER
+void gpu_launch_kernel(NbnxmGpu gmx_unused*    nb,
+                       const gmx::StepWorkload gmx_unused& stepWork,
+                       gmx::InteractionLocality gmx_unused iloc) GPU_FUNC_TERM;
+
+/*! \brief
+ * Launch asynchronously the nonbonded prune-only kernel.
+ *
+ *  The local and non-local list pruning are launched in their separate streams.
+ *
+ *  Notes for future scheduling tuning:
+ *  Currently we schedule the dynamic pruning between two MD steps *after* both local and
+ *  nonlocal force D2H transfers completed. We could launch already after the cpyback
+ *  is launched, but we want to avoid prune kernels (especially in the non-local
+ *  high prio-stream) competing with nonbonded work.
+ *
+ *  However, this is not ideal as this schedule does not expose the available
+ *  concurrency. The dynamic pruning kernel:
+ *    - should be allowed to overlap with any task other than force compute, including
+ *      transfers (F D2H and the next step's x H2D as well as force clearing).
+ *    - we'd prefer to avoid competition with non-bonded force kernels belonging
+ *      to the same rank and ideally other ranks too.
+ *
+ *  In the most general case, the former would require scheduling pruning in a separate
+ *  stream and adding additional event sync points to ensure that force kernels read
+ *  consistent pair list data. This would lead to some overhead (due to extra
+ *  cudaStreamWaitEvent calls, 3-5 us/call) which we might be able to live with.
+ *  The gains from additional overlap might not be significant as long as
+ *  update+constraints anyway takes longer than pruning, but there will still
+ *  be use-cases where more overlap may help (e.g. multiple ranks per GPU,
+ *  no/hbonds only constraints).
+ *  The above second point is harder to address given that multiple ranks will often
+ *  share a GPU. Ranks that complete their nonbondeds sooner can schedule pruning earlier
+ *  and without a third priority level it is difficult to avoid some interference of
+ *  prune kernels with force tasks (in particular preemption of low-prio local force task).
+ *
+ * \param [inout] nb        GPU nonbonded data.
+ * \param [in]    iloc      Interaction locality flag.
+ * \param [in]    numParts  Number of parts the pair list is split into in the rolling kernel.
+ */
+GPU_FUNC_QUALIFIER
+void gpu_launch_kernel_pruneonly(NbnxmGpu gmx_unused*                nb,
+                                 gmx::InteractionLocality gmx_unused iloc,
+                                 int gmx_unused                      numParts) GPU_FUNC_TERM;
+
+/*! \brief
+ * Launch asynchronously the download of short-range forces from the GPU
+ * (and energies/shift forces if required).
+ */
+GPU_FUNC_QUALIFIER
+void gpu_launch_cpyback(NbnxmGpu gmx_unused* nb,
+                        nbnxn_atomdata_t gmx_unused* nbatom,
+                        const gmx::StepWorkload gmx_unused& stepWork,
+                        gmx::AtomLocality gmx_unused        aloc) GPU_FUNC_TERM;
+
+/*! \brief Attempts to complete nonbonded GPU task.
+ *
+ *  This function attempts to complete the nonbonded task (both GPU and CPU auxiliary work).
+ *  Success, i.e. that the tasks completed and results are ready to be consumed, is signaled
+ *  by the return value (always true if blocking wait mode requested).
+ *
+ *  The \p completionKind parameter controls whether the behavior is non-blocking
+ *  (achieved by passing GpuTaskCompletion::Check) or blocking wait until the results
+ *  are ready (when GpuTaskCompletion::Wait is passed).
+ *  As the "Check" mode the function will return immediately if the GPU stream
+ *  still contain tasks that have not completed, it allows more flexible overlapping
+ *  of work on the CPU with GPU execution.
+ *
+ *  Note that it is only safe to use the results, and to continue to the next MD
+ *  step when this function has returned true which indicates successful completion of
+ *  - All nonbonded GPU tasks: both compute and device transfer(s)
+ *  - auxiliary tasks: updating the internal module state (timing accumulation, list pruning states) and
+ *  - internal staging reduction of (\p fshift, \p e_el, \p e_lj).
+ *
+ * In GpuTaskCompletion::Check mode this function does the timing and keeps correct count
+ * for the nonbonded task (incrementing only once per taks), in the GpuTaskCompletion::Wait mode
+ * timing is expected to be done in the caller.
+ *
+ *  TODO: improve the handling of outputs e.g. by ensuring that this function explcitly returns the
+ *  force buffer (instead of that being passed only to nbnxn_gpu_launch_cpyback()) and by returning
+ *  the energy and Fshift contributions for some external/centralized reduction.
+ *
+ * \param[in]  nb             The nonbonded data GPU structure
+ * \param[in]  stepWork       Step schedule flags
+ * \param[in]  aloc           Atom locality identifier
+ * \param[out] e_lj           Pointer to the LJ energy output to accumulate into
+ * \param[out] e_el           Pointer to the electrostatics energy output to accumulate into
+ * \param[out] shiftForces    Shift forces buffer to accumulate into
+ * \param[in]  completionKind Indicates whether nnbonded task completion should only be checked rather than waited for
+ * \param[out] wcycle         Pointer to wallcycle data structure
+ * \returns                   True if the nonbonded tasks associated with \p aloc locality have completed
+ */
+GPU_FUNC_QUALIFIER
+bool gpu_try_finish_task(NbnxmGpu gmx_unused*    nb,
+                         const gmx::StepWorkload gmx_unused& stepWork,
+                         gmx::AtomLocality gmx_unused        aloc,
+                         real gmx_unused* e_lj,
+                         real gmx_unused*                    e_el,
+                         gmx::ArrayRef<gmx::RVec> gmx_unused shiftForces,
+                         GpuTaskCompletion gmx_unused        completionKind,
+                         gmx_wallcycle gmx_unused* wcycle) GPU_FUNC_TERM_WITH_RETURN(false);
+
+/*! \brief  Completes the nonbonded GPU task blocking until GPU tasks and data
+ * transfers to finish.
+ *
+ * Also does timing accounting and reduction of the internal staging buffers.
+ * As this is called at the end of the step, it also resets the pair list and
+ * pruning flags.
+ *
+ * \param[in] nb The nonbonded data GPU structure
+ * \param[in]  stepWork        Step schedule flags
+ * \param[in] aloc Atom locality identifier
+ * \param[out] e_lj Pointer to the LJ energy output to accumulate into
+ * \param[out] e_el Pointer to the electrostatics energy output to accumulate into
+ * \param[out] shiftForces Shift forces buffer to accumulate into
+ * \param[out] wcycle         Pointer to wallcycle data structure               */
+GPU_FUNC_QUALIFIER
+float gpu_wait_finish_task(NbnxmGpu gmx_unused*    nb,
+                           const gmx::StepWorkload gmx_unused& stepWork,
+                           gmx::AtomLocality gmx_unused        aloc,
+                           real gmx_unused* e_lj,
+                           real gmx_unused*                    e_el,
+                           gmx::ArrayRef<gmx::RVec> gmx_unused shiftForces,
+                           gmx_wallcycle gmx_unused* wcycle) GPU_FUNC_TERM_WITH_RETURN(0.0);
+
+/*! \brief Initialization for X buffer operations on GPU.
+ * Called on the NS step and performs (re-)allocations and memory copies. !*/
+GPU_FUNC_QUALIFIER
+void nbnxn_gpu_init_x_to_nbat_x(const Nbnxm::GridSet gmx_unused& gridSet,
+                                NbnxmGpu gmx_unused* gpu_nbv) GPU_FUNC_TERM;
+
+/*! \brief X buffer operations on GPU: performs conversion from rvec to nb format.
+ *
+ * \param[in]     grid             Grid to be converted.
+ * \param[in,out] gpu_nbv          The nonbonded data GPU structure.
+ * \param[in]     d_x              Device-side coordinates in plain rvec format.
+ * \param[in]     xReadyOnDevice   Event synchronizer indicating that the coordinates are ready in
+ * the device memory.
+ * \param[in]     locality         Copy coordinates for local or non-local atoms.
+ * \param[in]     gridId           Index of the grid being converted.
+ * \param[in]     numColumnsMax    Maximum number of columns in the grid.
+ * \param[in]     mustInsertNonLocalDependency Whether synchronization between local and non-local
+ * streams should be added. Typically, true if and only if that is the last grid in gridset.
+ */
+GPU_FUNC_QUALIFIER
+void nbnxn_gpu_x_to_nbat_x(const Nbnxm::Grid gmx_unused& grid,
+                           NbnxmGpu gmx_unused*               gpu_nbv,
+                           DeviceBuffer<gmx::RVec> gmx_unused d_x,
+                           GpuEventSynchronizer gmx_unused* xReadyOnDevice,
+                           gmx::AtomLocality gmx_unused     locality,
+                           int gmx_unused                   gridId,
+                           int gmx_unused                   numColumnsMax,
+                           bool gmx_unused mustInsertNonLocalDependency) GPU_FUNC_TERM;
+
+/*! \brief Sync the nonlocal stream with dependent tasks in the local queue.
+ *
+ *  As the point where the local stream tasks can be considered complete happens
+ *  at the same call point where the nonlocal stream should be synced with the
+ *  the local, this function records the event if called with the local stream as
+ *  argument and inserts in the GPU stream a wait on the event on the nonlocal.
+ *
+ * \param[in] nb                   The nonbonded data GPU structure
+ * \param[in] interactionLocality  Local or NonLocal sync point
+ */
+GPU_FUNC_QUALIFIER
+void nbnxnInsertNonlocalGpuDependency(NbnxmGpu gmx_unused*                nb,
+                                      gmx::InteractionLocality gmx_unused interactionLocality) GPU_FUNC_TERM;
+
+/*! \brief Set up internal flags that indicate what type of short-range work there is.
+ *
+ * As nonbondeds and bondeds share input/output buffers and GPU queues,
+ * both are considered when checking for work in the current domain.
+ *
+ * This function is expected to be called every time the work-distribution
+ * can change (i.e. at search/domain decomposition steps).
+ *
+ * \param[inout]  nb               Pointer to the nonbonded GPU data structure
+ * \param[in]     listedForcesGpu  Pointer to the GPU bonded data structure
+ * \param[in]     iLocality        Interaction locality identifier
+ */
+GPU_FUNC_QUALIFIER
+void setupGpuShortRangeWork(NbnxmGpu gmx_unused*       nb,
+                            const gmx::ListedForcesGpu gmx_unused* listedForcesGpu,
+                            gmx::InteractionLocality gmx_unused    iLocality) GPU_FUNC_TERM;
+
+/*! \brief Returns true if there is GPU short-range work for the given interaction locality.
+ *
+ * Note that as, unlike nonbonded tasks, bonded tasks are not split into local/nonlocal,
+ * and therefore if there are GPU offloaded bonded interactions, this function will return
+ * true for both local and nonlocal atom range.
+ *
+ * \param[inout]  nb                   Pointer to the nonbonded GPU data structure
+ * \param[in]     interactionLocality  Interaction locality identifier
+ *
+ * \return Whether there is short range work for a given locality.
+ */
+GPU_FUNC_QUALIFIER
+bool haveGpuShortRangeWork(const NbnxmGpu gmx_unused* nb, gmx::InteractionLocality gmx_unused interactionLocality)
+        GPU_FUNC_TERM_WITH_RETURN(false);
+
+} // namespace Nbnxm
+#endif
diff --git a/src/include/gromacs/nbnxm/nbnxm_gpu_buffer_ops_internal.h b/src/include/gromacs/nbnxm/nbnxm_gpu_buffer_ops_internal.h
new file mode 100644 (file)
index 0000000..09b2d6e
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief
+ *  Wrapper for the backend-specific coordinate layout conversion functionality
+ *
+ *  \ingroup module_nbnxm
+ */
+#include "gromacs/gpu_utils/devicebuffer_datatype.h"
+#include "gromacs/gpu_utils/gputraits.h"
+
+class DeviceStream;
+struct NbnxmGpu;
+
+namespace Nbnxm
+{
+
+class Grid;
+
+/*! \brief Launch coordinate layout conversion kernel
+ *
+ * \param[in]     grid          Pair-search grid.
+ * \param[in,out] nb            Nbnxm main structure.
+ * \param[in]     d_x           Source atom coordinates.
+ * \param[in]     deviceStream  Device stream for kernel submission.
+ * \param[in]     numColumnsMax Max. number of columns per grid for offset calculation in \p nb.
+ * \param[in]     gridId        Grid index for offset calculation in \p nb.
+ */
+void launchNbnxmKernelTransformXToXq(const Grid&          grid,
+                                     NbnxmGpu*            nb,
+                                     DeviceBuffer<Float3> d_x,
+                                     const DeviceStream&  deviceStream,
+                                     unsigned int         numColumnsMax,
+                                     int                  gridId);
+
+} // namespace Nbnxm
diff --git a/src/include/gromacs/nbnxm/nbnxm_gpu_data_mgmt.h b/src/include/gromacs/nbnxm/nbnxm_gpu_data_mgmt.h
new file mode 100644 (file)
index 0000000..19652c7
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declare common functions for NBNXM GPU data management.
+ *
+ *  \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ *  \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_NBNXM_GPU_DATA_MGMT_H
+#define GMX_NBNXM_NBNXM_GPU_DATA_MGMT_H
+
+class DeviceContext;
+struct interaction_const_t;
+struct NBParamGpu;
+struct PairlistParams;
+
+namespace gmx
+{
+enum class InteractionLocality;
+}
+
+namespace Nbnxm
+{
+
+struct gpu_plist;
+
+/*! \brief Initializes the NBNXM GPU data structures. */
+void gpu_init_platform_specific(NbnxmGpu* nb);
+
+/*! \brief Releases the NBNXM GPU data structures. */
+void gpu_free_platform_specific(NbnxmGpu* nb);
+
+} // namespace Nbnxm
+
+#endif // GMX_NBNXM_NBNXM_GPU_DATA_MGMT_H
diff --git a/src/include/gromacs/nbnxm/nbnxm_simd.h b/src/include/gromacs/nbnxm/nbnxm_simd.h
new file mode 100644 (file)
index 0000000..f29e555
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief
+ * Defines constants used to know which Nbnxm kernel flavours (4xn or 2xnn)
+ * can be supported by the SIMD layer in use.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+#ifndef GMX_NBNXM_NBNXM_SIMD_H
+#define GMX_NBNXM_NBNXM_SIMD_H
+
+#include "config.h"
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/simd/simd.h"
+#include "gromacs/utility/real.h"
+
+#if GMX_SIMD && GMX_USE_SIMD_KERNELS
+/*! \brief The nbnxn SIMD 4xN and 2x(N+N) kernels can be added independently.
+ * Currently the 2xNN SIMD kernels only make sense with:
+ *  8-way SIMD: 4x4 setup, works with AVX-256 in single precision
+ * 16-way SIMD: 4x8 setup, not currently in use, but worked with Intel MIC
+ */
+#    if GMX_SIMD_REAL_WIDTH == 2 || GMX_SIMD_REAL_WIDTH == 4 || GMX_SIMD_REAL_WIDTH == 8
+#        define GMX_NBNXN_SIMD_4XN
+#    endif
+#    if GMX_SIMD_REAL_WIDTH == 8 || GMX_SIMD_REAL_WIDTH == 16
+#        define GMX_NBNXN_SIMD_2XNN
+#    endif
+
+#    if !(defined GMX_NBNXN_SIMD_4XN || defined GMX_NBNXN_SIMD_2XNN)
+#        error "No SIMD kernel type defined"
+#    endif
+
+#endif // GMX_SIMD && GMX_USE_SIMD_KERNELS
+
+#endif
diff --git a/src/include/gromacs/nbnxm/opencl/nbnxm_ocl_consts.h b/src/include/gromacs/nbnxm/opencl/nbnxm_ocl_consts.h
new file mode 100644 (file)
index 0000000..bef4f21
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief
+ * Declares constants for OpenCL code
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+#ifndef NBNXN_OCL_CONSTS_H
+#define NBNXN_OCL_CONSTS_H
+
+/*! \brief Macro definining default for the prune kernel's j4 processing concurrency.
+ *
+ *  The GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY macro allows compile-time override with the default value of 4.
+ */
+#ifndef GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY
+#    define GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY 4
+#endif
+
+#endif
diff --git a/src/include/gromacs/nbnxm/opencl/nbnxm_ocl_kernel_pruneonly.clh b/src/include/gromacs/nbnxm/opencl/nbnxm_ocl_kernel_pruneonly.clh
new file mode 100644 (file)
index 0000000..b469440
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief OpenCL pruning kernel.
+ *
+ *  OpenCL 1.2 support is expected; tested on AMD GCN and NVIDIA CC >3.0.
+ *
+ *  \author Szilárd Páll <pall.szilard@gmail.com>
+ *  \ingroup module_nbnxm
+ */
+
+#include "nbnxm_ocl_kernel_utils.clh"
+
+/* Note: the AMD compiler testing was done with (fglrx 15.12) performs best with wg
+ * size 256 (this is an artificial compiler limitation). The compiler is also
+ * sensitive to tidx/widx declaration and warp_any initialization.
+ * With the current tweaks the regular prune kenel achieves 90%, the rolling 100%
+ * occupancy with both Fiji and Hawaii.
+ * TODO: if the wg size limit is removed in an upcoming AMD compiler the NTHREAD_Z=4
+ * should be revisited.
+ *
+ */
+#define NTHREAD_Z GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY
+
+__attribute__((reqd_work_group_size(CL_SIZE, CL_SIZE, NTHREAD_Z)))
+#ifdef HAVE_FRESH_LIST
+__kernel void
+nbnxn_kernel_prune_opencl
+#else
+__kernel void
+nbnxn_kernel_prune_rolling_opencl
+#endif
+        (cl_nbparam_params_t nbparam_params,
+         const __global float4* restrict xq,
+         const __global float* restrict  shift_vec,
+         const __global nbnxn_sci_t* pl_sci,
+         __global nbnxn_cj4_t* pl_cj4,
+#if !defined HAVE_FRESH_LIST
+         const
+#endif
+         __global unsigned int* restrict prePrunedImask,
+         int                             numParts,
+         int                             part,
+         __local float4* xib)
+{
+    /* convenience variables */
+    const cl_nbparam_params_t* const nbparam = &nbparam_params;
+
+    const float rlistOuter_sq = nbparam->rlistOuter_sq;
+    const float rlistInner_sq = nbparam->rlistInner_sq;
+
+    /* thread/block/warp id-s */
+    const int tidxi = get_local_id(0);
+    const int tidxj = get_local_id(1);
+    const int tidx  = (int)(get_local_id(1) * get_local_size(0) + get_local_id(0));
+#if NTHREAD_Z == 1
+    const int tidxz = 0;
+#else
+    const int          tidxz         = get_local_id(2);
+#endif
+    const int bidx = get_group_id(0);
+    const int widx = tidx / WARP_SIZE;
+
+#ifdef HAVE_FRESH_LIST
+    const bool haveFreshList = true;
+#else
+    const bool         haveFreshList = false;
+#endif
+
+    // TODO move these consts to utils and unify their use with the nonbonded kernels
+    const int c_clSize = CL_SIZE;
+
+    // TODO pass this value at compile-time as a macro
+    const int c_nbnxnGpuClusterpairSplit = 2;
+
+    /*! i-cluster interaction mask for a super-cluster with all c_nbnxnGpuNumClusterPerSupercluster=8 bits set */
+    const unsigned superClInteractionMask = ((1U << c_nbnxnGpuNumClusterPerSupercluster) - 1U);
+
+#define LOCAL_OFFSET (xib + c_nbnxnGpuNumClusterPerSupercluster * c_clSize)
+    /* shmem buffer for i cj pre-loading */
+    CjType cjs = 0;
+#if USE_CJ_PREFETCH
+    cjs = (((__local int*)(LOCAL_OFFSET)) + tidxz * c_nbnxnGpuClusterpairSplit * c_nbnxnGpuJgroupSize);
+#    undef LOCAL_OFFSET
+/* Offset calculated using xib because cjs depends on on tidxz! */
+#    define LOCAL_OFFSET                                                        \
+        (((__local int*)(xib + c_nbnxnGpuNumClusterPerSupercluster * c_clSize)) \
+         + (NTHREAD_Z * c_nbnxnGpuClusterpairSplit * c_nbnxnGpuJgroupSize))
+#endif
+#if !USE_SUBGROUP_ANY
+    /* Local buffer used to implement __any warp vote function from CUDA.
+       volatile is used to avoid compiler optimizations for AMD builds. */
+    //NOLINTNEXTLINE(google-readability-casting)
+    volatile __local int* const warp_any     = (__local int*)(LOCAL_OFFSET);
+    const int                   warpVoteSlot = NTHREAD_Z * tidxz + widx;
+    /* Initialise warp vote.*/
+    // if (tidx == 0 || tidx == 32)
+    if (tidx % (c_clSize * c_clSize / c_nbnxnGpuClusterpairSplit) == 0)
+    {
+        warp_any[warpVoteSlot] = 0;
+    }
+#else
+    __local int* const warp_any      = 0;
+    const int          warpVoteSlot  = 0;
+#endif
+#undef LOCAL_OFFSET
+
+    const nbnxn_sci_t nb_sci =
+            pl_sci[bidx * numParts + part]; /* my i super-cluster's index = sciOffset + current bidx * numParts + part */
+    const int sci        = nb_sci.sci;           /* super-cluster */
+    const int cij4_start = nb_sci.cj4_ind_start; /* first ...*/
+    const int cij4_end   = nb_sci.cj4_ind_end;   /* and last index of j clusters */
+
+    if (tidxz == 0)
+    {
+        for (int i = 0; i < c_nbnxnGpuNumClusterPerSupercluster; i += CL_SIZE)
+        {
+            /* Pre-load i-atom x and q into shared memory */
+            const int ci = sci * c_nbnxnGpuNumClusterPerSupercluster + tidxj + i;
+            const int ai = ci * c_clSize + tidxi;
+
+            /* We don't need q, but using float4 in shmem avoids bank conflicts */
+            const float4 tmp = xq[ai];
+            const float4 xi  = tmp
+                              + (float4)(shift_vec[3 * nb_sci.shift],
+                                         shift_vec[3 * nb_sci.shift + 1],
+                                         shift_vec[3 * nb_sci.shift + 2],
+                                         0.0F);
+            xib[(tidxj + i) * c_clSize + tidxi] = xi;
+        }
+    }
+    barrier(CLK_LOCAL_MEM_FENCE);
+
+
+    /* loop over the j clusters = seen by any of the atoms in the current super-cluster */
+    for (int j4 = cij4_start + tidxz; j4 < cij4_end; j4 += NTHREAD_Z)
+    {
+        unsigned int imaskFull = 0, imaskCheck = 0, imaskNew = 0;
+
+        if (haveFreshList)
+        {
+            /* Read the mask from the list transferred from the CPU */
+            imaskFull = pl_cj4[j4].imei[widx].imask;
+            /* We attempt to prune all pairs present in the original list */
+            imaskCheck = imaskFull;
+            imaskNew   = 0;
+        }
+        else
+        {
+            /* Read the mask from the "warp-pruned" by rlistOuter mask array */
+            imaskFull = prePrunedImask[j4 * c_nbnxnGpuClusterpairSplit + widx];
+            /* Read the old rolling pruned mask, use as a base for new */
+            imaskNew = pl_cj4[j4].imei[widx].imask;
+            /* We only need to check pairs with different mask */
+            imaskCheck = (imaskNew ^ imaskFull);
+        }
+
+        preloadCj4(&cjs, pl_cj4[j4].cj, tidxi, tidxj, imaskCheck != 0U);
+
+        if (imaskCheck)
+        {
+#pragma unroll 4
+            for (int jm = 0; jm < c_nbnxnGpuJgroupSize; jm++)
+            {
+                if (imaskCheck & (superClInteractionMask << (jm * c_nbnxnGpuNumClusterPerSupercluster)))
+                {
+                    unsigned int mask_ji = (1U << (jm * c_nbnxnGpuNumClusterPerSupercluster));
+
+                    const int cj = loadCj(cjs, pl_cj4[j4].cj, jm, tidxi, tidxj);
+                    const int aj = cj * c_clSize + tidxj;
+
+                    /* load j atom data */
+                    const float4 tmp = xq[aj];
+                    const float3 xj  = (float3)(tmp.xyz);
+
+#pragma unroll 8
+                    for (int i = 0; i < c_nbnxnGpuNumClusterPerSupercluster; i++)
+                    {
+                        if (imaskCheck & mask_ji)
+                        {
+                            /* load i-cluster coordinates from shmem */
+                            const float4 xi = xib[i * c_clSize + tidxi];
+
+                            /* distance between i and j atoms */
+                            const float3 rv = (float3)(xi.xyz) - xj;
+                            const float  r2 = norm2(rv);
+
+                            if (haveFreshList)
+                            {
+                                /* If _none_ of the atoms pairs are in cutoff range,
+                                   the bit corresponding to the current
+                                   cluster-pair in imask gets set to 0. */
+                                if (!gmx_sub_group_any(warp_any, warpVoteSlot, r2 < rlistOuter_sq))
+                                {
+                                    imaskFull &= ~mask_ji;
+                                }
+                            }
+                            /* If any atom pair is within range, set the bit
+                               corresponding to the current cluster-pair. */
+                            if (gmx_sub_group_any(warp_any, warpVoteSlot, r2 < rlistInner_sq))
+                            {
+                                imaskNew |= mask_ji;
+                            }
+                        }
+
+                        /* shift the mask bit by 1 */
+                        mask_ji += mask_ji;
+                    }
+                }
+            }
+
+#ifdef HAVE_FRESH_LIST
+            {
+                /* copy the list pruned to rlistOuter to a separate buffer */
+                prePrunedImask[j4 * c_nbnxnGpuClusterpairSplit + widx] = imaskFull;
+            }
+#endif
+            /* update the imask with only the pairs up to rlistInner */
+            pl_cj4[j4].imei[widx].imask = imaskNew;
+        }
+    }
+}
+
+#undef USE_CJ_PREFETCH
diff --git a/src/include/gromacs/nbnxm/opencl/nbnxm_ocl_types.h b/src/include/gromacs/nbnxm/opencl/nbnxm_ocl_types.h
new file mode 100644 (file)
index 0000000..d2db195
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief
+ *  Data types used internally in the nbnxm_ocl module.
+ *
+ *  \author Anca Hamuraru <anca@streamcomputing.eu>
+ *  \author Szilárd Páll <pszilard@kth.se>
+ *  \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_NBNXM_OPENCL_TYPES_H
+#define GMX_NBNXM_NBNXM_OPENCL_TYPES_H
+
+#include "gromacs/gpu_utils/devicebuffer.h"
+#include "gromacs/gpu_utils/gmxopencl.h"
+#include "gromacs/gpu_utils/gpueventsynchronizer.h"
+#include "gromacs/gpu_utils/gputraits_ocl.h"
+#include "gromacs/gpu_utils/oclutils.h"
+#include "gromacs/mdtypes/interaction_const.h"
+#include "gromacs/nbnxm/gpu_types_common.h"
+#include "gromacs/nbnxm/nbnxm.h"
+#include "gromacs/nbnxm/nbnxm_gpu.h"
+#include "gromacs/nbnxm/pairlist.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_wallclock_gpu_nbnxn_t;
+
+/*! \brief Pruning kernel flavors.
+ *
+ * The values correspond to the first call of the pruning post-list generation
+ * and the rolling pruning, respectively.
+ */
+enum ePruneKind
+{
+    epruneFirst,
+    epruneRolling,
+    ePruneNR
+};
+
+/*! \internal
+ * \brief Data structure shared between the OpenCL device code and OpenCL host code
+ *
+ * Must not contain OpenCL objects (buffers)
+ * TODO: review, improve */
+typedef struct cl_nbparam_params
+{
+
+    //! type of electrostatics
+    enum Nbnxm::ElecType elecType;
+    //! type of VdW impl.
+    enum Nbnxm::VdwType vdwType;
+
+    //! charge multiplication factor
+    float epsfac;
+    //! Reaction-field/plain cutoff electrostatics const.
+    float c_rf;
+    //! Reaction-field electrostatics constant
+    float two_k_rf;
+    //! Ewald/PME parameter
+    float ewald_beta;
+    //! Ewald/PME correction term subtracted from the direct-space potential
+    float sh_ewald;
+    //! LJ-Ewald/PME correction term added to the correction potential
+    float sh_lj_ewald;
+    //! LJ-Ewald/PME coefficient
+    float ewaldcoeff_lj;
+
+    //! Coulomb cut-off squared
+    float rcoulomb_sq;
+
+    //! VdW cut-off squared
+    float rvdw_sq;
+    //! VdW switched cut-off
+    float rvdw_switch;
+    //! Full, outer pair-list cut-off squared
+    float rlistOuter_sq;
+    //! Inner, dynamic pruned pair-list cut-off squared
+    float rlistInner_sq;
+
+    //! VdW shift dispersion constants
+    shift_consts_t dispersion_shift;
+    //! VdW shift repulsion constants
+    shift_consts_t repulsion_shift;
+    //! VdW switch constants
+    switch_consts_t vdw_switch;
+
+    /* Ewald Coulomb force table data - accessed through texture memory */
+    //! table scale/spacing
+    float coulomb_tab_scale;
+} cl_nbparam_params_t;
+
+
+/*! \internal
+ * \brief Main data structure for OpenCL nonbonded force calculations.
+ */
+struct NbnxmGpu
+{
+    /* \brief OpenCL device context
+     *
+     * \todo Make it constant reference, once NbnxmGpu is a proper class.
+     */
+    const DeviceContext* deviceContext_;
+    //! OpenCL runtime data (context, kernels)
+    struct gmx_device_runtime_data_t* dev_rundata = nullptr;
+
+    /**< Pointers to non-bonded kernel functions
+     * organized similar with nb_kfunc_xxx arrays in nbnxn_ocl.cpp */
+    ///@{
+    cl_kernel kernel_noener_noprune_ptr[Nbnxm::c_numElecTypes][Nbnxm::c_numVdwTypes] = { { nullptr } };
+    cl_kernel kernel_ener_noprune_ptr[Nbnxm::c_numElecTypes][Nbnxm::c_numVdwTypes] = { { nullptr } };
+    cl_kernel kernel_noener_prune_ptr[Nbnxm::c_numElecTypes][Nbnxm::c_numVdwTypes] = { { nullptr } };
+    cl_kernel kernel_ener_prune_ptr[Nbnxm::c_numElecTypes][Nbnxm::c_numVdwTypes] = { { nullptr } };
+    ///@}
+    //! prune kernels, ePruneKind defined the kernel kinds
+    cl_kernel kernel_pruneonly[ePruneNR] = { nullptr };
+
+    //! true if prefetching fg i-atom LJ parameters should be used in the kernels
+    bool bPrefetchLjParam = false;
+
+    /**< auxiliary kernels implementing memset-like functions */
+    ///@{
+    cl_kernel kernel_memset_f  = nullptr;
+    cl_kernel kernel_memset_f2 = nullptr;
+    cl_kernel kernel_memset_f3 = nullptr;
+    ///@}
+
+    //! true if doing both local/non-local NB work on GPU
+    bool bUseTwoStreams = false;
+    //! true indicates that the nonlocal_done event was marked
+    bool bNonLocalStreamDoneMarked = false;
+
+    //! atom data
+    NBAtomDataGpu* atdat = nullptr;
+    //! parameters required for the non-bonded calc.
+    NBParamGpu* nbparam = nullptr;
+    //! pair-list data structures (local and non-local)
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, Nbnxm::gpu_plist*> plist = { nullptr };
+    //! staging area where fshift/energies get downloaded
+    NBStagingData nbst;
+
+    // Data for GPU-side coordinate conversion between integrator and NBNXM
+    /*! \brief array of atom indices */
+    DeviceBuffer<int> atomIndices;
+    /*! \brief size of atom indices */
+    int atomIndicesSize = 0;
+    /*! \brief size of atom indices allocated in device buffer */
+    int atomIndicesSize_alloc = 0;
+    /*! \brief x buf ops num of atoms */
+    DeviceBuffer<int> cxy_na;
+    /*! \brief number of elements in cxy_na */
+    int ncxy_na = 0;
+    /*! \brief number of elements allocated allocated in device buffer */
+    int ncxy_na_alloc = 0;
+    /*! \brief x buf ops cell index mapping */
+    DeviceBuffer<int> cxy_ind;
+    /*! \brief number of elements in cxy_ind */
+    int ncxy_ind = 0;
+    /*! \brief number of elements allocated allocated in device buffer */
+    int ncxy_ind_alloc = 0;
+
+    //! local and non-local GPU queues
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, const DeviceStream*> deviceStreams;
+
+    /*! \brief Events used for synchronization */
+    /*! \{ */
+    /*! \brief Event triggered when the non-local non-bonded
+     * kernel is done (and the local transfer can proceed) */
+    GpuEventSynchronizer nonlocal_done;
+    /*! \brief Event triggered when the tasks issued in the local
+     * stream that need to precede the non-local force or buffer
+     * operation calculations are done (e.g. f buffer 0-ing, local
+     * x/q H2D, buffer op initialization in local stream that is
+     * required also by nonlocal stream ) */
+    GpuEventSynchronizer misc_ops_and_local_H2D_done;
+    /*! \} */
+
+    //! True if there has been local/nonlocal GPU work, either bonded or nonbonded, scheduled
+    //  to be executed in the current domain. As long as bonded work is not split up into
+    //  local/nonlocal, if there is bonded GPU work, both flags will be true.
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, bool> haveWork;
+
+
+    //! True if event-based timing is enabled.
+    bool bDoTime = false;
+    //! OpenCL event-based timers.
+    Nbnxm::GpuTimers* timers = nullptr;
+    //! Timing data. TODO: deprecate this and query timers for accumulated data instead
+    gmx_wallclock_gpu_nbnxn_t* timings = nullptr;
+};
+
+#endif /* NBNXN_OPENCL_TYPES_H */
diff --git a/src/include/gromacs/nbnxm/pairlist.h b/src/include/gromacs/nbnxm/pairlist.h
new file mode 100644 (file)
index 0000000..bb9db40
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_NBNXM_PAIRLIST_H
+#define GMX_NBNXM_PAIRLIST_H
+
+#include <cstddef>
+
+#include "gromacs/gpu_utils/hostallocator.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/locality.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/defaultinitializationallocator.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/real.h"
+
+#include "pairlistparams.h"
+
+struct NbnxnPairlistCpuWork;
+struct NbnxnPairlistGpuWork;
+struct t_nblist;
+
+
+//! Convenience type for vector with aligned memory
+template<typename T>
+using AlignedVector = std::vector<T, gmx::AlignedAllocator<T>>;
+
+//! Convenience type for vector that avoids initialization at resize()
+template<typename T>
+using FastVector = std::vector<T, gmx::DefaultInitializationAllocator<T>>;
+
+/*! \brief Cache-line protection buffer
+ *
+ * A buffer data structure of 64 bytes
+ * to be placed at the beginning and end of structs
+ * to avoid cache invalidation of the real contents
+ * of the struct by writes to neighboring memory.
+ */
+typedef struct
+{
+    //! Unused field used to create space to protect cache lines that are in use
+    int dummy[16];
+} gmx_cache_protect_t;
+
+/*! \brief This is the actual cluster-pair list j-entry.
+ *
+ * cj is the j-cluster.
+ * The interaction bits in excl are indexed i-major, j-minor.
+ * The cj entries are sorted such that ones with exclusions come first.
+ * This means that once a full mask (=NBNXN_INTERACTION_MASK_ALL)
+ * is found, all subsequent j-entries in the i-entry also have full masks.
+ */
+struct nbnxn_cj_t
+{
+    //! The j-cluster
+    int cj;
+    //! The exclusion (interaction) bits
+    unsigned int excl;
+};
+
+/*! \brief Constants for interpreting interaction flags
+ *
+ * In nbnxn_ci_t the integer shift contains the shift in the lower 7 bits.
+ * The upper bits contain information for non-bonded kernel optimization.
+ * Simply calculating LJ and Coulomb for all pairs in a cluster pair is fine.
+ * But three flags can be used to skip interactions, currently only for subc=0
+ * !(shift & NBNXN_CI_DO_LJ(subc))   => we can skip LJ for all pairs
+ * shift & NBNXN_CI_HALF_LJ(subc)    => we can skip LJ for the second half of i
+ * !(shift & NBNXN_CI_DO_COUL(subc)) => we can skip Coulomb for all pairs
+ */
+//! \{
+#define NBNXN_CI_SHIFT 127
+#define NBNXN_CI_DO_LJ(subc) (1 << (7 + 3 * (subc)))
+#define NBNXN_CI_HALF_LJ(subc) (1 << (8 + 3 * (subc)))
+#define NBNXN_CI_DO_COUL(subc) (1 << (9 + 3 * (subc)))
+//! \}
+
+/*! \brief Cluster-pair Interaction masks
+ *
+ * Bit i*j-cluster-size + j tells if atom i and j interact.
+ */
+//! \{
+// TODO: Rename according to convention when moving into Nbnxn namespace
+//! All interaction mask is the same for all kernels
+constexpr unsigned int NBNXN_INTERACTION_MASK_ALL = 0xffffffffU;
+//! 4x4 kernel diagonal mask
+constexpr unsigned int NBNXN_INTERACTION_MASK_DIAG = 0x08ceU;
+//! 4x2 kernel diagonal masks
+//! \{
+constexpr unsigned int NBNXN_INTERACTION_MASK_DIAG_J2_0 = 0x0002U;
+constexpr unsigned int NBNXN_INTERACTION_MASK_DIAG_J2_1 = 0x002fU;
+//! \}
+//! 4x8 kernel diagonal masks
+//! \{
+constexpr unsigned int NBNXN_INTERACTION_MASK_DIAG_J8_0 = 0xf0f8fcfeU;
+constexpr unsigned int NBNXN_INTERACTION_MASK_DIAG_J8_1 = 0x0080c0e0U;
+//! \}
+//! \}
+
+/*! \brief Lower limit for square interaction distances in nonbonded kernels.
+ *
+ * For smaller values we will overflow when calculating r^-1 or r^-12, but
+ * to keep it simple we always apply the limit from the tougher r^-12 condition.
+ */
+#if GMX_DOUBLE
+// Some double precision SIMD architectures use single precision in the first
+// step, so although the double precision criterion would allow smaller rsq,
+// we need to stay in single precision with some margin for the N-R iterations.
+constexpr double c_nbnxnMinDistanceSquared = 1.0e-36;
+#else
+// The worst intermediate value we might evaluate is r^-12, which
+// means we should ensure r^2 stays above pow(GMX_FLOAT_MAX,-1.0/6.0)*1.01 (some margin)
+constexpr float c_nbnxnMinDistanceSquared = 3.82e-07F; // r > 6.2e-4
+#endif
+
+
+//! The number of clusters in a super-cluster, used for GPU
+constexpr int c_nbnxnGpuNumClusterPerSupercluster = 8;
+
+/*! \brief With GPU kernels we group cluster pairs in 4 to optimize memory usage
+ * of integers containing 32 bits.
+ */
+constexpr int c_nbnxnGpuJgroupSize = (32 / c_nbnxnGpuNumClusterPerSupercluster);
+
+/*! \internal
+ * \brief Simple pair-list i-unit
+ */
+struct nbnxn_ci_t
+{
+    //! i-cluster
+    int ci;
+    //! Shift vector index plus possible flags, see above
+    int shift;
+    //! Start index into cj
+    int cj_ind_start;
+    //! End index into cj
+    int cj_ind_end;
+};
+
+//! Grouped pair-list i-unit
+typedef struct nbnxn_sci
+{
+    //! Returns the number of j-cluster groups in this entry
+    int numJClusterGroups() const { return cj4_ind_end - cj4_ind_start; }
+
+    //! i-super-cluster
+    int sci;
+    //! Shift vector index plus possible flags
+    int shift;
+    //! Start index into cj4
+    int cj4_ind_start;
+    //! End index into cj4
+    int cj4_ind_end;
+} nbnxn_sci_t;
+
+//! Interaction data for a j-group for one warp
+struct nbnxn_im_ei_t
+{
+    //! The i-cluster interactions mask for 1 warp
+    unsigned int imask = 0U;
+    //! Index into the exclusion array for 1 warp, default index 0 which means no exclusions
+    int excl_ind = 0;
+};
+
+//! Four-way j-cluster lists
+typedef struct
+{
+    //! The 4 j-clusters
+    int cj[c_nbnxnGpuJgroupSize];
+    //! The i-cluster mask data for 2 warps
+    nbnxn_im_ei_t imei[c_nbnxnGpuClusterpairSplit];
+} nbnxn_cj4_t;
+
+//! Struct for storing the atom-pair interaction bits for a cluster pair in a GPU pairlist
+struct nbnxn_excl_t
+{
+    //! Constructor, sets no exclusions, so all atom pairs interacting
+    MSVC_DIAGNOSTIC_IGNORE(26495) // pair is not being initialized!
+    nbnxn_excl_t()
+    {
+        for (unsigned int& pairEntry : pair)
+        {
+            pairEntry = NBNXN_INTERACTION_MASK_ALL;
+        }
+    }
+    MSVC_DIAGNOSTIC_RESET
+
+    //! Topology exclusion interaction bits per warp
+    unsigned int pair[c_nbnxnGpuExclSize];
+};
+
+//! Cluster pairlist type for use on CPUs
+struct NbnxnPairlistCpu
+{
+    NbnxnPairlistCpu();
+
+    //! Cache protection
+    gmx_cache_protect_t cp0;
+
+    //! The number of atoms per i-cluster
+    int na_ci;
+    //! The number of atoms per j-cluster
+    int na_cj;
+    //! The radius for constructing the list
+    real rlist;
+    //! The i-cluster list
+    FastVector<nbnxn_ci_t> ci;
+    //! The outer, unpruned i-cluster list
+    FastVector<nbnxn_ci_t> ciOuter;
+
+    //! The j-cluster list, size ncj
+    FastVector<nbnxn_cj_t> cj;
+    //! The outer, unpruned j-cluster list
+    FastVector<nbnxn_cj_t> cjOuter;
+    //! The number of j-clusters that are used by ci entries in this list, will be <= cj.size()
+    int ncjInUse;
+
+    //! The total number of i clusters
+    int nci_tot;
+
+    //! Working data storage for list construction
+    std::unique_ptr<NbnxnPairlistCpuWork> work;
+
+    //! Cache protection
+    gmx_cache_protect_t cp1;
+};
+
+/* Cluster pairlist type, with extra hierarchies, for on the GPU
+ *
+ * NOTE: for better performance when combining lists over threads,
+ *       all vectors should use default initialization. But when
+ *       changing this, excl should be intialized when adding entries.
+ */
+struct NbnxnPairlistGpu
+{
+    /*! \brief Constructor
+     *
+     * \param[in] pinningPolicy  Sets the pinning policy for all buffers used on the GPU
+     */
+    NbnxnPairlistGpu(gmx::PinningPolicy pinningPolicy);
+
+    //! Cache protection
+    gmx_cache_protect_t cp0;
+
+    //! The number of atoms per i-cluster
+    int na_ci;
+    //! The number of atoms per j-cluster
+    int na_cj;
+    //! The number of atoms per super cluster
+    int na_sc;
+    //! The radius for constructing the list
+    real rlist;
+    // The i-super-cluster list, indexes into cj4;
+    gmx::HostVector<nbnxn_sci_t> sci;
+    // The list of 4*j-cluster groups
+    gmx::HostVector<nbnxn_cj4_t> cj4;
+    // Atom interaction bits (non-exclusions)
+    gmx::HostVector<nbnxn_excl_t> excl;
+    // The total number of i-clusters
+    int nci_tot;
+
+    //! Working data storage for list construction
+    std::unique_ptr<NbnxnPairlistGpuWork> work;
+
+    //! Cache protection
+    gmx_cache_protect_t cp1;
+};
+
+#endif
diff --git a/src/include/gromacs/nbnxm/pairlist_simd_2xmm.h b/src/include/gromacs/nbnxm/pairlist_simd_2xmm.h
new file mode 100644 (file)
index 0000000..82ad117
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief
+ * Declares inline-friendly code for making 2xNN pairlists
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+
+//! Stride of the packed x coordinate array
+static constexpr int c_xStride2xNN = (GMX_SIMD_REAL_WIDTH >= 2 * c_nbnxnCpuIClusterSize)
+                                             ? GMX_SIMD_REAL_WIDTH / 2
+                                             : c_nbnxnCpuIClusterSize;
+
+//! Copies PBC shifted i-cell packed atom coordinates to working array
+static inline void icell_set_x_simd_2xnn(int                   ci,
+                                         real                  shx,
+                                         real                  shy,
+                                         real                  shz,
+                                         int gmx_unused        stride,
+                                         const real*           x,
+                                         NbnxnPairlistCpuWork* work)
+{
+    real* x_ci_simd = work->iClusterData.xSimd.data();
+
+    const int ia = xIndexFromCi<NbnxnLayout::Simd2xNN>(ci);
+
+    store(x_ci_simd + 0 * GMX_SIMD_REAL_WIDTH,
+          loadU1DualHsimd(x + ia + 0 * c_xStride2xNN + 0) + SimdReal(shx));
+    store(x_ci_simd + 1 * GMX_SIMD_REAL_WIDTH,
+          loadU1DualHsimd(x + ia + 1 * c_xStride2xNN + 0) + SimdReal(shy));
+    store(x_ci_simd + 2 * GMX_SIMD_REAL_WIDTH,
+          loadU1DualHsimd(x + ia + 2 * c_xStride2xNN + 0) + SimdReal(shz));
+    store(x_ci_simd + 3 * GMX_SIMD_REAL_WIDTH,
+          loadU1DualHsimd(x + ia + 0 * c_xStride2xNN + 2) + SimdReal(shx));
+    store(x_ci_simd + 4 * GMX_SIMD_REAL_WIDTH,
+          loadU1DualHsimd(x + ia + 1 * c_xStride2xNN + 2) + SimdReal(shy));
+    store(x_ci_simd + 5 * GMX_SIMD_REAL_WIDTH,
+          loadU1DualHsimd(x + ia + 2 * c_xStride2xNN + 2) + SimdReal(shz));
+}
+
+/*! \brief SIMD code for checking and adding cluster-pairs to the list using coordinates in packed format.
+ *
+ * Checks bounding box distances and possibly atom pair distances.
+ * This is an accelerated version of make_cluster_list_simple.
+ *
+ * \param[in]     jGrid               The j-grid
+ * \param[in,out] nbl                 The pair-list to store the cluster pairs in
+ * \param[in]     icluster            The index of the i-cluster
+ * \param[in]     firstCell           The first cluster in the j-range, using i-cluster size indexing
+ * \param[in]     lastCell            The last cluster in the j-range, using i-cluster size indexing
+ * \param[in]     excludeSubDiagonal  Exclude atom pairs with i-index > j-index
+ * \param[in]     x_j                 Coordinates for the j-atom, in SIMD packed format
+ * \param[in]     rlist2              The squared list cut-off
+ * \param[in]     rbb2                The squared cut-off for putting cluster-pairs in the list based on bounding box distance only
+ * \param[in,out] numDistanceChecks   The number of distance checks performed
+ */
+static inline void makeClusterListSimd2xnn(const Grid&              jGrid,
+                                           NbnxnPairlistCpu*        nbl,
+                                           int                      icluster,
+                                           int                      firstCell,
+                                           int                      lastCell,
+                                           bool                     excludeSubDiagonal,
+                                           const real* gmx_restrict x_j,
+                                           real                     rlist2,
+                                           float                    rbb2,
+                                           int* gmx_restrict        numDistanceChecks)
+{
+    using namespace gmx;
+    const real* gmx_restrict        x_ci_simd = nbl->work->iClusterData.xSimd.data();
+    const BoundingBox* gmx_restrict bb_ci     = nbl->work->iClusterData.bb.data();
+
+    SimdReal jx_S, jy_S, jz_S;
+
+    SimdReal dx_S0, dy_S0, dz_S0;
+    SimdReal dx_S2, dy_S2, dz_S2;
+
+    SimdReal rsq_S0;
+    SimdReal rsq_S2;
+
+    SimdBool wco_S0;
+    SimdBool wco_S2;
+    SimdBool wco_any_S;
+
+    SimdReal rc2_S;
+
+    int jclusterFirst = cjFromCi<NbnxnLayout::Simd2xNN, 0>(firstCell);
+    int jclusterLast  = cjFromCi<NbnxnLayout::Simd2xNN, 1>(lastCell);
+    GMX_ASSERT(jclusterLast >= jclusterFirst,
+               "We should have a non-empty j-cluster range, since the calling code should have "
+               "ensured a non-empty cell range");
+
+    rc2_S = SimdReal(rlist2);
+
+    bool InRange = false;
+    while (!InRange && jclusterFirst <= jclusterLast)
+    {
+        const float d2 = clusterBoundingBoxDistance2(bb_ci[0], jGrid.jBoundingBoxes()[jclusterFirst]);
+        *numDistanceChecks += 2;
+
+        /* Check if the distance is within the distance where
+         * we use only the bounding box distance rbb,
+         * or within the cut-off and there is at least one atom pair
+         * within the cut-off.
+         */
+        if (d2 < rbb2)
+        {
+            InRange = true;
+        }
+        else if (d2 < rlist2)
+        {
+            const int xind_f = xIndexFromCj<NbnxnLayout::Simd2xNN>(
+                    cjFromCi<NbnxnLayout::Simd2xNN, 0>(jGrid.cellOffset()) + jclusterFirst);
+
+            jx_S = loadDuplicateHsimd(x_j + xind_f + 0 * c_xStride2xNN);
+            jy_S = loadDuplicateHsimd(x_j + xind_f + 1 * c_xStride2xNN);
+            jz_S = loadDuplicateHsimd(x_j + xind_f + 2 * c_xStride2xNN);
+
+            /* Calculate distance */
+            dx_S0 = load<SimdReal>(x_ci_simd + 0 * GMX_SIMD_REAL_WIDTH) - jx_S;
+            dy_S0 = load<SimdReal>(x_ci_simd + 1 * GMX_SIMD_REAL_WIDTH) - jy_S;
+            dz_S0 = load<SimdReal>(x_ci_simd + 2 * GMX_SIMD_REAL_WIDTH) - jz_S;
+            dx_S2 = load<SimdReal>(x_ci_simd + 3 * GMX_SIMD_REAL_WIDTH) - jx_S;
+            dy_S2 = load<SimdReal>(x_ci_simd + 4 * GMX_SIMD_REAL_WIDTH) - jy_S;
+            dz_S2 = load<SimdReal>(x_ci_simd + 5 * GMX_SIMD_REAL_WIDTH) - jz_S;
+
+            /* rsq = dx*dx+dy*dy+dz*dz */
+            rsq_S0 = norm2(dx_S0, dy_S0, dz_S0);
+            rsq_S2 = norm2(dx_S2, dy_S2, dz_S2);
+
+            wco_S0 = (rsq_S0 < rc2_S);
+            wco_S2 = (rsq_S2 < rc2_S);
+
+            wco_any_S = wco_S0 || wco_S2;
+
+            InRange = anyTrue(wco_any_S);
+
+            *numDistanceChecks += 2 * GMX_SIMD_REAL_WIDTH;
+        }
+        if (!InRange)
+        {
+            jclusterFirst++;
+        }
+    }
+    if (!InRange)
+    {
+        return;
+    }
+
+    InRange = false;
+    while (!InRange && jclusterLast > jclusterFirst)
+    {
+        const float d2 = clusterBoundingBoxDistance2(bb_ci[0], jGrid.jBoundingBoxes()[jclusterLast]);
+        *numDistanceChecks += 2;
+
+        /* Check if the distance is within the distance where
+         * we use only the bounding box distance rbb,
+         * or within the cut-off and there is at least one atom pair
+         * within the cut-off.
+         */
+        if (d2 < rbb2)
+        {
+            InRange = true;
+        }
+        else if (d2 < rlist2)
+        {
+            const int xind_l = xIndexFromCj<NbnxnLayout::Simd2xNN>(
+                    cjFromCi<NbnxnLayout::Simd2xNN, 0>(jGrid.cellOffset()) + jclusterLast);
+
+            jx_S = loadDuplicateHsimd(x_j + xind_l + 0 * c_xStride2xNN);
+            jy_S = loadDuplicateHsimd(x_j + xind_l + 1 * c_xStride2xNN);
+            jz_S = loadDuplicateHsimd(x_j + xind_l + 2 * c_xStride2xNN);
+
+            /* Calculate distance */
+            dx_S0 = load<SimdReal>(x_ci_simd + 0 * GMX_SIMD_REAL_WIDTH) - jx_S;
+            dy_S0 = load<SimdReal>(x_ci_simd + 1 * GMX_SIMD_REAL_WIDTH) - jy_S;
+            dz_S0 = load<SimdReal>(x_ci_simd + 2 * GMX_SIMD_REAL_WIDTH) - jz_S;
+            dx_S2 = load<SimdReal>(x_ci_simd + 3 * GMX_SIMD_REAL_WIDTH) - jx_S;
+            dy_S2 = load<SimdReal>(x_ci_simd + 4 * GMX_SIMD_REAL_WIDTH) - jy_S;
+            dz_S2 = load<SimdReal>(x_ci_simd + 5 * GMX_SIMD_REAL_WIDTH) - jz_S;
+
+            /* rsq = dx*dx+dy*dy+dz*dz */
+            rsq_S0 = norm2(dx_S0, dy_S0, dz_S0);
+            rsq_S2 = norm2(dx_S2, dy_S2, dz_S2);
+
+            wco_S0 = (rsq_S0 < rc2_S);
+            wco_S2 = (rsq_S2 < rc2_S);
+
+            wco_any_S = wco_S0 || wco_S2;
+
+            InRange = anyTrue(wco_any_S);
+
+            *numDistanceChecks += 2 * GMX_SIMD_REAL_WIDTH;
+        }
+        if (!InRange)
+        {
+            jclusterLast--;
+        }
+    }
+
+    if (jclusterFirst <= jclusterLast)
+    {
+        for (int jcluster = jclusterFirst; jcluster <= jclusterLast; jcluster++)
+        {
+            /* Store cj and the interaction mask */
+            nbnxn_cj_t cjEntry;
+            cjEntry.cj   = cjFromCi<NbnxnLayout::Simd2xNN, 0>(jGrid.cellOffset()) + jcluster;
+            cjEntry.excl = get_imask_simd_2xnn(excludeSubDiagonal, icluster, jcluster);
+            nbl->cj.push_back(cjEntry);
+        }
+        /* Increase the closing index in the i list */
+        nbl->ci.back().cj_ind_end = nbl->cj.size();
+    }
+}
diff --git a/src/include/gromacs/nbnxm/pairlist_simd_4xm.h b/src/include/gromacs/nbnxm/pairlist_simd_4xm.h
new file mode 100644 (file)
index 0000000..0b86343
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares inline-friendly code for making 4xN pairlists
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+//! Stride of the packed x coordinate array
+static constexpr int c_xStride4xN =
+        (GMX_SIMD_REAL_WIDTH > c_nbnxnCpuIClusterSize ? GMX_SIMD_REAL_WIDTH : c_nbnxnCpuIClusterSize);
+
+//! Copies PBC shifted i-cell packed atom coordinates to working array
+static inline void icell_set_x_simd_4xn(int                   ci,
+                                        real                  shx,
+                                        real                  shy,
+                                        real                  shz,
+                                        int gmx_unused        stride,
+                                        const real*           x,
+                                        NbnxnPairlistCpuWork* work)
+{
+    real* x_ci_simd = work->iClusterData.xSimd.data();
+
+    const int ia = xIndexFromCi<NbnxnLayout::Simd4xN>(ci);
+
+    store(x_ci_simd + 0 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 0 * c_xStride4xN] + shx));
+    store(x_ci_simd + 1 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 1 * c_xStride4xN] + shy));
+    store(x_ci_simd + 2 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 2 * c_xStride4xN] + shz));
+    store(x_ci_simd + 3 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 0 * c_xStride4xN + 1] + shx));
+    store(x_ci_simd + 4 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 1 * c_xStride4xN + 1] + shy));
+    store(x_ci_simd + 5 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 2 * c_xStride4xN + 1] + shz));
+    store(x_ci_simd + 6 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 0 * c_xStride4xN + 2] + shx));
+    store(x_ci_simd + 7 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 1 * c_xStride4xN + 2] + shy));
+    store(x_ci_simd + 8 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 2 * c_xStride4xN + 2] + shz));
+    store(x_ci_simd + 9 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 0 * c_xStride4xN + 3] + shx));
+    store(x_ci_simd + 10 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 1 * c_xStride4xN + 3] + shy));
+    store(x_ci_simd + 11 * GMX_SIMD_REAL_WIDTH, SimdReal(x[ia + 2 * c_xStride4xN + 3] + shz));
+}
+
+/*! \brief SIMD code for checking and adding cluster-pairs to the list using coordinates in packed format.
+ *
+ * Checks bounding box distances and possibly atom pair distances.
+ * This is an accelerated version of make_cluster_list_simple.
+ *
+ * \param[in]     jGrid               The j-grid
+ * \param[in,out] nbl                 The pair-list to store the cluster pairs in
+ * \param[in]     icluster            The index of the i-cluster
+ * \param[in]     firstCell           The first cluster in the j-range, using i-cluster size indexing
+ * \param[in]     lastCell            The last cluster in the j-range, using i-cluster size indexing
+ * \param[in]     excludeSubDiagonal  Exclude atom pairs with i-index > j-index
+ * \param[in]     x_j                 Coordinates for the j-atom, in SIMD packed format
+ * \param[in]     rlist2              The squared list cut-off
+ * \param[in]     rbb2                The squared cut-off for putting cluster-pairs in the list based on bounding box distance only
+ * \param[in,out] numDistanceChecks   The number of distance checks performed
+ */
+static inline void makeClusterListSimd4xn(const Grid&              jGrid,
+                                          NbnxnPairlistCpu*        nbl,
+                                          int                      icluster,
+                                          int                      firstCell,
+                                          int                      lastCell,
+                                          bool                     excludeSubDiagonal,
+                                          const real* gmx_restrict x_j,
+                                          real                     rlist2,
+                                          float                    rbb2,
+                                          int* gmx_restrict        numDistanceChecks)
+{
+    using namespace gmx;
+    const real* gmx_restrict        x_ci_simd = nbl->work->iClusterData.xSimd.data();
+    const BoundingBox* gmx_restrict bb_ci     = nbl->work->iClusterData.bb.data();
+
+    SimdReal jx_S, jy_S, jz_S;
+
+    SimdReal dx_S0, dy_S0, dz_S0;
+    SimdReal dx_S1, dy_S1, dz_S1;
+    SimdReal dx_S2, dy_S2, dz_S2;
+    SimdReal dx_S3, dy_S3, dz_S3;
+
+    SimdReal rsq_S0;
+    SimdReal rsq_S1;
+    SimdReal rsq_S2;
+    SimdReal rsq_S3;
+
+    SimdBool wco_S0;
+    SimdBool wco_S1;
+    SimdBool wco_S2;
+    SimdBool wco_S3;
+    SimdBool wco_any_S01, wco_any_S23, wco_any_S;
+
+    SimdReal rc2_S;
+
+    /* Convert the j-range from i-cluster size indexing to j-cluster indexing */
+    int jclusterFirst = cjFromCi<NbnxnLayout::Simd4xN, 0>(firstCell);
+    int jclusterLast  = cjFromCi<NbnxnLayout::Simd4xN, 1>(lastCell);
+    GMX_ASSERT(jclusterLast >= jclusterFirst,
+               "We should have a non-empty j-cluster range, since the calling code should have "
+               "ensured a non-empty cell range");
+
+    rc2_S = SimdReal(rlist2);
+
+    bool InRange = false;
+    while (!InRange && jclusterFirst <= jclusterLast)
+    {
+        const float d2 = clusterBoundingBoxDistance2(bb_ci[0], jGrid.jBoundingBoxes()[jclusterFirst]);
+        *numDistanceChecks += 2;
+
+        /* Check if the distance is within the distance where
+         * we use only the bounding box distance rbb,
+         * or within the cut-off and there is at least one atom pair
+         * within the cut-off.
+         */
+        if (d2 < rbb2)
+        {
+            InRange = true;
+        }
+        else if (d2 < rlist2)
+        {
+            const int xind_f = xIndexFromCj<NbnxnLayout::Simd4xN>(
+                    cjFromCi<NbnxnLayout::Simd4xN, 0>(jGrid.cellOffset()) + jclusterFirst);
+
+            jx_S = load<SimdReal>(x_j + xind_f + 0 * c_xStride4xN);
+            jy_S = load<SimdReal>(x_j + xind_f + 1 * c_xStride4xN);
+            jz_S = load<SimdReal>(x_j + xind_f + 2 * c_xStride4xN);
+
+
+            /* Calculate distance */
+            dx_S0 = load<SimdReal>(x_ci_simd + 0 * GMX_SIMD_REAL_WIDTH) - jx_S;
+            dy_S0 = load<SimdReal>(x_ci_simd + 1 * GMX_SIMD_REAL_WIDTH) - jy_S;
+            dz_S0 = load<SimdReal>(x_ci_simd + 2 * GMX_SIMD_REAL_WIDTH) - jz_S;
+            dx_S1 = load<SimdReal>(x_ci_simd + 3 * GMX_SIMD_REAL_WIDTH) - jx_S;
+            dy_S1 = load<SimdReal>(x_ci_simd + 4 * GMX_SIMD_REAL_WIDTH) - jy_S;
+            dz_S1 = load<SimdReal>(x_ci_simd + 5 * GMX_SIMD_REAL_WIDTH) - jz_S;
+            dx_S2 = load<SimdReal>(x_ci_simd + 6 * GMX_SIMD_REAL_WIDTH) - jx_S;
+            dy_S2 = load<SimdReal>(x_ci_simd + 7 * GMX_SIMD_REAL_WIDTH) - jy_S;
+            dz_S2 = load<SimdReal>(x_ci_simd + 8 * GMX_SIMD_REAL_WIDTH) - jz_S;
+            dx_S3 = load<SimdReal>(x_ci_simd + 9 * GMX_SIMD_REAL_WIDTH) - jx_S;
+            dy_S3 = load<SimdReal>(x_ci_simd + 10 * GMX_SIMD_REAL_WIDTH) - jy_S;
+            dz_S3 = load<SimdReal>(x_ci_simd + 11 * GMX_SIMD_REAL_WIDTH) - jz_S;
+
+            /* rsq = dx*dx+dy*dy+dz*dz */
+            rsq_S0 = norm2(dx_S0, dy_S0, dz_S0);
+            rsq_S1 = norm2(dx_S1, dy_S1, dz_S1);
+            rsq_S2 = norm2(dx_S2, dy_S2, dz_S2);
+            rsq_S3 = norm2(dx_S3, dy_S3, dz_S3);
+
+            wco_S0 = (rsq_S0 < rc2_S);
+            wco_S1 = (rsq_S1 < rc2_S);
+            wco_S2 = (rsq_S2 < rc2_S);
+            wco_S3 = (rsq_S3 < rc2_S);
+
+            wco_any_S01 = wco_S0 || wco_S1;
+            wco_any_S23 = wco_S2 || wco_S3;
+            wco_any_S   = wco_any_S01 || wco_any_S23;
+
+            InRange = anyTrue(wco_any_S);
+
+            *numDistanceChecks += 4 * GMX_SIMD_REAL_WIDTH;
+        }
+        if (!InRange)
+        {
+            jclusterFirst++;
+        }
+    }
+    if (!InRange)
+    {
+        return;
+    }
+
+    InRange = false;
+    while (!InRange && jclusterLast > jclusterFirst)
+    {
+        const float d2 = clusterBoundingBoxDistance2(bb_ci[0], jGrid.jBoundingBoxes()[jclusterLast]);
+        *numDistanceChecks += 2;
+
+        /* Check if the distance is within the distance where
+         * we use only the bounding box distance rbb,
+         * or within the cut-off and there is at least one atom pair
+         * within the cut-off.
+         */
+        if (d2 < rbb2)
+        {
+            InRange = true;
+        }
+        else if (d2 < rlist2)
+        {
+            const int xind_l = xIndexFromCj<NbnxnLayout::Simd4xN>(
+                    cjFromCi<NbnxnLayout::Simd4xN, 0>(jGrid.cellOffset()) + jclusterLast);
+
+            jx_S = load<SimdReal>(x_j + xind_l + 0 * c_xStride4xN);
+            jy_S = load<SimdReal>(x_j + xind_l + 1 * c_xStride4xN);
+            jz_S = load<SimdReal>(x_j + xind_l + 2 * c_xStride4xN);
+
+            /* Calculate distance */
+            dx_S0 = load<SimdReal>(x_ci_simd + 0 * GMX_SIMD_REAL_WIDTH) - jx_S;
+            dy_S0 = load<SimdReal>(x_ci_simd + 1 * GMX_SIMD_REAL_WIDTH) - jy_S;
+            dz_S0 = load<SimdReal>(x_ci_simd + 2 * GMX_SIMD_REAL_WIDTH) - jz_S;
+            dx_S1 = load<SimdReal>(x_ci_simd + 3 * GMX_SIMD_REAL_WIDTH) - jx_S;
+            dy_S1 = load<SimdReal>(x_ci_simd + 4 * GMX_SIMD_REAL_WIDTH) - jy_S;
+            dz_S1 = load<SimdReal>(x_ci_simd + 5 * GMX_SIMD_REAL_WIDTH) - jz_S;
+            dx_S2 = load<SimdReal>(x_ci_simd + 6 * GMX_SIMD_REAL_WIDTH) - jx_S;
+            dy_S2 = load<SimdReal>(x_ci_simd + 7 * GMX_SIMD_REAL_WIDTH) - jy_S;
+            dz_S2 = load<SimdReal>(x_ci_simd + 8 * GMX_SIMD_REAL_WIDTH) - jz_S;
+            dx_S3 = load<SimdReal>(x_ci_simd + 9 * GMX_SIMD_REAL_WIDTH) - jx_S;
+            dy_S3 = load<SimdReal>(x_ci_simd + 10 * GMX_SIMD_REAL_WIDTH) - jy_S;
+            dz_S3 = load<SimdReal>(x_ci_simd + 11 * GMX_SIMD_REAL_WIDTH) - jz_S;
+
+            /* rsq = dx*dx+dy*dy+dz*dz */
+            rsq_S0 = norm2(dx_S0, dy_S0, dz_S0);
+            rsq_S1 = norm2(dx_S1, dy_S1, dz_S1);
+            rsq_S2 = norm2(dx_S2, dy_S2, dz_S2);
+            rsq_S3 = norm2(dx_S3, dy_S3, dz_S3);
+
+            wco_S0 = (rsq_S0 < rc2_S);
+            wco_S1 = (rsq_S1 < rc2_S);
+            wco_S2 = (rsq_S2 < rc2_S);
+            wco_S3 = (rsq_S3 < rc2_S);
+
+            wco_any_S01 = wco_S0 || wco_S1;
+            wco_any_S23 = wco_S2 || wco_S3;
+            wco_any_S   = wco_any_S01 || wco_any_S23;
+
+            InRange = anyTrue(wco_any_S);
+
+            *numDistanceChecks += 4 * GMX_SIMD_REAL_WIDTH;
+        }
+        if (!InRange)
+        {
+            jclusterLast--;
+        }
+    }
+
+    if (jclusterFirst <= jclusterLast)
+    {
+        for (int jcluster = jclusterFirst; jcluster <= jclusterLast; jcluster++)
+        {
+            /* Store cj and the interaction mask */
+            nbnxn_cj_t cjEntry;
+            cjEntry.cj   = cjFromCi<NbnxnLayout::Simd4xN, 0>(jGrid.cellOffset()) + jcluster;
+            cjEntry.excl = get_imask_simd_4xn(excludeSubDiagonal, icluster, jcluster);
+            nbl->cj.push_back(cjEntry);
+        }
+        /* Increase the closing index in the i list */
+        nbl->ci.back().cj_ind_end = nbl->cj.size();
+    }
+}
diff --git a/src/include/gromacs/nbnxm/pairlist_tuning.h b/src/include/gromacs/nbnxm/pairlist_tuning.h
new file mode 100644 (file)
index 0000000..4e9a66d
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief Declares functions for tuning adjustable parameters for the nbnxn non-bonded search and interaction kernels
+ *
+ * \author Berk Hess <hess@kth.se>
+ *
+ * \inlibraryapi
+ * \ingroup module_nbnxm
+ */
+
+#ifndef NBNXM_PAIRLIST_TUNING_H
+#define NBNXM_PAIRLIST_TUNING_H
+
+#include <stdio.h>
+
+#include "gromacs/math/vectypes.h"
+
+namespace gmx
+{
+class CpuInfo;
+class MDLogger;
+} // namespace gmx
+
+struct gmx_mtop_t;
+struct interaction_const_t;
+struct PairlistParams;
+struct t_commrec;
+struct t_inputrec;
+
+/*! \brief Try to increase nstlist when using the Verlet cut-off scheme
+ *
+ * \param[in,out] fplog    Log file
+ * \param[in]     cr       The communication record
+ * \param[in]     ir       The input parameter record
+ * \param[in]     nstlistOnCmdline  The value of nstlist provided on the command line
+ * \param[in]     mtop     The global topology
+ * \param[in]     box      The unit cell
+ * \param[in]     useOrEmulateGpuForNonbondeds  Tells if we are using a GPU for non-bondeds
+ * \param[in]     cpuinfo  Information about the CPU(s)
+ */
+void increaseNstlist(FILE*               fplog,
+                     t_commrec*          cr,
+                     t_inputrec*         ir,
+                     int                 nstlistOnCmdline,
+                     const gmx_mtop_t*   mtop,
+                     const matrix        box,
+                     bool                useOrEmulateGpuForNonbondeds,
+                     const gmx::CpuInfo& cpuinfo);
+
+/*! \brief Set up the dynamic pairlist pruning
+ *
+ * \param[in,out] mdlog            MD logger
+ * \param[in]     inputrec         The input parameter record
+ * \param[in]     mtop             The global topology
+ * \param[in]     box              The unit cell
+ * \param[in]     interactionConst The nonbonded interactions constants
+ * \param[in,out] listParams       The list setup parameters
+ */
+void setupDynamicPairlistPruning(const gmx::MDLogger&       mdlog,
+                                 const t_inputrec&          inputrec,
+                                 const gmx_mtop_t&          mtop,
+                                 matrix                     box,
+                                 const interaction_const_t& interactionConst,
+                                 PairlistParams*            listParams);
+
+#endif /* NBNXM_PAIRLIST_TUNING_H */
diff --git a/src/include/gromacs/nbnxm/pairlistparams.h b/src/include/gromacs/nbnxm/pairlistparams.h
new file mode 100644 (file)
index 0000000..e1df6bb
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the PairlistType enum and PairlistParams class
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_PAIRLISTPARAMS_H
+#define GMX_NBNXM_PAIRLISTPARAMS_H
+
+#include "config.h"
+
+#include "gromacs/mdtypes/locality.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/real.h"
+
+namespace Nbnxm
+{
+enum class KernelType;
+}
+
+//! The i-cluster size for CPU kernels, always 4 atoms
+static constexpr int c_nbnxnCpuIClusterSize = 4;
+
+//! The i- and j-cluster size for GPU lists, 8 atoms for CUDA, set at configure time for OpenCL and SYCL
+#if GMX_GPU_OPENCL || GMX_GPU_SYCL
+static constexpr int c_nbnxnGpuClusterSize = GMX_GPU_NB_CLUSTER_SIZE;
+#else
+static constexpr int c_nbnxnGpuClusterSize = 8;
+#endif
+
+//! The number of clusters along Z in a pair-search grid cell for GPU lists
+static constexpr int c_gpuNumClusterPerCellZ = 2;
+//! The number of clusters along Y in a pair-search grid cell for GPU lists
+static constexpr int c_gpuNumClusterPerCellY = 2;
+//! The number of clusters along X in a pair-search grid cell for GPU lists
+static constexpr int c_gpuNumClusterPerCellX = 2;
+//! The number of clusters in a pair-search grid cell for GPU lists
+static constexpr int c_gpuNumClusterPerCell =
+        c_gpuNumClusterPerCellZ * c_gpuNumClusterPerCellY * c_gpuNumClusterPerCellX;
+
+
+/*! \brief The number of sub-parts used for data storage for a GPU cluster pair
+ *
+ * In CUDA the number of threads in a warp is 32 and we have cluster pairs
+ * of 8*8=64 atoms, so it's convenient to store data for cluster pair halves.
+ */
+static constexpr int c_nbnxnGpuClusterpairSplit = 2;
+
+//! The fixed size of the exclusion mask array for a half GPU cluster pair
+static constexpr int c_nbnxnGpuExclSize =
+        c_nbnxnGpuClusterSize * c_nbnxnGpuClusterSize / c_nbnxnGpuClusterpairSplit;
+
+//! The available pair list types
+enum class PairlistType : int
+{
+    Simple4x2,
+    Simple4x4,
+    Simple4x8,
+    HierarchicalNxN,
+    Count
+};
+
+//! Gives the i-cluster size for each pairlist type
+static constexpr gmx::EnumerationArray<PairlistType, int> IClusterSizePerListType = {
+    { c_nbnxnCpuIClusterSize, c_nbnxnCpuIClusterSize, c_nbnxnCpuIClusterSize, c_nbnxnGpuClusterSize }
+};
+//! Gives the j-cluster size for each pairlist type
+static constexpr gmx::EnumerationArray<PairlistType, int> JClusterSizePerListType = {
+    { 2, 4, 8, c_nbnxnGpuClusterSize }
+};
+//! True if given pairlist type is used on GPU, false if on CPU.
+static constexpr gmx::EnumerationArray<PairlistType, bool> sc_isGpuPairListType = {
+    { false, false, false, true }
+};
+
+/*! \internal
+ * \brief The setup for generating and pruning the nbnxn pair list.
+ *
+ * Without dynamic pruning rlistOuter=rlistInner.
+ */
+struct PairlistParams
+{
+    /*! \brief Constructor producing a struct with dynamic pruning disabled
+     */
+    PairlistParams(Nbnxm::KernelType kernelType, bool haveFep, real rlist, bool haveMultipleDomains);
+
+    //! The type of cluster-pair list
+    PairlistType pairlistType;
+    //! Tells whether we have perturbed interactions
+    bool haveFep;
+    //! Cut-off of the larger, outer pair-list
+    real rlistOuter;
+    //! Cut-off of the smaller, inner pair-list
+    real rlistInner;
+    //! True when using DD with multiple domains
+    bool haveMultipleDomains;
+    //! Are we using dynamic pair-list pruning
+    bool useDynamicPruning;
+    //! The interval in steps for computing non-bonded interactions, =1 without MTS
+    int mtsFactor;
+    //! Pair-list dynamic pruning interval
+    int nstlistPrune;
+    //! The number parts to divide the pair-list into for rolling pruning, a value of 1 gives no rolling pruning
+    int numRollingPruningParts;
+    //! Lifetime in steps of the pair-list
+    int lifetime;
+};
+
+#endif
diff --git a/src/include/gromacs/nbnxm/pairlistset.h b/src/include/gromacs/nbnxm/pairlistset.h
new file mode 100644 (file)
index 0000000..c88a907
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the PairlistSet class
+ *
+ * There is one PairlistSet object per locality. A PairlistSet
+ * holds a list of CPU- or GPU-type pairlist objects, one for each thread,
+ * as well as helper objects to construct each of those pairlists.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_PAIRLISTSET_H
+#define GMX_NBNXM_PAIRLISTSET_H
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/locality.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+#include "pairlist.h"
+
+struct nbnxn_atomdata_t;
+struct PairlistParams;
+struct PairsearchWork;
+struct SearchCycleCounting;
+struct t_nrnb;
+
+namespace gmx
+{
+template<typename>
+class ListOfLists;
+}
+
+namespace Nbnxm
+{
+class GridSet;
+}
+
+/*! \internal
+ * \brief An object that holds the local or non-local pairlists
+ */
+class PairlistSet
+{
+public:
+    //! Constructor: initializes the pairlist set as empty
+    PairlistSet(const PairlistParams& listParams);
+
+    ~PairlistSet();
+
+    //! Constructs the pairlists in the set using the coordinates in \p nbat
+    void constructPairlists(gmx::InteractionLocality      locality,
+                            const Nbnxm::GridSet&         gridSet,
+                            gmx::ArrayRef<PairsearchWork> searchWork,
+                            nbnxn_atomdata_t*             nbat,
+                            const gmx::ListOfLists<int>&  exclusions,
+                            int                           minimumIlistCountForGpuBalancing,
+                            t_nrnb*                       nrnb,
+                            SearchCycleCounting*          searchCycleCounting);
+
+    //! Dispatch the kernel for dynamic pairlist pruning
+    void dispatchPruneKernel(const nbnxn_atomdata_t* nbat, gmx::ArrayRef<const gmx::RVec> shift_vec);
+
+    //! Returns the lists of CPU pairlists
+    gmx::ArrayRef<const NbnxnPairlistCpu> cpuLists() const { return cpuLists_; }
+
+    //! Returns a pointer to the GPU pairlist, nullptr when not present
+    const NbnxnPairlistGpu* gpuList() const
+    {
+        if (!gpuLists_.empty())
+        {
+            return &gpuLists_[0];
+        }
+        else
+        {
+            return nullptr;
+        }
+    }
+
+    //! Returns the lists of free-energy pairlists, empty when nonbonded interactions are not perturbed
+    gmx::ArrayRef<const std::unique_ptr<t_nblist>> fepLists() const { return fepLists_; }
+
+private:
+    //! List of pairlists in CPU layout
+    std::vector<NbnxnPairlistCpu> cpuLists_;
+    //! List of working list for rebalancing CPU lists
+    std::vector<NbnxnPairlistCpu> cpuListsWork_;
+    //! List of pairlists in GPU layout
+    std::vector<NbnxnPairlistGpu> gpuLists_;
+    //! Pairlist parameters describing setup and ranges
+    const PairlistParams& params_;
+    //! Tells whether multiple lists get merged into one (the first) after creation
+    bool combineLists_;
+    //! Tells whether the lists is of CPU type, otherwise GPU type
+    gmx_bool isCpuType_;
+    //! Lists for perturbed interactions in simple atom-atom layout
+    std::vector<std::unique_ptr<t_nblist>> fepLists_;
+
+public:
+    /* Pair counts for flop counting */
+    //! Total number of atom pairs for LJ+Q kernel
+    int natpair_ljq_;
+    //! Total number of atom pairs for LJ kernel
+    int natpair_lj_;
+    //! Total number of atom pairs for Q kernel
+    int natpair_q_;
+};
+
+#endif
diff --git a/src/include/gromacs/nbnxm/pairlistsets.h b/src/include/gromacs/nbnxm/pairlistsets.h
new file mode 100644 (file)
index 0000000..8bbb92d
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief
+ * Declares the PairlistSets class
+ *
+ * This class holds the local and non-local pairlist sets.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_PAIRLISTSETS_H
+#define GMX_NBNXM_PAIRLISTSETS_H
+
+#include <memory>
+
+#include "gromacs/mdtypes/locality.h"
+
+#include "pairlistparams.h"
+
+struct nbnxn_atomdata_t;
+class PairlistSet;
+enum class PairlistType;
+class PairSearch;
+struct t_nrnb;
+
+namespace gmx
+{
+template<typename>
+class ListOfLists;
+}
+
+//! Contains sets of pairlists \internal
+class PairlistSets
+{
+public:
+    //! Constructor
+    PairlistSets(const PairlistParams& pairlistParams,
+                 bool                  haveMultipleDomains,
+                 int                   minimumIlistCountForGpuBalancing);
+
+    //! Construct the pairlist set for the given locality
+    void construct(gmx::InteractionLocality     iLocality,
+                   PairSearch*                  pairSearch,
+                   nbnxn_atomdata_t*            nbat,
+                   const gmx::ListOfLists<int>& exclusions,
+                   int64_t                      step,
+                   t_nrnb*                      nrnb);
+
+    //! Dispatches the dynamic pruning kernel for the given locality
+    void dispatchPruneKernel(gmx::InteractionLocality       iLocality,
+                             const nbnxn_atomdata_t*        nbat,
+                             gmx::ArrayRef<const gmx::RVec> shift_vec);
+
+    //! Returns the pair list parameters
+    const PairlistParams& params() const { return params_; }
+
+    //! Returns the number of steps performed with the current pair list
+    int numStepsWithPairlist(int64_t step) const
+    {
+        return static_cast<int>(step - outerListCreationStep_);
+    }
+
+    //! Returns whether step is a dynamic list pruning step, for CPU lists
+    bool isDynamicPruningStepCpu(int64_t step) const
+    {
+        return (params_.useDynamicPruning && numStepsWithPairlist(step) % params_.nstlistPrune == 0);
+    }
+
+    //! Returns whether step is a dynamic list pruning step, for GPU lists
+    bool isDynamicPruningStepGpu(int64_t step) const
+    {
+        const int age = numStepsWithPairlist(step);
+
+        return (params_.useDynamicPruning && age > 0 && age < params_.lifetime
+                && step % params_.mtsFactor == 0
+                && (params_.haveMultipleDomains || age % (2 * params_.mtsFactor) == 0));
+    }
+
+    //! Changes the pair-list outer and inner radius
+    void changePairlistRadii(real rlistOuter, real rlistInner)
+    {
+        params_.rlistOuter = rlistOuter;
+        params_.rlistInner = rlistInner;
+    }
+
+    //! Returns the pair-list set for the given locality
+    const PairlistSet& pairlistSet(gmx::InteractionLocality iLocality) const
+    {
+        if (iLocality == gmx::InteractionLocality::Local)
+        {
+            return *localSet_;
+        }
+        else
+        {
+            GMX_ASSERT(nonlocalSet_, "Need a non-local set when requesting access");
+            return *nonlocalSet_;
+        }
+    }
+
+private:
+    //! Returns the pair-list set for the given locality
+    PairlistSet& pairlistSet(gmx::InteractionLocality iLocality)
+    {
+        if (iLocality == gmx::InteractionLocality::Local)
+        {
+            return *localSet_;
+        }
+        else
+        {
+            GMX_ASSERT(nonlocalSet_, "Need a non-local set when requesting access");
+            return *nonlocalSet_;
+        }
+    }
+
+    //! Parameters for the search and list pruning setup
+    PairlistParams params_;
+    //! Pair list balancing parameter for use with GPU
+    int minimumIlistCountForGpuBalancing_;
+    //! Local pairlist set
+    std::unique_ptr<PairlistSet> localSet_;
+    //! Non-local pairlist set
+    std::unique_ptr<PairlistSet> nonlocalSet_;
+    //! MD step at with the outer lists in pairlistSets_ were created
+    int64_t outerListCreationStep_;
+};
+
+#endif
diff --git a/src/include/gromacs/nbnxm/pairlistwork.h b/src/include/gromacs/nbnxm/pairlistwork.h
new file mode 100644 (file)
index 0000000..7f3d8ee
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief Declares working data structures for the CPU and GPU pairlists
+ *
+ * \author Berk Hess <hess@kth.se>
+ *
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_PAIRLISTWORK_H
+#define GMX_NBNXM_PAIRLISTWORK_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/simd/simd.h"
+
+#include "boundingboxes.h"
+#include "grid.h"
+#include "pairlist.h"
+
+//! Working data for the actual i-supercell during pair search \internal
+struct NbnxnPairlistCpuWork
+{
+    //! Struct for storing coordinates and bounding box for an i-entry during search \internal
+    struct IClusterData
+    {
+        IClusterData() :
+            bb(1),
+            x(c_nbnxnCpuIClusterSize * DIM),
+            xSimd(c_nbnxnCpuIClusterSize * DIM * GMX_REAL_MAX_SIMD_WIDTH)
+        {
+        }
+
+        //! The bounding boxes, pbc shifted, for each cluster
+        AlignedVector<Nbnxm::BoundingBox> bb;
+        //! The coordinates, pbc shifted, for each atom
+        std::vector<real> x;
+        //! Aligned list for storing 4*DIM*GMX_SIMD_REAL_WIDTH reals
+        AlignedVector<real> xSimd;
+    };
+
+    //! Protect data from cache pollution between threads
+    gmx_cache_protect_t cp0;
+
+    //! Work data for generating an IEntry in the pairlist
+    IClusterData iClusterData;
+    //! The current cj_ind index for the current list
+    int cj_ind;
+    //! Temporary j-cluster list, used for sorting on exclusions
+    std::vector<nbnxn_cj_t> cj;
+
+    //! Nr. of cluster pairs without Coulomb for flop counting
+    int ncj_noq;
+    //! Nr. of cluster pairs with 1/2 LJ for flop count
+    int ncj_hlj;
+
+    //! Protect data from cache pollution between threads
+    gmx_cache_protect_t cp1;
+};
+
+/* Working data for the actual i-supercell during pair search */
+struct NbnxnPairlistGpuWork
+{
+    struct ISuperClusterData
+    {
+        ISuperClusterData() :
+            bb(c_gpuNumClusterPerCell),
+#if NBNXN_SEARCH_BB_SIMD4
+            bbPacked(c_gpuNumClusterPerCell / c_packedBoundingBoxesDimSize * c_packedBoundingBoxesSize),
+#endif
+            x(c_gpuNumClusterPerCell * c_nbnxnGpuClusterSize * DIM),
+            xSimd(c_gpuNumClusterPerCell * c_nbnxnGpuClusterSize * DIM)
+        {
+        }
+
+        //! The bounding boxes, pbc shifted, for each cluster
+        AlignedVector<Nbnxm::BoundingBox> bb;
+        //! As bb, but in packed xxxx format
+        AlignedVector<float> bbPacked;
+        //! The coordinates, pbc shifted, for each atom
+        AlignedVector<real> x;
+        //! Aligned coordinate list used for 4*DIM*GMX_SIMD_REAL_WIDTH floats
+        AlignedVector<real> xSimd;
+    };
+
+    NbnxnPairlistGpuWork() :
+        distanceBuffer(c_gpuNumClusterPerCell), sci_sort({}, { gmx::PinningPolicy::PinnedIfSupported })
+    {
+    }
+
+    //! Protect data from cache pollution between threads
+    gmx_cache_protect_t cp0;
+
+    //! Work data for generating an i-entry in the pairlist
+    ISuperClusterData iSuperClusterData;
+    //! The current j-cluster index for the current list
+    int cj_ind;
+    //! Bounding box distance work array
+    AlignedVector<float> distanceBuffer;
+
+    //! Buffer for sorting list entries
+    std::vector<int> sortBuffer;
+
+    //! Second sci array, for sorting
+    gmx::HostVector<nbnxn_sci_t> sci_sort;
+
+    //! Protect data from cache pollution between threads
+    gmx_cache_protect_t cp1;
+};
+
+#endif
diff --git a/src/include/gromacs/nbnxm/pairsearch.h b/src/include/gromacs/nbnxm/pairsearch.h
new file mode 100644 (file)
index 0000000..1bb1898
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ * \brief Declares the PairSearch class and helper structs
+ *
+ * The PairSearch class holds the domain setup, the search grids
+ * and helper object for the pair search. It manages the search work.
+ * The actual gridding and pairlist generation is performed by the
+ * GridSet/Grid and PairlistSet/Pairlist classes, respectively.
+ *
+ * \author Berk Hess <hess@kth.se>
+ *
+ * \ingroup module_nbnxm
+ */
+
+#ifndef GMX_NBNXM_PAIRSEARCH_H
+#define GMX_NBNXM_PAIRSEARCH_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/nbnxm/atomdata.h"
+#include "gromacs/timing/cyclecounter.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+#include "gridset.h"
+#include "pairlist.h"
+
+struct gmx_domdec_zones_t;
+struct PairsearchWork;
+
+
+/*! \brief Convenience declaration for an std::vector with aligned memory */
+template<class T>
+using AlignedVector = std::vector<T, gmx::AlignedAllocator<T>>;
+
+
+//! Local cycle count struct for profiling \internal
+class nbnxn_cycle_t
+{
+public:
+    //! Start counting cycles
+    void start() { start_ = gmx_cycles_read(); }
+    //! Stop counting cycles
+    void stop()
+    {
+        cycles_ += gmx_cycles_read() - start_;
+        count_++;
+    }
+    //! Return the number of periods of cycle counting
+    int count() const { return count_; }
+
+    //! Return the average number of million cycles per counting period
+    double averageMCycles() const
+    {
+        if (count_ > 0)
+        {
+            return static_cast<double>(cycles_) * 1e-6 / count_;
+        }
+        else
+        {
+            return 0;
+        }
+    }
+
+private:
+    //! Number of counting periods
+    int count_ = 0;
+    //! Total cycles in all counting periods
+    gmx_cycles_t cycles_ = 0;
+    //! Cycle count at the most recent start
+    gmx_cycles_t start_ = 0;
+};
+
+//! Local cycle count enum for profiling different parts of search
+enum
+{
+    enbsCCgrid,
+    enbsCCsearch,
+    enbsCCcombine,
+    enbsCCnr
+};
+
+/*! \internal
+ * \brief Struct for collecting detailed cycle counts for the search
+ */
+struct SearchCycleCounting
+{
+    //! Start a pair search cycle counter
+    void start(const int enbsCC) { cc_[enbsCC].start(); }
+
+    //! Stop a pair search cycle counter
+    void stop(const int enbsCC) { cc_[enbsCC].stop(); }
+
+    //! Print the cycle counts to \p fp
+    void printCycles(FILE* fp, gmx::ArrayRef<const PairsearchWork> work) const;
+
+    //! Tells whether we record cycles
+    bool recordCycles_ = false;
+    //! The number of times pairsearching has been performed, local+non-local count as 1
+    int searchCount_ = 0;
+    //! The set of cycle counters
+    nbnxn_cycle_t cc_[enbsCCnr];
+};
+
+// TODO: Move nbnxn_search_work_t definition to its own file
+
+//! Thread-local work struct, contains working data for Grid \internal
+struct PairsearchWork
+{
+    PairsearchWork();
+
+    ~PairsearchWork();
+
+    //! Buffer to avoid cache pollution
+    gmx_cache_protect_t cp0;
+
+    //! Temporary buffer for sorting atoms within a grid column
+    std::vector<int> sortBuffer;
+
+    //! Flags for force buffer access
+    std::vector<gmx_bitmask_t> buffer_flags;
+
+    //! Number of distance checks for flop counting
+    int ndistc;
+
+
+    //! Temporary FEP list for load balancing
+    std::unique_ptr<t_nblist> nbl_fep;
+
+    //! Counter for thread-local cycles
+    nbnxn_cycle_t cycleCounter;
+
+    //! Buffer to avoid cache polution
+    gmx_cache_protect_t cp1;
+};
+
+//! Main pair-search struct, contains the grid(s), not the pair-list(s) \internal
+class PairSearch
+{
+public:
+    //! Puts the atoms in \p ddZone on the grid and copies the coordinates to \p nbat
+    void putOnGrid(const matrix                   box,
+                   int                            ddZone,
+                   const rvec                     lowerCorner,
+                   const rvec                     upperCorner,
+                   const gmx::UpdateGroupsCog*    updateGroupsCog,
+                   gmx::Range<int>                atomRange,
+                   real                           atomDensity,
+                   gmx::ArrayRef<const int64_t>   atomInfo,
+                   gmx::ArrayRef<const gmx::RVec> x,
+                   int                            numAtomsMoved,
+                   const int*                     move,
+                   nbnxn_atomdata_t*              nbat)
+    {
+        cycleCounting_.start(enbsCCgrid);
+
+        gridSet_.putOnGrid(box,
+                           ddZone,
+                           lowerCorner,
+                           upperCorner,
+                           updateGroupsCog,
+                           atomRange,
+                           atomDensity,
+                           atomInfo,
+                           x,
+                           numAtomsMoved,
+                           move,
+                           nbat);
+
+        cycleCounting_.stop(enbsCCgrid);
+    }
+
+    /*! \brief Constructor
+     *
+     * \param[in] pbcType                  The periodic boundary conditions
+     * \param[in] doTestParticleInsertion  Whether test-particle insertion is active
+     * \param[in] numDDCells               The number of domain decomposition cells per dimension, without DD nullptr should be passed
+     * \param[in] zones                    The domain decomposition zone setup, without DD nullptr should be passed
+     * \param[in] pairlistType             The type of tte pair list
+     * \param[in] haveFep                  Tells whether non-bonded interactions are perturbed
+     * \param[in] maxNumThreads            The maximum number of threads used in the search
+     * \param[in] pinningPolicy            Sets the pinning policy for all buffers used on the GPU
+     */
+    PairSearch(PbcType                   pbcType,
+               bool                      doTestParticleInsertion,
+               const ivec*               numDDCells,
+               const gmx_domdec_zones_t* zones,
+               PairlistType              pairlistType,
+               bool                      haveFep,
+               int                       maxNumThreads,
+               gmx::PinningPolicy        pinningPolicy);
+
+    //! Sets the order of the local atoms to the order grid atom ordering
+    void setLocalAtomOrder() { gridSet_.setLocalAtomOrder(); }
+
+    //! Returns the set of search grids
+    const Nbnxm::GridSet& gridSet() const { return gridSet_; }
+
+    //! Returns the list of thread-local work objects
+    gmx::ArrayRef<const PairsearchWork> work() const { return work_; }
+
+    //! Returns the list of thread-local work objects
+    gmx::ArrayRef<PairsearchWork> work() { return work_; }
+
+private:
+    //! The set of search grids
+    Nbnxm::GridSet gridSet_;
+    //! Work objects, one entry for each thread
+    std::vector<PairsearchWork> work_;
+
+public:
+    //! Cycle counting for measuring components of the search
+    SearchCycleCounting cycleCounting_;
+};
+
+#endif
diff --git a/src/include/gromacs/nbnxm/sycl/nbnxm_sycl_kernel.h b/src/include/gromacs/nbnxm/sycl/nbnxm_sycl_kernel.h
new file mode 100644 (file)
index 0000000..03fc1b6
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares nbnxn sycl helper functions
+ *
+ * \ingroup module_nbnxm
+ */
+#ifndef GMX_NBNXM_SYCL_NBNXM_SYCL_KERNEL_H
+#define GMX_NBNXM_SYCL_NBNXM_SYCL_KERNEL_H
+
+// Forward declarations
+namespace gmx
+{
+enum class InteractionLocality;
+class StepWorkload;
+} // namespace gmx
+struct NbnxmGpu;
+
+namespace Nbnxm
+{
+using gmx::InteractionLocality;
+
+/*! \brief Launch SYCL NBNXM kernel.
+ *
+ * \param nb Non-bonded parameters.
+ * \param stepWork Workload flags for the current step.
+ * \param iloc Interaction locality.
+ */
+void launchNbnxmKernel(NbnxmGpu* nb, const gmx::StepWorkload& stepWork, const InteractionLocality iloc);
+
+} // namespace Nbnxm
+
+#endif // GMX_NBNXM_SYCL_NBNXM_SYCL_KERNEL_H
diff --git a/src/include/gromacs/nbnxm/sycl/nbnxm_sycl_kernel_pruneonly.h b/src/include/gromacs/nbnxm/sycl/nbnxm_sycl_kernel_pruneonly.h
new file mode 100644 (file)
index 0000000..3508f56
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares nbnxn sycl helper functions
+ *
+ * \ingroup module_nbnxm
+ */
+#ifndef GMX_NBNXM_SYCL_NBNXM_SYCL_KERNEL_PRUNEONLY_H
+#define GMX_NBNXM_SYCL_NBNXM_SYCL_KERNEL_PRUNEONLY_H
+
+// Forward declarations
+namespace gmx
+{
+enum class InteractionLocality;
+}
+struct NbnxmGpu;
+
+namespace Nbnxm
+{
+using gmx::InteractionLocality;
+
+/*! \brief Launch SYCL NBNXM prune-only kernel.
+ *
+ * \param nb Non-bonded parameters.
+ * \param iloc Interaction locality.
+ * \param numParts Total number of rolling-prune parts.
+ * \param part Number of the part to prune.
+ * \param numSciInPart Number of superclusters in \p part.
+ */
+void launchNbnxmKernelPruneOnly(NbnxmGpu*                 nb,
+                                const InteractionLocality iloc,
+                                const int                 numParts,
+                                const int                 part,
+                                const int                 numSciInPart);
+
+} // namespace Nbnxm
+
+#endif // GMX_NBNXM_SYCL_NBNXM_SYCL_KERNEL_PRUNEONLY_H
diff --git a/src/include/gromacs/nbnxm/sycl/nbnxm_sycl_kernel_utils.h b/src/include/gromacs/nbnxm/sycl/nbnxm_sycl_kernel_utils.h
new file mode 100644 (file)
index 0000000..f1d08db
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Helper functions and constants for SYCL NBNXM kernels
+ *
+ * \ingroup module_nbnxm
+ */
+#ifndef GMX_NBNXM_SYCL_NBNXN_SYCL_KERNEL_UTILS_H
+#define GMX_NBNXM_SYCL_NBNXN_SYCL_KERNEL_UTILS_H
+
+#include "gromacs/gpu_utils/sycl_kernel_utils.h"
+#include "gromacs/nbnxm/pairlist.h"
+#include "gromacs/nbnxm/pairlistparams.h"
+
+namespace Nbnxm
+{
+
+#ifndef GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY
+//! \brief Default for the prune kernel's j4 processing concurrency.
+#    define GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY 4
+#endif
+
+/*! \brief Prune kernel's j4 processing concurrency.
+ *
+ *  The \c GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY macro allows compile-time override.
+ */
+static constexpr int c_syclPruneKernelJ4Concurrency = GMX_NBNXN_PRUNE_KERNEL_J4_CONCURRENCY;
+
+/* Convenience constants */
+/*! \cond */
+// cluster size = number of atoms per cluster.
+static constexpr int c_clSize = c_nbnxnGpuClusterSize;
+// Square of cluster size.
+static constexpr int c_clSizeSq = c_clSize * c_clSize;
+// j-cluster size after split (4 in the current implementation).
+static constexpr int c_splitClSize = c_clSize / c_nbnxnGpuClusterpairSplit;
+// i-cluster interaction mask for a super-cluster with all c_nbnxnGpuNumClusterPerSupercluster=8 bits set.
+static constexpr unsigned superClInteractionMask = ((1U << c_nbnxnGpuNumClusterPerSupercluster) - 1U);
+
+// 1/sqrt(pi), same value as \c M_FLOAT_1_SQRTPI in other NB kernels.
+static constexpr float c_OneOverSqrtPi = 0.564189583547756F;
+// 1/6, same value as in other NB kernels.
+static constexpr float c_oneSixth = 0.16666667F;
+// 1/12, same value as in other NB kernels.
+static constexpr float c_oneTwelfth = 0.08333333F;
+/*! \endcond */
+
+} // namespace Nbnxm
+
+#endif // GMX_NBNXM_SYCL_NBNXN_SYCL_KERNEL_UTILS_H
diff --git a/src/include/gromacs/nbnxm/sycl/nbnxm_sycl_types.h b/src/include/gromacs/nbnxm/sycl/nbnxm_sycl_types.h
new file mode 100644 (file)
index 0000000..5b0abc4
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *  \brief
+ *  Data types used internally in the nbnxm_sycl module.
+ *
+ *  \ingroup module_nbnxm
+ */
+
+#ifndef NBNXM_SYCL_TYPES_H
+#define NBNXM_SYCL_TYPES_H
+
+#include "gromacs/gpu_utils/devicebuffer.h"
+#include "gromacs/gpu_utils/devicebuffer_sycl.h"
+#include "gromacs/gpu_utils/gmxsycl.h"
+#include "gromacs/gpu_utils/gpueventsynchronizer.h"
+#include "gromacs/gpu_utils/gputraits.h"
+#include "gromacs/nbnxm/gpu_types_common.h"
+#include "gromacs/nbnxm/nbnxm.h"
+#include "gromacs/nbnxm/pairlist.h"
+#include "gromacs/timing/gpu_timing.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+class GpuEventSynchronizer;
+
+/*! \internal
+ * \brief Main data structure for SYCL nonbonded force calculations.
+ */
+struct NbnxmGpu
+{
+    /*! \brief GPU device context.
+     *
+     * \todo Make it constant reference, once NbnxmGpu is a proper class.
+     */
+    const DeviceContext* deviceContext_;
+    /*! \brief true if doing both local/non-local NB work on GPU */
+    bool bUseTwoStreams = false;
+    /*! \brief true indicates that the nonlocal_done event was marked */
+    bool bNonLocalStreamDoneMarked = false;
+    /*! \brief atom data */
+    NBAtomDataGpu* atdat = nullptr;
+
+    // Data for GPU-side coordinate conversion between integrator and NBNXM
+    /*! \brief array of atom indices */
+    DeviceBuffer<int> atomIndices;
+    /*! \brief size of atom indices */
+    int atomIndicesSize = 0;
+    /*! \brief size of atom indices allocated in device buffer */
+    int atomIndicesSize_alloc = 0;
+    /*! \brief x buf ops num of atoms */
+    DeviceBuffer<int> cxy_na;
+    /*! \brief number of elements in cxy_na */
+    int ncxy_na = 0;
+    /*! \brief number of elements allocated allocated in device buffer */
+    int ncxy_na_alloc = 0;
+    /*! \brief x buf ops cell index mapping */
+    DeviceBuffer<int> cxy_ind;
+    /*! \brief number of elements in cxy_ind */
+    int ncxy_ind = 0;
+    /*! \brief number of elements allocated allocated in device buffer */
+    int ncxy_ind_alloc = 0;
+
+    NBParamGpu* nbparam = nullptr;
+    /*! \brief pair-list data structures (local and non-local) */
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, Nbnxm::gpu_plist*> plist = { { nullptr } };
+    /*! \brief staging area where fshift/energies get downloaded. Will be removed in SYCL. */
+    NBStagingData nbst;
+    /*! \brief local and non-local GPU streams */
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, const DeviceStream*> deviceStreams;
+
+    /*! \brief True if event-based timing is enabled. Always false for SYCL. */
+    bool bDoTime = false;
+    /*! \brief Dummy timers. */
+    Nbnxm::GpuTimers* timers = nullptr;
+    /*! \brief Dummy timing data. */
+    gmx_wallclock_gpu_nbnxn_t* timings = nullptr;
+
+    //! true when a pair-list transfer has been done at this step
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, bool> didPairlistH2D = { { false } };
+    //! true when we we did pruning on this step
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, bool> didPrune = { { false } };
+    //! true when we did rolling pruning (at the previous step)
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, bool> didRollingPrune = { { false } };
+
+    /*! \brief Event triggered when the non-local non-bonded
+     * kernel is done (and the local transfer can proceed) */
+    GpuEventSynchronizer nonlocal_done;
+    /*! \brief Event triggered when the tasks issued in the local
+     * stream that need to precede the non-local force or buffer
+     * operation calculations are done (e.g. f buffer 0-ing, local
+     * x/q H2D, buffer op initialization in local stream that is
+     * required also by nonlocal stream ) */
+    GpuEventSynchronizer misc_ops_and_local_H2D_done;
+
+    /*! \brief True if there is work for the current domain in the
+     * respective locality.
+     *
+     * This includes local/nonlocal GPU work, either bonded or
+     * nonbonded, scheduled to be executed in the current
+     * domain. As long as bonded work is not split up into
+     * local/nonlocal, if there is bonded GPU work, both flags
+     * will be true. */
+    gmx::EnumerationArray<Nbnxm::InteractionLocality, bool> haveWork = { { false } };
+
+    /*! \brief Pointer to event synchronizer triggered when the local
+     * GPU buffer ops / reduction is complete. Would be deprecated in SYCL.
+     *
+     * \note That the synchronizer is managed outside of this module
+     * in StatePropagatorDataGpu.
+     */
+    GpuEventSynchronizer* localFReductionDone = nullptr;
+};
+
+#endif /* NBNXM_SYCL_TYPES_H */
diff --git a/src/include/gromacs/onlinehelp/helpformat.h b/src/include/gromacs/onlinehelp/helpformat.h
new file mode 100644 (file)
index 0000000..c5cc667
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares common string formatting routines for online help.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+#ifndef GMX_ONLINEHELP_HELPFORMAT_H
+#define GMX_ONLINEHELP_HELPFORMAT_H
+
+#include <memory>
+#include <string>
+
+namespace gmx
+{
+
+class HelpWriterContext;
+
+/*! \libinternal \brief
+ * Formats rows of a table for text output.
+ *
+ * This utility class formats tabular data, mainly for console output.
+ * Each row in the table can take multiple lines, and automatic text wrapping
+ * is supported.  If text overflows the allocated width, the remaining columns
+ * on that line become shifted.  To avoid this, it is possible to start the
+ * output for different columns from different lines (it is the caller's
+ * responsibility to check that overflows are avoided or are acceptable).
+ *
+ * Column definitions are first set with addColumn().
+ * To format a row, first call clear().  Then call addColumnLine() to add text
+ * to each column (can be called multiple times on a single column to add
+ * multiple lines), and possibly setColumnFirstLineOffset() to adjust the line
+ * from which the column output should start.  Finally, call formatRow() to
+ * obtain the formatted row.
+ *
+ * A header (column titles and a horizontal line) is printed before the first
+ * line.
+ *
+ * Typical usage:
+ * \code
+   gmx::TextTableFormatter formatter;
+   formatter.addColumn("Name", 10, false);
+   formatter.addColumn("Type", 10, false);
+   formatter.addColumn("Description", 50, true);
+
+   formatter.clear();
+   formatter.addColumnLine(0, "name");
+   formatter.addColumnLine(1, "type");
+   formatter.addColumnLine(2, "Description for name");
+   printf("%s", formatter.formatRow().c_str());
+
+   formatter.clear();
+   formatter.addColumnLine(0, "averylongname");
+   formatter.addColumnLine(1, "type");
+   formatter.setColumnFirstLineOffset(1, 1);
+   formatter.addColumnLine(2, "Description for name");
+   printf("%s", formatter.formatRow().c_str());
+
+   // format other rows by repeating the above code
+ * \endcode
+ *
+ * Methods in this class may throw std::bad_alloc if out of memory.
+ * Other exceptions are not thrown.
+ *
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+class TextTableFormatter
+{
+public:
+    //! Constructs an empty formatter.
+    TextTableFormatter();
+    ~TextTableFormatter();
+
+    /*! \brief
+     * Adds a column to the table.
+     *
+     * \param[in]  title  Title string for the column (used for header).
+     * \param[in]  width  Width of the column (must be > 0).
+     * \param[in]  bWrap  Whether text that exceeds \p width is
+     *      automatically wrapped.
+     *
+     * The length of \p title must not exceed \p width.
+     */
+    void addColumn(const char* title, int width, bool bWrap);
+    /*! \brief
+     * Sets amount on indentation before the first column.
+     *
+     * \param[in]  indent  Number of spaces to use for indenting.
+     *
+     * Does not throw.
+     */
+    void setFirstColumnIndent(int indent);
+    /*! \brief
+     * Enables folding the last column to separate lines if it overflows.
+     *
+     * \param[in]  indent  Number of spaces to use for indenting the lines.
+     *
+     * If called with `indent >= 0`, the last column for each row is
+     * treated specially: if it contains more lines than the other columns,
+     * and if the text would fit more compactly as separate lines after the
+     * row, then the whole last column is written after the row with the
+     * given \p indent.  The column text then spans the whole space
+     * reserved for the table, making long text fit into a smaller amount
+     * of vertical space.
+     * If not called, the last column is not treates specially.
+     *
+     * Does not throw.
+     */
+    void setFoldLastColumnToNextLine(int indent);
+
+    /*! \brief
+     * Whether formatRow() has been successfully called.
+     *
+     * This method can be used to determine after-the-fact whether anything
+     * was written in the table.
+     *
+     * Does not throw.
+     */
+    bool didOutput() const;
+
+    /*! \brief
+     * Removes all text from all columns and resets the line offsets.
+     *
+     * Removes all text added using addColumnLine() and resets line offsets
+     * set with setColumnFirstLineOffset() to zero.
+     * Should be called before starting to add data for a row.
+     *
+     * Does not throw.
+     */
+    void clear();
+    /*! \brief
+     * Adds text to be printed in a column.
+     *
+     * \param[in]  index     Zero-based column index.
+     * \param[in]  text      Text to add.
+     *
+     * Can be called multiple times.  Additional calls append \p text as
+     * additional lines.  Any calls with \p text empty have no effect.
+     * To add an empty line, use "\n" as \p text.
+     *
+     * If \p text contains newlines, the text is automatically splitted to
+     * multiple lines.  The same happens if automatic wrapping is on for
+     * the column and the text contains lines that are longer than what
+     * fits the column.
+     */
+    void addColumnLine(int index, const std::string& text);
+    /*! \brief
+     * Adds text containing help markup to be printed in a column.
+     *
+     * \param[in]  index     Zero-based column index.
+     * \param[in]  context   Context to use for markup processing.
+     * \param[in]  text      Text to add.
+     *
+     * Works as addColumnLine(), except that it uses
+     * HelpWriterContext::substituteMarkupAndWrapToVector() to process
+     * markup in the input text instead of just wrapping it as plain text.
+     */
+    void addColumnHelpTextBlock(int index, const HelpWriterContext& context, const std::string& text);
+    /*! \brief
+     * Sets the first line to which text is printed for a column.
+     *
+     * \param[in]  index     Zero-based column index.
+     * \param[in]  firstLine Zero-based line index from which to start the
+     *      output.
+     *
+     * Can be called if there is no text for column \p index.
+     * Does not affect the output in this case.
+     *
+     * Does not throw.
+     */
+    void setColumnFirstLineOffset(int index, int firstLine);
+    /*! \brief
+     * Formats the lines for the current row.
+     *
+     * \returns  Current row formatted as a single string
+     *      (contains newlines).
+     *
+     * Formats the data as set after the previous clear()/formatRow() using
+     * addColumnLine() and setColumnFirstLineOffset().
+     *
+     * If this is the first line to be formatted, a header is also added to
+     * the beginning of the returned string if any column has a title.
+     *
+     * The return value always terminates with a newline.
+     *
+     * Calls clear() on successful return.
+     */
+    std::string formatRow();
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/onlinehelp/helpmanager.h b/src/include/gromacs/onlinehelp/helpmanager.h
new file mode 100644 (file)
index 0000000..03a4ec3
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::HelpManager.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+#ifndef GMX_ONLINEHELP_HELPMANAGER_H
+#define GMX_ONLINEHELP_HELPMANAGER_H
+
+#include <memory>
+#include <string>
+
+namespace gmx
+{
+
+class HelpWriterContext;
+class IHelpTopic;
+
+/*! \libinternal \brief
+ * Helper for providing interactive online help.
+ *
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+class HelpManager
+{
+public:
+    /*! \brief
+     * Creates a manager that uses a given root topic.
+     *
+     * \param[in] rootTopic  Help topic that can be accessed through this
+     *      manager.
+     * \param[in] context    Context object for writing the help.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * The provided topic and context objects must remain valid for the
+     * lifetime of this manager object.
+     */
+    HelpManager(const IHelpTopic& rootTopic, const HelpWriterContext& context);
+    ~HelpManager();
+
+    /*! \brief
+     * Enters a subtopic with the given name under the active topic.
+     *
+     * \param[in] name  Subtopic name to enter.
+     * \throws    std::bad_allod if out of memory.
+     * \throws    InvalidInputError if topic with \p name is not found.
+     */
+    void enterTopic(const char* name);
+    //! \copydoc enterTopic(const char *)
+    void enterTopic(const std::string& name);
+
+    /*! \brief
+     * Writes out the help for the currently active topic.
+     *
+     * \throws  std::bad_alloc if out of memory.
+     * \throws  FileIOError on any I/O error.
+     */
+    void writeCurrentTopic() const;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/onlinehelp/helptopic.h b/src/include/gromacs/onlinehelp/helptopic.h
new file mode 100644 (file)
index 0000000..875c1c0
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares helper classes for implementing gmx::IHelpTopic.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+#ifndef GMX_ONLINEHELP_HELPTOPIC_H
+#define GMX_ONLINEHELP_HELPTOPIC_H
+
+#include <memory>
+
+#include "gromacs/onlinehelp/ihelptopic.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Abstract base class for help topics that have simple text and no subtopics.
+ *
+ * This class implements subtopic-related methods from IHelpTopic such
+ * that there are no subtopics.  writeHelp() is also implemented such that it
+ * uses HelpTopicContext::writeTextBlock() to write out the text returned by a
+ * new virtual method helpText().
+ *
+ * \see SimpleHelpTopic
+ *
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+class AbstractSimpleHelpTopic : public IHelpTopic
+{
+public:
+    const char* name() const override  = 0;
+    const char* title() const override = 0;
+
+    bool              hasSubTopics() const override;
+    const IHelpTopic* findSubTopic(const char* name) const override;
+
+    void writeHelp(const HelpWriterContext& context) const override;
+
+protected:
+    /*! \brief
+     * Returns the help text for this topic.
+     *
+     * writeHelp() calls this method to obtain the actual text to format
+     * for the topic.  Markup substitution etc. is done automatically by
+     * writeHelp().
+     */
+    virtual std::string helpText() const = 0;
+};
+
+/*! \libinternal \brief
+ * Abstract base class for help topics that have simple text and subtopics.
+ *
+ * This class implements an internal container for subtopics and provides
+ * public methods for adding subtopics (as IHelpTopic objects).
+ * Subtopic-related methods from IHelpTopic are implemented to access
+ * the internal container.  writeHelp() is also implemented such that it
+ * uses HelpTopicContext::writeTextBlock() to write out the text returned by a
+ * new virtual method helpText(), and a list of subtopics is written after the
+ * actual text.
+ *
+ * \see CompositeHelpTopic
+ *
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+class AbstractCompositeHelpTopic : public IHelpTopic
+{
+public:
+    AbstractCompositeHelpTopic();
+    ~AbstractCompositeHelpTopic() override;
+
+    const char* name() const override  = 0;
+    const char* title() const override = 0;
+
+    bool              hasSubTopics() const override;
+    const IHelpTopic* findSubTopic(const char* name) const override;
+
+    void writeHelp(const HelpWriterContext& context) const override;
+
+    /*! \brief
+     * Adds a given topic as a subtopic of this topic.
+     *
+     * \param   topic  Topis to add.
+     * \throws  std::bad_alloc if out of memory.
+     *
+     * This topic takes ownership of the object.
+     *
+     * \see registerSubTopic()
+     */
+    void addSubTopic(HelpTopicPointer topic);
+    /*! \brief
+     * Registers a subtopic of a certain type to this topic.
+     *
+     * \tparam  Topic  Type of topic to register.
+     * \throws  std::bad_alloc if out of memory.
+     *
+     * \p Topic must be default-constructible and implement
+     * IHelpTopic.
+     *
+     * This method is provided as a convenient alternative to addSubTopic()
+     * for cases where each topic is implemented by a different type
+     * (which is a common case outside unit tests).
+     */
+    template<class Topic>
+    void registerSubTopic()
+    {
+        addSubTopic(HelpTopicPointer(new Topic));
+    }
+
+protected:
+    //! \copydoc gmx::AbstractSimpleHelpTopic::helpText()
+    virtual std::string helpText() const = 0;
+
+    /*! \brief
+     * Writes the list of subtopics.
+     *
+     * \param[in] context Context for writing the help.
+     * \param[in] title  Title for the written list.
+     * \returns   true if anything was printed.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    FileIOError on any I/O error.
+     *
+     * Subtopics with empty titles are skipped from the list.
+     * If there would be no subtopics in the list, \p title is not printed
+     * either.
+     *
+     * This method is provided for cases where helpText() does not provide
+     * the needed flexibility and the derived class needs to override
+     * writeHelp().  This method can then be called to print the same
+     * subtopic list that is printed by the default writeHelp()
+     * implementation.
+     */
+    bool writeSubTopicList(const HelpWriterContext& context, const std::string& title) const;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \cond libapi */
+/*! \libinternal \brief
+ * Smart pointer type to manage a AbstractCompositeHelpTopic object.
+ *
+ * \inlibraryapi
+ */
+typedef std::unique_ptr<AbstractCompositeHelpTopic> CompositeHelpTopicPointer;
+//! \endcond
+
+/*! \libinternal \brief
+ * Template for simple implementation of AbstractSimpleHelpTopic.
+ *
+ * \tparam HelpText Struct that defines the data for the topic.
+ *
+ * \p HelpText should have public static members \c "const char name[]",
+ * \c "const char title[]" and \c "const char *const text[]".
+ *
+ * Typical use:
+ * \code
+   struct ExampleHelpText
+   {
+       static const char name[];
+       static const char title[];
+       static const char *const text[];
+   };
+
+   const char ExampleHelpText::name[] = "example";
+   const char ExampleHelpText::title[] =
+       "Example title";
+   const char *const ExampleHelpText::text[] = {
+       "Text for the topic.",
+       "More text for the topic."
+   };
+
+   typedef SimpleHelpTopic<ExampleHelpText> ExampleHelpTopic;
+ * \endcode
+ *
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+template<class HelpText>
+class SimpleHelpTopic : public AbstractSimpleHelpTopic
+{
+public:
+    const char* name() const override { return HelpText::name; }
+    const char* title() const override { return HelpText::title; }
+
+protected:
+    std::string helpText() const override { return joinStrings(HelpText::text, "\n"); }
+};
+
+/*! \libinternal \brief
+ * Template for simple implementation of AbstractCompositeHelpTopic.
+ *
+ * \tparam HelpText Struct that defines the data for the topic.
+ *
+ * Used similarly to SimpleHelpTopic.
+ * \p HelpText should satisfy the same criteria as for SimpleHelpTopic.
+ *
+ * \see SimpleHelpTopic
+ *
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+template<class HelpText>
+class CompositeHelpTopic : public AbstractCompositeHelpTopic
+{
+public:
+    // copydocs are needed with Doxygen 1.8.10, but not 1.8.5...
+    //! \copydoc gmx::AbstractCompositeHelpTopic::name()
+    const char* name() const override { return HelpText::name; }
+    //! \copydoc gmx::AbstractCompositeHelpTopic::title()
+    const char* title() const override { return HelpText::title; }
+
+protected:
+    //! \copydoc gmx::AbstractCompositeHelpTopic::helpText()
+    std::string helpText() const override { return joinStrings(HelpText::text, "\n"); }
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/onlinehelp/helpwritercontext.h b/src/include/gromacs/onlinehelp/helpwritercontext.h
new file mode 100644 (file)
index 0000000..cf16c06
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::HelpWriterContext.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+#ifndef GMX_ONLINEHELP_HELPWRITERCONTEXT_H
+#define GMX_ONLINEHELP_HELPWRITERCONTEXT_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/classhelpers.h"
+
+namespace gmx
+{
+
+class TextLineWrapperSettings;
+class TextWriter;
+
+/*! \cond libapi */
+//! \libinternal Output format for help writing.
+enum HelpOutputFormat
+{
+    eHelpOutputFormat_Console, //!< Plain text directly on the console.
+    eHelpOutputFormat_Rst,     //!< reStructuredText for online manual and man pages.
+    eHelpOutputFormat_Other,   //!< Used for extensions in other modules.
+    eHelpOutputFormat_NR       //!< Used for the number of output formats.
+};
+//! \endcond
+
+/*! \libinternal \brief
+ * Hyperlink data for writing out help.
+ *
+ * This class is separate from HelpWriterContext to allow constructing the list
+ * of links once and reusing them across multiple help writer contexts.
+ * This is used when exporting all the help from the wrapper binary to avoid
+ * repeatedly constructing the same data structure for each help item.
+ *
+ * While the links are in principle independent of the output format, the
+ * constructor takes the output format to be able to preformat the links,
+ * avoiding repeated processing during markup substitution.  Could be hidden
+ * behind the scenes in HelpWriterContext, but that would complicate the
+ * implementation.
+ *
+ * \ingroup module_onlinehelp
+ */
+class HelpLinks
+{
+public:
+    /*! \brief
+     * Initializes an empty links collection for the given output format.
+     */
+    explicit HelpLinks(HelpOutputFormat format);
+    ~HelpLinks();
+
+    /*! \brief
+     * Adds a link.
+     *
+     * \param[in] linkName     Name of the link in input text.
+     * \param[in] targetName   Hyperlink target.
+     * \param[in] displayName  Text to show as the link.
+     *
+     * Any occurrence of \p linkName in the text passed to markup
+     * substitution methods in HelpWriterContext is made into a hyperlink
+     * to \p targetName if the markup format supports that.
+     */
+    void addLink(const std::string& linkName, const std::string& targetName, const std::string& displayName);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+
+    //! Allows the context to use the links.
+    friend class HelpWriterContext;
+};
+
+/*! \libinternal \brief
+ * Context information for writing out help.
+ *
+ * The purpose of this class is to pass information about the output format to
+ * methods that write help, and to abstract away most of the details of
+ * different output formats.
+ *
+ * The state of a context object (excluding the fact that the output file is
+ * written to) does not change after initial construction of the object.
+ * Copying creates a context objects that share state with the source.
+ *
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+class HelpWriterContext
+{
+public:
+    /*! \brief
+     * Initializes a context with the given output writer and format.
+     *
+     * \throws std::bad_alloc if out of memory.
+     */
+    HelpWriterContext(TextWriter* writer, HelpOutputFormat format);
+    /*! \brief
+     * Initializes a context with the given output writer, format and links.
+     *
+     * \throws std::bad_alloc if out of memory.
+     *
+     * A reference to \p links is stored until the HelpWriterContext
+     * is destructed.  The caller is responsible for ensuring that the
+     * links object remains valid long enough.
+     */
+    HelpWriterContext(TextWriter* writer, HelpOutputFormat format, const HelpLinks* links);
+    //! Creates a copy of the context.
+    HelpWriterContext(const HelpWriterContext& other);
+    ~HelpWriterContext();
+
+    /*! \brief
+     * Adds a string replacement for markup subsitution.
+     *
+     * \param[in] search   Text to replace in input.
+     * \param[in] replace  Text that each occurrence of \p search is
+     *     replaced with.
+     * \throws std::bad_alloc if out of memory.
+     *
+     * \todo
+     * Improve semantics if the same \p search item is set multiple
+     * times.
+     */
+    void setReplacement(const std::string& search, const std::string& replace);
+
+    /*! \brief
+     * Returns the active output format.
+     *
+     * Does not throw.
+     */
+    HelpOutputFormat outputFormat() const;
+    /*! \brief
+     * Returns the raw writer for writing the help.
+     *
+     * Using this writer directly should be avoided, as it requires one to
+     * have different code for each output format.
+     * Using other methods in this class should be preferred.
+     *
+     * Does not throw.
+     */
+    TextWriter& outputFile() const;
+
+    /*! \brief
+     * Creates a subsection in the output help.
+     *
+     * \param[in] title  Title for the subsection.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    FileIOError on any I/O error.
+     *
+     * Writes \p title using writeTitle() and makes any further
+     * writeTitle() calls write headings one level deeper.
+     *
+     * Typical use for writing a subsection is to create a copy of the
+     * context for the parent section, and then call enterSubSection() on
+     * the copy.
+     * The whole subsection should be written out using the returned
+     * context before calling any further methods in the parent context.
+     *
+     * This method is only necessary if the subsection will contain further
+     * subsections.  If there is only one level of subsections, it is
+     * possible to use writeTitle() directly.
+     */
+    void enterSubSection(const std::string& title);
+
+    /*! \brief
+     * Substitutes markup used in help text and wraps lines.
+     *
+     * \param[in] settings Line wrapper settings.
+     * \param[in] text     Text to substitute.
+     * \returns   \p text with markup substituted and wrapped.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * \see TextLineWrapper::wrapToString()
+     */
+    std::string substituteMarkupAndWrapToString(const TextLineWrapperSettings& settings,
+                                                const std::string&             text) const;
+    /*! \brief
+     * Substitutes markup used in help text and wraps lines.
+     *
+     * \param[in] settings Line wrapper settings.
+     * \param[in] text     Text to substitute.
+     * \returns   \p text with markup substituted and wrapped as a list of
+     *      lines.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * \see TextLineWrapper::wrapToVector()
+     */
+    std::vector<std::string> substituteMarkupAndWrapToVector(const TextLineWrapperSettings& settings,
+                                                             const std::string& text) const;
+    /*! \brief
+     * Writes a title for the current help topic.
+     *
+     * \param[in] title  Title to write.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    FileIOError on any I/O error.
+     */
+    void writeTitle(const std::string& title) const;
+    /*! \brief
+     * Writes a formatted text block into the output.
+     *
+     * \param[in] text  Text to format.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    FileIOError on any I/O error.
+     *
+     * Convenience function that calls substituteMarkupAndWrapToString()
+     * and writes the result directly to the output file.
+     */
+    void writeTextBlock(const std::string& text) const;
+    /*! \brief
+     * Ensures a paragraph break (empty line) in the output.
+     *
+     * Calls at the beginning and end of output do not produce extra empty
+     * lines, and consencutive calls only result in a single empty line.
+     * This allows calling the method before and after all output that
+     * needs to appear separated as empty lines.
+     */
+    void paragraphBreak() const;
+    /*! \brief
+     * Starts writing a list of options.
+     *
+     * Prints any necessary headers for a list of options formatted with
+     * writeOptionItem().
+     */
+    void writeOptionListStart() const;
+    /*! \brief
+     * Writes an entry for a single option into the output.
+     *
+     * \param[in] name  Name of the option.
+     * \param[in] value        Placeholder for option value.
+     * \param[in] defaultValue Default value for the option.
+     * \param[in] info         Additional (brief) info/attributes for the
+     *      option.
+     * \param[in] description  Full description of the option.
+     */
+    void writeOptionItem(const std::string& name,
+                         const std::string& value,
+                         const std::string& defaultValue,
+                         const std::string& info,
+                         const std::string& description) const;
+    /*! \brief
+     * Finishes writing a list of options.
+     *
+     * Prints any necessary footers for a list of options formatted with
+     * writeOptionItem().
+     */
+    void writeOptionListEnd() const;
+
+private:
+    class Impl;
+
+    /*! \brief
+     * Constructs a context object with the given implementation class.
+     *
+     * \param[in] impl  Implementation object.
+     *
+     * Does not throw.
+     */
+    explicit HelpWriterContext(Impl* impl);
+
+    std::unique_ptr<Impl> impl_;
+
+    GMX_DISALLOW_ASSIGN(HelpWriterContext);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/onlinehelp/ihelptopic.h b/src/include/gromacs/onlinehelp/ihelptopic.h
new file mode 100644 (file)
index 0000000..aee449a
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::IHelpTopic.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+#ifndef GMX_ONLINEHELP_IHELPTOPIC_H
+#define GMX_ONLINEHELP_IHELPTOPIC_H
+
+#include <memory>
+
+namespace gmx
+{
+
+class HelpWriterContext;
+
+/*! \libinternal \brief
+ * Provides a single online help topic.
+ *
+ * Implementations of these methods should not throw, except that writeHelp()
+ * is allowed to throw on out-of-memory or I/O errors since those it cannot
+ * avoid.
+ *
+ * Header helptopic.h contains classes that implement this interface and make
+ * it simple to write concrete help topic classes.
+ *
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+class IHelpTopic
+{
+public:
+    virtual ~IHelpTopic() {}
+
+    /*! \brief
+     * Returns the name of the topic.
+     *
+     * This should be a single lowercase word, used to identify the topic.
+     * It is not used for the root of the help topic tree.
+     */
+    virtual const char* name() const = 0;
+    /*! \brief
+     * Returns a title for the topic.
+     *
+     * May return NULL, in which case the topic is omitted from normal
+     * subtopic lists and no title is printed by the methods provided in
+     * helptopic.h.
+     */
+    virtual const char* title() const = 0;
+
+    //! Returns whether the topic has any subtopics.
+    virtual bool hasSubTopics() const = 0;
+    /*! \brief
+     * Finds a subtopic by name.
+     *
+     * \param[in] name  Name of subtopic to find.
+     * \returns   Pointer to the found subtopic, or NULL if matching topic
+     *      is not found.
+     */
+    virtual const IHelpTopic* findSubTopic(const char* name) const = 0;
+
+    /*! \brief
+     * Prints the help text for this topic.
+     *
+     * \param[in] context  Context object for writing the help.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    FileIOError on any I/O error.
+     */
+    virtual void writeHelp(const HelpWriterContext& context) const = 0;
+};
+
+//! Smart pointer type to manage a IHelpTopic object.
+typedef std::unique_ptr<IHelpTopic> HelpTopicPointer;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/onlinehelp/rstparser.h b/src/include/gromacs/onlinehelp/rstparser.h
new file mode 100644 (file)
index 0000000..3ac9998
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares classes for (partial) parsing of reStructuredText.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_onlinehelp
+ */
+#ifndef GMX_ONLINEHELP_RSTPARSER_H
+#define GMX_ONLINEHELP_RSTPARSER_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/utility/classhelpers.h"
+
+namespace gmx
+{
+
+class TextLineWrapperSettings;
+
+/*! \internal
+ * \brief
+ * Iterator over reStructuredText paragraphs.
+ *
+ * After initialization, nextParagraph() needs to be called to access the first
+ * paragraph.  Subsequence paragraphs can be accessed by repeated calls to
+ * nextParagraph().  After the last paragraph, nextParagraph() returns `false`.
+ *
+ * After each call to nextParagraph(), other methods can be called to query
+ * details of the current paragraph.
+ *
+ * \ingroup module_onlinehelp
+ */
+class RstParagraphIterator
+{
+public:
+    /*! \brief
+     * Initializes an iterator for given input text.
+     *
+     * Does not throw.
+     */
+    explicit RstParagraphIterator(const std::string& text);
+
+    /*! \brief
+     * Advances the iterator to the next paragraph.
+     *
+     * \returns `false` if there were no more paragraphs.
+     *
+     * Does not throw (except std::bad_alloc if std::string::compare()
+     * throws).
+     */
+    bool nextParagraph();
+
+    //! Returns the indentation for first line of this paragraph.
+    int firstLineIndent() const { return firstLineIndent_; }
+    //! Returns the indentation for subsequent lines of this paragraph.
+    int indent() const { return indent_; }
+    /*! \brief
+     * Returns the text
+     *
+     * \param[out] result  Variable to receive the paragraph text.
+     * \throws std::bad_alloc if out of memory.
+     *
+     * Indentation and internal line breaks have been stripped from the
+     * paragraph text (except for literal blocks etc.).  For literal
+     * blocks, the common indentation has been stripped and is returned in
+     * indent() instead.
+     *
+     * Leading newlines are returned to indicate necessary separation from
+     * the preceding paragraph.
+     */
+    void getParagraphText(std::string* result) const;
+
+private:
+    enum ParagraphType
+    {
+        eParagraphType_Normal,
+        eParagraphType_Literal,
+        eParagraphType_Title
+    };
+
+    //! The text to iterate over.
+    const std::string& text_; //NOLINT(google-runtime-member-string-references)
+
+    //! Start of the current paragraph.
+    size_t begin_;
+    //! End of the current paragraph (C++-style iterator).
+    size_t end_;
+    //! Type of the current paragraph.
+    ParagraphType type_;
+    //! Number of newlines to print before the current paragraph.
+    int breakSize_;
+    //! Indentation of the first line of this paragraph.
+    int firstLineIndent_;
+    //! (Minimum) indentation of other lines in this paragraph.
+    int indent_;
+
+    //! Start of the next paragrah.
+    size_t nextBegin_;
+    //! Number of newlines to print after the current paragraph.
+    int nextBreakSize_;
+    /*! \brief
+     * Indentation of the preceding paragraph that contained `::`.
+     *
+     * If the next paragraph is not a literal block, the value is `-1`.
+     */
+    int literalIndent_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(RstParagraphIterator);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/onlinehelp/tests/mock_helptopic.h b/src/include/gromacs/onlinehelp/tests/mock_helptopic.h
new file mode 100644 (file)
index 0000000..e8a3f13
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares mock implementation of gmx::IHelpTopic.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_onlinehelp
+ */
+#ifndef GMX_ONLINEHELP_TESTS_MOCK_HELPTOPIC_H
+#define GMX_ONLINEHELP_TESTS_MOCK_HELPTOPIC_H
+
+#include <gmock/gmock.h>
+
+#include "gromacs/onlinehelp/helptopic.h"
+#include "gromacs/onlinehelp/helpwritercontext.h"
+
+namespace gmx
+{
+namespace test
+{
+
+class MockHelpTopic : public AbstractCompositeHelpTopic
+{
+public:
+    static MockHelpTopic& addSubTopic(gmx::AbstractCompositeHelpTopic* parent,
+                                      const char*                      name,
+                                      const char*                      title,
+                                      const char*                      text);
+
+    MockHelpTopic(const char* name, const char* title, const char* text);
+    ~MockHelpTopic() override;
+
+    const char* name() const override;
+    const char* title() const override;
+
+    MOCK_CONST_METHOD1(writeHelp, void(const HelpWriterContext& context));
+
+    MockHelpTopic& addSubTopic(const char* name, const char* title, const char* text);
+    using AbstractCompositeHelpTopic::addSubTopic;
+
+    /*! \brief
+     * Calls base class writeHelp() method.
+     *
+     * This provides the possibility for the mock to do the actual help
+     * writing.
+     */
+    void writeHelpBase(const HelpWriterContext& context)
+    {
+        AbstractCompositeHelpTopic::writeHelp(context);
+    }
+
+private:
+    std::string helpText() const override;
+
+    const char* name_;
+    const char* title_;
+    const char* text_;
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/abstractoption.h b/src/include/gromacs/options/abstractoption.h
new file mode 100644 (file)
index 0000000..c64e68d
--- /dev/null
@@ -0,0 +1,593 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010-2017, The GROMACS development team.
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Defines gmx::AbstractOption, gmx::OptionTemplate and gmx::OptionInfo.
+ *
+ * This header defines base classes for option settings that are used with
+ * Options::addOption().  These classes implement the "named parameter"
+ * idiom for specifying option properties.
+ *
+ * These classes also take care of creating and setting up the actual option
+ * objects.
+ *
+ * This header is needed directly only when implementing new option types,
+ * but methods of OptionTemplate are visible even to the normal user through
+ * its subclasses.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_ABSTRACTOPTION_H
+#define GMX_OPTIONS_ABSTRACTOPTION_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/classhelpers.h"
+
+#include "optionflags.h"
+
+namespace gmx
+{
+
+class AbstractOptionStorage;
+template<typename T>
+class OptionStorageTemplate;
+class OptionManagerContainer;
+class Any;
+
+namespace internal
+{
+class OptionSectionImpl;
+}
+
+/*! \brief
+ * Abstract base class for specifying option properties.
+ *
+ * Concrete classes should normally not derive directly from this class,
+ * but from OptionTemplate instead.  Classes derived from this class
+ * are mainly designed to implement the "named parameter" idiom.  For
+ * efficiency and clarity, these classes should only store values provided to
+ * them.  All error checking and memory management should be postponed to the
+ * point when the actual option is created.
+ *
+ * Subclasses should override createStorage() to create the correct type
+ * of storage object.  If they use their own info type derived from OptionInfo,
+ * they should also have a public typedef \c InfoType that specifies that
+ * info type.  This is required for Options::addOption() to return the correct
+ * info type.
+ *
+ * \ingroup module_options
+ */
+class AbstractOption
+{
+public:
+    // Virtual only for completeness, in normal use should not be needed.
+    virtual ~AbstractOption() {}
+
+protected:
+    /*! \cond libapi */
+    //! Initializes the name and default values for an option.
+    explicit AbstractOption(const char* name) :
+        minValueCount_(1), maxValueCount_(1), name_(name), descr_(nullptr), storeIsSet_(nullptr)
+    {
+    }
+
+    /*! \brief
+     * Creates a default storage object for the option.
+     *
+     * \param[in] managers  Manager container (unused if the option does
+     *     not use a manager).
+     * \returns   The created storage object.
+     * \throws    APIError if invalid option settings have been provided.
+     *
+     * This method is called by Options::addOption() when initializing an
+     * option from the settings.
+     *
+     * Derived classes should implement the method to create an actual
+     * storage object and populate it with correct values.
+     * They should also throw APIError if they detect problems.
+     *
+     * Should only be called by Options::addOption().
+     *
+     * The ownership of the return value is passed, but is not using a
+     * smart pointer to avoid introducing such a dependency in an installed
+     * header.  The implementation will always consist of a single `new`
+     * call and returning that value, and the caller always immediately
+     * wraps the pointer in a smart pointer, so there is not exception
+     * safety issue.
+     */
+    virtual AbstractOptionStorage* createStorage(const OptionManagerContainer& managers) const = 0;
+
+    //! Sets the description for the option.
+    void setDescription(const char* descr) { descr_ = descr; }
+    //! Sets the storage location for whether the option is set.
+    void setStoreIsSet(bool* store) { storeIsSet_ = store; }
+    //! Sets a flag for the option.
+    void setFlag(OptionFlag flag) { flags_.set(flag); }
+    //! Clears a flag for the option.
+    void clearFlag(OptionFlag flag) { flags_.clear(flag); }
+    //! Sets or clears a flag for the option.
+    void setFlag(OptionFlag flag, bool bSet) { flags_.set(flag, bSet); }
+    //! Returns true if the option is vector-valued.
+    bool isVector() const { return hasFlag(efOption_Vector); }
+    /*! \brief
+     * Sets the option to be vector-valued.
+     *
+     * This method is provided for convenience to make management of value
+     * counts easier.  In order to implement a vector-valued option, the
+     * class derived from AbstractOption should expose a method that calls
+     * this method, and the storage object derived from
+     * AbstractOptionStorage should check isVector().
+     * If only a single value is provided, the storage object should fill
+     * the whole vector with that value.
+     *
+     * The length of the vector (the value of maxValueCount_) must be
+     * fixed.  The default length is 3 elements.
+     */
+    void setVector()
+    {
+        setFlag(efOption_Vector);
+        minValueCount_ = 1;
+        if (maxValueCount_ == 1)
+        {
+            maxValueCount_ = 3;
+        }
+    }
+    //! Sets the required number of values for the option.
+    void setValueCount(int count)
+    {
+        if (!hasFlag(efOption_Vector))
+        {
+            minValueCount_ = count;
+        }
+        maxValueCount_ = count;
+    }
+
+    //! Minimum number of values required for the option.
+    int minValueCount_;
+    //! Maximum number of values allowed for the option.
+    int maxValueCount_;
+    //! \endcond
+
+private:
+    //! Returns true if a flag has been set.
+    bool hasFlag(OptionFlag flag) const { return flags_.test(flag); }
+
+    const char* name_;
+    //! Pointer to description of the option.
+    const char* descr_;
+    OptionFlags flags_;
+    bool*       storeIsSet_;
+
+    /*! \brief
+     * Needed to initialize an AbstractOptionStorage object from this class
+     * without otherwise unnecessary accessors.
+     */
+    friend class AbstractOptionStorage;
+    //! Needed to be able to call createStorage().
+    friend class internal::OptionSectionImpl;
+};
+
+/*! \brief
+ * Templated base class for constructing concrete option settings classes.
+ *
+ * \tparam T Assignable type that stores a single option value.
+ * \tparam U Type of the derived class.
+ *
+ * This template is used as a base class like this:
+ * \code
+   class ConcreteOption : public OptionTemplate<int, ConcreteOption>
+   {
+ * \endcode
+ *
+ * All public functions in this class return \c *this casted to a reference to
+ * \p U.  They do not throw.
+ *
+ * For examples of how to use classes derived from this class, see the class
+ * documentation for Options.
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+template<typename T, class U>
+class OptionTemplate : public AbstractOption
+{
+public:
+    //! Type that stores a single option value.
+    typedef T ValueType;
+    //! Alias for the derived class type.
+    typedef U MyClass;
+
+    /*! \brief
+     * Sets a description for the option.
+     *
+     * \param[in] descr Description to set.
+     *
+     * String in \p descr is copied when the option is created.
+     */
+    MyClass& description(const char* descr)
+    {
+        setDescription(descr);
+        return me();
+    }
+    //! Hides the option from normal help output.
+    MyClass& hidden(bool bHidden = true)
+    {
+        setFlag(efOption_Hidden, bHidden);
+        return me();
+    }
+    /*! \brief
+     * Requires the option to be specified explicitly.
+     *
+     * Note that if you specify defaultValue() together with required(),
+     * the user is not required to explicitly provide the option.
+     * In this case, required() only affects possible help output.
+     */
+    MyClass& required(bool bRequired = true)
+    {
+        setFlag(efOption_Required, bRequired);
+        return me();
+    }
+    //! Allows the option to be specified multiple times.
+    MyClass& allowMultiple(bool bMulti = true)
+    {
+        setFlag(efOption_MultipleTimes, bMulti);
+        return me();
+    }
+    //! Requires exactly \p count values for the option.
+    MyClass& valueCount(int count)
+    {
+        setValueCount(count);
+        return me();
+    }
+    //! Allows any number of values for the option.
+    MyClass& multiValue(bool bMulti = true)
+    {
+        if (bMulti)
+        {
+            maxValueCount_ = -1;
+        }
+        return me();
+    }
+
+    /*! \brief
+     * Sets a default value for the option.
+     *
+     * \param[in] defaultValue Default value.
+     *
+     * If the option is never set, the default value is copied to the
+     * assigned storage.  Note that if the option is not set and there
+     * is no default value, the storage is not altered, which can also be
+     * used to provide a default value.  The latter method has to be used
+     * if the option can take multiple values.
+     *
+     * \p defaultValue is copied when the option is created.
+     */
+    MyClass& defaultValue(const T& defaultValue)
+    {
+        defaultValue_ = &defaultValue;
+        return me();
+    }
+    /*! \brief
+     * Sets a default value for the option when it is set.
+     *
+     * \param[in] defaultValue Default value.
+     *
+     * This value is used if the option is set, but no value is provided.
+     * If the option is never set, the value set with defaultValue() is
+     * used.  Can only be used for options that accept a single value.
+     *
+     * \p defaultValue is copied when the option is created.
+     */
+    MyClass& defaultValueIfSet(const T& defaultValue)
+    {
+        defaultValueIfSet_ = &defaultValue;
+        return me();
+    }
+    /*! \brief
+     * Stores value(s) in memory pointed by \p store.
+     *
+     * \param[in] store  Storage for option value(s).
+     *
+     * The caller is responsible for allocating enough memory such that
+     * the any allowed number of values fits into the array pointed by
+     * \p store.  If there is no maximum allowed number or if the maximum
+     * is inconveniently large, storeVector() should be used.
+     *
+     * For information on when values are available in the storage, see
+     * storeVector().
+     *
+     * The pointer provided should remain valid as long as the associated
+     * Options object exists.
+     */
+    MyClass& store(T* store)
+    {
+        store_ = store;
+        return me();
+    }
+    /*! \brief
+     * Stores number of values in the value pointed by \p countptr.
+     *
+     * \param[in] countptr Storage for the number of values.
+     *
+     * For information on when values are available in the storage, see
+     * storeVector().
+     *
+     * The pointers provided should remain valid as long as the associated
+     * Options object exists.
+     */
+    MyClass& storeCount(int* countptr)
+    {
+        countptr_ = countptr;
+        return me();
+    }
+    /*! \brief
+     * Stores option values in the provided vector.
+     *
+     * \param[in] store  Vector to store option values in.
+     *
+     * Values are added to the vector after each successful set of values
+     * is parsed.  Note that for some options, the value may be changed
+     * later, and is only guaranteed to be correct after Options::finish()
+     * has been called.
+     *
+     * The pointer provided should remain valid as long as the associated
+     * Options object exists.
+     */
+    MyClass& storeVector(std::vector<T>* store)
+    {
+        storeVector_ = store;
+        return me();
+    }
+    /*! \brief
+     * Stores whether the option was explicitly set.
+     *
+     * \param[in] store  Variable to store the flag in.
+     *
+     * The value is set to `false` on creation of the option, and to `true`
+     * as soon as a value is assigned to the option.  A default value does
+     * not set the flag to `true`, but assignment that uses
+     * defaultValueIfSet() does.
+     *
+     * The pointer provided should remain valid as long as the associated
+     * Options object exists.
+     */
+    MyClass& storeIsSet(bool* store)
+    {
+        setStoreIsSet(store);
+        return me();
+    }
+
+protected:
+    /*! \cond libapi */
+    //! Alias for the template class for use in base classes.
+    typedef OptionTemplate<T, U> MyBase;
+
+    //! Initializes the name and default values for an option.
+    explicit OptionTemplate(const char* name) :
+        AbstractOption(name),
+        defaultValue_(nullptr),
+        defaultValueIfSet_(nullptr),
+        store_(nullptr),
+        countptr_(nullptr),
+        storeVector_(nullptr)
+    {
+    }
+
+    /*! \brief
+     * Returns a pointer to user-specified default value, or NULL if there
+     * is none.
+     */
+    const T* defaultValue() const { return defaultValue_; }
+    /*! \brief
+     * Returns a pointer to user-specified default value, or NULL if there
+     * is none.
+     */
+    const T* defaultValueIfSet() const { return defaultValueIfSet_; }
+    /*! \brief
+     * Returns a pointer to the storage location, or NULL if none specified.
+     */
+    T* store() const { return store_; }
+    /*! \brief
+     * Returns a pointer to the storage vector, or NULL if none specified.
+     */
+    std::vector<T>* storeVector() const { return storeVector_; }
+    //! Returns \p *this casted into MyClass to reduce typing.
+    MyClass& me() { return static_cast<MyClass&>(*this); }
+    //! \endcond
+
+private:
+    const T*        defaultValue_;
+    const T*        defaultValueIfSet_;
+    T*              store_;
+    int*            countptr_;
+    std::vector<T>* storeVector_;
+
+    /*! \brief
+     * Needed to initialize storage from this class without otherwise
+     * unnecessary accessors.
+     */
+    friend class OptionStorageTemplate<T>;
+};
+
+/*! \brief
+ * Gives information and allows modifications to an option after creation.
+ *
+ * When an option is added with Options::addOption(), an object of a subclass
+ * of OptionInfo is returned.  This object can be later used to access
+ * information about the option.  Non-const methods also allow later changing
+ * (some of) the option settings provided at initialization time.
+ * The properties accessible/modifiable through this interface are implemented
+ * based on need, and may not be implemented for all cases.
+ *
+ * \if libapi
+ * This class is also used by OptionsVisitor and OptionsModifyingVisitor as
+ * the interface that allows querying/modifying each visited option.
+ * \endif
+ *
+ * This class isolates the details of the internal option implementation from
+ * callers.  Although this class is a simple reference to the underlying
+ * implementation, it is implemented as non-copyable to allow const/non-const
+ * status of a reference to this class to indicate whether modifications are
+ * allowed.  Otherwise, separate classes would be needed for access and
+ * modification, complicating the implementation.  In the implementation,
+ * there is always a single OptionInfo instance referring to one option.
+ * The underlying implementation object always owns this instance, and only
+ * references are passed to callers.
+ *
+ * \see Options::addOption()
+ * \if libapi
+ * \see OptionsVisitor
+ * \see OptionsModifyingVisitor
+ * \endif
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class OptionInfo
+{
+public:
+    virtual ~OptionInfo();
+
+    /*! \brief
+     * Test whether the option is of a particular type.
+     *
+     * \tparam InfoType  Option type to test for. Should be a class derived
+     *      from OptionInfo.
+     */
+    template<class InfoType>
+    bool isType() const
+    {
+        return toType<InfoType>() != nullptr;
+    }
+    /*! \brief
+     * Convert the info object to a particular type if the type is correct.
+     *
+     * \tparam InfoType  Option type to convert to. Should be a class
+     *      derived from OptionInfo.
+     * \retval this converted to a pointer to \p InfoType, or NULL if the
+     *      conversion is not possible.
+     */
+    template<class InfoType>
+    InfoType* toType()
+    {
+        return dynamic_cast<InfoType*>(this);
+    }
+    //! \copydoc toType()
+    template<class InfoType>
+    const InfoType* toType() const
+    {
+        return dynamic_cast<const InfoType*>(this);
+    }
+
+    //! Returns true if the option has been set.
+    bool isSet() const;
+    //! Returns true if the option is a hidden option.
+    bool isHidden() const;
+    //! Returns true if the option is required.
+    bool isRequired() const;
+    //! Returns the minimum number of values that this option accepts.
+    int minValueCount() const;
+    //! Returns the maximum number of values that this option accepts.
+    int maxValueCount() const;
+    //! Returns the name of the option.
+    const std::string& name() const;
+    //! Returns the type of the option as a string.
+    std::string type() const;
+    //! Returns the description of the option.
+    std::string formatDescription() const;
+
+    /*! \brief
+     * Returns the default value(s) of the option.
+     *
+     * The returned values should all be of the same type, but returning
+     * each as a separate any is currently simpler.
+     *
+     * Currently, this can only be called before option values have been
+     * assigned.
+     */
+    std::vector<Any> defaultValues() const;
+    /*! \brief
+     * Returns the default value(s) of the option as strings.
+     *
+     * If there is no default value, but defaultValueIfSet() is set, that
+     * is returned instead.
+     *
+     * Currently, this can only be called before option values have been
+     * assigned.
+     */
+    std::vector<std::string> defaultValuesAsStrings() const;
+    /*! \brief
+     * Converts given values to native representation for this option.
+     *
+     * For example, strings are parsed to the type that is actually used to
+     * store the options.
+     *
+     * The return value only depends on the option type, not on the current
+     * value of the option, and the current value in the option is not
+     * changed.
+     */
+    std::vector<Any> normalizeValues(const std::vector<Any>& values) const;
+
+protected:
+    /*! \cond libapi */
+    /*! \brief
+     * Wraps a given option object.
+     *
+     * Does not throw.
+     */
+    explicit OptionInfo(AbstractOptionStorage* option);
+
+    //! Returns the wrapped option storage object.
+    AbstractOptionStorage& option() { return option_; }
+    //! Returns the wrapped option storage object.
+    const AbstractOptionStorage& option() const { return option_; }
+    //! \endcond
+
+private:
+    //! The wrapped option.
+    AbstractOptionStorage& option_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(OptionInfo);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/abstractoptionstorage.h b/src/include/gromacs/options/abstractoptionstorage.h
new file mode 100644 (file)
index 0000000..b999466
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::AbstractOptionStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_ABSTRACTOPTIONSTORAGE_H
+#define GMX_OPTIONS_ABSTRACTOPTIONSTORAGE_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/classhelpers.h"
+
+#include "optionflags.h"
+
+namespace gmx
+{
+
+class AbstractOption;
+class OptionInfo;
+class Options;
+class Any;
+
+/*! \libinternal \brief
+ * Abstract base class for converting, validating, and storing option values.
+ *
+ * This class should normally not be subclassed directly, but the
+ * OptionStorageTemplate should be used instead.  The templated class provides
+ * basic functionality for most of the pure virtual methods, and also
+ * integrates well with option setting objects derived from OptionTemplate.
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ *
+ * \internal
+ * This class really consists of two parts: the public interface that is
+ * used by the internal implementation of the options module, and the
+ * interface that derived classes use to provide type-dependent functionality.
+ * The latter consists of a few pure virtual methods, of which a few simple
+ * query methods are also part of the module-internal interface, others are
+ * protected and called by the non-virtual methods when needed.
+ * The reason why these two roles are in one class is twofold:
+ *  -# Both the derived classes and the internal module implementation may need
+ *     access to the same information like the allowed number of values and the
+ *     name of the option.
+ *  -# Having only one class is consistent with the structure used for options
+ *     settings objects: there is very direct correspondence between
+ *     AbstractOption and AbstractOptionStorage and between OptionTemplate and
+ *     OptionStorageTemplate.
+ */
+class AbstractOptionStorage
+{
+public:
+    virtual ~AbstractOptionStorage();
+
+    //! Returns true if the option has been set.
+    bool isSet() const { return hasFlag(efOption_Set); }
+    /*! \brief
+     * Returns true if the option is a boolean option.
+     *
+     * This is used to optionally support an alternative syntax where an
+     * option provided with no value sets the value to true and an
+     * option prefixed with "no" clears the value.
+     */
+    bool isBoolean() const;
+    //! Returns true if the option is a hidden option.
+    bool isHidden() const { return hasFlag(efOption_Hidden); }
+    //! Returns true if the option is required.
+    bool isRequired() const { return hasFlag(efOption_Required); }
+    //! Returns true if the option is vector-valued.
+    bool isVector() const { return hasFlag(efOption_Vector); }
+    //! Returns the name of the option.
+    const std::string& name() const { return name_; }
+    //! Returns the description of the option set by the calling code.
+    const std::string& description() const { return descr_; }
+
+    //! Returns true if defaultValueIfSet() value is specified.
+    bool defaultValueIfSetExists() const { return hasFlag(efOption_DefaultValueIfSetExists); }
+    //! Returns the minimum number of values required in one set.
+    int minValueCount() const { return minValueCount_; }
+    //! Returns the maximum allowed number of values in one set (-1 = no limit).
+    int maxValueCount() const { return maxValueCount_; }
+
+    /*! \brief
+     * Returns an option info object corresponding to this option.
+     */
+    virtual OptionInfo& optionInfo() = 0;
+    /*! \brief
+     * Returns a short string describing the type of the option.
+     */
+    virtual std::string typeString() const = 0;
+    /*! \brief
+     * Formats additional description for the option.
+     *
+     * If this method returns a non-empty string, it is appended to the
+     * plain description when printing help texts.
+     * The default implementation returns an empty string.
+     */
+    virtual std::string formatExtraDescription() const { return std::string(); }
+    /*! \brief
+     * Returns the number of option values added so far.
+     */
+    virtual int valueCount() const = 0;
+    //! \copydoc OptionInfo::defaultValues()
+    virtual std::vector<Any> defaultValues() const = 0;
+    //! \copydoc OptionInfo::defaultValuesAsStrings()
+    virtual std::vector<std::string> defaultValuesAsStrings() const = 0;
+    //! \copydoc OptionInfo::normalizeValues()
+    virtual std::vector<Any> normalizeValues(const std::vector<Any>& values) const = 0;
+
+    /*! \brief
+     * Starts adding values from a new source for the option.
+     *
+     * This marks the vurrent value of the option as a default value,
+     * causing next call to startSet() to clear it.  This allows values
+     * from the new source to overwrite old values.
+     *
+     * This method does not throw.
+     */
+    void startSource();
+    /*! \brief
+     * Starts adding a new set of values for the option.
+     *
+     * \throws  InvalidInputError if option is specified multiple times,
+     *      but is not specified to accept it.
+     *
+     * If the parameter is specified multiple times, startSet() should be
+     * called before the values for each instance.
+     *
+     * Strong exception safety guarantee.
+     */
+    void startSet();
+    /*! \brief
+     * Adds a new value for the option.
+     *
+     * \param[in] value  Value to convert.
+     * \throws  InvalidInputError if value cannot be converted, or
+     *      if there are too many values.
+     *
+     * This method should only be called between startSet() and
+     * finishSet().
+     */
+    void appendValue(const Any& value);
+    /*! \brief
+     * Performs validation and/or actions once a set of values has been
+     * added.
+     *
+     * \throws  InvalidInputError if too few values have been provided, or
+     *      if the valid values since previous startSet() are invalid as a
+     *      set.
+     *
+     * If the parameter is specified multiple times, finishSet() should be
+     * called after the values for each instance.
+     */
+    void finishSet();
+    /*! \brief
+     * Performs validation and/or actions once all values have been added.
+     *
+     * \throws InvalidInputError if the option is required but not set, or
+     *      if all valid values together are invalid as a set.
+     *
+     * This method should be called after all values have been provided
+     * with appendValue().
+     */
+    void finish();
+
+protected:
+    /*! \brief
+     * Initializes the storage object from the settings object.
+     *
+     * \param[in] settings  Option settings.
+     * \param[in] staticFlags Option flags that are always set and specify
+     *      generic behavior of the option.
+     * \throws  APIError if invalid settings have been provided.
+     */
+    AbstractOptionStorage(const AbstractOption& settings, OptionFlags staticFlags);
+
+    //! Marks the option as set.
+    void markAsSet();
+    //! Returns true if the given flag is set.
+    bool hasFlag(OptionFlag flag) const { return flags_.test(flag); }
+    //! Sets the given flag.
+    void setFlag(OptionFlag flag) { return flags_.set(flag); }
+    //! Clears the given flag.
+    void clearFlag(OptionFlag flag) { return flags_.clear(flag); }
+
+    /*! \brief
+     * Sets a new minimum number of values required in one set.
+     *
+     * \param[in] count  New minimum number of values (must be > 0).
+     * \throws InvalidInputError if already provided values violate the limit.
+     *
+     * If values have already been provided, it is checked that there are
+     * enough.
+     *
+     * Cannot be called for options with ::efOption_MultipleTimes set,
+     * because it is impossible to check the requirement after the values
+     * have been set.
+     * If attempted, will assert.
+     */
+    void setMinValueCount(int count);
+    /*! \brief
+     * Sets a new maximum number of values required in one set.
+     *
+     * \param[in] count  New maximum number of values
+     *                   (must be > 0, or -1 for no limit).
+     * \throws InvalidInputError if already provided values violate the limit.
+     *
+     * If values have already been provided, it is checked that there are
+     * not too many.
+     *
+     * Cannot be called for options with ::efOption_MultipleTimes set,
+     * because it is impossible to check the requirement after the values
+     * have been set.
+     * If attempted, will assert.
+     */
+    void setMaxValueCount(int count);
+
+    /*! \brief
+     * Removes all values from temporary storage for a set.
+     *
+     * This function is always called before starting to add values to
+     * a set, allowing the storage to clear its internal buffers.
+     *
+     * Should not throw.
+     */
+    virtual void clearSet() = 0;
+    /*! \brief
+     * Adds a new value.
+     *
+     * \param[in] value  Value to convert.
+     * \throws  InvalidInputError if \p value is not valid for this option
+     *      or if there have been too many values in the set.
+     *
+     * This method may be called multiple times if the underlying
+     * option is defined to accept multiple values.
+     *
+     * \see OptionStorageTemplate::convertValue()
+     */
+    virtual void convertValue(const Any& value) = 0;
+    /*! \brief
+     * Performs validation and/or actions once a set of values has been
+     * added.
+     *
+     * \throws  InvalidInputError if the values in the set are not valid
+     *      as a whole.
+     *
+     * This method may be called multiple times if the underlying option
+     * can be specified multiple times.
+     * This method is not currently called if one of the convertValue()
+     * calls throwed.
+     *
+     * \todo
+     * Improve the call semantics.
+     *
+     * \see OptionStorageTemplate::processSetValues()
+     */
+    virtual void processSet() = 0;
+    /*! \brief
+     * Performs validation and/or actions once all values have been added.
+     *
+     * \throws  InvalidInputError if all provided values are not valid as
+     *      a set.
+     *
+     * This method is always called once.
+     *
+     * If the method throws, implementation should take care to leave the
+     * option in a consistent, meaningful state.  However, currently none
+     * of the implementations actually throw in any situation where the
+     * option may be left in an inconsistent state.
+     */
+    virtual void processAll() = 0;
+
+private:
+    std::string name_;
+    std::string descr_;
+    //! Flags for the option.
+    OptionFlags flags_;
+    bool*       storeIsSet_;
+    //! Minimum number of values required (in one set).
+    int minValueCount_;
+    //! Maximum allowed number of values (in one set), or -1 if no limit.
+    int maxValueCount_;
+    //! Whether we are currently assigning values to a set.
+    bool bInSet_;
+    //! Whether there were errors in set values.
+    bool bSetValuesHadErrors_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(AbstractOptionStorage);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/abstractsection.h b/src/include/gromacs/options/abstractsection.h
new file mode 100644 (file)
index 0000000..d1f3f98
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares base classes for declaring option sections.
+ *
+ * This header defines base classes for option section settings that are used
+ * with IOptionsContainerWithSections::addSection().  These classes implement
+ * the "named parameter" idiom for specifying section properties.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_ABSTRACTSECTION_H
+#define GMX_OPTIONS_ABSTRACTSECTION_H
+
+#include <memory>
+
+#include "gromacs/options/ioptionscontainerwithsections.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "isectionstorage.h"
+
+namespace gmx
+{
+
+class IOptionSectionStorage;
+
+namespace internal
+{
+class OptionSectionImpl;
+}
+
+/*! \brief
+ * Base class for specifying option section properties.
+ *
+ * \ingroup module_options
+ */
+class AbstractOptionSection
+{
+protected:
+    //! \cond libapi
+    //! Initializes option properties with the given name.
+    explicit AbstractOptionSection(const char* name) : name_(name) {}
+    virtual ~AbstractOptionSection() {}
+    /*! \brief
+     * Creates a storage object corresponding to this section.
+     *
+     * Similar to AbstractOption::createStorage().
+     */
+    virtual std::unique_ptr<IOptionSectionStorage> createStorage() const = 0;
+    //! \endcond
+
+private:
+    const char* name_;
+
+    friend class internal::OptionSectionImpl;
+};
+
+/*! \brief
+ * Base class for handles to option sections.
+ *
+ * This class implements the common functionality for adding options and
+ * subsections to option sections.
+ *
+ * \ingroup module_options
+ */
+class AbstractOptionSectionHandle : public IOptionsContainerWithSections
+{
+public:
+    // From IOptionsContainer
+    //! \copydoc IOptionsContainer::addGroup()
+    IOptionsContainer& addGroup() override;
+
+protected:
+    //! \cond libapi
+    /*! \brief
+     * Returns the storage for a particular type of section.
+     *
+     * This is intended for use in derived class constructors, where the
+     * handle needs access to the actual storage.  The handle should know
+     * the type of storage created for the section type it deals with, so
+     * the cast should always be successful.
+     */
+    template<typename StorageType>
+    static StorageType* getStorage(internal::OptionSectionImpl* section)
+    {
+        IOptionSectionStorage* storage      = getStorage(section);
+        StorageType*           typedStorage = dynamic_cast<StorageType*>(storage);
+        GMX_ASSERT(typedStorage != nullptr, "Mismatching section storage type");
+        return typedStorage;
+    }
+
+    //! Wraps a given section storage object.
+    explicit AbstractOptionSectionHandle(internal::OptionSectionImpl* section) : section_(section)
+    {
+    }
+    //! \endcond
+
+private:
+    // From IOptionsContainerWithSections
+    internal::OptionSectionImpl* addSectionImpl(const AbstractOptionSection& section) override;
+    // From IOptionsContainer
+    OptionInfo* addOptionImpl(const AbstractOption& settings) override;
+
+    /*! \brief
+     * Implementation helper for the template method.
+     *
+     * This allows encapsulating the implementation within the source file.
+     */
+    static IOptionSectionStorage* getStorage(internal::OptionSectionImpl* section);
+
+    internal::OptionSectionImpl* section_;
+};
+
+class AbstractOptionSectionInfo
+{
+public:
+    //! Wraps a given section storage object.
+    explicit AbstractOptionSectionInfo(internal::OptionSectionImpl* section) : section_(*section) {}
+
+    //! Returns the name of the section.
+    const std::string& name() const;
+
+    //! Returns the wrapped section storage object.
+    internal::OptionSectionImpl& section() { return section_; }
+    //! Returns the wrapped section storage object.
+    const internal::OptionSectionImpl& section() const { return section_; }
+
+private:
+    internal::OptionSectionImpl& section_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(AbstractOptionSectionInfo);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/basicoptions.h b/src/include/gromacs/options/basicoptions.h
new file mode 100644 (file)
index 0000000..20b3447
--- /dev/null
@@ -0,0 +1,861 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010-2018, The GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares option objects for basic option types.
+ *
+ * Together with options.h, this header forms the part of the public API
+ * that most classes will use to provide options.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_BASICOPTIONS_H
+#define GMX_OPTIONS_BASICOPTIONS_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/options/abstractoption.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "ivaluestore.h"
+
+namespace gmx
+{
+
+class BooleanOptionInfo;
+class BooleanOptionStorage;
+class IntegerOptionInfo;
+class IntegerOptionStorage;
+class Int64OptionInfo;
+class Int64OptionStorage;
+class DoubleOptionInfo;
+class DoubleOptionStorage;
+class FloatOptionInfo;
+class FloatOptionStorage;
+class StringOptionInfo;
+class StringOptionStorage;
+class EnumOptionInfo;
+class EnumOptionStorage;
+
+//! \addtogroup module_options
+//! \{
+
+/*! \brief
+ * Specifies an option that provides boolean values.
+ *
+ * Example:
+ * \code
+   bool  bPBC;
+   using gmx::BooleanOption;
+   options.addOption(BooleanOption("pbc").store(&bPBC));
+ * \endcode
+ *
+ * Public methods in this class do not throw.
+ *
+ * \inpublicapi
+ */
+class BooleanOption : public OptionTemplate<bool, BooleanOption>
+{
+public:
+    //! OptionInfo subclass corresponding to this option type.
+    typedef BooleanOptionInfo InfoType;
+
+    //! Initializes an option with the given name.
+    explicit BooleanOption(const char* name) : MyBase(name) {}
+
+private:
+    //! Creates a BooleanOptionStorage object.
+    AbstractOptionStorage* createStorage(const OptionManagerContainer& managers) const override;
+};
+
+/*! \brief
+ * Specifies an option that provides integer values.
+ *
+ * Examples:
+ * \code
+   using gmx::IntegerOption;
+   // Simple option
+   int  rcut = 0;
+   options.addOption(IntegerOption("rcut").store(&rcut));
+   // Vector-valued option
+   int  box[3] = {1, 1, 1};  // Default value
+   options.addOption(IntegerOption("box").store(box).vector());
+ * \endcode
+ *
+ * Public methods in this class do not throw.
+ *
+ * \inpublicapi
+ */
+class IntegerOption : public OptionTemplate<int, IntegerOption>
+{
+public:
+    //! OptionInfo subclass corresponding to this option type.
+    typedef IntegerOptionInfo InfoType;
+
+    //! Initializes an option with the given name.
+    explicit IntegerOption(const char* name) : MyBase(name) {}
+
+    /*! \brief
+     * Sets the option to return a vector value.
+     *
+     * A vector value returns a fixed number of values, the default being
+     * three (can be changed with valueCount()).  However, it also accepts
+     * a single value, in which case the value is used to fill the whole
+     * vector.
+     */
+    MyClass& vector()
+    {
+        setVector();
+        return me();
+    }
+
+private:
+    //! Creates an IntegerOptionStorage object.
+    AbstractOptionStorage* createStorage(const OptionManagerContainer& managers) const override;
+
+    /*! \brief
+     * Needed to initialize IntegerOptionStorage from this class without
+     * otherwise unnecessary accessors.
+     */
+    friend class IntegerOptionStorage;
+};
+
+/*! \brief
+ * Specifies an option that provides 64-bit integer values.
+ *
+ * Public methods in this class do not throw.
+ *
+ * \see IntegerOption
+ *
+ * \inpublicapi
+ */
+class Int64Option : public OptionTemplate<int64_t, Int64Option>
+{
+public:
+    //! OptionInfo subclass corresponding to this option type.
+    typedef Int64OptionInfo InfoType;
+
+    //! Initializes an option with the given name.
+    explicit Int64Option(const char* name) : MyBase(name) {}
+
+private:
+    //! Creates an Int64OptionStorage object.
+    AbstractOptionStorage* createStorage(const OptionManagerContainer& managers) const override;
+
+    /*! \brief
+     * Needed to initialize Int64OptionStorage from this class without
+     * otherwise unnecessary accessors.
+     */
+    friend class Int64OptionStorage;
+};
+
+/*! \brief
+ * Specifies an option that provides floating-point (double) values.
+ *
+ * Public methods in this class do not throw.
+ *
+ * \inpublicapi
+ */
+class DoubleOption : public OptionTemplate<double, DoubleOption>
+{
+public:
+    //! OptionInfo subclass corresponding to this option type.
+    typedef DoubleOptionInfo InfoType;
+
+    //! Initializes an option with the given name.
+    explicit DoubleOption(const char* name) : MyBase(name), bTime_(false) {}
+
+    //! \copydoc IntegerOption::vector()
+    MyClass& vector()
+    {
+        setVector();
+        return me();
+    }
+    /*! \brief
+     * Marks this option as providing a time value whose unit can be changed.
+     *
+     * By itself, this option does nothing.  It marks the option as a time
+     * value such that TimeUnitManager::scaleTimeOptions() can process it.
+     * In typical cases, \Gromacs scales the time options just before
+     * Options::finish() has been called, so the option value is only
+     * available after all option values have been processed.
+     * All values in the program are in ps (including any default value);
+     * user-provided values are scaled according to the time unit set in
+     * TimeUnitManager.
+     */
+    MyClass& timeValue()
+    {
+        bTime_ = true;
+        return me();
+    }
+
+private:
+    //! Creates a DoubleOptionStorage object.
+    AbstractOptionStorage* createStorage(const OptionManagerContainer& managers) const override;
+
+    bool bTime_;
+
+    /*! \brief
+     * Needed to initialize DoubleOptionStorage from this class without
+     * otherwise unnecessary accessors.
+     */
+    friend class DoubleOptionStorage;
+};
+
+/*! \brief
+ * Specifies an option that provides floating-point (float) values.
+ *
+ * Public methods in this class do not throw.
+ *
+ * \see DoubleOption
+ *
+ * \inpublicapi
+ */
+class FloatOption : public OptionTemplate<float, FloatOption>
+{
+public:
+    //! OptionInfo subclass corresponding to this option type.
+    typedef FloatOptionInfo InfoType;
+
+    //! Initializes an option with the given name.
+    explicit FloatOption(const char* name) : MyBase(name), bTime_(false) {}
+
+    //! \copydoc IntegerOption::vector()
+    MyClass& vector()
+    {
+        setVector();
+        return me();
+    }
+    //! \copydoc DoubleOption::timeValue()
+    MyClass& timeValue()
+    {
+        bTime_ = true;
+        return me();
+    }
+
+private:
+    //! Creates a FloatOptionStorage object.
+    AbstractOptionStorage* createStorage(const OptionManagerContainer& managers) const override;
+
+    bool bTime_;
+
+    /*! \brief
+     * Needed to initialize FloatOptionStorage from this class without
+     * otherwise unnecessary accessors.
+     */
+    friend class FloatOptionStorage;
+};
+
+/*! \brief
+ * Specifies an option that provides string values.
+ *
+ * Examples:
+ * \code
+   using gmx::StringOption;
+   // Simple option
+   std::string  str;
+   options.addOption(StringOption("str").store(&str));
+   // Option that only accepts predefined values
+   const char * const  allowed[] = { "atom", "residue", "molecule" };
+   std::string  str;
+   options.addOption(StringOption("type").enumValue(allowed).store(&str));
+ * \endcode
+ *
+ * Public methods in this class do not throw.
+ *
+ * \inpublicapi
+ */
+class StringOption : public OptionTemplate<std::string, StringOption>
+{
+public:
+    //! OptionInfo subclass corresponding to this option type.
+    typedef StringOptionInfo InfoType;
+
+    //! Initializes an option with the given name.
+    explicit StringOption(const char* name) :
+        MyBase(name), enumValues_(nullptr), enumValuesCount_(0), defaultEnumIndex_(-1)
+    {
+    }
+
+    /*! \brief
+     * Sets the option to only accept one of a fixed set of strings.
+     *
+     * \param[in] values  Array of strings to accept.
+     *
+     * Also accepts prefixes of the strings; if a prefix matches more than
+     * one of the possible strings, the shortest one is used (in a tie, the
+     * first one is).
+     *
+     * The strings are copied once the option is created.
+     */
+    template<size_t count>
+    MyClass& enumValue(const char* const (&values)[count])
+    {
+        GMX_ASSERT(enumValues_ == nullptr, "Multiple sets of enumerated values specified");
+        enumValues_      = values;
+        enumValuesCount_ = count;
+        return me();
+    }
+    /*! \brief
+     * Sets the option to only accept one of a fixed set of strings.
+     *
+     * \param[in] values  Array of strings to accept, with a NULL pointer
+     *      following the last string.
+     *
+     * Works otherwise as the array version, but accepts a pointer to
+     * an array of undetermined length.  The end of the array is indicated
+     * by a NULL pointer in the array.
+     *
+     * \see enumValue()
+     */
+    MyClass& enumValueFromNullTerminatedArray(const char* const* values)
+    {
+        GMX_ASSERT(enumValues_ == nullptr, "Multiple sets of enumerated values specified");
+        enumValues_      = values;
+        enumValuesCount_ = -1;
+        return me();
+    }
+    /*! \brief
+     * Sets the default value using an index into the enumeration table.
+     *
+     * Cannot be specified without enumValue().
+     */
+    MyClass& defaultEnumIndex(int index)
+    {
+        GMX_ASSERT(index >= 0, "Invalid enumeration index");
+        defaultEnumIndex_ = index;
+        return me();
+    }
+
+private:
+    //! Creates a StringOptionStorage object.
+    AbstractOptionStorage* createStorage(const OptionManagerContainer& managers) const override;
+
+    const char* const* enumValues_;
+    int                enumValuesCount_;
+    int                defaultEnumIndex_;
+
+    /*! \brief
+     * Needed to initialize StringOptionStorage from this class without
+     * otherwise unnecessary accessors.
+     */
+    friend class StringOptionStorage;
+};
+
+//! \}
+
+namespace internal
+{
+
+/*! \internal
+ * \brief
+ * Type-specific implementation for IOptionValueStore for an enum option.
+ *
+ * This class is instantiated for each enum type for which EnumOption is used,
+ * and takes care of managing `int`-to-`enum` conversions.  Having this part in
+ * the header allows the actual storage implementation to not be in the header,
+ * which would require exposing all the internals through this one header...
+ *
+ * \ingroup module_options
+ */
+template<typename EnumType>
+class EnumIndexStore : public IOptionValueStore<int>
+{
+public:
+    //! Initializes the storage for the given actual enum variables.
+    EnumIndexStore(EnumType* store, std::vector<EnumType>* storeVector) :
+        store_(store), storeVector_(storeVector)
+    {
+        if (storeVector_ != nullptr)
+        {
+            for (EnumType value : *storeVector_)
+            {
+                intStore_.push_back(static_cast<int>(value));
+            }
+        }
+        else if (store_ != nullptr)
+        {
+            // TODO: Copy more than one value if that would make sense.
+            intStore_.push_back(static_cast<int>(store_[0]));
+        }
+    }
+
+    int           valueCount() override { return ssize(intStore_); }
+    ArrayRef<int> values() override { return intStore_; }
+    void          clear() override
+    {
+        intStore_.clear();
+        if (storeVector_ != nullptr)
+        {
+            storeVector_->clear();
+        }
+    }
+    void reserve(size_t count) override
+    {
+        intStore_.reserve(intStore_.size() + count);
+        if (storeVector_ != nullptr)
+        {
+            storeVector_->reserve(storeVector_->size() + count);
+        }
+    }
+    void append(const int& value) override
+    {
+        const size_t count = intStore_.size();
+        intStore_.push_back(value);
+        if (store_ != nullptr)
+        {
+            store_[count] = static_cast<EnumType>(value);
+        }
+        if (storeVector_ != nullptr)
+        {
+            storeVector_->push_back(static_cast<EnumType>(value));
+        }
+    }
+
+private:
+    //! Stores the integer values for values().
+    std::vector<int>       intStore_;
+    EnumType*              store_;
+    std::vector<EnumType>* storeVector_;
+};
+
+//! \cond internal
+/*! \internal
+ * \brief
+ * Helper to create EnumOptionStorage instances.
+ *
+ * This function works as a proxy between EnumOption::createStorage() and the
+ * EnumOptionStorage constructor, such that the latter does not need to be
+ * exposed in the header.
+ *
+ * \ingroup module_options
+ */
+AbstractOptionStorage* createEnumOptionStorage(const AbstractOption& option,
+                                               const char* const*    enumValues,
+                                               int                   count,
+                                               int                   defaultValue,
+                                               int                   defaultValueIfSet,
+                                               std::unique_ptr<IOptionValueStore<int>> store);
+//! \endcond
+
+} // namespace internal
+
+//! \addtogroup module_options
+//! \{
+
+/*! \brief
+* Specifies an option that accepts an EnumerationArray of string values and writes the
+* selected index into an `enum` variable.
+*
+* \tparam EnumType  DataType of the variable that receives the values
+*
+* Examples:
+* \code
+  enum class MyEnum { Atom, Res, Mol, Count } : int;
+  EnumerationArray<MyEnum, const char *> myEnumNames = { "atom", "residue", "molecule" };
+  MyEnum       value = MyEnum::Atom; // default value
+  options.addOption(EnumOption<MyEnum>("type").enumValue(myEnumNames).store(&value));
+* \endcode
+*
+* storeCount() is not currently implemented for this option type, and
+* providing multiple default values through an array passed to store() does
+* not work consistently in all cases.
+* In the current implementation, the values of the enum type should correspond
+* to indices in the array passed to enumValue(), i.e., be consecutive
+* starting from zero.  Only values corresponding to valid indices are accepted
+* as parameters to, e.g., defaultValue().  However, other values can be used
+* as the initial value of the variable (`value` in the above example), and
+* those will be preserved if the option is not set.
+*
+* Public methods in this class do not throw.
+*
+* \inpublicapi
+*/
+template<typename EnumType>
+class EnumOption : public OptionTemplate<EnumType, EnumOption<EnumType>>
+{
+public:
+    //! OptionInfo subclass corresponding to this option type.
+    typedef EnumOptionInfo InfoType;
+
+    // This needs to be duplicated from OptionTemplate because this class
+    // is a template.
+    //! Short-hand for the base class.
+    typedef OptionTemplate<EnumType, EnumOption<EnumType>> MyBase;
+
+    //! Initializes an option with the given name.
+    explicit EnumOption(const char* name) : MyBase(name), enumValues_(nullptr), enumValuesCount_(0)
+    {
+    }
+
+    /*! \brief
+     * Sets the option to only accept one of a fixed set of strings.
+     *
+
+     * \param[in] values  Array of strings to accept.
+     *
+     * Also accepts prefixes of the strings; if a prefix matches more than
+     * one of the possible strings, the shortest one is used (in a tie, the
+     * first one is).
+     *
+     * The strings are copied once the option is created.
+     */
+    EnumOption& enumValue(const EnumerationArray<EnumType, const char*>& values)
+    {
+        GMX_ASSERT(enumValues_ == nullptr, "Multiple sets of enumerated values specified");
+        enumValues_      = values.data();
+        enumValuesCount_ = values.size();
+        return MyBase::me();
+    }
+
+private:
+    //! Helper function to convert default values for storage initialization.
+    static int convertToInt(const EnumType* defaultValue)
+    {
+        return defaultValue != nullptr ? static_cast<int>(*defaultValue) : -1;
+    }
+
+    //! Creates a EnumOptionStorage object.
+    AbstractOptionStorage* createStorage(const OptionManagerContainer& /*managers*/) const override
+    {
+        // TODO: Implement storeCount() if necessary.
+        return internal::createEnumOptionStorage(*this,
+                                                 enumValues_,
+                                                 enumValuesCount_,
+                                                 convertToInt(MyBase::defaultValue()),
+                                                 convertToInt(MyBase::defaultValueIfSet()),
+                                                 std::make_unique<internal::EnumIndexStore<EnumType>>(
+                                                         MyBase::store(), MyBase::storeVector()));
+    }
+
+    const char* const* enumValues_;
+    int                enumValuesCount_;
+
+    /*! \brief
+     * Needed to initialize EnumOptionStorage from this class without
+     * otherwise unnecessary accessors.
+     */
+    friend class EnumOptionStorage;
+};
+
+/*! \brief
+* Specifies an option that accepts enumerated string values and writes the
+* selected index into an `enum` variable.
+*
+* \tparam EnumType  Type of the variable that receives the values
+*     (can also be `int`).
+*
+* Examples:
+* \code
+  enum MyEnum { eAtom, eRes, eMol };
+  using gmx::LegacyEnumOption;
+  const char * const  allowed[] = { "atom", "residue", "molecule" };
+  MyEnum       value = eAtom; // default value
+  options.addOption(LegacyEnumOption<MyEnum>("type").enumValue(allowed).store(&value));
+* \endcode
+*
+* Works exactly as EnumOption.
+*
+* This is legacy support for pargsToOptions and can be removed when it
+* is removed.  No new uses of it should be made.
+*
+* Public methods in this class do not throw.
+*
+* \inpublicapi
+*/
+template<typename EnumType>
+class LegacyEnumOption : public OptionTemplate<EnumType, LegacyEnumOption<EnumType>>
+{
+public:
+    //! OptionInfo subclass corresponding to this option type.
+    typedef EnumOptionInfo InfoType;
+
+    // This needs to be duplicated from OptionTemplate because this class
+    // is a template.
+    //! Short-hand for the base class.
+    typedef OptionTemplate<EnumType, LegacyEnumOption<EnumType>> MyBase;
+
+    //! Initializes an option with the given name.
+    explicit LegacyEnumOption(const char* name) :
+        MyBase(name), enumValues_(nullptr), enumValuesCount_(0)
+    {
+    }
+
+    /*! \brief
+     * Sets the option to only accept one of a fixed set of strings.
+     *
+     * \param[in] values  Array of strings to accept.
+     *
+     * Also accepts prefixes of the strings; if a prefix matches more than
+     * one of the possible strings, the shortest one is used (in a tie, the
+     * first one is).
+     *
+     * The strings are copied once the option is created.
+     */
+    template<size_t count>
+    LegacyEnumOption& enumValue(const char* const (&values)[count])
+    {
+        GMX_ASSERT(enumValues_ == nullptr, "Multiple sets of enumerated values specified");
+        enumValues_      = values;
+        enumValuesCount_ = count;
+        return MyBase::me();
+    }
+    /*! \brief
+     * Sets the option to only accept one of a fixed set of strings.
+     *
+     * \param[in] values  Array of strings to accept, with a NULL pointer
+     *      following the last string.
+     *
+     * Works otherwise as the array version, but accepts a pointer to
+     * an array of undetermined length.  The end of the array is indicated
+     * by a NULL pointer in the array.
+     *
+     * \see enumValue()
+     */
+    LegacyEnumOption& enumValueFromNullTerminatedArray(const char* const* values)
+    {
+        GMX_ASSERT(enumValues_ == nullptr, "Multiple sets of enumerated values specified");
+        enumValues_      = values;
+        enumValuesCount_ = -1;
+        return MyBase::me();
+    }
+
+private:
+    //! Helper function to convert default values for storage initialization.
+    static int convertToInt(const EnumType* defaultValue)
+    {
+        return defaultValue != nullptr ? static_cast<int>(*defaultValue) : -1;
+    }
+
+    //! Creates a EnumOptionStorage object.
+    AbstractOptionStorage* createStorage(const OptionManagerContainer& /*managers*/) const override
+    {
+        // TODO: Implement storeCount() if necessary.
+        return internal::createEnumOptionStorage(*this,
+                                                 enumValues_,
+                                                 enumValuesCount_,
+                                                 convertToInt(MyBase::defaultValue()),
+                                                 convertToInt(MyBase::defaultValueIfSet()),
+                                                 std::make_unique<internal::EnumIndexStore<EnumType>>(
+                                                         MyBase::store(), MyBase::storeVector()));
+    }
+
+    const char* const* enumValues_;
+    int                enumValuesCount_;
+
+    /*! \brief
+     * Needed to initialize EnumOptionStorage from this class without
+     * otherwise unnecessary accessors.
+     */
+    friend class EnumOptionStorage;
+};
+
+/*! \brief
+ * Wrapper class for accessing boolean option information.
+ *
+ * \inpublicapi
+ */
+class BooleanOptionInfo : public OptionInfo
+{
+public:
+    //! Creates an option info object for the given option.
+    explicit BooleanOptionInfo(BooleanOptionStorage* option);
+
+    //! Returns the default value for this option.
+    bool defaultValue() const;
+
+private:
+    const BooleanOptionStorage& option() const;
+};
+
+/*! \brief
+ * Wrapper class for accessing integer option information.
+ *
+ * \inpublicapi
+ */
+class IntegerOptionInfo : public OptionInfo
+{
+public:
+    //! Creates an option info object for the given option.
+    explicit IntegerOptionInfo(IntegerOptionStorage* option);
+};
+
+/*! \brief
+ * Wrapper class for accessing 64-bit integer option information.
+ *
+ * \inpublicapi
+ */
+class Int64OptionInfo : public OptionInfo
+{
+public:
+    //! Creates an option info object for the given option.
+    explicit Int64OptionInfo(Int64OptionStorage* option);
+};
+
+/*! \brief
+ * Wrapper class for accessing floating-point option information.
+ *
+ * \inpublicapi
+ */
+class DoubleOptionInfo : public OptionInfo
+{
+public:
+    //! Creates an option info object for the given option.
+    explicit DoubleOptionInfo(DoubleOptionStorage* option);
+
+    //! Whether the option specifies a time value.
+    bool isTime() const;
+
+    /*! \brief
+     * Sets a scale factor for user-provided values.
+     *
+     * Any user-provided value is scaled by the provided factor.
+     * Programmatically set default values are not scaled.
+     * If called multiple times, later calls override the previously set
+     * value.  In other words, the scaling is not cumulative.
+     */
+    void setScaleFactor(double factor);
+
+private:
+    DoubleOptionStorage&       option();
+    const DoubleOptionStorage& option() const;
+};
+
+/*! \brief
+ * Wrapper class for accessing floating-point option information.
+ *
+ * \inpublicapi
+ */
+class FloatOptionInfo : public OptionInfo
+{
+public:
+    //! Creates an option info object for the given option.
+    explicit FloatOptionInfo(FloatOptionStorage* option);
+
+    //! Whether the option specifies a time value.
+    bool isTime() const;
+
+    //! \copydoc DoubleOptionInfo::setScaleFactor()
+    void setScaleFactor(double factor);
+
+private:
+    FloatOptionStorage&       option();
+    const FloatOptionStorage& option() const;
+};
+
+/*! \brief
+ * Wrapper class for accessing string option information.
+ *
+ * \inpublicapi
+ */
+class StringOptionInfo : public OptionInfo
+{
+public:
+    //! Creates an option info object for the given option.
+    explicit StringOptionInfo(StringOptionStorage* option);
+
+    /*! \brief
+     * Whether this option accepts an enumerated set of values.
+     *
+     * Returns true if StringOption::enumValues() was used when creating
+     * this option.
+     */
+    bool isEnumerated() const;
+    /*! \brief
+     * Returns the set of allowed values for this option.
+     *
+     * Returns an empty vector if isEnumerated() returns false.
+     */
+    const std::vector<std::string>& allowedValues() const;
+
+private:
+    const StringOptionStorage& option() const;
+};
+
+/*! \brief
+ * Wrapper class for accessing enum option information.
+ *
+ * \inpublicapi
+ */
+class EnumOptionInfo : public OptionInfo
+{
+public:
+    //! Creates an option info object for the given option.
+    explicit EnumOptionInfo(EnumOptionStorage* option);
+
+    /*! \brief
+     * Returns the set of allowed values for this option.
+     */
+    const std::vector<std::string>& allowedValues() const;
+
+private:
+    const EnumOptionStorage& option() const;
+};
+
+/*! \typedef RealOption
+ * \brief
+ * Typedef for either DoubleOption or FloatOption, depending on precision.
+ *
+ * Generally, new would be better using DoubleOption, but this is provided for
+ * cases where the output value needs to be of type `real` for some reason.
+ */
+/*! \typedef RealOptionInfo
+ * \brief
+ * Typedef for either DoubleOptionInfo or FloatOptionInfo, depending on precision.
+ *
+ * Generally, new would be better using DoubleOption, but this is provided for
+ * cases where the output value needs to be of type `real` for some reason.
+ */
+#if GMX_DOUBLE
+typedef DoubleOption     RealOption;
+typedef DoubleOptionInfo RealOptionInfo;
+#else
+typedef FloatOption     RealOption;
+typedef FloatOptionInfo RealOptionInfo;
+#endif
+
+//! \}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/basicoptionstorage.h b/src/include/gromacs/options/basicoptionstorage.h
new file mode 100644 (file)
index 0000000..8c0df59
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010-2018, The GROMACS development team.
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares storage classes for basic option types.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_BASICOPTIONSTORAGE_H
+#define GMX_OPTIONS_BASICOPTIONSTORAGE_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/options/basicoptions.h"
+#include "gromacs/options/optionstoragetemplate.h"
+
+namespace gmx
+{
+
+/*! \addtogroup module_options
+ * \{
+ */
+
+/*! \internal \brief
+ * Converts, validates, and stores boolean values.
+ */
+class BooleanOptionStorage : public OptionStorageTemplateSimple<bool>
+{
+public:
+    /*! \brief
+     * Initializes the storage from option settings.
+     *
+     * \param[in] settings   Storage settings.
+     */
+    explicit BooleanOptionStorage(const BooleanOption& settings) : MyBase(settings), info_(this) {}
+
+    OptionInfo& optionInfo() override { return info_; }
+    std::string typeString() const override { return "bool"; }
+    std::string formatSingleValue(const bool& value) const override;
+
+    //! \copydoc BooleanOptionInfo::defaultValue()
+    bool defaultValue() const { return valueCount() > 0 && values()[0]; }
+
+private:
+    void initConverter(ConverterType* converter) override;
+
+    BooleanOptionInfo info_;
+};
+
+/*! \internal \brief
+ * Converts, validates, and stores integer values.
+ */
+class IntegerOptionStorage : public OptionStorageTemplateSimple<int>
+{
+public:
+    //! \copydoc BooleanOptionStorage::BooleanOptionStorage()
+    explicit IntegerOptionStorage(const IntegerOption& settings) : MyBase(settings), info_(this) {}
+
+    OptionInfo& optionInfo() override { return info_; }
+    std::string typeString() const override { return isVector() ? "vector" : "int"; }
+    std::string formatSingleValue(const int& value) const override;
+
+private:
+    void initConverter(ConverterType* converter) override;
+    void processSetValues(ValueList* values) override;
+
+    IntegerOptionInfo info_;
+};
+
+/*! \internal \brief
+ * Converts, validates, and stores integer values.
+ */
+class Int64OptionStorage : public OptionStorageTemplateSimple<int64_t>
+{
+public:
+    //! \copydoc BooleanOptionStorage::BooleanOptionStorage()
+    explicit Int64OptionStorage(const Int64Option& settings) : MyBase(settings), info_(this) {}
+
+    OptionInfo& optionInfo() override { return info_; }
+    std::string typeString() const override { return "int"; }
+    std::string formatSingleValue(const int64_t& value) const override;
+
+private:
+    void initConverter(ConverterType* converter) override;
+
+    Int64OptionInfo info_;
+};
+
+/*! \internal \brief
+ * Converts, validates, and stores floating-point (double) values.
+ */
+class DoubleOptionStorage : public OptionStorageTemplateSimple<double>
+{
+public:
+    //! \copydoc IntegerOptionStorage::IntegerOptionStorage()
+    explicit DoubleOptionStorage(const DoubleOption& settings);
+
+    OptionInfo& optionInfo() override { return info_; }
+    std::string typeString() const override;
+    std::string formatSingleValue(const double& value) const override;
+
+    //! \copydoc DoubleOptionInfo::isTime()
+    bool isTime() const { return bTime_; }
+    //! \copydoc DoubleOptionInfo::setScaleFactor()
+    void setScaleFactor(double factor);
+
+private:
+    void   initConverter(ConverterType* converter) override;
+    double processValue(const double& value) const override;
+    void   processSetValues(ValueList* values) override;
+
+    DoubleOptionInfo info_;
+    bool             bTime_;
+    double           factor_;
+};
+
+/*! \internal \brief
+ * Converts, validates, and stores floating-point (float) values.
+ */
+class FloatOptionStorage : public OptionStorageTemplateSimple<float>
+{
+public:
+    //! \copydoc IntegerOptionStorage::IntegerOptionStorage()
+    explicit FloatOptionStorage(const FloatOption& settings);
+
+    OptionInfo& optionInfo() override { return info_; }
+    std::string typeString() const override;
+    std::string formatSingleValue(const float& value) const override;
+
+    //! \copydoc DoubleOptionStorage::isTime()
+    bool isTime() const { return bTime_; }
+    //! \copydoc DoubleOptionStorage::setScaleFactor()
+    void setScaleFactor(double factor);
+
+private:
+    void  initConverter(ConverterType* converter) override;
+    float processValue(const float& value) const override;
+    void  processSetValues(ValueList* values) override;
+
+    FloatOptionInfo info_;
+    bool            bTime_;
+    double          factor_;
+};
+
+/*! \internal \brief
+ * Converts, validates, and stores string values.
+ */
+class StringOptionStorage : public OptionStorageTemplateSimple<std::string>
+{
+public:
+    //! \copydoc DoubleOptionStorage::DoubleOptionStorage()
+    explicit StringOptionStorage(const StringOption& settings);
+
+    OptionInfo& optionInfo() override { return info_; }
+    std::string typeString() const override { return allowed_.empty() ? "string" : "enum"; }
+    std::string formatExtraDescription() const override;
+    std::string formatSingleValue(const std::string& value) const override;
+
+    //! \copydoc StringOptionInfo::allowedValues()
+    const ValueList& allowedValues() const { return allowed_; }
+
+private:
+    void        initConverter(ConverterType* converter) override;
+    std::string processValue(const std::string& value) const override;
+
+    StringOptionInfo info_;
+    ValueList        allowed_;
+};
+
+/*! \internal \brief
+ * Converts, validates, and stores enum values.
+ */
+class EnumOptionStorage : public OptionStorageTemplateSimple<int>
+{
+public:
+    /*! \brief
+     * Initializes the storage from option settings.
+     *
+     * \param[in] settings      Basic storage settings.
+     * \param[in] enumValues    Allowed values.
+     * \param[in] count         Number of elements in \p enumValues,
+     *     or -1 if \p enumValues is `NULL`-terminated.
+     * \param[in] defaultValue  Default value, or -1 if no default.
+     * \param[in] defaultValueIfSet  Default value if set, or -1 if none.
+     * \param[in] store         Storage to convert the values to/from `int`.
+     *
+     * This constructor takes more parameters than other storage parameters
+     * because the front-end option type is a template, and as such cannot
+     * be passed here without exposing also this header as an installed
+     * header.
+     */
+    EnumOptionStorage(const AbstractOption& settings,
+                      const char* const*    enumValues,
+                      int                   count,
+                      int                   defaultValue,
+                      int                   defaultValueIfSet,
+                      StorePointer          store);
+
+    OptionInfo& optionInfo() override { return info_; }
+    std::string typeString() const override { return "enum"; }
+    std::string formatExtraDescription() const override;
+    std::string formatSingleValue(const int& value) const override;
+    Any         normalizeValue(const int& value) const override;
+
+    //! \copydoc EnumOptionInfo::allowedValues()
+    const std::vector<std::string>& allowedValues() const { return allowed_; }
+
+private:
+    void initConverter(ConverterType* converter) override;
+
+    EnumOptionInfo           info_;
+    std::vector<std::string> allowed_;
+};
+
+/*!\}*/
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/behaviorcollection.h b/src/include/gromacs/options/behaviorcollection.h
new file mode 100644 (file)
index 0000000..204a584
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::OptionsBehaviorCollection.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_BEHAVIORCOLLECTION_H
+#define GMX_OPTIONS_BEHAVIORCOLLECTION_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/utility/classhelpers.h"
+
+namespace gmx
+{
+
+class IOptionsBehavior;
+class Options;
+
+//! Smart pointer for behaviors stored in OptionsBehaviorCollection.
+typedef std::shared_ptr<IOptionsBehavior> OptionsBehaviorPointer;
+
+/*! \libinternal \brief
+ * Container for IOptionsBehavior objects.
+ *
+ * This class provides a container to keep IOptionsBehavior objects, and to
+ * call the IOptionsBehavior methods for the contained objects.
+ *
+ * IOptionsBehavior methods are called for the contained objects in the same
+ * order as in which the behaviors were inserted.
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class OptionsBehaviorCollection
+{
+public:
+    /*! \brief
+     * Constructs a container for storing behaviors associated with given
+     * Options.
+     *
+     * Caller needs to ensure that provided Options remains in existence
+     * while the container exists.
+     */
+    explicit OptionsBehaviorCollection(Options* options);
+    ~OptionsBehaviorCollection();
+
+    //! Adds a behavior to the collection.
+    void addBehavior(const OptionsBehaviorPointer& behavior);
+    //! Calls IOptionsBehavior::optionsFinishing() on all behaviors.
+    void optionsFinishing();
+    //! Calls IOptionsBehavior::optionsFinished() on all behaviors.
+    void optionsFinished();
+
+private:
+    Options*                            options_;
+    std::vector<OptionsBehaviorPointer> behaviors_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(OptionsBehaviorCollection);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/filenameoption.h b/src/include/gromacs/options/filenameoption.h
new file mode 100644 (file)
index 0000000..74df7e0
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::FileNameOption and gmx::FileNameOptionInfo.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_FILENAMEOPTION_H
+#define GMX_OPTIONS_FILENAMEOPTION_H
+
+#include <string>
+#include <vector>
+
+#include "gromacs/options/abstractoption.h"
+#include "gromacs/options/optionfiletype.h"
+
+namespace gmx
+{
+
+template<typename T>
+class ArrayRef;
+class FileNameOptionInfo;
+class FileNameOptionManager;
+class FileNameOptionStorage;
+
+/*! \brief
+ * Specifies an option that provides file names.
+ *
+ * Public methods in this class do not throw.
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class FileNameOption : public OptionTemplate<std::string, FileNameOption>
+{
+public:
+    //! OptionInfo subclass corresponding to this option type.
+    typedef FileNameOptionInfo InfoType;
+
+    //! Initializes an option with the given name.
+    explicit FileNameOption(const char* name) :
+        MyBase(name),
+        optionType_(OptionFileType::Count),
+        legacyType_(-1),
+        defaultBasename_(nullptr),
+        defaultType_(-1),
+        bLegacyOptionalBehavior_(false),
+        bRead_(false),
+        bWrite_(false),
+        bLibrary_(false),
+        bAllowMissing_(false)
+    {
+    }
+
+    /*! \brief
+     * Sets the type of the file this option accepts.
+     *
+     * Either this attribute or legacyType() must be provided.
+     */
+    MyClass& filetype(OptionFileType type)
+    {
+        optionType_ = type;
+        return me();
+    }
+    /*! \brief
+     * Sets the type of the file from an enum in filetypes.h.
+     *
+     * New code should prefer filetype(), extending the enumeration if
+     * necessary.
+     */
+    MyClass& legacyType(int type)
+    {
+        legacyType_ = type;
+        return me();
+    }
+    /*! \brief
+     * Changes the behavior of optional options to match old t_filenm.
+     *
+     * If this is not set, optional options return an empty string if not
+     * set.  If this is set, a non-empty value is always returned.
+     * In the latter case, whether the option is set only affects the
+     * return value of OptionInfo::isSet() and Options::isSet().
+     */
+    MyClass& legacyOptionalBehavior()
+    {
+        bLegacyOptionalBehavior_ = true;
+        return me();
+    }
+    //! Tells that the file provided by this option is used for input only.
+    MyClass& inputFile()
+    {
+        bRead_  = true;
+        bWrite_ = false;
+        return me();
+    }
+    //! Tells that the file provided by this option is used for output only.
+    MyClass& outputFile()
+    {
+        bRead_  = false;
+        bWrite_ = true;
+        return me();
+    }
+    /*! \brief
+     * Tells that the file provided by this option is used for input and
+     * output both.
+     */
+    MyClass& inputOutputFile()
+    {
+        bRead_ = bWrite_ = true;
+        return me();
+    }
+    /*! \brief
+     * Sets the read/write usage for this file from boolean flags.
+     */
+    MyClass& readWriteFlags(bool bRead, bool bWrite)
+    {
+        bRead_  = bRead;
+        bWrite_ = bWrite;
+        return me();
+    }
+    /*! \brief
+     * Tells that the file will be looked up in library directories in
+     * addition to working directory.
+     *
+     * \todo
+     * Currently, this flag only affects the help output.  Callers must
+     * take care themselves to actually search the file in the library
+     * directories.  It would be nicer to do this searching within the
+     * file name option implementation.
+     */
+    MyClass& libraryFile(bool bLibrary = true)
+    {
+        bLibrary_ = bLibrary;
+        return me();
+    }
+    /*! \brief
+     * Tells that missing file names explicitly provided by the user are
+     * valid for this input option.
+     *
+     * If this method is not called, an error will be raised if the user
+     * explicitly provides a file name that does not name an existing file,
+     * or if the default value does not resolve to a valid file name for a
+     * required option that the user has not set.
+     *
+     * This method only has effect with input files, and only if a
+     * FileNameOptionManager is being used.
+     */
+    MyClass& allowMissing(bool bAllow = true)
+    {
+        bAllowMissing_ = bAllow;
+        return me();
+    }
+    /*! \brief
+     * Sets a default basename for the file option.
+     *
+     * Use this method instead of defaultValue() or defaultValueIfSet() to
+     * set a default value for a file name option.  No extension needs to
+     * be provided; it is automatically added based on filetype() or
+     * defaultType().
+     * The behavior is also adjusted based on required(): if the option is
+     * required, the value given to defaultBasename() is treated as for
+     * both defaultValue() and defaultValueIfSet(), otherwise it is treated
+     * as for defaultValueIfSet().
+     *
+     * For input files that accept multiple extensions, the extension is
+     * completed to the default extension on creation of the option or at
+     * time of parsing an option without a value.
+     *
+     * If FileNameOptionManager is used, the extension may change during
+     * Options::finish(), as this is the time when the default names are
+     * checked against the file system to provide an extension that matches
+     * an existing file if that is possible.
+     *
+     * If FileNameOptionManager is used, and
+     * FileNameOptionManager::addDefaultFileNameOption() is used, and the
+     * user provides a global default file name using that option, then the
+     * global default takes precedence over defaultBasename().
+     */
+    MyClass& defaultBasename(const char* basename)
+    {
+        defaultBasename_ = basename;
+        return me();
+    }
+    /*! \brief
+     * Sets a default type/extension for the file option.
+     *
+     * For options that accept multiple types of files (e.g.,
+     * eftTrajectory), this method sets the default extension used
+     * for completing defaultBasename(), as well as the default extension
+     * used by FileNameOptionManager to complete various file names.
+     *
+     * The value should be one of the enumerated `ef*` values from
+     * filetypes.h, and be a valid type for the type specified with
+     * filetype().
+     */
+    MyClass& defaultType(int filetype)
+    {
+        defaultType_ = filetype;
+        return me();
+    }
+
+private:
+    // Use defaultBasename() instead.
+    using MyBase::defaultValue;
+    using MyBase::defaultValueIfSet;
+
+    //! Creates a FileNameOptionStorage object.
+    AbstractOptionStorage* createStorage(const OptionManagerContainer& managers) const override;
+
+    OptionFileType optionType_;
+    int            legacyType_;
+    const char*    defaultBasename_;
+    int            defaultType_;
+    bool           bLegacyOptionalBehavior_;
+    bool           bRead_;
+    bool           bWrite_;
+    bool           bLibrary_;
+    bool           bAllowMissing_;
+
+    /*! \brief
+     * Needed to initialize FileNameOptionStorage from this class without
+     * otherwise unnecessary accessors.
+     */
+    friend class FileNameOptionStorage;
+};
+
+/*! \brief
+ * Wrapper class for accessing file name option information.
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class FileNameOptionInfo : public OptionInfo
+{
+public:
+    //! Shorthand for a list of extensions.
+    typedef std::vector<const char*> ExtensionList;
+
+    //! Creates an option info object for the given option.
+    explicit FileNameOptionInfo(FileNameOptionStorage* option);
+
+    //! Whether the option specifies an input file.
+    bool isInputFile() const;
+    //! Whether the option specifies an output file.
+    bool isOutputFile() const;
+    //! Whether the option specifies a file used for both input and output.
+    bool isInputOutputFile() const;
+    /*! \brief
+     * Whether the option specifies a library file.
+     *
+     * \see FileNameOption::libraryFile()
+     */
+    bool isLibraryFile() const;
+    //! Whether the (input) option allows missing files to be provided.
+    bool allowMissing() const;
+
+    //! Whether the option specifies directories.
+    bool isDirectoryOption() const;
+    //! Whether the option specifies a generic trajectory file.
+    bool isTrajectoryOption() const;
+    //! Returns the default extension for this option.
+    const char* defaultExtension() const;
+    //! Returns the list of extensions this option accepts.
+    ExtensionList extensions() const;
+    //! Returns whether \p fileType (from filetypes.h) is accepted for this option.
+    bool isValidType(int fileType) const;
+    //! Returns the list of file types this option accepts.
+    ArrayRef<const int> fileTypes() const;
+
+private:
+    const FileNameOptionStorage& option() const;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/filenameoptionmanager.h b/src/include/gromacs/options/filenameoptionmanager.h
new file mode 100644 (file)
index 0000000..642426c
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::FileNameOptionManager.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_FILENAMEOPTIONMANAGER_H
+#define GMX_OPTIONS_FILENAMEOPTIONMANAGER_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/options/options.h"
+
+namespace gmx
+{
+
+class FileNameOptionInfo;
+class IFileInputRedirector;
+class IOptionsContainer;
+
+/*! \brief
+ * Handles interaction of file name options with global options.
+ *
+ * This class contains all logic that completes file names based on user input
+ * and file system contents.  Additionally, this class implements support for a
+ * global default file name that overrides any option-specific default, as well
+ * as additional control over how the completion is done.
+ *
+ * \todo
+ * Most of the functionality in this class is specific to command line parsing,
+ * so it would be cleaner to replace this with an interface, and have the
+ * actual code in the `commandline` module.
+ *
+ * Adding a FileNameOptionManager for an Options object is optional, even if
+ * the Options contains FileNameOption options.  Features from the manager are
+ * not available if the manager is not created, but otherwise the options work:
+ * the values provided to FileNameOption are used as they are, and exceptions
+ * are thrown if they are no valid instead of attempting to complete them.
+ *
+ * \see Options::addManager()
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class FileNameOptionManager : public IOptionManager
+{
+public:
+    FileNameOptionManager();
+    ~FileNameOptionManager() override;
+
+    /*! \brief
+     * Redirects file existence checks.
+     *
+     * \param[in] redirector  File redirector to use for existence checks.
+     *
+     * The manager checks for existence of various files on the file system
+     * to complete file extensions.  This method can be used to redirect
+     * those checks to an alternative implementation.
+     *
+     * This is used for unit tests to more easily control the result of the
+     * checks and to keep the tests as fast as possible by avoiding real
+     * file system access.  To keep implementation options open, behavior
+     * with `redirector == NULL` is undefined and should not be relied on.
+     * For tests, there should only be need to call this a single time,
+     * right after creating the manager.
+     */
+    void setInputRedirector(const IFileInputRedirector* redirector);
+
+    /*! \brief
+     * Disables special input file option handling.
+     *
+     * If disabled, this removes all file system calls from the file
+     * name option parsing.
+     * The values returned by FileNameOption for input and input/output
+     * files are handled with the same simple rule as for output files:
+     * the default extension is added if the file does not end in a
+     * recognized extension, and no other checking is done.
+     *
+     * This changes the following behavior:
+     *  - Providing non-existent files does not trigger errors.
+     *  - Extensions for input files are not completed to an existing file.
+     *  - Compressed input files do not work.
+     */
+    void disableInputOptionChecking(bool bDisable);
+
+    /*! \brief
+     * Adds an option for setting the default global file name.
+     *
+     * \param     options Options to add the option to.
+     * \param[in] name    Name of the option to add.
+     *
+     * If the user sets the option, it affects all file name options that
+     * would normally return a default value: the basename for the returned
+     * value is taken from the value of the default file name option,
+     * instead from an option-specific default
+     * (FileNameOption::defaultBaseName()).
+     */
+    void addDefaultFileNameOption(IOptionsContainer* options, const char* name);
+
+    /*! \brief
+     * Completes file name option values.
+     *
+     * \param[in] value  Value provided by the user.
+     * \param[in] option Option for which the value should be completed.
+     * \returns   Value for the file name option.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    InvalidInputError if the value is not valid for this
+     *     option.
+     *
+     * This method is called for each value that the user provides to
+     * a FileNameOption.  The return value (if non-empty) is used as the
+     * value of the option instead of the user-provided one.
+     */
+    std::string completeFileName(const std::string& value, const FileNameOptionInfo& option);
+    /*! \brief
+     * Completes default values for file name options.
+     *
+     * \param[in] prefix Default prefix for the file name.
+     * \param[in] option Option for which the value should be completed.
+     * \returns   Value for the file name option.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    InvalidInputError if the value is not valid for this
+     *     option.
+     *
+     * This method is called for each FileNameOption that has a default
+     * value (either a standard default value, or if the user provided the
+     * option without an explicit value).  \p prefix is the default value
+     * without the default extension for the option.
+     * If the return value is non-empty, it is used as the default value
+     * for the option instead of \p prefix + default extension.
+     */
+    std::string completeDefaultFileName(const std::string& prefix, const FileNameOptionInfo& option);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/filenameoptionstorage.h b/src/include/gromacs/options/filenameoptionstorage.h
new file mode 100644 (file)
index 0000000..adc1aea
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares gmx::FileNameOptionStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_FILENAMEOPTIONSTORAGE_H
+#define GMX_OPTIONS_FILENAMEOPTIONSTORAGE_H
+
+#include <string>
+#include <vector>
+
+#include "gromacs/options/filenameoption.h"
+#include "gromacs/options/optionfiletype.h"
+#include "gromacs/options/optionstoragetemplate.h"
+
+namespace gmx
+{
+
+class FileNameOption;
+class FileNameOptionManager;
+
+/*! \internal \brief
+ * Converts, validates, and stores file names.
+ */
+class FileNameOptionStorage : public OptionStorageTemplateSimple<std::string>
+{
+public:
+    /*! \brief
+     * Initializes the storage from option settings.
+     *
+     * \param[in] settings   Storage settings.
+     * \param     manager    Manager for this object (can be NULL).
+     */
+    FileNameOptionStorage(const FileNameOption& settings, FileNameOptionManager* manager);
+
+    OptionInfo& optionInfo() override { return info_; }
+    std::string typeString() const override;
+    std::string formatExtraDescription() const override;
+    std::string formatSingleValue(const std::string& value) const override;
+
+    //! \copydoc FileNameOptionInfo::isInputFile()
+    bool isInputFile() const { return bRead_ && !bWrite_; }
+    //! \copydoc FileNameOptionInfo::isOutputFile()
+    bool isOutputFile() const { return !bRead_ && bWrite_; }
+    //! \copydoc FileNameOptionInfo::isInputOutputFile()
+    bool isInputOutputFile() const { return bRead_ && bWrite_; }
+    //! \copydoc FileNameOptionInfo::isLibraryFile()
+    bool isLibraryFile() const { return bLibrary_; }
+    //! \copydoc FileNameOptionInfo::allowMissing()
+    bool allowMissing() const { return bAllowMissing_; }
+
+    //! \copydoc FileNameOptionInfo::isDirectoryOption()
+    bool isDirectoryOption() const;
+    //! \copydoc FileNameOptionInfo::isTrajectoryOption()
+    bool isTrajectoryOption() const;
+    //! \copydoc FileNameOptionInfo::defaultExtension()
+    const char* defaultExtension() const;
+    //! \copydoc FileNameOptionInfo::extensions()
+    std::vector<const char*> extensions() const;
+    //! \copydoc FileNameOptionInfo::isValidType()
+    bool isValidType(int fileType) const;
+    //! \copydoc FileNameOptionInfo::fileTypes()
+    ArrayRef<const int> fileTypes() const;
+
+private:
+    void        initConverter(ConverterType* converter) override;
+    std::string processValue(const std::string& value) const override;
+    void        processAll() override;
+
+    FileNameOptionInfo     info_;
+    FileNameOptionManager* manager_;
+    int                    fileType_;
+    const char*            defaultExtension_;
+    bool                   bRead_;
+    bool                   bWrite_;
+    bool                   bLibrary_;
+    bool                   bAllowMissing_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/ioptionsbehavior.h b/src/include/gromacs/options/ioptionsbehavior.h
new file mode 100644 (file)
index 0000000..d5d816c
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::IOptionsBehavior.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_IOPTIONSBEHAVIOR_H
+#define GMX_OPTIONS_IOPTIONSBEHAVIOR_H
+
+namespace gmx
+{
+
+class Options;
+
+/*! \brief
+ * Interface to provide extension points for options parsing.
+ *
+ * Currently, this is only used in the context of ICommandLineOptionsModule and
+ * some other command-line handling, but it is declared in the options module
+ * for the lack of a better place: most implementations of the interface are in
+ * modules that do not otherwise depend on the commandline module.
+ *
+ * \if libapi
+ * Any code that wants to support these extension points needs to use
+ * OptionsBehaviorCollection and call the methods there at appropriate points.
+ * This is not (at least, not currently) integrated in any automatic way to the
+ * actual Options object.
+ * \endif
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class IOptionsBehavior
+{
+public:
+    virtual ~IOptionsBehavior();
+
+    /*! \brief
+     * Called when the behavior is associated with an options object.
+     *
+     * This method can, e.g., use Options::addManager() to associate
+     * managers with the options object.
+     */
+    virtual void initBehavior(Options* options) = 0;
+    /*! \brief
+     * Called when all option values have been assigned.
+     *
+     * This is called just before Options::finish(), and can, e.g., do
+     * operations that still influence the option values.
+     */
+    virtual void optionsFinishing(Options* options) = 0;
+    /*! \brief
+     * Called when all option values have been processed.
+     *
+     * This is called after Options::finish() (and typically after
+     * higher-level optionsFinished() methods, such as that in
+     * ICommandLineOptionsModule).  This can add behavior that performs
+     * tasks based on the option values provided.
+     */
+    virtual void optionsFinished() = 0;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/ioptionscontainer.h b/src/include/gromacs/options/ioptionscontainer.h
new file mode 100644 (file)
index 0000000..1217dac
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::IOptionsContainer.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_IOPTIONSCONTAINER_H
+#define GMX_OPTIONS_IOPTIONSCONTAINER_H
+
+#include "gromacs/options/abstractoption.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+/*! \brief
+ * Interface for adding input options.
+ *
+ * This interface provides methods to add new options.
+ * Standard usage is for code to receive this interface and populate it with
+ * supported options:
+ * \code
+   // <as class attributes>
+   std::string  arg1;
+   int          arg2;
+
+   void MyClass::initOptions(gmx::IOptionsContainer *options)
+   {
+       options->addOption(gmx::StringOption("arg1").store(&arg1));
+       options->addOption(gmx::IntegerOption("arg2").store(&arg2));
+   }
+   \endcode
+ * The caller can collect options from multiple sources into a single container
+ * (a gmx::Options), and use a parser implementation such as CommandLineParser
+ * to provide values for the options.
+ *
+ * Header basicoptions.h provides declarations of several standard
+ * option types for use with addOption().  Documentation of those classes
+ * also give more examples of how to define options.
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class IOptionsContainer
+{
+public:
+    /*! \brief
+     * Creates a subgroup of options within the current options.
+     *
+     * To add options to the group, use the returned interface.
+     *
+     * Currently, this is only used to influence the order of options:
+     * all options in a group appear before options in a group added after
+     * it, no matter in which order the options are added to the groups.
+     * In the future, the groups could also be used to influence the help
+     * output.
+     */
+    virtual IOptionsContainer& addGroup() = 0;
+    /*! \brief
+     * Adds a recognized option.
+     *
+     * \tparam    OptionType Type of the options description object.
+     * \param[in] settings   Option description.
+     * \returns   OptionInfo object for the created option (never NULL).
+     * \throws    APIError if invalid option settings are provided.
+     *
+     * The return value is a pointer for more convenient use in callers:
+     * often callers need to declare the variable that will hold the return
+     * value in wider scope than would be achieved by declaring it at the
+     * site where addOption() is called.
+     * The returned pointer must not be freed.
+     *
+     * See \link Options class documentation \endlink for example usage.
+     *
+     * \libinternal
+     * \p OptionType::InfoType must specify a type that derives from
+     * OptionInfo and matches the type that is returned by
+     * AbstractOptionStorage::optionInfo() for the storage object that
+     * corresponds to \p OptionType.
+     */
+    template<class OptionType>
+    typename OptionType::InfoType* addOption(const OptionType& settings)
+    {
+        OptionInfo* info = addOptionImpl(static_cast<const AbstractOption&>(settings));
+        GMX_ASSERT(info->isType<typename OptionType::InfoType>(),
+                   "Mismatching option info type declaration and implementation");
+        return info->toType<typename OptionType::InfoType>();
+    }
+
+protected:
+    // Disallow deletion through the interface.
+    // (no need for the virtual, but some compilers warn otherwise)
+    virtual ~IOptionsContainer();
+
+    /*! \brief
+     * Adds a recognized option.
+     *
+     * \param[in] settings Option description.
+     * \returns   OptionInfo object for the created option (never NULL).
+     * \throws    APIError if invalid option settings are provided.
+     *
+     * This method provides the internal implementation, but the templated
+     * method is called from user code.  See the templated method for more
+     * details.
+     */
+    virtual OptionInfo* addOptionImpl(const AbstractOption& settings) = 0;
+
+    GMX_DEFAULT_CONSTRUCTORS(IOptionsContainer);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/ioptionscontainerwithsections.h b/src/include/gromacs/options/ioptionscontainerwithsections.h
new file mode 100644 (file)
index 0000000..e7efcbd
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::IOptionsContainerWithSections.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_IOPTIONSCONTAINERWITHSECTIONS_H
+#define GMX_OPTIONS_IOPTIONSCONTAINERWITHSECTIONS_H
+
+#include "gromacs/options/ioptionscontainer.h"
+
+namespace gmx
+{
+
+class AbstractOptionSection;
+class AbstractOptionSectionHandle;
+
+namespace internal
+{
+class OptionSectionImpl;
+}
+
+/*! \brief
+ * Interface for adding input options with sections.
+ *
+ * This interface extends IOptionsContainer with an additional addSection()
+ * method that supports creating a hierarchy of sections for the options.
+ *
+ * Header optionsection.h provides OptionSection.
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class IOptionsContainerWithSections : public IOptionsContainer
+{
+public:
+    /*! \brief
+     * Adds a section to this collection.
+     *
+     * \tparam    SectionType Type of the section description object.
+     * \param[in] section     Section description.
+     * \returns   AbstractOptionSectionHandle object for the created option.
+     * \throws    APIError if invalid option settings are provided.
+     *
+     * Options can be added to the section through the returned handle.
+     *
+     * \internal
+     * \p SectionType::HandleType must specify a type that derives from
+     * AbstractinOptionSectionHandle and has a suitable constructor.
+     */
+    template<class SectionType>
+    typename SectionType::HandleType addSection(const SectionType& section)
+    {
+        internal::OptionSectionImpl* storage =
+                addSectionImpl(static_cast<const AbstractOptionSection&>(section));
+        return typename SectionType::HandleType(storage);
+    }
+
+protected:
+    // Disallow deletion through the interface.
+    // (no need for the virtual, but some compilers warn otherwise)
+    ~IOptionsContainerWithSections() override;
+
+    /*! \brief
+     * Adds a section to this container.
+     *
+     * \param[in] section     Section description.
+     * \returns   Pointer to the internal section representation object.
+     */
+    virtual internal::OptionSectionImpl* addSectionImpl(const AbstractOptionSection& section) = 0;
+
+    GMX_DEFAULT_CONSTRUCTORS(IOptionsContainerWithSections);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/isectionstorage.h b/src/include/gromacs/options/isectionstorage.h
new file mode 100644 (file)
index 0000000..04a7cfc
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::IOptionSectionStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_ISECTIONSTORAGE_H
+#define GMX_OPTIONS_ISECTIONSTORAGE_H
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief
+ * Provides behavior specific to a certain option section type.
+ *
+ * \ingroup module_options
+ */
+class IOptionSectionStorage
+{
+public:
+    virtual ~IOptionSectionStorage();
+
+    /*! \brief
+     * Called once before the first call to startSection().
+     *
+     * This is called once all options have been added to the section.
+     * The current implementation does not call this if startSection() is
+     * never called.
+     */
+    virtual void initStorage() = 0;
+    /*! \brief
+     * Called when option assignment enters this section.
+     */
+    virtual void startSection() = 0;
+    /*! \brief
+     * Called when option assignment leaves this section.
+     */
+    virtual void finishSection() = 0;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/ivaluestore.h b/src/include/gromacs/options/ivaluestore.h
new file mode 100644 (file)
index 0000000..e0ca86f
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::IOptionValueStore.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_IVALUESTORE_H
+#define GMX_OPTIONS_IVALUESTORE_H
+
+namespace gmx
+{
+
+template<typename T>
+class ArrayRef;
+
+/*! \internal
+ * \brief
+ * Represents the final storage location of option values.
+ *
+ * \todo
+ * Try to make this more like a write-only interface, getting rid of the need
+ * to access the stored values through this interface.  That would simplify
+ * things.
+ *
+ * \ingroup module_options
+ */
+template<typename T>
+class IOptionValueStore
+{
+public:
+    virtual ~IOptionValueStore() {}
+
+    //! Returns the number of values stored so far.
+    virtual int valueCount() = 0;
+    //! Returns a reference to the actual values.
+    virtual ArrayRef<T> values() = 0;
+    //! Removes all stored values.
+    virtual void clear() = 0;
+    //! Reserves memory for additional `count` entries.
+    virtual void reserve(size_t count) = 0;
+    //! Appends a value to the store.
+    virtual void append(const T& value) = 0;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/optionfiletype.h b/src/include/gromacs/options/optionfiletype.h
new file mode 100644 (file)
index 0000000..619091c
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2015,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Defines an enumeration type for specifying file types for options.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONFILETYPE_HPP
+#define GMX_OPTIONS_OPTIONFILETYPE_HPP
+
+namespace gmx
+{
+
+/*! \brief
+ * Purpose of file(s) provided through an option.
+ *
+ * \ingroup module_options
+ */
+enum class OptionFileType : int
+{
+    Topology,
+    RunInput,
+    Trajectory,
+    Energy,
+    PDB,
+    Index,
+    Plot,
+    GenericData,
+    Csv,
+    QMInput,
+    Count
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/optionflags.h b/src/include/gromacs/options/optionflags.h
new file mode 100644 (file)
index 0000000..5758533
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2014,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Defines flags used in option implementation.
+ *
+ * Symbols in this header are considered an implementation detail, and should
+ * not be accessed outside the module.
+ * Because of details in the implementation, it is still installed.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONFLAGS_H
+#define GMX_OPTIONS_OPTIONFLAGS_H
+
+#include "gromacs/utility/flags.h"
+
+namespace gmx
+{
+
+/*! \cond libapi */
+/*! \libinternal \brief
+ * Flags for options.
+ *
+ * These flags are not part of the public interface, even though they are in an
+ * installed header.  They are needed in a few template class implementations.
+ *
+ * \todo
+ * The flags related to default values are confusing, consider reorganizing
+ * them.
+ */
+enum OptionFlag : uint64_t
+{
+    //! %Option has been set.
+    efOption_Set = 1 << 0,
+    //! The current value of the option is a programmatic default value.
+    efOption_HasDefaultValue = 1 << 1,
+    //! An explicit default value has been provided for the option.
+    efOption_ExplicitDefaultValue = 1 << 2,
+    /*! \brief
+     * Next assignment to the option clears old values.
+     *
+     * This flag is set when a new option source starts, such that values
+     * from the new source will overwrite old ones.
+     */
+    efOption_ClearOnNextSet = 1 << 3,
+    //! %Option is required to be set.
+    efOption_Required = 1 << 4,
+    //! %Option can be specified multiple times.
+    efOption_MultipleTimes = 1 << 5,
+    //! %Option is hidden from standard help.
+    efOption_Hidden = 1 << 6,
+    /*! \brief
+     * %Option value is a vector, but a single value is also accepted.
+     *
+     * \see AbstractOption::setVector()
+     */
+    efOption_Vector = 1 << 8,
+    //! %Option has a defaultValueIfSet() specified.
+    efOption_DefaultValueIfSetExists = 1 << 11,
+    //! %Option does not support default values.
+    efOption_NoDefaultValue = 1 << 9,
+    /*! \brief
+     * Storage object does its custom checking for minimum value count.
+     *
+     * If this flag is set, the class derived from OptionStorageTemplate should
+     * implement processSetValues(), processAll(), and possible other functions
+     * it provides such that it always fails if not enough values are provided.
+     * This is useful to override the default check, which is done in
+     * OptionStorageTemplate::processSet().
+     */
+    efOption_DontCheckMinimumCount = 1 << 10
+};
+
+//! \libinternal Holds a combination of ::OptionFlag values.
+typedef FlagsTemplate<OptionFlag> OptionFlags;
+//! \endcond
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/optionmanagercontainer.h b/src/include/gromacs/options/optionmanagercontainer.h
new file mode 100644 (file)
index 0000000..cfd343b
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::OptionManagerContainer.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONMANAGERCONTAINER_H
+#define GMX_OPTIONS_OPTIONMANAGERCONTAINER_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+class IOptionManager;
+
+/*! \libinternal
+ * \brief
+ * Container to keep managers added with Options::addManager() and pass them
+ * to options.
+ *
+ * Consistency of the managers (e.g., that there is at most one manager of a
+ * certain type) is only checked when the managers are accessed.
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class OptionManagerContainer
+{
+public:
+    OptionManagerContainer() {}
+
+    //! Returns `true` if there are no managers.
+    bool empty() const { return list_.empty(); }
+
+    //! Adds a manager to the container.
+    void add(IOptionManager* manager) { list_.push_back(manager); }
+    /*! \brief
+     * Retrieves a manager of a certain type.
+     *
+     * \tparam  ManagerType  Type of manager to retrieve
+     *     (should derive from IOptionManager).
+     * \returns The manager, or `NULL` if there is none.
+     *
+     * This method is used in AbstractOption::createStorage() to retrieve
+     * a manager of a certain type for options that use a manager.
+     *
+     * The return value is `NULL` if there is no manager of the given type.
+     * The caller needs to handle this (either by asserting, or by handling
+     * the manager as optional).
+     */
+    template<class ManagerType>
+    ManagerType* get() const
+    {
+        ManagerType* result = nullptr;
+        for (ListType::const_iterator i = list_.begin(); i != list_.end(); ++i)
+        {
+            ManagerType* curr = dynamic_cast<ManagerType*>(*i);
+            if (curr != nullptr)
+            {
+                GMX_RELEASE_ASSERT(result == nullptr,
+                                   "More than one applicable option manager is set");
+                result = curr;
+            }
+        }
+        return result;
+    }
+
+private:
+    //! Shorthand for the internal container type.
+    typedef std::vector<IOptionManager*> ListType;
+
+    ListType list_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(OptionManagerContainer);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/options.h b/src/include/gromacs/options/options.h
new file mode 100644 (file)
index 0000000..b2ef296
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::Options.
+ *
+ * Together with basicoptions.h, this header forms the part of the public
+ * API that most classes will use to provide options.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONS_H
+#define GMX_OPTIONS_OPTIONS_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/options/ioptionscontainerwithsections.h"
+
+namespace gmx
+{
+
+class AbstractOption;
+class OptionSection;
+class OptionSectionInfo;
+class OptionsAssigner;
+
+namespace internal
+{
+class OptionsImpl;
+}
+
+/*! \brief
+ * Base class for option managers.
+ *
+ * This class is used as a marker for all classes that are used with
+ * Options::addManager().  It doesn't provide any methods, but only supports
+ * transporting these classes through the Options collection into the
+ * individual option implementation classes.
+ *
+ * The virtual destructor is present to make this class polymorphic, such that
+ * `dynamic_cast` can be used when retrieving a manager of a certain type for
+ * the individual options.
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class IOptionManager
+{
+protected:
+    virtual ~IOptionManager();
+};
+
+/*! \brief
+ * Collection of options.
+ *
+ * See \ref module_options for an overview of how the options work.
+ * The IOptionsContainerWithSections interface documents how to add options.
+ *
+ * In order to keep the public interface of this class simple, functionality
+ * to assign values to options is provided by a separate OptionsAssigner class.
+ * Similarly, functionality for looping over all options (e.g., for writing out
+ * help) is provided by OptionsIterator.
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class Options : public IOptionsContainerWithSections
+{
+public:
+    //! Initializes an empty options root container.
+    Options();
+    ~Options() override;
+
+    /*! \brief
+     * Adds an option manager.
+     *
+     * \param    manager Manager to add.
+     * \throws   std::bad_alloc if out of memory.
+     *
+     * Option managers are used by some types of options that require
+     * interaction between different option instances (e.g., selection
+     * options), or need to support globally set properties (e.g., a global
+     * default file prefix).  Option objects can retrieve the pointer to
+     * their manager when they are created, and the caller can alter the
+     * behavior of the options through the manager.
+     * See the individual managers for details.
+     *
+     * Caller is responsible for memory management of \p manager.
+     * The Options object (and its contained options) only stores a
+     * reference to the object.
+     *
+     * This method cannot be called after adding options or sections.
+     */
+    void addManager(IOptionManager* manager);
+
+    // From IOptionsContainer
+    IOptionsContainer& addGroup() override;
+
+    //! Returns a handle to the root section.
+    OptionSectionInfo& rootSection();
+    //! Returns a handle to the root section.
+    const OptionSectionInfo& rootSection() const;
+
+    /*! \brief
+     * Notifies the collection that all option values are assigned.
+     *
+     * \throws InvalidInputError if invalid user input is detected.
+     *
+     * This function should be called after no more option values are
+     * to be assigned.  Values in storage variables are guaranteed to be
+     * available only after this call, although in most cases, they are
+     * available already during assignment.
+     *
+     * If invalid option values, e.g., missing required option, is detected
+     * at this point, this function throws.  The thrown exception contains
+     * information on all errors detected during the call.
+     */
+    void finish();
+
+private:
+    // From IOptionsContainerWithSections
+    internal::OptionSectionImpl* addSectionImpl(const AbstractOptionSection& section) override;
+    // From IOptionsContainer
+    OptionInfo* addOptionImpl(const AbstractOption& settings) override;
+
+    std::unique_ptr<internal::OptionsImpl> impl_;
+
+    //! Needed to be able to extend the interface of this object.
+    friend class OptionsAssigner;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/options_impl.h b/src/include/gromacs/options/options_impl.h
new file mode 100644 (file)
index 0000000..cd0c357
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010-2018, The GROMACS development team.
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation class for gmx::Options.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONS_IMPL_H
+#define GMX_OPTIONS_OPTIONS_IMPL_H
+
+#include <list>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/options/abstractoption.h"
+#include "gromacs/options/abstractoptionstorage.h"
+#include "gromacs/options/ioptionscontainer.h"
+#include "gromacs/options/ioptionscontainerwithsections.h"
+#include "gromacs/options/optionmanagercontainer.h"
+#include "gromacs/options/options.h"
+#include "gromacs/options/optionsection.h"
+#include "gromacs/utility/classhelpers.h"
+
+#include "isectionstorage.h"
+
+namespace gmx
+{
+
+namespace internal
+{
+
+/*! \internal
+ * \brief
+ * Internal implementation class for storing an option section.
+ *
+ * All options are stored within a section: the top-level contents of an
+ * Options object are handled within an unnamed, "root" section.
+ * This class handles the common functionality for all sections, related to
+ * storing the options and subsections.  Functionality specific to a section
+ * type is provided by IOptionSectionStorage.
+ *
+ * \ingroup module_options
+ */
+class OptionSectionImpl : public IOptionsContainerWithSections
+{
+public:
+    /*! \internal \brief
+     * Describes a group of options (see Options::addGroup()).
+     *
+     * \ingroup module_options
+     */
+    class Group : public IOptionsContainer
+    {
+    public:
+        //! Convenience typedef for list of options.
+        typedef std::vector<AbstractOptionStorage*> OptionList;
+        //! Convenience typedef for list of subgroups.
+        typedef std::list<Group> SubgroupList;
+
+        //! Creates a group within the given Options.
+        explicit Group(OptionSectionImpl* parent) : parent_(parent) {}
+
+        // From IOptionsContainer
+        IOptionsContainer& addGroup() override;
+        OptionInfo*        addOptionImpl(const AbstractOption& settings) override;
+
+        //! Containing options object.
+        OptionSectionImpl* parent_;
+        /*! \brief
+         * List of options, in insertion order.
+         *
+         * Pointers in this container point to the objects managed by
+         * Impl::optionsMap_.
+         */
+        OptionList options_;
+        //! List of groups, in insertion order.
+        SubgroupList subgroups_;
+    };
+
+    //! Smart pointer for managing an AbstractOptionStorage object.
+    typedef std::unique_ptr<AbstractOptionStorage> AbstractOptionStoragePointer;
+    //! Convenience typedef for a map that contains all the options.
+    typedef std::map<std::string, AbstractOptionStoragePointer> OptionMap;
+    //! Smart pointer for managing subsections.
+    typedef std::unique_ptr<OptionSectionImpl> SectionPointer;
+    //! Convenience typedef for a container for subsections.
+    typedef std::vector<SectionPointer> SectionList;
+
+    //! Creates storage for a new section.
+    OptionSectionImpl(const OptionManagerContainer&          managers,
+                      std::unique_ptr<IOptionSectionStorage> storage,
+                      const char*                            name) :
+        managers_(managers),
+        storage_(std::move(storage)),
+        info_(this),
+        name_(name),
+        rootGroup_(this),
+        storageInitialized_(false)
+    {
+    }
+
+    // From IOptionsContainerWithSections
+    OptionSectionImpl* addSectionImpl(const AbstractOptionSection& section) override;
+
+    // From IOptionsContainer
+    IOptionsContainer& addGroup() override;
+    OptionInfo*        addOptionImpl(const AbstractOption& settings) override;
+
+    //! Returns section info object for this section.
+    OptionSectionInfo& info() { return info_; }
+    //! Returns section info object for this section.
+    const OptionSectionInfo& info() const { return info_; }
+
+    /*! \brief
+     * Finds a subsection by name.
+     *
+     * \param[in] name  Name to search for.
+     * \returns Pointer to the found subsection, or NULL if not found.
+     *
+     * Does not throw.
+     */
+    OptionSectionImpl* findSection(const char* name) const;
+    /*! \brief
+     * Finds an option by name.
+     *
+     * \param[in] name  Name to search for.
+     * \returns Pointer to the found option, or NULL if not found.
+     *
+     * Does not throw.
+     */
+    AbstractOptionStorage* findOption(const char* name) const;
+
+    /*! \brief
+     * Called when entering the section.
+     *
+     * Calls AbstractOptionStorage::startSource() for all options.
+     */
+    void start();
+    /*! \brief
+     * Calls AbstractOptionStorage::finish() for all options.
+     */
+    void finish();
+
+    //! Reference to the option managers in the parent Options object.
+    const OptionManagerContainer& managers_;
+    //! Type-specific storage object for this section.
+    std::unique_ptr<IOptionSectionStorage> storage_;
+    //! Info object for this section.
+    OptionSectionInfo info_;
+    //! Name of this section (empty and unused for the root section).
+    std::string name_;
+    /*! \brief
+     * Group that contains all options (and subgroups).
+     *
+     * This is used to store the insertion order of options.
+     */
+    Group rootGroup_;
+    //! Map from option names to options; owns the option storage objects.
+    OptionMap optionMap_;
+    //! List of subsections, in insertion order.
+    SectionList subsections_;
+    //! Whether initStorage() has been called for `storage_`.
+    bool storageInitialized_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(OptionSectionImpl);
+};
+
+/*! \internal
+ * \brief
+ * Private implementation class for Options.
+ *
+ * Note that in addition to Options, the OptionsAssigner class also directly
+ * accesses this class.
+ *
+ * \ingroup module_options
+ */
+class OptionsImpl
+{
+public:
+    OptionsImpl();
+
+    //! Option managers set for this collection.
+    OptionManagerContainer managers_;
+    //! Root section for this collection.
+    OptionSectionImpl rootSection_;
+};
+
+} // namespace internal
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/optionsassigner.h b/src/include/gromacs/options/optionsassigner.h
new file mode 100644 (file)
index 0000000..0ab59fe
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2016,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::OptionsAssigner.
+ *
+ * This header is only needed when implementing option parsers.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONSASSIGNER_H
+#define GMX_OPTIONS_OPTIONSASSIGNER_H
+
+#include <memory>
+#include <string>
+
+namespace gmx
+{
+
+class Options;
+class Any;
+
+/*! \libinternal \brief
+ * Decorator class for assigning values to Options.
+ *
+ * This class extends the interface of an Options object by providing methods
+ * to set values for options.  It also keeps track of necessary state variables
+ * to assign values to options in subsections within the Options object.
+ * Typical use (without error handling):
+ * \code
+   gmx::Options options("name", "Title");
+   // Set up options
+
+   gmx::OptionsAssigner assigner(&options);
+   assigner.start();
+   assigner.startOption("opt1");
+   assigner.appendValue("3");
+   assigner.finishOption();
+   assigner.startSection("section");
+   assigner.startOption("opt2"); // Now in the subsection
+   assigner.appendValue("yes");
+   assigner.finishOption();
+   assigner.finishSection()
+   assigner.startOption("opt3"); // Again in the main options
+   assigner.appendValue("2");
+   assigner.finishOption();
+   assigner.finish();
+ * \endcode
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class OptionsAssigner
+{
+public:
+    /*! \brief
+     * Creates an object that assigns to the given object.
+     */
+    explicit OptionsAssigner(Options* options);
+    ~OptionsAssigner();
+
+    /*! \brief
+     * Sets the assigner to recognize boolean options with a "no" prefix.
+     *
+     * With this option set, \c startOption("noname") is interpreted as
+     * \c startOption("name") followed by \c appendValue("no"), if there is
+     * no option by the name "noname", but there is a boolean option with
+     * name "name".
+     *
+     * By default, the prefix is not recognized.
+     *
+     * Can be set or cleared at any time, and will have effect on all
+     * subsequent calls of startOption().
+     *
+     * Does not throw.
+     */
+    void setAcceptBooleanNoPrefix(bool bEnabled);
+
+    /*! \brief
+     * Starts assigning values.
+     *
+     * Does not throw.
+     */
+    void start();
+    /*! \brief
+     * Starts assigning values to options in a subsection.
+     *
+     * \param[in] name  Name of the subsection to start assigning to.
+     * \throws InvalidInputError if such a subsection is not found.
+     *
+     * Strong exception safety guarantee.
+     */
+    void startSection(const char* name);
+    /*! \brief
+     * Starts assigning values for an option.
+     *
+     * \param[in] name  Name of the option to start assigning to.
+     * \throws InvalidInputError if such an option is not found, or if the
+     *      option is specified more than once but doesn't support it.
+     */
+    void startOption(const char* name);
+    /*! \brief
+     * Starts assigning values for an option.
+     *
+     * \param[in] name  Name of the option to start assigning to.
+     * \returns   true if \p name is a valid option name.
+     * \throws InvalidInputError if the option is specified more than once
+     *      but doesn't support it.
+     */
+    bool tryStartOption(const char* name);
+    /*! \brief
+     * Appends a value to the value list of the current option.
+     *
+     * \param[in] value  Value to assign.
+     * \throws InvalidInputError if the value cannot be converted or if
+     *      there are too many values for an option.
+     *
+     * Basic exception safety guarantee:
+     * If this method throws, erroneous values are ignored, but it is
+     * possible to continue assigning values to the same option.  However,
+     * if \p value would result in more than one value, and some of them
+     * can be converted, but some result in errors, it is currently
+     * possible that some values have been added to the option even if an
+     * exception is thrown.
+     *
+     * Strong exception safety guarantee if the option provides value
+     * conversion with the same guarantee.  All options where a single
+     * input value always results in a single output value provide this.
+     *
+     * \internal
+     * This method provides the same exception safety guarantee as the
+     * OptionStorageTemplate::convertValue() method of the storage class
+     * implementing the option where the value is assigned to.
+     */
+    void appendValue(const Any& value);
+    /*! \brief
+     * Appends a value to the value list of the current option.
+     *
+     * \param[in] value  Value to assign.
+     *
+     * See appendValue(const Any &) for more details.
+     */
+    void appendValue(const std::string& value);
+    /*! \brief
+     * Finish assigning values for the current option.
+     *
+     * \throws InvalidInputError if the set of values since startOption()
+     *      is not valid.
+     *
+     * If this method throws, it returns to the state where the option was
+     * before startOption(), i.e., all values added with appendValue()
+     * since the last startOption() are discarded.
+     *
+     * Independent of whether the method throws, the option opened with
+     * startOption() will be closed after the call.
+     */
+    void finishOption();
+    /*! \brief
+     * Finish assigning values to a subsection.
+     *
+     * Does not throw.
+     */
+    void finishSection();
+    /*! \brief
+     * Finish assigning options through the object.
+     *
+     * Does not throw.
+     */
+    void finish();
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/optionsection.h b/src/include/gromacs/options/optionsection.h
new file mode 100644 (file)
index 0000000..e7f6ed9
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::OptionSection and gmx::OptionSectionInfo.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONSECTION_H
+#define GMX_OPTIONS_OPTIONSECTION_H
+
+#include <memory>
+
+#include "abstractsection.h"
+
+namespace gmx
+{
+
+class OptionSectionHandle;
+
+/*! \brief
+ * Declares a simple option section.
+ *
+ * This class declares a simple section that only provides structure for
+ * grouping the options, but does not otherwise influence the behavior of the
+ * contained options.
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class OptionSection : public AbstractOptionSection
+{
+public:
+    //! AbstractOptionSectionHandle corresponding to this option type.
+    typedef OptionSectionHandle HandleType;
+
+    //! Creates a section with the given name.
+    explicit OptionSection(const char* name) : AbstractOptionSection(name) {}
+
+private:
+    std::unique_ptr<IOptionSectionStorage> createStorage() const override;
+};
+
+/*! \brief
+ * Allows adding options to an OptionSection.
+ *
+ * An instance of this class is returned from
+ * IOptionsContainerWithSections::addSection(), and supports adding options and
+ * subsections to a section created with OptionSection.
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class OptionSectionHandle : public AbstractOptionSectionHandle
+{
+public:
+    //! Wraps a given section storage object.
+    explicit OptionSectionHandle(internal::OptionSectionImpl* section) :
+        AbstractOptionSectionHandle(section)
+    {
+    }
+};
+
+class OptionSectionInfo : public AbstractOptionSectionInfo
+{
+public:
+    //! Wraps a given section storage object.
+    explicit OptionSectionInfo(internal::OptionSectionImpl* section) :
+        AbstractOptionSectionInfo(section)
+    {
+    }
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/optionstoragetemplate.h b/src/include/gromacs/options/optionstoragetemplate.h
new file mode 100644 (file)
index 0000000..e8445fd
--- /dev/null
@@ -0,0 +1,597 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010-2018, The GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Defines gmx::OptionStorageTemplate template.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
+#define GMX_OPTIONS_OPTIONSTORAGETEMPLATE_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/options/abstractoption.h"
+#include "gromacs/options/abstractoptionstorage.h"
+#include "gromacs/utility/any.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "valueconverter.h"
+#include "valuestore.h"
+
+namespace gmx
+{
+
+class Options;
+
+/*! \libinternal \brief
+ * Templated base class for constructing option value storage classes.
+ *
+ * \tparam T Assignable type that stores a single option value.
+ *
+ * Provides an implementation of the clearSet(), valueCount(), processSet(),
+ * and defaultValuesAsStrings() methods of AbstractOptionStorage, as well as a
+ * basic no-action implementation of processAll().  Two new virtual methods are
+ * added: processSetValues() and formatSingleValue().
+ * This leaves typeString(), convertValue() and formatStringValue() to be
+ * implemented in derived classes.
+ * processSetValues() and processAll() can also be implemented if necessary.
+ *
+ * Implements transaction support for adding values within a set: all calls to
+ * addValue() add the value to a temporary storage, processSetValues() operates
+ * on this temporary storage, and commitValues() then copies these values to
+ * the real storage.  commitValues() provides a strong exception safety
+ * guarantee for the process (and it only throws if it runs out of memory).
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+template<typename T>
+class OptionStorageTemplate : public AbstractOptionStorage
+{
+public:
+    //! Alias for the template class for use in base classes.
+    typedef OptionStorageTemplate<T> MyBase;
+    //! Type of the container that contains the current values.
+    typedef std::vector<T> ValueList;
+
+    // No implementation in this class for the pure virtual methods, but
+    // the declarations are still included for clarity.
+    // The various copydoc calls are needed with Doxygen 1.8.10, although
+    // things work without with 1.8.5...
+    std::string typeString() const override = 0;
+    //! \copydoc gmx::AbstractOptionStorage::valueCount()
+    int valueCount() const override { return store_->valueCount(); }
+    //! \copydoc gmx::AbstractOptionStorage::defaultValues()
+    std::vector<Any> defaultValues() const override;
+    /*! \copydoc gmx::AbstractOptionStorage::defaultValuesAsStrings()
+     *
+     * OptionStorageTemplate implements handling of defaultValueIfSet()
+     * cases and composing the vector.
+     * Derived classes must implement formatSingleValue() to provide the
+     * actual formatting for a value of type \p T.
+     */
+    std::vector<std::string> defaultValuesAsStrings() const override;
+
+protected:
+    //! Smart pointer for managing the final storage interface.
+    typedef std::unique_ptr<IOptionValueStore<T>> StorePointer;
+
+    /*! \brief
+     * Initializes the storage from option settings.
+     *
+     * \param[in] settings  Option settings.
+     * \param[in] staticFlags Option flags that are always set and specify
+     *      generic behavior of the option.
+     * \throws  APIError if invalid settings have been provided.
+     */
+    template<class U>
+    explicit OptionStorageTemplate(const OptionTemplate<T, U>& settings,
+                                   OptionFlags                 staticFlags = OptionFlags());
+    /*! \brief
+     * Initializes the storage from base option settings.
+     *
+     * \param[in] settings  Option settings.
+     * \param[in] store     Final storage location.
+     * \throws  APIError if invalid settings have been provided.
+     *
+     * This constructor works for cases where there is no matching
+     * OptionTemplate (e.g., EnumOption).
+     */
+    OptionStorageTemplate(const AbstractOption& settings, StorePointer store);
+
+    //! \copydoc gmx::AbstractOptionStorage::clearSet()
+    void clearSet() override;
+    /*! \copydoc gmx::AbstractOptionStorage::convertValue()
+     *
+     * Derived classes should call addValue() after they have converted
+     * \p value to the storage type.  It is allowed to call addValue()
+     * more than once, or not at all.  OptionsAssigner::appendValue()
+     * provides the same exception safety guarantee as this method, so it
+     * should be considered whether the implementation can be made strongly
+     * exception safe.
+     */
+    void convertValue(const Any& value) override = 0;
+    /*! \brief
+     * Processes values for a set after all have been converted.
+     *
+     * \param[in,out] values Valid values in the set.
+     * \throws InvalidInputError if the values do not form a valid set.
+     *
+     * This method is called after all convertValue() calls for a set.
+     * \p values contains all values that were validly converted by
+     * convertValue().  The derived class may alter the values, but should
+     * in such a case ensure that a correct number of values is produced.
+     * If the derived class throws, all values in \p values are discarded.
+     */
+    virtual void processSetValues(ValueList* values) { GMX_UNUSED_VALUE(values); }
+    /*! \copydoc gmx::AbstractOptionStorage::processSet()
+     *
+     * OptionStorageTemplate implements transaction support for a set of
+     * values in this method (see the class description), and provides a
+     * more detailed processSetValues() method that can be overridden in
+     * subclasses to process the actual values.  Derived classes should
+     * override that method instead of this one if set value processing is
+     * necessary.
+     */
+    void processSet() override;
+    /*! \copydoc gmx::AbstractOptionStorage::processAll()
+     *
+     * The implementation in OptionStorageTemplate does nothing.
+     */
+    void processAll() override {}
+    /*! \brief
+     * Formats a single value as a string.
+     *
+     * \param[in] value  Value to format.
+     * \returns   \p value formatted as a string.
+     *
+     * The derived class must provide this method to format values a
+     * strings.  Called by defaultValuesAsStrings() to do the actual
+     * formatting.
+     */
+    virtual std::string formatSingleValue(const T& value) const = 0;
+
+    /*! \brief
+     * Adds a value to a temporary storage.
+     *
+     * \param[in] value  Value to add. A copy is made.
+     * \throws std::bad_alloc if out of memory.
+     * \throws InvalidInputError if the maximum value count has been reached.
+     *
+     * Derived classes should call this function from the convertValue()
+     * implementation to add converted values to the storage.
+     * If the maximum value count has been reached, the value is discarded
+     * and an exception is thrown.
+     *
+     * If adding values outside convertValue() (e.g., to set a custom
+     * default value), derived classes should call clearSet() before adding
+     * values (unless in the constructor) and commitValues() once all
+     * values are added.
+     */
+    void addValue(const T& value);
+    /*! \brief
+     * Commits values added with addValue().
+     *
+     * \throws std::bad_alloc if out of memory.
+     *
+     * If this function succeeds, values added with addValue() since the
+     * previous clearSet() are added to the storage for the option.
+     * Only throws in out-of-memory conditions, and provides the strong
+     * exception safety guarantee as long as the copy constructor of `T`
+     * does not throw.
+     *
+     * See addValue() for cases where this method should be used in derived
+     * classes.
+     *
+     * Calls clearSet() if it is successful.
+     */
+    void commitValues();
+
+    /*! \brief
+     * Sets the default value for the option.
+     *
+     * \param[in] value  Default value to set.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * This method can be used from the derived class constructor to
+     * programmatically set a default value.
+     */
+    void setDefaultValue(const T& value);
+    /*! \brief
+     * Sets the default value if set for the option.
+     *
+     * \param[in] value  Default value to set.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * This method can be used from the derived class constructor to
+     * programmatically set a default value.
+     */
+    void setDefaultValueIfSet(const T& value);
+
+    /*! \brief
+     * Provides derived classes access to the current list of values.
+     *
+     * The non-const any should only be used from processAll() in
+     * derived classes if necessary.
+     */
+    ArrayRef<T> values() { return store_->values(); }
+    //! Provides derived classes access to the current list of values.
+    ArrayRef<const T> values() const { return store_->values(); }
+
+private:
+    //! Creates the internal storage object for final values..
+    StorePointer createStore(ValueList* storeVector, T* store, int* storeCount, int initialCount);
+
+    /*! \brief
+     * Vector for temporary storage of values before commitSet() is called.
+     */
+    ValueList setValues_;
+    //! Final storage for option values.
+    const StorePointer store_;
+    // This never releases ownership.
+    std::unique_ptr<T> defaultValueIfSet_;
+
+    // Copy and assign disallowed by base.
+};
+
+
+/*! \libinternal \brief
+ * Simplified option storage template for options that have one-to-one value
+ * conversion.
+ *
+ * \tparam T Assignable type that stores a single option value.
+ *
+ * To implement an option that always map a single input value to a single
+ * output value, derive from this class instead of OptionStorageTemplate.
+ * This class implements convertValue() in terms of two new virtual methods:
+ * initConverter() and processValue().
+ *
+ * To specify how different types of values need to be converted, implement
+ * initConverter().
+ * To do common post-processing of the values after conversion, but before they
+ * are added to the underlying storage, override processValue().
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+template<typename T>
+class OptionStorageTemplateSimple : public OptionStorageTemplate<T>
+{
+public:
+    //! Alias for the template class for use in base classes.
+    typedef OptionStorageTemplateSimple<T> MyBase;
+
+protected:
+    //! Alias for the converter parameter type for initConverter().
+    typedef OptionValueConverterSimple<T> ConverterType;
+
+    //! Initializes the storage.
+    template<class U>
+    explicit OptionStorageTemplateSimple(const OptionTemplate<T, U>& settings,
+                                         OptionFlags                 staticFlags = OptionFlags()) :
+        OptionStorageTemplate<T>(settings, staticFlags), initialized_(false)
+    {
+    }
+    //! Initializes the storage.
+    OptionStorageTemplateSimple(const AbstractOption&                           settings,
+                                typename OptionStorageTemplate<T>::StorePointer store) :
+        OptionStorageTemplate<T>(settings, std::move(store)), initialized_(false)
+    {
+    }
+
+    std::vector<Any> normalizeValues(const std::vector<Any>& values) const override
+    {
+        const_cast<MyBase*>(this)->ensureConverterInitialized();
+        std::vector<Any> result;
+        result.reserve(values.size());
+        for (const auto& value : values)
+        {
+            result.push_back(normalizeValue(converter_.convert(value)));
+        }
+        return result;
+    }
+
+    /*! \brief
+     * Specifies how different types are converted.
+     *
+     * See OptionValueConverterSimple for more details.
+     */
+    virtual void initConverter(ConverterType* converter) = 0;
+    /*! \brief
+     * Post-processes a value after conversion to the output type.
+     *
+     * \param[in] value  Value after conversion.
+     * \returns   Value to store for the option.
+     *
+     * The default implementation only provides an identity mapping.
+     */
+    virtual T processValue(const T& value) const { return value; }
+    /*! \brief
+     * Converts a single value to normalized type.
+     *
+     * \param[in] value  Value after conversion.
+     * \returns   Value to store for the option.
+     *
+     * This can be overridden to serialize a different type than `T`
+     * when using the option with KeyValueTreeObject.
+     */
+    virtual Any normalizeValue(const T& value) const { return Any::create<T>(processValue(value)); }
+
+private:
+    void convertValue(const Any& any) override
+    {
+        ensureConverterInitialized();
+        this->addValue(processValue(converter_.convert(any)));
+    }
+    void ensureConverterInitialized()
+    {
+        if (!initialized_)
+        {
+            initConverter(&converter_);
+            initialized_ = true;
+        }
+    }
+
+    ConverterType converter_;
+    bool          initialized_;
+};
+
+
+/********************************************************************
+ * OptionStorageTemplate implementation
+ */
+
+template<typename T>
+template<class U>
+OptionStorageTemplate<T>::OptionStorageTemplate(const OptionTemplate<T, U>& settings,
+                                                OptionFlags                 staticFlags) :
+    AbstractOptionStorage(settings, staticFlags),
+    store_(createStore(settings.storeVector_,
+                       settings.store_,
+                       settings.countptr_,
+                       (settings.isVector() ? settings.maxValueCount_ : settings.minValueCount_)))
+{
+    if (hasFlag(efOption_NoDefaultValue)
+        && (settings.defaultValue_ != nullptr || settings.defaultValueIfSet_ != nullptr))
+    {
+        GMX_THROW(APIError("Option does not support default value, but one is set"));
+    }
+    if (!hasFlag(efOption_NoDefaultValue))
+    {
+        setFlag(efOption_HasDefaultValue);
+        if (settings.defaultValue_ != nullptr)
+        {
+            setDefaultValue(*settings.defaultValue_);
+        }
+        if (settings.defaultValueIfSet_ != nullptr)
+        {
+            setDefaultValueIfSet(*settings.defaultValueIfSet_);
+        }
+    }
+}
+
+
+template<typename T>
+OptionStorageTemplate<T>::OptionStorageTemplate(const AbstractOption& settings, StorePointer store) :
+    AbstractOptionStorage(settings, OptionFlags()), store_(std::move(store))
+{
+}
+
+
+template<typename T>
+std::unique_ptr<IOptionValueStore<T>> OptionStorageTemplate<T>::createStore(ValueList* storeVector,
+                                                                            T*         store,
+                                                                            int* storeCount, // NOLINT(readability-non-const-parameter) passed non-const to OptionValueStorePlain
+                                                                            int initialCount)
+{
+    if (storeVector != nullptr)
+    {
+        GMX_RELEASE_ASSERT(store == nullptr && storeCount == nullptr,
+                           "Cannot specify more than one storage location");
+        return StorePointer(new OptionValueStoreVector<T>(storeVector));
+    }
+    else if (store != nullptr)
+    {
+        // If the maximum number of values is not known, storage to
+        // caller-allocated memory is unsafe.
+        if (maxValueCount() < 0 || hasFlag(efOption_MultipleTimes))
+        {
+            GMX_THROW(APIError("Cannot set user-allocated storage for arbitrary number of values"));
+        }
+        if (storeCount == nullptr && !isVector() && minValueCount() != maxValueCount())
+        {
+            GMX_THROW(
+                    APIError("Count storage is not set, although the number of produced values is "
+                             "not known"));
+        }
+        if (hasFlag(efOption_NoDefaultValue))
+        {
+            initialCount = 0;
+        }
+        return StorePointer(new OptionValueStorePlain<T>(store, storeCount, initialCount));
+    }
+    GMX_RELEASE_ASSERT(storeCount == nullptr, "Cannot specify count storage without value storage");
+    return StorePointer(new OptionValueStoreNull<T>());
+}
+
+
+template<typename T>
+std::vector<Any> OptionStorageTemplate<T>::defaultValues() const
+{
+    std::vector<Any> result;
+    if (hasFlag(efOption_NoDefaultValue))
+    {
+        return result;
+    }
+    GMX_RELEASE_ASSERT(
+            hasFlag(efOption_HasDefaultValue),
+            "Current option implementation can only provide default values before assignment");
+    for (const auto& value : values())
+    {
+        result.push_back(Any::create<T>(value));
+    }
+    return normalizeValues(result);
+}
+
+
+template<typename T>
+std::vector<std::string> OptionStorageTemplate<T>::defaultValuesAsStrings() const
+{
+    std::vector<std::string> result;
+    if (hasFlag(efOption_NoDefaultValue))
+    {
+        return result;
+    }
+    GMX_RELEASE_ASSERT(
+            hasFlag(efOption_HasDefaultValue),
+            "Current option implementation can only provide default values before assignment");
+    for (const auto& value : values())
+    {
+        result.push_back(formatSingleValue(value));
+    }
+    if (result.empty() || (result.size() == 1 && result[0].empty()))
+    {
+        result.clear();
+        if (defaultValueIfSet_ != nullptr)
+        {
+            result.push_back(formatSingleValue(*defaultValueIfSet_));
+        }
+    }
+    return result;
+}
+
+
+template<typename T>
+void OptionStorageTemplate<T>::clearSet()
+{
+    setValues_.clear();
+}
+
+
+template<typename T>
+void OptionStorageTemplate<T>::processSet()
+{
+    processSetValues(&setValues_);
+    if (setValues_.empty() && defaultValueIfSet_ != nullptr)
+    {
+        addValue(*defaultValueIfSet_);
+        setFlag(efOption_HasDefaultValue);
+    }
+    else
+    {
+        clearFlag(efOption_HasDefaultValue);
+    }
+    if (!hasFlag(efOption_DontCheckMinimumCount)
+        && setValues_.size() < static_cast<size_t>(minValueCount()))
+    {
+        GMX_THROW(InvalidInputError("Too few (valid) values"));
+    }
+    commitValues();
+}
+
+
+template<typename T>
+void OptionStorageTemplate<T>::addValue(const T& value)
+{
+    if (maxValueCount() >= 0 && setValues_.size() >= static_cast<size_t>(maxValueCount()))
+    {
+        GMX_THROW(InvalidInputError("Too many values"));
+    }
+    setValues_.push_back(value);
+}
+
+template<typename T>
+void OptionStorageTemplate<T>::commitValues()
+{
+    if (hasFlag(efOption_ClearOnNextSet))
+    {
+        store_->clear();
+    }
+    store_->reserve(setValues_.size());
+    // For bool the loop variable isn't a reference (it's its special reference type)
+    // clang-format off
+    CLANG_DIAGNOSTIC_IGNORE(-Wrange-loop-analysis);
+    // clang-format on
+    for (const auto& value : setValues_)
+    {
+        store_->append(value);
+    }
+    CLANG_DIAGNOSTIC_RESET;
+    clearSet();
+}
+
+template<typename T>
+void OptionStorageTemplate<T>::setDefaultValue(const T& value)
+{
+    if (hasFlag(efOption_NoDefaultValue))
+    {
+        GMX_THROW(APIError("Option does not support default value, but one is set"));
+    }
+    if (hasFlag(efOption_HasDefaultValue))
+    {
+        setFlag(efOption_ExplicitDefaultValue);
+        store_->clear();
+        store_->append(value);
+    }
+}
+
+
+template<typename T>
+void OptionStorageTemplate<T>::setDefaultValueIfSet(const T& value)
+{
+    if (hasFlag(efOption_NoDefaultValue))
+    {
+        GMX_THROW(APIError("Option does not support default value, but one is set"));
+    }
+    if (hasFlag(efOption_MultipleTimes))
+    {
+        GMX_THROW(APIError("defaultValueIfSet() is not supported with allowMultiple()"));
+    }
+    setFlag(efOption_DefaultValueIfSetExists);
+    defaultValueIfSet_ = std::make_unique<T>(value);
+}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/optionsvisitor.h b/src/include/gromacs/options/optionsvisitor.h
new file mode 100644 (file)
index 0000000..64b888a
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2014,2016 by the GROMACS development team.
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::OptionsVisitor interface and supporting classes.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_OPTIONSVISITOR_H
+#define GMX_OPTIONS_OPTIONSVISITOR_H
+
+#include <cstddef>
+
+#include <memory>
+#include <string>
+
+#include "gromacs/options/abstractoption.h"
+#include "gromacs/utility/classhelpers.h"
+
+namespace gmx
+{
+
+class Options;
+class OptionSectionInfo;
+
+namespace internal
+{
+class OptionSectionImpl;
+}
+
+/*! \libinternal \brief
+ * Pure interface for visiting options in a Options object.
+ *
+ * \see OptionsIterator
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class OptionsVisitor
+{
+public:
+    virtual ~OptionsVisitor() {}
+
+    //! Called for each section.
+    virtual void visitSection(const OptionSectionInfo& section) = 0;
+    //! Called for each option.
+    virtual void visitOption(const OptionInfo& option) = 0;
+};
+
+/*! \libinternal \brief
+ * Abstract base class for visiting options of a particular type.
+ *
+ * \see OptionsIterator
+ * \see OptionsVisitor
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+template<class InfoType>
+class OptionsTypeVisitor : public OptionsVisitor
+{
+public:
+    ~OptionsTypeVisitor() override {}
+
+    void visitSection(const OptionSectionInfo& section) override = 0;
+    /*! \brief
+     * Called for each option of type \p InfoType.
+     */
+    virtual void visitOptionType(const InfoType& option) = 0;
+
+private:
+    void visitOption(const OptionInfo& option) override
+    {
+        const InfoType* subtype = option.toType<InfoType>();
+        if (subtype != NULL)
+        {
+            visitOptionType(*subtype);
+        }
+    }
+};
+
+/*! \libinternal \brief
+ * Decorator class for visiting options in a Options object.
+ *
+ * This class provides an interface for looping through sections and
+ * options in a Options object.
+ *
+ * Typical use (loop over all options, iteratively descending into
+ * subsections):
+ * \code
+   class Visitor : public gmx::OptionsVisitor
+   {
+       public:
+           virtual void visitSection(const OptionSectionInfo &section)
+           {
+               OptionsIterator iterator(section);
+               iterator.acceptSections(this);
+               iterator.acceptOptions(this);
+           }
+
+           virtual void visitOption(const OptionInfo &option)
+           {
+               // Do something.
+           }
+   }
+
+   Visitor().visitSection(options.rootSection());
+ * \endcode
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class OptionsIterator
+{
+public:
+    /*! \brief
+     * Creates an object for visiting options in a Options object.
+     *
+     * The created iterator iterates over the "root" section in the Options
+     * object.
+     */
+    explicit OptionsIterator(const Options& options);
+    //! Creates an object for visiting options in an options section.
+    explicit OptionsIterator(const OptionSectionInfo& section);
+
+    //! Visits each section in the wrapped section.
+    void acceptSections(OptionsVisitor* visitor) const;
+    //! Visits each option in the wrapped section.
+    void acceptOptions(OptionsVisitor* visitor) const;
+
+private:
+    //! The wrapped section object.
+    const internal::OptionSectionImpl& section_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(OptionsIterator);
+};
+
+/*! \libinternal \brief
+ * Pure interface for visiting options in a Options object, allowing
+ * modifications.
+ *
+ * \see OptionsModifyingIterator
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class OptionsModifyingVisitor
+{
+public:
+    virtual ~OptionsModifyingVisitor() {}
+
+    //! Called for each section.
+    virtual void visitSection(OptionSectionInfo* section) = 0;
+    //! Called for each option.
+    virtual void visitOption(OptionInfo* option) = 0;
+};
+
+/*! \libinternal \brief
+ * Abstract base class for visiting options of a particular type, allowing
+ * modifications.
+ *
+ * \see OptionsModifyingIterator
+ * \see OptionsModifyingVisitor
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+template<class InfoType>
+class OptionsModifyingTypeVisitor : public OptionsModifyingVisitor
+{
+public:
+    ~OptionsModifyingTypeVisitor() override {}
+
+    void visitSection(OptionSectionInfo* section) override = 0;
+    /*! \brief
+     * Called for each option of type \p InfoType.
+     */
+    virtual void visitOptionType(InfoType* option) = 0;
+
+private:
+    void visitOption(OptionInfo* option) override
+    {
+        InfoType* subtype = option->toType<InfoType>();
+        if (subtype != nullptr)
+        {
+            visitOptionType(subtype);
+        }
+    }
+};
+
+/*! \libinternal \brief
+ * Decorator class for visiting options in a Options object, allowing changes.
+ *
+ * This class works exactly like OptionsIterator, except that it uses
+ * OptionsModifyingVisitor interface, which allows modifying the options.
+ *
+ * \see OptionsIterator
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+class OptionsModifyingIterator
+{
+public:
+    /*! \brief
+     * Creates an object for visiting options in a Options object.
+     *
+     * The created iterator iterates over the "root" section in the Options
+     * object.
+     */
+    explicit OptionsModifyingIterator(Options* options);
+    //! Creates an object for visiting options in an options section.
+    explicit OptionsModifyingIterator(OptionSectionInfo* section);
+
+    //! Visits each section in the wrapped section.
+    void acceptSections(OptionsModifyingVisitor* visitor) const;
+    //! Visits each option in the wrapped section.
+    void acceptOptions(OptionsModifyingVisitor* visitor) const;
+
+private:
+    //! The wrapped section object.
+    internal::OptionSectionImpl& section_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(OptionsModifyingIterator);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/repeatingsection.h b/src/include/gromacs/options/repeatingsection.h
new file mode 100644 (file)
index 0000000..8325859
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::RepeatingOptionSection and related classes.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_REPEATINGSECTION_H
+#define GMX_OPTIONS_REPEATINGSECTION_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/options/ioptionscontainerwithsections.h"
+
+#include "abstractsection.h"
+#include "isectionstorage.h"
+#include "valuestore.h"
+
+namespace gmx
+{
+
+template<class T>
+class RepeatingOptionSectionHandle;
+template<class T>
+class RepeatingOptionSectionStorage;
+
+/*! \brief
+ * Declares an option section that creates a structure for each instance.
+ *
+ * \tparam  T  Structure that stores the values for an instance of the section.
+ *
+ * This class declares a section that can be specified multiple times, and
+ * creates an instance of `T` for each instance.  Options within the section
+ * can store their values in the created structure.
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+template<class T>
+class RepeatingOptionSection : public AbstractOptionSection
+{
+public:
+    //! AbstractOptionSectionHandle corresponding to this option type.
+    typedef RepeatingOptionSectionHandle<T> HandleType;
+
+    //! Creates a section with the given name.
+    explicit RepeatingOptionSection(const char* name) :
+        AbstractOptionSection(name), values_(nullptr)
+    {
+    }
+
+    //! Specifies a vector to receive the section structures.
+    RepeatingOptionSection& storeVector(std::vector<T>* values)
+    {
+        values_ = values;
+        return *this;
+    }
+
+private:
+    std::unique_ptr<IOptionSectionStorage> createStorage() const override;
+
+    std::vector<T>* values_;
+
+    //! Allows accessing the properties when initializing the storage.
+    friend class RepeatingOptionSectionStorage<T>;
+};
+
+/*! \internal
+ * \brief
+ * Implements handling of the structures that stores per-section values.
+ *
+ * \ingroup module_options
+ */
+template<class T>
+class RepeatingOptionSectionStorage : public IOptionSectionStorage
+{
+public:
+    //! Initializes the storage for given section properties.
+    explicit RepeatingOptionSectionStorage(const RepeatingOptionSection<T>& section) :
+        store_(new OptionValueStoreVector<T>(section.values_)), currentData_()
+    {
+    }
+
+    void initStorage() override { defaultValues_ = currentData_; }
+    void startSection() override { resetSection(); }
+    void finishSection() override
+    {
+        store_->append(currentData_);
+        resetSection();
+    }
+
+private:
+    void resetSection() { currentData_ = defaultValues_; }
+
+    //! Final storage location for the section structures.
+    const std::unique_ptr<IOptionValueStore<T>> store_;
+    T                                           defaultValues_;
+    /*! \brief
+     * Stores the values for the current in-process section.
+     *
+     * Options within the section store their values to this structure, and
+     * they are then copied to the final storage when the section finishes.
+     */
+    T currentData_;
+
+    //! Allows binding option storage to the current section data structure.
+    friend class RepeatingOptionSectionHandle<T>;
+};
+
+template<class T>
+std::unique_ptr<IOptionSectionStorage> RepeatingOptionSection<T>::createStorage() const
+{
+
+    return std::make_unique<RepeatingOptionSectionStorage<T>>(*this);
+}
+
+/*! \brief
+ * Allows adding options to an RepeatingOptionSection.
+ *
+ * An instance of this class is returned from
+ * IOptionsContainerWithSections::addSection(), and supports adding options and
+ * subsections to a section created with OptionSection.
+ *
+ * Example:
+ * \code
+   struct SectionData { int value; }
+   // as class attribute
+   std::vector<SectionData> values;
+
+   void MyClass::initOptions(gmx::IOptionsContainerWithSections *options)
+   {
+       auto sec = options->addSection(gmx::RepeatingOptionSection<SectionData>("sec").storeVector(&values));
+       sec->addOption(gmx::IntegerOption("arg").store(&sec.bind().value));
+   }
+   \endcode
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+template<class T>
+class RepeatingOptionSectionHandle : public AbstractOptionSectionHandle
+{
+public:
+    //! Wraps a given section storage object.
+    explicit RepeatingOptionSectionHandle(internal::OptionSectionImpl* section) :
+        AbstractOptionSectionHandle(section),
+        storage_(getStorage<RepeatingOptionSectionStorage<T>>(section))
+    {
+    }
+
+    /*! \brief
+     * Supports storing option values within the per-section data structure.
+     *
+     * See class documentation for an example.
+     */
+    T& bind() { return storage_->currentData_; }
+
+private:
+    RepeatingOptionSectionStorage<T>* storage_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/timeunitmanager.h b/src/include/gromacs/options/timeunitmanager.h
new file mode 100644 (file)
index 0000000..ad62d20
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::TimeUnitManager.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_TIMEUNITMANAGER_H
+#define GMX_OPTIONS_TIMEUNITMANAGER_H
+
+#include <memory>
+
+#include "gromacs/fileio/oenv.h"
+#include "gromacs/options/ioptionsbehavior.h"
+#include "gromacs/utility/classhelpers.h"
+
+namespace gmx
+{
+
+class IOptionsContainer;
+class Options;
+
+/*! \brief
+ * Provides common functionality for time unit conversions.
+ *
+ * Methods/objects that need to deal with time units can either take a
+ * TimeUnitManager object, or they can take a TimeUnit value and construct a
+ * TimeUnitManager object internally.
+ *
+ * Default copy constructor and assignment are used: the copy is an independent
+ * object that is initialized with the same time unit as the original.
+ *
+ * \if internal
+ * \todo
+ * This class is independent of the options implementation.
+ * To ease reuse, it could be moved to the utility module, and only
+ * TimeUnitBehavior left here.
+ * \endif
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class TimeUnitManager
+{
+public:
+    //! Creates a time unit manager with the default (ps) time unit.
+    TimeUnitManager();
+    //! Creates a time unit manager with the given time unit.
+    explicit TimeUnitManager(TimeUnit unit);
+
+    //! Returns the currently selected time unit.
+    TimeUnit timeUnit() const { return timeUnit_; }
+    //! Set a new time unit for the manager.
+    void setTimeUnit(TimeUnit unit);
+
+    //! Returns a string constant corresponding to the current time unit.
+    const char* timeUnitAsString() const;
+
+    //! Returns the scaling factor to convert times to ps.
+    double timeScaleFactor() const;
+    //! Returns the scaling factor to convert times from ps.
+    double inverseTimeScaleFactor() const;
+
+private:
+    //! Currently set time unit for this manager.
+    TimeUnit timeUnit_;
+};
+
+/*! \brief
+ * Options behavior to add a time unit option.
+ *
+ * This class provides functionality to add a time unit option that affects the
+ * input unit for time options (specified with FloatOption::timeValue() or
+ * DoubleOption::timeValue()).  When options are finished, it scales each time
+ * option such that any user-given values are interpreted as given in the time
+ * unit specified by the user, and scaled to picoseconds.  Programmatically
+ * given values (e.g., as default values for the options) are not scaled.
+ *
+ * \inpublicapi
+ * \ingroup module_options
+ */
+class TimeUnitBehavior : public IOptionsBehavior
+{
+public:
+    TimeUnitBehavior();
+
+    //! Returns the current time unit.
+    TimeUnit timeUnit() const { return timeUnit_; }
+    //! Sets the time unit.
+    void setTimeUnit(TimeUnit unit);
+
+    /*! \brief
+     * Sets a storage location for the selected time unit.
+     *
+     * \param[in] store  Location that will receive the selected time unit.
+     *
+     * \p *store will be set to the time unit selected by the user (or
+     * programmatically).  The value is guaranteed to be set once the
+     * options have been finished.
+     */
+    void setTimeUnitStore(TimeUnit* store);
+
+    /*! \brief
+     * Sets the default time unit from an environment variable.
+     *
+     * This should be called before addTimeUnitOption() for consistent
+     * behavior.
+     */
+    void setTimeUnitFromEnvironment();
+    /*! \brief
+     * Adds a common option for selecting the time unit.
+     *
+     * \param[in,out] options Options to which the common option is added.
+     * \param[in]     name    Name of the option to add.
+     *
+     * Adds an enum option to \p options to select the time unit for this
+     * behavior.
+     */
+    void addTimeUnitOption(IOptionsContainer* options, const char* name);
+
+    // From IOptionsBehavior
+    void initBehavior(Options* /*options*/) override {}
+    void optionsFinishing(Options* options) override;
+    void optionsFinished() override {}
+
+private:
+    TimeUnit  timeUnit_;
+    TimeUnit* timeUnitStore_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(TimeUnitBehavior);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/treesupport.h b/src/include/gromacs/options/treesupport.h
new file mode 100644 (file)
index 0000000..142305c
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares functions for using keyvaluetree.h with Options.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_TREESUPPORT_H
+#define GMX_OPTIONS_TREESUPPORT_H
+
+namespace gmx
+{
+
+class IKeyValueTreeErrorHandler;
+class KeyValueTreeObject;
+class Options;
+
+//! \cond libapi
+
+/*! \libinternal \brief
+ * Assigns option values from a given KeyValueTreeObject.
+ *
+ * Each property with a simple value (or an array of simple values) is assigned
+ * to an option with the same name.  Objects and arrays of objects are assigned
+ * to sections with the same name.
+ *
+ * \ingroup module_options
+ */
+void assignOptionsFromKeyValueTree(Options*                   options,
+                                   const KeyValueTreeObject&  tree,
+                                   IKeyValueTreeErrorHandler* errorHandler);
+/*! \libinternal \brief
+ * Checks that a given KeyValueTreeObject can be assigned to given Options.
+ *
+ * Throws an exception if `tree` contains any values that are not recognized by
+ * `options`.  Does not verify the type of the values, only that an option with
+ * the correct names exists.
+ *
+ * \ingroup module_options
+ */
+void checkForUnknownOptionsInKeyValueTree(const KeyValueTreeObject& tree, const Options& options);
+/*! \libinternal \brief
+ * Adjusts a KeyValueTreeObject to the structure of given Options.
+ *
+ * Assumes that all values in the input KeyValueTreeObject are valid values for
+ * the options.  The output has all the values in the input, but in the order
+ * they are in the options.  Values are also converted to the native type for
+ * the underlying option (e.g., strings are parsed to integers if the option
+ * accepts those).  For any option that does not have a corresponding value in
+ * the input, the output has it with a default value (if one exists for the
+ * option).
+ *
+ * Any values in `tree` that do not have matching options are not present in
+ * the output.  If this is not desirable, call
+ * checkForUnknownOptionsInKeyValueTree() before calling this function to
+ * ensure that no such values are present.
+ *
+ * Does not currently work for option sections in an array.
+ *
+ * \ingroup module_options
+ */
+KeyValueTreeObject adjustKeyValueTreeFromOptions(const KeyValueTreeObject& tree, const Options& options);
+
+//! \endcond
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/valueconverter.h b/src/include/gromacs/options/valueconverter.h
new file mode 100644 (file)
index 0000000..1f64e41
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Provides gmx::OptionValueConverterSimple.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_VALUECONVERTER_H
+#define GMX_OPTIONS_VALUECONVERTER_H
+
+#include <functional>
+#include <map>
+#include <typeindex>
+
+#include "gromacs/utility/any.h"
+#include "gromacs/utility/exceptions.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Helper for converting from Any to a given type.
+ *
+ * \tparam OutType  Type this converter converts to.
+ *
+ * Default-constructed converter only supports identity mapping from the a
+ * Any holding `OutType`.  To add support for additional input types,
+ * provide conversion functions with addConverter().  To use a non-identity
+ * mapping for an `OutType` -> `OutType` conversion, provide an alternative
+ * conversion from `OutType` with addConverter().
+ *
+ * \inlibraryapi
+ * \ingroup module_options
+ */
+template<typename OutType>
+class OptionValueConverterSimple
+{
+public:
+    /*! \brief
+     * Converts a Any value to the output type.
+     *
+     * \returns  Converted value.
+     * \throws InvalidInputError If the input Any has a type that is
+     *     not recognized by any conversion.
+     */
+    OutType convert(const Any& value) const
+    {
+        std::type_index type(value.type());
+        auto            iter = converters_.find(type);
+        if (iter == converters_.end())
+        {
+            if (value.isType<OutType>())
+            {
+                return value.cast<OutType>();
+            }
+            GMX_THROW(InvalidInputError("Invalid type of value"));
+        }
+        return iter->second(value);
+    }
+
+    /*! \brief
+     * Adds a supported conversion.
+     *
+     * \tparam InType  Type to convert from.
+     * \param  func    Function to convert from `InType` to `OutType`.
+     */
+    template<typename InType>
+    void addConverter(std::function<OutType(const InType&)> func)
+    {
+        converters_[std::type_index(typeid(InType))] = [func](const Any& value) {
+            return func(value.cast<InType>());
+        };
+    }
+    /*! \brief
+     * Adds a supported conversion from a type that can be directly cast.
+     *
+     * \tparam InType  Type to convert from with a simple cast.
+     */
+    template<typename InType>
+    void addCastConversion()
+    {
+        converters_[std::type_index(typeid(InType))] = [](const Any& value) {
+            return static_cast<OutType>(value.cast<InType>());
+        };
+    }
+
+private:
+    typedef std::function<OutType(const Any& value)> ConversionFunction;
+
+    std::map<std::type_index, ConversionFunction> converters_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/options/valuestore.h b/src/include/gromacs/options/valuestore.h
new file mode 100644 (file)
index 0000000..b014f63
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares implementations for IOptionValueStore.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_options
+ */
+#ifndef GMX_OPTIONS_VALUESTORE_H
+#define GMX_OPTIONS_VALUESTORE_H
+
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+
+#include "ivaluestore.h"
+
+namespace gmx
+{
+
+template<typename T>
+class OptionValueStorePlain : public IOptionValueStore<T>
+{
+public:
+    OptionValueStorePlain(T* store, int* storeCount, int initialCount) :
+        count_(initialCount), store_(store), storeCount_(storeCount)
+    {
+    }
+
+    int         valueCount() override { return count_; }
+    ArrayRef<T> values() override { return arrayRefFromArray(store_, count_); }
+    void        clear() override
+    {
+        count_ = 0;
+        if (storeCount_ != nullptr)
+        {
+            *storeCount_ = count_;
+        }
+    }
+    void reserve(size_t /*count*/) override {}
+    void append(const T& value) override
+    {
+        store_[count_] = value;
+        ++count_;
+        if (storeCount_ != nullptr)
+        {
+            *storeCount_ = count_;
+        }
+    }
+
+private:
+    int  count_;
+    T*   store_;
+    int* storeCount_;
+};
+
+template<typename T>
+class OptionValueStoreVector : public IOptionValueStore<T>
+{
+public:
+    explicit OptionValueStoreVector(std::vector<T>* store) : store_(store) {}
+
+    int         valueCount() override { return static_cast<int>(store_->size()); }
+    ArrayRef<T> values() override { return *store_; }
+    void        clear() override { store_->clear(); }
+    void        reserve(size_t count) override { store_->reserve(store_->size() + count); }
+    void        append(const T& value) override { store_->push_back(value); }
+
+private:
+    std::vector<T>* store_;
+};
+
+// Specialization that works around std::vector<bool> specialities.
+template<>
+class OptionValueStoreVector<bool> : public IOptionValueStore<bool>
+{
+public:
+    explicit OptionValueStoreVector(std::vector<bool>* store) : store_(store) {}
+
+    int            valueCount() override { return static_cast<int>(store_->size()); }
+    ArrayRef<bool> values() override
+    {
+        return arrayRefFromArray(reinterpret_cast<bool*>(boolStore_.data()), boolStore_.size());
+    }
+    void clear() override
+    {
+        boolStore_.clear();
+        store_->clear();
+    }
+    void reserve(size_t count) override
+    {
+        boolStore_.reserve(boolStore_.size() + count);
+        store_->reserve(store_->size() + count);
+    }
+    void append(const bool& value) override
+    {
+        boolStore_.push_back({ value });
+        store_->push_back(value);
+    }
+
+private:
+    struct Bool
+    {
+        bool value;
+    };
+
+    std::vector<Bool>  boolStore_;
+    std::vector<bool>* store_;
+};
+
+/*! \internal
+ * \brief
+ * Value storage that does not store anywhere.
+ *
+ * This is needed because even though the values are not stored anywhere, the
+ * code still expects to access them later through valueCount() and values().
+ *
+ * \ingroup module_options
+ */
+template<typename T>
+class OptionValueStoreNull : public IOptionValueStore<T>
+{
+public:
+    OptionValueStoreNull() : store_(&vector_) {}
+
+    int         valueCount() override { return store_.valueCount(); }
+    ArrayRef<T> values() override { return store_.values(); }
+    void        clear() override { store_.clear(); }
+    void        reserve(size_t count) override { store_.reserve(count); }
+    void        append(const T& value) override { store_.append(value); }
+
+private:
+    std::vector<T>            vector_;
+    OptionValueStoreVector<T> store_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/pbcutil/boxutilities.h b/src/include/gromacs/pbcutil/boxutilities.h
new file mode 100644 (file)
index 0000000..a40d02e
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_PBCUTIL_BOXUTILITIES_H
+#define GMX_PBCUTIL_BOXUTILITIES_H
+
+#include <stdio.h>
+
+#include "gromacs/math/vectypes.h"
+
+/*! \brief Change box components to preserve the relative box shape
+ *
+ * Change box components to box[XX][XX]*box_rel to preserve the relative box shape
+ */
+void do_box_rel(int ndim, const matrix deform, matrix box_rel, matrix b, bool bInit);
+
+namespace gmx
+{
+
+/*! \brief
+ * Returns whether two boxes are of equal size and shape (within reasonable
+ * tolerance).
+ */
+bool boxesAreEqual(const matrix box1, const matrix box2);
+
+/*! \brief
+ * Returns whether a box is only initialised to zero or not.
+ */
+bool boxIsZero(const matrix box);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/pbcutil/com.h b/src/include/gromacs/pbcutil/com.h
new file mode 100644 (file)
index 0000000..e2456e3
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Helper methods to place particle COM in boxes.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_pbcutil
+ */
+#ifndef GMX_PBCUTIL_COM_H
+#define GMX_PBCUTIL_COM_H
+
+#include <algorithm>
+#include <memory>
+
+#include "gromacs/math/vec.h"
+#include "gromacs/utility/arrayref.h"
+
+#include "pbcenums.h"
+
+struct gmx_mtop_t;
+enum class PbcType : int;
+namespace gmx
+{
+
+//! How COM shifting should be applied.
+enum class COMShiftType : int
+{
+    Residue,
+    Molecule,
+    Count
+};
+
+/*! \brief
+ * Shift all coordinates.
+ *
+ * Shift coordinates by a previously calculated value.
+ *
+ * Can be used to e.g. place particles in a box.
+ *
+ * \param[in] shift Translation that should be applied.
+ * \param[in] x Coordinates to translate.
+ */
+void shiftAtoms(const RVec& shift, ArrayRef<RVec> x);
+
+/*! \brief
+ * Moves collection of atoms along the center of mass into a box.
+ *
+ * This ensures that the centre of mass (COM) of a molecule is placed
+ * within a predefined coordinate space (usually a simulation box).
+ *
+ * \param[in]      pbcType      What kind of PBC are we handling today.
+ * \param[in]      unitCellType Kind of unitcell used for the box.
+ * \param[in]      centerType   How atoms should be centered.
+ * \param[in]      box          The currently available box to place things into.
+ * \param[in, out] x            View in coordinates to shift.
+ * \param[in]      mtop         Topology with residue and molecule information.
+ * \param[in]      comShiftType Whether residues or molecules are shifted.
+ */
+void placeCoordinatesWithCOMInBox(const PbcType&    pbcType,
+                                  UnitCellType      unitCellType,
+                                  CenteringType     centerType,
+                                  const matrix      box,
+                                  ArrayRef<RVec>    x,
+                                  const gmx_mtop_t& mtop,
+                                  COMShiftType      comShiftType);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/pbcutil/ishift.h b/src/include/gromacs/pbcutil/ishift.h
new file mode 100644 (file)
index 0000000..7a25fd3
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_PBCUTIL_ISHIFT_H
+#define GMX_PBCUTIL_ISHIFT_H
+
+namespace gmx
+{
+//! Maximum dimensions of grid expressing shifts across PBC
+//! \{
+constexpr int c_dBoxZ = 1;
+constexpr int c_dBoxY = 1;
+constexpr int c_dBoxX = 2;
+//! \}
+namespace detail
+{
+constexpr int c_nBoxZ    = 2 * gmx::c_dBoxZ + 1;
+constexpr int c_nBoxY    = 2 * gmx::c_dBoxY + 1;
+constexpr int c_nBoxX    = 2 * gmx::c_dBoxX + 1;
+constexpr int c_numIvecs = detail::c_nBoxZ * detail::c_nBoxY * detail::c_nBoxX;
+} // namespace detail
+
+constexpr int c_centralShiftIndex = detail::c_numIvecs / 2;
+constexpr int c_numShiftVectors   = detail::c_numIvecs;
+
+//! Convert grid coordinates to shift index
+static inline int xyzToShiftIndex(int x, int y, int z)
+{
+    return (detail::c_nBoxX * (detail::c_nBoxY * ((z) + gmx::c_dBoxZ) + (y) + gmx::c_dBoxY) + (x)
+            + gmx::c_dBoxX);
+}
+
+//! Convert grid coordinates to shift index
+static inline int ivecToShiftIndex(ivec iv)
+{
+    return (xyzToShiftIndex((iv)[XX], (iv)[YY], (iv)[ZZ]));
+}
+
+//! Return the shift in the X dimension of grid space corresponding to \c iv
+static inline int shiftIndexToXDim(int iv)
+{
+    return (((iv) % detail::c_nBoxX) - gmx::c_dBoxX);
+}
+} // namespace gmx
+#endif
diff --git a/src/include/gromacs/pbcutil/mshift.h b/src/include/gromacs/pbcutil/mshift.h
new file mode 100644 (file)
index 0000000..7b3838c
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_PBCUTIL_MSHIFT_H
+#define GMX_PBCUTIL_MSHIFT_H
+
+#include <cstdio>
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/listoflists.h"
+
+struct InteractionList;
+struct gmx_moltype_t;
+class InteractionDefinitions;
+struct t_idef;
+enum class PbcType : int;
+
+typedef enum
+{
+    egcolWhite,
+    egcolGrey,
+    egcolBlack,
+    egcolNR
+} egCol;
+
+/* Struct used to make molecules broken over PBC whole
+ *
+ * TODO: Should be turned into a proper class
+ */
+struct t_graph
+{
+    /* Describes the connectivity between, potentially, multiple parts of
+     * the ilist that are internally chemically bonded together.
+     */
+    enum class BondedParts
+    {
+        Single,               /* All atoms are connected through chemical bonds */
+        MultipleDisconnected, /* There are multiple parts, e.g. monomers, that are all disconnected */
+        MultipleConnected /* There are multiple parts, e.g. monomers, that are partially or fully connected between each other by interactions other than chemical bonds */
+    };
+
+    // Returns the number of nodes stored in the graph (can be less than shiftAtomEnd)
+    int numNodes() const { return edges.size(); }
+
+    // Shift atoms up to shiftAtomEnd
+    int shiftAtomEnd = 0;
+    // The number of atoms that are connected to other atoms in the graph
+    int numConnectedAtoms = 0;
+    // The first connected atom in the graph
+    int edgeAtomBegin = 0;
+    // The last connected atom in the graph
+    int edgeAtomEnd = 0;
+    //  The graph: list of atoms connected to each atom, indexing is offset by -edgeAtomBegin
+    gmx::ListOfLists<int> edges;
+    // Whether we are using screw PBC
+    bool useScrewPbc = false;
+    // Shift for each particle, updated after putting atoms in the box
+    std::vector<gmx::IVec> ishift;
+    // Work buffer for coloring nodes
+    std::vector<egCol> edgeColor;
+    // Tells how connected this graph is
+    BondedParts parts = BondedParts::Single;
+};
+
+#define SHIFT_IVEC(g, i) ((g)->ishift[i])
+
+t_graph mk_graph(const InteractionDefinitions& idef, int numAtoms);
+/* Build a graph from an idef description. The graph can be used
+ * to generate mol-shift indices.
+ * numAtoms should coincide will molecule boundaries,
+ */
+
+t_graph* mk_graph(FILE*                         fplog,
+                  const InteractionDefinitions& idef,
+                  int                           shiftAtomEnd,
+                  gmx_bool                      bShakeOnly,
+                  gmx_bool                      bSettle);
+/* Build a graph from an idef description. The graph can be used
+ * to generate mol-shift indices.
+ * Shifts atoms up to shiftAtomEnd, which should coincide with a molecule boundary,
+ * for the whole system this is simply natoms.
+ * If bShakeOnly, only the connections in the shake list are used.
+ * If bSettle && bShakeOnly the settles are used too.
+ */
+
+t_graph* mk_graph(FILE* fplog, const struct t_idef* idef, int shiftAtomEnd, gmx_bool bShakeOnly, gmx_bool bSettle);
+/* Build a graph from an idef description. The graph can be used
+ * to generate mol-shift indices.
+ * Shifts atoms up to shiftAtomEnd, which should coincide with a molecule boundary,
+ * for the whole system this is simply natoms.
+ * If bShakeOnly, only the connections in the shake list are used.
+ * If bSettle && bShakeOnly the settles are used too.
+ */
+
+t_graph mk_graph_moltype(const gmx_moltype_t& moltype);
+/* As mk_graph, but takes gmx_moltype_t iso t_idef */
+
+
+void done_graph(t_graph* g);
+/* Free the memory in *g and the pointer g */
+
+void p_graph(FILE* log, const char* title, const t_graph* g);
+/* Print a graph to log */
+
+void mk_mshift(FILE* log, t_graph* g, PbcType pbcType, const matrix box, const rvec x[]);
+/* Calculate the mshift codes, based on the connection graph in g. */
+
+void shift_x(const t_graph* g, const matrix box, const rvec x[], rvec x_s[]);
+/* Add the shift vector to x, and store in x_s (may be same array as x) */
+
+void shift_self(const t_graph& g, const matrix box, rvec x[]);
+/* Id. but in place */
+
+void shift_self(const t_graph* g, const matrix box, rvec x[]);
+/* Id. but in place */
+
+void unshift_x(const t_graph* g, const matrix box, rvec x[], const rvec x_s[]);
+/* Subtract the shift vector from x_s, and store in x (may be same array) */
+
+void unshift_self(const t_graph* g, const matrix box, rvec x[]);
+/* Id, but in place */
+
+#endif
diff --git a/src/include/gromacs/pbcutil/pbc.h b/src/include/gromacs/pbcutil/pbc.h
new file mode 100644 (file)
index 0000000..8114df7
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_PBCUTIL_PBC_H
+#define GMX_PBCUTIL_PBC_H
+
+#include <stdio.h>
+
+#include <string>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_domdec_t;
+struct gmx_mtop_t;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+} // namespace gmx
+
+//! Names for all values in PBC types enumeration
+extern const gmx::EnumerationArray<PbcType, std::string> c_pbcTypeNames;
+
+/* Maximum number of combinations of single triclinic box vectors
+ * required to shift atoms that are within a brick of the size of
+ * the diagonal of the box to within the maximum cut-off distance.
+ */
+#define MAX_NTRICVEC 12
+
+/*! \brief Structure containing info on periodic boundary conditions */
+typedef struct t_pbc
+{
+    //! The PBC type
+    PbcType pbcType;
+    //! Number of dimensions in which PBC is exerted
+    int ndim_ePBC;
+    /*! \brief Determines how to compute distance vectors.
+     *
+     *  Indicator of how to compute distance vectors, depending
+     *  on PBC type (depends on pbcType and dimensions with(out) DD)
+     *  and the box angles.
+     */
+    int pbcTypeDX;
+    /*! \brief Used for selecting which dimensions to use in PBC.
+     *
+     *  In case of 1-D PBC this indicates which dimension is used,
+     *  in case of 2-D PBC this indicates the opposite
+     */
+    int dim;
+    //! The simulation box
+    matrix box;
+    //! The lengths of the diagonal of the full box
+    rvec fbox_diag;
+    //! Halve of the above
+    rvec hbox_diag;
+    //! Negative of the above
+    rvec mhbox_diag;
+    //! Maximum allowed cutoff squared for the box and PBC used
+    real max_cutoff2;
+    /*! \brief Number of triclinic shift vectors.
+     *
+     *  Number of triclinic shift vectors depends on the skewedness
+     *  of the box, that is mostly on the angles. For triclinic boxes
+     *  we first take the closest image along each Cartesian dimension
+     *  independently. When the resulting distance^2 is larger than
+     *  max_cutoff2, up to ntric_vec triclinic shift vectors need to
+     *  be tried. Because of the restrictions imposed on the unit-cell
+     *  by GROMACS, ntric_vec <= MAX_NTRICVEC = 12.
+     */
+    int ntric_vec;
+    //! The triclinic shift vectors in grid cells. Internal use only.
+    ivec tric_shift[MAX_NTRICVEC];
+    //!  The triclinic shift vectors in length units
+    rvec tric_vec[MAX_NTRICVEC];
+} t_pbc;
+
+#define TRICLINIC(box) ((box)[YY][XX] != 0 || (box)[ZZ][XX] != 0 || (box)[ZZ][YY] != 0)
+
+#define NTRICIMG 14
+#define NCUCVERT 24
+#define NCUCEDGE 36
+
+enum
+{
+    ecenterTRIC, /* 0.5*(a+b+c)                  */
+    ecenterRECT, /* (0.5*a[x],0.5*b[y],0.5*c[z]) */
+    ecenterZERO, /* (0,0,0)                      */
+    ecenterDEF = ecenterTRIC
+};
+
+/*! \brief Returns the number of dimensions that use pbc
+ *
+ * \param[in] pbcType The periodic boundary condition type
+ * \return the number of dimensions that use pbc, starting at X
+ */
+int numPbcDimensions(PbcType pbcType);
+
+/*! \brief Dump the contents of the pbc structure to the file
+ *
+ * \param[in] fp  The file pointer to write to
+ * \param[in] pbc The periodic boundary condition information structure
+ */
+void dump_pbc(FILE* fp, t_pbc* pbc);
+
+/*! \brief Check the box for consistency
+ *
+ * When \p pbcType=PbcTypes::Unset, the type of pbc is guessed from the box matrix.
+ *
+ * \param[in] pbcType The pbc identifier
+ * \param[in] box     The box matrix
+ * \return NULL if the box is supported by Gromacs.
+ *         Otherwise returns a string with the problem.
+ */
+const char* check_box(PbcType pbcType, const matrix box);
+
+/*! \brief Creates box matrix from edge lengths and angles.
+ *
+ * \param[in,out] box        The box matrix
+ * \param[in] vec            The edge lengths
+ * \param[in] angleInDegrees The angles
+ */
+void matrix_convert(matrix box, const rvec vec, const rvec angleInDegrees);
+
+/*! \brief Compute the maximum cutoff for the box
+
+ * Returns the square of the maximum cut-off allowed for the box,
+ * taking into account that the grid neighborsearch code and pbc_dx
+ * only check combinations of single box-vector shifts.
+ *
+ * \param[in] pbcType The pbc identifier
+ * \param[in] box  The box matrix
+ * \return the maximum cut-off.
+ */
+real max_cutoff2(PbcType pbcType, const matrix box);
+
+/*! \brief Guess PBC type
+ *
+ * Guesses the type of periodic boundary conditions using the box
+ *
+ * \param[in] box  The box matrix
+ * \return The pbc type identifier
+ */
+PbcType guessPbcType(const matrix box);
+
+/*! \brief Corrects the box if necessary
+ *
+ * Checks for un-allowed box angles and corrects the box.
+ *
+ * \param[in] fplog File for debug output
+ * \param[in] step  The MD step number
+ * \param[in] box   The simulation cell
+ * \return TRUE when the box was corrected.
+ */
+bool correct_box(FILE* fplog, int step, tensor box);
+
+/*! \brief Initiate the periodic boundary condition algorithms.
+ *
+ * pbc_dx will not use pbc and return the normal difference vector
+ * when one or more of the diagonal elements of box are zero.
+ * When \p pbcType=PbcType::Unset, the type of pbc is guessed from the box matrix.
+ *
+ * \param[in,out] pbc The pbc information structure
+ * \param[in] pbcType The PBC identifier
+ * \param[in] box     The box tensor
+ */
+void set_pbc(t_pbc* pbc, PbcType pbcType, const matrix box);
+
+/*! \brief Initiate the periodic boundary condition algorithms.
+ *
+ * As set_pbc, but additionally sets that correct distances can
+ * be obtained using (combinations of) single box-vector shifts.
+ * Should be used with pbc_dx_aiuc.
+ * If domdecCells!=NULL pbc is not used for directions
+ * with dd->nc[i]==1 with bSingleDir==TRUE or
+ * with dd->nc[i]<=2 with bSingleDir==FALSE.
+ * Note that when no PBC is required only pbc->pbcType is set,
+ * the rest of the struct will be invalid.
+ *
+ * \param[in,out] pbc     The pbc information structure
+ * \param[in] pbcType     The PBC identifier
+ * \param[in] domdecCells 3D integer vector describing the number of DD cells
+ *                        or nullptr if not using DD.
+ * \param[in] bSingleDir  TRUE if DD communicates only in one direction along dimensions
+ * \param[in] box         The box tensor
+ * \return the pbc structure when pbc operations are required, NULL otherwise.
+ */
+t_pbc* set_pbc_dd(t_pbc* pbc, PbcType pbcType, const ivec domdecCells, bool bSingleDir, const matrix box);
+
+/*! \brief Compute distance with PBC
+ *
+ * Calculate the correct distance vector from x2 to x1 and put it in dx.
+ * set_pbc must be called before ever calling this routine.
+ *
+ * Note that for triclinic boxes that do not obey the GROMACS unit-cell
+ * restrictions, pbc_dx and pbc_dx_aiuc will not correct for PBC.
+ * \param[in,out] pbc The pbc information structure
+ * \param[in]    x1  Coordinates for particle 1
+ * \param[in]    x2  Coordinates for particle 2
+ * \param[out]   dx  Distance vector
+ */
+void pbc_dx(const t_pbc* pbc, const rvec x1, const rvec x2, rvec dx);
+
+/*! \brief Compute distance vector for simple PBC types
+ *
+ * Calculate the correct distance vector from x2 to x1 and put it in dx,
+ * This function can only be used when all atoms are in the rectangular
+ * or triclinic unit-cell.
+ * set_pbc_dd or set_pbc must be called before ever calling this routine.
+ * \param[in,out] pbc The pbc information structure
+ * \param[in]    x1  Coordinates for particle 1
+ * \param[in]    x2  Coordinates for particle 2
+ * \param[out]   dx  Distance vector
+ * \return the ishift required to shift x1 at closest distance to x2;
+ * i.e. if 0<=ishift<c_numShiftVectors then x1 - x2 + shift_vec[ishift] = dx
+ * (see calc_shifts below on how to obtain shift_vec)
+ */
+int pbc_dx_aiuc(const t_pbc* pbc, const rvec x1, const rvec x2, rvec dx);
+
+/*! \brief Compute distance with PBC
+ *
+ * As pbc_dx, but for double precision vectors.
+ * set_pbc must be called before ever calling this routine.
+ * \param[in,out] pbc The pbc information structure
+ * \param[in]    x1  Coordinates for particle 1
+ * \param[in]    x2  Coordinates for particle 2
+ * \param[out]   dx  Distance vector
+ */
+void pbc_dx_d(const t_pbc* pbc, const dvec x1, const dvec x2, dvec dx);
+
+/*! \brief Computes shift vectors
+ *
+ * This routine calculates ths shift vectors necessary to use the
+ * neighbor searching routine.
+ * \param[in]  box       The simulation box
+ * \param[out] shift_vec The shifting vectors
+ */
+void calc_shifts(const matrix box, gmx::ArrayRef<gmx::RVec> shift_vec);
+
+/*! \brief Calculates the center of the box.
+ *
+ * See the description for the enum ecenter above.
+ * \param[in]  ecenter    Description of center type
+ * \param[in]  box        The simulation box
+ * \param[out] box_center The center of the box
+ */
+void calc_box_center(int ecenter, const matrix box, rvec box_center);
+
+/*! \brief Calculates the NTRICIMG box images
+ *
+ * \param[in]  box The simulation box
+ * \param[out] img The triclinic box images
+ */
+void calc_triclinic_images(const matrix box, rvec img[]);
+
+/*! \brief Calculates the NCUCVERT vertices of a compact unitcell
+ *
+ * \param[in]  ecenter The center type
+ * \param[in]  box     The simulation box
+ * \param[out] vert    The vertices
+ */
+void calc_compact_unitcell_vertices(int ecenter, const matrix box, rvec vert[]);
+
+/*! \brief Compute unitcell edges
+ *
+ * \return an array of unitcell edges of length NCUCEDGE*2,
+ * this is an index in vert[], which is calculated by calc_unitcell_vertices.
+ * The index consists of NCUCEDGE pairs of vertex indices.
+ * The index does not change, so it needs to be retrieved only once.
+ */
+int* compact_unitcell_edges();
+
+/*! \brief Put atoms inside the simulations box
+ *
+ * These routines puts ONE or ALL atoms in the box, not caring
+ * about charge groups!
+ * Also works for triclinic cells.
+ *
+ * \param[in]     pbcType The pbc type
+ * \param[in]     box     The simulation box
+ * \param[in,out] x       The coordinates of the atoms
+ */
+void put_atoms_in_box(PbcType pbcType, const matrix box, gmx::ArrayRef<gmx::RVec> x);
+
+/*! \brief Parallellizes put_atoms_in_box()
+ *
+ * This wrapper function around put_atoms_in_box() with the ugly manual
+ * workload splitting is needed to avoid silently introducing multithreading
+ * in tools.
+ *
+ * \param[in]     pbcType    The pbc type
+ * \param[in]     box        The simulation box
+ * \param[in,out] x          The coordinates of the atoms
+ * \param[in]     nth        number of threads to be used in the given module
+ */
+void put_atoms_in_box_omp(PbcType pbcType, const matrix box, gmx::ArrayRef<gmx::RVec> x, gmx_unused int nth);
+
+/*! \brief Put atoms inside triclinic box
+ *
+ * This puts ALL atoms in the triclinic unit cell, centered around the
+ * box center as calculated by calc_box_center.
+ * \param[in]    ecenter The pbc center type
+ * \param[in]    box     The simulation box
+ * \param[in,out] x       The coordinates of the atoms
+ */
+void put_atoms_in_triclinic_unitcell(int ecenter, const matrix box, gmx::ArrayRef<gmx::RVec> x);
+
+/*! \brief Put atoms inside the unitcell
+ *
+ * This puts ALL atoms at the closest distance for the center of the box
+ * as calculated by calc_box_center.
+ * When \p pbcType=PbcType::Unset, the type of pbc is guessed from the box matrix.
+ *
+ * \param[in]    pbcType The pbc type
+ * \param[in]    ecenter The pbc center type
+ * \param[in]    box     The simulation box
+ * \param[in,out] x      The coordinates of the atoms
+ */
+void put_atoms_in_compact_unitcell(PbcType pbcType, int ecenter, const matrix box, gmx::ArrayRef<gmx::RVec> x);
+
+/*! \brief Make all molecules whole by shifting positions
+ *
+ * \param[in]     fplog     Log file
+ * \param[in]     pbcType   The PBC type
+ * \param[in]     box       The simulation box
+ * \param[in]     mtop      System topology definition
+ * \param[in,out] x         The coordinates of the atoms
+ */
+void do_pbc_first_mtop(FILE* fplog, PbcType pbcType, const matrix box, const gmx_mtop_t* mtop, rvec x[]);
+
+/*! \brief Make molecules consisting of multiple charge groups whole by shifting positions
+ *
+ * \param[in]     pbcType   The PBC type
+ * \param[in]     box       The simulation box
+ * \param[in]     mtop      System topology definition
+ * \param[in,out] x         The coordinates of the atoms
+ */
+void do_pbc_mtop(PbcType pbcType, const matrix box, const gmx_mtop_t* mtop, rvec x[]);
+
+#endif
diff --git a/src/include/gromacs/pbcutil/pbc_aiuc.h b/src/include/gromacs/pbcutil/pbc_aiuc.h
new file mode 100644 (file)
index 0000000..97717d5
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ *
+ * \brief Structure and basic routines to handle periodic boundary conditions.
+ *
+ * This file contains CPU
+ *
+ * \todo CPU, GPU and SIMD routines essentially do the same operations on
+ *       different data-types. Currently this leads to code duplication,
+ *       which has to be resolved. For details, see Issue #2863
+ *       https://gitlab.com/gromacs/gromacs/-/issues/2863
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \author Berk Hess <hess@kth.se>
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_pbcutil
+ */
+#ifndef GMX_PBCUTIL_PBC_AIUC_H
+#define GMX_PBCUTIL_PBC_AIUC_H
+
+#include "gromacs/pbcutil/ishift.h"
+
+/*! \brief Compact and ordered version of the PBC matrix.
+ *
+ * The structure contains all the dimensions of the periodic box,
+ * arranged so that the memory access pattern is more efficient.
+ * This duplicates the information, stored in PBC 'box' matrix object,
+ * but without duplicating off-diagonal members of the matrix.
+ * The structure can be set by setPbcAiuc( ... ) routine below.
+ */
+struct PbcAiuc
+{
+    //! 1/box[ZZ][ZZ]
+    float invBoxDiagZ;
+    //! box[ZZ][XX]
+    float boxZX;
+    //! box[ZZ][YY]
+    float boxZY;
+    //! box[ZZ][ZZ]
+    float boxZZ;
+    //! 1/box[YY][YY]
+    float invBoxDiagY;
+    //! box[YY][XX]
+    float boxYX;
+    //! box[YY][YY]
+    float boxYY;
+    //! 1/box[XX][XX]
+    float invBoxDiagX;
+    //! box[XX][XX]
+    float boxXX;
+};
+
+/*! \brief Set the PBC data-structure.
+ *
+ * \param[in]   numPbcDim   Number of periodic dimensions:
+ *                          0 - no periodicity.
+ *                          1 - periodicity along X-axis.
+ *                          2 - periodicity in XY plane.
+ *                          3 - periodicity along all dimensions.
+ * \param[in]   box         Matrix, describing the periodic cell.
+ * \param[out]  pbcAiuc     Pointer to PbcAiuc structure to be initialized.
+ *
+ */
+static inline void setPbcAiuc(int numPbcDim, const matrix box, PbcAiuc* pbcAiuc)
+{
+
+    pbcAiuc->invBoxDiagZ = 0.0F;
+    pbcAiuc->boxZX       = 0.0F;
+    pbcAiuc->boxZY       = 0.0F;
+    pbcAiuc->boxZZ       = 0.0F;
+    pbcAiuc->invBoxDiagY = 0.0F;
+    pbcAiuc->boxYX       = 0.0F;
+    pbcAiuc->boxYY       = 0.0F;
+    pbcAiuc->invBoxDiagX = 0.0F;
+    pbcAiuc->boxXX       = 0.0F;
+
+    if (numPbcDim > ZZ)
+    {
+        pbcAiuc->invBoxDiagZ = 1.0F / box[ZZ][ZZ];
+        pbcAiuc->boxZX       = box[ZZ][XX];
+        pbcAiuc->boxZY       = box[ZZ][YY];
+        pbcAiuc->boxZZ       = box[ZZ][ZZ];
+    }
+    if (numPbcDim > YY)
+    {
+        pbcAiuc->invBoxDiagY = 1.0F / box[YY][YY];
+        pbcAiuc->boxYX       = box[YY][XX];
+        pbcAiuc->boxYY       = box[YY][YY];
+    }
+    if (numPbcDim > XX)
+    {
+        pbcAiuc->invBoxDiagX = 1.0F / box[XX][XX];
+        pbcAiuc->boxXX       = box[XX][XX];
+    }
+}
+
+/*! \brief Computes the vector between two points taking PBC into account.
+ *
+ * Computes the vector dr between points r2 and r1, taking into account the
+ * periodic boundary conditions, described in pbcAiuc object. Note that this
+ * routine always does the PBC arithmetic for all directions, multiplying the
+ * displacements by zeroes if the corresponding direction is not periodic.
+ * For triclinic boxes only distances up to half the smallest box diagonal
+ * element are guaranteed to be the shortest. This means that distances from
+ * 0.5/sqrt(2) times a box vector length (e.g. for a rhombic dodecahedron)
+ * can use a more distant periodic image.
+ *
+ * \todo This routine operates on rvec types and uses PbcAiuc to define
+ *       periodic box, but essentially does the same thing as SIMD and GPU
+ *       version. These will have to be unified in future to avoid code
+ *       duplication. See Issue #2863:
+ *       https://gitlab.com/gromacs/gromacs/-/issues/2863
+ *
+ * \param[in]  pbcAiuc  PBC object.
+ * \param[in]  r1       Coordinates of the first point.
+ * \param[in]  r2       Coordinates of the second point.
+ * \param[out] dr       Resulting distance.
+ */
+static inline void pbcDxAiuc(const PbcAiuc& pbcAiuc, const rvec& r1, const rvec& r2, rvec dr)
+{
+    dr[XX] = r1[XX] - r2[XX];
+    dr[YY] = r1[YY] - r2[YY];
+    dr[ZZ] = r1[ZZ] - r2[ZZ];
+
+    float shz = rintf(dr[ZZ] * pbcAiuc.invBoxDiagZ);
+    dr[XX] -= shz * pbcAiuc.boxZX;
+    dr[YY] -= shz * pbcAiuc.boxZY;
+    dr[ZZ] -= shz * pbcAiuc.boxZZ;
+
+    float shy = rintf(dr[YY] * pbcAiuc.invBoxDiagY);
+    dr[XX] -= shy * pbcAiuc.boxYX;
+    dr[YY] -= shy * pbcAiuc.boxYY;
+
+    float shx = rintf(dr[XX] * pbcAiuc.invBoxDiagX);
+    dr[XX] -= shx * pbcAiuc.boxXX;
+}
+
+
+#endif // GMX_PBCUTIL_PBC_AIUC_H
diff --git a/src/include/gromacs/pbcutil/pbc_aiuc_sycl.h b/src/include/gromacs/pbcutil/pbc_aiuc_sycl.h
new file mode 100644 (file)
index 0000000..d41029b
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief Basic routines to handle periodic boundary conditions with SYCL.
+ *
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_pbcutil
+ */
+#ifndef GMX_PBCUTIL_PBC_AIUC_SYCL_H
+#define GMX_PBCUTIL_PBC_AIUC_SYCL_H
+
+#include "gromacs/pbcutil/pbc_aiuc.h"
+
+/*! \brief Computes the vector between two points taking PBC into account.
+ *
+ * Computes the vector dr between points r2 and r1, taking into account the
+ * periodic boundary conditions, described in pbcAiuc object. Note that this
+ * routine always does the PBC arithmetic for all directions, multiplying the
+ * displacements by zeroes if the corresponding direction is not periodic.
+ * For triclinic boxes only distances up to half the smallest box diagonal
+ * element are guaranteed to be the shortest. This means that distances from
+ * 0.5/sqrt(2) times a box vector length (e.g. for a rhombic dodecahedron)
+ * can use a more distant periodic image.
+ *
+ * \todo This routine operates on rvec types and uses PbcAiuc to define
+ *       periodic box, but essentially does the same thing as SIMD and GPU
+ *       version. These will have to be unified in future to avoid code
+ *       duplication. See Issue #2863:
+ *       https://gitlab.com/gromacs/gromacs/-/issues/2863
+ *
+ * \param[in]  pbcAiuc  PBC object.
+ * \param[in]  r1       Coordinates of the first point.
+ * \param[in]  r2       Coordinates of the second point.
+ * \param[out]    dr       Resulting distance.
+ */
+static inline void pbcDxAiucSycl(const PbcAiuc& pbcAiuc, const rvec& r1, const rvec& r2, rvec dr)
+{
+    dr[XX] = r1[XX] - r2[XX];
+    dr[YY] = r1[YY] - r2[YY];
+    dr[ZZ] = r1[ZZ] - r2[ZZ];
+
+    float shz = cl::sycl::rint(dr[ZZ] * pbcAiuc.invBoxDiagZ);
+    dr[XX] -= shz * pbcAiuc.boxZX;
+    dr[YY] -= shz * pbcAiuc.boxZY;
+    dr[ZZ] -= shz * pbcAiuc.boxZZ;
+
+    float shy = cl::sycl::rint(dr[YY] * pbcAiuc.invBoxDiagY);
+    dr[XX] -= shy * pbcAiuc.boxYX;
+    dr[YY] -= shy * pbcAiuc.boxYY;
+
+    float shx = cl::sycl::rint(dr[XX] * pbcAiuc.invBoxDiagX);
+    dr[XX] -= shx * pbcAiuc.boxXX;
+}
+
+#endif // GMX_PBCUTIL_PBC_AIUC_SYCL_H
diff --git a/src/include/gromacs/pbcutil/pbc_simd.h b/src/include/gromacs/pbcutil/pbc_simd.h
new file mode 100644 (file)
index 0000000..09ba1e7
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file contains a definition, declaration and inline function
+ * for SIMD accelerated PBC calculations.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_pbcutil
+ */
+
+#ifndef GMX_PBCUTIL_PBC_SIMD_H
+#define GMX_PBCUTIL_PBC_SIMD_H
+
+#include "config.h"
+
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/simd/simd.h"
+
+struct gmx_domdec_t;
+
+/*! \brief Set the SIMD PBC data from a normal t_pbc struct.
+ *
+ * \param pbc        Type of periodic boundary,
+ *                   NULL can be passed for then no PBC will be used.
+ * \param pbc_simd   Pointer to aligned memory with (DIM + DIM*(DIM+1)/2)
+ *                   GMX_SIMD_REAL_WIDTH reals describing the box vectors
+ *                   unrolled by GMX_SIMD_REAL_WIDTH.
+ *                   These are sorted in a slightly non-standard
+ *                   order so that we always issue the memory loads in order
+ *                   (to improve prefetching) in pbc_correct_dx_simd().
+ *                   The order is inv_bzz, bzx, bzy, bzz, inv_byy, byx, byy,
+ *                   inv_bxx, and bxx.
+ */
+void set_pbc_simd(const t_pbc* pbc, real* pbc_simd);
+
+#if GMX_SIMD_HAVE_REAL
+
+/*! \brief Correct SIMD distance vector *dx,*dy,*dz for PBC using SIMD.
+ *
+ * For rectangular boxes all returned distance vectors are the shortest.
+ * For triclinic boxes only distances up to half the smallest box diagonal
+ * element are guaranteed to be the shortest. This means that distances from
+ * 0.5/sqrt(2) times a box vector length (e.g. for a rhombic dodecahedron)
+ * can use a more distant periodic image.
+ * Note that this routine always does PBC arithmetic, even for dimensions
+ * without PBC. But on modern processors the overhead of this, often called,
+ * routine should be low. On e.g. Intel Haswell/Broadwell it takes 8 cycles.
+ */
+static inline void gmx_simdcall pbc_correct_dx_simd(gmx::SimdReal* dx,
+                                                    gmx::SimdReal* dy,
+                                                    gmx::SimdReal* dz,
+                                                    const real*    pbc_simd)
+{
+    using namespace gmx;
+    SimdReal shz, shy, shx;
+
+    shz = round(*dz * load<SimdReal>(pbc_simd + 0 * GMX_SIMD_REAL_WIDTH)); // load inv_bzz
+    *dx = *dx - shz * load<SimdReal>(pbc_simd + 1 * GMX_SIMD_REAL_WIDTH);  // load bzx
+    *dy = *dy - shz * load<SimdReal>(pbc_simd + 2 * GMX_SIMD_REAL_WIDTH);  // load bzy
+    *dz = *dz - shz * load<SimdReal>(pbc_simd + 3 * GMX_SIMD_REAL_WIDTH);  // load bzz
+
+    shy = round(*dy * load<SimdReal>(pbc_simd + 4 * GMX_SIMD_REAL_WIDTH)); // load inv_byy
+    *dx = *dx - shy * load<SimdReal>(pbc_simd + 5 * GMX_SIMD_REAL_WIDTH);  // load byx
+    *dy = *dy - shy * load<SimdReal>(pbc_simd + 6 * GMX_SIMD_REAL_WIDTH);  // load byy
+
+    shx = round(*dx * load<SimdReal>(pbc_simd + 7 * GMX_SIMD_REAL_WIDTH)); // load inv_bxx
+    *dx = *dx - shx * load<SimdReal>(pbc_simd + 8 * GMX_SIMD_REAL_WIDTH);  // load bxx
+}
+
+/*! \brief Calculates the PBC corrected distance between SIMD coordinates.
+ *
+ * \param pbc_simd  SIMD formatted PBC information
+ * \param x1        Packed coordinates of atom1, size 3*GMX_SIMD_REAL_WIDTH
+ * \param x2        Packed coordinates of atom2, size 3*GMX_SIMD_REAL_WIDTH
+ * \param dx        The PBC corrected distance x1 - x2
+ *
+ * This routine only returns the shortest distance correctd for PBC
+ * when all atoms are in the unit-cell (aiuc).
+ * This is the SIMD equivalent of the scalar version declared in pbc.h.
+ */
+static inline void gmx_simdcall pbc_dx_aiuc(const real*          pbc_simd,
+                                            const gmx::SimdReal* x1,
+                                            const gmx::SimdReal* x2,
+                                            gmx::SimdReal*       dx)
+{
+    for (int d = 0; d < DIM; d++)
+    {
+        dx[d] = x1[d] - x2[d];
+    }
+    pbc_correct_dx_simd(&dx[XX], &dx[YY], &dx[ZZ], pbc_simd);
+}
+
+#endif /* GMX_SIMD_HAVE_REAL */
+
+#endif
diff --git a/src/include/gromacs/pbcutil/pbcenums.h b/src/include/gromacs/pbcutil/pbcenums.h
new file mode 100644 (file)
index 0000000..c12f03d
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Defines enum classes for centering and unit cell types.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \libinternal
+ * \ingroup module_pbcutil
+ */
+#ifndef GMX_PBCUTIL_PBCENUMS_H
+#define GMX_PBCUTIL_PBCENUMS_H
+
+namespace gmx
+{
+
+/*! \brief
+ * Helper enum class to define centering types.
+ */
+enum class CenteringType : int
+{
+    Triclinic,
+    Rectangular,
+    Zero,
+    Count
+};
+
+/*! \brief
+ * Helper enum class to define Unit cell representation types.
+ */
+enum class UnitCellType : int
+{
+    Triclinic,
+    Rectangular,
+    Compact,
+    Count
+};
+
+/*! \brief
+ * Get names for the different centering types.
+ *
+ * \param[in] type What name needs to be provided.
+ */
+const char* centerTypeNames(CenteringType type);
+
+/*! \brief
+ * Get names for the different unit cell representation types.
+ *
+ * \param[in] type What name needs to be provided.
+ */
+const char* unitCellTypeNames(UnitCellType type);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/pbcutil/pbcmethods.h b/src/include/gromacs/pbcutil/pbcmethods.h
new file mode 100644 (file)
index 0000000..262d32a
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_PBCUTIL_PBCMETHODS_H
+#define GMX_PBCUTIL_PBCMETHODS_H
+
+struct t_topology;
+struct t_block;
+struct t_atom;
+enum class PbcType : int;
+
+#include "gmxpre.h"
+
+#include "gromacs/math/vec.h"
+
+enum
+{
+    euSel,
+    euRect,
+    euTric,
+    euCompact,
+    euNR
+};
+
+void calc_pbc_cluster(int ecenter, int nrefat, t_topology* top, PbcType pbcType, rvec x[], const int index[], matrix box);
+
+
+void put_molecule_com_in_box(int      unitcell_enum,
+                             int      ecenter,
+                             t_block* mols,
+                             int      natoms,
+                             t_atom   atom[],
+                             PbcType  pbcType,
+                             matrix   box,
+                             rvec     x[]);
+
+void put_residue_com_in_box(int     unitcell_enum,
+                            int     ecenter,
+                            int     natoms,
+                            t_atom  atom[],
+                            PbcType pbcType,
+                            matrix  box,
+                            rvec    x[]);
+
+void center_x(int ecenter, rvec x[], matrix box, int n, int nc, const int ci[]);
+
+#endif
diff --git a/src/include/gromacs/pbcutil/rmpbc.h b/src/include/gromacs/pbcutil/rmpbc.h
new file mode 100644 (file)
index 0000000..ac7411e
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2016,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_PBCUTIL_RMPBC_H
+#define GMX_PBCUTIL_RMPBC_H
+
+#include "gromacs/math/vectypes.h"
+
+class InteractionDefinitions;
+struct t_atoms;
+struct t_idef;
+struct t_trxframe;
+enum class PbcType : int;
+
+typedef struct gmx_rmpbc* gmx_rmpbc_t;
+
+gmx_rmpbc_t gmx_rmpbc_init(const InteractionDefinitions& idef, PbcType pbcType, int natoms);
+
+gmx_rmpbc_t gmx_rmpbc_init(const t_idef* idef, PbcType pbcType, int natoms);
+
+void gmx_rmpbc_done(gmx_rmpbc_t gpbc);
+
+void gmx_rmpbc(gmx_rmpbc_t gpbc, int natoms, const matrix box, rvec x[]);
+/* Correct coordinates x for atoms within every molecule for the periodic
+ * boundary conditions such that every molecule is whole.
+ * natoms is the size x and can be smaller than the number
+ * of atoms in idef, but should only contain complete molecules.
+ * When pbcType=PbcType::Unset, the type of pbc is guessed from the box matrix.
+ */
+
+void gmx_rmpbc_copy(gmx_rmpbc_t gpbc, int natoms, const matrix box, rvec x[], rvec x_s[]);
+/* As gmx_rmpbc, but outputs in x_s and does not modify x. */
+
+void gmx_rmpbc_trxfr(gmx_rmpbc_t gpbc, struct t_trxframe* fr);
+/* As gmx_rmpbc but operates on a t_trxframe data structure. */
+
+void rm_gropbc(const t_atoms* atoms, rvec x[], const matrix box);
+/* Simple routine for use in analysis tools that just have a pdb or
+ * similar file.
+ */
+
+#endif
diff --git a/src/include/gromacs/pulling/output.h b/src/include/gromacs/pulling/output.h
new file mode 100644 (file)
index 0000000..d68f6ad
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * This file declares functions for pull output writing.
+ *
+ * \author Berk Hess
+ *
+ * \inlibraryapi
+ */
+
+#ifndef GMX_PULLING_OUTPUT_H
+#define GMX_PULLING_OUTPUT_H
+
+#include "gromacs/utility/basedefinitions.h"
+
+struct pull_t;
+struct gmx_output_env_t;
+struct ObservablesHistory;
+struct t_filenm;
+
+namespace gmx
+{
+enum class StartingBehavior;
+}
+
+/*! \brief Set up and open the pull output files, when requested.
+ *
+ * NOTE: This should only be called on the master rank and only when
+ *       doing dynamics (e.g. not with energy minimization).
+ *
+ * \param pull        The pull work data struct
+ * \param nfile       Number of files.
+ * \param fnm         Standard filename struct.
+ * \param oenv        Output options.
+ * \param startingBehavior  Describes whether this is a restart appending to output files
+ */
+void init_pull_output_files(pull_t*                 pull,
+                            int                     nfile,
+                            const t_filenm          fnm[],
+                            const gmx_output_env_t* oenv,
+                            gmx::StartingBehavior   startingBehavior);
+
+/*! \brief Print the pull output (x and/or f)
+ *
+ * \param pull     The pull data structure.
+ * \param step     Time step number.
+ * \param time     Time.
+ */
+void pull_print_output(pull_t* pull, int64_t step, double time);
+
+/*! \brief Allocate and initialize pull work history (for average pull output) and set it in a pull work struct
+ *
+ * \param pull                The pull work struct
+ * \param observablesHistory  Container of history data, e.g., pull history.
+ */
+void initPullHistory(pull_t* pull, ObservablesHistory* observablesHistory);
+
+#endif
diff --git a/src/include/gromacs/pulling/pull.h b/src/include/gromacs/pulling/pull.h
new file mode 100644 (file)
index 0000000..90a1296
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ *
+ * \brief
+ * This file contains datatypes and function declarations necessary
+   for mdrun to interface with the pull code.
+ *
+ * \author Berk Hess
+ *
+ * \inlibraryapi
+ */
+
+#ifndef GMX_PULLING_PULL_H
+#define GMX_PULLING_PULL_H
+
+#include <cstdio>
+#include <optional>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/pull_params.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_mtop_t;
+struct gmx_output_env_t;
+struct pull_coord_work_t;
+struct pull_params_t;
+struct pull_t;
+struct t_commrec;
+struct t_filenm;
+struct t_inputrec;
+struct t_pbc;
+class t_state;
+enum class PbcType;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+class ForceWithVirial;
+class LocalAtomSetManager;
+} // namespace gmx
+
+/*! \brief Returns the units of the pull coordinate.
+ *
+ * \param[in] pcrd The pull coordinate to query the units for.
+ * \returns a string with the units of the coordinate.
+ */
+const char* pull_coordinate_units(const t_pull_coord& pcrd);
+
+/*! \brief Returns the conversion factor from the pull coord init/rate unit to internal value unit.
+ *
+ * \param[in] pcrd The pull coordinate to get the conversion factor for.
+ * \returns the conversion factor.
+ */
+double pull_conversion_factor_userinput2internal(const t_pull_coord& pcrd);
+
+/*! \brief Returns the conversion factor from the pull coord internal value unit to the init/rate unit.
+ *
+ * \param[in] pcrd The pull coordinate to get the conversion factor for.
+ * \returns the conversion factor.
+ */
+double pull_conversion_factor_internal2userinput(const t_pull_coord& pcrd);
+
+/*! \brief Get the value for pull coord coord_ind.
+ *
+ * \param[in,out] pull        The pull struct.
+ * \param[in]     coordIndex  Index of the pull coordinate in the list of coordinates
+ * \param[in]     pbc         Information structure about periodicity.
+ * \returns the value of the pull coordinate.
+ */
+double get_pull_coord_value(pull_t* pull, int coordIndex, const t_pbc& pbc);
+
+/*! \brief Registers the provider of an external potential for a coordinate.
+ *
+ * This function is only used for checking the consistency of the pull setup.
+ * For each pull coordinate of type external-potential, selected by the user
+ * in the mdp file, there has to be a module that provides this potential.
+ * The module registers itself as the provider by calling this function.
+ * The passed \p provider string has to match the string that the user
+ * passed with the potential-provider pull coordinate mdp option.
+ * This function should be called after init_pull has been called and before
+ * pull_potential is called for the first time.
+ * This function does many consistency checks and when it returns and the
+ * first call to do_potential passes, the pull setup is guaranteed to be
+ * correct (unless the module doesn't call apply_external_pull_coord_force
+ * every step or calls it with incorrect forces). This registering function
+ * will exit with a (release) assertion failure when used incorrely or
+ * with a fatal error when the user (mdp) input in inconsistent.
+ *
+ * Thread-safe for simultaneous registration from multiple threads.
+ *
+ * \param[in,out] pull         The pull struct.
+ * \param[in]     coord_index  The pull coordinate index to register the external potential for.
+ * \param[in]     provider     Provider string, should match the potential-provider pull coordinate mdp option.
+ */
+void register_external_pull_potential(struct pull_t* pull, int coord_index, const char* provider);
+
+
+/*! \brief Apply forces of an external potential to a pull coordinate.
+ *
+ * This function applies the external scalar force \p coord_force to
+ * the pull coordinate, distributing it over the atoms in the groups
+ * involved in the pull coordinate. The corresponding potential energy
+ * value should be added to the pull or the module's potential energy term
+ * separately by the module itself.
+ * This function should be called after pull_potential has been called and,
+ * obviously, before the coordinates are updated uses the forces.
+ *
+ * \param[in,out] pull             The pull struct.
+ * \param[in]     coord_index      The pull coordinate index to set the force for.
+ * \param[in]     coord_force      The scalar force for the pull coordinate.
+ * \param[in]     masses           Atoms masses.
+ * \param[in,out] forceWithVirial  Force and virial buffers.
+ */
+void apply_external_pull_coord_force(struct pull_t*            pull,
+                                     int                       coord_index,
+                                     double                    coord_force,
+                                     gmx::ArrayRef<const real> masses,
+                                     gmx::ForceWithVirial*     forceWithVirial);
+
+/*! \brief Set the all the pull forces to zero.
+ *
+ * \param pull              The pull group.
+ */
+void clear_pull_forces(pull_t* pull);
+
+
+/*! \brief Determine the COM pull forces and add them to f, return the potential
+ *
+ * \param[in,out] pull   The pull struct.
+ * \param[in]     masses Atoms masses.
+ * \param[in]     pbc    Information struct about periodicity.
+ * \param[in]     cr     Struct for communication info.
+ * \param[in]     t      Time.
+ * \param[in]     lambda The value of lambda in FEP calculations.
+ * \param[in]     x      Positions.
+ * \param[in,out] force  Forces and virial.
+ * \param[out] dvdlambda Pull contribution to dV/d(lambda).
+ *
+ * \returns The pull potential energy.
+ */
+real pull_potential(pull_t*                        pull,
+                    gmx::ArrayRef<const real>      masses,
+                    const t_pbc&                   pbc,
+                    const t_commrec*               cr,
+                    double                         t,
+                    real                           lambda,
+                    gmx::ArrayRef<const gmx::RVec> x,
+                    gmx::ForceWithVirial*          force,
+                    real*                          dvdlambda);
+
+
+/*! \brief Constrain the coordinates xp in the directions in x
+ * and also constrain v when v != NULL.
+ *
+ * \param[in,out] pull   The pull data.
+ * \param[in]     masses Atoms masses.
+ * \param[in]     pbc    Information struct about periodicity.
+ * \param[in]     cr     Struct for communication info.
+ * \param[in]     dt     The time step length.
+ * \param[in]     t      The time.
+ * \param[in]     x      Positions.
+ * \param[in,out] xp     Updated x, can be NULL.
+ * \param[in,out] v      Velocities, which may get a pull correction.
+ * \param[in,out] vir    The virial, which, if != NULL, gets a pull correction.
+ */
+void pull_constraint(struct pull_t*            pull,
+                     gmx::ArrayRef<const real> masses,
+                     const t_pbc&              pbc,
+                     const t_commrec*          cr,
+                     double                    dt,
+                     double                    t,
+                     gmx::ArrayRef<gmx::RVec>  x,
+                     gmx::ArrayRef<gmx::RVec>  xp,
+                     gmx::ArrayRef<gmx::RVec>  v,
+                     tensor                    vir);
+
+
+/*! \brief Make a selection of the home atoms for all pull groups.
+ * Should be called at every domain decomposition.
+ *
+ * \param cr             Structure for communication info.
+ * \param pull           The pull group.
+ */
+void dd_make_local_pull_groups(const t_commrec* cr, pull_t* pull);
+
+
+/*! \brief Allocate, initialize and return a pull work struct.
+ *
+ * \param fplog       General output file, normally md.log.
+ * \param pull_params The pull input parameters containing all pull settings.
+ * \param ir          The inputrec.
+ * \param mtop        The topology of the whole system.
+ * \param cr          Struct for communication info.
+ * \param atomSets    The manager that handles the pull atom sets
+ * \param lambda      FEP lambda.
+ */
+struct pull_t* init_pull(FILE*                     fplog,
+                         const pull_params_t*      pull_params,
+                         const t_inputrec*         ir,
+                         const gmx_mtop_t&         mtop,
+                         const t_commrec*          cr,
+                         gmx::LocalAtomSetManager* atomSets,
+                         real                      lambda);
+
+
+/*! \brief Close the pull output files and delete pull.
+ *
+ * \param pull       The pull data structure.
+ */
+void finish_pull(struct pull_t* pull);
+
+
+/*! \brief Calculates centers of mass all pull groups.
+ *
+ * \param[in] cr       Struct for communication info.
+ * \param[in] pull     The pull data structure.
+ * \param[in] masses   Atoms masses.
+ * \param[in] pbc      Information struct about periodicity.
+ * \param[in] t        Time, only used for cylinder ref.
+ * \param[in] x        The local positions.
+ * \param[in,out] xp   Updated x, can be NULL.
+ *
+ */
+void pull_calc_coms(const t_commrec*               cr,
+                    pull_t*                        pull,
+                    gmx::ArrayRef<const real>      masses,
+                    const t_pbc&                   pbc,
+                    double                         t,
+                    gmx::ArrayRef<const gmx::RVec> x,
+                    gmx::ArrayRef<gmx::RVec>       xp);
+
+/*! \brief Margin for checking pull group PBC distances compared to half the box size */
+static constexpr real c_pullGroupPbcMargin = 0.9;
+/*! \brief Threshold (as a factor of half the box size) for accepting pull groups without explicitly set refatom */
+static constexpr real c_pullGroupSmallGroupThreshold = 0.5;
+
+/*! \brief Checks whether all groups that use a reference atom are within PBC restrictions
+ *
+ * Groups that use a reference atom for determining PBC should have all their
+ * atoms within half the box size from the PBC atom. The box size is used
+ * per dimension for rectangular boxes, but can be a combination of
+ * dimensions for triclinic boxes, depending on which dimensions are
+ * involved in the pull coordinates a group is involved in. A margin is specified
+ * to ensure that atoms are not too close to the maximum distance.
+ *
+ * Should be called without MPI parallelization and after pull_calc_coms()
+ * has been called at least once.
+ *
+ * \param[in] pull       The pull data structure
+ * \param[in] x          The coordinates
+ * \param[in] pbc        Information struct about periodicity
+ * \param[in] pbcMargin  The minimum margin (as a fraction) to half the box size
+ * \returns -1 when all groups obey PBC or the first group index that fails PBC
+ */
+int pullCheckPbcWithinGroups(const pull_t& pull, gmx::ArrayRef<const gmx::RVec> x, const t_pbc& pbc, real pbcMargin);
+
+/*! \brief Checks whether a specific group that uses a reference atom is within PBC restrictions
+ *
+ * Groups that use a reference atom for determining PBC should have all their
+ * atoms within half the box size from the PBC atom. The box size is used
+ * per dimension for rectangular boxes, but can be a combination of
+ * dimensions for triclinic boxes, depending on which dimensions are
+ * involved in the pull coordinates a group is involved in. A margin is specified
+ * to ensure that atoms are not too close to the maximum distance. Only one group is
+ * checked.
+ *
+ * Should be called without MPI parallelization and after pull_calc_coms()
+ * has been called at least once.
+ *
+ * \param[in] pull       The pull data structure
+ * \param[in] x          The coordinates
+ * \param[in] pbc        Information struct about periodicity
+ * \param[in] groupNr    The index of the group (in pull.group[]) to check
+ * \param[in] pbcMargin  The minimum margin (as a fraction) to half the box size
+ * \returns true if the group obeys PBC otherwise false
+ */
+bool pullCheckPbcWithinGroup(const pull_t&                  pull,
+                             gmx::ArrayRef<const gmx::RVec> x,
+                             const t_pbc&                   pbc,
+                             int                            groupNr,
+                             real                           pbcMargin);
+
+/*! \brief Returns if we have pull coordinates with potential pulling.
+ *
+ * \param[in] pull     The pull data structure.
+ */
+bool pull_have_potential(const pull_t& pull);
+
+
+/*! \brief Returns if we have pull coordinates with constraint pulling.
+ *
+ * \param[in] pull     The pull data structure.
+ */
+bool pull_have_constraint(const pull_t& pull);
+
+/*! \brief Returns if inputrec has pull coordinates with constraint pulling.
+ *
+ * \param[in] pullParameters  Pulling input parameters from input record.
+ */
+bool pull_have_constraint(const pull_params_t& pullParameters);
+
+/*! \brief Returns the maxing distance for pulling
+ *
+ * For distance geometries, only dimensions with pcrd->params[dim]=1
+ * are included in the distance calculation.
+ * For directional geometries, only dimensions with pcrd->vec[dim]!=0
+ * are included in the distance calculation.
+ *
+ * \param[in] pcrd Pulling data structure
+ * \param[in] pbc  Information on periodic boundary conditions
+ * \returns The maximume distance
+ */
+real max_pull_distance2(const pull_coord_work_t& pcrd, const t_pbc& pbc);
+
+/*! \brief Sets the previous step COM in pull to the current COM, and optionally
+ *         updates it in the provided ArrayRef
+ *
+ * \param[in] pull  The COM pull force calculation data structure
+ * \param[in] comPreviousStep  The COM of the previous step of each pull group
+ */
+void updatePrevStepPullCom(pull_t* pull, std::optional<gmx::ArrayRef<double>> comPreviousStep);
+
+/*! \brief Returns a copy of the previous step pull COM as flat vector
+ *
+ * Used for modular simulator checkpointing. Allows to keep the
+ * implementation details of pull_t hidden from its users.
+ *
+ * \param[in] pull  The COM pull force calculation data structure
+ * \return A copy of the previous step COM
+ */
+std::vector<double> prevStepPullCom(const pull_t* pull);
+
+/*! \brief Set the previous step pull COM from a flat vector
+ *
+ * Used to restore modular simulator checkpoints. Allows to keep the
+ * implementation details of pull_t hidden from its users.
+ *
+ * \param[in] pull  The COM pull force calculation data structure
+ * \param[in] prevStepPullCom  The previous step COM to set
+ */
+void setPrevStepPullCom(pull_t* pull, gmx::ArrayRef<const double> prevStepPullCom);
+
+/*! \brief Allocates, initializes and communicates the previous step pull COM (if that option is set to true).
+ *
+ * If ir->pull->bSetPbcRefToPrevStepCOM is not true nothing is done.
+ *
+ * \param[in] ir                     The input options/settings of the simulation.
+ * \param[in] pull_work              The COM pull force calculation data structure
+ * \param[in] masses                 Atoms masses.
+ * \param[in] state                  The local (to this rank) state.
+ * \param[in] state_global           The global state.
+ * \param[in] cr                     Struct for communication info.
+ * \param[in] startingFromCheckpoint Is the simulation starting from a checkpoint?
+ */
+void preparePrevStepPullCom(const t_inputrec*         ir,
+                            pull_t*                   pull_work,
+                            gmx::ArrayRef<const real> masses,
+                            t_state*                  state,
+                            const t_state*            state_global,
+                            const t_commrec*          cr,
+                            bool                      startingFromCheckpoint);
+
+/*! \brief Initializes the COM of the previous step (set to initial COM)
+ *
+ * \param[in] cr       Struct for communication info.
+ * \param[in] pull     The pull data structure.
+ * \param[in] masses   Atoms masses.
+ * \param[in] pbc      Information struct about periodicity.
+ * \param[in] x        The local positions.
+ */
+void initPullComFromPrevStep(const t_commrec*               cr,
+                             pull_t*                        pull,
+                             gmx::ArrayRef<const real>      masses,
+                             const t_pbc&                   pbc,
+                             gmx::ArrayRef<const gmx::RVec> x);
+
+/*! \brief Initializes the previous step pull COM for new simulations (no reading from checkpoint).
+ *
+ * \param[in] cr               Struct for communication info.
+ * \param[in] pull_work        The COM pull force calculation data structure.
+ * \param[in] masses           Atoms masses.
+ * \param[in] x                The local positions.
+ * \param[in] box              The current box matrix.
+ * \param[in] pbcType          The type of periodic boundary conditions.
+ * \param[in] comPreviousStep  The COM of the previous step of each pull group.
+ */
+void preparePrevStepPullComNewSimulation(const t_commrec*                       cr,
+                                         pull_t*                                pull_work,
+                                         gmx::ArrayRef<const real>              masses,
+                                         gmx::ArrayRef<const gmx::RVec>         x,
+                                         const matrix                           box,
+                                         PbcType                                pbcType,
+                                         std::optional<gmx::ArrayRef<double>>&& comPreviousStep);
+
+#endif
diff --git a/src/include/gromacs/pulling/pull_internal.h b/src/include/gromacs/pulling/pull_internal.h
new file mode 100644 (file)
index 0000000..3d095e3
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ *
+ *
+ * \brief
+ * This file contains datatypes and function declarations for internal
+   use in the pull code.
+ *
+ * \author Berk Hess
+ */
+
+#ifndef GMX_PULLING_PULL_INTERNAL_H
+#define GMX_PULLING_PULL_INTERNAL_H
+
+#include "config.h"
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/domdec/localatomset.h"
+#include "gromacs/mdtypes/pull_params.h"
+#include "gromacs/utility/gmxmpi.h"
+
+#include "pullcoordexpressionparser.h"
+
+/*! \brief Determines up to what local atom count a pull group gets processed single-threaded.
+ *
+ * We set this limit to 1 with debug to catch bugs.
+ * On Haswell with GCC 5 the cross-over point is around 400 atoms,
+ * independent of thread count and hyper-threading.
+ */
+#ifdef NDEBUG
+static const int c_pullMaxNumLocalAtomsSingleThreaded = 100;
+#else
+static const int c_pullMaxNumLocalAtomsSingleThreaded = 1;
+#endif
+
+class PullHistory;
+enum class PbcType : int;
+
+class t_state;
+
+enum
+{
+    epgrppbcNONE,
+    epgrppbcREFAT,
+    epgrppbcCOS,
+    epgrppbcPREVSTEPCOM
+};
+
+/*! \internal
+ * \brief Pull group data used during pulling
+ */
+struct pull_group_work_t
+{
+    /*! \brief Constructor
+     *
+     * \param[in] params                  The group parameters set by the user
+     * \param[in] atomSet                 The global to local atom set manager
+     * \param[in] setPbcRefToPrevStepCOM Does this pull group use the COM from the previous step as reference position?
+     * \param[in] maxNumThreads           Use either this number of threads of 1 for operations on x and f
+     */
+    pull_group_work_t(const t_pull_group& params,
+                      gmx::LocalAtomSet   atomSet,
+                      bool                setPbcRefToPrevStepCOM,
+                      int                 maxNumThreads);
+
+    //! Returns the number of threads to use for local atom operations based on the local atom count
+    int numThreads() const
+    {
+        return atomSet.numAtomsLocal() <= c_pullMaxNumLocalAtomsSingleThreaded ? 1 : maxNumThreads;
+    }
+
+    /* Data only modified at initialization */
+    const t_pull_group params;   /**< The pull group parameters */
+    const int          epgrppbc; /**< The type of pbc for this pull group, see enum above */
+    const int maxNumThreads; /**< The maximum number of threads to use for operations on x and f */
+    bool      needToCalcCom; /**< Do we need to calculate the COM? (Not for group 0 or if only used as cylinder group) */
+    std::vector<real> globalWeights; /**< Weights per atom set by the user and/or mass/friction coefficients, if empty all weights are equal */
+
+    /* Data modified only at init or at domain decomposition */
+    gmx::LocalAtomSet                  atomSet;      /**< Global to local atom set mapper */
+    std::vector<real>                  localWeights; /**< Weights for the local atoms */
+    std::unique_ptr<gmx::LocalAtomSet> pbcAtomSet;   /**< Keeps index of the pbc reference atom.
+                                                          The stored LocalAtomSet consists of exactly   one atom when pbc reference atom is required.
+                                                          When no pbc refence atom is used, this   pointer   shall be null. */
+
+    /* Data, potentially, changed at every pull call */
+    real mwscale; /**< mass*weight scaling factor 1/sum w m */
+    real wscale;  /**< scaling factor for the weights: sum w m/sum w w m */
+    real invtm;   /**< inverse total mass of the group: 1/wscale sum w m */
+    std::vector<gmx::BasicVector<double>> mdw; /**< mass*gradient(weight) for atoms */
+    std::vector<double>                   dv;  /**< distance to the other group(s) along vec */
+    dvec                                  x;   /**< COM before update */
+    dvec                                  xp;  /**< COM after update before constraining */
+    dvec                                  x_prev_step; /**< center of mass of the previous step */
+};
+
+/* Struct describing the instantaneous spatial layout of a pull coordinate */
+struct PullCoordSpatialData
+{
+    dvec   dr01;       /* The direction vector of group 1 relative to group 0 */
+    dvec   dr23;       /* The direction vector of group 3 relative to group 2 */
+    dvec   dr45;       /* The direction vector of group 5 relative to group 4 */
+    dvec   vec;        /* The pull direction */
+    double vec_len;    /* Length of vec for direction-relative */
+    dvec   ffrad;      /* conversion factor from vec to radial force */
+    double cyl_dev;    /* The deviation from the reference position */
+    dvec   planevec_m; /* Normal of plane for groups 0, 1, 2, 3 for geometry dihedral */
+    dvec   planevec_n; /* Normal of plane for groups 2, 3, 4, 5 for geometry dihedral */
+
+    double value; /* The current value of the coordinate, units of nm or rad */
+};
+
+//! \brief Struct with parameters and force evaluation local data for a pull coordinate
+struct pull_coord_work_t
+{
+    //! Constructor
+    pull_coord_work_t(const t_pull_coord& params) :
+        params(params),
+        value_ref(0),
+        spatialData(),
+        scalarForce(0),
+        bExternalPotentialProviderHasBeenRegistered(false),
+        expressionParser(params.eGeom == PullGroupGeometry::Transformation ? params.expression : "",
+                         params.coordIndex),
+        transformationVariables(params.eGeom == PullGroupGeometry::Transformation ? params.coordIndex : 0)
+    {
+    }
+
+    //! Pull coordinate parameters
+    const t_pull_coord params;
+
+    //! Dynamic pull group 0 for this coordinate with dynamic weights, only present when needed */
+    std::unique_ptr<pull_group_work_t> dynamicGroup0;
+    //! The reference value, usually init+rate*t, units of nm or rad.
+    double value_ref;
+
+    //! Data defining the current geometry
+    PullCoordSpatialData spatialData;
+
+    //! Scalar force for this cooordinate
+    double scalarForce;
+
+    //! For external-potential coordinates only, for checking if a provider has been registered
+    bool bExternalPotentialProviderHasBeenRegistered;
+
+    //! The expression parser for a transformation coordinate
+    gmx::PullCoordExpressionParser expressionParser;
+    //! Variables from other pull coordinates for a transformation coordinate
+    std::vector<double> transformationVariables;
+};
+
+/* Struct for storing vectorial forces for a pull coordinate */
+struct PullCoordVectorForces
+{
+    dvec force01; /* Force due to the pulling/constraining for groups 0, 1 */
+    dvec force23; /* Force for groups 2 and 3 */
+    dvec force45; /* Force for groups 4 and 5 */
+};
+
+/* Struct for sums over (local) atoms in a pull group */
+struct ComSums
+{
+    /* For normal weighting */
+    double sum_wm;   /* Sum of weight*mass        */
+    double sum_wwm;  /* Sum of weight*weight*mass */
+    dvec   sum_wmx;  /* Sum of weight*mass*x      */
+    dvec   sum_wmxp; /* Sum of weight*mass*xp     */
+
+    /* For cosine weighting */
+    double sum_cm;  /* Sum of cos(x)*mass          */
+    double sum_sm;  /* Sum of sin(x)*mass          */
+    double sum_ccm; /* Sum of cos(x)*cos(x)*mass   */
+    double sum_csm; /* Sum of cos(x)*sin(x)*mass   */
+    double sum_ssm; /* Sum of sin(x)*sin(x)*mass   */
+    double sum_cmp; /* Sum of cos(xp)*sin(xp)*mass */
+    double sum_smp; /* Sum of sin(xp)*sin(xp)*mass */
+
+    /* Dummy data to ensure adjacent elements in an array are separated
+     * by a cache line size, max 128 bytes.
+     * TODO: Replace this by some automated mechanism.
+     */
+    int dummy[32];
+};
+
+/*! \brief The normal COM buffer needs 3 elements per group */
+static constexpr int c_comBufferStride = 3;
+
+/*! \brief The cylinder buffer needs 9 elements per group */
+static constexpr int c_cylinderBufferStride = 9;
+
+struct pull_comm_t
+{
+    gmx_bool bParticipateAll; /* Do all ranks always participate in pulling? */
+    gmx_bool bParticipate;    /* Does our rank participate in pulling? */
+#if GMX_MPI
+    MPI_Comm mpi_comm_com; /* Communicator for pulling */
+#endif
+    int nparticipate; /* The number of ranks participating */
+    bool isMasterRank; /* Tells whether our rank is the master rank and thus should add the pull virial */
+
+    int64_t setup_count; /* The number of decomposition calls */
+    int64_t must_count;  /* The last count our rank needed to be part */
+
+    /* Buffers for parallel reductions */
+    std::vector<gmx::RVec>                pbcAtomBuffer; /* COM calculation buffer */
+    std::vector<gmx::BasicVector<double>> comBuffer;     /* COM calculation buffer */
+    std::vector<double> cylinderBuffer; /* cylinder ref. groups calculation buffer */
+};
+
+// The COM pull force calculation data structure
+// TODO Convert this into a ForceProvider
+struct pull_t
+{
+    /* Global parameters */
+    pull_params_t params; /* The pull parameters, from inputrec */
+
+    gmx_bool bPotential;  /* Are there coordinates with potential? */
+    gmx_bool bConstraint; /* Are there constrained coordinates? */
+    gmx_bool bAngle;      /* Are there angle geometry coordinates? */
+
+    PbcType  pbcType;   /* the boundary conditions */
+    int      npbcdim;   /* do pbc in dims 0 <= dim < npbcdim */
+    gmx_bool bRefAt;    /* do we need reference atoms for a group COM ? */
+    int      cosdim;    /* dimension for cosine weighting, -1 if none */
+    gmx_bool bCylinder; /* Is group 0 a cylinder group? */
+
+    /* Parameters + dynamic data for groups */
+    std::vector<pull_group_work_t> group; /* The pull group param and work data */
+
+    /* Parameters + dynamic data for coordinates */
+    std::vector<pull_coord_work_t> coord; /* The pull group param and work data */
+
+    /* Global dynamic data */
+    gmx_bool bSetPBCatoms; /* Do we need to set x_pbc for the groups? */
+
+    std::vector<ComSums> comSums; /* Work array for summing for COM, 1 entry per thread */
+
+    pull_comm_t comm; /* Communication parameters, communicator and buffers */
+
+    FILE* out_x; /* Output file for pull data */
+    FILE* out_f; /* Output file for pull data */
+
+    bool bXOutAverage; /* Output average pull coordinates */
+    bool bFOutAverage; /* Output average pull forces */
+
+    PullHistory* coordForceHistory; /* Pull coordinate and force history */
+
+    /* The number of coordinates using an external potential */
+    int numCoordinatesWithExternalPotential;
+    /* Counter for checking external potential registration */
+    int numUnregisteredExternalPotentials;
+    /* */
+    int numExternalPotentialsStillToBeAppliedThisStep;
+};
+
+/*! \brief Copies the pull group COM of the previous step from the checkpoint state to the pull state
+ *
+ * \param[in]   pull  The COM pull force calculation data structure
+ * \param[in]   state The global state container
+ */
+void setPrevStepPullComFromState(struct pull_t* pull, const t_state* state);
+
+/*! \brief Resizes the vector, in the state container, containing the COMs from the previous step
+ *
+ * \param[in]   state The global state container
+ * \param[in]   pull  The COM pull force calculation data structure
+ */
+void allocStatePrevStepPullCom(t_state* state, const pull_t* pull);
+
+
+#endif
diff --git a/src/include/gromacs/pulling/pull_rotation.h b/src/include/gromacs/pulling/pull_rotation.h
new file mode 100644 (file)
index 0000000..d367317
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief
+ * Declares functions to enforce rotational motion upon a group of particles.
+ *
+ * \author Carsten Kutzner <ckutzne@gwdg.de>
+ *
+ * \inlibraryapi
+ */
+
+#ifndef GMX_PULLING_PULL_ROTATION_H
+#define GMX_PULLING_PULL_ROTATION_H
+
+#include <stdio.h>
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+
+struct gmx_domdec_t;
+struct gmx_enfrot;
+struct gmx_mtop_t;
+struct gmx_output_env_t;
+struct t_commrec;
+struct t_filenm;
+struct t_inputrec;
+struct t_rot;
+class t_state;
+
+namespace gmx
+{
+enum class StartingBehavior;
+class LocalAtomSetManager;
+struct MdrunOptions;
+template<typename>
+class ArrayRef;
+
+class EnforcedRotation
+{
+public:
+    EnforcedRotation();
+    ~EnforcedRotation();
+
+    /*! \brief Getter for working data
+     *
+     * This is needed while the module is still under
+     * construction. */
+    gmx_enfrot* getLegacyEnfrot();
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+/*! \brief Initializes the enforced rotation groups.
+ *
+ * This routine does the memory allocation for various helper arrays, opens
+ * the output files etc.
+ *
+ * \param fplog    General output file, normally md.log.
+ * \param ir       Struct containing MD input parameters, among those
+ *                 also the enforced rotation parameters.
+ * \param nfile    Number of entries in the fnm structure.
+ * \param fnm      The filenames struct containing also the names
+ *                 of the rotation output files.
+ * \param atomSets Tracks indices of atoms subject to enforced rotation for each DD rank.
+ * \param cr       Pointer to MPI communication data.
+ * \param globalState  The global state, only used on the master rank.
+ * \param mtop     Molecular topology.
+ * \param oenv     Needed to open the rotation output xvgr file.
+ * \param mdrunOptions  Options for mdrun.
+ * \param startingBehavior  Describes whether this is a restart appending to output files
+ * \return         An enforced rotation module.
+ */
+std::unique_ptr<gmx::EnforcedRotation> init_rot(FILE*                     fplog,
+                                                t_inputrec*               ir,
+                                                int                       nfile,
+                                                const t_filenm            fnm[],
+                                                const t_commrec*          cr,
+                                                gmx::LocalAtomSetManager* atomSets,
+                                                const t_state*            globalState,
+                                                const gmx_mtop_t&         mtop,
+                                                const gmx_output_env_t*   oenv,
+                                                const gmx::MdrunOptions&  mdrunOptions,
+                                                gmx::StartingBehavior     startingBehavior);
+
+/*! \brief Calculates the enforced rotation potential(s).
+ *
+ * This is the main enforced rotation module which is called during every time
+ * step. Here the rotation potential as well as the resulting forces are
+ * calculated.
+ *
+ * \param cr      Pointer to MPI communication data.
+ * \param er      Pointer to the enforced rotation working data.
+ * \param box     Simulation box, needed to make group whole.
+ * \param coords  The positions of all the local particles.
+ * \param t       Time.
+ * \param step    The time step.
+ * \param bNS     After domain decomposition / neighbor searching several
+ *                local arrays have to be updated (masses, shifts)
+ */
+void do_rotation(const t_commrec*               cr,
+                 gmx_enfrot*                    er,
+                 const matrix                   box,
+                 gmx::ArrayRef<const gmx::RVec> coords,
+                 real                           t,
+                 int64_t                        step,
+                 bool                           bNS);
+
+
+/*! \brief Add the enforced rotation forces to the official force array.
+ *
+ * Adds the forces from enforced rotation potential to the local forces and
+ * sums up the contributions to the rotation potential from all the nodes. Since
+ * this needs communication, this routine should be called after the short range
+ * forces have been evaluated (in order not to spoil cycle counts).
+ * This routine also outputs data to the rotation output files (e.g.
+ * the potential, the angle of the group(s), and torques).
+ *
+ * \param er      Pointer to the enforced rotation working data.
+ * \param force   The local forces to which the rotational forces have
+ *                to be added.
+ * \param cr      Pointer to MPI communication data.
+ * \param step    The time step, used for output.
+ * \param t       Time, used for output.
+ * \returns       The potential energy of the rotation potentials.
+ */
+real add_rot_forces(gmx_enfrot* er, gmx::ArrayRef<gmx::RVec> force, const t_commrec* cr, int64_t step, real t);
+
+
+#endif
diff --git a/src/include/gromacs/pulling/pullcoordexpressionparser.h b/src/include/gromacs/pulling/pullcoordexpressionparser.h
new file mode 100644 (file)
index 0000000..053b384
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief
+ * Contains classes and methods related to use of MuParser in pulling
+ *
+ * \author Oliver Fleetwood <oliver.fleetwood@gmail.com>
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Berk Hess <hess@kth.se>
+ *
+ */
+#ifndef GMX_PULL_PULLCOORDEXPRESSIONPARSER_H
+#define GMX_PULL_PULLCOORDEXPRESSIONPARSER_H
+
+#include "config.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#if HAVE_MUPARSER
+#    include <muParser.h>
+#else
+namespace mu
+{
+//! Defines a dummy Parser type to reduce use of the preprocessor.
+using Parser = std::false_type;
+} // namespace mu
+#endif
+
+struct pull_coord_work_t;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+
+/*! \brief Class with a mathematical expression and parser.
+ * \internal
+ *
+ * The class handles parser instantiation from an mathematical expression, e.g. 'x1*x2',
+ * and evaluates the expression given the variables' numerical values.
+ *
+ * Note that for performance reasons you should not create a new PullCoordExpressionParser
+ * for every evaluation.
+ * Instead, instantiate one PullCoordExpressionParser per expression,
+ * then update the variables before the next evaluation.
+ *
+ */
+class PullCoordExpressionParser
+{
+public:
+    //! Constructor which takes a mathematical expression and the number of variables as arguments
+    PullCoordExpressionParser(const std::string& expression, int numVariables);
+
+    //! Evaluates the expression with the numerical values passed in \p variables.
+    double evaluate(ArrayRef<const double> variables);
+
+private:
+    /*! \brief The mathematical expression, e.g. 'x1*x2' */
+    std::string expression_;
+
+    /*! \brief A vector containing the numerical values of the variables before parser evaluation.
+     *
+     * muParser compiles the expression to bytecode, then binds to the memory address
+     * of these vector elements, making the evaluations fast and memory efficient.
+     * */
+    std::vector<double> variableValues_;
+
+    /*! \brief The parser_ which compiles and evaluates the mathematical expression */
+    std::unique_ptr<mu::Parser> parser_;
+};
+
+} // namespace gmx
+
+#endif // GMX_PULL_PULLCOORDEXPRESSIONPARSER_H
diff --git a/src/include/gromacs/pulling/transformationcoordinate.h b/src/include/gromacs/pulling/transformationcoordinate.h
new file mode 100644 (file)
index 0000000..c9b6c50
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ *
+ * \brief
+ * Declares function for compute transformation coordinate values and forces
+ *
+ * \author Oliver Fleetwood <oliver.fleetwood@gmail.com>
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Berk Hess <hess@kth.se>
+ *
+ */
+#ifndef GMX_PULL_TRANSFORMATIONCOORDINATE_H
+#define GMX_PULL_TRANSFORMATIONCOORDINATE_H
+
+struct pull_t;
+struct pull_coord_work_t;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+
+/*! \brief Calculates pull->coord[coord_ind].spatialData.value for a transformation pull coordinate
+ *
+ * This requires the values of the pull coordinates of lower indices to be set
+ * \param[in] coord  The (transformation) coordinate to compute the value for
+ * \param[in] variableCoords  Pull coordinates used as variables, entries 0 to coord->coordIndex
+ * will be used \returns Transformation value for pull coordinate.
+ */
+double getTransformationPullCoordinateValue(pull_coord_work_t*                coord,
+                                            ArrayRef<const pull_coord_work_t> variableCoords);
+
+/*! \brief Applies a force of a transformation pull coordinate and distributes it to pull coordinates of lower rank
+ *
+ * \param[in,out] pcrd            The transformation pull coordinate to act on
+ * \param[in,out] variableCoords  List of variable coords up to the coord index of \p pcrd
+ * \param[in] transformationCoordForce  The force working on coord \p pcrd
+ */
+void applyTransformationPullCoordForce(pull_coord_work_t*               pcrd,
+                                       gmx::ArrayRef<pull_coord_work_t> variableCoords,
+                                       double                           transformationCoordForce);
+
+} // namespace gmx
+
+#endif // GMX_PULL_TRANSFORMATIONCOORDINATE_H
diff --git a/src/include/gromacs/random/exponentialdistribution.h b/src/include/gromacs/random/exponentialdistribution.h
new file mode 100644 (file)
index 0000000..a038f1f
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \file
+ * \brief The exponential distribution
+ *
+ * Portable version of the exponential distribution that generates the same
+ * sequence on all platforms. Since stdlibc++ and libc++ provide different
+ * sequences we prefer this one so unit tests produce the same values on all
+ * platforms.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \inpublicapi
+ * \ingroup module_random
+ */
+
+#ifndef GMX_RANDOM_EXPONENTIALDISTRIBUTION_H
+#define GMX_RANDOM_EXPONENTIALDISTRIBUTION_H
+
+#include <cmath>
+
+#include <limits>
+#include <memory>
+
+#include "gromacs/random/uniformrealdistribution.h"
+#include "gromacs/utility/classhelpers.h"
+
+/*
+ * The portable version of the exponential distribution (to make sure we get the
+ * same values on all platforms) has been modified from the LLVM libcxx headers,
+ * distributed under the MIT license:
+ *
+ * Copyright (c) The LLVM compiler infrastructure
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace gmx
+{
+
+/*! \brief Exponential distribution
+ *
+ *  The C++ standard library does provide an exponential distribution, but even
+ *  though they all sample from a correct distribution, different standard
+ *  library implementations appear to return different sequences of numbers
+ *  for the same random number generator. To make it easier to use GROMACS
+ *  unit tests that depend on random numbers we have our own implementation.
+ *
+ * \tparam RealType Floating-point type, real by default in GROMACS.
+ */
+template<class RealType = real>
+class ExponentialDistribution
+{
+public:
+    /*! \brief Type of values returned */
+    typedef RealType result_type;
+
+    /*! \brief Exponential distribution parameters */
+    class param_type
+    {
+        /*! \brief The lambda/decay parameter */
+        result_type lambda_;
+
+    public:
+        /*! \brief Reference back to the distribution class */
+        typedef ExponentialDistribution distribution_type;
+
+        /*! \brief Construct parameter block
+         *
+         * \param lambda   lambda/decay parameter
+         */
+        explicit param_type(result_type lambda = 1.0) : lambda_(lambda) {}
+
+        /*! \brief Return lambda parameter */
+        result_type lambda() const { return lambda_; }
+
+        /*! \brief True if two parameter sets will return the same exponential distribution.
+         *
+         * \param x  Instance to compare with.
+         */
+        bool operator==(const param_type& x) const { return lambda_ == x.lambda_; }
+
+        /*! \brief True if two parameter sets will return different exponential distributions
+         *
+         * \param x  Instance to compare with.
+         */
+        bool operator!=(const param_type& x) const { return !operator==(x); }
+    };
+
+    /*! \brief Construct new distribution with given floating-point parameter.
+     *
+     * \param lambda   lambda/decay parameter
+
+     */
+    explicit ExponentialDistribution(result_type lambda = 1.0) : param_(param_type(lambda)) {}
+
+    /*! \brief Construct new distribution from parameter class
+     *
+     * \param param  Parameter class as defined inside gmx::ExponentialDistribution.
+     */
+    explicit ExponentialDistribution(const param_type& param) : param_(param) {}
+
+    /*! \brief Flush all internal saved values  */
+    void reset() {}
+
+    /*! \brief Return values from exponential distribution with internal parameters
+     *
+     *  \tparam Rng   Random engine class
+     *
+     *  \param  g     Random engine
+     */
+    template<class Rng>
+    result_type operator()(Rng& g)
+    {
+        return (*this)(g, param_);
+    }
+
+    /*! \brief Return value from exponential distribution with given parameters
+     *
+     *  \tparam Rng   Random engine class
+     *
+     *  \param  g     Random engine
+     *  \param  param Parameters to use
+     */
+    template<class Rng>
+    result_type operator()(Rng& g, const param_type& param)
+    {
+        return -std::log(result_type(1)
+                         - generateCanonical<result_type, std::numeric_limits<result_type>::digits>(g))
+               / param.lambda();
+    }
+
+    /*! \brief Return the lambda parameter of the exponential distribution */
+    result_type lambda() const { return param_.lambda(); }
+
+    /*! \brief Return the full parameter class of exponential distribution */
+    param_type param() const { return param_; }
+
+    /*! \brief Smallest value that can be returned from exponential distribution */
+    result_type min() const { return 0; }
+
+    /*! \brief Largest value that can be returned from exponential distribution */
+    result_type max() const { return std::numeric_limits<result_type>::infinity(); }
+
+    /*! \brief True if two exponential distributions will produce the same values.
+     *
+     * \param  x     Instance to compare with.
+     */
+    bool operator==(const ExponentialDistribution& x) const { return param_ == x.param_; }
+
+    /*! \brief True if two exponential distributions will produce different values.
+     *
+     * \param  x     Instance to compare with.
+     */
+    bool operator!=(const ExponentialDistribution& x) const { return !operator==(x); }
+
+private:
+    /*! \brief Internal value for parameters, can be overridden at generation time. */
+    param_type param_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(ExponentialDistribution);
+};
+
+} // namespace gmx
+
+#endif // GMX_MATH_RANDOM_H
diff --git a/src/include/gromacs/random/gammadistribution.h b/src/include/gromacs/random/gammadistribution.h
new file mode 100644 (file)
index 0000000..276fd4c
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \file
+ * \brief The gamma distribution
+ *
+ * Portable version of the gamma distribution that generates the same sequence
+ * on all platforms.
+ *
+ * \note The gamma distribution is broken in some standard library headers
+ * (including those shipped with gcc-4.9), and it is not guaranteed to
+ * generate the same result on stdlibc++ and libc++. Use this one instead so
+ * our unit tests produce the same values on all platforms.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \inpublicapi
+ * \ingroup module_random
+ */
+
+#ifndef GMX_RANDOM_GAMMADISTRIBUTION_H
+#define GMX_RANDOM_GAMMADISTRIBUTION_H
+
+#include <cmath>
+
+#include <limits>
+#include <memory>
+
+#include "gromacs/random/exponentialdistribution.h"
+#include "gromacs/random/uniformrealdistribution.h"
+#include "gromacs/utility/classhelpers.h"
+
+/*
+ * The workaround implementation for the broken std::gamma_distribution in the
+ * gcc-4.6 headers has been modified from the LLVM libcxx headers, distributed
+ * under the MIT license:
+ *
+ * Copyright (c) The LLVM compiler infrastructure
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace gmx
+{
+
+/*! \brief Gamma distribution
+ *
+ *  The C++ standard library does provide a gamma distribution, but when
+ *  using libstdc++-4.4.7 with at least gcc-4.6 the headers
+ *  produce errors. Even for newer compilers, libstdc++ and libc++ appear to
+ *  use different algorithms to generate it, which means their values differ
+ *  in contrast to the uniform and normal distributions where they are
+ *  identical. To avoid both compiler bugs and make it easier to use
+ *  GROMACS unit tests that depend on random numbers, we have our
+ *  own implementation.
+ *
+ *  Be warned that the gamma distribution works like the standard
+ *  normal distribution and keeps drawing values from the random engine
+ *  in a loop, so you want to make sure you use a random stream with a
+ *  very large margin to make sure you do not run out of random numbers
+ *  in an unlucky case (which will lead to an exception with the GROMACS
+ *  default random engine).
+ *
+ *  The gamma distribution is defined as
+ *
+ * \f[
+ *     p(x|\alpha,\beta) = \frac{1}{\Gamma(\alpha)\beta^{alpha}} x^{\alpha - 1}
+ * e^{-\frac{x}{\beta}}, x\geq 0 \f]
+ *
+ * \tparam RealType Floating-point type, real by default in GROMACS.
+ */
+template<class RealType = real>
+class GammaDistribution
+{
+public:
+    /*! \brief Type of values returned */
+    typedef RealType result_type;
+
+    /*! \brief Gamma distribution parameters */
+    class param_type
+    {
+        /*! \brief First parameter of gamma distribution */
+        result_type alpha_;
+        /*! \brief Second parameter of gamma distribution */
+        result_type beta_;
+
+    public:
+        /*! \brief Reference back to the distribution class */
+        typedef GammaDistribution distribution_type;
+
+        /*! \brief Construct parameter block
+         *
+         * \param alpha  First parameter of gamma distribution
+         * \param beta   Second parameter of gamma distribution
+         */
+        explicit param_type(result_type alpha = 1.0, result_type beta = 1.0) :
+            alpha_(alpha), beta_(beta)
+        {
+        }
+
+        /*! \brief Return first parameter */
+        result_type alpha() const { return alpha_; }
+        /*! \brief Return second parameter */
+        result_type beta() const { return beta_; }
+
+        /*! \brief True if two parameter sets will return the same distribution.
+         *
+         * \param x  Instance to compare with.
+         */
+        bool operator==(const param_type& x) const
+        {
+            return alpha_ == x.alpha_ && beta_ == x.beta_;
+        }
+
+        /*! \brief True if two parameter sets will return different distributions
+         *
+         * \param x  Instance to compare with.
+         */
+        bool operator!=(const param_type& x) const { return !operator==(x); }
+    };
+
+    /*! \brief Construct new distribution with given floating-point parameters.
+     *
+     * \param alpha  First parameter of gamma distribution
+     * \param beta   Second parameter of gamma distribution
+     */
+    explicit GammaDistribution(result_type alpha = 1.0, result_type beta = 1.0) :
+        param_(param_type(alpha, beta))
+    {
+    }
+
+    /*! \brief Construct new distribution from parameter class
+     *
+     * \param param  Parameter class as defined inside gmx::GammaDistribution.
+     */
+    explicit GammaDistribution(const param_type& param) : param_(param) {}
+
+    /*! \brief Flush all internal saved values  */
+    void reset() {}
+
+    /*! \brief Return values from gamma distribution with internal parameters
+     *
+     *  \tparam Rng   Random engine class
+     *
+     *  \param  g     Random engine
+     */
+    template<class Rng>
+    result_type operator()(Rng& g)
+    {
+        return (*this)(g, param_);
+    }
+
+    /*! \brief Return value from gamma distribution with given parameters
+     *
+     *  \tparam Rng   Random engine class
+     *
+     *  \param  g     Random engine
+     *  \param  param Parameters to use
+     */
+    template<class Rng>
+    result_type operator()(Rng& g, const param_type& param)
+    {
+        result_type                          alpha = param.alpha();
+        UniformRealDistribution<result_type> uniformDist(0, 1);
+        ExponentialDistribution<result_type> expDist;
+
+        result_type x;
+
+        if (alpha == 1.0)
+        {
+            x = expDist(g);
+        }
+        else if (alpha > 1.0)
+        {
+            const result_type b = alpha - 1.0;
+            const result_type c = 3.0 * alpha - result_type(0.75);
+
+            while (true)
+            {
+                const result_type u = uniformDist(g);
+                const result_type v = uniformDist(g);
+                const result_type w = u * (1 - u);
+
+                if (w != 0)
+                {
+                    const result_type y = std::sqrt(c / w) * (u - result_type(0.5));
+                    x                   = b + y;
+
+                    if (x >= 0)
+                    {
+                        const result_type z = 64 * w * w * w * v * v;
+
+                        if (z <= 1.0 - 2.0 * y * y / x)
+                        {
+                            break;
+                        }
+                        if (std::log(z) <= 2.0 * (b * std::log(x / b) - y))
+                        {
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        else // __a < 1
+        {
+            while (true)
+            {
+                const result_type u  = uniformDist(g);
+                const result_type es = expDist(g);
+
+                if (u <= 1.0 - alpha)
+                {
+                    x = std::pow(u, 1.0 / alpha);
+
+                    if (x <= es)
+                    {
+                        break;
+                    }
+                }
+                else
+                {
+                    const result_type e = -std::log((1.0 - u) / alpha);
+                    x                   = std::pow(1.0 - alpha + alpha * e, 1.0 / alpha);
+
+                    if (x <= e + es)
+                    {
+                        break;
+                    }
+                }
+            }
+        }
+        return x * param.beta();
+    }
+
+    /*! \brief Return the first parameter of gamma distribution */
+    result_type alpha() const { return param_.alpha(); }
+
+    /*! \brief Return the second parameter of gamma distribution */
+    result_type beta() const { return param_.beta(); }
+
+    /*! \brief Return the full parameter class of gamma distribution */
+    param_type param() const { return param_; }
+
+    /*! \brief Smallest value that can be returned from gamma distribution */
+    result_type min() const { return 0; }
+
+    /*! \brief Largest value that can be returned from gamma distribution */
+    result_type max() const { return std::numeric_limits<result_type>::infinity(); }
+
+    /*! \brief True if two gamma distributions will produce the same values.
+     *
+     * \param  x     Instance to compare with.
+     */
+    bool operator==(const GammaDistribution& x) const { return param_ == x.param_; }
+
+    /*! \brief True if two gamma distributions will produce different values.
+     *
+     * \param  x     Instance to compare with.
+     */
+    bool operator!=(const GammaDistribution& x) const { return !operator==(x); }
+
+private:
+    /*! \brief Internal value for parameters, can be overridden at generation time. */
+    param_type param_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(GammaDistribution);
+};
+
+} // namespace gmx
+
+#endif // GMX_RANDOM_GAMMADISTRIBUTION_H
diff --git a/src/include/gromacs/random/normaldistribution.h b/src/include/gromacs/random/normaldistribution.h
new file mode 100644 (file)
index 0000000..0925bf2
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \file
+ * \brief The normal distribution
+ *
+ * Portable version of the normal distribution that generates the same sequence
+ * on all platforms. Since stdlibc++ and libc++ provide different sequences
+ * we prefer this one so unit tests produce the same values on all platforms.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \inpublicapi
+ * \ingroup module_random
+ */
+
+#ifndef GMX_RANDOM_NORMALDISTRIBUTION_H
+#define GMX_RANDOM_NORMALDISTRIBUTION_H
+
+#include <cmath>
+
+#include <limits>
+#include <memory>
+
+#include "gromacs/random/uniformrealdistribution.h"
+#include "gromacs/utility/classhelpers.h"
+
+/*
+ * The portable version of the normal distribution (to make sure we get the same
+ * values on all platforms) has been modified from the LLVM libcxx headers,
+ * distributed under the MIT license:
+ *
+ * Copyright (c) The LLVM compiler infrastructure
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace gmx
+{
+
+/*! \brief Normal distribution
+ *
+ *  The C++ standard library does provide a normal distribution, but even
+ *  though they all sample from the normal distribution different standard
+ *  library implementations appear to return different sequences of numbers
+ *  for the same random number generator. To make it easier to use GROMACS
+ *  unit tests that depend on random numbers we have our own implementation.
+ *
+ *  Be warned that the normal distribution draws values from the random engine
+ *  in a loop, so you want to make sure you use a random stream with a
+ *  very large margin to make sure you do not run out of random numbers
+ *  in an unlucky case (which will lead to an exception with the GROMACS
+ *  default random engine).
+ *
+ * \tparam RealType Floating-point type, real by default in GROMACS.
+ */
+template<class RealType = real>
+class NormalDistribution
+{
+public:
+    /*! \brief Type of values returned */
+    typedef RealType result_type;
+
+    /*! \brief Normal distribution parameters */
+    class param_type
+    {
+        /*! \brief Mean of normal distribution */
+        result_type mean_;
+        /*! \brief Standard deviation of distribution */
+        result_type stddev_;
+
+    public:
+        /*! \brief Reference back to the distribution class */
+        typedef NormalDistribution distribution_type;
+
+        /*! \brief Construct parameter block
+         *
+         * \param mean     Mean of normal distribution
+         * \param stddev   Standard deviation of normal distribution
+         */
+        explicit param_type(result_type mean = 0.0, result_type stddev = 1.0) :
+            mean_(mean), stddev_(stddev)
+        {
+        }
+
+        /*! \brief Return first parameter */
+        result_type mean() const { return mean_; }
+        /*! \brief Return second parameter */
+        result_type stddev() const { return stddev_; }
+
+        /*! \brief True if two parameter sets will return the same normal distribution.
+         *
+         * \param x  Instance to compare with.
+         */
+        bool operator==(const param_type& x) const
+        {
+            return mean_ == x.mean_ && stddev_ == x.stddev_;
+        }
+
+        /*! \brief True if two parameter sets will return different normal distributions
+         *
+         * \param x  Instance to compare with.
+         */
+        bool operator!=(const param_type& x) const { return !operator==(x); }
+    };
+
+    /*! \brief Construct new distribution with given floating-point parameters.
+     *
+     * \param mean     Mean of normal distribution
+     * \param stddev   Standard deviation of normal distribution
+     */
+    explicit NormalDistribution(result_type mean = 0.0, result_type stddev = 1.0) :
+        param_(param_type(mean, stddev)), hot_(false), saved_(0)
+    {
+    }
+
+    /*! \brief Construct new distribution from parameter class
+     *
+     * \param param  Parameter class as defined inside gmx::NormalDistribution.
+     */
+    explicit NormalDistribution(const param_type& param) : param_(param), hot_(false), saved_(0) {}
+
+    /*! \brief Flush all internal saved values  */
+    void reset() { hot_ = false; }
+
+    /*! \brief Return values from normal distribution with internal parameters
+     *
+     *  \tparam Rng   Random engine class
+     *
+     *  \param  g     Random engine
+     */
+    template<class Rng>
+    result_type operator()(Rng& g)
+    {
+        return (*this)(g, param_);
+    }
+
+    /*! \brief Return value from normal distribution with given parameters
+     *
+     *  \tparam Rng   Random engine class
+     *
+     *  \param  g     Random engine
+     *  \param  param Parameters to use
+     */
+    template<class Rng>
+    result_type operator()(Rng& g, const param_type& param)
+    {
+        result_type result;
+
+        if (hot_)
+        {
+            hot_   = false;
+            result = saved_;
+        }
+        else
+        {
+            UniformRealDistribution<result_type> uniformDist(-1.0, 1.0);
+            result_type                          u;
+            result_type                          v;
+            result_type                          s;
+
+            do
+            {
+                u = uniformDist(g);
+                v = uniformDist(g);
+                s = u * u + v * v;
+            } while (s > 1.0 || s == 0.0);
+
+            s      = std::sqrt(-2.0 * std::log(s) / s);
+            saved_ = v * s;
+            hot_   = true;
+            result = u * s;
+        }
+        return result * param.stddev() + param.mean();
+    }
+
+    /*! \brief Return the mean of the normal distribution */
+    result_type mean() const { return param_.mean(); }
+
+    /*! \brief Return the standard deviation of the normal distribution */
+    result_type stddev() const { return param_.stddev(); }
+
+    /*! \brief Return the full parameter class of the normal distribution */
+    param_type param() const { return param_; }
+
+    /*! \brief Smallest value that can be returned from normal distribution */
+    result_type min() const { return -std::numeric_limits<result_type>::infinity(); }
+
+    /*! \brief Largest value that can be returned from normal distribution */
+    result_type max() const { return std::numeric_limits<result_type>::infinity(); }
+
+    /*! \brief True if two normal distributions will produce the same values.
+     *
+     * \param  x     Instance to compare with.
+     */
+    bool operator==(const NormalDistribution& x) const
+    {
+        /* Equal if: Params are identical, and saved-state is identical,
+         * and if we have something saved, it must be identical.
+         */
+        return param_ == x.param_ && hot_ == x.hot_ && (!hot_ || saved_ == x.saved_);
+    }
+
+    /*! \brief True if two normal distributions will produce different values.
+     *
+     * \param  x     Instance to compare with.
+     */
+    bool operator!=(const NormalDistribution& x) const { return !operator==(x); }
+
+private:
+    /*! \brief Internal value for parameters, can be overridden at generation time. */
+    param_type param_;
+    /*! \brief True if there is a saved result to return */
+    bool hot_;
+    /*! \brief The saved result to return - only valid if hot_ is true */
+    result_type saved_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(NormalDistribution);
+};
+
+
+} // namespace gmx
+
+#endif // GMX_RANDOM_NORMALDISTRIBUTION_H
diff --git a/src/include/gromacs/random/seed.h b/src/include/gromacs/random/seed.h
new file mode 100644 (file)
index 0000000..1baf028
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \file
+ * \brief Random seed and domain utilities
+ *
+ * This file contains utilities to create true random seeds from the system,
+ * and logic to keep track of different random domains for random engines such
+ * as ThreeFry that can take a second seed value.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \inpublicapi
+ * \ingroup module_random
+ */
+
+#ifndef GMX_RANDOM_SEED_H
+#define GMX_RANDOM_SEED_H
+
+#include <random>
+
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+
+/*! \brief Return 64 random bits from the random device, suitable as seed.
+ *
+ *  If the internal random device output is smaller than 64 bits, this routine
+ *  will use multiple calls internally until we have 64 bits of random data.
+ *
+ *  \return 64-bit unsigned integer with random bits.
+ */
+uint64_t makeRandomSeed();
+
+/*! \brief Random device
+ *
+ *  For now this is identical to the standard library, but since we use
+ *  the GROMACS random module for all other random engines and distributions
+ *  it is convenient to have this too in the same module.
+ */
+typedef std::random_device RandomDevice;
+
+/*! \brief Enumerated values for fixed part of random seed (domain)
+ *
+ *  Random numbers are used in many places in GROMACS, and to avoid identical
+ *  streams the random seeds should be different. Instead of keeping track of
+ *  several different user-provided seeds, it is better to use the fact that
+ *  generators like ThreeFry take two 64-bit keys, and combine a general
+ *  user-provided 64-bit random seed with a second constant value from this list
+ *  to make each stream guaranteed unique.
+ *
+ *  \note There is no reason to go overboard with adding options; we only
+ *        need to guarantee different streams for cases that might be present
+ *        simultaneously in a single simulation. As an example, two different
+ *        integrators (or thermostats) can reuse the same domain.
+ *  \note When you do add options, leave some space between the values so
+ *        you can group new options with old ones without changing old values.
+ */
+enum class RandomDomain
+{
+    Other                 = 0x00000000, //!< Generic - stream uniqueness is not important
+    MaxwellVelocities     = 0x00001000, //!< Veolcity assignment from Maxwell distribution
+    TestParticleInsertion = 0x00002000, //!< Test particle insertion
+    UpdateCoordinates     = 0x00003000, //!< Particle integrators
+    UpdateConstraints     = 0x00004000, //!< Second integrator step for constraints
+    Thermostat            = 0x00005000, //!< Stochastic temperature coupling
+    Barostat              = 0x00006000, //!< Stochastic pressure coupling
+    ReplicaExchange       = 0x00007000, //!< Replica exchange metropolis moves
+    ExpandedEnsemble      = 0x00008000, //!< Expanded ensemble lambda moves
+    AwhBiasing            = 0x00009000  //!< AWH biasing reference value moves
+};
+
+} // namespace gmx
+
+#endif // GMX_RANDOM_SEED_H
diff --git a/src/include/gromacs/random/tabulatednormaldistribution.h b/src/include/gromacs/random/tabulatednormaldistribution.h
new file mode 100644 (file)
index 0000000..fa69e62
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \file
+ * \brief Tabulated normal distribution
+ *
+ * A very fast normal distribution, but with limited resolution.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \inpublicapi
+ * \ingroup module_random
+ */
+
+#ifndef GMX_RANDOM_TABULATEDNORMALDISTRIBUTION_H
+#define GMX_RANDOM_TABULATEDNORMALDISTRIBUTION_H
+
+#include <cmath>
+
+#include <array>
+#include <limits>
+#include <memory>
+
+#include "gromacs/math/functions.h"
+#include "gromacs/math/utilities.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+namespace detail
+{
+
+//! Number of bits that determines the resolution of the lookup table for the normal distribution.
+constexpr int c_TabulatedNormalDistributionDefaultBits = 14;
+
+} // namespace detail
+
+/*! \brief Tabulated normal random distribution
+ *
+ *  Random distribution compatible with C++11 distributions - it can be
+ *  used with any C++11 random engine.
+ *
+ *  \tparam RealType  Type of the return value. Float or double. Note that
+ *                    GROMACS uses "real" type by default in contrast to the C++11
+ *                    standard library, to avoid double/float conversions.
+ *  \tparam tableBits Size of the table, specified in bits. The storage
+ *                    space required is sizeof(RealType)*2^tableBits. To
+ *                    keep things sane this is limited to 24 bits.
+ *
+ *  Some stochastic integrators depend on drawing a lot of normal
+ *  distribution random numbers quickly, but in many cases the only
+ *  important property is the distribution - given the noise in forces
+ *  we do not need very high resolution.
+ *  This distribution uses an internal table to return samples from a
+ *  normal distribution with limited resolution. By default the table
+ *  uses c_TabulatedNormalDistributionDefaultBits bits, but this is
+ *  specified with a template parameter.
+ *
+ *  Since this distribution only uses tableBits bits per value generated,
+ *  the values draw from the random engine are used for several results.
+ *  To make sure you get a reproducible result when using counter-based
+ *  random engines (such as ThreeFry2x64), remember to call the reset()
+ *  method to cancel the internal memory of the distribution.
+ *
+ *  \note For modern NUMA systems, you likely want to use separate
+ *        distributions for each thread, and make sure they are initialized
+ *        on the CPU where they will run, so the table is placed in that
+ *        NUMA memory pool.
+ *  \note The finite table resolution means this distribution will NOT
+ *        return arbitrarily small/large values, but with e.g. 14 bits
+ *        the results are limited to roughly +/- 4 standard deviations.
+ */
+template<class RealType = real, unsigned int tableBits = detail::c_TabulatedNormalDistributionDefaultBits>
+class TabulatedNormalDistribution
+{
+    static_assert(tableBits <= 24,
+                  "Normal distribution table is limited to 24bits (64MB in single precision)");
+
+public:
+    /*! \brief  Type of normal distribution results */
+    typedef RealType result_type;
+
+    /*! \brief  Normal distribution parameter class (mean and stddev) */
+    class param_type
+    {
+    public:
+        /*! \brief The type of distribution the parameters describe */
+        typedef TabulatedNormalDistribution distribution_type;
+
+        /*! \brief Constructor. Default is classical distr. with mean 0, stddev 1.
+         *
+         * \param mean     Expectation value.
+         * \param stddev   Standard deviation.
+         *
+         */
+        explicit param_type(result_type mean = 0.0, result_type stddev = 1.0) :
+            mean_(mean), stddev_(stddev)
+        {
+        }
+
+        /*! \brief Return mean parameter of normal distribution */
+        result_type mean() const { return mean_; }
+
+        /*! \brief Return standard deviation parameter of normal distribution */
+        result_type stddev() const { return stddev_; }
+
+        /*! \brief True if two sets of normal distributions parameters are identical
+         *
+         * \param x Instance to compare with.
+         */
+        bool operator==(const param_type& x) const
+        {
+            return (mean_ == x.mean_ && stddev_ == x.stddev_);
+        }
+
+        /*! \brief True if two sets of normal distributions parameters are different.
+         *
+         * \param x Instance to compare with.
+         */
+        bool operator!=(const param_type& x) const { return !operator==(x); }
+
+    private:
+        /*! \brief Internal storage for mean of normal distribution */
+        result_type mean_;
+        /*! \brief Internal storage for standard deviation of normal distribution */
+        result_type stddev_;
+    };
+
+    /*! \brief Fill the table with values for the normal distribution
+     *
+     *  This routine returns a new a std::array with the table data.
+     *
+     *  This routine is used to help construct objects of this class,
+     *  and is exposed only to permit testing. Normal code should not
+     *  need to call this function.
+     */
+    static std::array<RealType, 1 << tableBits> makeTable()
+    {
+        /* Fill the table with the integral of a gaussian distribution, which
+         * corresponds to the inverse error function.
+         * We avoid integrating a gaussian numerically, since that leads to
+         * some loss-of-precision which also accumulates so it is worse for
+         * larger indices in the table. */
+        constexpr std::size_t tableSize   = 1 << tableBits;
+        constexpr std::size_t halfSize    = tableSize / 2;
+        constexpr double      invHalfSize = 1.0 / halfSize;
+
+        std::array<RealType, tableSize> table;
+
+        // Fill in all but the extremal entries of the table
+        for (std::size_t i = 0; i < halfSize - 1; i++)
+        {
+            double r = (i + 0.5) * invHalfSize;
+            double x = std::sqrt(2.0) * erfinv(r);
+
+            table.at(halfSize - 1 - i) = -x;
+            table.at(halfSize + i)     = x;
+        }
+        // We want to fill in the extremal table entries with
+        // values that make the total variance equal to 1, so
+        // measure the variance by summing the squares of the
+        // other values of the distribution, starting from the
+        // smallest values.
+        double sumOfSquares = 0;
+        for (std::size_t i = 1; i < halfSize; i++)
+        {
+            double value = table.at(i);
+            sumOfSquares += value * value;
+        }
+        double missingVariance = 1.0 - 2.0 * sumOfSquares / tableSize;
+        GMX_RELEASE_ASSERT(missingVariance > 0,
+                           "Incorrect computation of tabulated normal distribution");
+        double extremalValue = std::sqrt(0.5 * missingVariance * tableSize);
+        table.at(0)          = -extremalValue;
+        table.back()         = extremalValue;
+
+        return table;
+    }
+
+    /*! \brief Construct new normal distribution with specified mean & stdddev.
+     *
+     *  \param mean    Mean value of tabulated normal distribution
+     *  \param stddev  Standard deviation of tabulated normal distribution
+     */
+    explicit TabulatedNormalDistribution(result_type mean = 0.0, result_type stddev = 1.0) :
+        param_(param_type(mean, stddev)), savedRandomBits_(0), savedRandomBitsLeft_(0)
+    {
+    }
+
+    /*! \brief Construct new normal distribution from parameter type.
+     *
+     *  \param param Parameter class containing mean and standard deviation.
+     */
+    explicit TabulatedNormalDistribution(const param_type& param) :
+        param_(param), savedRandomBits_(0), savedRandomBitsLeft_(0)
+    {
+    }
+
+    /*! \brief Smallest value that can be generated in normal distrubiton.
+     *
+     * \note The smallest value is not -infinity with a table, but it
+     *       depends on the table resolution. With 14 bits, this is roughly
+     *       four standard deviations below the mean.
+     */
+    result_type min() const { return c_table_[0]; }
+
+    /*! \brief Largest value that can be generated in normal distribution.
+     *
+     * \note The largest value is not infinity with a table, but it
+     *       depends on the table resolution. With 14 bits, this is roughly
+     *       four standard deviations above the mean.
+     */
+    result_type max() const { return c_table_[c_table_.size() - 1]; }
+
+    /*! \brief Mean of the present normal distribution */
+    result_type mean() const { return param_.mean(); }
+
+    /*! \brief Standard deviation of the present normal distribution */
+
+    result_type stddev() const { return param_.stddev(); }
+
+    /*! \brief The parameter class (mean & stddev) of the normal distribution */
+    param_type param() const { return param_; }
+
+    /*! \brief Clear all internal saved random bits from the random engine */
+    void reset() { savedRandomBitsLeft_ = 0; }
+
+    /*! \brief Return normal distribution value specified by internal parameters.
+     *
+     * \tparam Rng   Random engine type used to provide uniform random bits.
+     * \param  g     Random engine of class Rng. For normal GROMACS usage
+     *               you likely want to use ThreeFry2x64.
+     */
+    template<class Rng>
+    result_type operator()(Rng& g)
+    {
+        return (*this)(g, param_);
+    }
+
+    /*! \brief Return normal distribution value specified by given parameters
+     *
+     * \tparam Rng   Random engine type used to provide uniform random bits.
+     * \param  g     Random engine of class Rng. For normal GROMACS usage
+     *               you likely want to use ThreeFry2x64.
+     * \param  param Parameters used to specify normal distribution.
+     */
+    template<class Rng>
+    result_type operator()(Rng& g, const param_type& param)
+    {
+        if (savedRandomBitsLeft_ < tableBits)
+        {
+            // We do not know whether the generator g returns 64 or 32 bits,
+            // since g is not known when we construct this class.
+            // To keep things simple, we always draw one random number,
+            // store it in our 64-bit value, and set the number of active bits.
+            // For tableBits up to 16 this will be as efficient both with 32
+            // and 64 bit random engines when drawing multiple numbers
+            // (our default value is
+            // c_TabulatedNormalDistributionDefaultBits == 14). It
+            // also avoids drawing multiple 32-bit random numbers
+            // even if we just call this routine for a single
+            // result.
+            savedRandomBits_     = static_cast<uint64_t>(g());
+            savedRandomBitsLeft_ = std::numeric_limits<typename Rng::result_type>::digits;
+        }
+        result_type value = c_table_[savedRandomBits_ & ((1ULL << tableBits) - 1)];
+        savedRandomBits_ >>= tableBits;
+        savedRandomBitsLeft_ -= tableBits;
+        return param.mean() + value * param.stddev();
+    }
+
+    /*!\brief Check if two tabulated normal distributions have identical states.
+     *
+     * \param  x     Instance to compare with.
+     */
+    bool operator==(const TabulatedNormalDistribution<RealType, tableBits>& x) const
+    {
+        return (param_ == x.param_ && savedRandomBits_ == x.savedRandomBits_
+                && savedRandomBitsLeft_ == x.savedRandomBitsLeft_);
+    }
+
+    /*!\brief Check if two tabulated normal distributions have different states.
+     *
+     * \param  x     Instance to compare with.
+     */
+    bool operator!=(const TabulatedNormalDistribution<RealType, tableBits>& x) const
+    {
+        return !operator==(x);
+    }
+
+private:
+    /*! \brief Parameters of normal distribution (mean and stddev) */
+    param_type param_;
+    /*! \brief Array with tabluated values of normal distribution */
+    static const std::array<RealType, 1 << tableBits> c_table_;
+    /*! \brief Saved output from random engine, shifted tableBits right each time */
+    uint64_t savedRandomBits_;
+    /*! \brief Number of valid bits remaining i savedRandomBits_ */
+    unsigned int savedRandomBitsLeft_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(TabulatedNormalDistribution);
+};
+
+// MSVC does not handle extern template class members correctly even in MSVC 2015,
+// so in that case we have to instantiate in every object using it. In addition,
+// doxygen is convinced this defines a function (which leads to crashes in our python
+// scripts), so to avoid confusion we hide it from doxygen too.
+#if !defined(_MSC_VER) && !defined(DOXYGEN)
+// Declaration of template specialization
+template<>
+const std::array<real, 1 << detail::c_TabulatedNormalDistributionDefaultBits> TabulatedNormalDistribution<>::c_table_;
+
+extern template const std::array<real, 1 << detail::c_TabulatedNormalDistributionDefaultBits>
+        TabulatedNormalDistribution<>::c_table_;
+#endif
+
+// Instantiation for all tables without specialization
+template<class RealType, unsigned int tableBits>
+const std::array<RealType, 1 << tableBits> TabulatedNormalDistribution<RealType, tableBits>::c_table_ =
+        TabulatedNormalDistribution<RealType, tableBits>::makeTable();
+
+} // namespace gmx
+
+#endif // GMX_RANDOM_TABULATEDNORMALDISTRIBUTION_H
diff --git a/src/include/gromacs/random/threefry.h b/src/include/gromacs/random/threefry.h
new file mode 100644 (file)
index 0000000..9ff38a7
--- /dev/null
@@ -0,0 +1,894 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \file
+ * \brief Implementation of the 2x64 ThreeFry random engine
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \inpublicapi
+ * \ingroup module_random
+ */
+
+#ifndef GMX_RANDOM_THREEFRY_H
+#define GMX_RANDOM_THREEFRY_H
+
+#include <array>
+#include <limits>
+#include <memory>
+
+#include "gromacs/math/functions.h"
+#include "gromacs/random/seed.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/exceptions.h"
+
+/*
+ * The GROMACS implementation of the ThreeFry random engine has been
+ * heavily inspired by the versions proposed to Boost by:
+ *
+ * John Salmon, Copyright 2010-2014 by D. E. Shaw Research
+ * https://github.com/DEShawResearch/Random123-Boost
+ *
+ * Thijs van den Berg, Copyright (c) 2014 M.A. (Thijs) van den Berg
+ * https://github.com/sitmo/threefry
+ *
+ * Both of them are covered by the Boost Software License:
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+namespace gmx
+{
+
+namespace internal
+{
+// Variable-bitfield counters used to increment internal counters as
+// part of std::arrays.
+
+struct highBitCounter
+{
+    /*! \brief Clear highBits higest bits of ctr, return false if they were non-zero.
+     *
+     *  This function clears the space required for the internal counters,
+     *  and returns true if they were correctly zero when calling, false otherwise.
+     *
+     *  \tparam        UIntType  Integer type to use for each word in counter
+     *  \tparam        words     Number of UIntType words in counter
+     *  \tparam        highBits  Number of bits to check. The template parameter makes it
+     *                           possible to optimize this extensively at compile time.
+     *  \param         ctr       Reference to counter to check and clear.
+     */
+    template<class UIntType, std::size_t words, unsigned int highBits>
+    static bool checkAndClear(std::array<UIntType, words>* ctr)
+    {
+        const std::size_t bitsPerWord = std::numeric_limits<UIntType>::digits;
+        const std::size_t bitsTotal   = bitsPerWord * words;
+
+        static_assert(highBits <= bitsTotal, "High bits do not fit in counter.");
+
+        const std::size_t lastWordIdx       = (bitsTotal - highBits) / bitsPerWord;
+        const std::size_t lastWordLowBitIdx = (bitsTotal - highBits) % bitsPerWord;
+        const UIntType    lastWordOne       = static_cast<UIntType>(1) << lastWordLowBitIdx;
+        const UIntType    mask              = lastWordOne - 1;
+
+        bool isClear = true;
+
+        for (unsigned int i = words - 1; i > lastWordIdx; --i)
+        {
+            if ((*ctr)[i])
+            {
+                isClear   = false;
+                (*ctr)[i] = 0;
+            }
+        }
+        if (highBits > 0 && (*ctr)[lastWordIdx] >= lastWordOne)
+        {
+            isClear = false;
+            (*ctr)[lastWordIdx] &= mask;
+        }
+        return isClear;
+    }
+
+    /*! \brief Increment the internal counter in highBits by one
+     *
+     *  \tparam         UIntType  Integer type to use for each word in counter
+     *  \tparam         words     Number of UIntType words in counter
+     *  \tparam         highBits  Number of bits reserved for the internal counter.
+     *  \param          ctr       Reference to the counter value to increment.
+     *
+     *  \throws InternalError if internal counter space is exhausted.
+     *
+     *  This routine will work across the word boundaries for any number
+     *  of internal counter bits that fits in the total counter.
+     */
+    template<class UIntType, std::size_t words, unsigned int highBits>
+    static void increment(std::array<UIntType, words>* ctr)
+    {
+        const std::size_t bitsPerWord = std::numeric_limits<UIntType>::digits;
+        const std::size_t bitsTotal   = bitsPerWord * words;
+
+        static_assert(highBits <= bitsTotal, "High bits do not fit in counter.");
+
+        const std::size_t lastWordIdx       = (bitsTotal - highBits) / bitsPerWord;
+        const std::size_t lastWordLowBitIdx = (bitsTotal - highBits) % bitsPerWord;
+        const UIntType    lastWordOne       = static_cast<UIntType>(1) << lastWordLowBitIdx;
+
+        // For algorithm & efficiency reasons we need to store the internal counter in
+        // the same array as the user-provided counter, so we use the higest bits, possibly
+        // crossing several words.
+        //
+        // To have the computer help us with the dirty carry arithmetics we store the bits
+        // in the internal counter part in normal fashion, but the internal counter words in
+        // reverse order; the highest word of the total counter array (words-1) is thus
+        // the least significant part of the internal counter (if it spans several words).
+        //
+        // The incrementation works as follows:
+        //
+        // 0) If the index of the least significant internal counter word is larger
+        //    than words-1, there was never any space.
+        // 1) If the internal counter spans more than one word, we must have one or
+        //    more internal counter words that correspond entirely to the this counter.
+        //    Start with the least significant one (words-1) and increment it.
+        //    If the new value is not zero we did not loop around (no carry), so everything
+        //    is good, and we are done - return!
+        //    If the new value is zero, we need to move the carry result to the next word,
+        //    so we just continue the loop until we have gone through all words that
+        //    are internal-counter-only.
+        // 2) After the loop, there is stuff remaining to add, and by definition there
+        //    is some internal counter space in the next word, but the question
+        //    is if we have exhausted it. We already created a constant that corresponds
+        //    to the bit that represents '1' for the internal counter part of this word.
+        //    When we add this constant it will not affect the user-counter-part at all,
+        //    and if we exhaust the internal counter space the high bits will cause the entire
+        //    word to wrap around, and the result will be smaller than the bit we added.
+        //    If this happens we throw, otherwise we're done.
+        //
+        // Since all constants will be evaluated at compile time, this entire routine
+        // will usually be reduced to simply incrementing a word by a constant, and throwing
+        // if the result is smaller than the constant.
+
+        if (lastWordIdx >= words)
+        {
+            GMX_THROW(InternalError(
+                    "Cannot increment random engine defined with 0 internal counter bits."));
+        }
+
+        for (unsigned int i = words - 1; i > lastWordIdx; --i)
+        {
+            (*ctr)[i]++;
+            if ((*ctr)[i])
+            {
+                return; // No carry means we are done
+            }
+        }
+        (*ctr)[lastWordIdx] += lastWordOne;
+        if ((*ctr)[lastWordIdx] < lastWordOne)
+        {
+            GMX_THROW(InternalError("Random engine stream ran out of internal counter space."));
+        }
+    }
+
+    /*! \brief Increment the internal counter in highBits by a value.
+     *
+     *  \tparam        UIntType  Integer type to use for each word in counter
+     *  \tparam        words     Number of UIntType words in counter
+     *  \tparam        highBits  Number of bits reserved for the internal counter.
+     *  \param         ctr       Reference to the counter to increment.
+     *  \param         addend    Value to add to internal.
+     *
+     *  \throws InternalError if internal counter space is exhausted.
+     *
+     *  This routine will work across the word boundaries for any number
+     *  of internal counter bits that fits in the total counter.
+     */
+    template<class UIntType, std::size_t words, unsigned int highBits>
+    static void increment(std::array<UIntType, words>* ctr, UIntType addend)
+    {
+        const std::size_t bitsPerWord = std::numeric_limits<UIntType>::digits;
+        const std::size_t bitsTotal   = bitsPerWord * words;
+
+        static_assert(highBits <= bitsTotal, "High bits do not fit in counter.");
+
+        const std::size_t lastWordIdx       = (bitsTotal - highBits) / bitsPerWord;
+        const std::size_t lastWordLowBitIdx = (bitsTotal - highBits) % bitsPerWord;
+        const UIntType    lastWordOne       = static_cast<UIntType>(1) << lastWordLowBitIdx;
+        const UIntType    lastWordMaxVal    = (~static_cast<UIntType>(0)) >> lastWordLowBitIdx;
+
+        if (lastWordIdx >= words)
+        {
+            GMX_THROW(InternalError(
+                    "Cannot increment random engine defined with 0 internal counter bits."));
+        }
+
+        for (unsigned int i = words - 1; i > lastWordIdx; --i)
+        {
+            (*ctr)[i] += addend;
+            addend = ((*ctr)[i] < addend); // 1 is the carry!
+            if (addend == 0)
+            {
+                return;
+            }
+        }
+
+        if (addend > lastWordMaxVal)
+        {
+            GMX_THROW(InternalError("Random engine stream ran out of internal counter space."));
+        }
+        addend *= lastWordOne;
+
+        (*ctr)[lastWordIdx] += addend;
+
+        if ((*ctr)[lastWordIdx] < addend)
+        {
+            GMX_THROW(InternalError("Random engine stream ran out of internal counter space."));
+        }
+    }
+};
+} // namespace internal
+
+/*! \brief General implementation class for ThreeFry counter-based random engines.
+ *
+ *  This class is used to implement several different ThreeFry2x64 random engines
+ *  differing in the number of rounds executed in and the number of bits reserved
+ *  for the internal counter. It is compatible with C++11 random engines, and
+ *  can be used e.g. with all random distributions from the standard library.
+ *
+ *  ThreeFry is a counter-based rather than state-based random engine. This
+ *  means that we seed it with a "key", after which we can get the
+ *  N:th random number in a sequence (specified by a counter) directly. This
+ *  means we are guaranteed the same sequence of numbers even when running in
+ *  parallel if using e.g. step and atom index as counters.
+ *
+ *  However, it is also useful to be able to use it as a normal random engine,
+ *  for instance if you need more than 2 64-bit random values for a specific
+ *  counter value, not to mention where you just need good normal random numbers.
+ *  To achieve this, this implementation uses John Salmon's idea of reserving
+ *  a couple of the highest bits in the user-provided counter for an internal
+ *  counter. For instance, if reserving 3 bits, this means you get a stream of
+ *  8 iterations (each with 2 random values) after every restart. If you call
+ *  the engine after these bits have been exhausted, it will throw an
+ *  exception to make sure you don't get overlapping streams by mistake.
+ *  Reserving 3 bits also means you can only use 64-3=61 bits of the highest
+ *  word when restarting (i.e., setting) the counters.
+ *
+ *  This version also supports using internalCounterBits=0. In this case the
+ *  random engine will be able to return a single counter round, i.e. 2 64-bit
+ *  values for ThreeFry2x64, after which an exception is thrown. In this case no
+ *  high bits are reserved, which means the class implements the raw ThreeFry2x64
+ *  random function.
+ *
+ *  \tparam rounds  The number of encryption iterations used when generating.
+ *                  This can in principle be any value, but 20 rounds has been
+ *                  shown to pass all BigCrush random tests, and with 13 rounds
+ *                  only one fails. This is a very stringent test, and the
+ *                  standard Mersenne Twister engine fails two, so 13 rounds
+ *                  should be a perfectly fine balance in most cases.
+ *  \tparam internalCounterBits
+ *                  Number of high bits in the user-provided counter reserved
+ *                  for the internal counter. The number of values the engine
+ *                  can return after each restart will be
+ *                  words*2^internalCounterBits.
+ */
+template<unsigned int rounds, unsigned int internalCounterBits>
+class ThreeFry2x64General
+{
+    // While this class will formally work with any value for rounds, there is
+    // no reason to go lower than 13, and this might help catch some typos.
+    // If we find a reason to use lower values in the future, or if you simply
+    // want to test, this assert can safely be removed.
+    static_assert(rounds >= 13,
+                  "You should not use less than 13 encryption rounds for ThreeFry2x64.");
+
+public:
+    // result_type must be lower case to be compatible with C++11 standard library
+
+    /*! \brief Integer type for output. */
+    typedef uint64_t result_type;
+    /*! \brief Use array for counter & key states so it is allocated on the stack */
+    typedef std::array<result_type, 2> counter_type;
+
+private:
+    /*! \brief Rotate value left by specified number of bits
+     *
+     *  \param i    Value to rotate (result_type, which should be 64-bit).
+     *  \param bits Number of bits to rotate i.
+     *
+     *  \return Input value rotated 'bits' left.
+     */
+    result_type rotLeft(result_type i, unsigned int bits)
+    {
+        return (i << bits) | (i >> (std::numeric_limits<result_type>::digits - bits));
+    }
+
+    /*! \brief Perform encryption step for ThreeFry2x64 algorithm
+     *
+     *  It performs the encryption step of the standard ThreeFish symmetric-key
+     *  tweakable block cipher, which is the core of the ThreeFry random
+     *  engine. The number of encryption rounds is specified by the class
+     *  template parameter 'rounds'.
+     *
+     *  \param key   Reference to key value
+     *  \param ctr   Counter value to use
+     *
+     *  \return Newly encrypted 2x64 block, according to the class template parameters.
+     */
+    counter_type generateBlock(const counter_type& key, const counter_type& ctr)
+    {
+        const unsigned int rotations[] = { 16, 42, 12, 31, 16, 32, 24, 21 };
+        counter_type       x           = ctr;
+
+        result_type ks[3] = { 0x0, 0x0, 0x1bd11bdaa9fc1a22 };
+
+        // This is actually a pretty simple routine that merely executes the
+        // for-block specified further down 'rounds' times. However, both
+        // clang and gcc have problems unrolling and replacing rotations[r%8]
+        // with constants, so we unroll the first 20 iterations manually.
+
+        if (rounds > 0)
+        {
+            ks[0] = key[0];
+            ks[2] ^= key[0];
+            x[0]  = x[0] + key[0];
+            ks[1] = key[1];
+            ks[2] ^= key[1];
+            x[1] = x[1] + key[1];
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 16);
+            x[1] ^= x[0];
+        }
+        if (rounds > 1)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 42);
+            x[1] ^= x[0];
+        }
+        if (rounds > 2)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 12);
+            x[1] ^= x[0];
+        }
+        if (rounds > 3)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 31);
+            x[1] ^= x[0];
+            x[0] += ks[1];
+            x[1] += ks[2] + 1;
+        }
+        if (rounds > 4)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 16);
+            x[1] ^= x[0];
+        }
+        if (rounds > 5)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 32);
+            x[1] ^= x[0];
+        }
+        if (rounds > 6)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 24);
+            x[1] ^= x[0];
+        }
+        if (rounds > 7)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 21);
+            x[1] ^= x[0];
+            x[0] += ks[2];
+            x[1] += ks[0] + 2;
+        }
+        if (rounds > 8)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 16);
+            x[1] ^= x[0];
+        }
+        if (rounds > 9)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 42);
+            x[1] ^= x[0];
+        }
+        if (rounds > 10)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 12);
+            x[1] ^= x[0];
+        }
+        if (rounds > 11)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 31);
+            x[1] ^= x[0];
+            x[0] += ks[0];
+            x[1] += ks[1] + 3;
+        }
+        if (rounds > 12)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 16);
+            x[1] ^= x[0];
+        }
+        if (rounds > 13)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 32);
+            x[1] ^= x[0];
+        }
+        if (rounds > 14)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 24);
+            x[1] ^= x[0];
+        }
+        if (rounds > 15)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 21);
+            x[1] ^= x[0];
+            x[0] += ks[1];
+            x[1] += ks[2] + 4;
+        }
+        if (rounds > 16)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 16);
+            x[1] ^= x[0];
+        }
+        if (rounds > 17)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 42);
+            x[1] ^= x[0];
+        }
+        if (rounds > 18)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 12);
+            x[1] ^= x[0];
+        }
+        if (rounds > 19)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], 31);
+            x[1] ^= x[0];
+            x[0] += ks[2];
+            x[1] += ks[0] + 5;
+        }
+
+        for (unsigned int r = 20; r < rounds; r++)
+        {
+            x[0] += x[1];
+            x[1] = rotLeft(x[1], rotations[r % 8]);
+            x[1] ^= x[0];
+            if (((r + 1) & 3) == 0)
+            {
+                unsigned int r4 = (r + 1) >> 2;
+                x[0] += ks[r4 % 3];
+                x[1] += ks[(r4 + 1) % 3] + r4;
+            }
+        }
+        return x;
+    }
+
+public:
+    //! \brief Smallest value that can be returned from random engine.
+#if !defined(_MSC_VER)
+    static constexpr
+#else
+    // Avoid constexpr bug in MSVC 2015, note that max() below does work
+    static
+#endif
+            result_type
+            min()
+    {
+        return std::numeric_limits<result_type>::min();
+    }
+
+    //! \brief Largest value that can be returned from random engine.
+    static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }
+
+    /*! \brief Construct random engine with 2x64 key values
+     *
+     *  This constructor takes two values, and should only be used with
+     *  the 2x64 implementations.
+     *
+     *  \param key0   Random seed in the form of a 64-bit unsigned value.
+     *  \param domain Random domain. This is used to guarantee that different
+     *                applications of a random engine inside the code get different
+     *                streams of random numbers, without requiring the user
+     *                to provide lots of random seeds. Pick a value from the
+     *                RandomDomain class, or RandomDomain::Other if it is
+     *                not important. In the latter case you might want to use
+     *                \ref gmx::DefaultRandomEngine instead.
+     *
+     *  \note The random domain is really another 64-bit seed value.
+     *
+     *  \throws InternalError if the high bits needed to encode the number of counter
+     *          bits are nonzero.
+     */
+    //NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
+    ThreeFry2x64General(uint64_t key0 = 0, RandomDomain domain = RandomDomain::Other)
+    {
+        seed(key0, domain);
+    }
+
+    /*! \brief Construct random engine from 2x64-bit unsigned integers
+     *
+     *  This constructor assigns the raw 128 bit key data from unsigned integers.
+     *  It is meant for the case when you want full control over the key,
+     *  for instance to compare with reference values of the ThreeFry
+     *  function during testing.
+     *
+     *  \param key0   First word of key/random seed.
+     *  \param key1   Second word of key/random seed.
+     *
+     *  \throws InternalError if the high bits needed to encode the number of counter
+     *          bits are nonzero. To test arbitrary values, use 0 internal counter bits.
+     */
+    //NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
+    ThreeFry2x64General(uint64_t key0, uint64_t key1) { seed(key0, key1); }
+
+    /*! \brief Seed 2x64 random engine with two 64-bit key values
+     *
+     *  \param key0   First word of random seed, in the form of 64-bit unsigned values.
+     *  \param domain Random domain. This is used to guarantee that different
+     *                applications of a random engine inside the code get different
+     *                streams of random numbers, without requiring the user
+     *                to provide lots of random seeds. Pick a value from the
+     *                RandomDomain class, or RandomDomain::Other if it is
+     *                not important. In the latter case you might want to use
+     *                \ref gmx::DefaultRandomEngine instead.
+     *
+     *  \note The random domain is really another 64-bit seed value.
+     *
+     *  Re-initialized the seed similar to the counter constructor.
+     *  Same rules apply: The highest few bits of the last word are
+     *  reserved to encode the number of internal counter bits, but
+     *  to save the user the trouble of making sure these are zero
+     *  when using e.g. a random device, we just ignore them.
+     */
+    void seed(uint64_t key0 = 0, RandomDomain domain = RandomDomain::Other)
+    {
+        seed(key0, static_cast<uint64_t>(domain));
+    }
+
+    /*! \brief Seed random engine from 2x64-bit unsigned integers
+     *
+     *  This assigns the raw 128 bit key data from unsigned integers.
+     *  It is meant for the case when you want full control over the key,
+     *  for instance to compare with reference values of the ThreeFry
+     *  function during testing.
+     *
+     *  \param key0   First word of key/random seed.
+     *  \param key1   Second word of key/random seed.
+     *
+     *  \throws InternalError if the high bits needed to encode the number of counter
+     *          bits are nonzero. To test arbitrary values, use 0 internal counter bits.
+     */
+    void seed(uint64_t key0, uint64_t key1)
+    {
+        const unsigned int internalCounterBitsBits =
+                (internalCounterBits > 0) ? (StaticLog2<internalCounterBits>::value + 1) : 0;
+
+        key_ = { { key0, key1 } };
+
+        if (internalCounterBits > 0)
+        {
+            internal::highBitCounter::checkAndClear<result_type, 2, internalCounterBitsBits>(&key_);
+            internal::highBitCounter::increment<result_type, 2, internalCounterBitsBits>(
+                    &key_, internalCounterBits - 1);
+        }
+        restart(0, 0);
+    }
+
+    /*! \brief Restart 2x64 random engine counter from 2 64-bit values
+     *
+     *  \param ctr0 First word of new counter, in the form of 64-bit unsigned values.
+     *  \param ctr1 Second word of new counter
+     *
+     * Restarting the engine with a new counter is extremely fast with ThreeFry64,
+     * and basically just consists of storing the counter value, so you should
+     * use this liberally in your innermost loops to restart the engine with
+     * e.g. the current step and atom index as counter values.
+     *
+     * \throws InternalError if any of the highest bits that are reserved
+     *         for the internal part of the counter are set. The number of
+     *         reserved bits is to the last template parameter to the class.
+     */
+    void restart(uint64_t ctr0 = 0, uint64_t ctr1 = 0)
+    {
+
+        counter_ = { { ctr0, ctr1 } };
+        if (!internal::highBitCounter::checkAndClear<result_type, 2, internalCounterBits>(&counter_))
+        {
+            GMX_THROW(InternalError(
+                    "High bits of counter are reserved for the internal stream counter."));
+        }
+        block_ = generateBlock(key_, counter_);
+        index_ = 0;
+    }
+
+    /*! \brief Generate the next random number
+     *
+     *  This will return the next stored 64-bit value if one is available,
+     *  and otherwise generate a new block, update the internal counters, and
+     *  return the first value while storing the others.
+     *
+     *  \throws InternalError if the internal counter space is exhausted.
+     */
+    result_type operator()()
+    {
+        if (index_ >= c_resultsPerCounter_)
+        {
+            internal::highBitCounter::increment<result_type, 2, internalCounterBits>(&counter_);
+            block_ = generateBlock(key_, counter_);
+            index_ = 0;
+        }
+        return block_[index_++];
+    }
+
+    /*! \brief Skip next n random numbers
+     *
+     *  Moves the internal random stream for the give key/counter value
+     *  n positions forward. The count is based on the number of random values
+     *  returned, such that skipping 5 values gives exactly the same result as
+     *  drawing 5 values that are ignored.
+     *
+     *  \param n Number of values to jump forward.
+     *
+     *  \throws InternalError if the internal counter space is exhausted.
+     */
+    void discard(uint64_t n)
+    {
+        index_ += n % c_resultsPerCounter_;
+        n /= c_resultsPerCounter_;
+
+        if (index_ > c_resultsPerCounter_)
+        {
+            index_ -= c_resultsPerCounter_;
+            n++;
+        }
+
+        // Make sure the state is the same as if we came to this counter and
+        // index by natural generation.
+        if (index_ == 0 && n > 0)
+        {
+            index_ = c_resultsPerCounter_;
+            n--;
+        }
+        internal::highBitCounter::increment<result_type, 2, internalCounterBits>(&counter_, n);
+        block_ = generateBlock(key_, counter_);
+    }
+
+    /*! \brief Return true if two ThreeFry2x64 engines are identical
+     *
+     * \param  x    Instance to compare with.
+     *
+     * This routine should return true if the two engines will generate
+     * identical random streams when drawing.
+     */
+    bool operator==(const ThreeFry2x64General<rounds, internalCounterBits>& x) const
+    {
+        // block_ is uniquely specified by key_ and counter_.
+        return (key_ == x.key_ && counter_ == x.counter_ && index_ == x.index_);
+    }
+
+    /*! \brief Return true of two ThreeFry2x64 engines are not identical
+     *
+     * \param  x    Instance to compare with.
+     *
+     * This routine should return true if the two engines will generate
+     * different random streams when drawing.
+     */
+    bool operator!=(const ThreeFry2x64General<rounds, internalCounterBits>& x) const
+    {
+        return !operator==(x);
+    }
+
+private:
+    /*! \brief Number of results returned for each invocation of the block generation */
+    static const unsigned int c_resultsPerCounter_ =
+            static_cast<unsigned int>(sizeof(counter_type) / sizeof(result_type));
+
+    /*! \brief ThreeFry2x64 key, i.e. the random seed for this stream.
+     *
+     *  The highest few bits of the key are replaced to encode the value of
+     *  internalCounterBits, in order to make all streams unique.
+     */
+    counter_type key_;
+
+    /*! \brief ThreeFry2x64 total counter.
+     *
+     *  The highest internalCounterBits are reserved for an internal counter
+     *  so that the combination of a key and counter provides a stream that
+     *  returns 2*2^internalCounterBits (ThreeFry2x64) random 64-bit values before
+     *  the internal counter space is exhausted and an exception is thrown.
+     */
+    counter_type counter_;
+    /*! \brief The present block encrypted from values of key and counter. */
+    counter_type block_;
+    /*! \brief Index of the next value in block_ to return from random engine */
+    unsigned int index_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(ThreeFry2x64General);
+};
+
+
+/*! \brief ThreeFry2x64 random engine with 20 iteractions.
+ *
+ *  \tparam internalCounterBits, default 64.
+ *
+ *  This class provides very high quality random numbers that pass all
+ *  BigCrush tests, it works with two 64-bit values each for keys and
+ *  counters, and is most  efficient when we only need a few random values
+ *  before restarting the counters with new values.
+ */
+template<unsigned int internalCounterBits = 64>
+class ThreeFry2x64 : public ThreeFry2x64General<20, internalCounterBits>
+{
+public:
+    /*! \brief Construct ThreeFry random engine with 2x64 key values, 20 rounds.
+     *
+     *  \param key0   Random seed in the form of a 64-bit unsigned value.
+     *  \param domain Random domain. This is used to guarantee that different
+     *                applications of a random engine inside the code get different
+     *                streams of random numbers, without requiring the user
+     *                to provide lots of random seeds. Pick a value from the
+     *                RandomDomain class, or RandomDomain::Other if it is
+     *                not important. In the latter case you might want to use
+     *                \ref gmx::DefaultRandomEngine instead.
+     *
+     *  \note The random domain is really another 64-bit seed value.
+     *
+     *  \throws InternalError if the high bits needed to encode the number of counter
+     *          bits are nonzero.
+     */
+    ThreeFry2x64(uint64_t key0 = 0, RandomDomain domain = RandomDomain::Other) :
+        ThreeFry2x64General<20, internalCounterBits>(key0, domain)
+    {
+    }
+
+    /*! \brief Construct random engine from 2x64-bit unsigned integers, 20 rounds
+     *
+     *  This constructor assigns the raw 128 bit key data from unsigned integers.
+     *  It is meant for the case when you want full control over the key,
+     *  for instance to compare with reference values of the ThreeFry
+     *  function during testing.
+     *
+     *  \param key0   First word of key/random seed.
+     *  \param key1   Second word of key/random seed.
+     *
+     *  \throws InternalError if the high bits needed to encode the number of counter
+     *          bits are nonzero. To test arbitrary values, use 0 internal counter bits.
+     */
+    ThreeFry2x64(uint64_t key0, uint64_t key1) :
+        ThreeFry2x64General<20, internalCounterBits>(key0, key1)
+    {
+    }
+};
+
+/*! \brief ThreeFry2x64 random engine with 13 iteractions.
+ *
+ *  \tparam internalCounterBits, default 64.
+ *
+ *  This class provides relatively high quality random numbers that only
+ *  fail one BigCrush test, and it is a bit faster than the 20-round version.
+ *  It works with two 64-bit values each for keys and counters, and is most
+ *  efficient when we only need a few random values before restarting
+ *  the counters with new values.
+ */
+template<unsigned int internalCounterBits = 64>
+class ThreeFry2x64Fast : public ThreeFry2x64General<13, internalCounterBits>
+{
+public:
+    /*! \brief Construct ThreeFry random engine with 2x64 key values, 13 rounds.
+     *
+     *  \param key0   Random seed in the form of a 64-bit unsigned value.
+     *  \param domain Random domain. This is used to guarantee that different
+     *                applications of a random engine inside the code get different
+     *                streams of random numbers, without requiring the user
+     *                to provide lots of random seeds. Pick a value from the
+     *                RandomDomain class, or RandomDomain::Other if it is
+     *                not important. In the latter case you might want to use
+     *                \ref gmx::DefaultRandomEngine instead.
+     *
+     *  \note The random domain is really another 64-bit seed value.
+     *
+     *  \throws InternalError if the high bits needed to encode the number of counter
+     *          bits are nonzero.
+     */
+    ThreeFry2x64Fast(uint64_t key0 = 0, RandomDomain domain = RandomDomain::Other) :
+        ThreeFry2x64General<13, internalCounterBits>(key0, domain)
+    {
+    }
+
+    /*! \brief Construct ThreeFry random engine from 2x64-bit unsigned integers, 13 rounds.
+     *
+     *  This constructor assigns the raw 128 bit key data from unsigned integers.
+     *  It is meant for the case when you want full control over the key,
+     *  for instance to compare with reference values of the ThreeFry
+     *  function during testing.
+     *
+     *  \param key0   First word of key/random seed.
+     *  \param key1   Second word of key/random seed.
+     *
+     *  \throws InternalError if the high bits needed to encode the number of counter
+     *          bits are nonzero. To test arbitrary values, use 0 internal counter bits.
+     */
+    ThreeFry2x64Fast(uint64_t key0, uint64_t key1) :
+        ThreeFry2x64General<13, internalCounterBits>(key0, key1)
+    {
+    }
+};
+
+
+/*! \brief Default fast and accurate random engine in Gromacs
+ *
+ *  This engine will return 2*2^64 random results using the default
+ *  gmx::RandomDomain::Other stream, and can be initialized with a single
+ *  seed argument without having to remember empty template angle brackets.
+ */
+typedef ThreeFry2x64Fast<> DefaultRandomEngine;
+
+} // namespace gmx
+
+#endif // GMX_RANDOM_THREEFRY_H
diff --git a/src/include/gromacs/random/uniformintdistribution.h b/src/include/gromacs/random/uniformintdistribution.h
new file mode 100644 (file)
index 0000000..ded2311
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \file
+ * \brief The uniform integer distribution
+ *
+ * Portable version of the uniform integer that generates the same sequence
+ * on all platforms. Since stdlibc++ and libc++ provide different sequences
+ * we prefer this one so unit tests produce the same values on all platforms.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \inpublicapi
+ * \ingroup module_random
+ */
+
+#ifndef GMX_RANDOM_UNIFORMINTDISTRIBUTION_H
+#define GMX_RANDOM_UNIFORMINTDISTRIBUTION_H
+
+#include <limits>
+#include <memory>
+
+#include "gromacs/math/functions.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+/*! \brief Uniform integer distribution
+ *
+ *  The C++ standard library does provide this distribution, but even
+ *  though they all sample from the correct distribution different standard
+ *  library implementations appear to return different sequences of numbers
+ *  for the same random number generator. To make it easier to use GROMACS
+ *  unit tests that depend on random numbers we have our own implementation.
+ *
+ * \tparam IntType Integer type, int by default.
+ */
+template<class IntType = int>
+class UniformIntDistribution
+{
+public:
+    /*! \brief Type of values returned */
+    typedef IntType result_type;
+
+    /*! \brief Uniform int distribution parameters */
+    class param_type
+    {
+        /*! \brief Lower end of range (inclusive) */
+        result_type a_;
+        /*! \brief Upper end of range (inclusive) */
+        result_type b_;
+
+    public:
+        /*! \brief Reference back to the distribution class */
+        typedef UniformIntDistribution distribution_type;
+
+        /*! \brief Construct parameter block
+         *
+         * \param a   Lower end of range (inclusive)
+         * \param b   Upper end of range (inclusive)
+         */
+        explicit param_type(result_type a = 0, result_type b = std::numeric_limits<result_type>::max()) :
+            a_(a), b_(b)
+        {
+            GMX_RELEASE_ASSERT(a <= b, "The uniform integer distribution requires a<=b");
+        }
+
+        /*! \brief Return lower range */
+        result_type a() const { return a_; }
+        /*! \brief Return upper range */
+        result_type b() const { return b_; }
+
+        /*! \brief True if two parameter sets will return the same uniform int distribution.
+         *
+         * \param x  Instance to compare with.
+         */
+        bool operator==(const param_type& x) const
+        {
+            // rangeBits is a function of a & b, so it does not have to be tested
+            return a_ == x.a_ && b_ == x.b_;
+        }
+
+        /*! \brief True if two parameter sets will return different uniform int distributions
+         *
+         * \param x  Instance to compare with.
+         */
+        bool operator!=(const param_type& x) const { return !operator==(x); }
+    };
+
+    /*! \brief Construct new distribution with given integer parameters.
+     *
+     * \param a   Lower end of range (inclusive)
+     * \param b   Upper end of range (inclusive)
+     */
+    explicit UniformIntDistribution(result_type a = 0,
+                                    result_type b = std::numeric_limits<result_type>::max()) :
+        param_(param_type(a, b)), savedRandomBits_(0), savedRandomBitsLeft_(0)
+    {
+    }
+
+    /*! \brief Construct new distribution from parameter class
+     *
+     * \param param  Parameter class as defined inside gmx::UniformIntDistribution.
+     */
+    explicit UniformIntDistribution(const param_type& param) :
+        param_(param), savedRandomBits_(0), savedRandomBitsLeft_(0)
+    {
+    }
+
+    /*! \brief Flush all internal saved values  */
+    void reset() { savedRandomBitsLeft_ = 0; }
+
+    /*! \brief Return values from uniform int distribution with internal parameters
+     *
+     * \tparam Rng  Uniform random engine class
+     *
+     * \param  g    Random engine
+     */
+    template<class Rng>
+    result_type operator()(Rng& g)
+    {
+        return (*this)(g, param_);
+    }
+
+    /*! \brief Return value from uniform int distribution with given parameters
+     *
+     * \tparam Rng   Uniform random engine class
+     *
+     * \param  g     Random engine
+     * \param  param Parameters to use
+     */
+    template<class Rng>
+    result_type operator()(Rng& g, const param_type& param)
+    {
+        static_assert(sizeof(typename Rng::result_type) >= sizeof(uint32_t),
+                      "The random engine result_type should be 32 or 64 bits");
+
+        result_type  range = param.b() - param.a();
+        unsigned int rangeBits;
+        result_type  result;
+
+        if (range == 0)
+        {
+            return param.a();
+        }
+        else if (range == std::numeric_limits<result_type>::max())
+        {
+            rangeBits = std::numeric_limits<result_type>::digits; // Use all bits in type
+        }
+        else
+        {
+            if (sizeof(result_type) == sizeof(uint32_t))
+            {
+                rangeBits = log2I(static_cast<uint32_t>(range));
+            }
+            else
+            {
+                rangeBits = log2I(range);
+            }
+            rangeBits += ((range >> rangeBits) > 0);
+        }
+
+        do
+        {
+            if (savedRandomBitsLeft_ < rangeBits)
+            {
+                savedRandomBits_     = static_cast<uint64_t>(g());
+                savedRandomBitsLeft_ = std::numeric_limits<typename Rng::result_type>::digits;
+
+                if (sizeof(typename Rng::result_type) == sizeof(uint32_t))
+                {
+                    savedRandomBits_ <<= std::numeric_limits<uint32_t>::digits;
+                    savedRandomBits_ |= g();
+                    savedRandomBitsLeft_ += std::numeric_limits<uint32_t>::digits;
+                }
+            }
+            result = savedRandomBits_;
+            savedRandomBits_ >>= rangeBits;
+            result = result - (savedRandomBits_ << rangeBits);
+            savedRandomBitsLeft_ -= rangeBits;
+        } while (result > range);
+
+        return result + param.a();
+    }
+
+    /*! \brief Return the lower range uniform int distribution */
+    result_type a() const { return param_.a(); }
+
+    /*! \brief Return the upper range of the uniform int distribution */
+    result_type b() const { return param_.b(); }
+
+    /*! \brief Return the full parameter class of the uniform int distribution */
+    param_type param() const { return param_; }
+
+    /*! \brief Smallest value that can be returned from uniform int distribution */
+    result_type min() const { return a(); }
+
+    /*! \brief Largest value that can be returned from uniform int distribution */
+    result_type max() const { return b(); }
+
+    /*! \brief True if two uniform int distributions will produce the same values.
+     *
+     * \param  x     Instance to compare with.
+     */
+    bool operator==(const UniformIntDistribution& x) const { return param_ == x.param_; }
+
+    /*! \brief True if two uniform int distributions will produce different values.
+     *
+     * \param  x     Instance to compare with.
+     */
+    bool operator!=(const UniformIntDistribution& x) const { return !operator==(x); }
+
+private:
+    /*! \brief Internal value for parameters, can be overridden at generation time. */
+    param_type param_;
+    /*! \brief Saved output from random engine, shifted tableBits right each time */
+    uint64_t savedRandomBits_;
+    /*! \brief Number of valid bits remaining i savedRandomBits_ */
+    unsigned int savedRandomBitsLeft_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(UniformIntDistribution);
+};
+
+} // namespace gmx
+
+#endif // GMX_RANDOM_UNIFORMINTDISTRIBUTION_H
diff --git a/src/include/gromacs/random/uniformrealdistribution.h b/src/include/gromacs/random/uniformrealdistribution.h
new file mode 100644 (file)
index 0000000..f54fb43
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \file
+ * \brief The uniform real distribution
+ *
+ * Portable version of the uniform real that generates the same sequence
+ * on all platforms. Since stdlibc++ and libc++ provide different sequences
+ * we prefer this one so unit tests produce the same values on all platforms.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \inpublicapi
+ * \ingroup module_random
+ */
+
+#ifndef GMX_RANDOM_UNIFORMREALDISTRIBUTION_H
+#define GMX_RANDOM_UNIFORMREALDISTRIBUTION_H
+
+#include <cmath>
+
+#include <algorithm>
+#include <limits>
+#include <memory>
+#include <type_traits>
+
+#include "gromacs/math/functions.h"
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+/*
+ * The portable version of the uniform real distribution (to make sure we get
+ * the same values on all platforms) has been modified from the LLVM libcxx
+ * headers, distributed under the MIT license:
+ *
+ * Copyright (c) The LLVM compiler infrastructure
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+namespace gmx
+{
+
+/*! \brief Generate a floating-point value with specified number of random bits
+ *
+ * \tparam RealType  Floating-point type to generate
+ * \tparam Bits      Number of random bits to generate
+ * \tparam Rng       Random number generator class
+ *
+ * \param  g         Random number generator to use
+ *
+ * This implementation avoids the bug in libc++ and stdlibc++ (which is due
+ * to the C++ standard being unclear) where 1.0 can be returned occasionally.
+ *
+ */
+template<class RealType = real, unsigned int Bits, class Rng>
+RealType generateCanonical(Rng& g)
+{
+    // No point in using more bits than fit in RealType
+    const uint64_t digits   = std::numeric_limits<RealType>::digits;
+    const uint64_t realBits = std::min(digits, static_cast<uint64_t>(Bits));
+    const uint64_t log2R    = std::numeric_limits<typename Rng::result_type>::digits;
+    uint64_t       k        = realBits / log2R + (realBits % log2R != 0) + (realBits == 0);
+    // Note that Rng::max and Rng::min are typically an integer type.
+    // Only unsigned integer types can express the range using the
+    // same type. Converting to RealType before computing the range
+    // would work but we have no need for that.
+    static_assert(std::is_unsigned_v<decltype(Rng::max())> && std::is_unsigned_v<decltype(Rng::min())>,
+                  "Rng::max and Rng::min must be unsigned");
+    RealType r    = RealType(Rng::max() - Rng::min()) + RealType(1);
+    RealType s    = g() - Rng::min();
+    RealType base = r;
+    RealType result;
+
+    for (uint64_t i = 1; i < k; ++i)
+    {
+        s += RealType(g() - Rng::min()) * base;
+        base *= r;
+    }
+    result = s / base;
+
+    // This implementation is specified by the C++ standard, but unfortunately it
+    // has a bug where 1.0 can be generated occasionally due to the limited
+    // precision of floating point, while 0.0 is only generated half as often as
+    // it should. We "solve" both these issues by swapping 1.0 for 0.0 when it happens.
+    //
+    // See:
+    // https://llvm.org/bugs/show_bug.cgi?id=18767
+    // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63176
+    //
+    // Note that we prefer not to use the gcc 'fix' of looping until the result
+    // is smaller than 1.0, since that breaks the strict specification of the
+    // number of times the rng will be called.
+    //
+    // This can only happen when we ask for the same number of bits that fit
+    // in RealType, so by checking for that we avoid the extra code in all other
+    // cases. If you are worried about it: Use RealType=double with 32 bits.
+    //
+    if (realBits == digits && result == 1.0)
+    {
+        result = 0.0;
+    }
+    return result;
+}
+
+
+/*! \brief Uniform real distribution
+ *
+ *  The C++ standard library does provide this distribution, but even
+ *  though they all sample from the correct distribution different standard
+ *  library implementations appear to return different sequences of numbers
+ *  for the same random number generator. To make it easier to use GROMACS
+ *  unit tests that depend on random numbers we have our own implementation.
+ *
+ * \tparam RealType Floating-point type, real by default in GROMACS.
+ */
+template<class RealType = real>
+class UniformRealDistribution
+{
+public:
+    /*! \brief Type of values returned */
+    typedef RealType result_type;
+
+    /*! \brief Uniform real distribution parameters */
+    class param_type
+    {
+        /*! \brief Lower end of range (inclusive) */
+        result_type a_;
+        /*! \brief Upper end of range (exclusive) */
+        result_type b_;
+
+    public:
+        /*! \brief Reference back to the distribution class */
+        typedef UniformRealDistribution distribution_type;
+
+        /*! \brief Construct parameter block
+         *
+         * \param a   Lower end of range (inclusive)
+         * \param b   Upper end of range (exclusive)
+         */
+        explicit param_type(result_type a = 0.0, result_type b = 1.0) : a_(a), b_(b)
+        {
+            GMX_RELEASE_ASSERT(a < b, "The uniform real distribution requires a<b");
+        }
+
+        /*! \brief Return first parameter */
+        result_type a() const { return a_; }
+        /*! \brief Return second parameter */
+        result_type b() const { return b_; }
+
+        /*! \brief True if two parameter sets will return the same uniform real distribution.
+         *
+         * \param x  Instance to compare with.
+         */
+        bool operator==(const param_type& x) const { return a_ == x.a_ && b_ == x.b_; }
+
+        /*! \brief True if two parameter sets will return different uniform real distributions
+         *
+         * \param x  Instance to compare with.
+         */
+        bool operator!=(const param_type& x) const { return !operator==(x); }
+    };
+
+    /*! \brief Construct new distribution with given floating-point parameters.
+     *
+     * \param a   Lower end of range (inclusive)
+     * \param b   Upper end of range (exclusive)
+     */
+    explicit UniformRealDistribution(result_type a = 0.0, result_type b = 1.0) :
+        param_(param_type(a, b))
+    {
+    }
+
+    /*! \brief Construct new distribution from parameter class
+     *
+     * \param param  Parameter class as defined inside gmx::UniformRealDistribution.
+     */
+    explicit UniformRealDistribution(const param_type& param) : param_(param) {}
+
+    /*! \brief Flush all internal saved values  */
+    void reset() {}
+
+    /*! \brief Return values from uniform real distribution with internal parameters
+     *
+     * \tparam Rng  Random engine class
+     *
+     * \param  g    Random engine
+     */
+    template<class Rng>
+    result_type operator()(Rng& g)
+    {
+        return (*this)(g, param_);
+    }
+
+    /*! \brief Return value from uniform real distribution with given parameters
+     *
+     * \tparam Rng   Random engine class
+     *
+     * \param  g     Random engine
+     * \param  param Parameters to use
+     */
+    template<class Rng>
+    result_type operator()(Rng& g, const param_type& param)
+    {
+        result_type r = generateCanonical<RealType, std::numeric_limits<RealType>::digits>(g);
+        return (param.b() - param.a()) * r + param.a();
+    }
+
+    /*! \brief Return the lower range uniform real distribution */
+    result_type a() const { return param_.a(); }
+
+    /*! \brief Return the upper range of the uniform real distribution */
+    result_type b() const { return param_.b(); }
+
+    /*! \brief Return the full parameter class of the uniform real distribution */
+    param_type param() const { return param_; }
+
+    /*! \brief Smallest value that can be returned from uniform real distribution */
+    result_type min() const { return a(); }
+
+    /*! \brief Largest value that can be returned from uniform real distribution */
+    result_type max() const { return b(); }
+
+    /*! \brief True if two uniform real distributions will produce the same values.
+     *
+     * \param  x     Instance to compare with.
+     */
+    bool operator==(const UniformRealDistribution& x) const { return param_ == x.param_; }
+
+    /*! \brief True if two uniform real distributions will produce different values.
+     *
+     * \param  x     Instance to compare with.
+     */
+    bool operator!=(const UniformRealDistribution& x) const { return !operator==(x); }
+
+private:
+    /*! \brief Internal value for parameters, can be overridden at generation time. */
+    param_type param_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(UniformRealDistribution);
+};
+
+} // namespace gmx
+
+#endif // GMX_RANDOM_UNIFORMREALDISTRIBUTION_H
diff --git a/src/include/gromacs/restraint/manager.h b/src/include/gromacs/restraint/manager.h
new file mode 100644 (file)
index 0000000..eda0e81
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GROMACS_RESTRAINT_MANAGER_H
+#define GROMACS_RESTRAINT_MANAGER_H
+
+/*! \libinternal \file
+ * \brief Declare the Manager for restraint potentials.
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_restraint
+ */
+
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include "gromacs/restraint/restraintpotential.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct t_commrec;
+struct t_mdatoms;
+struct pull_t;
+
+namespace gmx
+{
+
+/*! \libinternal \ingroup module_restraint
+ * \brief Manage the Restraint potentials available for Molecular Dynamics.
+ *
+ * A simulation runner owns one manager resource to hold restraint objects used
+ * in the simulation. In the case of thread MPI simulations, multiple runner
+ * instances will have handles to the same underlying resource. With further
+ * factoring of the mdrun call stack, this facility can be combined with others
+ * into a simulation context object from which simulation code can retrieve
+ * support code for a user-configured simulation.
+ *
+ * Calling code provides the manager with a means to access the various required input data
+ * to be used when restraints are computed.
+ *
+ * \todo This should be generalized as work description and factory functions in Context.
+ */
+class RestraintManager final
+{
+public:
+    //! Create new restraint manager resources with empty set of restraints.
+    RestraintManager();
+
+    ~RestraintManager();
+
+    /*!
+     * \brief Client code can access the shared resource by copying or moving a handle.
+     * \{
+     */
+    RestraintManager(const RestraintManager& /* unused */) = default;
+    RestraintManager& operator=(const RestraintManager& /* unused */) = default;
+    RestraintManager(RestraintManager&&) noexcept                     = default;
+    RestraintManager& operator=(RestraintManager&& /* unused */) noexcept = default;
+    /*! \} */
+
+    /*!
+     * \brief Clear registered restraints and reset the manager.
+     */
+    void clear() noexcept;
+
+    /*!
+     * \brief Get the number of currently managed restraints.
+     *
+     * \return number of restraints.
+     *
+     * \internal
+     * Only considers the IRestraintPotential objects
+     */
+    unsigned long countRestraints() noexcept;
+
+    /*! \brief Obtain the ability to create a restraint MDModule
+     *
+     * Though the name is reminiscent of the evolving idea of a work specification, the
+     * Spec here is just a list of restraint modules.
+     *
+     * \param restraint shared ownership of a restraint potential interface.
+     * \param name key by which to reference the restraint.
+     */
+    void addToSpec(std::shared_ptr<gmx::IRestraintPotential> restraint, const std::string& name);
+
+    /*!
+     * \brief Get a copy of the current set of restraints to be applied.
+     *
+     * This function is to be used when launching a simulation to get the
+     * restraint handles to bind, so it is not performance sensitive. A new
+     * vector is returned with each call because it is unspecified whether
+     * the set of handles point to the same objects on all threads or between
+     * calls to getRestraints.
+     *
+     * \return a copy of the list of restraint potentials.
+     */
+    std::vector<std::shared_ptr<IRestraintPotential>> getRestraints() const;
+
+private:
+    class Impl;
+    //! Ownership of the shared reference to the global manager.
+    std::shared_ptr<Impl> instance_;
+};
+
+} // end namespace gmx
+
+#endif // GROMACS_RESTRAINT_MANAGER_H
diff --git a/src/include/gromacs/restraint/restraintmdmodule.h b/src/include/gromacs/restraint/restraintmdmodule.h
new file mode 100644 (file)
index 0000000..57fd28d
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GROMACS_RESTRAINTMDMODULE_H
+#define GROMACS_RESTRAINTMDMODULE_H
+
+/*! \libinternal \file
+ * \brief Library interface for RestraintMDModule
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_restraint
+ */
+
+#include "gromacs/mdtypes/imdmodule.h"
+#include "gromacs/restraint/restraintpotential.h"
+
+namespace gmx
+{
+
+// Forward declaration to allow opaque pointer to library internal class.
+class RestraintMDModuleImpl;
+struct MDModulesNotifiers;
+
+/*! \libinternal \ingroup module_restraint
+ * \brief MDModule wrapper for Restraint implementations.
+ *
+ * Shares ownership of an object implementing the IRestraintPotential interface.
+ * Provides the IMDModule interfaces.
+ */
+class RestraintMDModule final : public gmx::IMDModule
+{
+public:
+    RestraintMDModule() = delete;
+
+    /*!
+     * \brief Constructor used by static create() method.
+     */
+    explicit RestraintMDModule(std::unique_ptr<RestraintMDModuleImpl> restraint);
+
+    ~RestraintMDModule() override;
+
+    /*!
+     * \brief Wrap a restraint potential as an MDModule
+     *
+     * Consumers of the interfaces provided by an IMDModule do not extend the lifetime
+     * of the interface objects returned by mdpOptionProvider(), outputProvider(), or
+     * registered via initForceProviders(). Calling code must keep this object alive
+     * as long as those interfaces are needed (probably the duration of an MD run).
+     *
+     * \param restraint shared ownership of an object for calculating restraint forces
+     * \param sites list of sites for the framework to pass to the restraint
+     * \return new wrapper object sharing ownership of restraint
+     */
+    static std::unique_ptr<RestraintMDModule> create(std::shared_ptr<gmx::IRestraintPotential> restraint,
+                                                     const std::vector<int>& sites);
+
+    /*!
+     * \brief Implement IMDModule interface
+     *
+     * Unused.
+     *
+     * \return nullptr.
+     */
+    IMdpOptionProvider* mdpOptionProvider() override;
+
+    /*!
+     * \brief Implement IMDModule interface
+     *
+     * Unused.
+     *
+     * \return nullptr.
+     */
+    IMDOutputProvider* outputProvider() override;
+
+    /*!
+     * \brief Implement IMDModule interface.
+     *
+     * See gmx::IMDModule::initForceProviders()
+     * \param forceProviders manager in the force record.
+     */
+    void initForceProviders(ForceProviders* forceProviders) override;
+
+    //! Subscribe to simulation setup notifications
+    void subscribeToSimulationSetupNotifications(MDModulesNotifiers* notifiers) override;
+    //! Subscribe to pre processing notifications
+    void subscribeToPreProcessingNotifications(MDModulesNotifiers* notifiers) override;
+
+private:
+    /*!
+     * \brief Private implementation opaque pointer.
+     */
+    std::unique_ptr<RestraintMDModuleImpl> impl_;
+};
+
+} // end namespace gmx
+
+#endif // GROMACS_RESTRAINTMDMODULE_H
diff --git a/src/include/gromacs/restraint/restraintmdmodule_impl.h b/src/include/gromacs/restraint/restraintmdmodule_impl.h
new file mode 100644 (file)
index 0000000..bc22320
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GROMACS_RESTRAINTMDMODULE_IMPL_H
+#define GROMACS_RESTRAINTMDMODULE_IMPL_H
+
+/*! \libinternal \file
+ * \brief Implementation details for RestraintMDModule
+ *
+ * \author M. Eric Irrgang <ericirrgang@gmail.com>
+ *
+ * \ingroup module_restraint
+ */
+
+#include <iostream>
+#include <mutex>
+
+#include "gromacs/domdec/domdec_struct.h"
+#include "gromacs/domdec/ga2la.h"
+#include "gromacs/gmxlib/network.h"
+#include "gromacs/math/vec.h"
+#include "gromacs/mdtypes/commrec.h"
+#include "gromacs/mdtypes/iforceprovider.h"
+#include "gromacs/mdtypes/imdmodule.h"
+#include "gromacs/mdtypes/imdoutputprovider.h"
+#include "gromacs/mdtypes/imdpoptionprovider.h"
+#include "gromacs/mdtypes/mdatom.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/restraint/restraintpotential.h"
+#include "gromacs/utility/arrayref.h"
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief Abstraction for a restraint interaction site.
+ *
+ * A restraint may operate on a single atom or some other entity, such as a selection of atoms.
+ * The Restraint implementation is very independent from how coordinates are provided or what they mean.
+ *
+ * First implementation can only represent single atoms in a global context.
+ *
+ * Ultimately, this should be replaced with a more universal facility for acting
+ * on distributed atom data or simple transformations thereof.
+ *
+ * \inlibraryapi
+ * \ingroup module_restraint
+ */
+class Site
+{
+public:
+    /*! \brief Construct from global atom indices
+     *
+     * \param globalIndex Atom index in the global state (as input to the simulation)
+     */
+    explicit Site(int globalIndex) : index_(globalIndex), r_(0, 0, 0) {}
+
+    /*!
+     * \brief Explicitly define copies.
+     *
+     * Implicit definition is not possible because of the mutex member,
+     * and a copy constructor is necessary to use Site in a std::vector. Really, we should make
+     * a copy point to the same implementation object to reuse its cache.
+
+     */
+    Site(const Site& site) : index_(site.index_), r_(site.r_) {}
+
+    /*! \brief Disallow assignment.
+     *
+     * Assignment doesn't make sense because it implies that a site's meaning is fuzzy.
+     * If the definition of a site is changing, just make a new site.
+     * There's nothing to be gained by reusing one or by creating it uninitialized.
+     */
+    Site& operator=(const Site&) = delete;
+
+    /*!
+     * \brief Get the global atom index of an atomic site.
+     *
+     * \return global index provided at construction.
+     *
+     */
+    int index() const { return index_; }
+
+    /*!
+     * \brief Get the position of this site at time t.
+     *
+     * \param cr Communications record.
+     * \param nx Number of locally available atoms (size of local atom data arrays)
+     * \param x Array of locally available atom coordinates.
+     * \param t the current time.
+     * \return position vector.
+     *
+     * \internal
+     * By providing the current time, we can cache results in order to use them once per timestep.
+     * In the long term, we would prefer to also allow client code to preregister interest in a
+     * position at a given time, or issue "futures".
+     */
+    RVec centerOfMass(const t_commrec& cr, size_t nx, ArrayRef<const RVec> x, double gmx_unused t)
+    {
+        // Center of mass to return for the site. Currently the only form of site
+        // implemented is as a global atomic coordinate.
+        gmx::RVec r = { 0, 0, 0 };
+        if (haveDDAtomOrdering(cr)) // Domain decomposition
+        {
+            // Get global-to-local indexing structure
+            auto* crossRef = cr.dd->ga2la;
+            GMX_ASSERT(crossRef, "Domain decomposition must provide global/local cross-reference.");
+            if (const auto* localIndex = crossRef->findHome(index_))
+            {
+                GMX_ASSERT(localIndex,
+                           "Expect not to reach this point if findHome does not find index_.");
+                GMX_ASSERT(*localIndex < static_cast<decltype(*localIndex)>(nx),
+                           "We assume that the local index cannot be larger than the number of "
+                           "atoms.");
+                GMX_ASSERT(*localIndex >= 0, "localIndex is a signed type, but is assumed >0.");
+                // If atom is local, get its location
+                copy_rvec(x[*localIndex], r);
+            }
+            else
+            {
+                // Nothing to contribute on this rank. Leave position == [0,0,0].
+            }
+            // AllReduce across the ranks of the simulation to get the center-of-mass
+            // of the site locally available everywhere. For single-atom sites, this
+            // is trivial: exactly one rank should have a non-zero position.
+            // For future multi-atom selections,
+            // we will receive weighted center-of-mass contributions from
+            // each rank and combine to get the global center of mass.
+            // \todo use generalized "pull group" facility when available.
+            std::array<double, 3> buffer{ { r[0], r[1], r[2] } };
+            // This should be an all-reduce sum, which gmx_sumd appears to be.
+            gmx_sumd(3, buffer.data(), &cr);
+            r[0] = static_cast<real>(buffer[0]);
+            r[1] = static_cast<real>(buffer[1]);
+            r[2] = static_cast<real>(buffer[2]);
+
+        } // end domain decomposition branch
+        else
+        {
+            // No DD so all atoms are local.
+            copy_rvec(x[index_], r);
+            (void)nx;
+        }
+        // Update cache and cache status.
+        copy_rvec(r, r_);
+
+        return r_;
+    }
+
+private:
+    /*!
+     * \brief Global index of the single-atom site.
+     *
+     * \todo This class should be a specialization of a more general Site data source.
+     * \todo use LocalAtomSet
+     */
+    const int index_;
+
+    /*!
+     * \brief Last known value of the center-of-mass.
+     *
+     * Updated with centerOfMass().
+     */
+    RVec r_;
+};
+
+/*! \internal
+ * \brief Provide IForceProvider for RestraintMDModuleImpl
+ *
+ * Adapter class from IForceProvider to IRestraintPotential.
+ * Objects of this type are uniquely owned by instances of RestraintMDModuleImpl. The object will
+ * dispatch calls to IForceProvider->calculateForces() to the functor managed by
+ * RestraintMDModuleImpl. \ingroup module_restraint
+ */
+class RestraintForceProvider final : public gmx::IForceProvider
+{
+public:
+    /*!
+     * \brief Can only be constructed when initialized from a restraint.
+     */
+    RestraintForceProvider() = delete;
+
+    ~RestraintForceProvider() = default;
+
+    /*!
+     * \brief RAII construction with an IRestraintPotential
+     *
+     * Note, this object must outlive the pointer that will be provided to ForceProviders.
+     * \param restraint handle to an object providing restraint potential calculation
+     * \param sites List of atomic site indices
+     */
+    explicit RestraintForceProvider(std::shared_ptr<gmx::IRestraintPotential> restraint,
+                                    const std::vector<int>&                   sites);
+
+    /*!
+     * \brief Implement the IForceProvider interface.
+     *
+     * Update the force array with restraint contribution(s) for local atoms.
+     *
+     * RestraintForceProvider is implemented with the assumption that few
+     * restraints apply to many atoms.
+     * That is, the number of restraints affecting a large number of atoms is small,
+     * though there may be several restraints that apply to few atoms each.
+     * Under this assumption, it is considered computationally inexpensive to iterate
+     * over restraints in an outer loop and iterate over atoms within each restraint.
+     * This would be an invalid assumption if, say, several restraints applied
+     * to an entire membrane or the entire solvent group.
+     *
+     * If the assumption causes performance problems, we can look for a good
+     * way to reduce from several restraints in a single pass or a very
+     * lightweight way to determine whether a given restraint applies to a given atom.
+     * There is also the notion in the pulling code of a limited number of
+     * "pull groups" used by the "pull coordinates".
+     * The right optimization will depend on how the code is being used.
+     *
+     * Call the evaluator(s) for the restraints for the configured sites.
+     * Forces are applied to atoms in the first and last site listed.
+     * Intermediate sites are used as reference coordinates when the relevant
+     * vector between sites is on the order of half a box length or otherwise
+     * ambiguous in the case of periodic boundary conditions.
+     */
+    void calculateForces(const ForceProviderInput& forceProviderInput,
+                         ForceProviderOutput*      forceProviderOutput) override;
+
+private:
+    std::shared_ptr<gmx::IRestraintPotential> restraint_;
+    std::vector<Site>                         sites_;
+};
+
+/*! \internal
+ * \brief IMDModule implementation for RestraintMDModule.
+ *
+ * Provides IMDModule interface.
+ *
+ * \ingroup module_restraint
+ */
+class RestraintMDModuleImpl final
+{
+public:
+    RestraintMDModuleImpl() = delete;
+    /*!
+     * \brief Wrap an object implementing IRestraintPotential
+     *
+     * \param restraint handle to restraint to wrap.
+     * \param sites list of sites for framework to process for restraint force calculator.
+     */
+    RestraintMDModuleImpl(std::shared_ptr<gmx::IRestraintPotential> restraint,
+                          const std::vector<int>&                   sites);
+
+    /*!
+     * \brief Allow moves.
+     *
+     * \{
+     */
+    RestraintMDModuleImpl(RestraintMDModuleImpl&&) noexcept = default;
+    RestraintMDModuleImpl& operator=(RestraintMDModuleImpl&&) noexcept = default;
+    /*! \} */
+
+    ~RestraintMDModuleImpl();
+
+    /*!
+     * \brief Implement IMDModule interface.
+     *
+     * \param forceProviders force module manager in the force record that will call this.
+     *
+     * The calling code must ensure that this object stays alive as long as forceProviders needs
+     * the RestraintForceProvider, since forceProviders can't. Typically that is the duration of a do_md() call.
+     */
+    void initForceProviders(ForceProviders* forceProviders);
+
+    //! handle to RestraintForceProvider implementation
+    std::unique_ptr<RestraintForceProvider> forceProvider_;
+};
+
+} // end namespace gmx
+
+#endif // GROMACS_RESTRAINTMDMODULE_IMPL_H
diff --git a/src/include/gromacs/selection/centerofmass.h b/src/include/gromacs/selection/centerofmass.h
new file mode 100644 (file)
index 0000000..155f942
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2016,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief API for calculation of centers of mass/geometry.
+ *
+ * This header defines a few functions that can be used to calculate
+ * centers of mass/geometry for a group of atoms.
+ * These routines can be used independently of the other parts of the
+ * library, but they are also used internally by the selection engine.
+ * In most cases, it should not be necessary to call these functions
+ * directly.
+ * Instead, one should write an analysis tool such that it gets all
+ * positions through selections.
+ *
+ * The functions in the header can be divided into a few groups based on the
+ * parameters they take. The simplest group of functions calculates the center
+ * of a single group of atoms:
+ *  - gmx_calc_cog(): Calculates the center of geometry (COG) of a given
+ *    group of atoms.
+ *  - gmx_calc_com(): Calculates the center of mass (COM) of a given group
+ *    of atoms.
+ *  - gmx_calc_comg(): Calculates either the COM or COG, based on a
+ *    boolean flag.
+ *
+ * A second set of routines is provided for calculating the centers for groups
+ * that wrap over periodic boundaries (gmx_calc_cog_pbc(), gmx_calc_com_pbc(),
+ * gmx_calc_comg_pbc()). These functions are slower, because they need to
+ * adjust the center iteratively.
+ *
+ * It is also possible to calculate centers for several groups of atoms in
+ * one call. The functions gmx_calc_cog_block(), gmx_calc_com_block() and
+ * gmx_calc_comg_block() take an index group and a partitioning of that index
+ * group (as a \c t_block structure), and calculate the centers for
+ * each group defined by the \c t_block structure separately.
+ *
+ * Finally, there is a function gmx_calc_comg_blocka() that takes both the
+ * index group and the partitioning as a single \c t_blocka structure.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_CENTEROFMASS_H
+#define GMX_SELECTION_CENTEROFMASS_H
+
+#include "gromacs/math/vectypes.h"
+
+struct gmx_mtop_t;
+struct t_block;
+struct t_blocka;
+struct t_pbc;
+
+/*! \brief
+ * Calculate a single center of geometry.
+ *
+ * \param[in]  top    Topology structure (unused, can be NULL).
+ * \param[in]  x      Position vectors of all atoms.
+ * \param[in]  nrefat Number of atoms in the index.
+ * \param[in]  index  Indices of atoms.
+ * \param[out] xout   COG position for the indexed atoms.
+ */
+void gmx_calc_cog(const gmx_mtop_t* top, rvec x[], int nrefat, const int index[], rvec xout);
+/** Calculate a single center of mass. */
+void gmx_calc_com(const gmx_mtop_t* top, rvec x[], int nrefat, const int index[], rvec xout);
+/** Calculate force on a single center of geometry. */
+void gmx_calc_cog_f(const gmx_mtop_t* top, rvec f[], int nrefat, const int index[], rvec fout);
+/*! \brief
+ * Calculate force on a single center of mass.
+ *
+ * \param[in]  top    Topology structure (unused, can be NULL).
+ * \param[in]  f      Forces on all atoms.
+ * \param[in]  nrefat Number of atoms in the index.
+ * \param[in]  index  Indices of atoms.
+ * \param[out] fout   Force on the COM position for the indexed atoms.
+ */
+void gmx_calc_com_f(const gmx_mtop_t* top, rvec f[], int nrefat, const int index[], rvec fout);
+/** Calculate a single center of mass/geometry. */
+void gmx_calc_comg(const gmx_mtop_t* top, rvec x[], int nrefat, const int index[], bool bMass, rvec xout);
+/** Calculate force on a single center of mass/geometry. */
+void gmx_calc_comg_f(const gmx_mtop_t* top, rvec f[], int nrefat, const int index[], bool bMass, rvec fout);
+
+/** Calculate a single center of geometry iteratively, taking PBC into account. */
+void gmx_calc_cog_pbc(const gmx_mtop_t* top, rvec x[], const t_pbc* pbc, int nrefat, const int index[], rvec xout);
+/** Calculate a single center of mass iteratively, taking PBC into account. */
+void gmx_calc_com_pbc(const gmx_mtop_t* top, rvec x[], const t_pbc* pbc, int nrefat, const int index[], rvec xout);
+/** Calculate a single center of mass/geometry iteratively with PBC. */
+void gmx_calc_comg_pbc(const gmx_mtop_t* top,
+                       rvec              x[],
+                       const t_pbc*      pbc,
+                       int               nrefat,
+                       const int         index[],
+                       bool              bMass,
+                       rvec              xout);
+
+/*! \brief
+ * Calculate centers of geometry for a blocked index.
+ *
+ * \param[in]  top   Topology structure (unused, can be NULL).
+ * \param[in]  x     Position vectors of all atoms.
+ * \param[in]  block t_block structure that divides \p index into blocks.
+ * \param[in]  index Indices of atoms.
+ * \param[out] xout  \p block->nr COG positions.
+ */
+void gmx_calc_cog_block(const gmx_mtop_t* top, rvec x[], const t_block* block, const int index[], rvec xout[]);
+/** Calculate centers of mass for a blocked index. */
+void gmx_calc_com_block(const gmx_mtop_t* top, rvec x[], const t_block* block, const int index[], rvec xout[]);
+/** Calculate forces on centers of geometry for a blocked index. */
+void gmx_calc_cog_f_block(const gmx_mtop_t* top, rvec f[], const t_block* block, const int index[], rvec fout[]);
+/*! \brief
+ * Calculate forces on centers of mass for a blocked index.
+ *
+ * \param[in]  top   Topology structure (unused, can be NULL).
+ * \param[in]  f     Forces on all atoms.
+ * \param[in]  block t_block structure that divides \p index into blocks.
+ * \param[in]  index Indices of atoms.
+ * \param[out] fout  \p block->nr Forces on COM positions.
+ */
+void gmx_calc_com_f_block(const gmx_mtop_t* top, rvec f[], const t_block* block, const int index[], rvec fout[]);
+/** Calculate centers of mass/geometry for a blocked index. */
+void gmx_calc_comg_block(const gmx_mtop_t* top,
+                         rvec              x[],
+                         const t_block*    block,
+                         const int         index[],
+                         bool              bMass,
+                         rvec              xout[]);
+/** Calculate forces on centers of mass/geometry for a blocked index. */
+void gmx_calc_comg_f_block(const gmx_mtop_t* top,
+                           rvec              f[],
+                           const t_block*    block,
+                           const int         index[],
+                           bool              bMass,
+                           rvec              fout[]);
+/** Calculate centers of mass/geometry for a set of blocks; */
+void gmx_calc_comg_blocka(const gmx_mtop_t* top, rvec x[], const t_blocka* block, bool bMass, rvec xout[]);
+/** Calculate forces on centers of mass/geometry for a set of blocks; */
+void gmx_calc_comg_f_blocka(const gmx_mtop_t* top, rvec x[], const t_blocka* block, bool bMass, rvec xout[]);
+
+#endif
diff --git a/src/include/gromacs/selection/compiler.h b/src/include/gromacs/selection/compiler.h
new file mode 100644 (file)
index 0000000..97c9fb9
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2013,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares gmx::SelectionCompiler.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_COMPILER_H
+#define GMX_SELECTION_COMPILER_H
+
+namespace gmx
+{
+
+class SelectionCollection;
+
+/*! \internal
+ * \brief
+ * Implements selection compilation.
+ *
+ * This function is used to implement SelectionCollection::compile().
+ * It prepares the selections in a selection collection for evaluation and
+ * performs some optimizations.
+ *
+ * Before compilation, the selection collection should have been initialized
+ * with gmx_ana_selcollection_parse_*().
+ * The compiled selection collection can be passed to
+ * gmx_ana_selcollection_evaluate() to evaluate the selection for a frame.
+ * If an error occurs, \p coll is cleared.
+ *
+ * The covered fraction information in \p coll is initialized to
+ * \ref CFRAC_NONE.
+ *
+ * See \ref page_module_selection_compiler.
+ *
+ * \param[in, out] coll Selection collection to work on.
+ *
+ * \ingroup module_selection
+ */
+void compileSelection(SelectionCollection* coll);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/evaluate.h b/src/include/gromacs/selection/evaluate.h
new file mode 100644 (file)
index 0000000..f78f06b
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2016,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Evaluation functions for sel_evalfunc().
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ * Users should only use SelectionCollection::evaluate() to evaluate
+ * selections.
+ *
+ * The functions defined in this header file are all the possible values
+ * for the gmx::SelectionTreeElement::evaluate field (in addition to NULL).
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_EVALUATE_H
+#define GMX_SELECTION_EVALUATE_H
+
+#include "selelem.h"
+
+struct gmx_ana_index_t;
+struct gmx_mtop_t;
+struct gmx_sel_mempool_t;
+struct t_pbc;
+struct t_trxframe;
+
+/*! \internal \brief
+ * Data structure for passing information required during evaluation.
+ */
+struct gmx_sel_evaluate_t
+{
+    /** Memory pool for intermediate values. */
+    gmx_sel_mempool_t* mp;
+    /** Index group that contains all the atoms. */
+    gmx_ana_index_t* gall;
+    /** Topology information. */
+    const gmx_mtop_t* top;
+    /** Current frame. */
+    t_trxframe* fr;
+    /** PBC data. */
+    t_pbc* pbc;
+};
+
+/*! \name Utility functions
+ */
+/*@{*/
+/** Initializes an evaluation data structure. */
+void _gmx_sel_evaluate_init(gmx_sel_evaluate_t* data,
+                            gmx_sel_mempool_t*  mp,
+                            gmx_ana_index_t*    gall,
+                            const gmx_mtop_t*   top,
+                            t_trxframe*         fr,
+                            t_pbc*              pbc);
+/** Evaluates the children of a general selection element. */
+void _gmx_sel_evaluate_children(gmx_sel_evaluate_t*                     data,
+                                const gmx::SelectionTreeElementPointer& sel,
+                                gmx_ana_index_t*                        g);
+/** Evaluates the children of a \ref SEL_EXPRESSION element. */
+void _gmx_sel_evaluate_method_params(gmx_sel_evaluate_t*                     data,
+                                     const gmx::SelectionTreeElementPointer& sel,
+                                     gmx_ana_index_t*                        g);
+/*@}*/
+
+/*! \name Misc. evaluation functions
+ */
+/*@{*/
+/*! \brief
+ * Evaluates a root selection element.
+ *
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g   Group for which \p sel should be evaluated
+ *   (not used, can be NULL).
+ * \returns   0 on success, a non-zero error code on error.
+ *
+ * Evaluates the first child element in the group defined by \p sel->u.cgrp.
+ * If \p sel->u.cgrp is empty, nothing is done.
+ * The value of \p sel is not touched (root elements do not evaluate to
+ * values).
+ *
+ * This function can be used as gmx::SelectionTreeElement::evaluate for
+ * \ref SEL_ROOT elements.
+ */
+void _gmx_sel_evaluate_root(gmx_sel_evaluate_t*                     data,
+                            const gmx::SelectionTreeElementPointer& sel,
+                            gmx_ana_index_t*                        g);
+/*! \brief
+ * Evaluates a static group selection element.
+ *
+ * \param[in] data Data for the current frame.
+ * \param[in] sel Selection element being evaluated.
+ * \param[in] g   Group for which \p sel should be evaluated.
+ * \returns   0 for success.
+ *
+ * Sets the value of \p sel to the intersection of \p g and \p sel->u.cgrp.
+ *
+ * This function can be used as gmx::SelectionTreeElement::evaluate for
+ * \ref SEL_CONST elements with value type \ref GROUP_VALUE.
+ */
+void _gmx_sel_evaluate_static(gmx_sel_evaluate_t*                     data,
+                              const gmx::SelectionTreeElementPointer& sel,
+                              gmx_ana_index_t*                        g);
+/** Evaluates an arithmetic expression element. */
+void _gmx_sel_evaluate_arithmetic(gmx_sel_evaluate_t*                     data,
+                                  const gmx::SelectionTreeElementPointer& sel,
+                                  gmx_ana_index_t*                        g);
+/*@}*/
+
+/*! \name Subexpression evaluation functions
+ */
+/*@{*/
+/** Evaluates a subexpression when there is only one reference. */
+void _gmx_sel_evaluate_subexpr_simple(gmx_sel_evaluate_t*                     data,
+                                      const gmx::SelectionTreeElementPointer& sel,
+                                      gmx_ana_index_t*                        g);
+/** Evaluates a subexpression when the evaluation group is static. */
+void _gmx_sel_evaluate_subexpr_staticeval(gmx_sel_evaluate_t*                     data,
+                                          const gmx::SelectionTreeElementPointer& sel,
+                                          gmx_ana_index_t*                        g);
+/** Evaluates a subexpression. */
+void _gmx_sel_evaluate_subexpr(gmx_sel_evaluate_t*                     data,
+                               const gmx::SelectionTreeElementPointer& sel,
+                               gmx_ana_index_t*                        g);
+/** Evaluates a subexpression reference when there are no other references. */
+void _gmx_sel_evaluate_subexprref_simple(gmx_sel_evaluate_t*                     data,
+                                         const gmx::SelectionTreeElementPointer& sel,
+                                         gmx_ana_index_t*                        g);
+/** Evaluates a subexpression reference. */
+void _gmx_sel_evaluate_subexprref(gmx_sel_evaluate_t*                     data,
+                                  const gmx::SelectionTreeElementPointer& sel,
+                                  gmx_ana_index_t*                        g);
+/*@}*/
+
+/*! \name Method evaluation functions
+ */
+/*@{*/
+
+/** Evaluates a method expression. */
+void _gmx_sel_evaluate_method(gmx_sel_evaluate_t*                     data,
+                              const gmx::SelectionTreeElementPointer& sel,
+                              gmx_ana_index_t*                        g);
+/** Evaluates a modifier expression. */
+void _gmx_sel_evaluate_modifier(gmx_sel_evaluate_t*                     data,
+                                const gmx::SelectionTreeElementPointer& sel,
+                                gmx_ana_index_t*                        g);
+/*@}*/
+
+/*! \name Boolean evaluation functions
+ */
+/*@{*/
+/** Evaluates a boolean NOT element. */
+void _gmx_sel_evaluate_not(gmx_sel_evaluate_t*                     data,
+                           const gmx::SelectionTreeElementPointer& sel,
+                           gmx_ana_index_t*                        g);
+/** Evaluates a boolean AND element with short-circuiting. */
+void _gmx_sel_evaluate_and(gmx_sel_evaluate_t*                     data,
+                           const gmx::SelectionTreeElementPointer& sel,
+                           gmx_ana_index_t*                        g);
+/** Evaluates a boolean OR element with short-circuiting. */
+void _gmx_sel_evaluate_or(gmx_sel_evaluate_t*                     data,
+                          const gmx::SelectionTreeElementPointer& sel,
+                          gmx_ana_index_t*                        g);
+/*@}*/
+
+#endif
diff --git a/src/include/gromacs/selection/indexutil.h b/src/include/gromacs/selection/indexutil.h
new file mode 100644 (file)
index 0000000..a191cc7
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009-2018, The GROMACS development team.
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief API for handling index files and index groups.
+ *
+ * The API contains functions and data structures for handling index
+ * files more conveniently than as several separate variables.
+ * In addition to basic functions for initializing the data structures and
+ * making copies, functions are provided for performing (most) set operations
+ * on sorted index groups.
+ * There is also a function for partitioning a index group based on
+ * topology information such as residues or molecules.
+ * Finally, there is a set of functions for constructing mappings between
+ * an index group and its subgroups such.
+ * These can be used with dynamic index group in calculations if one
+ * needs to have a unique ID for each possible atom/residue/molecule in the
+ * selection, e.g., for analysis of dynamics or for look-up tables.
+ *
+ * Mostly, these functions are used internally by the selection engine, but
+ * it is necessary to use some of these functions in order to provide external
+ * index groups to a gmx::SelectionCollection.
+ * Some of the checking functions can be useful outside the selection engine to
+ * check the validity of input groups.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_INDEXUTIL_H
+#define GMX_SELECTION_INDEXUTIL_H
+
+#include <cstdio>
+
+#include <string>
+#include <vector>
+
+#include "gromacs/topology/block.h"
+#include "gromacs/utility/arrayref.h"
+
+namespace gmx
+{
+class TextWriter;
+
+/*!\internal \brief Bundle index groups with their names.
+ *
+ * \note This class has to outlive the t_blocka &indexGroup it is given.
+ *       We want to avoid a deep-copy due to the potentially very large data
+ *       stored in the t_blocka.
+ *
+ * \todo update this class, once the two legacy data structures, t_blocka and
+ *       the char ** of group names are refactored.
+ */
+class IndexGroupsAndNames
+{
+public:
+    /*!\brief Construct from index group and group names
+     * \param[in] indexGroup
+     * \param[in] groupNames names of the index groups
+     */
+    IndexGroupsAndNames(const t_blocka& indexGroup, ArrayRef<char const* const> groupNames);
+
+    /*!\brief Return if a group name is contained in the groups.
+     *
+     * String comparison is case insensitive
+     *
+     * \param[in] groupName the group name to be queried
+     * \returns true if index group name is contained
+     */
+    bool containsGroupName(const std::string& groupName) const;
+
+    /*!\brief Return the integer indices of a group.
+     *
+     * If two index groups share a name, return the one found first.
+     *
+     * Indices may be empty.
+     *
+     * \param[in] groupName the name of the group whose indices shall be returned
+     * \returns atom indices of the selected index group
+     * \throws if groupName is not present as index group
+     */
+    std::vector<index> indices(const std::string& groupName) const;
+
+private:
+    const t_blocka&          indexGroup_;
+    std::vector<std::string> groupNames_;
+};
+
+} // namespace gmx
+
+struct gmx_mtop_t;
+
+/** Stores a set of index groups. */
+struct gmx_ana_indexgrps_t;
+
+/*! \brief
+ * Specifies the type of index partition or index mapping in several contexts.
+ *
+ * \see gmx_ana_index_make_block(), gmx_ana_indexmap_init()
+ */
+typedef enum
+{
+    INDEX_UNKNOWN, /**< Unknown index type.*/
+    INDEX_ATOM,    /**< Each atom in a separate block.*/
+    INDEX_RES,     /**< Each residue in a separate block.*/
+    INDEX_MOL,     /**< Each molecule in a separate block.*/
+    INDEX_ALL      /**< All atoms in a single block.*/
+} e_index_t;
+
+/*! \brief
+ * Stores a single index group.
+ */
+struct gmx_ana_index_t
+{
+    /** Number of atoms. */
+    int isize;
+    /** List of atoms. */
+    int* index;
+    /** Number of items allocated for \p index. */
+    int nalloc_index;
+};
+
+/*! \brief
+ * Data structure for calculating index group mappings.
+ */
+struct gmx_ana_indexmap_t
+{
+    /** Type of the mapping. */
+    e_index_t type;
+    /*! \brief
+     * Current reference IDs.
+     *
+     * This array provides a mapping from the current index group (last given
+     * to gmx_ana_indexmap_update()) to the blocks in \p b, i.e., the
+     * original index group used in gmx_ana_indexmap_init().
+     * The mapping is zero-based.
+     * If \p bMaskOnly is provided to gmx_ana_indexmap_update(), the indices
+     * for blocks not present in the current group are set to -1, otherwise
+     * they are removed completely and the \p nr field updated.
+     */
+    int* refid;
+    /*! \brief
+     * Current mapped IDs.
+     *
+     * This array provides IDs for the current index group.  Instead of a
+     * zero-based mapping that \p refid provides, the values from the \p orgid
+     * array are used, thus allowing the mapping to be customized.
+     * In other words, `mapid[i] = orgid[refid[i]]`.
+     * If \p bMaskOnly is provided to gmx_ana_indexmap_update(), this array
+     * equals \p orgid.
+     */
+    int* mapid;
+    /*! \brief
+     * Mapped block structure.
+     *
+     * A block structure that corresponds to the current index group.
+     * \c mapb.nra and \c mapb.a correspond to the last mapped index group.
+     */
+    t_blocka mapb;
+
+    /*! \brief
+     * Customizable ID numbers for the original blocks.
+     *
+     * This array has \p b.nr elements, each defining an original ID number for
+     * a block in \p b (i.e., in the original group passed to
+     * gmx_ana_indexmap_init()).
+     * These are initialized in gmx_ana_indexmap_init() based on the type:
+     *  - \ref INDEX_ATOM : the atom indices
+     *  - \ref INDEX_RES :  the residue indices
+     *  - \ref INDEX_MOL :  the molecule indices
+     *
+     * All the above numbers are zero-based.
+     * After gmx_ana_indexmap_init(), the caller is free to change these values
+     * if the above are not appropriate.
+     * The mapped values can be read through \p mapid.
+     */
+    int* orgid;
+
+    /*! \brief
+     * Block data that defines the mapping (internal use only).
+     *
+     * The data is initialized by gmx_ana_indexmap_init() and is not changed
+     * after that.
+     * Hence, it cannot be directly applied to the index group passed to
+     * gmx_ana_indexmap_update() unless \p bMaskOnly was specified or the
+     * index group is identical to the one provided to gmx_ana_indexmap_init().
+     */
+    t_blocka b;
+    /*! \brief
+     * true if the current reference IDs are for the whole group (internal use only).
+     *
+     * This is used internally to optimize the evaluation such that
+     * gmx_ana_indexmap_update() does not take any time if the group is
+     * actually static.
+     */
+    bool bStatic;
+};
+
+
+/*! \name Functions for handling gmx_ana_indexgrps_t
+ */
+/*@{*/
+/** Reads index groups from a file or constructs them from topology. */
+void gmx_ana_indexgrps_init(gmx_ana_indexgrps_t** g, gmx_mtop_t* top, const char* fnm);
+/** Frees memory allocated for index groups. */
+void gmx_ana_indexgrps_free(gmx_ana_indexgrps_t* g);
+/** Returns true if the index group structure is emtpy. */
+bool gmx_ana_indexgrps_is_empty(gmx_ana_indexgrps_t* g);
+
+/** Returns a pointer to an index group. */
+gmx_ana_index_t* gmx_ana_indexgrps_get_grp(gmx_ana_indexgrps_t* g, int n);
+/** Extracts a single index group. */
+bool gmx_ana_indexgrps_extract(gmx_ana_index_t* dest, std::string* destName, gmx_ana_indexgrps_t* src, int n);
+/** Finds and extracts a single index group by name. */
+bool gmx_ana_indexgrps_find(gmx_ana_index_t*     dest,
+                            std::string*         destName,
+                            gmx_ana_indexgrps_t* src,
+                            const char*          name);
+
+/** Writes out a list of index groups. */
+void gmx_ana_indexgrps_print(gmx::TextWriter* writer, gmx_ana_indexgrps_t* g, int maxn);
+/*@}*/
+
+/*! \name Functions for handling gmx_ana_index_t
+ */
+/*@{*/
+/** Reserves memory to store an index group of size \p isize. */
+void gmx_ana_index_reserve(gmx_ana_index_t* g, int isize);
+/** Frees any memory not necessary to hold the current contents. */
+void gmx_ana_index_squeeze(gmx_ana_index_t* g);
+/** Initializes an empty index group. */
+void gmx_ana_index_clear(gmx_ana_index_t* g);
+/** Constructs a \c gmx_ana_index_t from given values. */
+void gmx_ana_index_set(gmx_ana_index_t* g, int isize, int* index, int nalloc);
+/** Creates a simple index group from the first to the \p natoms'th atom. */
+void gmx_ana_index_init_simple(gmx_ana_index_t* g, int natoms);
+/** Frees memory allocated for an index group. */
+void gmx_ana_index_deinit(gmx_ana_index_t* g);
+/** Copies a \c gmx_ana_index_t. */
+void gmx_ana_index_copy(gmx_ana_index_t* dest, gmx_ana_index_t* src, bool bAlloc);
+
+/** Writes out the contents of a index group. */
+void gmx_ana_index_dump(gmx::TextWriter* writer, gmx_ana_index_t* g, int maxn);
+
+/*! \brief
+ * Returns maximum atom index that appears in an index group.
+ *
+ * \param[in]  g      Index group to query.
+ * \returns    Largest atom index that appears in \p g, or zero if \p g is empty.
+ */
+int gmx_ana_index_get_max_index(gmx_ana_index_t* g);
+/** Checks whether an index group is sorted. */
+bool gmx_ana_index_check_sorted(gmx_ana_index_t* g);
+/*! \brief
+ * Checks whether an index group has atoms from a defined range.
+ *
+ * \param[in]  g      Index group to check.
+ * \param[in]  natoms Largest atom number allowed.
+ * \returns    true if all atoms in the index group are in the
+ *     range 0 to \p natoms (i.e., no atoms over \p natoms are referenced).
+ */
+bool gmx_ana_index_check_range(gmx_ana_index_t* g, int natoms);
+/*@}*/
+
+/*! \name Functions for set operations on gmx_ana_index_t
+ */
+/*@{*/
+/** Sorts the indices within an index group. */
+void gmx_ana_index_sort(gmx_ana_index_t* g);
+/*! \brief
+ * Removes duplicates from a sorted index group.
+ *
+ * \param[in,out] g  Index group to be processed.
+ */
+void gmx_ana_index_remove_duplicates(gmx_ana_index_t* g);
+/** Checks whether two index groups are equal. */
+bool gmx_ana_index_equals(gmx_ana_index_t* a, gmx_ana_index_t* b);
+/** Checks whether a sorted index group contains another sorted index group. */
+bool gmx_ana_index_contains(gmx_ana_index_t* a, gmx_ana_index_t* b);
+
+/** Calculates the intersection between two sorted index groups. */
+void gmx_ana_index_intersection(gmx_ana_index_t* dest, gmx_ana_index_t* a, gmx_ana_index_t* b);
+/** Calculates the set difference between two sorted index groups. */
+void gmx_ana_index_difference(gmx_ana_index_t* dest, gmx_ana_index_t* a, gmx_ana_index_t* b);
+/** Calculates the size of the difference between two sorted index groups. */
+int gmx_ana_index_difference_size(gmx_ana_index_t* a, gmx_ana_index_t* b);
+/** Calculates the union of two sorted index groups. */
+void gmx_ana_index_union(gmx_ana_index_t* dest, gmx_ana_index_t* a, gmx_ana_index_t* b);
+/*! \brief
+ * Calculates the union of two index groups, where the second group may not be sorted.
+ *
+ * \param[out] dest Output index group (the union of \p a and \p b).
+ * \param[in]  a    First index group (must be sorted).
+ * \param[in]  b    Second index group.
+ *
+ * \p a and \p b can have common items.
+ * \p dest can equal \p a or \p b.
+ */
+void gmx_ana_index_union_unsorted(gmx_ana_index_t* dest, gmx_ana_index_t* a, gmx_ana_index_t* b);
+/** Merges two distinct sorted index groups. */
+void gmx_ana_index_merge(gmx_ana_index_t* dest, gmx_ana_index_t* a, gmx_ana_index_t* b);
+/** Calculates the intersection and the difference in one call. */
+void gmx_ana_index_partition(gmx_ana_index_t* dest1,
+                             gmx_ana_index_t* dest2,
+                             gmx_ana_index_t* src,
+                             gmx_ana_index_t* g);
+/*@}*/
+
+/*! \name Functions for handling gmx_ana_indexmap_t and related things
+ */
+/*@{*/
+/** Partition a group based on topology information. */
+void gmx_ana_index_make_block(t_blocka* t, const gmx_mtop_t* top, gmx_ana_index_t* g, e_index_t type, bool bComplete);
+/** Checks whether a group consists of full blocks. */
+bool gmx_ana_index_has_full_blocks(const gmx_ana_index_t* g, const gmx::RangePartitioning* b);
+/** Checks whether a group consists of full blocks. */
+bool gmx_ana_index_has_full_ablocks(gmx_ana_index_t* g, t_blocka* b);
+/** Checks whether a group consists of full residues/molecules. */
+bool gmx_ana_index_has_complete_elems(gmx_ana_index_t* g, e_index_t type, const gmx_mtop_t* top);
+
+/** Initializes an empty index group mapping. */
+void gmx_ana_indexmap_clear(gmx_ana_indexmap_t* m);
+/** Reserves memory for an index group mapping. */
+void gmx_ana_indexmap_reserve(gmx_ana_indexmap_t* m, int nr, int isize);
+/** Initializes an index group mapping. */
+void gmx_ana_indexmap_init(gmx_ana_indexmap_t* m, gmx_ana_index_t* g, const gmx_mtop_t* top, e_index_t type);
+/*! \brief
+ * Initializes `orgid` entries based on topology grouping.
+ *
+ * \param[in,out] m     Mapping structure to use/initialize.
+ * \param[in]     top   Topology structure
+ *     (can be NULL if not required for \p type).
+ * \param[in]     type  Type of groups to use.
+ * \returns  The number of groups of type \p type that were present in \p m.
+ * \throws   InconsistentInputError if the blocks in \p m do not have a unique
+ *     group (e.g., contain atoms from multiple residues with `type == INDEX_RES`).
+ *
+ * By default, the gmx_ana_indexmap_t::orgid fields are initialized to
+ * atom/residue/molecule indices from the topology (see documentation for the
+ * struct for more details).
+ * This function can be used to set the field to a zero-based group index
+ * instead.  The first block will always get `orgid[0] = 0`, and all following
+ * blocks that belong to the same residue/molecule (\p type) will get the same
+ * index.  Each time a block does not belong to the same group, it will get the
+ * next available number.
+ * If `type == INDEX_ATOM`, the `orgid` field is initialized as 0, 1, ...,
+ * independent of whether the blocks are single atoms or not.
+ *
+ * Strong exception safety guarantee.
+ */
+int gmx_ana_indexmap_init_orgid_group(gmx_ana_indexmap_t* m, const gmx_mtop_t* top, e_index_t type);
+/** Sets an index group mapping to be static. */
+void gmx_ana_indexmap_set_static(gmx_ana_indexmap_t* m, t_blocka* b);
+/** Frees memory allocated for index group mapping. */
+void gmx_ana_indexmap_deinit(gmx_ana_indexmap_t* m);
+/** Makes a deep copy of an index group mapping. */
+void gmx_ana_indexmap_copy(gmx_ana_indexmap_t* dest, gmx_ana_indexmap_t* src, bool bFirst);
+/** Updates an index group mapping. */
+void gmx_ana_indexmap_update(gmx_ana_indexmap_t* m, gmx_ana_index_t* g, bool bMaskOnly);
+/*@}*/
+
+#endif
diff --git a/src/include/gromacs/selection/keywords.h b/src/include/gromacs/selection/keywords.h
new file mode 100644 (file)
index 0000000..c6060d0
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2012,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Definitions of generic keyword evaluation structures.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_KEYWORDS_H
+#define GMX_SELECTION_KEYWORDS_H
+
+#include "parsetree.h"
+#include "selelem.h"
+
+struct gmx_ana_selmethod_t;
+
+/** Selection method data for comparison expression evaluation. */
+extern struct gmx_ana_selmethod_t sm_compare;
+
+/** Selection method data for integer keyword evaluation. */
+extern struct gmx_ana_selmethod_t sm_keyword_int;
+/** Selection method data for real keyword evaluation. */
+extern struct gmx_ana_selmethod_t sm_keyword_real;
+/** Selection method data for string keyword evaluation. */
+extern struct gmx_ana_selmethod_t sm_keyword_str;
+/** Selection method data for position keyword evaluation. */
+extern struct gmx_ana_selmethod_t sm_keyword_pos;
+
+/** Prints information about a comparison expression. */
+void _gmx_selelem_print_compare_info(FILE* fp, void* data);
+
+/*! \brief
+ * Returns whether the selection element is a default position keyword.
+ *
+ * \param[in] sel   Selection element to query.
+ * \returns   ``true`` if ``sel`` represents a position keyword evaluation that
+ *     uses the default (implicit) position keyword.
+ *
+ * This method only works before the selection has been compiled.
+ */
+bool _gmx_selelem_is_default_kwpos(const gmx::SelectionTreeElement& sel);
+/** Sets the position type for position keyword evaluation. */
+void _gmx_selelem_set_kwpos_type(gmx::SelectionTreeElement* sel, const char* type);
+/** Sets the flags for position keyword evaluation. */
+void _gmx_selelem_set_kwpos_flags(gmx::SelectionTreeElement* sel, int flags);
+
+/** Sets the string match type for string keyword evaluation. */
+void _gmx_selelem_set_kwstr_match_type(const gmx::SelectionTreeElementPointer& sel,
+                                       gmx::SelectionStringMatchType           matchType);
+
+/** Does custom processing for parameters of the \c same selection method. */
+void _gmx_selelem_custom_init_same(struct gmx_ana_selmethod_t**                    method,
+                                   const gmx::SelectionParserParameterListPointer& params,
+                                   void*                                           scanner);
+
+/*! \brief
+ * Initializes a selection element for evaluating a keyword in a given group.
+ *
+ * \param[in]   method  Keyword selection method to evaluate.
+ * \param[in]   child   The group/positions to evaluate \p method in.
+ * \param[in]   scanner Scanner data structure.
+ * \returns     Pointer to the created selection element.
+ *
+ * Creates a \ref SEL_EXPRESSION selection element that evaluates the keyword
+ * method given by \p method in the group/positions given by \p child.
+ *
+ * \p child should be a selection tree that evaluates to \ref GROUP_VALUE or
+ * \ref POS_VALUE.
+ */
+gmx::SelectionTreeElementPointer _gmx_sel_init_keyword_evaluator(struct gmx_ana_selmethod_t* method,
+                                                                 const gmx::SelectionTreeElementPointer& child,
+                                                                 void* scanner);
+
+#endif
diff --git a/src/include/gromacs/selection/mempool.h b/src/include/gromacs/selection/mempool.h
new file mode 100644 (file)
index 0000000..258528e
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2014,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Declarations for memory pooling functions.
+ *
+ * \todo
+ * Document these functions.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_MEMPOOL_H
+#define GMX_SELECTION_MEMPOOL_H
+
+#include <cstddef>
+
+struct gmx_ana_index_t;
+
+/** Opaque struct for memory pooling. */
+typedef struct gmx_sel_mempool_t gmx_sel_mempool_t;
+
+/** Create an empty memory pool. */
+gmx_sel_mempool_t* _gmx_sel_mempool_create();
+/** Destroy a memory pool. */
+void _gmx_sel_mempool_destroy(gmx_sel_mempool_t* mp);
+
+/** Allocate memory from a memory pool. */
+void* _gmx_sel_mempool_alloc(gmx_sel_mempool_t* mp, size_t size);
+/** Release memory allocated from a memory pool. */
+void _gmx_sel_mempool_free(gmx_sel_mempool_t* mp, void* ptr);
+/** Set the size of a memory pool. */
+void _gmx_sel_mempool_reserve(gmx_sel_mempool_t* mp, size_t size);
+
+/** Convenience function for allocating an index group from a memory pool. */
+void _gmx_sel_mempool_alloc_group(gmx_sel_mempool_t* mp, struct gmx_ana_index_t* g, int isize);
+/** Convenience function for freeing an index group from a memory pool. */
+void _gmx_sel_mempool_free_group(gmx_sel_mempool_t* mp, struct gmx_ana_index_t* g);
+
+#endif
diff --git a/src/include/gromacs/selection/nbsearch.h b/src/include/gromacs/selection/nbsearch.h
new file mode 100644 (file)
index 0000000..9fb86b6
--- /dev/null
@@ -0,0 +1,610 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009-2018, The GROMACS development team.
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief API for neighborhood searching for analysis.
+ *
+ * The main part of the API is the class gmx::AnalysisNeighborhood.
+ * See \ref page_analysisnbsearch for an overview.
+ *
+ * The classes within this file can be used independently of the other parts
+ * of the selection module.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_NBSEARCH_H
+#define GMX_SELECTION_NBSEARCH_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/math/vec.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+struct t_pbc;
+
+namespace gmx
+{
+template<typename>
+class ListOfLists;
+
+namespace internal
+{
+class AnalysisNeighborhoodSearchImpl;
+class AnalysisNeighborhoodPairSearchImpl;
+}; // namespace internal
+
+class AnalysisNeighborhoodSearch;
+class AnalysisNeighborhoodPairSearch;
+
+/*! \brief
+ * Input positions for neighborhood searching.
+ *
+ * This class supports uniformly specifying sets of positions for various
+ * methods in the analysis neighborhood searching classes
+ * (AnalysisNeighborhood and AnalysisNeighborhoodSearch).
+ *
+ * Note that copies are not made: only a reference to the positions passed to
+ * the constructors are kept.  The caller is responsible to ensure that those
+ * positions remain in scope as long as the neighborhood search object requires
+ * access to them.
+ *
+ * Also note that in addition to constructors here, Selection and
+ * SelectionPosition provide conversions operators to this type.  It is done
+ * this way to not introduce a cyclic dependency between the selection code and
+ * the neighborhood search code, which in turn allows splitting this search
+ * code into a separate lower-level module if desired at some point.
+ *
+ * Methods in this class do not throw.
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class AnalysisNeighborhoodPositions
+{
+public:
+    /*! \brief
+     * Initializes positions from a single position vector.
+     *
+     * For positions initialized this way, AnalysisNeighborhoodPair always
+     * returns zero in the corresponding index.
+     *
+     * This constructor is not explicit to allow directly passing an rvec
+     * to methods that accept positions.
+     */
+    AnalysisNeighborhoodPositions(const rvec& x) :
+        count_(1), index_(-1), x_(&x), exclusionIds_(nullptr), indices_(nullptr)
+    {
+    }
+    /*! \brief
+     * Initializes positions from an array of position vectors.
+     */
+    AnalysisNeighborhoodPositions(const rvec x[], int count) :
+        count_(count), index_(-1), x_(x), exclusionIds_(nullptr), indices_(nullptr)
+    {
+    }
+    /*! \brief
+     * Initializes positions from a vector of position vectors.
+     */
+    AnalysisNeighborhoodPositions(const std::vector<RVec>& x) :
+        count_(ssize(x)), index_(-1), x_(as_rvec_array(x.data())), exclusionIds_(nullptr), indices_(nullptr)
+    {
+    }
+
+    /*! \brief
+     * Sets indices to use for mapping exclusions to these positions.
+     *
+     * The exclusion IDs can always be set, but they are ignored unless
+     * actual exclusions have been set with
+     * AnalysisNeighborhood::setTopologyExclusions().
+     */
+    AnalysisNeighborhoodPositions& exclusionIds(ArrayRef<const int> ids)
+    {
+        GMX_ASSERT(ssize(ids) == count_, "Exclusion id array should match the number of positions");
+        exclusionIds_ = ids.data();
+        return *this;
+    }
+    /*! \brief
+     * Sets indices that select a subset of all positions from the array.
+     *
+     * If called, selected positions from the array of positions passed to
+     * the constructor is used instead of the whole array.
+     * All returned indices from AnalysisNeighborhoodPair objects are
+     * indices to the \p indices array passed here.
+     */
+    AnalysisNeighborhoodPositions& indexed(ArrayRef<const int> indices)
+    {
+        count_   = ssize(indices);
+        indices_ = indices.data();
+        return *this;
+    }
+
+    /*! \brief
+     * Selects a single position to use from an array.
+     *
+     * If called, a single position from the array of positions passed to
+     * the constructor is used instead of the whole array.
+     * In contrast to the AnalysisNeighborhoodPositions(const rvec &)
+     * constructor, AnalysisNeighborhoodPair objects return \p index
+     * instead of zero.
+     *
+     * If used together with indexed(), \p index references the index array
+     * passed to indexed() instead of the position array.
+     */
+    AnalysisNeighborhoodPositions& selectSingleFromArray(int index)
+    {
+        GMX_ASSERT(index >= 0 && index < count_, "Invalid position index");
+        index_ = index;
+        return *this;
+    }
+
+private:
+    int         count_;
+    int         index_;
+    const rvec* x_;
+    const int*  exclusionIds_;
+    const int*  indices_;
+
+    //! To access the positions for initialization.
+    friend class internal::AnalysisNeighborhoodSearchImpl;
+    //! To access the positions for initialization.
+    friend class internal::AnalysisNeighborhoodPairSearchImpl;
+};
+
+/*! \brief
+ * Neighborhood searching for analysis tools.
+ *
+ * See \ref page_analysisnbsearch for an overview.
+ *
+ * To use the search, create an object of this type, call setCutoff() to
+ * initialize it, and then repeatedly call initSearch() to start a search with
+ * different sets of reference positions.  For each set of reference positions,
+ * use methods in the returned AnalysisNeighborhoodSearch to find the reference
+ * positions that are within the given cutoff from a provided position.
+ *
+ * initSearch() is thread-safe and can be called from multiple threads.  Each
+ * call returns a different instance of the search object that can be used
+ * independently of the others.  The returned AnalysisNeighborhoodSearch
+ * objects are also thread-safe, and can be used concurrently from multiple
+ * threads.  It is also possible to create multiple concurrent searches within
+ * a single thread.
+ *
+ * \todo
+ * Generalize the exclusion machinery to make it easier to use for other cases
+ * than atom-atom exclusions from the topology.
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class AnalysisNeighborhood
+{
+public:
+    //! Searching algorithm to use.
+    enum SearchMode
+    {
+        //! Select algorithm based on heuristic efficiency considerations.
+        eSearchMode_Automatic,
+        //! Use a simple loop over all pairs.
+        eSearchMode_Simple,
+        //! Use grid-based searching whenever possible.
+        eSearchMode_Grid
+    };
+
+    //! Creates an uninitialized neighborhood search.
+    AnalysisNeighborhood();
+    ~AnalysisNeighborhood();
+
+    /*! \brief
+     * Sets cutoff distance for the neighborhood searching.
+     *
+     * \param[in]  cutoff Cutoff distance for the search
+     *   (<=0 stands for no cutoff).
+     *
+     * Currently, can only be called before the first call to initSearch().
+     * If this method is not called, no cutoff is used in the searches.
+     *
+     * Does not throw.
+     */
+    void setCutoff(real cutoff);
+    /*! \brief
+     * Sets the search to only happen in the XY plane.
+     *
+     * Z component of the coordinates is not used in the searching,
+     * and returned distances are computed in the XY plane.
+     * Only boxes with the third box vector parallel to the Z axis are
+     * currently implemented.
+     *
+     * Does not throw.
+     */
+    void setXYMode(bool bXY);
+    /*! \brief
+     * Sets atom exclusions from a topology.
+     *
+     * The \p excls structure specifies the exclusions from test positions
+     * to reference positions, i.e., a block starting at `excls->index[i]`
+     * specifies the exclusions for test position `i`, and the indices in
+     * `excls->a` are indices of the reference positions.  If `excls->nr`
+     * is smaller than a test position id, then such test positions do not
+     * have any exclusions.
+     * It is assumed that the indices within a block of indices in
+     * `excls->a` is ascending.
+     *
+     * Does not throw.
+     *
+     * \see AnalysisNeighborhoodPositions::exclusionIds()
+     */
+    void setTopologyExclusions(const ListOfLists<int>* excls);
+    /*! \brief
+     * Sets the algorithm to use for searching.
+     *
+     * \param[in] mode  Search mode to use.
+     *
+     * Note that if \p mode is \ref eSearchMode_Grid, it is still only a
+     * suggestion: grid-based searching may not be possible with the
+     * provided input, in which case a simple search is still used.
+     * This is mainly useful for testing purposes to force a mode.
+     *
+     * Does not throw.
+     */
+    void setMode(SearchMode mode);
+    //! Returns the currently active search mode.
+    SearchMode mode() const;
+
+    /*! \brief
+     * Initializes neighborhood search for a set of positions.
+     *
+     * \param[in] pbc        PBC information for the frame.
+     * \param[in] positions  Set of reference positions to use.
+     * \returns   Search object that can be used to find positions from
+     *      \p x within the given cutoff.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * Currently, the input positions cannot use
+     * AnalysisNeighborhoodPositions::selectSingleFromArray().
+     */
+    AnalysisNeighborhoodSearch initSearch(const t_pbc* pbc, const AnalysisNeighborhoodPositions& positions);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \brief
+ * Value type to represent a pair of positions found in neighborhood searching.
+ *
+ * Methods in this class do not throw.
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class AnalysisNeighborhoodPair
+{
+public:
+    //! Initializes an invalid pair.
+    AnalysisNeighborhoodPair() : refIndex_(-1), testIndex_(0), distance2_(0.0), dx_() {}
+    //! Initializes a pair object with the given data.
+    AnalysisNeighborhoodPair(int refIndex, int testIndex, real distance2, const rvec dx) :
+        refIndex_(refIndex), testIndex_(testIndex), distance2_(distance2), dx_()
+    {
+        copy_rvec(dx, dx_);
+    }
+
+    /*! \brief
+     * Whether this pair is valid.
+     *
+     * If isValid() returns false, other methods should not be called.
+     */
+    bool isValid() const { return refIndex_ >= 0; }
+
+    /*! \brief
+     * Returns the index of the reference position in the pair.
+     *
+     * This index is always the index into the position array provided to
+     * AnalysisNeighborhood::initSearch().
+     */
+    int refIndex() const
+    {
+        GMX_ASSERT(isValid(), "Accessing invalid object");
+        return refIndex_;
+    }
+    /*! \brief
+     * Returns the index of the test position in the pair.
+     *
+     * The contents of this index depends on the context (method call) that
+     * produces the pair.
+     * If there was no array in the call, this index is zero.
+     */
+    int testIndex() const
+    {
+        GMX_ASSERT(isValid(), "Accessing invalid object");
+        return testIndex_;
+    }
+    /*! \brief
+     * Returns the squared distance between the pair of positions.
+     */
+    real distance2() const
+    {
+        GMX_ASSERT(isValid(), "Accessing invalid object");
+        return distance2_;
+    }
+    /*! \brief
+     * Returns the shortest vector between the pair of positions.
+     *
+     * The vector is from the test position to the reference position.
+     */
+    const rvec& dx() const
+    {
+        GMX_ASSERT(isValid(), "Accessing invalid object");
+        return dx_;
+    }
+
+private:
+    int  refIndex_;
+    int  testIndex_;
+    real distance2_;
+    rvec dx_;
+};
+
+/*! \brief
+ * Initialized neighborhood search with a fixed set of reference positions.
+ *
+ * An instance of this class is obtained through
+ * AnalysisNeighborhood::initSearch(), and can be used to do multiple searches
+ * against the provided set of reference positions.
+ * It is possible to create concurrent pair searches (including from different
+ * threads), as well as call other methods in this class while a pair search is
+ * in progress.
+ *
+ * This class works like a pointer: copies of it point to the same search.
+ * In general, avoid creating copies, and only use the copy/assignment support
+ * for moving the variable around.  With C++11, this class would best be
+ * movable.
+ *
+ * Methods in this class do not throw unless otherwise indicated.
+ *
+ * \todo
+ * Make it such that reset() is not necessary to call in code that repeatedly
+ * assigns the result of AnalysisNeighborhood::initSearch() to the same
+ * variable (see sm_distance.cpp).
+ *
+ * \todo
+ * Consider removing minimumDistance(), as nearestPoint() already returns the
+ * distance.
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class AnalysisNeighborhoodSearch
+{
+public:
+    /*! \brief
+     * Internal short-hand type for a pointer to the implementation class.
+     *
+     * shared_ptr is used here to automatically keep a reference count to
+     * track whether an implementation class is still used outside the
+     * AnalysisNeighborhood object.  Ownership currently always stays with
+     * AnalysisNeighborhood; it always keeps one instance of the pointer.
+     */
+    typedef std::shared_ptr<internal::AnalysisNeighborhoodSearchImpl> ImplPointer;
+
+    /*! \brief
+     * Initializes an invalid search.
+     *
+     * Such an object cannot be used for searching.  It needs to be
+     * assigned a value from AnalysisNeighborhood::initSearch() before it
+     * can be used.  Provided to allow declaring a variable to hold the
+     * search before calling AnalysisNeighborhood::initSearch().
+     */
+    AnalysisNeighborhoodSearch();
+    /*! \brief
+     * Internally initialize the search.
+     *
+     * Used to implement AnalysisNeighborhood::initSearch().
+     * Cannot be called from user code.
+     */
+    explicit AnalysisNeighborhoodSearch(const ImplPointer& impl);
+
+    /*! \brief
+     * Clears this search.
+     *
+     * Equivalent to \c "*this = AnalysisNeighborhoodSearch();".
+     * Currently, this is necessary to avoid unnecessary memory allocation
+     * if the previous search variable is still in scope when you want to
+     * call AnalysisNeighborhood::initSearch() again.
+     */
+    void reset();
+
+    /*! \brief
+     * Returns the searching algorithm that this search is using.
+     *
+     * The return value is never AnalysisNeighborhood::eSearchMode_Automatic.
+     */
+    AnalysisNeighborhood::SearchMode mode() const;
+
+    /*! \brief
+     * Checks whether a point is within a neighborhood.
+     *
+     * \param[in] positions  Set of test positions to use.
+     * \returns   true if any of the test positions is within the cutoff of
+     *     any reference position.
+     */
+    bool isWithin(const AnalysisNeighborhoodPositions& positions) const;
+    /*! \brief
+     * Calculates the minimum distance from the reference points.
+     *
+     * \param[in] positions  Set of test positions to use.
+     * \returns   The distance to the nearest reference position, or the
+     *     cutoff value if there are no reference positions within the
+     *     cutoff.
+     */
+    real minimumDistance(const AnalysisNeighborhoodPositions& positions) const;
+    /*! \brief
+     * Finds the closest reference point.
+     *
+     * \param[in] positions  Set of test positions to use.
+     * \returns   The reference index identifies the reference position
+     *     that is closest to the test positions.
+     *     The test index identifies the test position that is closest to
+     *     the provided test position.  The returned pair is invalid if
+     *     no reference position is within the cutoff.
+     */
+    AnalysisNeighborhoodPair nearestPoint(const AnalysisNeighborhoodPositions& positions) const;
+
+    /*! \brief
+     * Starts a search to find all reference position pairs within a cutoff.
+     *
+     * \returns   Initialized search object to loop through all reference
+     *     position pairs within the configured cutoff.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * This works as if the reference positions were passed to
+     * startPairSearch(), except that it only returns each pair once,
+     * instead of returning both i-j and j-i pairs, as startPairSearch()
+     * does.  i-i pairs are not returned.  Note that the order of ref/test
+     * indices in the returned pairs is not predictable.  That is, one of
+     * i-j or j-i is always returned, but there is no control which one.
+     */
+    AnalysisNeighborhoodPairSearch startSelfPairSearch() const;
+
+    /*! \brief
+     * Starts a search to find reference positions within a cutoff.
+     *
+     * \param[in] positions  Set of test positions to use.
+     * \returns   Initialized search object to loop through all reference
+     *     positions within the configured cutoff.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * If you want to pass the same positions here as you used for the
+     * reference positions, consider using startSelfPairSearch().
+     * It can be up to 50% faster.
+     */
+    AnalysisNeighborhoodPairSearch startPairSearch(const AnalysisNeighborhoodPositions& positions) const;
+
+private:
+    typedef internal::AnalysisNeighborhoodSearchImpl Impl;
+
+    ImplPointer impl_;
+};
+
+/*! \brief
+ * Initialized neighborhood pair search with a fixed set of positions.
+ *
+ * This class is used to loop through pairs of neighbors within the cutoff
+ * provided to AnalysisNeighborhood.  The following code demonstrates its use:
+ * \code
+   gmx::AnalysisNeighborhood       nb;
+   nb.setCutoff(cutoff);
+   gmx::AnalysisNeighborhoodPositions refPos(xref, nref);
+   gmx::AnalysisNeighborhoodSearch search = nb.initSearch(pbc, refPos);
+   gmx::AnalysisNeighborhoodPairSearch pairSearch = search.startPairSearch(selection);
+   gmx::AnalysisNeighborhoodPair pair;
+   while (pairSearch.findNextPair(&pair))
+   {
+       // <do something for each found pair the information in pair>
+   }
+ * \endcode
+ *
+ * It is not possible to use a single search object from multiple threads
+ * concurrently.
+ *
+ * This class works like a pointer: copies of it point to the same search.
+ * In general, avoid creating copies, and only use the copy/assignment support
+ * for moving the variable around.  With C++11, this class would best be
+ * movable.
+ *
+ * Methods in this class do not throw.
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class AnalysisNeighborhoodPairSearch
+{
+public:
+    /*! \brief
+     * Internal short-hand type for a pointer to the implementation class.
+     *
+     * See AnalysisNeighborhoodSearch::ImplPointer for rationale of using
+     * shared_ptr and ownership semantics.
+     */
+    typedef std::shared_ptr<internal::AnalysisNeighborhoodPairSearchImpl> ImplPointer;
+
+    /*! \brief
+     * Internally initialize the search.
+     *
+     * Used to implement AnalysisNeighborhoodSearch::startPairSearch().
+     * Cannot be called from user code.
+     */
+    explicit AnalysisNeighborhoodPairSearch(const ImplPointer& impl);
+
+    /*! \brief
+     * Finds the next pair within the cutoff.
+     *
+     * \param[out] pair  Information about the found pair.
+     * \returns    false if there were no more pairs.
+     *
+     * If the method returns false, \p pair will be invalid.
+     *
+     * \see AnalysisNeighborhoodPair
+     * \see AnalysisNeighborhoodSearch::startPairSearch()
+     */
+    bool findNextPair(AnalysisNeighborhoodPair* pair);
+    /*! \brief
+     * Skip remaining pairs for a test position in the search.
+     *
+     * When called after findNextPair(), makes subsequent calls to
+     * findNextPair() skip any pairs that have the same test position as
+     * that previously returned.
+     * This is useful if the caller wants to search whether any reference
+     * position within the cutoff satisfies some condition.  This method
+     * can be used to skip remaining pairs after the first such position
+     * has been found if the remaining pairs would not have an effect on
+     * the outcome.
+     */
+    void skipRemainingPairsForTestPosition();
+
+private:
+    ImplPointer impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/parser.h b/src/include/gromacs/selection/parser.h
new file mode 100644 (file)
index 0000000..2e04ac7
--- /dev/null
@@ -0,0 +1,184 @@
+/* A Bison parser, made by GNU Bison 3.0.4.  */
+
+/* Bison interface for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+#ifndef YY__GMX_SEL_YY_PARSER_H_INCLUDED
+# define YY__GMX_SEL_YY_PARSER_H_INCLUDED
+/* Debug traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 1
+#endif
+#if YYDEBUG
+extern int _gmx_sel_yydebug;
+#endif
+/* "%code requires" blocks.  */
+#line 1 "parser.y" /* yacc.c:1909  */
+
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#line 76 "parser.y" /* yacc.c:1909  */
+
+#include "parsetree.h"
+#include "selelem.h"
+
+#define YYLTYPE ::gmx::SelectionLocation
+
+#line 87 "parser.h" /* yacc.c:1909  */
+
+/* Token type.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+  enum yytokentype
+  {
+    INVALID = 258,
+    TOK_INT = 259,
+    TOK_REAL = 260,
+    STR = 261,
+    IDENTIFIER = 262,
+    CMD_SEP = 263,
+    GROUP = 264,
+    TO = 265,
+    VARIABLE_NUMERIC = 266,
+    VARIABLE_GROUP = 267,
+    VARIABLE_POS = 268,
+    KEYWORD_NUMERIC = 269,
+    KEYWORD_STR = 270,
+    KEYWORD_POS = 271,
+    KEYWORD_GROUP = 272,
+    METHOD_NUMERIC = 273,
+    METHOD_GROUP = 274,
+    METHOD_POS = 275,
+    MODIFIER = 276,
+    EMPTY_POSMOD = 277,
+    PARAM = 278,
+    END_OF_METHOD = 279,
+    OF = 280,
+    CMP_OP = 281,
+    PARAM_REDUCT = 282,
+    OR = 283,
+    XOR = 284,
+    AND = 285,
+    NOT = 286,
+    UNARY_NEG = 287,
+    NUM_REDUCT = 288
+  };
+#endif
+
+/* Value type.  */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+
+union YYSTYPE
+{
+#line 83 "parser.y" /* yacc.c:1909  */
+
+    int                         i;
+    real                        r;
+    char                       *str;
+    struct gmx_ana_selmethod_t *meth;
+
+    gmx::SelectionStringMatchType                smt;
+
+    gmx::SelectionTreeElementPointer            *sel;
+    gmx::SelectionParserValue                   *val;
+    gmx::SelectionParserValueListPointer        *vlist;
+    gmx::SelectionParserParameter               *param;
+    gmx::SelectionParserParameterListPointer    *plist;
+
+#line 148 "parser.h" /* yacc.c:1909  */
+};
+
+typedef union YYSTYPE YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+/* Location type.  */
+#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
+typedef struct YYLTYPE YYLTYPE;
+struct YYLTYPE
+{
+  int first_line;
+  int first_column;
+  int last_line;
+  int last_column;
+};
+# define YYLTYPE_IS_DECLARED 1
+# define YYLTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+#ifndef YYPUSH_MORE_DEFINED
+# define YYPUSH_MORE_DEFINED
+enum { YYPUSH_MORE = 4 };
+#endif
+
+typedef struct _gmx_sel_yypstate _gmx_sel_yypstate;
+
+int _gmx_sel_yypush_parse (_gmx_sel_yypstate *ps, int pushed_char, YYSTYPE const *pushed_val, YYLTYPE *pushed_loc, void *scanner);
+
+_gmx_sel_yypstate * _gmx_sel_yypstate_new ();
+void _gmx_sel_yypstate_delete (_gmx_sel_yypstate *ps);
+
+#endif /* !YY__GMX_SEL_YY_PARSER_H_INCLUDED  */
diff --git a/src/include/gromacs/selection/parser_internal.h b/src/include/gromacs/selection/parser_internal.h
new file mode 100644 (file)
index 0000000..bc2f63b
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Helper functions for the selection parser.
+ *
+ * This header is includes only from parser.cpp (generated from parser.y), and
+ * it includes functions and macros used internally by the parser.
+ * They are in a separate file to make then easier to edit (no need to
+ * regenerate the parser), and to keep parser.y as simple as possible.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_PARSER_INTERNAL_H
+#define GMX_SELECTION_PARSER_INTERNAL_H
+
+#include <exception>
+#include <memory>
+
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "parsetree.h"
+#include "scanner.h"
+#include "selelem.h"
+
+//! Error handler needed by Bison.
+static void yyerror(YYLTYPE* location, yyscan_t scanner, char const* s)
+{
+    try
+    {
+        std::string            context(_gmx_sel_lexer_get_text(scanner, *location));
+        gmx::InvalidInputError ex(s);
+        // TODO: Examine how to show better context information.
+        if (!context.empty())
+        {
+            context = gmx::formatString("Near '%s'", context.c_str());
+            ex.prependContext(context);
+        }
+        _gmx_sel_lexer_set_exception(scanner, std::make_exception_ptr(ex));
+    }
+    catch (const std::exception&)
+    {
+        _gmx_sel_lexer_set_exception(scanner, std::current_exception());
+    }
+}
+
+//! Logic for computing the location of the output of Bison reduction.
+#define YYLLOC_DEFAULT(Current, Rhs, N)                                            \
+    do                                                                             \
+    {                                                                              \
+        if ((N) != 0)                                                              \
+        {                                                                          \
+            (Current).startIndex = YYRHSLOC(Rhs, 1).startIndex;                    \
+            (Current).endIndex   = YYRHSLOC(Rhs, N).endIndex;                      \
+        }                                                                          \
+        else                                                                       \
+        {                                                                          \
+            (Current).startIndex = (Current).endIndex = YYRHSLOC(Rhs, 0).endIndex; \
+        }                                                                          \
+        _gmx_sel_lexer_set_current_location(scanner, (Current));                   \
+    } while (0)
+
+/*! \brief
+ * Custom macro to influence Bison behavior.
+ *
+ * This macro added to parser.cpp through our patch to force Bison to
+ * use C-style logic for stack reallocation even though we have provided
+ * YYLTYPE and are compiling the code in C++ (our YYLTYPE can safely be copied
+ * this way).
+ * An alternative would be to provide the whole reallocation logic through an
+ * undocumented yyoverflow() macro, but that is probably also more trouble than
+ * it is worth.
+ */
+#define GMX_YYFORCE_C_STACK_EXTENSION 1
+
+/*! \name Exception handling macros for actions
+ *
+ * These macros should be used at the beginning and end of each semantic action
+ * that may throw an exception. For robustness, it's best to wrap all actions
+ * that call functions declared outside parser.y should be wrapped.
+ * These macros take care to catch any exceptions, store the exception (or
+ * handle it and allow the parser to continue), and terminate the parser
+ * cleanly if necessary.
+ * The code calling the parser should use
+ * _gmx_sel_lexer_rethrow_exception_if_occurred() to rethrow any exceptions.
+ * \{
+ */
+//! Starts an action that may throw exceptions.
+#define BEGIN_ACTION \
+    try              \
+    {
+//! Finishes an action that may throw exceptions.
+#define END_ACTION                                         \
+    }                                                      \
+    catch (std::exception & ex)                            \
+    {                                                      \
+        if (_gmx_selparser_handle_exception(scanner, &ex)) \
+        {                                                  \
+            YYERROR;                                       \
+        }                                                  \
+        else                                               \
+        {                                                  \
+            YYABORT;                                       \
+        }                                                  \
+    }
+//! Finishes an action that may throw exceptions and does not support resuming.
+#define END_ACTION_TOPLEVEL                                              \
+    }                                                                    \
+    catch (const std::exception&)                                        \
+    {                                                                    \
+        _gmx_sel_lexer_set_exception(scanner, std::current_exception()); \
+        YYABORT;                                                         \
+    }
+//!\}
+
+/*! \brief
+ * Retrieves a semantic value.
+ *
+ * \param[in] src  Semantic value to get the value from.
+ * \returns   Retrieved value.
+ * \throws    unspecified  Any exception thrown by the move constructor of
+ *      ValueType.
+ *
+ * There should be no statements that may throw exceptions in actions before
+ * this function has been called for all semantic values that have a C++ object
+ * stored.  Together with set(), this function abstracts away exception
+ * safety issues that arise from the use of a plain pointer for storing the
+ * semantic values.
+ *
+ * Does not throw for smart pointer types.  If used with types that may throw,
+ * the order of operations should be such that it is exception-safe.
+ */
+template<typename ValueType>
+static ValueType get(ValueType* src)
+{
+    GMX_RELEASE_ASSERT(src != nullptr, "Semantic value pointers should be non-NULL");
+    const std::unique_ptr<ValueType> srcGuard(src);
+    return ValueType(std::move(*src));
+}
+/*! \brief
+ * Sets a semantic value.
+ *
+ * \tparam     ValueType Type of value to set.
+ * \param[out] dest  Semantic value to set (typically $$).
+ * \param[in]  value Value to put into the semantic value.
+ * \throws     std::bad_alloc if out of memory.
+ * \throws     unspecified  Any exception thrown by the move constructor of
+ *      ValueType.
+ *
+ * This should be the last statement before ::END_ACTION, except for a
+ * possible ::CHECK_SEL.
+ */
+template<typename ValueType>
+static void set(ValueType*& dest, ValueType value)
+{
+    dest = new ValueType(std::move(value));
+}
+/*! \brief
+ * Sets an empty semantic value.
+ *
+ * \tparam     ValueType Type of value to set (must be default constructible).
+ * \param[out] dest  Semantic value to set (typically $$).
+ * \throws     std::bad_alloc if out of memory.
+ * \throws     unspecified  Any exception thrown by the default constructor of
+ *      ValueType.
+ *
+ * This should be the last statement before ::END_ACTION, except for a
+ * possible ::CHECK_SEL.
+ */
+template<typename ValueType>
+static void set_empty(ValueType*& dest)
+{
+    dest = new ValueType;
+}
+/*! \brief
+ * Checks that a valid tree was set.
+ *
+ * Should be called after set() if it was used to set a value where NULL
+ * pointer indicates an error.
+ *
+ * \todo
+ * Get rid of this macro.  It should now be possible to handle all errors using
+ * exceptions.
+ */
+#define CHECK_SEL(sel) \
+    if (!*(sel))       \
+    {                  \
+        delete (sel);  \
+        YYERROR;       \
+    }
+
+#endif
diff --git a/src/include/gromacs/selection/parsetree.h b/src/include/gromacs/selection/parsetree.h
new file mode 100644 (file)
index 0000000..7c46ea0
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009-2018, The GROMACS development team.
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Handling of intermediate selection parser data.
+ *
+ * The data types declared in this header are used by the parser to store
+ * intermediate data when constructing method expressions.
+ * In particular, the parameters for the method are stored.
+ * The intermediate data is freed once a gmx::SelectionTreeElement object can
+ * be constructed.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_PARSETREE_H
+#define GMX_SELECTION_PARSETREE_H
+
+#include <exception>
+#include <list>
+#include <memory>
+#include <string>
+
+#include "gromacs/math/vec.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+#include "selelem.h"
+#include "selvalue.h"
+
+struct gmx_ana_indexgrps_t;
+struct gmx_ana_selmethod_t;
+struct gmx_ana_selparam_t;
+
+namespace gmx
+{
+
+//! \cond internal
+/*! \internal \brief
+ * String matching mode for string keyword expressions.
+ *
+ * \ingroup module_selection
+ */
+enum SelectionStringMatchType
+{
+    eStringMatchType_Auto,             //!< Deduce from the string.
+    eStringMatchType_Exact,            //!< Match as a literal string.
+    eStringMatchType_Wildcard,         //!< Match using ? and * as wildcards.
+    eStringMatchType_RegularExpression //!< Match using regular expressions.
+};
+/*! \endcond */
+
+class SelectionParserValue;
+
+//! Container for a list of SelectionParserValue objects.
+typedef std::list<SelectionParserValue> SelectionParserValueList;
+//! Smart pointer type for managing a SelectionParserValueList.
+typedef std::unique_ptr<SelectionParserValueList> SelectionParserValueListPointer;
+
+/*! \internal
+ * \brief
+ * Describes a parsed value, possibly resulting from expression evaluation.
+ *
+ * All factory methods and the constructors may throw an std::bad_alloc if
+ * out of memory.
+ *
+ * \ingroup module_selection
+ */
+class SelectionParserValue
+{
+public:
+    //! Allocates and initializes an empty value list.
+    static SelectionParserValueListPointer createList()
+    {
+        return std::make_unique<SelectionParserValueList>();
+    }
+    /*! \brief
+     * Allocates and initializes a value list with a single value.
+     *
+     * \param[in] value  Initial value to put in the list.
+     * \returns   Pointer to a new value list that contains \p value.
+     */
+    static SelectionParserValueListPointer createList(const SelectionParserValue& value)
+    {
+        SelectionParserValueListPointer list(new SelectionParserValueList);
+        list->push_back(value);
+        return list;
+    }
+    /*! \brief
+     * Allocates and initializes an expression value.
+     *
+     * \param[in] expr  Root of the expression tree to assign to the value.
+     * \returns   The newly created value.
+     */
+    static SelectionParserValue createExpr(const gmx::SelectionTreeElementPointer& expr)
+    {
+        return SelectionParserValue(expr);
+    }
+    /*! \brief
+     * Allocates and initializes a constant integer value.
+     *
+     * \param[in] value    Integer value to assign to the value.
+     * \param[in] location Location of the value.
+     * \returns   The newly created value.
+     */
+    static SelectionParserValue createInteger(int value, const SelectionLocation& location)
+    {
+        SelectionParserValue result(INT_VALUE, location);
+        result.u.i.i1 = result.u.i.i2 = value;
+        return result;
+    }
+    /*! \brief
+     * Allocates and initializes a constant integer range value.
+     *
+     * \param[in] from     Beginning of the range to assign to the value.
+     * \param[in] to       End of the range to assign to the value.
+     * \param[in] location Location of the value.
+     * \returns   The newly created value.
+     */
+    static SelectionParserValue createIntegerRange(int from, int to, const SelectionLocation& location)
+    {
+        SelectionParserValue result(INT_VALUE, location);
+        result.u.i.i1 = from;
+        result.u.i.i2 = to;
+        return result;
+    }
+    /*! \brief
+     * Allocates and initializes a constant floating-point value.
+     *
+     * \param[in] value    Floating-point value to assign to the value.
+     * \param[in] location Location of the value.
+     * \returns   The newly created value.
+     */
+    static SelectionParserValue createReal(real value, const SelectionLocation& location)
+    {
+        SelectionParserValue result(REAL_VALUE, location);
+        result.u.r.r1 = result.u.r.r2 = value;
+        return result;
+    }
+    /*! \brief
+     * Allocates and initializes a constant floating-point range value.
+     *
+     * \param[in] from     Beginning of the range to assign to the value.
+     * \param[in] to       End of the range to assign to the value.
+     * \param[in] location Location of the value.
+     * \returns   The newly created value.
+     */
+    static SelectionParserValue createRealRange(real from, real to, const SelectionLocation& location)
+    {
+        SelectionParserValue result(REAL_VALUE, location);
+        result.u.r.r1 = from;
+        result.u.r.r2 = to;
+        return result;
+    }
+    /*! \brief
+     * Allocates and initializes a constant string value.
+     *
+     * \param[in] value    String to assign to the value.
+     * \param[in] location Location of the value.
+     * \returns   The newly created value.
+     */
+    static SelectionParserValue createString(const char* value, const SelectionLocation& location)
+    {
+        SelectionParserValue result(STR_VALUE, location);
+        result.str = value;
+        return result;
+    }
+    /*! \brief
+     * Allocates and initializes a constant position value.
+     *
+     * \param[in] value    Position vector to assign to the value.
+     * \param[in] location Location of the value.
+     * \returns   The newly created value.
+     */
+    static SelectionParserValue createPosition(rvec value, const SelectionLocation& location)
+    {
+        SelectionParserValue result(POS_VALUE, location);
+        copy_rvec(value, result.u.x);
+        return result;
+    }
+
+    //! Returns the location of this value in the parsed selection text.
+    const SelectionLocation& location() const { return location_; }
+    //! Returns true if the value comes from expression evaluation.
+    bool hasExpressionValue() const { return static_cast<bool>(expr); }
+
+    //! Returns the string value (\a type must be ::STR_VALUE).
+    const std::string& stringValue() const
+    {
+        GMX_ASSERT(type == STR_VALUE && !hasExpressionValue(),
+                   "Attempted to retrieve string value from a non-string value");
+        return str;
+    }
+
+    // TODO: boost::any or similar could be nicer for the implementation.
+    //! Type of the value.
+    e_selvalue_t type;
+    //! Expression pointer if the value is the result of an expression.
+    gmx::SelectionTreeElementPointer expr;
+    //! String value for \a type ::STR_VALUE.
+    std::string str;
+    //! The actual value if \a expr is NULL and \a type is not ::STR_VALUE.
+    union
+    {
+        //! The integer value/range (\a type ::INT_VALUE).
+        struct
+        {
+            //! Beginning of the range.
+            int i1;
+            //! End of the range; equals \a i1 for a single integer.
+            int i2;
+        } i;
+        //! The real value/range (\a type ::REAL_VALUE).
+        struct
+        {
+            //! Beginning of the range.
+            real r1;
+            //! End of the range; equals \a r1 for a single number.
+            real r2;
+        } r;
+        //! The position value (\a type ::POS_VALUE).
+        rvec x;
+    } u;
+
+private:
+    /*! \brief
+     * Initializes a new value.
+     *
+     * \param[in] type     Type for the new value.
+     * \param[in] location Location for the value.
+     */
+    SelectionParserValue(e_selvalue_t type, const SelectionLocation& location);
+    /*! \brief
+     * Initializes a new expression value.
+     *
+     * \param[in] expr  Expression for the value.
+     */
+    explicit SelectionParserValue(const gmx::SelectionTreeElementPointer& expr);
+
+    //! Location of the value in the parsed text.
+    SelectionLocation location_;
+};
+
+class SelectionParserParameter;
+
+//! Container for a list of SelectionParserParameter objects.
+typedef std::list<SelectionParserParameter> SelectionParserParameterList;
+//! Smart pointer type for managing a SelectionParserParameterList.
+typedef std::unique_ptr<SelectionParserParameterList> SelectionParserParameterListPointer;
+
+/*! \internal \brief
+ * Describes a parsed method parameter.
+ *
+ * \ingroup module_selection
+ */
+class SelectionParserParameter
+{
+public:
+    // Default move constructor and assignment. Only needed for old compilers.
+    //! \cond
+    SelectionParserParameter(SelectionParserParameter&& o) noexcept :
+        name_(std::move(o.name_)), location_(o.location_), values_(std::move(o.values_))
+    {
+    }
+
+    SelectionParserParameter& operator=(SelectionParserParameter&& o) noexcept
+    {
+        name_     = std::move(o.name_);
+        location_ = o.location_;
+        values_   = std::move(o.values_);
+        return *this;
+    }
+    //! \endcond
+
+    //! Allocates and initializes an empty parameter list.
+    static SelectionParserParameterListPointer createList()
+    {
+        return std::make_unique<SelectionParserParameterList>();
+    }
+    /*! \brief
+     * Allocates and initializes a parsed method parameter.
+     *
+     * \param[in] name     Name for the new parameter (can be NULL).
+     * \param[in] values   List of values for the parameter.
+     * \param[in] location Location of the parameter.
+     * \returns   Pointer to the newly allocated parameter.
+     * \throws    std::bad_alloc if out of memory.
+     */
+    static SelectionParserParameter create(const char*                     name,
+                                           SelectionParserValueListPointer values,
+                                           const SelectionLocation&        location)
+    {
+        return SelectionParserParameter(name, std::move(values), location);
+    }
+    //! \copydoc create(const char *, SelectionParserValueListPointer, const SelectionLocation &)
+    static SelectionParserParameter create(const std::string&              name,
+                                           SelectionParserValueListPointer values,
+                                           const SelectionLocation&        location)
+    {
+        return SelectionParserParameter(name.c_str(), std::move(values), location);
+    }
+    /*! \brief
+     * Allocates and initializes a parsed method parameter.
+     *
+     * \param[in] name     Name for the new parameter (can be NULL).
+     * \param[in] value    Value for the parameter.
+     * \param[in] location Location of the parameter.
+     * \returns   Pointer to the newly allocated parameter.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * This overload is a convenience wrapper for the case when creating
+     * parameters outside the actual Bison parser and only a single value
+     * is necessary.
+     */
+    static SelectionParserParameter create(const char*                 name,
+                                           const SelectionParserValue& value,
+                                           const SelectionLocation&    location)
+    {
+        return create(name, SelectionParserValue::createList(value), location);
+    }
+    /*! \brief
+     * Allocates and initializes a parsed method parameter.
+     *
+     * \param[in] name    Name for the new parameter (can be NULL).
+     * \param[in] expr    Expression value for the parameter.
+     * \returns   Pointer to the newly allocated parameter.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * This overload is a convenience wrapper for the case when creating
+     * parameters outside the actual Bison parser and only a single
+     * expression value is necessary.
+     */
+    static SelectionParserParameter createFromExpression(const char*                        name,
+                                                         const SelectionTreeElementPointer& expr)
+    {
+        return create(name, SelectionParserValue::createExpr(expr), expr->location());
+    }
+    //! \copydoc createFromExpression(const char *, const SelectionTreeElementPointer &)
+    static SelectionParserParameter createFromExpression(const std::string&                 name,
+                                                         const SelectionTreeElementPointer& expr)
+    {
+        return create(name.c_str(), SelectionParserValue::createExpr(expr), expr->location());
+    }
+
+    //! Returns the name of the parameter (may be empty).
+    const std::string& name() const { return name_; }
+    //! Returns the location of this parameter in the parsed selection text.
+    const SelectionLocation& location() const { return location_; }
+    //! Returns the values for the parameter.
+    const SelectionParserValueList& values() const { return *values_; }
+
+private:
+    /*! \brief
+     * Initializes a parsed method parameter.
+     *
+     * \param[in] name     Name for the new parameter (can be NULL).
+     * \param[in] values   List of values for the parameter.
+     * \param[in] location Location of the parameter.
+     * \throws    std::bad_alloc if out of memory.
+     */
+    SelectionParserParameter(const char*                     name,
+                             SelectionParserValueListPointer values,
+                             const SelectionLocation&        location);
+
+    //! Name of the parameter.
+    std::string name_;
+    //! Location of the parameter in the parsed text.
+    SelectionLocation location_;
+
+    // TODO: Make private, there is only one direct user.
+public:
+    //! Values for this parameter.
+    SelectionParserValueListPointer values_;
+};
+
+} // namespace gmx
+
+/*! \brief
+ * Handles exceptions caught within the Bison code.
+ *
+ * \retval `true`  if the parser should attempt error recovery.
+ * \retval `false` if the parser should immediately abort.
+ *
+ * This function is called whenever an exception is caught within Bison
+ * actions.  Since exceptions cannot propagate through Bison code, the
+ * exception is saved (potentially with some extra context information) so that
+ * the caller of the parser can rethrow the exception.
+ *
+ * If it is possible to recover from the exception, then the function returns
+ * `true`, and Bison enters error recovery state.  At the end of the recovery,
+ * _gmx_selparser_handle_error() is called.
+ * If this function returns false, then Bison immediately aborts the parsing
+ * so that the caller can rethrow the exception.
+ */
+bool _gmx_selparser_handle_exception(void* scanner, std::exception* ex);
+/*! \brief
+ * Handles errors in the selection parser.
+ *
+ * \returns `true` if parsing can continue with the next selection.
+ * \throws  std::bad_alloc if out of memory during the error processing.
+ * \throws  unspecified    Can throw the stored exception if recovery from that
+ *     exception is not possible.
+ *
+ * This function is called during error recovery, after Bison has discarded all
+ * the symbols for the erroneous selection.
+ * At this point, the full selection that caused the error is known, and can be
+ * added to the error context.
+ *
+ * For an interactive parser, this function returns `true` to let the parsing
+ * continue with the next selection, or to let the user enter the next
+ * selection, if it was possible to recover from the exception.
+ * For other cases, this will either rethrow the original exception with added
+ * context, or return `false` after adding the context to the error reporter.
+ * Any exceptions thrown from this method are again caught by Bison and result
+ * in termination of the parsing; the caller can then rethrow them.
+ */
+bool _gmx_selparser_handle_error(void* scanner);
+
+/** Propagates the flags for selection elements. */
+void _gmx_selelem_update_flags(const gmx::SelectionTreeElementPointer& sel);
+
+/** Initializes the method parameter data of \ref SEL_EXPRESSION and
+ * \ref SEL_MODIFIER elements. */
+void _gmx_selelem_init_method_params(const gmx::SelectionTreeElementPointer& sel, void* scanner);
+/** Initializes the method for a \ref SEL_EXPRESSION selection element. */
+void _gmx_selelem_set_method(const gmx::SelectionTreeElementPointer& sel,
+                             struct gmx_ana_selmethod_t*             method,
+                             void*                                   scanner);
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#    define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+/** \brief Creates a gmx::SelectionTreeElement for arithmetic expression evaluation.
+ *
+ * \param[in]  left    Selection element for the left hand side.
+ * \param[in]  right   Selection element for the right hand side.
+ * \param[in]  op      String representation of the operator.
+ * \param[in]  scanner Scanner data structure.
+ * \returns    The created selection element.
+ *
+ * This function handles the creation of a gmx::SelectionTreeElement object for
+ * arithmetic expressions.
+ */
+gmx::SelectionTreeElementPointer _gmx_sel_init_arithmetic(const gmx::SelectionTreeElementPointer& left,
+                                                          const gmx::SelectionTreeElementPointer& right,
+                                                          char     op,
+                                                          yyscan_t scanner);
+/** Creates a gmx::SelectionTreeElement for comparsion expression evaluation. */
+gmx::SelectionTreeElementPointer _gmx_sel_init_comparison(const gmx::SelectionTreeElementPointer& left,
+                                                          const gmx::SelectionTreeElementPointer& right,
+                                                          const char* cmpop,
+                                                          void*       scanner);
+/** Creates a gmx::SelectionTreeElement for a keyword expression from the parsed data. */
+gmx::SelectionTreeElementPointer _gmx_sel_init_keyword(struct gmx_ana_selmethod_t*          method,
+                                                       gmx::SelectionParserValueListPointer args,
+                                                       const char*                          rpost,
+                                                       void* scanner);
+/** Creates a gmx::SelectionTreeElement for string-matching keyword expression. */
+gmx::SelectionTreeElementPointer _gmx_sel_init_keyword_strmatch(struct gmx_ana_selmethod_t* method,
+                                                                gmx::SelectionStringMatchType matchType,
+                                                                gmx::SelectionParserValueListPointer args,
+                                                                const char* rpost,
+                                                                void*       scanner);
+/** Creates a gmx::SelectionTreeElement for "keyword of" expression. */
+gmx::SelectionTreeElementPointer _gmx_sel_init_keyword_of(struct gmx_ana_selmethod_t* method,
+                                                          const gmx::SelectionTreeElementPointer& group,
+                                                          const char* rpost,
+                                                          void*       scanner);
+/** Creates a gmx::SelectionTreeElement for a method expression from the parsed data. */
+gmx::SelectionTreeElementPointer _gmx_sel_init_method(struct gmx_ana_selmethod_t* method,
+                                                      gmx::SelectionParserParameterListPointer params,
+                                                      const char* rpost,
+                                                      void*       scanner);
+/** Creates a gmx::SelectionTreeElement for a modifier expression from the parsed data. */
+gmx::SelectionTreeElementPointer _gmx_sel_init_modifier(struct gmx_ana_selmethod_t* mod,
+                                                        gmx::SelectionParserParameterListPointer params,
+                                                        const gmx::SelectionTreeElementPointer& sel,
+                                                        void* scanner);
+/** Creates a gmx::SelectionTreeElement for evaluation of reference positions. */
+gmx::SelectionTreeElementPointer _gmx_sel_init_position(const gmx::SelectionTreeElementPointer& expr,
+                                                        const char* type,
+                                                        void*       scanner);
+
+/** Creates a gmx::SelectionTreeElement for a constant position. */
+gmx::SelectionTreeElementPointer _gmx_sel_init_const_position(real x, real y, real z, void* scanner);
+/** Creates a gmx::SelectionTreeElement for a index group expression using group name. */
+gmx::SelectionTreeElementPointer _gmx_sel_init_group_by_name(const char* name, void* scanner);
+/** Creates a gmx::SelectionTreeElement for a index group expression using group index. */
+gmx::SelectionTreeElementPointer _gmx_sel_init_group_by_id(int id, void* scanner);
+/** Creates a gmx::SelectionTreeElement for a variable reference */
+gmx::SelectionTreeElementPointer _gmx_sel_init_variable_ref(const gmx::SelectionTreeElementPointer& sel,
+                                                            void* scanner);
+
+/** Creates a root gmx::SelectionTreeElement for a selection. */
+gmx::SelectionTreeElementPointer _gmx_sel_init_selection(const char* name,
+                                                         const gmx::SelectionTreeElementPointer& sel,
+                                                         void* scanner);
+/** Creates a root gmx::SelectionTreeElement elements for a variable assignment. */
+gmx::SelectionTreeElementPointer _gmx_sel_assign_variable(const char* name,
+                                                          const gmx::SelectionTreeElementPointer& expr,
+                                                          void* scanner);
+/** Appends a root gmx::SelectionTreeElement to a selection collection. */
+gmx::SelectionTreeElementPointer _gmx_sel_append_selection(const gmx::SelectionTreeElementPointer& sel,
+                                                           gmx::SelectionTreeElementPointer last,
+                                                           void* scanner);
+/** Check whether the parser should finish. */
+bool _gmx_sel_parser_should_finish(void* scanner);
+
+/* In params.c */
+/** Initializes an array of parameters based on input from the selection parser. */
+void _gmx_sel_parse_params(const gmx::SelectionParserParameterList& params,
+                           int                                      nparam,
+                           struct gmx_ana_selparam_t*               param,
+                           const gmx::SelectionTreeElementPointer&  root,
+                           void*                                    scanner);
+
+#endif
diff --git a/src/include/gromacs/selection/poscalc.h b/src/include/gromacs/selection/poscalc.h
new file mode 100644 (file)
index 0000000..f17c859
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009-2016, The GROMACS development team.
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * API for structured and optimized calculation of positions.
+ *
+ * This header declares an API for calculating positions in an automated way,
+ * for internal use by the selection engine.  This is useful in particular with
+ * dynamic selections, because the same COM/COG positions may be needed in
+ * several contexts.  The API makes it possible to optimize the evaluation such
+ * that any heavy calculation is only done once, and the results just copied if
+ * needed more than once.  The functions also provide a convenient interface
+ * for keeping the whole \c gmx_ana_pos_t structure up-to-date.
+ *
+ * The API is documented in more detail in gmx::PositionCalculationCollection.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_POSCALC_H
+#define GMX_SELECTION_POSCALC_H
+
+#include <cstdio>
+
+#include <memory>
+
+/*! \name Flags for position calculation.
+ * \anchor poscalc_flags
+ */
+/*@{*/
+/*! \brief
+ * Use mass weighting.
+ *
+ * If this flag is set, the positions will be calculated using mass weighting,
+ * i.e., one gets center-of-mass positions.
+ * Without the flag, center-of-geometry positions are calculated.
+ * Does not have any effect if the calculation type is \ref POS_ATOM.
+ */
+#define POS_MASS 1
+/*! \brief
+ * Calculate positions for the same atoms in residues/molecules.
+ *
+ * If this flag is set, the positions are always calculated using the same
+ * atoms for each residue/molecule, even if the evaluation group contains only
+ * some of the atoms for some frames.
+ * The group passed to gmx_ana_poscalc_set_maxindex() is used to determine
+ * the atoms to use for the calculation.
+ *
+ * Has no effect unless \ref POS_DYNAMIC is set or if the calculation type
+ * is not \ref POS_RES of \ref POS_MOL.
+ */
+#define POS_COMPLMAX 2
+/*! \brief
+ * Calculate positions for whole residues/molecules.
+ *
+ * If this flag is set, the positions will be calculated for whole
+ * residues/molecules, even if the group contains only some of the atoms in
+ * the residue/molecule.
+ *
+ * Has no effect unless the calculation type is \ref POS_RES or \ref POS_MOL.
+ */
+#define POS_COMPLWHOLE 4
+/*! \brief
+ * Enable handling of changing calculation groups.
+ *
+ * Can be used for static calculations as well, but implies a small
+ * performance penalty.
+ */
+#define POS_DYNAMIC 16
+/*! \brief
+ * Update \c gmx_ana_pos_t::m dynamically for an otherwise static
+ * calculation.
+ *
+ * Has effect only if \ref POS_DYNAMIC is not set.
+ */
+#define POS_MASKONLY 32
+/*! \brief
+ * Calculate velocities of the positions.
+ */
+#define POS_VELOCITIES 64
+/*! \brief
+ * Calculate forces on the positions.
+ */
+#define POS_FORCES 128
+/*@}*/
+
+/** Specifies the type of positions to be calculated. */
+typedef enum
+{
+    POS_ATOM,   /**< Copy atomic coordinates. */
+    POS_RES,    /**< Calculate center for each residue. */
+    POS_MOL,    /**< Calculate center for each molecule. */
+    POS_ALL,    /**< Calculate center for the whole group. */
+    POS_ALL_PBC /**< Calculate center for the whole group with PBC. */
+} e_poscalc_t;
+
+/** Data structure for position calculation. */
+struct gmx_ana_poscalc_t;
+
+struct gmx_ana_index_t;
+struct gmx_ana_pos_t;
+struct gmx_mtop_t;
+struct t_pbc;
+struct t_trxframe;
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief
+ * Collection of \c gmx_ana_poscalc_t structures for the same topology.
+ *
+ * Calculations within one collection share the same topology, and they are
+ * optimized.  Calculations in different collections do not interact.
+ * The topology for a collection can be set with setTopology().
+ * This needs to be done before calling gmx_ana_poscalc_set_maxindex() for
+ * any calculation in the collection, unless that calculation does not
+ * require topology information.
+ *
+ * A new calculation is created with createCalculation().
+ * If flags need to be adjusted later, gmx_ana_poscalc_set_flags() can be
+ * used.
+ * After the flags are final, the largest possible index group for which the
+ * positions are needed has to be set with gmx_ana_poscalc_set_maxindex().
+ * setTopology() should have been called before this function is called.
+ * After the above calls, gmx_ana_poscalc_init_pos() can be used to initialize
+ * output to a \c gmx_ana_pos_t structure.  Several different structures can be
+ * initialized for the same calculation; the only requirement is that the
+ * structure passed later to gmx_ana_poscalc_update() has been initialized
+ * properly.
+ * The memory allocated for a calculation can be freed with
+ * gmx_ana_poscalc_free().
+ *
+ * The position evaluation is simple: initFrame() should be
+ * called once for each frame, and gmx_ana_poscalc_update() can then be called
+ * for each calculation that is needed for that frame.
+ *
+ * It is also possible to initialize the calculations based on a type provided
+ * as a string.
+ * The possible strings are listed in \ref typeEnumValues, and the string can
+ * be converted to the parameters for createCalculation() using typeFromEnum().
+ * createCalculationFromEnum() is also provided for convenience.
+ *
+ * \ingroup module_selection
+ */
+class PositionCalculationCollection
+{
+public:
+    //! Describes what topology information is needed for position calculation.
+    enum class RequiredTopologyInfo
+    {
+        None,             //!< No topology is needed.
+        Topology,         //!< Topology is needed (residue/molecule info).
+        TopologyAndMasses //!< Masses are needed.
+    };
+
+    /*! \brief
+     * Array of strings acceptable for position calculation type enum.
+     *
+     * This array contains the acceptable values for typeFromEnum() and
+     * createCalculationFromEnum().
+     * The array contains a NULL pointer after the last item to indicate
+     * the end of the list.
+     */
+    static const char* const typeEnumValues[];
+
+    /*! \brief
+     * Converts a string to parameters for createCalculationFromEnum().
+     *
+     * \param[in]     post  String (typically an enum argument).
+     *     Allowed values: 'atom', 'res_com', 'res_cog', 'mol_com', 'mol_cog',
+     *     or one of the last four prepended by 'whole_', 'part_', or 'dyn_'.
+     * \param[out]    type  \c e_poscalc_t corresponding to \p post.
+     * \param[in,out] flags Flags corresponding to \p post.
+     *     On input, the flags should contain the default flags.
+     *     On exit, the flags \ref POS_MASS, \ref POS_COMPLMAX and
+     *     \ref POS_COMPLWHOLE have been set according to \p post
+     *     (the completion flags are left at the default values if no
+     *     completion prefix is given).
+     * \throws  InternalError  if post is not recognized.
+     *
+     * \attention
+     * Checking is not complete, and other values than those listed above
+     * may be accepted for \p post, but the results are undefined.
+     *
+     * \see typeEnumValues
+     */
+    static void typeFromEnum(const char* post, e_poscalc_t* type, int* flags);
+    /*! \brief
+     * Returns what information is needed for position evaluation.
+     *
+     * \param[in] post   Position type (see typeFromEnum()).
+     * \param[in] forces Whether forces are needed.
+     * \returns   What topology information is required for initializing
+     *     and/or evaluating the positions.
+     */
+    static RequiredTopologyInfo requiredTopologyInfoForType(const char* post, bool forces);
+
+    /*! \brief
+     * Creates a new position calculation collection object.
+     *
+     * \throws  std::bad_alloc if out of memory.
+     */
+    PositionCalculationCollection();
+    /*! \brief
+     * Destroys a position calculation collection and its calculations.
+     *
+     * Any calculations in the collection are also freed, even if
+     * references to them are left.
+     */
+    ~PositionCalculationCollection();
+
+    /*! \brief
+     * Sets the topology used for the calculations.
+     *
+     * \param[in]     top   Topology data structure.
+     *
+     * This function should be called to set the topology before using
+     * gmx_ana_poscalc_set_maxindex() for any calculation that requires
+     * topology information.
+     *
+     * Does not throw.
+     */
+    void setTopology(const gmx_mtop_t* top);
+    /*! \brief
+     * Prints information about calculations.
+     *
+     * \param[in] fp    File handle to receive the output.
+     *
+     * The output is very technical, making this function mainly useful for
+     * debugging purposes.
+     *
+     * Does not throw.
+     */
+    void printTree(FILE* fp) const;
+
+    /*! \brief
+     * Creates a new position calculation.
+     *
+     * \param[in]  type  Type of calculation.
+     * \param[in]  flags Flags for setting calculation options
+     *   (see \ref poscalc_flags "documentation of the flags").
+     *
+     * Does not throw currently, but may throw std::bad_alloc in the
+     * future.
+     */
+    gmx_ana_poscalc_t* createCalculation(e_poscalc_t type, int flags);
+    /*! \brief
+     * Creates a new position calculation based on an enum value.
+     *
+     * \param[in]  post  One of the strings acceptable for
+     *      typeFromEnum().
+     * \param[in]  flags Flags for setting calculation options
+     *      (see \ref poscalc_flags "documentation of the flags").
+     * \throws     InternalError  if post is not recognized.
+     *
+     * This is a convenience wrapper for createCalculation().
+     * \p flags sets the default calculation options if not overridden by
+     * \p post; see typeFromEnum().
+     *
+     * May also throw std::bad_alloc in the future.
+     *
+     * \see createCalculation(), typeFromEnum()
+     */
+    gmx_ana_poscalc_t* createCalculationFromEnum(const char* post, int flags);
+
+    /*! \brief
+     * Computes the atoms required to evaluate this collection.
+     *
+     * \param[out] out  Maximal group of atoms required to evaluate the
+     *     positions.
+     *
+     * Does not throw.
+     */
+    void getRequiredAtoms(gmx_ana_index_t* out) const;
+
+    /*! \brief
+     * Initializes evaluation for a position calculation collection.
+     *
+     * This function does some final initialization of the data structures
+     * in the collection to prepare them for evaluation.
+     * After this function has been called, it is no longer possible to add
+     * new calculations to the collection.
+     *
+     * Multiple calls to the function are ignored.
+     *
+     * Does not throw currently, but may throw std::bad_alloc in the
+     * future.
+     */
+    void initEvaluation();
+    /*! \brief
+     * Initializes a position calculation collection for a new frame.
+     *
+     * \param[in] fr  Frame to initialize evaluation for.
+     *
+     * Should be called for each frame before calling
+     * gmx_ana_poscalc_update().
+     *
+     * This function calls initEvaluation() automatically if it has not
+     * been called earlier.
+     *
+     * Does not currently throw, but may throw std::bad_alloc in the
+     * future.
+     */
+    void initFrame(const t_trxframe* fr);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+
+    /*! \brief
+     * Needed to access the implementation class from the C code.
+     */
+    friend struct ::gmx_ana_poscalc_t;
+};
+
+} // namespace gmx
+
+/** Sets the flags for position calculation. */
+void gmx_ana_poscalc_set_flags(gmx_ana_poscalc_t* pc, int flags);
+/** Sets the maximum possible input index group for position calculation. */
+void gmx_ana_poscalc_set_maxindex(gmx_ana_poscalc_t* pc, gmx_ana_index_t* g);
+/** Initializes positions for position calculation output. */
+void gmx_ana_poscalc_init_pos(gmx_ana_poscalc_t* pc, gmx_ana_pos_t* p);
+/** Frees the memory allocated for position calculation. */
+void gmx_ana_poscalc_free(gmx_ana_poscalc_t* pc);
+/*! \brief
+ * Returns true if the position calculation requires topology information.
+ *
+ * \param[in] pc  Position calculation data to query.
+ * \returns   Which topology information \p pc requires for initialization
+ *     and/or evaluation.
+ */
+gmx::PositionCalculationCollection::RequiredTopologyInfo
+gmx_ana_poscalc_required_topology_info(gmx_ana_poscalc_t* pc);
+
+/** Updates a single COM/COG structure for a frame. */
+void gmx_ana_poscalc_update(gmx_ana_poscalc_t* pc,
+                            gmx_ana_pos_t*     p,
+                            gmx_ana_index_t*   g,
+                            t_trxframe*        fr,
+                            const t_pbc*       pbc);
+
+#endif
diff --git a/src/include/gromacs/selection/position.h b/src/include/gromacs/selection/position.h
new file mode 100644 (file)
index 0000000..be559bc
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief API for handling positions.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_POSITION_H
+#define GMX_SELECTION_POSITION_H
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/selection/indexutil.h"
+
+/*! \brief
+ * Stores a set of positions together with their origins.
+ */
+struct gmx_ana_pos_t
+{
+    //! Initializes an empty position structure.
+    gmx_ana_pos_t();
+    ~gmx_ana_pos_t();
+
+    //! Returns the number of positions.
+    int count() const { return m.mapb.nr; }
+
+    /*! \brief
+     * Array of positions.
+     */
+    rvec* x;
+    /*! \brief
+     * Velocities (can be NULL).
+     */
+    rvec* v;
+    /*! \brief
+     * Forces (can be NULL).
+     */
+    rvec* f;
+    /*! \brief
+     * Mapping of the current positions to the original group.
+     *
+     * \see gmx_ana_indexmap_t
+     */
+    gmx_ana_indexmap_t m;
+    /*! \brief
+     * Number of elements allocated for \c x.
+     */
+    int nalloc_x;
+};
+
+/** Ensures that enough memory has been allocated to store positions. */
+void gmx_ana_pos_reserve(gmx_ana_pos_t* pos, int n, int isize);
+/** Request memory allocation for velocities. */
+void gmx_ana_pos_reserve_velocities(gmx_ana_pos_t* pos);
+/** Request memory allocation for forces. */
+void gmx_ana_pos_reserve_forces(gmx_ana_pos_t* pos);
+/** Reserves memory for use with gmx_ana_pos_append_init(). */
+void gmx_ana_pos_reserve_for_append(gmx_ana_pos_t* pos, int n, int isize, bool bVelocities, bool bForces);
+/** Initializes a \c gmx_ana_pos_t to represent a constant position. */
+void gmx_ana_pos_init_const(gmx_ana_pos_t* pos, const rvec x);
+/** Copies the evaluated positions to a preallocated data structure. */
+void gmx_ana_pos_copy(gmx_ana_pos_t* dest, gmx_ana_pos_t* src, bool bFirst);
+
+/** Sets the number of positions in a position structure. */
+void gmx_ana_pos_set_nr(gmx_ana_pos_t* pos, int n);
+/** Empties a position data structure with full initialization. */
+void gmx_ana_pos_empty_init(gmx_ana_pos_t* pos);
+/** Empties a position data structure. */
+void gmx_ana_pos_empty(gmx_ana_pos_t* pos);
+/** Appends a position to a preallocated data structure with full
+ * initialization. */
+void gmx_ana_pos_append_init(gmx_ana_pos_t* dest, gmx_ana_pos_t* src, int i);
+/** Appends a position to a preallocated data structure. */
+void gmx_ana_pos_append(gmx_ana_pos_t* dest, gmx_ana_pos_t* src, int i, int refid);
+/** Updates position data structure state after appends. */
+void gmx_ana_pos_append_finish(gmx_ana_pos_t* pos);
+void
+/** Appends atoms from a position into a preallocated index group. */
+gmx_ana_pos_add_to_group(gmx_ana_index_t* g, gmx_ana_pos_t* src, int i);
+
+#endif
diff --git a/src/include/gromacs/selection/scanner.h b/src/include/gromacs/selection/scanner.h
new file mode 100644 (file)
index 0000000..32ad428
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2012,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Parser/scanner interaction functions.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef SELECTION_SCANNER_H
+#define SELECTION_SCANNER_H
+
+#include <exception>
+#include <string>
+
+#include "parser.h"
+
+namespace gmx
+{
+class TextWriter;
+}
+
+struct gmx_ana_indexgrps_t;
+struct gmx_ana_selcollection_t;
+
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#    define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/** Initializes the selection scanner. */
+void _gmx_sel_init_lexer(yyscan_t*                       scannerp,
+                         struct gmx_ana_selcollection_t* sc,
+                         gmx::TextWriter*                statusWriter,
+                         int                             maxnr,
+                         bool                            bGroups,
+                         struct gmx_ana_indexgrps_t*     grps);
+/** Frees memory allocated for the selection scanner. */
+void _gmx_sel_free_lexer(yyscan_t scanner);
+/** Stores an exception that is caught during parsing. */
+void _gmx_sel_lexer_set_exception(yyscan_t scanner, const std::exception_ptr& ex);
+/** Rethrows and clears the stored exception if one is present. */
+// TODO: The semantics is a bit confusing, need to be thought more,
+// but easier to do as part of larger refactoring of the parsing.
+void _gmx_sel_lexer_rethrow_exception_if_occurred(yyscan_t scanner);
+
+/** Returns writer for status output (if not NULL, the scanner is interactive). */
+gmx::TextWriter* _gmx_sel_lexer_get_status_writer(yyscan_t scanner);
+/** Returns the selection collection for the scanner. */
+struct gmx_ana_selcollection_t* _gmx_sel_lexer_selcollection(yyscan_t scanner);
+/** Returns true if the external index groups for the scanner are set. */
+bool _gmx_sel_lexer_has_groups_set(yyscan_t scanner);
+/** Returns the external index groups for the scanner. */
+struct gmx_ana_indexgrps_t* _gmx_sel_lexer_indexgrps(yyscan_t scanner);
+/** Returns the number of selections after which the parser should stop. */
+int _gmx_sel_lexer_exp_selcount(yyscan_t scanner);
+
+/** Returns a pretty string of the current selection.  */
+const char* _gmx_sel_lexer_pselstr(yyscan_t scanner);
+/*! \brief
+ * Sets the current parser context location.
+ *
+ * This location is set while Bison reductions are being processed, and
+ * identifies the location of the current rule/reduction.
+ */
+void _gmx_sel_lexer_set_current_location(yyscan_t scanner, const gmx::SelectionLocation& location);
+/*! \brief
+ * Returns the current parser context location.
+ *
+ * This returns the location last set with
+ * _gmx_sel_lexer_set_current_location().
+ */
+const gmx::SelectionLocation& _gmx_sel_lexer_get_current_location(yyscan_t scanner);
+/*! \brief
+ * Returns the selection text for the current parser context.
+ *
+ * This returns the selection text that corresponds to the position set last
+ * with _gmx_sel_lexer_set_current_location().
+ */
+std::string _gmx_sel_lexer_get_current_text(yyscan_t scanner);
+/*! \brief
+ * Returns the selection text at the given location.
+ */
+std::string _gmx_sel_lexer_get_text(yyscan_t scanner, const gmx::SelectionLocation& location);
+/** Clears the current selection string.  */
+void _gmx_sel_lexer_clear_pselstr(yyscan_t scanner);
+/** Clears the method stack in the scanner in error situations. */
+void _gmx_sel_lexer_clear_method_stack(yyscan_t scanner);
+/** Notifies the scanner that a complete method expression has been parsed. */
+void _gmx_sel_finish_method(yyscan_t scanner);
+/** Initializes the scanner to scan a file. */
+void _gmx_sel_set_lex_input_file(yyscan_t scanner, FILE* fp);
+/** Initializes the scanner to scan a string. */
+void _gmx_sel_set_lex_input_str(yyscan_t scanner, const char* str);
+
+/** The scanning function generated by Flex. */
+#define YY_DECL int _gmx_sel_yylex(YYSTYPE* yylval, YYLTYPE* yylloc, yyscan_t yyscanner)
+YY_DECL;
+
+#endif
diff --git a/src/include/gromacs/selection/scanner_flex.h b/src/include/gromacs/selection/scanner_flex.h
new file mode 100644 (file)
index 0000000..239c614
--- /dev/null
@@ -0,0 +1,357 @@
+#ifndef _gmx_sel_yyHEADER_H
+#define _gmx_sel_yyHEADER_H 1
+#define _gmx_sel_yyIN_HEADER 1
+
+#line 6 "scanner_flex.h"
+#line 50 "scanner.l"
+#if !defined _gmx_sel_yyIN_HEADER
+#include "gmxpre.h"
+#endif
+
+// Required before flex definitions, since it includes <stdint.h>.
+// Otherwise, compilers not strictly C99 get macro redefinition errors,
+// since flex defines INT32_MAX etc. in such cases.
+#include "gromacs/utility/basedefinitions.h"
+
+
+
+#line 19 "scanner_flex.h"
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 0
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+#ifdef __cplusplus
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else  /* ! __cplusplus */
+
+/* C99 requires __STDC__ to be defined as 1. */
+#if defined (__STDC__)
+
+#define YY_USE_CONST
+
+#endif /* defined (__STDC__) */
+#endif /* ! __cplusplus */
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+/* An opaque pointer. */
+#ifndef YY_TYPEDEF_YY_SCANNER_T
+#define YY_TYPEDEF_YY_SCANNER_T
+typedef void* yyscan_t;
+#endif
+
+/* For convenience, these vars (plus the bison vars far below)
+   are macros in the reentrant scanner. */
+#define yyin yyg->yyin_r
+#define yyout yyg->yyout_r
+#define yyextra yyg->yyextra_r
+#define yyleng yyg->yyleng_r
+#define yytext yyg->yytext_r
+#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
+#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
+#define yy_flex_debug yyg->yy_flex_debug_r
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+       {
+       FILE *yy_input_file;
+
+       char *yy_ch_buf;                /* input buffer */
+       char *yy_buf_pos;               /* current position in input buffer */
+
+       /* Size of input buffer in bytes, not including room for EOB
+        * characters.
+        */
+       yy_size_t yy_buf_size;
+
+       /* Number of characters read into yy_ch_buf, not including EOB
+        * characters.
+        */
+       yy_size_t yy_n_chars;
+
+       /* Whether we "own" the buffer - i.e., we know we created it,
+        * and can realloc() it to grow it, and should free() it to
+        * delete it.
+        */
+       int yy_is_our_buffer;
+
+       /* Whether this is an "interactive" input source; if so, and
+        * if we're using stdio for input, then we want to use getc()
+        * instead of fread(), to make sure we stop fetching input after
+        * each newline.
+        */
+       int yy_is_interactive;
+
+       /* Whether we're considered to be at the beginning of a line.
+        * If so, '^' rules will be active on the next match, otherwise
+        * not.
+        */
+       int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+    
+       /* Whether to try to fill the input buffer when we reach the
+        * end of it.
+        */
+       int yy_fill_buffer;
+
+       int yy_buffer_status;
+
+       };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+void _gmx_sel_yyrestart (FILE *input_file ,yyscan_t yyscanner );
+void _gmx_sel_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+YY_BUFFER_STATE _gmx_sel_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
+void _gmx_sel_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void _gmx_sel_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
+void _gmx_sel_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
+void _gmx_sel_yypop_buffer_state (yyscan_t yyscanner );
+
+YY_BUFFER_STATE _gmx_sel_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
+YY_BUFFER_STATE _gmx_sel_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
+YY_BUFFER_STATE _gmx_sel_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
+
+void *_gmx_sel_yyalloc (yy_size_t ,yyscan_t yyscanner );
+void *_gmx_sel_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
+void _gmx_sel_yyfree (void * ,yyscan_t yyscanner );
+
+#define _gmx_sel_yywrap(yyscanner) (/*CONSTCOND*/1)
+#define YY_SKIP_YYWRAP
+
+#define yytext_ptr yytext_r
+
+#ifdef YY_HEADER_EXPORT_START_CONDITIONS
+#define INITIAL 0
+#define matchof 1
+#define matchbool 2
+#define cmdstart 3
+
+#endif
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+int _gmx_sel_yylex_init (yyscan_t* scanner);
+
+int _gmx_sel_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int _gmx_sel_yylex_destroy (yyscan_t yyscanner );
+
+int _gmx_sel_yyget_debug (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_debug (int debug_flag ,yyscan_t yyscanner );
+
+YY_EXTRA_TYPE _gmx_sel_yyget_extra (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
+
+FILE *_gmx_sel_yyget_in (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_in  (FILE * _in_str ,yyscan_t yyscanner );
+
+FILE *_gmx_sel_yyget_out (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_out  (FILE * _out_str ,yyscan_t yyscanner );
+
+yy_size_t _gmx_sel_yyget_leng (yyscan_t yyscanner );
+
+char *_gmx_sel_yyget_text (yyscan_t yyscanner );
+
+int _gmx_sel_yyget_lineno (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_lineno (int _line_number ,yyscan_t yyscanner );
+
+int _gmx_sel_yyget_column  (yyscan_t yyscanner );
+
+void _gmx_sel_yyset_column (int _column_no ,yyscan_t yyscanner );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int _gmx_sel_yywrap (yyscan_t yyscanner );
+#else
+extern int _gmx_sel_yywrap (yyscan_t yyscanner );
+#endif
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
+#endif
+
+#ifndef YY_NO_INPUT
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int _gmx_sel_yylex (yyscan_t yyscanner);
+
+#define YY_DECL int _gmx_sel_yylex (yyscan_t yyscanner)
+#endif /* !YY_DECL */
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+#undef YY_NEW_FILE
+#undef YY_FLUSH_BUFFER
+#undef yy_set_bol
+#undef yy_new_buffer
+#undef yy_set_interactive
+#undef YY_DO_BEFORE_ACTION
+
+#ifdef YY_DECL_IS_OURS
+#undef YY_DECL_IS_OURS
+#undef YY_DECL
+#endif
+
+#line 178 "scanner.l"
+
+#line 363 "scanner_flex.h"
+#undef _gmx_sel_yyIN_HEADER
+#endif /* _gmx_sel_yyHEADER_H */
diff --git a/src/include/gromacs/selection/scanner_internal.h b/src/include/gromacs/selection/scanner_internal.h
new file mode 100644 (file)
index 0000000..3cae7ac
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009-2018, The GROMACS development team.
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Internal header file used by the selection tokenizer.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef SELECTION_SCANNER_INTERNAL_H
+#define SELECTION_SCANNER_INTERNAL_H
+
+#include <exception>
+#include <string>
+
+#include "parser.h"
+
+namespace gmx
+{
+class SelectionParserSymbol;
+class TextWriter;
+} // namespace gmx
+
+/* These need to be defined before including scanner_flex.h, because it
+ * uses YY_EXTRA_TYPE. But we also need to include it before defining
+ * gmx_sel_lexer_t; hence the forward declaration. */
+struct gmx_sel_lexer_t;
+#define YY_EXTRA_TYPE struct gmx_sel_lexer_t*
+
+/* We cannot include scanner_flex.h from the scanner itself, because it
+ * seems to break everything. */
+/* And we need to define YY_NO_UNISTD_H here as well, otherwise unistd.h
+ * gets included in other files than scanner.cpp... */
+#ifndef FLEX_SCANNER
+#    define YY_NO_UNISTD_H
+#    include "scanner_flex.h"
+#endif
+
+/*! \internal \brief
+ * Internal data structure for the selection tokenizer state.
+ */
+typedef struct gmx_sel_lexer_t
+{
+    //! Selection collection to put parsed selections in.
+    struct gmx_ana_selcollection_t* sc;
+    //! Stores an exception that occurred during parsing.
+    std::exception_ptr exception;
+    //! Whether external index groups have been set.
+    bool bGroups;
+    //! External index groups for resolving \c group keywords.
+    struct gmx_ana_indexgrps_t* grps;
+    //! Number of selections at which the parser should stop.
+    int nexpsel;
+
+    //! Writer to use for status output (if not NULL, parser is interactive).
+    gmx::TextWriter* statusWriter;
+
+    //! Pretty-printed version of the string parsed since last clear.
+    std::string pselstr;
+    /*! \brief
+     * Position of the result of the current Bison action.
+     *
+     * This identifies the part of \a pselstr that corresponds to the
+     * subselection that is currently being reduced by Bison.
+     */
+    gmx::SelectionLocation currentLocation;
+
+    //! Stack of methods in which parameters should be looked up.
+    struct gmx_ana_selmethod_t** mstack;
+    //! Index of the top of the stack in \a mstack.
+    int msp;
+    //! Number of elements allocated for \a mstack.
+    int mstack_alloc;
+
+    //! Number of END_OF_METHOD tokens to return before \a nextparam.
+    int neom;
+    //! Parameter symbol to return before resuming scanning.
+    struct gmx_ana_selparam_t* nextparam;
+    //! Whether \a nextparam was a boolean parameter with a 'no' prefix.
+    bool bBoolNo;
+    /*! \brief
+     * Method symbol to return before resuming scanning
+     *
+     * Only used when \p nextparam is NULL.
+     */
+    const gmx::SelectionParserSymbol* nextMethodSymbol;
+    //! Used to track whether the previous token was a position modifier.
+    int prev_pos_kw;
+
+    //! Whether the 'of' keyword is acceptable as the next token.
+    bool bMatchOf;
+    //! Whether boolean values (yes/no/on/off) are acceptable as the next token.
+    bool bMatchBool;
+    //! Whether the next token starts a new selection.
+    bool bCmdStart;
+
+    //! Whether an external buffer is set for the scanner.
+    bool bBuffer;
+    //! The current buffer for the scanner.
+    YY_BUFFER_STATE buffer;
+} gmx_sel_lexer_t;
+
+/* Because Flex defines yylval, yytext, and yyleng as macros,
+ * and this file is included from scanner.l,
+ * we cannot have them here as parameter names... */
+/** Internal function for cases where several tokens need to be returned. */
+int _gmx_sel_lexer_process_pending(YYSTYPE* /*yylval*/, YYLTYPE*, gmx_sel_lexer_t* state);
+/** Internal function that processes identifier tokens. */
+int _gmx_sel_lexer_process_identifier(YYSTYPE* /*yylval*/,
+                                      YYLTYPE*,
+                                      char* /*yytext*/,
+                                      size_t /*yyleng*/,
+                                      gmx_sel_lexer_t* state);
+/** Internal function to add a token to the pretty-printed selection text. */
+void _gmx_sel_lexer_add_token(YYLTYPE*, const char* str, int len, gmx_sel_lexer_t* state);
+
+#endif
diff --git a/src/include/gromacs/selection/selection.h b/src/include/gromacs/selection/selection.h
new file mode 100644 (file)
index 0000000..8d950d5
--- /dev/null
@@ -0,0 +1,753 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::Selection and supporting classes.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTION_H
+#define GMX_SELECTION_SELECTION_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/gmxassert.h"
+
+#include "position.h"
+#include "selectionenums.h"
+
+struct gmx_mtop_t;
+
+namespace gmx
+{
+
+class SelectionOptionStorage;
+class SelectionTreeElement;
+
+class AnalysisNeighborhoodPositions;
+class Selection;
+class SelectionPosition;
+
+//! Container of selections used in public selection interfaces.
+typedef std::vector<Selection> SelectionList;
+
+namespace internal
+{
+
+/*! \internal
+ * \brief
+ * Internal data for a single selection.
+ *
+ * This class is internal to the selection module, but resides in a public
+ * header because of efficiency reasons: it allows frequently used access
+ * methods in \ref Selection to be inlined.
+ *
+ * Methods in this class do not throw unless otherwise specified.
+ *
+ * \ingroup module_selection
+ */
+class SelectionData
+{
+public:
+    /*! \brief
+     * Creates a new selection object.
+     *
+     * \param[in] elem   Root of the evaluation tree for this selection.
+     * \param[in] selstr String that was parsed to produce this selection.
+     * \throws    std::bad_alloc if out of memory.
+     */
+    SelectionData(SelectionTreeElement* elem, const char* selstr);
+    ~SelectionData();
+
+    //! Returns the name for this selection.
+    const char* name() const { return name_.c_str(); }
+    //! Returns the string that was parsed to produce this selection.
+    const char* selectionText() const { return selectionText_.c_str(); }
+    //! Returns true if the size of the selection (posCount()) is dynamic.
+    bool isDynamic() const { return bDynamic_; }
+    //! Returns the type of positions in the selection.
+    e_index_t type() const { return rawPositions_.m.type; }
+    //! Returns true if the selection only contains positions with a single atom each.
+    bool hasOnlyAtoms() const { return type() == INDEX_ATOM; }
+    //! Returns `true` if the atom indices in the selection are in ascending order.
+    bool hasSortedAtomIndices() const;
+
+    //! Number of positions in the selection.
+    int posCount() const { return rawPositions_.count(); }
+    //! Returns the root of the evaluation tree for this selection.
+    SelectionTreeElement& rootElement() { return rootElement_; }
+
+    //! Returns whether the covered fraction can change between frames.
+    bool isCoveredFractionDynamic() const { return bDynamicCoveredFraction_; }
+
+    //! Returns true if the given flag is set.
+    bool hasFlag(SelectionFlag flag) const { return flags_.test(flag); }
+    //! Sets the flags for this selection.
+    void setFlags(SelectionFlags flags) { flags_ = flags; }
+    //! Returns the current flags.
+    SelectionFlags flags() const { return flags_; }
+
+    //! \copydoc Selection::initCoveredFraction()
+    bool initCoveredFraction(e_coverfrac_t type);
+
+    /*! \brief
+     * Updates the name of the selection if missing.
+     *
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * If selections get their value from a group reference that cannot be
+     * resolved during parsing, the name is final only after group
+     * references have been resolved.
+     *
+     * This function is called by SelectionCollection::setIndexGroups().
+     */
+    void refreshName();
+    /*! \brief
+     * Computes total masses and charges for all selection positions.
+     *
+     * \param[in] top   Topology information.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * For dynamic selections, the values need to be updated after each
+     * evaluation with refreshMassesAndCharges().
+     * This is done by SelectionEvaluator.
+     *
+     * This function is called by SelectionCompiler.
+     *
+     * Strong exception safety guarantee.
+     */
+    void initializeMassesAndCharges(const gmx_mtop_t* top);
+    /*! \brief
+     * Updates masses and charges after dynamic selection has been
+     * evaluated.
+     *
+     * \param[in] top   Topology information.
+     *
+     * Called by SelectionEvaluator.
+     */
+    void refreshMassesAndCharges(const gmx_mtop_t* top);
+    /*! \brief
+     * Updates the covered fraction after a selection has been evaluated.
+     *
+     * Called by SelectionEvaluator.
+     */
+    void updateCoveredFractionForFrame();
+    /*! \brief
+     * Computes average covered fraction after all frames have been evaluated.
+     *
+     * \param[in] nframes  Number of frames that have been evaluated.
+     *
+     * \p nframes should be equal to the number of calls to
+     * updateCoveredFractionForFrame().
+     * Called by SelectionEvaluator::evaluateFinal().
+     */
+    void computeAverageCoveredFraction(int nframes);
+    /*! \brief
+     * Restores position information to state it was in after compilation.
+     *
+     * \param[in] top   Topology information.
+     *
+     * Depends on SelectionCompiler storing the original atoms in the
+     * \a rootElement_ object.
+     * Called by SelectionEvaluator::evaluateFinal().
+     */
+    void restoreOriginalPositions(const gmx_mtop_t* top);
+
+private:
+    //! Name of the selection.
+    std::string name_;
+    //! The actual selection string.
+    std::string selectionText_;
+    //! Low-level representation of selected positions.
+    gmx_ana_pos_t rawPositions_;
+    //! Total masses for the current positions.
+    std::vector<real> posMass_;
+    //! Total charges for the current positions.
+    std::vector<real> posCharge_;
+    SelectionFlags    flags_;
+    //! Root of the selection evaluation tree.
+    SelectionTreeElement& rootElement_;
+    //! Type of the covered fraction.
+    e_coverfrac_t coveredFractionType_;
+    //! Covered fraction of the selection for the current frame.
+    real coveredFraction_;
+    //! The average covered fraction (over the trajectory).
+    real averageCoveredFraction_;
+    //! true if the value can change as a function of time.
+    bool bDynamic_;
+    //! true if the covered fraction depends on the frame.
+    bool bDynamicCoveredFraction_;
+
+    /*! \brief
+     * Needed to wrap access to information.
+     */
+    friend class gmx::Selection;
+    /*! \brief
+     * Needed for proper access to position information.
+     */
+    friend class gmx::SelectionPosition;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(SelectionData);
+};
+
+} // namespace internal
+
+/*! \brief
+ * Provides access to a single selection.
+ *
+ * This class provides a public interface for accessing selection information.
+ * General information about the selection can be accessed with methods name(),
+ * selectionText(), isDynamic(), and type().  The first three can be accessed
+ * any time after the selection has been parsed, and type() can be accessed
+ * after the selection has been compiled.
+ *
+ * There are a few methods that can be used to change the behavior of the
+ * selection.  setEvaluateVelocities() and setEvaluateForces() can be called
+ * before the selection is compiled to request evaluation of velocities and/or
+ * forces in addition to coordinates.
+ *
+ * Each selection is made of a set of positions.  Each position has associated
+ * coordinates, and possibly velocities and forces if they have been requested
+ * and are available.  It also has a set of atoms associated with it; typically
+ * the coordinates are the center-of-mass or center-of-geometry coordinates for
+ * that set of atoms.  To access the number of positions in the selection, use
+ * posCount().  To access individual positions, use position().
+ * See SelectionPosition for details of how to use individual positions.
+ * setOriginalId() can be used to adjust the return value of
+ * SelectionPosition::mappedId(); see that method for details.
+ *
+ * It is also possible to access the list of atoms that make up all the
+ * positions directly: atomCount() returns the total number of atoms in the
+ * selection and atomIndices() an array of their indices.
+ * Similarly, it is possible to access the coordinates and other properties
+ * of the positions as continuous arrays through coordinates(), velocities(),
+ * forces(), masses(), charges(), refIds(), and mappedIds().
+ *
+ * Both positions and atoms can be accessed after the selection has been
+ * compiled.  For dynamic selections, the return values of these methods change
+ * after each evaluation to reflect the situation for the current frame.
+ * Before any frame has been evaluated, these methods return the maximal set
+ * to which the selection can evaluate.
+ *
+ * There are two possible modes for how positions for dynamic selections are
+ * handled.  In the default mode, posCount() can change, and for each frame,
+ * only the positions that are selected in that frame can be accessed.  In a
+ * masked mode, posCount() remains constant, i.e., the positions are always
+ * evaluated for the maximal set, and SelectionPosition::selected() is used to
+ * determine whether a position is selected for a frame.  The masked mode can
+ * be requested with SelectionOption::dynamicMask().
+ *
+ * The class also provides methods for printing out information: printInfo()
+ * and printDebugInfo().  These are mainly for internal use by Gromacs.
+ *
+ * This class works like a pointer type: copying and assignment is lightweight,
+ * and all copies work interchangeably, accessing the same internal data.
+ *
+ * Methods in this class do not throw.
+ *
+ * \see SelectionPosition
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class Selection
+{
+public:
+    /*! \brief
+     * Creates a selection wrapper that has no associated selection.
+     *
+     * Any attempt to call methods in the object before a selection is
+     * assigned results in undefined behavior.
+     * isValid() returns `false` for the selection until it is initialized.
+     */
+    Selection() : sel_(nullptr) {}
+    /*! \brief
+     * Creates a new selection object.
+     *
+     * \param  sel  Selection data to wrap.
+     *
+     * Only for internal use by the selection module.
+     */
+    explicit Selection(internal::SelectionData* sel) : sel_(sel) {}
+
+    //! Returns whether the selection object is initialized.
+    bool isValid() const { return sel_ != nullptr; }
+
+    //! Returns whether two selection objects wrap the same selection.
+    bool operator==(const Selection& other) const { return sel_ == other.sel_; }
+    //! Returns whether two selection objects wrap different selections.
+    bool operator!=(const Selection& other) const { return !operator==(other); }
+
+    //! Returns the name of the selection.
+    const char* name() const { return data().name(); }
+    //! Returns the string that was parsed to produce this selection.
+    const char* selectionText() const { return data().selectionText(); }
+    //! Returns true if the size of the selection (posCount()) is dynamic.
+    bool isDynamic() const { return data().isDynamic(); }
+    //! Returns the type of positions in the selection.
+    e_index_t type() const { return data().type(); }
+    //! Returns true if the selection only contains positions with a single atom each.
+    bool hasOnlyAtoms() const { return data().hasOnlyAtoms(); }
+    //! Returns `true` if the atom indices in the selection are in ascending order.
+    bool hasSortedAtomIndices() const { return data().hasSortedAtomIndices(); }
+
+    //! Total number of atoms in the selection.
+    int atomCount() const { return data().rawPositions_.m.mapb.nra; }
+    //! Returns atom indices of all atoms in the selection.
+    ArrayRef<const int> atomIndices() const
+    {
+        return constArrayRefFromArray(sel_->rawPositions_.m.mapb.a, sel_->rawPositions_.m.mapb.nra);
+    }
+    //! Number of positions in the selection.
+    int posCount() const { return data().posCount(); }
+    //! Access a single position.
+    SelectionPosition position(int i) const;
+    //! Returns coordinates for this selection as a continuous array.
+    ArrayRef<const rvec> coordinates() const
+    {
+        return constArrayRefFromArray(data().rawPositions_.x, posCount());
+    }
+    //! Returns whether velocities are available for this selection.
+    bool hasVelocities() const { return data().rawPositions_.v != nullptr; }
+    /*! \brief
+     * Returns velocities for this selection as a continuous array.
+     *
+     * Must not be called if hasVelocities() returns false.
+     */
+    ArrayRef<const rvec> velocities() const
+    {
+        GMX_ASSERT(hasVelocities(), "Velocities accessed, but unavailable");
+        return constArrayRefFromArray(data().rawPositions_.v, posCount());
+    }
+    //! Returns whether forces are available for this selection.
+    bool hasForces() const { return sel_->rawPositions_.f != nullptr; }
+    /*! \brief
+     * Returns forces for this selection as a continuous array.
+     *
+     * Must not be called if hasForces() returns false.
+     */
+    ArrayRef<const rvec> forces() const
+    {
+        GMX_ASSERT(hasForces(), "Forces accessed, but unavailable");
+        return constArrayRefFromArray(data().rawPositions_.f, posCount());
+    }
+    //! Returns masses for this selection as a continuous array.
+    ArrayRef<const real> masses() const
+    {
+        // posMass_ may have more entries than posCount() in the case of
+        // dynamic selections that don't have a topology
+        // (and thus the masses and charges are fixed).
+        GMX_ASSERT(data().posMass_.size() >= static_cast<size_t>(posCount()),
+                   "Internal inconsistency");
+        return makeArrayRef(data().posMass_).subArray(0, posCount());
+    }
+    //! Returns charges for this selection as a continuous array.
+    ArrayRef<const real> charges() const
+    {
+        // posCharge_ may have more entries than posCount() in the case of
+        // dynamic selections that don't have a topology
+        // (and thus the masses and charges are fixed).
+        GMX_ASSERT(data().posCharge_.size() >= static_cast<size_t>(posCount()),
+                   "Internal inconsistency");
+        return makeArrayRef(data().posCharge_).subArray(0, posCount());
+    }
+    /*! \brief
+     * Returns reference IDs for this selection as a continuous array.
+     *
+     * \see SelectionPosition::refId()
+     */
+    ArrayRef<const int> refIds() const
+    {
+        return constArrayRefFromArray(data().rawPositions_.m.refid, posCount());
+    }
+    /*! \brief
+     * Returns mapped IDs for this selection as a continuous array.
+     *
+     * \see SelectionPosition::mappedId()
+     */
+    ArrayRef<const int> mappedIds() const
+    {
+        return constArrayRefFromArray(data().rawPositions_.m.mapid, posCount());
+    }
+
+    //! Returns whether the covered fraction can change between frames.
+    bool isCoveredFractionDynamic() const { return data().isCoveredFractionDynamic(); }
+    //! Returns the covered fraction for the current frame.
+    real coveredFraction() const { return data().coveredFraction_; }
+
+    /*! \brief
+     * Allows passing a selection directly to neighborhood searching.
+     *
+     * When initialized this way, AnalysisNeighborhoodPair objects return
+     * indices that can be used to index the selection positions with
+     * position().
+     *
+     * Works exactly like if AnalysisNeighborhoodPositions had a
+     * constructor taking a Selection object as a parameter.
+     * See AnalysisNeighborhoodPositions for rationale and additional
+     * discussion.
+     */
+    operator AnalysisNeighborhoodPositions() const;
+
+    /*! \brief
+     * Initializes information about covered fractions.
+     *
+     * \param[in] type Type of covered fraction required.
+     * \returns   true if the covered fraction can be calculated for the
+     *      selection.
+     */
+    bool initCoveredFraction(e_coverfrac_t type) { return data().initCoveredFraction(type); }
+    /*! \brief
+     * Sets whether this selection evaluates velocities for positions.
+     *
+     * \param[in] bEnabled  If true, velocities are evaluated.
+     *
+     * If you request the evaluation, but then evaluate the selection for
+     * a frame that does not contain velocity information, results are
+     * undefined.
+     *
+     * \todo
+     * Implement it such that in the above case, hasVelocities() will
+     * return false for such frames.
+     *
+     * Does not throw.
+     */
+    void setEvaluateVelocities(bool bEnabled)
+    {
+        data().flags_.set(efSelection_EvaluateVelocities, bEnabled);
+    }
+    /*! \brief
+     * Sets whether this selection evaluates forces for positions.
+     *
+     * \param[in] bEnabled  If true, forces are evaluated.
+     *
+     * If you request the evaluation, but then evaluate the selection for
+     * a frame that does not contain force information, results are
+     * undefined.
+     *
+     * Does not throw.
+     */
+    void setEvaluateForces(bool bEnabled)
+    {
+        data().flags_.set(efSelection_EvaluateForces, bEnabled);
+    }
+
+    /*! \brief
+     * Sets the ID for the \p i'th position for use with
+     * SelectionPosition::mappedId().
+     *
+     * \param[in] i  Zero-based index
+     * \param[in] id Identifier to set.
+     *
+     * This method is not part of SelectionPosition because that interface
+     * only provides access to const data by design.
+     *
+     * This method can only be called after compilation, before the
+     * selection has been evaluated for any frame.
+     *
+     * \see SelectionPosition::mappedId()
+     */
+    void setOriginalId(int i, int id);
+    /*! \brief
+     * Inits the IDs for use with SelectionPosition::mappedId() for
+     * grouping.
+     *
+     * \param[in] top   Topology information
+     *     (can be NULL if not required for \p type).
+     * \param[in] type  Type of groups to generate.
+     * \returns   Number of groups that were present in the selection.
+     * \throws    InconsistentInputError if the selection positions cannot
+     *     be assigned to groups of the given type.
+     *
+     * If `type == INDEX_ATOM`, the IDs are initialized to 0, 1, 2, ...,
+     * and the return value is the number of positions.
+     * If `type == INDEX_ALL`, all the IDs are initialized to 0, and the
+     * return value is one.
+     * If `type == INDEX_RES` or `type == INDEX_MOL`, the first position
+     * will get ID 0, and all following positions that belong to the same
+     * residue/molecule will get the same ID.  The first position that
+     * belongs to a different residue/molecule will get ID 1, and so on.
+     * If some position contains atoms from multiple residues/molecules,
+     * i.e., the mapping is ambiguous, an exception is thrown.
+     * The return value is the number of residues/molecules that are
+     * present in the selection positions.
+     *
+     * This method is useful if the calling code needs to group the
+     * selection, e.g., for computing aggregate properties for each residue
+     * or molecule.  It can then use this method to initialize the
+     * appropriate grouping, use the return value to allocate a
+     * sufficiently sized buffer to store the aggregated values, and then
+     * use SelectionPosition::mappedId() to identify the location where to
+     * aggregate to.
+     *
+     * \see setOriginalId()
+     * \see SelectionPosition::mappedId()
+     */
+    int initOriginalIdsToGroup(const gmx_mtop_t* top, e_index_t type);
+
+    /*! \brief
+     * Prints out one-line description of the selection.
+     *
+     * \param[in] fp      Where to print the information.
+     *
+     * The output contains the name of the selection, the number of atoms
+     * and the number of positions, and indication of whether the selection
+     * is dynamic.
+     */
+    void printInfo(FILE* fp) const;
+    /*! \brief
+     * Prints out extended information about the selection for debugging.
+     *
+     * \param[in] fp      Where to print the information.
+     * \param[in] nmaxind Maximum number of values to print in lists
+     *      (-1 = print all).
+     */
+    void printDebugInfo(FILE* fp, int nmaxind) const;
+
+private:
+    internal::SelectionData& data()
+    {
+        GMX_ASSERT(sel_ != nullptr, "Attempted to access uninitialized selection");
+        return *sel_;
+    }
+    const internal::SelectionData& data() const
+    {
+        GMX_ASSERT(sel_ != nullptr, "Attempted to access uninitialized selection");
+        return *sel_;
+    }
+
+    /*! \brief
+     * Pointer to internal data for the selection.
+     *
+     * The memory for this object is managed by a SelectionCollection
+     * object, and the \ref Selection class simply provides a public
+     * interface for accessing the data.
+     */
+    internal::SelectionData* sel_;
+
+    /*! \brief
+     * Needed to access the data to adjust flags.
+     */
+    friend class SelectionOptionStorage;
+};
+
+/*! \brief
+ * Provides access to information about a single selected position.
+ *
+ * Each position has associated coordinates, and possibly velocities and forces
+ * if they have been requested and are available.  It also has a set of atoms
+ * associated with it; typically the coordinates are the center-of-mass or
+ * center-of-geometry coordinates for that set of atoms.  It is possible that
+ * there are not atoms associated if the selection has been provided as a fixed
+ * position.
+ *
+ * After the selection has been compiled, but not yet evaluated, the contents
+ * of the coordinate, velocity and force vectors are undefined.
+ *
+ * Default copy constructor and assignment operators are used, and work as
+ * intended: the copy references the same position and works identically.
+ *
+ * Methods in this class do not throw.
+ *
+ * \see Selection
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class SelectionPosition
+{
+public:
+    /*! \brief
+     * Constructs a wrapper object for given selection position.
+     *
+     * \param[in] sel    Selection from which the position is wrapped.
+     * \param[in] index  Zero-based index of the position to wrap.
+     *
+     * Asserts if \p index is out of range.
+     *
+     * Only for internal use of the library.  To obtain a SelectionPosition
+     * object in other code, use Selection::position().
+     */
+    SelectionPosition(const internal::SelectionData& sel, int index) : sel_(&sel), i_(index)
+    {
+        GMX_ASSERT(index >= 0 && index < sel.posCount(), "Invalid selection position index");
+    }
+
+    /*! \brief
+     * Returns type of this position.
+     *
+     * Currently always returns the same as Selection::type().
+     */
+    e_index_t type() const { return sel_->type(); }
+    //! Returns coordinates for this position.
+    const rvec& x() const { return sel_->rawPositions_.x[i_]; }
+    /*! \brief
+     * Returns velocity for this position.
+     *
+     * Must not be called if Selection::hasVelocities() returns false.
+     */
+    const rvec& v() const
+    {
+        GMX_ASSERT(sel_->rawPositions_.v != nullptr, "Velocities accessed, but unavailable");
+        return sel_->rawPositions_.v[i_];
+    }
+    /*! \brief
+     * Returns force for this position.
+     *
+     * Must not be called if Selection::hasForces() returns false.
+     */
+    const rvec& f() const
+    {
+        GMX_ASSERT(sel_->rawPositions_.f != nullptr, "Forces accessed, but unavailable");
+        return sel_->rawPositions_.f[i_];
+    }
+    /*! \brief
+     * Returns total mass for this position.
+     *
+     * Returns the total mass of atoms that make up this position.
+     * If there are no atoms associated or masses are not available,
+     * returns unity.
+     */
+    real mass() const { return sel_->posMass_[i_]; }
+    /*! \brief
+     * Returns total charge for this position.
+     *
+     * Returns the sum of charges of atoms that make up this position.
+     * If there are no atoms associated or charges are not available,
+     * returns zero.
+     */
+    real charge() const { return sel_->posCharge_[i_]; }
+    //! Returns the number of atoms that make up this position.
+    int atomCount() const
+    {
+        return sel_->rawPositions_.m.mapb.index[i_ + 1] - sel_->rawPositions_.m.mapb.index[i_];
+    }
+    //! Return atom indices that make up this position.
+    ArrayRef<const int> atomIndices() const
+    {
+        const int* atoms = sel_->rawPositions_.m.mapb.a;
+        if (atoms == nullptr)
+        {
+            return ArrayRef<const int>();
+        }
+        const int first = sel_->rawPositions_.m.mapb.index[i_];
+        return constArrayRefFromArray(&atoms[first], atomCount());
+    }
+    /*! \brief
+     * Returns whether this position is selected in the current frame.
+     *
+     * The return value is equivalent to \c refid() == -1.  Returns always
+     * true if SelectionOption::dynamicMask() has not been set.
+     *
+     * \see refId()
+     */
+    bool selected() const { return refId() >= 0; }
+    /*! \brief
+     * Returns reference ID for this position.
+     *
+     * For dynamic selections, this provides means to associate positions
+     * across frames.  After compilation, these IDs are consequently
+     * numbered starting from zero.  For each frame, the ID then reflects
+     * the location of the position in the original array of positions.
+     * If SelectionOption::dynamicMask() has been set for the parent
+     * selection, the IDs for positions not present in the current
+     * selection are set to -1, otherwise they are removed completely.
+     *
+     * Example:
+     * If a dynamic selection consists of at most three positions, after
+     * compilation refId() will return 0, 1, 2 for them, respectively.
+     * If for a particular frame, only the first and the third are present,
+     * refId() will return 0, 2.
+     * If SelectionOption::dynamicMask() has been set, all three positions
+     * can be accessed also for that frame and refId() will return 0, -1,
+     * 2.
+     */
+    int refId() const { return sel_->rawPositions_.m.refid[i_]; }
+    /*! \brief
+     * Returns mapped ID for this position.
+     *
+     * Returns ID of the position that corresponds to that set with
+     * Selection::setOriginalId().
+     *
+     * If for an array \c id, \c setOriginalId(i, id[i]) has been called
+     * for each \c i, then it always holds that
+     * \c mappedId()==id[refId()].
+     *
+     * Selection::setOriginalId() has not been called, the default values
+     * are dependent on type():
+     *  - ::INDEX_ATOM: atom indices
+     *  - ::INDEX_RES:  residue indices
+     *  - ::INDEX_MOL:  molecule indices
+     *  .
+     * All the default values are zero-based.
+     */
+    int mappedId() const { return sel_->rawPositions_.m.mapid[i_]; }
+
+    /*! \brief
+     * Allows passing a selection position directly to neighborhood searching.
+     *
+     * When initialized this way, AnalysisNeighborhoodPair objects return
+     * the index that can be used to access this position using
+     * Selection::position().
+     *
+     * Works exactly like if AnalysisNeighborhoodPositions had a
+     * constructor taking a SelectionPosition object as a parameter.
+     * See AnalysisNeighborhoodPositions for rationale and additional
+     * discussion.
+     */
+    operator AnalysisNeighborhoodPositions() const;
+
+private:
+    const internal::SelectionData* sel_;
+    int                            i_;
+};
+
+
+inline SelectionPosition Selection::position(int i) const
+{
+    return SelectionPosition(data(), i);
+}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/selectioncollection.h b/src/include/gromacs/selection/selectioncollection.h
new file mode 100644 (file)
index 0000000..e02ed9f
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2016,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::SelectionCollection.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONCOLLECTION_H
+#define GMX_SELECTION_SELECTIONCOLLECTION_H
+
+#include <cstdio>
+
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "gromacs/selection/selection.h" // For gmx::SelectionList
+
+struct gmx_ana_indexgrps_t;
+struct gmx_mtop_t;
+struct t_pbc;
+struct t_trxframe;
+
+namespace gmx
+{
+
+class IOptionsContainer;
+class SelectionEvaluator;
+class TextInputStream;
+class TextOutputStream;
+struct SelectionTopologyProperties;
+
+/*! \brief
+ * Collection of selections.
+ *
+ * This class is the main interface to the core of the selection engine.
+ * It is used to initialize and manage a collection of selections that share
+ * the same topology.  Selections within one collection can share variables and
+ * can be optimized together.  Selections from two different collections do not
+ * interact.
+ *
+ * The constructor creates an empty selection collection object.  To initialize
+ * the object, either call initOptions(), or both setReferencePosType() and
+ * setOutputPosType().  See these methods for more details on the
+ * initialization options.
+ *
+ * SelectionCollections can be copied. Copies retain the same pointers to external indices (if set)
+ * and the topology (if set), and are compiled if the copied collection is compiled. Selection
+ * objects created from a given SelectionCollection are tied only to the original collection, so
+ * a copy of a SelectionCollection will not update pre-existing Selections on evaluate() calls.
+ *
+ * After setting the default values, one or more selections can be parsed with
+ * one or more calls to parseInteractive(), parseFromStdin(), parseFromFile(), and/or
+ * parseFromString().  After all selections are parsed, the topology must be
+ * set with setTopology() unless requiresTopology() returns false (the topology
+ * can also be set earlier).
+ * setIndexGroups() must also be called if external index group references are
+ * used in the selections; it can be called at any point before compile().
+ * Once all selections are parsed, they must be compiled all at once using
+ * compile().
+ *
+ * After compilation, dynamic selections have the maximum number of atoms they
+ * can evaluate to, but positions have undefined values (see \ref Selection and
+ * SelectionPosition).  evaluate() can be used to update the selections for a
+ * new frame.  evaluateFinal() can be called after all the frames have been
+ * processed to restore the selection values back to the ones they were after
+ * compile().
+ *
+ * At any point, requiresTopology() can be called to see whether the
+ * information provided so far requires loading the topology.
+ * Similarly, requiresIndexGroups() tells whether external index groups are
+ * requires.
+ * printTree() can be used to print the internal representation of the
+ * selections (mostly useful for debugging).
+ *
+ * Note that for trajectory analysis using TrajectoryAnalysisModule, the
+ * SelectionCollection object is managed by Gromacs, and \ref Selection objects
+ * are obtained from SelectionOption.
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class SelectionCollection
+{
+public:
+    //! Flag for initOptions() to select how to behave with -seltype option.
+    enum SelectionTypeOption
+    {
+        /*! \brief
+         * Add the option for the user to select default value for
+         * setOutputPosType().
+         */
+        IncludeSelectionTypeOption,
+        /*! \brief
+         * Do not add the option, selections will always select atoms by
+         * default.
+         */
+        AlwaysAtomSelections
+    };
+
+    /*! \brief
+     * Creates an empty selection collection.
+     *
+     * \throws  std::bad_alloc if out of memory.
+     */
+    SelectionCollection();
+    ~SelectionCollection();
+
+    SelectionCollection(const SelectionCollection& rhs);
+    SelectionCollection& operator=(SelectionCollection rhs);
+    void                 swap(SelectionCollection& rhs);
+
+    /*! \brief
+     * Initializes options for setting global properties on the collection.
+     *
+     * \param[in,out] options Options object to initialize.
+     * \param[in]     selectionTypeOption
+     *     Whether to add option to influence setOutputPosType().
+     * \throws        std::bad_alloc if out of memory.
+     *
+     * Adds options to \p options that can be used to set the default
+     * position types (see setReferencePosType() and setOutputPosType())
+     * and debugging flags.
+     */
+    void initOptions(IOptionsContainer* options, SelectionTypeOption selectionTypeOption);
+
+    /*! \brief
+     * Sets the default reference position handling for a selection
+     * collection.
+     *
+     * \param[in]     type      Default selection reference position type
+     *     (one of the strings acceptable for
+     *     PositionCalculationCollection::typeFromEnum()).
+     * \throws  InternalError if \p type is invalid.
+     *
+     * Should be called before calling the parser functions, unless
+     * initOptions() has been called.  In the latter case, can still be
+     * used to override the default value (before initOptions() is called)
+     * and/or the value provided through the Options object.
+     *
+     * Strong exception safety.
+     */
+    void setReferencePosType(const char* type);
+    /*! \brief
+     * Sets the default reference position handling for a selection
+     * collection.
+     *
+     * \param[in]     type      Default selection output position type
+     *     (one of the strings acceptable for
+     *     PositionCalculationCollection::typeFromEnum()).
+     * \throws  InternalError if \p type is invalid.
+     *
+     * Should be called before calling the parser functions, unless
+     * initOptions() has been called.  In the latter case, can still be
+     * used to override the default value (before initOptions() is called)
+     * and/or the value provided through the Options object.
+     *
+     * Strong exception safety.
+     */
+    void setOutputPosType(const char* type);
+    /*! \brief
+     * Sets the debugging level for the selection collection.
+     *
+     * \param[in]   debugLevel  Debug level to set (0 = no debug
+     *      information).
+     *
+     * initOptions() creates debugging options that can also be used to set
+     * the debug level.  These are normally hidden, but if this method is
+     * called before initOptions() with a non-zero \p debugLevel, they are
+     * made visible.
+     *
+     * Mostly useful for debugging tools.
+     *
+     * Does not throw.
+     */
+    void setDebugLevel(int debugLevel);
+
+    /*! \brief
+     * Returns what topology information is required for evaluation.
+     *
+     * \returns What topology information is required for compiling and/or
+     *     evaluating the selections in the collection.
+     *
+     * Before the parser functions have been called, the return value is
+     * based just on the position types set.
+     * After parser functions have been called, the return value also takes
+     * into account the selection keywords used.
+     * After the compiler has been called, the return value is final and
+     * also considers possible force evaluation requested for the
+     * selections.
+     *
+     * Does not throw.
+     */
+    SelectionTopologyProperties requiredTopologyProperties() const;
+    /*! \brief
+     * Returns true if the collection requires external index groups.
+     *
+     * \returns true if any selection has an unresolved index group reference.
+     *
+     * The return value is `false` after setIndexGroups() has been called.
+     *
+     * Does not throw.
+     */
+    bool requiresIndexGroups() const;
+    /*! \brief
+     * Sets the topology for the collection.
+     *
+     * \param[in]     top       Topology data.
+     * \param[in]     natoms    Number of atoms. If <=0, the number of
+     *      atoms in the topology is used.
+     *
+     * Either the topology must be provided, or \p natoms must be > 0.
+     *
+     * \p natoms determines the largest atom index that can be selected by
+     * the selection: even if the topology contains more atoms, they will
+     * not be selected.
+     *
+     * Does not throw currently, but this is subject to change when more
+     * underlying code is converted to C++.
+     */
+    void setTopology(const gmx_mtop_t* top, int natoms);
+    /*! \brief
+     * Sets the external index groups to use for the selections.
+     *
+     * \param[in]  grps  Index groups to use for the selections.
+     * \throws  std::bad_alloc if out of memory.
+     * \throws  InconsistentInputError if a group reference cannot be resolved.
+     *
+     * Only the first call to this method can have a non-NULL \p grps.
+     * At this point, any selections that have already been provided are
+     * searched for references to external groups, and the references are
+     * replaced by the contents of the groups.  If any referenced group
+     * cannot be found in \p grps (or if \p grps is NULL and there are any
+     * references), InconsistentInputError is thrown.
+     *
+     * The selection collection keeps a reference to \p grps until this
+     * method is called with a NULL \p grps.
+     * If this method is not called before compile(), it is automatically
+     * called as setIndexGroups(NULL).
+     */
+    void setIndexGroups(gmx_ana_indexgrps_t* grps);
+    /*! \brief
+     * Parses selection(s) from standard input.
+     *
+     * \param[in]  count    Number of selections to parse
+     *      (if -1, parse as many as provided by the user).
+     * \param[in]  bInteractive Whether the parser should behave
+     *      interactively.
+     * \param[in]  context  Context to print for interactive input.
+     * \returns    Vector of parsed selections.
+     * \throws     std::bad_alloc if out of memory.
+     * \throws     InvalidInputError if there is a parsing error
+     *      (an interactive parser only throws this if too few selections
+     *      are provided and the user forced the end of input).
+     *
+     * The returned objects remain valid for the lifetime of
+     * the selection collection.
+     * Some information about the selections only becomes available once
+     * compile() has been called; see \ref Selection.
+     *
+     * The string provided to \p context should be such that it can replace
+     * the three dots in "Specify selections ...:".  It can be empty.
+     */
+    SelectionList parseFromStdin(int count, bool bInteractive, const std::string& context);
+    /*! \brief
+     * Parses selection(s) interactively using provided streams.
+     *
+     * \param[in]  count    Number of selections to parse
+     *      (if -1, parse as many as provided by the user).
+     * \param[in]  inputStream  Stream to use for input.
+     * \param[in]  outputStream Stream to use for output
+     *      (if NULL, the parser runs non-interactively and does not
+     *      produce any status messages).
+     * \param[in]  context  Context to print for interactive input.
+     * \returns    Vector of parsed selections.
+     * \throws     std::bad_alloc if out of memory.
+     * \throws     InvalidInputError if there is a parsing error
+     *      (an interactive parser only throws this if too few selections
+     *      are provided and the user forced the end of input).
+     *
+     * Works the same as parseFromStdin(), except that the caller can
+     * provide streams to use instead of `stdin` and `stderr`.
+     *
+     * Mainly usable for unit testing interactive input.
+     */
+    SelectionList parseInteractive(int                count,
+                                   TextInputStream*   inputStream,
+                                   TextOutputStream*  outputStream,
+                                   const std::string& context);
+    /*! \brief
+     * Parses selection(s) from a file.
+     *
+     * \param[in]  filename Name of the file to parse selections from.
+     * \returns    Vector of parsed selections.
+     * \throws     std::bad_alloc if out of memory.
+     * \throws     InvalidInputError if there is a parsing error.
+     *
+     * The returned objects remain valid for the lifetime of
+     * the selection collection.
+     * Some information about the selections only becomes available once
+     * compile() has been called; see \ref Selection.
+     */
+    SelectionList parseFromFile(const std::string& filename);
+    /*! \brief
+     * Parses selection(s) from a string.
+     *
+     * \param[in]  str      String to parse selections from.
+     * \returns    Vector of parsed selections.
+     * \throws     std::bad_alloc if out of memory.
+     * \throws     InvalidInputError if there is a parsing error.
+     *
+     * The returned objects remain valid for the lifetime of
+     * the selection collection.
+     * Some information about the selections only becomes available once
+     * compile() has been called; see \ref Selection.
+     */
+    SelectionList parseFromString(const std::string& str);
+    /*! \brief
+     * Prepares the selections for evaluation and performs optimizations.
+     *
+     * \throws  InconsistentInputError if topology is required but not set.
+     * \throws  InvalidInputError if setIndexGroups() has not been called
+     *      and there are index group references.
+     * \throws  unspecified if compilation fails (TODO: list/reduce these).
+     *
+     * Before compilation, selections should have been added to the
+     * collection using the parseFrom*() functions.
+     * The compiled selection collection can be passed to evaluate() to
+     * evaluate the selection for a frame.
+     * Before the compiled selection is evaluated, the selections indicate
+     * the maximal set of atoms/positions to which they can be evaluated;
+     * see \ref Selection.
+     *
+     * If an error occurs, the collection is cleared.
+     *
+     * The covered fraction information is initialized to ::CFRAC_NONE for
+     * all selections.
+     */
+    void compile();
+    /*! \brief
+     * Evaluates selections in the collection.
+     *
+     * \param[in] fr  Frame for which the evaluation should be carried out.
+     * \param[in] pbc PBC data, or NULL if no PBC should be used.
+     * \throws    unspeficied  Multiple possible exceptions to indicate
+     *      evaluation failure (TODO: enumerate).
+     */
+    void evaluate(t_trxframe* fr, t_pbc* pbc);
+    /*! \brief
+     * Evaluates the largest possible index groups from dynamic selections.
+     *
+     * \param[in] nframes Total number of frames.
+     *
+     * This method restores the selections to the state they were after
+     * compile().
+     *
+     * \p nframes should equal the number of times evaluate() has been
+     * called.
+     *
+     * Does not throw.
+     */
+    void evaluateFinal(int nframes);
+    /*! \brief
+     * Retrieves a Selection handle for the selection with the given name
+     *
+     * @param selName name of the selection to return
+     * @return The selection with the given name, or nullopt if no such selection exists.
+     */
+    [[nodiscard]] std::optional<Selection> selection(std::string_view selName) const;
+    /*! \brief
+     * Prints a human-readable version of the internal selection element
+     * tree.
+     *
+     * \param[in] fp      File handle to receive the output.
+     * \param[in] bValues If true, the evaluated values of selection
+     *      elements are printed as well.
+     *
+     * The output is very techical, and intended for debugging purposes.
+     *
+     * Does not throw.
+     */
+    void printTree(FILE* fp, bool bValues) const;
+    /*! \brief
+     * Prints the selection strings into an XVGR file as comments.
+     *
+     * \param[in] fp   Output file.
+     *
+     * Does not throw.
+     */
+    void printXvgrInfo(FILE* fp) const;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+
+    // Needed for the compiler to freely modify the collection.
+    friend void compileSelection(SelectionCollection* coll);
+    // Needed for the evaluator to freely modify the collection.
+    friend class SelectionEvaluator;
+};
+
+void swap(SelectionCollection& lhs, SelectionCollection& rhs);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/selectioncollection_impl.h b/src/include/gromacs/selection/selectioncollection_impl.h
new file mode 100644 (file)
index 0000000..f5cec6b
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009-2016, The GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation class for gmx::SelectionCollection.
+ *
+ * This header also defines ::gmx_ana_selcollection_t, which is used in the old
+ * C code for handling selection collections.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONCOLLECTION_IMPL_H
+#define GMX_SELECTION_SELECTIONCOLLECTION_IMPL_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/onlinehelp/ihelptopic.h"
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/selection/selection.h" // For gmx::SelectionList
+#include "gromacs/selection/selectioncollection.h"
+
+#include "poscalc.h"
+#include "selelem.h"
+
+struct gmx_mtop_t;
+struct gmx_sel_mempool_t;
+struct t_pbc;
+struct t_trxframe;
+
+namespace gmx
+{
+
+//! Smart pointer for managing an internal selection data object.
+typedef std::unique_ptr<internal::SelectionData> SelectionDataPointer;
+//! Container for storing a list of selections internally.
+typedef std::vector<SelectionDataPointer> SelectionDataList;
+
+class SelectionParserSymbolTable;
+struct SelectionTopologyProperties;
+
+} // namespace gmx
+
+/*! \internal \brief
+ * Information for a collection of selections.
+ *
+ * \ingroup module_selection
+ */
+struct gmx_ana_selcollection_t
+{
+    //! Position calculation collection used for selection position evaluation.
+    gmx::PositionCalculationCollection pcc;
+    //! Root of the selection element tree.
+    gmx::SelectionTreeElementPointer root;
+    /*! \brief
+     * Array of compiled selections.
+     *
+     * Has the responsibility of managing the memory for the contained objects,
+     * but note that gmx::Selection instances also hold pointers to the
+     * objects.
+     */
+    gmx::SelectionDataList sel;
+    /** Number of variables defined. */
+    int nvars;
+    /** Selection strings for variables. */
+    char** varstrs;
+
+    /** Topology for the collection. */
+    const gmx_mtop_t* top;
+    /** Index group that contains all the atoms. */
+    gmx_ana_index_t gall;
+    /** Memory pool used for selection evaluation. */
+    gmx_sel_mempool_t* mempool;
+    //! Parser symbol table.
+    // Never releases ownership.
+    std::unique_ptr<gmx::SelectionParserSymbolTable> symtab;
+    //! Root of help topic tree (NULL is no help yet requested).
+    gmx::HelpTopicPointer rootHelp;
+};
+
+namespace gmx
+{
+
+class ExceptionInitializer;
+
+/*! \internal \brief
+ * Private implemention class for SelectionCollection.
+ *
+ * \ingroup module_selection
+ */
+class SelectionCollection::Impl
+{
+public:
+    /*! \brief
+     * Creates a new selection collection.
+     *
+     * \throws  std::bad_alloc if out of memory.
+     */
+    Impl();
+    ~Impl();
+
+    /*! \brief
+     * Clears the symbol table of the selection collection.
+     *
+     * Does not throw.
+     */
+    void clearSymbolTable();
+    /*! \brief
+     * Replace group references by group contents.
+     *
+     * \param[in]    root    Root of selection tree to process.
+     * \param        errors  Object for reporting any error messages.
+     * \throws std::bad_alloc if out of memory.
+     *
+     * Recursively searches the selection tree for unresolved external
+     * references.  If one is found, finds the referenced group in
+     * \a grps_ and replaces the reference with a constant element that
+     * contains the atoms from the referenced group.  Any failures to
+     * resolve references are reported to \p errors.
+     */
+    void resolveExternalGroups(const gmx::SelectionTreeElementPointer& root, ExceptionInitializer* errors);
+
+    //! Whether forces have been requested for some selection.
+    bool areForcesRequested() const;
+    /*! \brief
+     * Returns topology properties needed for a certain position type.
+     */
+    static SelectionTopologyProperties requiredTopologyPropertiesForPositionType(const std::string& post,
+                                                                                 bool forces);
+
+    //! Describes the available debugging levels
+    enum class DebugLevel : int
+    {
+        //! No debugging
+        None,
+        //! Print selection trees after parsing and compilation
+        Basic,
+        //! As PrintCompiled, also print intermediate compilation trees
+        Compiled,
+        //! As PrintCompiled, also print the tree after evaluation
+        Evaluated,
+        //! Combine Compiled and Evaluated
+        Full,
+        //! Ends the enumeration
+        Count
+    };
+
+    //! Internal data, used for interfacing with old C code.
+    gmx_ana_selcollection_t sc_;
+    //! Default reference position type for selections.
+    std::string rpost_;
+    //! Default output position type for selections.
+    std::string spost_;
+    //! Atoms needed for evaluating the selections.
+    gmx_ana_index_t requiredAtoms_;
+    //! Debugging level for the collection.
+    DebugLevel debugLevel_;
+    //! Whether setIndexGroups() has been called.
+    bool bExternalGroupsSet_;
+    //! External index groups (can be NULL).
+    gmx_ana_indexgrps_t* grps_;
+};
+
+/*! \internal
+ * \brief
+ * Implements selection evaluation.
+ *
+ * This class is used to implement SelectionCollection::evaluate() and
+ * SelectionCollection::evaluateFinal().
+ *
+ * \ingroup module_selection
+ */
+class SelectionEvaluator
+{
+public:
+    SelectionEvaluator();
+
+    /*! \brief
+     * Evaluates selections in a collection.
+     */
+    void evaluate(SelectionCollection* sc, t_trxframe* fr, t_pbc* pbc);
+    /*! \brief
+     * Evaluates the final state for dynamic selections.
+     */
+    void evaluateFinal(SelectionCollection* sc, int nframes);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/selectionenums.h b/src/include/gromacs/selection/selectionenums.h
new file mode 100644 (file)
index 0000000..039423c
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares common types used in selections.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONENUMS_H
+#define GMX_SELECTION_SELECTIONENUMS_H
+
+#include "gromacs/utility/flags.h"
+
+/*! \brief
+ * Defines the type of covered fraction.
+ *
+ * \inpublicapi
+ */
+typedef enum
+{
+    CFRAC_NONE,      /**< No covered fraction (everything covered). */
+    CFRAC_SOLIDANGLE /**< Fraction of a solid (3D) angle covered. */
+} e_coverfrac_t;
+
+namespace gmx
+{
+
+/*! \cond internal */
+/*! \brief
+ * Flags for options.
+ *
+ * These flags are not part of the public interface, even though they are in an
+ * installed header.  They are needed in the implementation of SelectionOption.
+ */
+enum SelectionFlag : uint64_t
+{
+    efSelection_OnlyStatic = 1 << 0,
+    efSelection_OnlyAtoms  = 1 << 1,
+    efSelection_OnlySorted = 1 << 2,
+    //! Whether ::POS_MASKONLY should be used for output position evaluation.
+    efSelection_DynamicMask = 1 << 3,
+    //! If set, unconditionally empty selections result in compilation errors.
+    efSelection_DisallowEmpty = 1 << 4,
+    //! Whether velocities of output positions should be evaluated.
+    efSelection_EvaluateVelocities = 1 << 5,
+    //! Whether forces on output positions should be evaluated.
+    efSelection_EvaluateForces = 1 << 6,
+};
+
+//! Holds a collection of ::SelectionFlag values.
+typedef FlagsTemplate<SelectionFlag> SelectionFlags;
+//! \endcond
+
+/*! \brief
+ * Describes topology properties required for selection evaluation.
+ *
+ * See SelectionCollection::requiredTopologyProperties().
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+struct SelectionTopologyProperties
+{
+    //! Returns a property object that requires generic topology info.
+    static SelectionTopologyProperties topology()
+    {
+        return SelectionTopologyProperties(true, false);
+    }
+    //! Returns a property object that requires atom masses.
+    static SelectionTopologyProperties masses() { return SelectionTopologyProperties(true, true); }
+
+    //! Initializes properties that does not require anything.
+    SelectionTopologyProperties() : needsTopology(false), needsMasses(false) {}
+    //! Initializes properties with the given flags.
+    SelectionTopologyProperties(bool needsTopology, bool needsMasses) :
+        needsTopology(needsTopology), needsMasses(needsMasses)
+    {
+    }
+
+    //! Combines flags from another properties object to this.
+    void merge(const SelectionTopologyProperties& other)
+    {
+        needsTopology = needsTopology || other.needsTopology;
+        needsMasses   = needsMasses || other.needsMasses;
+    }
+    //! Whether all flags are `true` (for short-ciruiting logic).
+    bool hasAll() const { return needsTopology && needsMasses; }
+    //! Whether any flag is `true`.
+    bool hasAny() const { return needsTopology || needsMasses; }
+
+    //! Whether topology information is needed for selection evaluation.
+    bool needsTopology;
+    //! Whether atom masses are needed for selection evaluation.
+    bool needsMasses;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/selectionfileoption.h b/src/include/gromacs/selection/selectionfileoption.h
new file mode 100644 (file)
index 0000000..7394252
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::SelectionFileOption and gmx::SelectionFileOptionInfo.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONFILEOPTION_H
+#define GMX_SELECTION_SELECTIONFILEOPTION_H
+
+#include "gromacs/options/abstractoption.h"
+
+namespace gmx
+{
+
+class SelectionFileOptionInfo;
+class SelectionFileOptionStorage;
+class SelectionOptionManager;
+
+/*! \libinternal \brief
+ * Specifies a special option that provides selections from a file.
+ *
+ * This option is used internally by the command-line framework to implement
+ * file input for selections.  The option takes a file name, and reads it in
+ * using SelectionOptionManager::parseRequestedFromFile().  This means that
+ * selections from the file are assigned to selection options that have been
+ * explicitly provided without values earlier on the command line.
+ *
+ * Public methods in this class do not throw.
+ *
+ * \inlibraryapi
+ * \ingroup module_selection
+ */
+class SelectionFileOption : public AbstractOption
+{
+public:
+    //! OptionInfo subclass corresponding to this option type.
+    typedef SelectionFileOptionInfo InfoType;
+
+    //! Initializes an option with the given name.
+    explicit SelectionFileOption(const char* name);
+
+private:
+    AbstractOptionStorage* createStorage(const OptionManagerContainer& managers) const override;
+};
+
+/*! \libinternal \brief
+ * Wrapper class for accessing and modifying selection file option information.
+ *
+ * \inlibraryapi
+ * \ingroup module_selection
+ */
+class SelectionFileOptionInfo : public OptionInfo
+{
+public:
+    /*! \brief
+     * Creates option info object for given storage object.
+     *
+     * Does not throw.
+     */
+    explicit SelectionFileOptionInfo(SelectionFileOptionStorage* option);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/selectionfileoptionstorage.h b/src/include/gromacs/selection/selectionfileoptionstorage.h
new file mode 100644 (file)
index 0000000..f41b194
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares gmx::SelectionFileOptionStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONFILEOPTIONSTORAGE_H
+#define GMX_SELECTION_SELECTIONFILEOPTIONSTORAGE_H
+
+#include "gromacs/options/abstractoptionstorage.h"
+#include "gromacs/selection/selectionfileoption.h"
+
+namespace gmx
+{
+
+class SelectionFileOption;
+class SelectionOptionManager;
+
+/*! \internal \brief
+ * Implementation for a special option for reading selections from files.
+ *
+ * \ingroup module_selection
+ */
+class SelectionFileOptionStorage : public AbstractOptionStorage
+{
+public:
+    /*! \brief
+     * Initializes the storage from option settings.
+     *
+     * \param[in] settings   Storage settings.
+     * \param     manager    Manager for this object.
+     */
+    SelectionFileOptionStorage(const SelectionFileOption& settings, SelectionOptionManager* manager);
+
+    OptionInfo&              optionInfo() override { return info_; }
+    std::string              typeString() const override { return "file"; }
+    int                      valueCount() const override { return 0; }
+    std::vector<Any>         defaultValues() const override { return {}; }
+    std::vector<std::string> defaultValuesAsStrings() const override { return {}; }
+    std::vector<Any>         normalizeValues(const std::vector<Any>& values) const override
+    {
+        return values;
+    }
+
+private:
+    void clearSet() override;
+    void convertValue(const Any& value) override;
+    void processSet() override;
+    void processAll() override {}
+
+    SelectionFileOptionInfo info_;
+    SelectionOptionManager& manager_;
+    bool                    bValueParsed_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/selectionoption.h b/src/include/gromacs/selection/selectionoption.h
new file mode 100644 (file)
index 0000000..c213d8e
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::SelectionOption and gmx::SelectionOptionInfo.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONOPTION_H
+#define GMX_SELECTION_SELECTIONOPTION_H
+
+#include "gromacs/options/abstractoption.h"
+#include "gromacs/selection/selection.h"
+
+#include "selectionenums.h"
+
+namespace gmx
+{
+
+class SelectionOptionInfo;
+class SelectionOptionManager;
+class SelectionOptionStorage;
+
+/*! \brief
+ * Specifies an option that provides selection(s).
+ *
+ * Public methods in this class do not throw.
+ *
+ * To use options of this type, SelectionOptionManager must first be added to
+ * the Options collection.  For trajectory analysis tools, the framework takes
+ * care of this.
+ *
+ * \todo
+ * Support for specifying that an option accepts, e.g., two to four selections.
+ * Currently, only a fixed count or any number of selections is possible.
+ * \if internal
+ * In addition to allowing this in OptionTemplate, also SelectionOptionManager
+ * needs to be updated.
+ * \endif
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class SelectionOption : public OptionTemplate<Selection, SelectionOption>
+{
+public:
+    //! OptionInfo subclass corresponding to this option type.
+    typedef SelectionOptionInfo InfoType;
+
+    //! Initializes an option with the given name.
+    explicit SelectionOption(const char* name) :
+        MyBase(name), defaultText_(""), selectionFlags_(efSelection_DisallowEmpty)
+    {
+    }
+
+    /*! \brief
+     * Request velocity evaluation for output positions.
+     *
+     * \see Selection::setEvaluateVelocities()
+     */
+    MyClass& evaluateVelocities()
+    {
+        selectionFlags_.set(efSelection_EvaluateVelocities);
+        return me();
+    }
+    /*! \brief
+     * Request force evaluation for output positions.
+     *
+     * \see Selection::setEvaluateForces()
+     */
+    MyClass& evaluateForces()
+    {
+        selectionFlags_.set(efSelection_EvaluateForces);
+        return me();
+    }
+    /*! \brief
+     * Only accept selections that evaluate to atom positions.
+     */
+    MyClass& onlyAtoms()
+    {
+        selectionFlags_.set(efSelection_OnlyAtoms);
+        return me();
+    }
+    /*! \brief
+     * Only accept selections that evaluate to atom positions in sorted order.
+     */
+    MyClass& onlySortedAtoms()
+    {
+        selectionFlags_.set(efSelection_OnlyAtoms);
+        selectionFlags_.set(efSelection_OnlySorted);
+        return me();
+    }
+    /*! \brief
+     * Only accept static selections for this option.
+     */
+    MyClass& onlyStatic()
+    {
+        selectionFlags_.set(efSelection_OnlyStatic);
+        return me();
+    }
+    /*! \brief
+     * Handle dynamic selections for this option with position masks.
+     *
+     * \see Selection
+     * \see SelectionPosition::selected()
+     */
+    MyClass& dynamicMask()
+    {
+        selectionFlags_.set(efSelection_DynamicMask);
+        return me();
+    }
+    /*! \brief
+     * Allow specifying an unconditionally empty selection for this option.
+     *
+     * If this option is not set, selections that are unconditionally empty
+     * (i.e., can never match any atoms) result in errors.
+     * Note that even without this option, it is still possible that a
+     * dynamic selection evaluates to zero atoms for some frames.
+     */
+    MyClass& allowEmpty()
+    {
+        selectionFlags_.clear(efSelection_DisallowEmpty);
+        return me();
+    }
+
+    /*! \brief
+     * Sets default selection text for the option.
+     *
+     * If the option is not set by the user, the provided text is parsed as
+     * the value of the selection.
+     */
+    MyClass& defaultSelectionText(const char* text)
+    {
+        defaultText_ = text;
+        return me();
+    }
+
+private:
+    // Disable possibility to allow multiple occurrences, since it isn't
+    // implemented.
+    using MyBase::allowMultiple;
+    // Disable default value because it is impossible to provide a
+    // Selection object.
+    using MyBase::defaultValue;
+    using MyBase::defaultValueIfSet;
+
+    AbstractOptionStorage* createStorage(const OptionManagerContainer& managers) const override;
+
+    const char*    defaultText_;
+    SelectionFlags selectionFlags_;
+
+    /*! \brief
+     * Needed to initialize SelectionOptionStorage from this class without
+     * otherwise unnecessary accessors.
+     */
+    friend class SelectionOptionStorage;
+};
+
+/*! \brief
+ * Wrapper class for accessing and modifying selection option information.
+ *
+ * Allows changes to a selection option after creation.
+ *
+ * This class provides the necessary interface for changing, e.g., the number
+ * of allowed selections for a selection option after the option has been
+ * created with Options::addOption().  This is needed if the number or other
+ * flags are only known after other options have been parsed.  The main
+ * advantage of this class over custom checks is that if used before
+ * interactive selection prompt, the interactive prompt is updated accordingly.
+ *
+ * When using this class, the option should be initially created with the most
+ * permissive flags, and this class should be used to place restrictions where
+ * appropriate.  Otherwise, values that are provided before adjustments will
+ * need to follow the more strict checks.  In most cases in trajectory analysis
+ * (which is the main use case for selection options), the adjustments should
+ * be done in TrajectoryAnalysisModule::optionsFinished() for them to take
+ * place before interactive selection prompts.
+ *
+ * An instance of this class for a selection option can be obtained with
+ * SelectionOption::getAdjuster() when the option is created.
+ *
+ * Example use:
+ * \code
+   SelectionList sel;
+   Options options("example", "Example options");
+   SelectionOptionInfo *info;
+   info = options.addOption(SelectionOption("sel").storeVector(&sel)
+                                .multiValue());
+   // < ... assign values to options ...>
+   if ( condition )
+   {
+       // Put limitations on the selections based on the condition,
+       // which can depend on other option values.
+       // Throws if input given so far violates the limitations.
+       info->setValueCount(2);
+       info->setOnlyStatic(true);
+   }
+ * \endcode
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class SelectionOptionInfo : public OptionInfo
+{
+public:
+    /*! \brief
+     * Creates option info object for given storage object.
+     *
+     * Does not throw.
+     */
+    explicit SelectionOptionInfo(SelectionOptionStorage* option);
+
+    /*! \brief
+     * Sets the number of selections allowed for the option.
+     *
+     * \param[in] count  Number of allowed selections.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    InvalidInputError if values have already been provided
+     *      and their count does not match.
+     */
+    void setValueCount(int count);
+
+    /*! \brief
+     * Sets whether this option evaluates velocities for positions.
+     *
+     * \param[in] bEnabled  If true, velocities are evaluated.
+     *
+     * Does not throw.
+     *
+     * \see Selection::setEvaluateVelocities()
+     */
+    void setEvaluateVelocities(bool bEnabled);
+    /*! \brief
+     * Sets whether this option evaluates forces for positions.
+     *
+     * \param[in] bEnabled  If true, forces are evaluated.
+     *
+     * Does not throw.
+     *
+     * \see Selection::setEvaluateForces()
+     */
+    void setEvaluateForces(bool bEnabled);
+    /*! \brief
+     * Sets whether this option accepts positions that come from multiple
+     * atoms.
+     *
+     * \param[in] bEnabled  If true, the option accepts only positions that
+     *      evaluate to atom positions.
+     *
+     * \see SelectionOption::onlyAtoms()
+     */
+    void setOnlyAtoms(bool bEnabled);
+    /*! \brief
+     * Sets whether this option accepts dynamic selections.
+     *
+     * \param[in] bEnabled  If true, the option accepts only static
+     *      selections.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    InvalidInputError if dynamic selections have already been
+     *      provided.
+     *
+     * Strong exception safety guarantee.
+     *
+     * \see SelectionOption::onlyStatic()
+     */
+    void setOnlyStatic(bool bEnabled);
+    /*! \brief
+     * Sets whether this option uses position masks for dynamic selections.
+     *
+     * \param[in] bEnabled  If true, the position masks are used.
+     *
+     * Does not throw.
+     *
+     * \see SelectionOption::dynamicMask()
+     */
+    void setDynamicMask(bool bEnabled);
+
+private:
+    SelectionOptionStorage&       option();
+    const SelectionOptionStorage& option() const;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/selectionoptionbehavior.h b/src/include/gromacs/selection/selectionoptionbehavior.h
new file mode 100644 (file)
index 0000000..cc26a90
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::SelectionOptionBehavior.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONOPTIONBEHAVIOR_H
+#define GMX_SELECTION_SELECTIONOPTIONBEHAVIOR_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/options/ioptionsbehavior.h"
+
+struct gmx_mtop_t;
+
+namespace gmx
+{
+
+class IOptionsContainer;
+class Options;
+class SelectionCollection;
+
+/*! \brief
+ * Provides topology information to SelectionOptionBehavior.
+ *
+ * Modules that use SelectionOptionBehavior need to implement this interface
+ * to provide functionality to load topology information for use with the
+ * selections.
+ *
+ * If future need arises to use similar information elsewhere, this can be
+ * moved to, e.g., the topology module, but for now it is here for simplicity.
+ * Also, if/when there will be more modules that use this, we can identify
+ * common code from those users and possibly provide a shared implementation
+ * (e.g., in the form of another IOptionsBehavior), but currently there are too
+ * few users to identify any useful reusable functionality from the callers.
+ *
+ * \see SelectionCollection::setTopology().
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class ITopologyProvider
+{
+public:
+    /*! \brief
+     * Returns the topology to use.
+     *
+     * \param[in] required  Whether the topology is required by the caller.
+     *
+     * Can return NULL if \p required is `false` and the topology is not
+     * provided by the user.
+     * If \p required is `true`, should throw an error if the topology
+     * cannot be loaded.
+     *
+     * This method may get called multiple times, potentially with
+     * different values of \p required.  Subsequent calls should just
+     * return the same topology that was loaded in the first call.
+     */
+    virtual gmx_mtop_t* getTopology(bool required) = 0;
+    /*! \brief
+     * Returns the number of atoms.
+     *
+     * This method is only called if getTopology() returns NULL.
+     * It should return the number of atoms that at most need to be
+     * selected by the selections.
+     */
+    virtual int getAtomCount() = 0;
+
+protected:
+    virtual ~ITopologyProvider();
+};
+
+/*! \brief
+ * Options behavior to allow using SelectionOptions.
+ *
+ * This behavior wraps SelectionOptionManager, as well as all calls to
+ * SelectionCollection up to and including selection compilation.
+ *
+ * To use selections through SelectionOption options in a
+ * ICommandLineOptionsModule, you need to
+ *  * create a SelectionCollection object,
+ *  * implement ITopologyProvider to return the topology or number of atoms
+ *    to be used with the selections,
+ *  * create and add a SelectionOptionBehavior, and call initOptions() to add
+ *    common options for selection control,
+ *  * use SelectionOption options to specify the selections used, and
+ *  * evaluate the selections when running the module for the desired sets of
+ *    coordinates (see SelectionCollection::evaluate()).
+ *
+ * The SelectionOptionBehavior provides the following functionalities to the
+ * module:
+ *  * Creates SelectionOptionManager and manages all calls to it.
+ *  * Creates an option to provide an `ndx` file, and loads it when necessary.
+ *  * Creates options to control general aspects of selections (see
+ *    SelectionCollection::initOptions()), as well as a generic option to
+ *    provide selections from a file.
+ *  * After all options have been processed, provides an interactive
+ *    command-line prompt for any missing selections.
+ *  * Compiles the selections.
+ *
+ * The behavior needs to be added before any options are created.
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class SelectionOptionBehavior : public IOptionsBehavior
+{
+public:
+    /*! \brief
+     * Creates a behavior to use selections.
+     *
+     * \param[in,out] selections  Selection collection to use.
+     * \param[in]     topologyProvider  Callback to load/provide topology
+     *     information to selections when required.
+     *
+     * The methods in \p topologyProvider are called after all options have
+     * been parsed and finished, so the caller can, e.g., load the topology
+     * from a file specified by a file option.
+     */
+    SelectionOptionBehavior(SelectionCollection* selections, ITopologyProvider* topologyProvider);
+    ~SelectionOptionBehavior() override;
+
+    /*! \brief
+     * Add common options for controlling selections.
+     *
+     * This method is separate from the constructor so that the caller can
+     * control the order of options better.
+     */
+    void initOptions(IOptionsContainer* options);
+
+    // From IOptionsBehavior
+    void initBehavior(Options* options) override;
+    void optionsFinishing(Options* /*options*/) override {}
+    void optionsFinished() override;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/selectionoptionmanager.h b/src/include/gromacs/selection/selectionoptionmanager.h
new file mode 100644 (file)
index 0000000..2bdcc5f
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::SelectionOptionManager.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONOPTIONMANAGER_H
+#define GMX_SELECTION_SELECTIONOPTIONMANAGER_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/options/options.h"
+
+namespace gmx
+{
+
+class SelectionCollection;
+class SelectionOptionStorage;
+
+/*! \brief
+ * Handles interaction of selection options with other options and user input.
+ *
+ * This class implements interaction of SelectionOption with
+ * SelectionCollection, and also implements features of SelectionOption that
+ * require actions outside options parsing.
+ * It also implements the coupling between SelectionOption and
+ * SelectionFileOption.
+ * It needs to be added using Options::addManager() before SelectionOption or
+ * SelectionFileOption options can be added to an Options collection.
+ *
+ * The main features of this class are:
+ *  - convertOptionValue(), which is used to convert string values into
+ *    selections for options.
+ *  - requestOptionDelayedParsing(), which is called by the internal
+ *    implementation of selection options when an option is provided on the
+ *    command line without a value.  Such calls are remembered, and the value
+ *    for all requested options can be later provided by calling one of
+ *    parseRequestedFromStdin(), parseRequestedFromFile() or
+ *    parseRequstedFromString().
+ *
+ * \inpublicapi
+ * \ingroup module_selection
+ */
+class SelectionOptionManager : public IOptionManager
+{
+public:
+    /*! \brief
+     * Creates a manager for selection options.
+     *
+     * \throws  std::bad_alloc if out of memory.
+     */
+    explicit SelectionOptionManager(SelectionCollection* selections);
+    ~SelectionOptionManager() override;
+
+    /*! \brief
+     * Adds a selection option to be managed.
+     *
+     * \param     storage  Storage object for the option to register.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * This is only for internal use by the selection module.
+     * It is not possible to obtain a SelectionOptionStorage pointer
+     * through any public or library API.
+     *
+     * Strong exception safety.
+     */
+    void registerOption(SelectionOptionStorage* storage);
+    /*! \brief
+     * Converts a string value to selections for an option.
+     *
+     * \param     storage  Storage object to receive the selections.
+     * \param[in] value    Value to convert.
+     * \param[in] bFullValue  If true, the provided selections are the full
+     *      value of the option, and additional checks are performed.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    InvalidInputError if the selection string is not valid,
+     *      or uses a feature not supported by the option.
+     *
+     * This is only for internal use by the selection module.
+     * It is not possible to obtain a SelectionOptionStorage pointer
+     * through any public or library API.
+     */
+    void convertOptionValue(SelectionOptionStorage* storage, const std::string& value, bool bFullValue);
+    /*! \brief
+     * Adds a selection option for delayed user input.
+     *
+     * \param     storage  Storage object for the option to request.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * This is only for internal use by the selection module.
+     * It is not possible to obtain a SelectionOptionStorage pointer
+     * through any public or library API.
+     *
+     * Strong exception safety.
+     */
+    void requestOptionDelayedParsing(SelectionOptionStorage* storage);
+
+    /*! \brief
+     * Returns whether there are requested selections that need input from
+     * parseRequestedFrom*().
+     */
+    bool hasRequestedSelections() const;
+
+    /*! \brief
+     * Initializes options for setting global selection properties.
+     *
+     * \param[in,out] options Options object to initialize.
+     * \throws        std::bad_alloc if out of memory.
+     *
+     * \see SelectionCollection::initOptions()
+     */
+    void initOptions(IOptionsContainer* options);
+
+    /*! \brief
+     * Parses selection(s) from standard input for options not yet
+     * provided.
+     *
+     * \param[in]  bInteractive Whether the parser should behave
+     *      interactively.
+     * \throws     unspecified  Can throw any exception thrown by
+     *      SelectionCollection::parseFromStdin().
+     * \throws     std::bad_alloc if out of memory.
+     *
+     * This method cooperates with SelectionOption to allow interactive
+     * input of requested selections after all options have been processed.
+     * It should be called after the Options::finish() method has been
+     * called on all options that add selections to this collection.
+     * For each required selection option that has not been given, as well
+     * as for optional selection options that have been specified without
+     * values, it will prompt the user to input the necessary selections.
+     */
+    void parseRequestedFromStdin(bool bInteractive);
+    /*! \brief
+     * Parses selection(s) from a file for options not yet provided.
+     *
+     * \param[in]  filename Name of the file to parse selections from.
+     * \throws     unspecified  Can throw any exception thrown by
+     *      SelectionCollection::parseFromFile().
+     * \throws     std::bad_alloc if out of memory.
+     * \throws     InvalidInputError if
+     *      - the number of selections in \p filename doesn't match the
+     *        number requested.
+     *      - any selection uses a feature that is not allowed for the
+     *        corresponding option.
+     *      - if there is a request for any number of selections that is
+     *        not the last (in which case it is not possible to determine
+     *        which selections belong to which request).
+     *
+     * This method behaves as parseRequestedFromStdin(), with two
+     * exceptions:
+     *  -# It reads the selections from a file instead of standard input.
+     *  -# If no requests are pending, assigns values to all required
+     *     options that have not yet been set.
+     *
+     * This method used to implement SelectionFileOption.
+     *
+     * \see parseRequestedFromStdin()
+     */
+    void parseRequestedFromFile(const std::string& filename);
+    /*! \brief
+     * Parses selection(s) from a string for options not yet provided.
+     *
+     * \param[in]  str     String to parse.
+     * \throws     unspecified  Can throw any exception thrown by
+     *      SelectionCollection::parseFromString().
+     * \throws     std::bad_alloc if out of memory.
+     * \throws     InvalidInputError in same conditions as
+     *      parseRequestedFromFile().
+     *
+     * This method behaves as parseRequestedFromFile(), but reads the
+     * selections from a string instead of a file.
+     * This method is mainly used for testing.
+     *
+     * \see parseRequestedFromFile()
+     */
+    void parseRequestedFromString(const std::string& str);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+
+    /*! \brief
+     * Needed for handling delayed selection parsing requests.
+     */
+    friend class SelectionOptionStorage;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/selectionoptionstorage.h b/src/include/gromacs/selection/selectionoptionstorage.h
new file mode 100644 (file)
index 0000000..ff66606
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010-2018, The GROMACS development team.
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares gmx::SelectionOptionStorage.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELECTIONOPTIONSTORAGE_H
+#define GMX_SELECTION_SELECTIONOPTIONSTORAGE_H
+
+#include <string>
+
+#include "gromacs/options/optionstoragetemplate.h"
+#include "gromacs/selection/selection.h"
+#include "gromacs/selection/selectionoption.h"
+
+#include "selectionenums.h"
+
+namespace gmx
+{
+
+class SelectionOption;
+class SelectionOptionManager;
+
+/*! \internal \brief
+ * Converts, validates, and stores selection values.
+ *
+ * \see SelectionOptionManager
+ *
+ * \ingroup module_selection
+ */
+class SelectionOptionStorage : public OptionStorageTemplate<Selection>
+{
+public:
+    /*! \brief
+     * Initializes the storage from option settings.
+     *
+     * \param[in] settings   Storage settings.
+     * \param     manager    Manager for this object.
+     */
+    SelectionOptionStorage(const SelectionOption& settings, SelectionOptionManager* manager);
+
+    OptionInfo&      optionInfo() override { return info_; }
+    std::string      typeString() const override { return "selection"; }
+    std::string      formatSingleValue(const Selection& value) const override;
+    std::vector<Any> normalizeValues(const std::vector<Any>& values) const override;
+
+    /*! \brief
+     * Adds selections to the storage.
+     *
+     * \param[in] selections  List of selections to add.
+     * \param[in] bFullValue  If true, the provided selections are the full
+     *      value of the option, and additional checks are performed.
+     * \throws  std::bad_alloc if out of memory.
+     * \throws  InvalidInputError if
+     *      - There is an incorrect number of selections in \p selections.
+     *      - Any selection in \p selections is not allowed for this
+     *        option.
+     *
+     * This function is used to add selections from SelectionOptionManager.
+     * It is called with \p bFullValue set to false from
+     * SelectionOptionManager::convertOptionValue(), and \p bFullValue set
+     * to true when parsing requested selections.
+     */
+    void addSelections(const SelectionList& selections, bool bFullValue);
+
+    // Required to access the number of values in selection requests.
+    // See SelectionCollection::Impl.
+    using MyBase::maxValueCount;
+    //! Whether the option allows only atom-valued selections.
+    bool allowsOnlyAtoms() const { return selectionFlags_.test(efSelection_OnlyAtoms); }
+    //! \copydoc SelectionOptionInfo::setValueCount()
+    void setAllowedValueCount(int count);
+    /*! \brief
+     * Alters flags for the selections created by this option.
+     *
+     * \param[in] flag        Flag to change.
+     * \param[in] bSet        Whether to set or clear the flag.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    InvalidInputError if selections have already been
+     *      provided and conflict with the given flags.
+     *
+     * If selections have already been provided, it is checked that they
+     * match the limitations enforced by the flags.  Pending requests are
+     * also affected.
+     *
+     * Strong exception safety guarantee.
+     */
+    void setSelectionFlag(SelectionFlag flag, bool bSet);
+
+private:
+    void convertValue(const Any& value) override;
+    void processSetValues(ValueList* values) override;
+    void processAll() override;
+
+    SelectionOptionInfo     info_;
+    SelectionOptionManager& manager_;
+    std::string             defaultText_;
+    SelectionFlags          selectionFlags_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/selelem.h b/src/include/gromacs/selection/selelem.h
new file mode 100644 (file)
index 0000000..dfaeca2
--- /dev/null
@@ -0,0 +1,547 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009-2017, The GROMACS development team.
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares gmx::SelectionTreeElement and related things.
+ *
+ * The selection element trees constructed by the parser and the compiler
+ * are described on the respective pages:
+ * \ref page_module_selection_parser and \ref page_module_selection_compiler.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELELEM_H
+#define GMX_SELECTION_SELELEM_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/selection/indexutil.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/real.h"
+
+#include "selvalue.h"
+
+struct gmx_ana_poscalc_t;
+struct gmx_ana_selparam_t;
+struct gmx_ana_selmethod_t;
+
+struct gmx_sel_evaluate_t;
+struct gmx_sel_mempool_t;
+
+struct t_compiler_data;
+
+namespace gmx
+{
+class SelectionTreeElement;
+struct SelectionTopologyProperties;
+
+//! Smart pointer type for selection tree element pointers.
+typedef std::shared_ptr<SelectionTreeElement> SelectionTreeElementPointer;
+} // namespace gmx
+
+/********************************************************************/
+/*! \name Enumerations for expression types
+ ********************************************************************/
+//!\{
+
+/** Defines the type of a gmx::SelectionTreeElement object. */
+typedef enum
+{
+    /** Constant-valued expression. */
+    SEL_CONST,
+    /** Method expression that requires evaluation. */
+    SEL_EXPRESSION,
+    /** Boolean expression. */
+    SEL_BOOLEAN,
+    /** Arithmetic expression. */
+    SEL_ARITHMETIC,
+    /** Root node of the evaluation tree. */
+    SEL_ROOT,
+    /** Subexpression that may be referenced several times. */
+    SEL_SUBEXPR,
+    /** Reference to a subexpression. */
+    SEL_SUBEXPRREF,
+    /** Unresolved reference to an external group. */
+    SEL_GROUPREF,
+    /** Post-processing of selection value. */
+    SEL_MODIFIER
+} e_selelem_t;
+
+/** Defines the boolean operation of gmx::SelectionTreeElement objects with type \ref SEL_BOOLEAN. */
+typedef enum
+{
+    BOOL_NOT, /**< Not */
+    BOOL_AND, /**< And */
+    BOOL_OR,  /**< Or */
+    BOOL_XOR  /**< Xor (not implemented). */
+} e_boolean_t;
+
+/** Defines the arithmetic operation of gmx::SelectionTreeElement objects with type \ref SEL_ARITHMETIC. */
+typedef enum
+{
+    ARITH_PLUS,  /**< Addition (`+`) */
+    ARITH_MINUS, /**< Subtraction (`-`) */
+    ARITH_NEG,   /**< Unary `-` */
+    ARITH_MULT,  /**< Multiplication (`*`) */
+    ARITH_DIV,   /**< Division (`/`) */
+    ARITH_EXP    /**< Power (`^`) */
+} e_arithmetic_t;
+
+/** Returns a string representation of the type of a gmx::SelectionTreeElement. */
+extern const char* _gmx_selelem_type_str(const gmx::SelectionTreeElement& sel);
+/** Returns a string representation of the boolean type of a \ref SEL_BOOLEAN gmx::SelectionTreeElement. */
+extern const char* _gmx_selelem_boolean_type_str(const gmx::SelectionTreeElement& sel);
+/** Returns a string representation of the type of a \c gmx_ana_selvalue_t. */
+extern const char* _gmx_sel_value_type_str(const gmx_ana_selvalue_t* val);
+
+//!\}
+
+
+/********************************************************************/
+/*! \name Selection expression flags
+ * \anchor selelem_flags
+ ********************************************************************/
+//!\{
+/*! \brief
+ * Selection value flags are set.
+ *
+ * If this flag is set, the flags covered by \ref SEL_VALFLAGMASK
+ * have been set properly for the element.
+ */
+#define SEL_FLAGSSET 1
+/*! \brief
+ * The element evaluates to a single value.
+ *
+ * This flag is always set for \ref GROUP_VALUE elements.
+ */
+#define SEL_SINGLEVAL 2
+/*! \brief
+ * The element evaluates to one value for each input atom.
+ */
+#define SEL_ATOMVAL 4
+/*! \brief
+ * The element evaluates to an arbitrary number of values.
+ */
+#define SEL_VARNUMVAL 8
+/*! \brief
+ * The element (or one of its children) is dynamic.
+ */
+#define SEL_DYNAMIC 16
+/*! \brief
+ * The element may contain atom indices in an unsorted order.
+ */
+#define SEL_UNSORTED 32
+/*! \brief
+ * Mask that covers the flags that describe the number of values.
+ */
+#define SEL_VALTYPEMASK (SEL_SINGLEVAL | SEL_ATOMVAL | SEL_VARNUMVAL)
+/*! \brief
+ * Mask that covers the flags that describe the value type.
+ */
+#define SEL_VALFLAGMASK (SEL_FLAGSSET | SEL_VALTYPEMASK | SEL_DYNAMIC)
+/*! \brief
+ * Data has been allocated for the \p v.u union.
+ *
+ * If not set, the \p v.u.ptr points to data allocated externally.
+ * This is the case if the value of the element is used as a parameter
+ * for a selection method or if the element evaluates the final value of
+ * a selection.
+ *
+ * Even if the flag is set, \p v.u.ptr can be NULL during initialization.
+ *
+ * \todo
+ * This flag overlaps with the function of \p v.nalloc field, and could
+ * probably be removed, making memory management simpler. Currently, the
+ * \p v.nalloc field is not kept up-to-date in all cases when this flag
+ * is changed and is used in places where this flag is not, so this would
+ * require a careful investigation of the selection code.
+ */
+#define SEL_ALLOCVAL (1 << 8)
+/*! \brief
+ * Data has been allocated for the group/position structure.
+ *
+ * If not set, the memory allocated for fields in \p v.u.g or \p v.u.p is
+ * managed externally.
+ *
+ * This field has no effect if the value type is not \ref GROUP_VALUE or
+ * \ref POS_VALUE, but should not be set.
+ */
+#define SEL_ALLOCDATA (1 << 9)
+/*! \brief
+ * \p method->init_frame should be called for the frame.
+ */
+#define SEL_INITFRAME (1 << 10)
+/*! \brief
+ * Parameter has been evaluated for the current frame.
+ *
+ * This flag is set for children of \ref SEL_EXPRESSION elements (which
+ * describe method parameters) after the element has been evaluated for the
+ * current frame.
+ * It is not set for \ref SEL_ATOMVAL elements, because they may need to
+ * be evaluated multiple times.
+ */
+#define SEL_EVALFRAME (1 << 11)
+/*! \brief
+ * \p method->init has been called.
+ */
+#define SEL_METHODINIT (1 << 12)
+/*! \brief
+ * \p method->outinit has been called.
+ *
+ * This flag is also used for \ref SEL_SUBEXPRREF elements.
+ */
+#define SEL_OUTINIT (1 << 13)
+//!\}
+
+
+namespace gmx
+{
+
+class ExceptionInitializer;
+
+//! \cond internal
+/*! \brief
+ * Function pointer for evaluating a gmx::SelectionTreeElement.
+ */
+typedef void (*sel_evalfunc)(struct gmx_sel_evaluate_t*         data,
+                             const SelectionTreeElementPointer& sel,
+                             gmx_ana_index_t*                   g);
+//! \endcond
+
+/*! \internal
+ * \brief
+ * Stores the location of a selection element in the selection text.
+ *
+ * The location is stored as a range in the pretty-printed selection text
+ * (where whitespace has been sanitized), and can be used to extract that text
+ * for error messages and other diagnostic purposes.
+ * During parsing, the extraction is done with _gmx_sel_lexer_get_text().
+ *
+ * This needs to be a plain C struct for Bison to properly deal with it.
+ */
+struct SelectionLocation
+{
+    //! Returns an empty location.
+    static SelectionLocation createEmpty()
+    {
+        SelectionLocation empty = { 0, 0 };
+        return empty;
+    }
+
+    //! Start index of the string where this element has been parsed from.
+    int startIndex;
+    //! End index of the string where this element has been parsed from.
+    int endIndex;
+};
+
+/*! \internal \brief
+ * Represents an element of a selection expression.
+ */
+class SelectionTreeElement
+{
+public:
+    /*! \brief
+     * Allocates memory and performs common initialization.
+     *
+     * \param[in] type     Type of selection element to create.
+     * \param[in] location Location of the element.
+     *
+     * \a type is set to \p type,
+     * \a v::type is set to \ref GROUP_VALUE for boolean and comparison
+     * expressions and \ref NO_VALUE for others, and
+     * \ref SEL_ALLOCVAL is set for non-root elements (\ref SEL_ALLOCDATA
+     * is also set for \ref SEL_BOOLEAN elements).
+     * All the pointers are set to NULL.
+     */
+    SelectionTreeElement(e_selelem_t type, const SelectionLocation& location);
+    ~SelectionTreeElement();
+
+    //! Frees the memory allocated for the \a v union.
+    void freeValues();
+    //! Frees the memory allocated for the \a u union.
+    void freeExpressionData();
+    /* In compiler.cpp */
+    /*! \brief
+     * Frees the memory allocated for the selection compiler.
+     *
+     * This function only frees the data for the given selection, not its
+     * children.  It is safe to call the function when compiler data has
+     * not been allocated or has already been freed; in such a case,
+     * nothing is done.
+     */
+    void freeCompilerData();
+
+    /*! \brief
+     * Reserves memory for value from a memory pool.
+     *
+     * \param[in]     count Number of values to reserve memory for.
+     *
+     * Reserves memory for the values of this element from the \a mempool
+     * memory pool.
+     * If no memory pool is set, nothing is done.
+     */
+    void mempoolReserve(int count);
+    /*! \brief
+     * Releases memory pool used for value.
+     *
+     * Releases the memory allocated for the values of this element from the
+     * \a mempool memory pool.
+     * If no memory pool is set, nothing is done.
+     */
+    void mempoolRelease();
+
+    //! Returns the name of the element.
+    const std::string& name() const { return name_; }
+    //! Returns the location of the element.
+    const SelectionLocation& location() const { return location_; }
+
+    /*! \brief
+     * Sets the name of the element.
+     *
+     * \param[in] name  Name to set (can be NULL).
+     * \throws    std::bad_alloc if out of memory.
+     */
+    void setName(const char* name) { name_ = (name != nullptr ? name : ""); }
+    //! \copydoc setName(const char *)
+    void setName(const std::string& name) { name_ = name; }
+    /*! \brief
+     * Sets the name of a root element if it is missing.
+     *
+     * \param[in] selectionText  Full selection text to use as a fallback.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * If index groups have not yet been set and the selection is a result
+     * of a group reference, the name may still be empty after this call.
+     *
+     * Strong exception safety guarantee.
+     */
+    void fillNameIfMissing(const char* selectionText);
+
+    /*! \brief
+     * Returns which topology properties the selection element subtree requires
+     * for evaluation.
+     *
+     * \returns   List of topology properties required for evaluation.
+     */
+    SelectionTopologyProperties requiredTopologyProperties() const;
+    /*! \brief
+     * Checks that this element and its children do not contain unsupported
+     * elements with unsorted atoms.
+     *
+     * \param[in] bUnsortedAllowed Whether this element's parents allow it
+     *     to have unsorted atoms.
+     * \param     errors           Object for reporting any error messages.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * Errors are reported as nested exceptions in \p errors.
+     */
+    void checkUnsortedAtoms(bool bUnsortedAllowed, ExceptionInitializer* errors) const;
+    /*! \brief
+     * Checks whether the element or its children have unresolved index
+     * group references.
+     *
+     * Does not throw.
+     */
+    bool requiresIndexGroups() const;
+    /*! \brief
+     * Resolves an unresolved reference to an index group.
+     *
+     * \param[in] grps   Index groups to use to resolve the reference.
+     * \param[in] natoms Maximum number of atoms the selections can evaluate to
+     *     (zero if the topology/atom count is not set yet).
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    InconsistentInputError if the reference cannot be
+     *     resolved.
+     */
+    void resolveIndexGroupReference(gmx_ana_indexgrps_t* grps, int natoms);
+    /*! \brief
+     * Checks that an index group has valid atom indices.
+     *
+     * \param[in] natoms Maximum number of atoms the selections can evaluate to.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    InconsistentInputError if there are invalid atom indices.
+     */
+    void checkIndexGroup(int natoms);
+
+    //! Type of the element.
+    e_selelem_t type;
+    /*! \brief
+     * Value storage of the element.
+     *
+     * This field contains the evaluated value of the element, as well as
+     * the output value type.
+     */
+    gmx_ana_selvalue_t v;
+    /*! \brief
+     * Evaluation function for the element.
+     *
+     * Can be either NULL (if the expression is a constant and does not
+     * require evaluation) or point to one of the functions defined in
+     * evaluate.h.
+     */
+    sel_evalfunc evaluate;
+    /*! \brief
+     * Information flags about the element.
+     *
+     * Allowed flags are listed here:
+     * \ref selelem_flags "flags for gmx::SelectionTreeElement".
+     */
+    int flags;
+    //! Data required by the evaluation function.
+    union
+    {
+        /*! \brief Index group data for several element types.
+         *
+         *  - \ref SEL_CONST : if the value type is \ref GROUP_VALUE,
+         *    this field holds the unprocessed group value.
+         *  - \ref SEL_ROOT : holds the group value for which the
+         *    selection subtree should be evaluated.
+         *  - \ref SEL_SUBEXPR : holds the group for which the subexpression
+         *    has been evaluated.
+         */
+        gmx_ana_index_t cgrp;
+        //! Data for \ref SEL_EXPRESSION and \ref SEL_MODIFIER elements.
+        struct
+        {
+            //! Pointer the method used in this expression.
+            struct gmx_ana_selmethod_t* method;
+            //! Pointer to the data allocated by the method's \p init_data (see sel_datafunc()).
+            void* mdata;
+            //! Pointer to the position data passed to the method.
+            struct gmx_ana_pos_t* pos;
+            //! Pointer to the evaluation data for \p pos.
+            struct gmx_ana_poscalc_t* pc;
+        } expr;
+        //! Operation type for \ref SEL_BOOLEAN elements.
+        e_boolean_t boolt;
+        //! Operation type for \ref SEL_ARITHMETIC elements.
+        struct
+        {
+            //! Operation type.
+            e_arithmetic_t type;
+            //! String representation.
+            char* opstr;
+        } arith;
+        //! Associated selection parameter for \ref SEL_SUBEXPRREF elements.
+        struct gmx_ana_selparam_t* param;
+        //! The string/number used to reference the group.
+        struct
+        {
+            //! Name of the referenced external group.
+            char* name;
+            //! If \a name is NULL, the index number of the referenced group.
+            int id;
+        } gref;
+    } u;
+    //! Memory pool to use for values, or NULL if standard memory handling.
+    struct gmx_sel_mempool_t* mempool;
+    //! Internal data for the selection compiler.
+    t_compiler_data* cdata;
+
+    /*! \brief The first child element.
+     *
+     * Other children can be accessed through the \p next field of \p child.
+     */
+    SelectionTreeElementPointer child;
+    //! The next sibling element.
+    SelectionTreeElementPointer next;
+
+private:
+    /*! \brief
+     * Name of the element.
+     *
+     * This field is only used for diagnostic purposes.
+     */
+    std::string name_;
+    /*! \brief
+     * Location of the element in the selection text.
+     *
+     * This field is only used for diagnostic purposes (including error
+     * messages).
+     */
+    SelectionLocation location_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(SelectionTreeElement);
+};
+
+} // namespace gmx
+
+/********************************************************************/
+/*! \name Selection expression functions
+ */
+//!\{
+
+/* In evaluate.c */
+/** Writes out a human-readable name for an evaluation function. */
+void _gmx_sel_print_evalfunc_name(FILE* fp, gmx::sel_evalfunc evalfunc);
+
+/** Sets the value type of a gmx::SelectionTreeElement. */
+void _gmx_selelem_set_vtype(const gmx::SelectionTreeElementPointer& sel, e_selvalue_t vtype);
+
+/*! \brief
+ * Frees the memory allocated for a selection method parameter.
+ *
+ * \param[in] param Parameter to free.
+ */
+void _gmx_selelem_free_param(struct gmx_ana_selparam_t* param);
+/*! \brief
+ * Frees the memory allocated for a selection method.
+ *
+ * \param[in] method Method to free.
+ * \param[in] mdata  Method data to free.
+ */
+void _gmx_selelem_free_method(struct gmx_ana_selmethod_t* method, void* mdata);
+
+/** Prints a human-readable version of a selection element subtree. */
+void _gmx_selelem_print_tree(FILE* fp, const gmx::SelectionTreeElement& sel, bool bValues, int level);
+/* In compiler.c */
+/** Prints a human-readable version of the internal compiler data structure. */
+void _gmx_selelem_print_compiler_info(FILE* fp, const gmx::SelectionTreeElement& sel, int level);
+
+/* In sm_insolidangle.c */
+/** Returns true if the covered fraction of the selection can be calculated. */
+bool _gmx_selelem_can_estimate_cover(const gmx::SelectionTreeElement& sel);
+/** Returns the covered fraction of the selection for the current frame. */
+real _gmx_selelem_estimate_coverfrac(const gmx::SelectionTreeElement& sel);
+
+//!\}
+
+#endif
diff --git a/src/include/gromacs/selection/selhelp.h b/src/include/gromacs/selection/selhelp.h
new file mode 100644 (file)
index 0000000..ec11760
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2012,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Functions for initializing online help for selections.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELHELP_H
+#define GMX_SELECTION_SELHELP_H
+
+#include "gromacs/onlinehelp/ihelptopic.h"
+
+namespace gmx
+{
+
+//! \cond libapi
+/*! \libinternal \brief
+ * Creates a help tree for selections.
+ *
+ * \throws   std::bad_alloc if out of memory.
+ * \returns  Root topic of the created selection tree.
+ *
+ * \ingroup module_selection
+ */
+HelpTopicPointer createSelectionHelpTopic();
+//! \endcond
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/selmethod.h b/src/include/gromacs/selection/selmethod.h
new file mode 100644 (file)
index 0000000..669f49c
--- /dev/null
@@ -0,0 +1,733 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009-2016, The GROMACS development team.
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal
+ * \page page_module_selection_custom Custom selection methods
+ *
+ * Custom selection methods are defined by creating a new instance of
+ * \c gmx_ana_selmethod_t and filling it with the necessary data for handling
+ * the selection.
+ * The structure contains callback pointers that define the actual behavior
+ * of the method.
+ * The following sections discuss how the structure should be filled and how
+ * to implement the callbacks.
+ *
+ *
+ * \section selmethods_define \c gmx_ana_selmethod_t data structure
+ *
+ * An example \c gmx_ana_selmethod_t definition could look like this:
+ *
+ * \code
+   gmx_ana_selmethod_t sm_example = {
+       "example", GROUP_VALUE, 0,
+       asize(sm_params_example), sm_params_example,
+       &init_data_example,
+        NULL,
+       &init_example,
+        NULL,
+       &free_data_example,
+       &init_frame_example,
+       &evaluate_example,
+        NULL,
+       {"example from POS_EXPR [cutoff REAL]", NULL, 0, NULL},
+   };
+ * \endcode
+ *
+ * The first value defines the name of the method.
+ * It is used mostly for informational purposes; the actual name(s) recognized
+ * by the selection parser are defined by the call to
+ * gmx_ana_selmethod_register() (see \ref selmethods_register).
+ *
+ * The second value defines the type of the value the method returns.
+ * Possible values are
+ *  - \ref NO_VALUE : This is allowed only for methods that have the flag
+ *    \ref SMETH_MODIFIER set (see \ref selmethods_modifiers).
+ *  - \ref INT_VALUE : The method returns one or more integer values.
+ *  - \ref REAL_VALUE : The method returns one or more floating-point values.
+ *  - \ref STR_VALUE : The method returns one or more strings.
+ *  - \ref POS_VALUE : The method returns one or more 3D vectors.
+ *  - \ref GROUP_VALUE : The method returns a single index group.
+ *
+ * The third value gives additional information about the method using
+ * a combination of flags.
+ * Possible flags are:
+ *  - \ref SMETH_REQTOP : If set, the topology information is always loaded
+ *    and the \p top pointer passed to the callbacks is guaranteed to be
+ *    non-NULL. Should be set if the method requires topology information
+ *    for evaluation.
+ *  - \ref SMETH_REQMASS : If set, masses of atoms is always loaded
+ *    and the \p top pointer passed to the callbacks is guaranteed to be
+ *    non-NULL and have meaningful masses.  Should be set if the method requires
+ *    atom masses for evaluation.  Implies \ref SMETH_REQTOP.
+ *  - \ref SMETH_DYNAMIC : If set, the method can only be evaluated dynamically,
+ *    i.e., it requires data from the trajectory frame.
+ *  - \ref SMETH_MODIFIER : If set, the method is a selection modifier and
+ *    not an actual selection method.
+ *    For more details, see \ref selmethods_modifiers.
+ *  - \ref SMETH_ALLOW_UNSORTED : If set, the method supports unsorted atoms
+ *    in its input parameters. \ref SMETH_MODIFIER methods are assumed to always
+ *    support unsorted atoms, as their purpose is to affect the ordering.
+ *
+ * There are two additional flags that specify the number of values the
+ * method returns. Only one of them can be set at a time.
+ * If neither is set, the default behavior is to evaluate a value for each
+ * input atom (except for \ref GROUP_VALUE methods, which always return a
+ * single group).
+ * Other behaviors can be specified with these flags:
+ *  - \ref SMETH_SINGLEVAL : If set, the method evaluates to a single value.
+ *    This is automatically set if the type is \ref GROUP_VALUE.
+ *  - \ref SMETH_VARNUMVAL : If set, the method evaluates to an arbitrary
+ *    number of values.
+ *    The number of values is determined based on the values given by the user
+ *    to the method parameters (see \ref selmethods_params).
+ *  .
+ * If either of these flags is specified (and the method type is not
+ * \ref GROUP_VALUE), the group passed to the evaluation callback should not
+ * be used as it can be NULL.
+ * Currently, the above flags only work (have been tested) for \ref POS_VALUE
+ * methods.
+ *
+ * There is one additional flag that can only be specified for \ref STR_VALUE
+ * methods: \ref SMETH_CHARVAL . It is meant for to ease implementation of
+ * methods that evaluate to strings consisting of single characters.
+ *
+ * The next two values determine the number of parameters and a pointer to
+ * the parameter array. The contents of the parameter array are described in
+ * \ref selmethods_params. If the method does not take parameters, the first
+ * value should be 0 and the second can be NULL.
+ * Currently, \ref STR_VALUE methods cannot take parameters, but this limitation
+ * should be easy to lift if required.
+ *
+ * These are followed by function callbacks that determine the
+ * actual behavior of the method. Any of these except the evaluation callback
+ * can be NULL (the evaluation callback can also be NULL if \ref NO_VALUE is
+ * specified for a selection modifier). However, the presence of parameters
+ * can require some of the callbacks to be implemented.
+ * The details are described in \ref selmethods_callbacks.
+ *
+ * Finally, there is a data structure that gives help texts for the method.
+ *
+ * The \c gmx_ana_selmethod_t variable should be declared as a global variable
+ * or it should be otherwise ensured that the structure is not freed: only a
+ * pointer to the structure is stored by the library.
+ *
+ *
+ * \section selmethods_params Defining parameters
+ *
+ * Parameters to selection methods are defined in a separate array of
+ * \c gmx_ana_selparam_t structures.
+ * The order of the parameters does not matter (except possibly for callback
+ * implementation), with one important exception:
+ * If the method evaluates to a \ref POS_VALUE, the first parameter should
+ * have \ref GROUP_VALUE and be the one that is used to calculate the
+ * positions.
+ *
+ * An example parameter definition:
+ * \code
+   static gmx_ana_selparam_t sm_params_example[] = {
+     {"cutoff", {REAL_VALUE, 1, {NULL}}, NULL, SPAR_OPTIONAL},
+     {"from",   {POS_VALUE, -1, {NULL}}, NULL, SPAR_DYNAMIC | SPAR_VARNUM},
+   };
+ * \endcode
+ *
+ * The first value gives the name of the parameter.
+ * The first parameter can have a NULL name, which means that the value should
+ * immediately follow the method name. This can be used to specify methods
+ * of the type 'within 5 of ...'.
+ *
+ * The second value specifies the type of the value that the parameter accepts.
+ * \ref NO_VALUE can be used to specify a boolean parameter, other possibilities
+ * are the same as for the selection method type.
+ *
+ * The third value gives the number of values that the parameter accepts.
+ * For boolean parameters (\ref NO_VALUE), it should be 0.
+ * For parameters with \ref SPAR_VARNUM of \ref SPAR_ATOMVAL, it should be set
+ * to -1 for consistency (it is not used).
+ * If \ref SPAR_RANGES is specified, it should be either 1 (to accept a single
+ * continuous range) or -1 (if combined with \ref SPAR_VARNUM).
+ * In all other cases, it should be a positive integer; in most cases, it
+ * should be 1.
+ *
+ * The nest two pointers should always be NULL (they should be initialized in
+ * the callbacks), except the first pointer in the case of \ref SPAR_ENUMVAL
+ * (see below).
+ *
+ * The final value gives additional information about the acceptable values
+ * for the parameter using a combination of flags.
+ * The possible flags are:
+ *  - \ref SPAR_OPTIONAL : If set, the user does not need to provide a value
+ *    for the parameter. If not set, an error is reported if the parameter
+ *    is not specified by the user.
+ *  - \ref SPAR_DYNAMIC : If set, the method can handle dynamic values for
+ *    the parameter, i.e., the value(s) can be given by an expression that
+ *    evaluates to different values for different frames.
+ *  - \ref SPAR_RANGES : Can be set only for \ref INT_VALUE and
+ *    \ref REAL_VALUE parameters,
+ *    and cannot be combined with \ref SPAR_DYNAMIC.
+ *    If set, the parameter accepts ranges of values.
+ *    The ranges are automatically sorted and compacted such that a minimum
+ *    amount of non-overlapping ranges are given for the method.
+ *  - \ref SPAR_VARNUM : If set, the parameter can have a variable number
+ *    of values. These can be provided by the user as a list of values, or
+ *    using a single \ref SMETH_VARNUMVAL (or a single \ref SMETH_SINGLEVAL)
+ *    method.
+ *  - \ref SPAR_ATOMVAL : If set, the parameter accepts either a single value
+ *    or an expression that evaluates to a value for each input atom.
+ *    The single input value is treated as if the same value was returned for
+ *    each atom.
+ *    Cannot be combined with \ref SPAR_RANGES or \ref SPAR_VARNUM.
+ *  - \ref SPAR_ENUMVAL : Can only be set for \ref STR_VALUE parameters that
+ *    take a single value, and cannot be combined with any other flag than
+ *    \ref SPAR_OPTIONAL. If set, the parameter only accepts one of predefined
+ *    string values. See \ref SPAR_ENUMVAL documentation for details on how
+ *    to specify the acceptable values.
+ *
+ *
+ * \section selmethods_callbacks Implementing callbacks
+ *
+ * There are eight differen callback functions that can be implemented for
+ * selection methods: sel_datafunc(), sel_posfunc(), sel_initfunc(),
+ * sel_outinitfunc(), sel_freefunc(), sel_framefunc(), and two update functions.
+ * They are in this order in the \c gmx_ana_selmethod_t data structure.
+ * In general, any of the callbacks can be NULL, but the presence of
+ * parameters or other callbacks imposes some restrictions:
+ *  - sel_datafunc() should be provided if the method takes parameters.
+ *  - sel_initfunc() should be provided if the method takes
+ *    any parameters with the \ref SPAR_VARNUM or \ref SPAR_ATOMVAL flags,
+ *    except if those parameters have a \ref POS_VALUE.
+ *  - sel_outinitfunc() should be provided for \ref POS_VALUE methods
+ *    and \ref SMETH_VARNUMVAL methods.
+ *  - sel_freefunc() should be provided if sel_datafunc() and/or
+ *    sel_initfunc() allocate any dynamic memory in addition to the data
+ *    structure itself (or allocates the data structure using some other means
+ *    than malloc()).
+ *  - sel_updatefunc_pos() only makes sense for methods with \ref SMETH_DYNAMIC
+ *    set.
+ *  - At least one update function should be provided unless the method type is
+ *    \ref NO_VALUE.
+ *
+ * The documentations for the function pointer types provide more information
+ * about how the callbacks should be implemented.
+ *
+ *
+ * \section selmethods_modifiers Selection modifiers
+ *
+ * Selection modifiers are a special kind of selection methods that can be
+ * appended to the end of a selection. They are specified by adding the
+ * \ref SMETH_MODIFIER flag to the \c gmx_ana_selmethod_t.
+ * They can have two different types:
+ *  - \ref POS_VALUE : These modifiers are given the final positions
+ *    as an input, and they can make modifications to the selection that are
+ *    not possible otherwise (e.g., permute the atoms).
+ *    The modifier should implement sel_updatefunc_pos() and also have
+ *    one NULL parameter in the beginning of the parameter list that takes
+ *    \ref POS_VALUE and is used to give the input positions.
+ *  - \ref NO_VALUE : These modifiers do not modify the final selection, but
+ *    can be used to implement per-selection options for analysis tools
+ *    or to control the default behavior of the selection engine
+ *    (currently, such a framework is not implemented, but should be easy to
+ *    implement if required).
+ *
+ * In addition to restricting the type of the method, selection modifiers
+ * do not allow the flags \ref SMETH_SINGLEVAL and \ref SMETH_VARNUMVAL
+ * (they would not make sense).
+ *
+ * Parameters and callbacks should be implemented as with normal selection
+ * method, but beware that very little of the functionality has been tested.
+ *
+ * \todo
+ * The modifier handling could be made more flexible and more generic;
+ * the current implementation does not allow many things which would be
+ * possible with slight changes in the internals of the library.
+ *
+ *
+ * \section selmethods_register Registering the method
+ *
+ * After defining the method with \c gmx_ana_selmethod_t, it should be
+ * registered with the selection engine.
+ * In analysis programs, this can be done by calling
+ * gmx_ana_selmethod_register().
+ * If adding the method to the library, you should add a pointer to the new
+ * method structure into the \c smtable_def array (in selmethod.cpp), and it is
+ * registered automatically.
+ * In both cases, gmx_ana_selmethod_register() does several checks on the
+ * structure and reports any errors or inconsistencies it finds.
+ */
+/*! \internal \file
+ * \brief API for handling selection methods.
+ *
+ * There should be no need to use the data structures or call the
+ * functions in this file directly unless implementing a custom selection
+ * method.
+ *
+ * Instructions for implementing custom selection methods can be found
+ * on a separate page: \ref page_module_selection_custom
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELMETHOD_H
+#define GMX_SELECTION_SELMETHOD_H
+
+#include "selparam.h"
+#include "selvalue.h"
+
+struct gmx_ana_index_t;
+struct gmx_ana_pos_t;
+struct gmx_ana_selcollection_t;
+struct gmx_mtop_t;
+struct t_pbc;
+struct t_trxframe;
+
+namespace gmx
+{
+class PositionCalculationCollection;
+class SelectionParserSymbolTable;
+
+/*! \internal
+ * \brief
+ * Evaluation context for selection methods.
+ *
+ * This structure encapsulates common parameters passed to selection method
+ * evaluation functions.  The contained values describe the evaluation context,
+ * i.e., the topology and the current trajectory frame.
+ *
+ * \ingroup module_selection
+ */
+struct SelMethodEvalContext
+{
+    //! Initializes the context with given values.
+    SelMethodEvalContext(const gmx_mtop_t* top, t_trxframe* fr, const t_pbc* pbc) :
+        top(top), fr(fr), pbc(pbc)
+    {
+    }
+
+    /*! \brief
+     * Topology.
+     *
+     * Can be NULL if \ref SMETH_REQTOP or \ref SMETH_REQMASS is not set for
+     * the method.
+     */
+    const gmx_mtop_t* top;
+    /*! \brief
+     * Trajectory frame.
+     *
+     * For static methods that are evaluated based on topology information
+     * alone, this is `NULL`.
+     */
+    t_trxframe* fr;
+    /*! \brief
+     * Periodic boundary condition information.
+     *
+     * Can be `NULL`, in which case PBC should not be used.
+     */
+    const t_pbc* pbc;
+};
+
+} // namespace gmx
+
+/*! \name Selection method flags
+ * \anchor selmethod_flags
+ */
+/*@{*/
+//! If set, the method requires topology information.
+#define SMETH_REQTOP 1
+//! If set, the method requires atom masses.
+#define SMETH_REQMASS 2
+//! If set, the method can only be evaluated dynamically.
+#define SMETH_DYNAMIC 4
+/*! \brief
+ * If set, the method evaluates to a single value.
+ *
+ * The default is that the method evaluates to a value for each input atom.
+ * Cannot be combined with \ref SMETH_VARNUMVAL.
+ */
+#define SMETH_SINGLEVAL 8
+/*! \brief
+ * If set, the method evaluates to an arbitrary number of values.
+ *
+ * The default is that the method evaluates to a value for each input atom.
+ * Cannot be combined with \ref SMETH_SINGLEVAL or with \ref GROUP_VALUE.
+ */
+#define SMETH_VARNUMVAL 16
+/*! \brief
+ * If set, the method evaluates to single-character strings.
+ *
+ * This flag can only be set for \ref STR_VALUE methods. If it is set, the
+ * selection engine automatically allocates and frees the required strings.
+ * The evaluation function should store the character values as the first
+ * character in the strings in the output data structure and should not change
+ * the string pointers.
+ */
+#define SMETH_CHARVAL 64
+/*! \brief
+ * If set, the method accepts unsorted atoms in its input parameters.
+ *
+ * Currently, the support for this functionality is fairly limited, and only
+ * static index group references can actually contain unsorted atoms.
+ * But to make this single case work, the position evaluation must support
+ * unsorted atoms as well.
+ */
+#define SMETH_ALLOW_UNSORTED 128
+/*! \brief
+ * If set, the method is a selection modifier.
+ *
+ * The method type should be \ref GROUP_VALUE or \ref NO_VALUE .
+ * Cannot be combined with \ref SMETH_SINGLEVAL or \ref SMETH_VARNUMVAL .
+ */
+#define SMETH_MODIFIER 256
+/*@}*/
+
+/*! \brief
+ * Allocates and initializes internal data and parameter values.
+ *
+ * \param[in]     npar  Number of parameters in \p param.
+ * \param[in,out] param Pointer to (a copy of) the method's
+ *   \c gmx_ana_selmethod_t::param.
+ * \returns       Pointer to method-specific data structure.
+ *   This pointer will be passed as the last parameter of all other function
+ *   calls.
+ * \throws        unspecified Any errors should be indicated by throwing an
+ *      exception.
+ *
+ * Should allocate and initialize any internal data required by the method.
+ * Should also initialize the value pointers (\c gmx_ana_selparam_t::val) in
+ * \p param to point to variables within the internal data structure,
+ * with the exception of parameters that specify the \ref SPAR_VARNUM or
+ * the \ref SPAR_ATOMVAL flag (these should be handled in sel_initfunc()).
+ * However, parameters with a position value should be initialized.
+ * It is also possible to initialize \ref SPAR_ENUMVAL statically outside
+ * this function (see \ref SPAR_ENUMVAL).
+ * The \c gmx_ana_selparam_t::nvalptr should also be initialized for
+ * non-position-valued parameters that have both \ref SPAR_VARNUM and
+ * \ref SPAR_DYNAMIC set (it can also be initialized for other parameters if
+ * desired, but the same information will be available through other means).
+ * For optional parameters, the default values can (and should) be initialized
+ * here, as the parameter values are not changed if the parameter is not
+ * provided.
+ *
+ * For boolean parameters (type equals \ref NO_VALUE), the default value
+ * should be set here. The user can override the value by giving the parameter
+ * either as 'NAME'/'noNAME', or as 'NAME on/off/yes/no'.
+ *
+ * If the method takes any parameters, this function must be provided.
+ */
+typedef void* (*sel_datafunc)(int npar, gmx_ana_selparam_t* param);
+/*! \brief
+ * Sets the position calculation collection for the method.
+ *
+ * \param[in]  pcc   Position calculation collection that the method should use
+ *   for position calculations.
+ * \param      data  Internal data structure from sel_datafunc().
+ *
+ * This function should be provided if the method uses the routines from
+ * poscalc.h for calculating positions.
+ * The pointer \p pcc should then be stored and used for initialization for
+ * any position calculation structures.
+ */
+typedef void (*sel_posfunc)(gmx::PositionCalculationCollection* pcc, void* data);
+/*! \brief
+ * Does initialization based on topology and/or parameter values.
+ *
+ * \param[in]  top   Topology structure
+ *   (can be NULL if \ref SMETH_REQTOP or \ref SMETH_REQMASS is not set).
+ * \param[in]  npar  Number of parameters in \p param.
+ * \param[in]  param Pointer to (an initialized copy of) the method's
+ *   \c gmx_ana_selmethod_t::param.
+ * \param      data  Internal data structure from sel_datafunc().
+ * \returns    0 on success, a non-zero error code on failure.
+ *
+ * This function is called after the parameters have been processed:
+ * the values of the parameters are stored at the locations set in
+ * sel_datafunc().
+ * The flags \ref SPAR_DYNAMIC and \ref SPAR_ATOMVAL are cleared before
+ * calling the function if the value is static or single-valued, respectively.
+ * If a parameter had the \ref SPAR_VARNUM or \ref SPAR_ATOMVAL flag (and
+ * is not \ref POS_VALUE), a pointer to the memory allocated for the values is
+ * found in \c gmx_ana_selparam_t::val.
+ * The pointer should be stored by this function, otherwise the values
+ * cannot be accessed.
+ * For \ref SPAR_VARNUM parameters, the number of values can be accessed
+ * through \c gmx_ana_selparam_t::val. For parameters with \ref SPAR_DYNAMIC,
+ * the number is the maximum number of values (the actual number can be
+ * accessed in sel_framefunc() and in the update callback through the value
+ * pointed by \c gmx_ana_selparam_t::nvalptr).
+ * For \ref SPAR_ATOMVAL parameters, \c gmx_ana_selparam_t::val::nr is set to
+ * 1 if a single value was provided, otherwise it is set to the maximum number
+ * of values possibly passed to the method.
+ * The value pointed by \c gmx_ana_selparam_t::nvalptr always contains the same
+ * value as \c gmx_ana_selparam_t::val::nr.
+ *
+ * For dynamic \ref GROUP_VALUE parameters (\ref SPAR_DYNAMIC set), the value
+ * will be the largest possible selection that may occur during the
+ * evaluation. For other types of dynamic parameters, the values are
+ * undefined.
+ *
+ * If the method takes any parameters with the \ref SPAR_VARNUM or
+ * \ref SPAR_ATOMVAL flags, this function must be provided, except if these
+ * parameters all have \ref POS_VALUE.
+ *
+ * This function may be called multiple times for the same method if the
+ * method takes parameters with \ref SPAR_ATOMVAL set.
+ */
+typedef void (*sel_initfunc)(const gmx_mtop_t* top, int npar, gmx_ana_selparam_t* param, void* data);
+/*! \brief
+ * Initializes output data structure.
+ *
+ * \param[in]     top   Topology structure
+ *   (can be NULL if \ref SMETH_REQTOP or \ref SMETH_REQMASS is not set).
+ * \param[in,out] out   Output data structure.
+ * \param[in]     data  Internal data structure from sel_datafunc().
+ * \returns       0 on success, an error code on error.
+ *
+ * This function is called immediately after sel_initfunc().
+ *
+ * If the method evaluates to a position (\ref POS_VALUE), this function
+ * should be provided, and it should initialize the \c gmx_ana_pos_t data
+ * structure pointed by \p out.p (the pointer is guaranteed to be non-NULL).
+ * The \p out.p->g pointer should be initialized to the group that is used
+ * to evaluate positions in sel_updatefunc() or sel_updatefunc_pos().
+ *
+ * The function should also be provided for non-position-valued
+ * \ref SMETH_VARNUMVAL methods. For these methods, it suffices to set the
+ * \p out->nr field to reflect the maximum number of values returned by the
+ * method.
+ *
+ * Currently, this function is not needed for other types of methods.
+ *
+ * This function may be called multiple times for the same method if the
+ * method takes parameters with \ref SPAR_ATOMVAL set.
+ */
+typedef void (*sel_outinitfunc)(const gmx_mtop_t* top, gmx_ana_selvalue_t* out, void* data);
+/*! \brief
+ * Frees the internal data.
+ *
+ * \param[in] data Internal data structure from sel_datafunc().
+ *
+ * This function should be provided if the internal data structure contains
+ * dynamically allocated data, and should free any such data.
+ * The data structure itself should also be freed.
+ * For convenience, if there is no dynamically allocated data within the
+ * structure and the structure is allocated using malloc()/snew(), this
+ * function is not needed: the selection engine automatically frees the
+ * structure using sfree().
+ * Any memory pointers received as values of parameters are managed externally,
+ * and should not be freed.
+ * Pointers set as the value pointer of \ref SPAR_ENUMVAL parameters should not
+ * be freed.
+ */
+typedef void (*sel_freefunc)(void* data);
+
+/*! \brief
+ * Initializes the evaluation for a new frame.
+ *
+ * \param[in]  context  Evaluation context.
+ * \param      data Internal data structure from sel_datafunc().
+ * \returns    0 on success, a non-zero error code on failure.
+ *
+ * This function should be implemented if the selection method needs to
+ * do some preprocessing for each frame, and the preprocessing does not
+ * depend on the evaluation group.
+ * Because \p sel_updatefunc_* can be called more than once for a frame,
+ * it is inefficient do the preprocessing there.
+ * It is ensured that this function will be called before
+ * \p sel_updatefunc_* for each frame, and that it will be called at most
+ * once for each frame.
+ * For static methods, it is called once, with \p fr and \p pbc set to
+ * NULL.
+ */
+typedef void (*sel_framefunc)(const gmx::SelMethodEvalContext& context, void* data);
+/*! \brief
+ * Evaluates a selection method.
+ *
+ * \param[in]  context  Evaluation context.
+ * \param[in]  g    Index group for which the method should be evaluated.
+ * \param[out] out  Output data structure.
+ * \param      data Internal data structure from sel_datafunc().
+ * \returns    0 on success, a non-zero error code on error.
+ *
+ * This function should evaluate the method for each atom included in \p g,
+ * and write the output to \p out. The pointer in the union \p out->u that
+ * corresponds to the type of the method should be used.
+ * Enough memory has been allocated to store the output values.
+ * The number of values in \p out should also be updated if necessary.
+ * However, \ref POS_VALUE or \ref GROUP_VALUE methods should not touch
+ * \p out->nr (it should be 1 anyways).
+ *
+ * For \ref STR_VALUE methods, the pointers stored in \p out->s are discarded
+ * without freeing; it is the responsibility of this function to provide
+ * pointers that can be discarded without memory leaks.
+ *
+ * If the method accesses \p fr outside the index group specified in \p g or
+ * what it receives from its parameters, it must check that \p fr actually
+ * contains such an atom in case the \p fr has been loaded from a trajectory
+ * that only contains a subset of the system.
+ */
+typedef void (*sel_updatefunc)(const gmx::SelMethodEvalContext& context,
+                               gmx_ana_index_t*                 g,
+                               gmx_ana_selvalue_t*              out,
+                               void*                            data);
+/*! \brief
+ * Evaluates a selection method using positions.
+ *
+ * \param[in]  context  Evaluation context.
+ * \param[in]  pos  Positions for which the method should be evaluated.
+ * \param[out] out  Output data structure.
+ * \param      data Internal data structure from sel_datafunc().
+ * \returns    0 on success, a non-zero error code on error.
+ *
+ * This function should evaluate the method for each position in \p pos,
+ * and write the output values to \p out. The pointer in the union \p out->u
+ * that corresponds to the type of the method should be used.
+ * Enough memory has been allocated to store the output values.
+ * The number of values in \p out should also be updated if necessary.
+ * \ref POS_VALUE or \ref GROUP_VALUE methods should not touch
+ * \p out->nr (it should be 1 anyways).  For other types of methods, the number
+ * of output values should equal the number of positions in \p pos.
+ *
+ * For \ref STR_VALUE methods, the pointers stored in \p out->s are discarded
+ * without freeing; it is the responsibility of this function to provide
+ * pointers that can be discarded without memory leaks.
+ *
+ * If the method accesses \p fr outside the atoms referenced in \p pos or
+ * what it receives from its parameters, it must check that \p fr actually
+ * contains such an atom in case the \p fr has been loaded from a trajectory
+ * that only contains a subset of the system.
+ */
+typedef void (*sel_updatefunc_pos)(const gmx::SelMethodEvalContext& context,
+                                   gmx_ana_pos_t*                   pos,
+                                   gmx_ana_selvalue_t*              out,
+                                   void*                            data);
+
+/*! \internal
+ * \brief
+ * Help information for a selection method.
+ *
+ * If some information is not available, the corresponding field can be set to
+ * 0/NULL.
+ */
+struct gmx_ana_selmethod_help_t
+{
+    /*! \brief
+     * One-line description of the syntax of the method.
+     *
+     * If NULL, the name of the method is used.
+     */
+    const char* syntax;
+    /*! \brief
+     * Title for the help text in \p help.
+     *
+     * If NULL, the name of the method is used.
+     * Only used if `nlhelp > 0`.
+     */
+    const char* helpTitle;
+    /*! \brief
+     * Number of strings in \p help.
+     *
+     * Set to 0 if \p help is NULL.
+     */
+    int nlhelp;
+    /*! \brief
+     * Detailed help for the method.
+     *
+     * If there is no help available in addition to \p syntax, this can be set
+     * to NULL.
+     */
+    const char* const* help;
+};
+
+/*! \internal
+ * \brief
+ * Describes a selection method.
+ *
+ * Any of the function pointers except the update call can be NULL if the
+ * operation is not required or not supported. In this case,
+ * corresponding function calls are skipped.
+ *
+ * See the function pointer type documentation for details of how the
+ * functions should be implemented.
+ * More details on implementing new selection methods can be found on a
+ * separate page: \ref page_module_selection_custom.
+ */
+struct gmx_ana_selmethod_t
+{
+    /** Name of the method. */
+    const char* name;
+    /** Type which the method returns. */
+    e_selvalue_t type;
+    /*! \brief
+     * Flags to specify how the method should be handled.
+     *
+     * See \ref selmethod_flags for allowed values.
+     */
+    int flags;
+    /** Number of parameters the method takes. */
+    int nparams;
+    /** Pointer to the array of parameter descriptions. */
+    gmx_ana_selparam_t* param;
+
+    /** Function for allocating and initializing internal data and parameters. */
+    sel_datafunc init_data;
+    /** Function to set the position calculation collection. */
+    sel_posfunc set_poscoll;
+    /** Function to do initialization based on topology and/or parameter values. */
+    sel_initfunc init;
+    /** Function to initialize output data structure. */
+    sel_outinitfunc outinit;
+    /** Function to free the internal data. */
+    sel_freefunc free;
+
+    /** Function to initialize the calculation for a new frame. */
+    sel_framefunc init_frame;
+    /** Function to evaluate the value. */
+    sel_updatefunc update;
+    /** Function to evaluate the value using positions. */
+    sel_updatefunc_pos pupdate;
+
+    /** Help data for the method. */
+    gmx_ana_selmethod_help_t help;
+};
+
+/** Registers a selection method. */
+int gmx_ana_selmethod_register(gmx::SelectionParserSymbolTable* symtab,
+                               const char*                      name,
+                               gmx_ana_selmethod_t*             method);
+/** Registers all selection methods in the library. */
+int gmx_ana_selmethod_register_defaults(gmx::SelectionParserSymbolTable* symtab);
+
+/** Finds a parameter from a selection method by name. */
+gmx_ana_selparam_t* gmx_ana_selmethod_find_param(const char* name, gmx_ana_selmethod_t* method);
+
+#endif
diff --git a/src/include/gromacs/selection/selmethod_impl.h b/src/include/gromacs/selection/selmethod_impl.h
new file mode 100644 (file)
index 0000000..12af47c
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SELECTION_SELMETHOD_IMPL_H
+#define GMX_SELECTION_SELMETHOD_IMPL_H
+
+#include "selmethod.h"
+
+/*
+ * These global variables cannot be const because gmx_ana_selmethod_register()
+ * modifies them to set some defaults. This is a small price to pay for the
+ * convenience of not having to remember exactly how the selection compiler
+ * expects the structures to be filled, and even more so if the expectations
+ * change. Also, even if the gmx_ana_selmethod_t structures were made const,
+ * the parameters could not be without typecasts somewhere, because the param
+ * field in gmx_ana_selmethod_t cannot be declared const.
+ *
+ * Even though the variables may be modified, this should be thread-safe as
+ * modifications are done only in gmx_ana_selmethod_register(), and it should
+ * work even if called more than once for the same structure, and even if
+ * called concurrently from multiple threads (as long as the selection
+ * collection is not the same).
+ *
+ * All of these problems should go away if/when the selection methods are
+ * implemented as C++ classes.
+ */
+
+/* From sm_com.c */
+extern gmx_ana_selmethod_t sm_cog;
+extern gmx_ana_selmethod_t sm_com;
+/* From sm_simple.c */
+extern gmx_ana_selmethod_t sm_all;
+extern gmx_ana_selmethod_t sm_none;
+extern gmx_ana_selmethod_t sm_atomnr;
+extern gmx_ana_selmethod_t sm_resnr;
+extern gmx_ana_selmethod_t sm_resindex;
+extern gmx_ana_selmethod_t sm_molindex;
+extern gmx_ana_selmethod_t sm_atomname;
+extern gmx_ana_selmethod_t sm_pdbatomname;
+extern gmx_ana_selmethod_t sm_atomtype;
+extern gmx_ana_selmethod_t sm_resname;
+extern gmx_ana_selmethod_t sm_insertcode;
+extern gmx_ana_selmethod_t sm_chain;
+extern gmx_ana_selmethod_t sm_mass;
+extern gmx_ana_selmethod_t sm_charge;
+extern gmx_ana_selmethod_t sm_altloc;
+extern gmx_ana_selmethod_t sm_occupancy;
+extern gmx_ana_selmethod_t sm_betafactor;
+extern gmx_ana_selmethod_t sm_x;
+extern gmx_ana_selmethod_t sm_y;
+extern gmx_ana_selmethod_t sm_z;
+/* From sm_distance.c */
+extern gmx_ana_selmethod_t sm_distance;
+extern gmx_ana_selmethod_t sm_mindistance;
+extern gmx_ana_selmethod_t sm_within;
+/* From sm_insolidangle.c */
+extern gmx_ana_selmethod_t sm_insolidangle;
+/* From sm_same.c */
+extern gmx_ana_selmethod_t sm_same;
+
+/* From sm_merge.c */
+extern gmx_ana_selmethod_t sm_merge;
+extern gmx_ana_selmethod_t sm_plus;
+/* From sm_permute.c */
+extern gmx_ana_selmethod_t sm_permute;
+
+#endif
diff --git a/src/include/gromacs/selection/selparam.h b/src/include/gromacs/selection/selparam.h
new file mode 100644 (file)
index 0000000..784db15
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief API for handling parameters used in selections.
+ *
+ * There should be no need to use the data structures or call the
+ * functions in this file directly unless implementing a custom selection
+ * method.
+ *
+ * More details can be found on the page discussing
+ * \ref page_module_selection_custom "custom selection methods".
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELPARAM_H
+#define GMX_SELECTION_SELPARAM_H
+
+#include "gromacs/selection/indexutil.h"
+
+#include "selvalue.h"
+
+/*! \name Parameter flags
+ * \anchor selparam_flags
+ */
+/*@{*/
+/*! \brief
+ * This flag is set if the user has provided the parameter.
+ *
+ * This flag is set automatically, and should not be set by the user.
+ */
+#define SPAR_SET 1
+/*! \brief
+ * If not set, an error is reported if the parameter is not specified by the
+ * user.
+ */
+#define SPAR_OPTIONAL 2
+/*! \brief
+ * If set, the parameter value can be dynamic, i.e., be different for
+ * different frames.
+ *
+ * If set, the parameter value should only be accessed in the update function
+ * of \c gmx_ana_selmethod_t.
+ * The flag is cleared before sel_initfunc() if the value provided is actually
+ * static.
+ */
+#define SPAR_DYNAMIC 4
+/*! \brief
+ * If set, the parameter value is parsed into sorted ranges.
+ *
+ * Can only be specified for integer parameters.
+ * If specified, the value of the parameter (\c gmx_ana_selparam_t::val)
+ * consists of sets of two integers, each specifying a range.
+ * The values give the endpoints of the ranges (inclusive).
+ * The ranges are sorted and overlapping/continuous ranges are merged into
+ * a single range to minimize the number of ranges.
+ *
+ * If this flag is specified, \c gmx_ana_selparam_t::nval gives the number of
+ * ranges. \p gmx_ana_selparam_t::nval should be 1 or \ref SPAR_VARNUM should be
+ * specified; other values would lead to unpredictable behavior.
+ */
+#define SPAR_RANGES 8
+/*! \brief
+ * If set, the parameter can have any number of values.
+ *
+ * If specified, the data pointer in \c gmx_ana_selparam_t::val should be NULL;
+ * the memory is allocated by the parameter parser.
+ * The implementation of the method should ensure that the pointer to the
+ * allocated memory is stored somewhere in sel_initfunc();
+ * otherwise, the memory is lost.
+ *
+ * The initial value of \c gmx_ana_selparam_t::nval is not used with this flag.
+ * Instead, it will give the number of actual values provided by the user
+ * after the parameters have been parsed.
+ * For consistency, it should be initialized to -1.
+ *
+ * Cannot be combined with \ref GROUP_VALUE parameters.
+ */
+#define SPAR_VARNUM 16
+/*! \brief
+ * If set, the parameter can have a separate value for each atom.
+ *
+ * The flag is cleared before sel_initfunc() if the value provided is actually
+ * a single value.
+ *
+ * Cannot be combined with \ref POS_VALUE or \ref GROUP_VALUE parameters.
+ */
+#define SPAR_ATOMVAL 32
+/*! \brief
+ * If set, the parameter takes one of a set of predefined strings.
+ *
+ * Can only be specified for a \ref STR_VALUE parameter that takes a single
+ * string.
+ * The data pointer in \c gmx_ana_selparam_t::val should be initialized into an
+ * array of strings such that the first and last elements are NULL, and the
+ * rest give the possible values. For optional values, the second element in
+ * the array should give the default value. The string given by the user is
+ * matched against the beginnings of the given strings, and if a unique match
+ * is found, the first pointer in the array will be initialized to point to
+ * the matching string.
+ * The data pointer can be initialized as a static array; duplication of the
+ * array for multiple instances of the same method is automatically taken care
+ * of.
+ */
+#define SPAR_ENUMVAL 128
+/*@}*/
+
+/*! \internal \brief
+ * Describes a single parameter for a selection method.
+ */
+typedef struct gmx_ana_selparam_t
+{
+    /** Name of the parameter. */
+    const char* name;
+    /*! \brief
+     * The parameter value.
+     *
+     * Type \ref NO_VALUE can be used to define a boolean parameter.
+     * The number of values should be 0 for boolean parameters.
+     *
+     * The value pointer be initialized to NULL in the definition of a
+     * \c gmx_ana_selmethod_t and initialized in the
+     * \c gmx_ana_selmethod_t::init_data call
+     * (see sel_datafunc()).
+     * However, if \ref SPAR_VARNUM is provided and the parameter is not
+     * \ref POS_VALUE, this field should not be initialized. Instead,
+     * sufficient memory is allocated automatically and the pointer should be
+     * stored in \c gmx_ana_selmethod_t::init
+     * (see sel_initfunc()).
+     *
+     * The values cannot be accessed outside these two functions: the compiler
+     * makes a copy of the parameter structure for each instance of the
+     * method, and the original parameter array is not changed.
+     */
+    gmx_ana_selvalue_t val;
+    /*! \brief
+     * Pointer to store the number of values.
+     *
+     * If not NULL, the number of values for the parameter is stored in the
+     * pointed value.
+     * Should be specified if \ref SPAR_VARNUM and \ref SPAR_DYNAMIC are both
+     * set.
+     *
+     * Should be initialized to NULL in the definition a \c gmx_ana_selmethod_t
+     * and initialized in sel_datafunc().
+     */
+    int* nvalptr;
+    /*! \brief
+     * Flags that alter the way the parameter is parsed/handled.
+     *
+     * See \ref selparam_flags for allowed values.
+     */
+    int flags;
+} gmx_ana_selparam_t;
+
+/** Finds a parameter from an array by name. */
+gmx_ana_selparam_t* gmx_ana_selparam_find(const char* name, int nparam, gmx_ana_selparam_t* param);
+
+#endif
diff --git a/src/include/gromacs/selection/selvalue.h b/src/include/gromacs/selection/selvalue.h
new file mode 100644 (file)
index 0000000..d2a51c7
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2014,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares ::gmx_ana_selvalue_t.
+ *
+ * There should be no need to use the data structures in this file directly
+ * unless implementing a custom selection routine.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SELVALUE_H
+#define GMX_SELECTION_SELVALUE_H
+
+#include "gromacs/utility/real.h"
+
+/** Defines the value type of a different selection objects. */
+typedef enum
+{
+    NO_VALUE,   /**< No value; either an error condition or an boolean
+                     parameter. */
+    INT_VALUE,  /**< One or more integer values. */
+    REAL_VALUE, /**< One or more real values. */
+    STR_VALUE,  /**< One or more string values. */
+    POS_VALUE,  /**< One or more position values. */
+    GROUP_VALUE /**< One group of atoms. */
+} e_selvalue_t;
+
+/*! \internal
+ * \brief
+ * Describes a value of a selection expression or of a selection method
+ * parameter.
+ *
+ * Which field in the union is used depends on the \p type.
+ */
+typedef struct gmx_ana_selvalue_t
+{
+    /** Type of the value. */
+    e_selvalue_t type;
+    /*! \brief
+     * Number of values in the array pointed by the union.
+     *
+     * Note that for position and group values, it is the number of
+     * data structures in the array, not the number of positions or
+     * the number of atoms in the group.
+     */
+    int nr;
+    /** Pointer to the value. */
+    union
+    {
+        /*! \brief
+         * Generic pointer for operations that do not need type information.
+         *
+         * Needs to be the first member to be able to use initialized arrays.
+         */
+        void* ptr;
+        /** Integer value(s) (type \ref INT_VALUE). */
+        int* i;
+        /** Real value(s) (type \ref REAL_VALUE). */
+        real* r;
+        /** String value(s) (type \ref STR_VALUE). */
+        char** s;
+        /** Structure for the position value(s) (type \ref POS_VALUE). */
+        struct gmx_ana_pos_t* p;
+        /** Group value (type \ref GROUP_VALUE). */
+        struct gmx_ana_index_t* g;
+        /** Boolean value (only parameters of type \ref NO_VALUE); */
+        bool* b;
+    } u;
+    /*! \brief
+     * Number of elements allocated for the value array.
+     */
+    int nalloc;
+} gmx_ana_selvalue_t;
+
+/*! \brief
+ * Initializes an empty selection value structure.
+ *
+ * \param[out] val  Output structure
+ *
+ * The type of \p val is not touched.
+ * Any contents of \p val are discarded without freeing.
+ */
+void _gmx_selvalue_clear(gmx_ana_selvalue_t* val);
+/*! \brief
+ * Frees memory allocated for a selection value structure.
+ *
+ * \param[in,out] val  Values to free.
+ *
+ * The type of \p val is not touched.
+ * If memory is not allocated, the value pointers are simply cleared without
+ * freeing.
+ */
+void _gmx_selvalue_free(gmx_ana_selvalue_t* val);
+/*! \brief
+ * Reserve memory for storing selection values.
+ *
+ * \param[in,out] val  Value structure to allocate.
+ * \param[in]     n    Maximum number of values needed.
+ *
+ * Reserves memory for the values within \p val to store at least \p n values,
+ * of the type specified in the \p val structure.
+ *
+ * If the type is \ref POS_VALUE or \ref GROUP_VALUE, memory is reserved for
+ * the data structures, but no memory is reserved inside these newly allocated
+ * data structures.
+ * Similarly, for \ref STR_VALUE values, the pointers are set to NULL.
+ * For other values, the memory is uninitialized.
+ */
+void _gmx_selvalue_reserve(gmx_ana_selvalue_t* val, int n);
+/*! \brief
+ * Gets and releases the memory pointer for storing selection values.
+ *
+ * \param[in,out] val    Value structure to release.
+ * \param[out]    ptr    Pointer where the values are stored.
+ * \param[out]    nalloc Pointer where the values are stored.
+ *
+ * Returns the pointer where values of \p val were stored in \p ptr and
+ * \p nalloc, and clears the memory in \p val.
+ */
+void _gmx_selvalue_getstore_and_release(gmx_ana_selvalue_t* val, void** ptr, int* nalloc);
+/*! \brief
+ * Sets the memory for storing selection values.
+ *
+ * \param[in,out] val    Value structure to set storage for.
+ * \param[in]     ptr    Pointer where the values should be stored.
+ *
+ * Automatic memory management is disabled for \p ptr.
+ * Asserts if \p val had a previous storage that it owned, as that would result
+ * in a memory leak.
+ */
+void _gmx_selvalue_setstore(gmx_ana_selvalue_t* val, void* ptr);
+/*! \brief
+ * Sets the memory for storing selection values and marks it for automatic freeing.
+ *
+ * \param[in,out] val    Value structure to set storage for.
+ * \param[in]     ptr    Pointer where the values should be stored.
+ * \param[in]     nalloc Number of values allocated for \p ptr.
+ *
+ * Asserts if \p val had a previous storage that it owned, as that would result
+ * in a memory leak.
+ */
+void _gmx_selvalue_setstore_alloc(gmx_ana_selvalue_t* val, void* ptr, int nalloc);
+
+#endif
diff --git a/src/include/gromacs/selection/symrec.h b/src/include/gromacs/selection/symrec.h
new file mode 100644 (file)
index 0000000..21e6fbc
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
+ * Copyright (c) 2014,2015,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Handling of selection parser symbol table.
+ *
+ * This is an implementation header: there should be no need to use it outside
+ * this directory.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_SYMREC_H
+#define GMX_SELECTION_SYMREC_H
+
+#include <iterator>
+#include <memory>
+#include <string>
+
+#include <boost/stl_interfaces/iterator_interface.hpp>
+
+#include "selelem.h"
+
+struct gmx_ana_selmethod_t;
+
+namespace gmx
+{
+
+class SelectionParserSymbolTable;
+
+/*! \internal
+ * \brief
+ * Single symbol for the selection parser.
+ *
+ * Public methods in this class do not throw.
+ *
+ * \ingroup module_selection
+ */
+class SelectionParserSymbol
+{
+public:
+    //! Defines the type of the symbol.
+    enum SymbolType
+    {
+        ReservedSymbol, //!< The symbol is a reserved keyword.
+        VariableSymbol, //!< The symbol is a variable.
+        MethodSymbol,   //!< The symbol is a selection method.
+        PositionSymbol  //!< The symbol is a position keyword.
+    };
+
+    ~SelectionParserSymbol();
+
+    //! Returns the name of the symbol.
+    const std::string& name() const;
+    //! Returns the type of the symbol.
+    SymbolType type() const;
+
+    /*! \brief
+     * Returns the method associated with a \ref MethodSymbol symbol.
+     *
+     * \returns   The method associated with the symbol.
+     *
+     * Must only be called if type() returns \ref MethodSymbol.
+     */
+    gmx_ana_selmethod_t* methodValue() const;
+    /*! \brief
+     * Returns the selection tree associated with a \ref VariableSymbol symbol.
+     *
+     * \returns   The variable expression associated with the symbol.
+     *
+     * Must only be called if type() returns \ref VariableSymbol.
+     */
+    const SelectionTreeElementPointer& variableValue() const;
+
+private:
+    class Impl;
+
+    /*! \brief
+     * Initializes a new symbol with the given data.
+     *
+     * \param  impl  Implementation data.
+     * \throws std::bad_alloc if out of memory.
+     *
+     * Only the parent symbol table creates symbol objects.
+     */
+    explicit SelectionParserSymbol(Impl* impl);
+
+    std::unique_ptr<Impl> impl_;
+
+    /*! \brief
+     * Needed to call the constructor and for other initialization.
+     */
+    friend class SelectionParserSymbolTable;
+};
+
+/*! \internal
+ * \brief
+ * Forward iterator for iterating symbols of a given type.
+ *
+ * Behaves as standard C++ forward iterator.  To get an iterator, call
+ * SelectionParserSymbolTable::beginIterator().  Each time the iterator is
+ * incremented, it moves to the next symbol of the type given when the iterator
+ * was created.  When there are no more symbols, the iterator will equal
+ * SelectionParserSymbolTable::endIterator().  It is not allowed to dereference
+ * or increment an iterator that has reached the end.
+ *
+ * Construction and assignment may throw std::bad_alloc if out of memory.
+ * Other methods do not throw.
+ *
+ * \see SelectionParserSymbolTable::beginIterator()
+ *
+ * \ingroup module_selection
+ */
+class SelectionParserSymbolIterator :
+    public boost::stl_interfaces::iterator_interface<SelectionParserSymbolIterator, std::forward_iterator_tag, const SelectionParserSymbol>
+{
+    using Base =
+            boost::stl_interfaces::iterator_interface<SelectionParserSymbolIterator, std::forward_iterator_tag, const SelectionParserSymbol>;
+
+public:
+    //! Creates an independent copy of an iterator.
+    SelectionParserSymbolIterator(const SelectionParserSymbolIterator& other);
+    ~SelectionParserSymbolIterator();
+
+    //! Creates an independent copy of an iterator.
+    SelectionParserSymbolIterator& operator=(const SelectionParserSymbolIterator& other);
+
+    //! Equality comparison for iterators.
+    bool operator==(const SelectionParserSymbolIterator& other) const;
+    //! Dereferences the iterator.
+    reference operator*() const;
+    //! Moves the iterator to the next symbol.
+    SelectionParserSymbolIterator& operator++();
+    using Base::                   operator++;
+
+private:
+    class Impl;
+
+    /*! \brief
+     * Initializes a new iterator with the given data.
+     *
+     * \param  impl  Implementation data.
+     *
+     * Only the parent symbol table can create non-default-constructed
+     * iterators.
+     */
+    explicit SelectionParserSymbolIterator(Impl* impl);
+
+    std::unique_ptr<Impl> impl_;
+
+    /*! \brief
+     * Needed to access the constructor.
+     */
+    friend class SelectionParserSymbolTable;
+};
+
+/*! \internal \brief
+ * Symbol table for the selection parser.
+ *
+ * \ingroup module_selection
+ */
+class SelectionParserSymbolTable
+{
+public:
+    /*! \brief
+     * Creates a new symbol table.
+     *
+     * \throws std::bad_alloc if out of memory.
+     *
+     * The created table is initialized with reserved and position symbols.
+     */
+    SelectionParserSymbolTable();
+    ~SelectionParserSymbolTable();
+
+    /*! \brief
+     * Finds a symbol by name.
+     *
+     * \param[in] name   Symbol name to find.
+     * \returns   Pointer to the symbol with name \p name, or
+     *      NULL if not found.
+     *
+     * Does not throw.
+     */
+    const SelectionParserSymbol* findSymbol(const std::string& name) const;
+
+    /*! \brief
+     * Returns the start iterator for iterating symbols of a given type.
+     *
+     * \param[in] type  Type of symbols to iterate over.
+     * \returns   Iterator that points to the first symbol of type \p type.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * \see SelectionParserSymbolIterator
+     */
+    SelectionParserSymbolIterator beginIterator(SelectionParserSymbol::SymbolType type) const;
+    /*! \brief
+     * Returns the end iterator for symbol iteration.
+     *
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * Currently, the end value is the same for all symbol types.
+     *
+     * \see SelectionParserSymbolIterator
+     */
+    SelectionParserSymbolIterator endIterator() const;
+
+    /*! \brief
+     * Adds a new variable symbol.
+     *
+     * \param[in] name   Name of the new symbol.
+     * \param[in] sel    Value of the variable.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    InvalidInputError if there was a symbol with the same
+     *      name.
+     */
+    void addVariable(const char* name, const SelectionTreeElementPointer& sel);
+    /*! \brief
+     * Adds a new method symbol.
+     *
+     * \param[in] name   Name of the new symbol.
+     * \param[in] method Method that this symbol represents.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    APIError if there was a symbol with the same name.
+     */
+    void addMethod(const char* name, gmx_ana_selmethod_t* method);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+
+    /*! \brief
+     * Needed to access implementation types.
+     */
+    friend class SelectionParserSymbolIterator;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/selection/tests/toputils.h b/src/include/gromacs/selection/tests/toputils.h
new file mode 100644 (file)
index 0000000..533c245
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Helper routines for constructing topologies for tests.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_selection
+ */
+#ifndef GMX_SELECTION_TESTS_TOPUTILS_H
+#define GMX_SELECTION_TESTS_TOPUTILS_H
+
+#include <memory>
+#include <vector>
+
+struct gmx_mtop_t;
+struct t_atoms;
+struct t_trxframe;
+
+namespace gmx
+{
+
+template<typename T>
+class ArrayRef;
+
+namespace test
+{
+
+/*! \internal
+ * \brief Helps create and manage the lifetime of topology and
+ * related data structures. */
+class TopologyManager
+{
+public:
+    TopologyManager();
+    ~TopologyManager();
+
+    //@{
+    /*! \brief Prepare the memory within a trajectory frame needed
+     * for test */
+    void requestFrame();
+    void requestVelocities();
+    void requestForces();
+    void initFrameIndices(const ArrayRef<const int>& index);
+    //@}
+
+    //! Load a topology from an input file relative to the source tree
+    void loadTopology(const char* filename);
+
+    //@{
+    /*! \brief Functions for creating simplistic topologies
+     *
+     * These are easy to work with for some kinds of tests. */
+    void initAtoms(int count);
+    void initAtomTypes(const ArrayRef<const char* const>& types);
+    void initUniformResidues(int residueSize);
+    void initUniformMolecules(int moleculeSize);
+    //@}
+
+    //@{
+    /*! \brief Functions for creating realistic topologies
+     *
+     * Real topologies aren't uniform, so we need to be able to
+     * create custom topologies to test against.
+     *
+     * Ideally, we'd just be able to push new molecule types and
+     * blocks, but the data structures are not mature enough for
+     * that yet. The intended usage pattern is to initialize the
+     * topology and declare the number of molecule types and
+     * blocks, and then call the setter functions to fill in the
+     * data structures. */
+    void initTopology(int numMoleculeTypes, int numMoleculeBlocks);
+    void setMoleculeType(int moleculeTypeIndex, ArrayRef<const int> residueSizes);
+    void setMoleculeBlock(int moleculeBlockIndex, int moleculeTypeIndex, int numMoleculesToAdd);
+    void finalizeTopology();
+    //@}
+
+    //@{
+    //! Getters
+    gmx_mtop_t* topology() { return mtop_.get(); }
+    t_atoms&    atoms();
+    t_trxframe* frame() { return frame_; }
+    //@}
+
+private:
+    //! Topology
+    std::unique_ptr<gmx_mtop_t> mtop_;
+    //! Trajectory frame
+    t_trxframe* frame_;
+    //! Atom type names
+    std::vector<char*> atomtypes_;
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd.h b/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd.h
new file mode 100644 (file)
index 0000000..72798b1
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_NEON_ASIMD_H
+#define GMX_SIMD_IMPL_ARM_NEON_ASIMD_H
+
+#include "impl_arm_neon_asimd_definitions.h"
+#include "impl_arm_neon_asimd_general.h"
+// No double precision SIMD4 on neon Asimd
+#include "impl_arm_neon_asimd_simd4_float.h"
+#include "impl_arm_neon_asimd_simd_double.h"
+#include "impl_arm_neon_asimd_simd_float.h"
+#include "impl_arm_neon_asimd_util_double.h"
+#include "impl_arm_neon_asimd_util_float.h"
+
+#endif // GMX_SIMD_IMPL_ARM_NEON_ASIMD_H
diff --git a/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_definitions.h b/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_definitions.h
new file mode 100644 (file)
index 0000000..f40c43b
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_NEON_ASIMD_DEFINITIONS_H
+#define GMX_SIMD_IMPL_ARM_NEON_ASIMD_DEFINITIONS_H
+
+// ARM (AArch64) NEON Advanced SIMD
+
+#define GMX_SIMD 1
+#define GMX_SIMD_HAVE_FLOAT 1
+#define GMX_SIMD_HAVE_DOUBLE 1
+#define GMX_SIMD_HAVE_LOADU 1
+#define GMX_SIMD_HAVE_STOREU 1
+#define GMX_SIMD_HAVE_LOGICAL 1
+#define GMX_SIMD_HAVE_FMA 1
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT 1
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 1
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 1
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 1
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 0 // No need for half-simd, width is 4
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 0
+
+#define GMX_SIMD4_HAVE_FLOAT 1
+#define GMX_SIMD4_HAVE_DOUBLE 0
+
+// Implementation details
+#define GMX_SIMD_FLOAT_WIDTH 4
+#define GMX_SIMD_DOUBLE_WIDTH 2
+#define GMX_SIMD_FINT32_WIDTH 4
+#define GMX_SIMD_DINT32_WIDTH 2
+#define GMX_SIMD4_WIDTH 4
+#define GMX_SIMD_ALIGNMENT 16 // Bytes (4*single or 2*double)
+#define GMX_SIMD_RSQRT_BITS 8
+#define GMX_SIMD_RCP_BITS 8
+
+#endif // GMX_SIMD_IMPL_ARM_NEON_ASIMD_DEFINITIONS_H
diff --git a/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_general.h b/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_general.h
new file mode 100644 (file)
index 0000000..d08a00a
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_IMPL_ARM_NEON_ASIMD_GENERAL_H
+#define GMX_SIMD_IMPL_ARM_NEON_ASIMD_GENERAL_H
+
+namespace gmx
+{
+
+static inline void simdPrefetch(void* m)
+{
+#ifdef __GNUC__
+    __builtin_prefetch(m);
+#endif
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_NEON_ASIMD_GENERAL_H
diff --git a/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_simd4_float.h b/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_simd4_float.h
new file mode 100644 (file)
index 0000000..f23c7d8
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_IMPL_ARM_NEON_ASIMD_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPL_ARM_NEON_ASIMD_SIMD4_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+
+#include <arm_neon.h>
+
+namespace gmx
+{
+
+class Simd4Float
+{
+public:
+    Simd4Float() {}
+
+    Simd4Float(float f) : simdInternal_(vdupq_n_f32(f)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4Float(float32x4_t simd) : simdInternal_(simd) {}
+
+    float32x4_t simdInternal_;
+};
+
+class Simd4FBool
+{
+public:
+    Simd4FBool() {}
+
+    //! \brief Construct from scalar bool
+    Simd4FBool(bool b) : simdInternal_(vdupq_n_u32(b ? 0xFFFFFFFF : 0)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4FBool(uint32x4_t simd) : simdInternal_(simd) {}
+
+    uint32x4_t simdInternal_;
+};
+
+static inline Simd4Float gmx_simdcall load4(const float* m)
+{
+    assert(size_t(m) % 16 == 0);
+    return { vld1q_f32(m) };
+}
+
+static inline void gmx_simdcall store4(float* m, Simd4Float a)
+{
+    assert(size_t(m) % 16 == 0);
+    vst1q_f32(m, a.simdInternal_);
+}
+
+static inline Simd4Float gmx_simdcall load4U(const float* m)
+{
+    return { vld1q_f32(m) };
+}
+
+static inline void gmx_simdcall store4U(float* m, Simd4Float a)
+{
+    vst1q_f32(m, a.simdInternal_);
+}
+
+static inline Simd4Float gmx_simdcall simd4SetZeroF()
+{
+    return { vdupq_n_f32(0.0F) };
+}
+
+static inline Simd4Float gmx_simdcall operator&(Simd4Float a, Simd4Float b)
+{
+    return { vreinterpretq_f32_s32(vandq_s32(vreinterpretq_s32_f32(a.simdInternal_),
+                                             vreinterpretq_s32_f32(b.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall andNot(Simd4Float a, Simd4Float b)
+{
+    return { vreinterpretq_f32_s32(vbicq_s32(vreinterpretq_s32_f32(b.simdInternal_),
+                                             vreinterpretq_s32_f32(a.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall operator|(Simd4Float a, Simd4Float b)
+{
+    return { vreinterpretq_f32_s32(vorrq_s32(vreinterpretq_s32_f32(a.simdInternal_),
+                                             vreinterpretq_s32_f32(b.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall operator^(Simd4Float a, Simd4Float b)
+{
+    return { vreinterpretq_f32_s32(veorq_s32(vreinterpretq_s32_f32(a.simdInternal_),
+                                             vreinterpretq_s32_f32(b.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall operator+(Simd4Float a, Simd4Float b)
+{
+    return { vaddq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float a, Simd4Float b)
+{
+    return { vsubq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float x)
+{
+    return { vnegq_f32(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator*(Simd4Float a, Simd4Float b)
+{
+    return { vmulq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall rsqrt(Simd4Float x)
+{
+    return { vrsqrteq_f32(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall abs(Simd4Float x)
+{
+    return { vabsq_f32(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall max(Simd4Float a, Simd4Float b)
+{
+    return { vmaxq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall min(Simd4Float a, Simd4Float b)
+{
+    return { vminq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline void gmx_simdcall transpose(Simd4Float* v0, Simd4Float* v1, Simd4Float* v2, Simd4Float* v3)
+{
+    float32x4x2_t t0  = vuzpq_f32(v0->simdInternal_, v2->simdInternal_);
+    float32x4x2_t t1  = vuzpq_f32(v1->simdInternal_, v3->simdInternal_);
+    float32x4x2_t t2  = vtrnq_f32(t0.val[0], t1.val[0]);
+    float32x4x2_t t3  = vtrnq_f32(t0.val[1], t1.val[1]);
+    v0->simdInternal_ = t2.val[0];
+    v1->simdInternal_ = t3.val[0];
+    v2->simdInternal_ = t2.val[1];
+    v3->simdInternal_ = t3.val[1];
+}
+
+static inline Simd4FBool gmx_simdcall operator==(Simd4Float a, Simd4Float b)
+{
+    return { vceqq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator!=(Simd4Float a, Simd4Float b)
+{
+    return { vmvnq_u32(vceqq_f32(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<(Simd4Float a, Simd4Float b)
+{
+    return { vcltq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<=(Simd4Float a, Simd4Float b)
+{
+    return { vcleq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator&&(Simd4FBool a, Simd4FBool b)
+{
+    return { vandq_u32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator||(Simd4FBool a, Simd4FBool b)
+{
+    return { vorrq_u32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall selectByMask(Simd4Float a, Simd4FBool m)
+{
+    return { vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall selectByNotMask(Simd4Float a, Simd4FBool m)
+{
+    return { vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall blend(Simd4Float a, Simd4Float b, Simd4FBool sel)
+{
+    return { vbslq_f32(sel.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vfmaq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vnegq_f32(vfmsq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall fnma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vfmsq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vnegq_f32(vfmaq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall round(Simd4Float x)
+{
+    return { vrndnq_f32(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall trunc(Simd4Float x)
+{
+    return { vrndq_f32(x.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(Simd4FBool a)
+{
+    return (vmaxvq_u32(a.simdInternal_) != 0);
+}
+
+static inline float gmx_simdcall reduce(Simd4Float a)
+{
+    float32x4_t b = a.simdInternal_;
+    b             = vpaddq_f32(b, b);
+    b             = vpaddq_f32(b, b);
+    return vgetq_lane_f32(b, 0);
+}
+
+static inline float gmx_simdcall dotProduct(Simd4Float a, Simd4Float b)
+{
+    Simd4Float c;
+
+    c = a * b;
+    /* set 4th element to 0, then add all of them */
+    c.simdInternal_ = vsetq_lane_f32(0.0F, c.simdInternal_, 3);
+    return reduce(c);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_NEON_ASIMD_SIMD4_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_simd_double.h b/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_simd_double.h
new file mode 100644 (file)
index 0000000..f42243a
--- /dev/null
@@ -0,0 +1,537 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_NEON_ASIMD_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPL_ARM_NEON_ASIMD_SIMD_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+
+#include <arm_neon.h>
+
+#include "gromacs/math/utilities.h"
+
+#include "impl_arm_neon_asimd_simd_float.h"
+
+namespace gmx
+{
+
+class SimdDouble
+{
+public:
+    SimdDouble() {}
+
+    SimdDouble(double d) : simdInternal_(vdupq_n_f64(d)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDouble(float64x2_t simd) : simdInternal_(simd) {}
+
+    float64x2_t simdInternal_;
+};
+
+class SimdDInt32
+{
+public:
+    SimdDInt32() {}
+
+    SimdDInt32(std::int32_t i) : simdInternal_(vdup_n_s32(i)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDInt32(int32x2_t simd) : simdInternal_(simd) {}
+
+    int32x2_t simdInternal_;
+};
+
+class SimdDBool
+{
+public:
+    SimdDBool() {}
+
+    SimdDBool(bool b) : simdInternal_(vdupq_n_u64(b ? 0xFFFFFFFFFFFFFFFF : 0)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDBool(uint64x2_t simd) : simdInternal_(simd) {}
+
+    uint64x2_t simdInternal_;
+};
+
+class SimdDIBool
+{
+public:
+    SimdDIBool() {}
+
+    SimdDIBool(bool b) : simdInternal_(vdup_n_u32(b ? 0xFFFFFFFF : 0)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDIBool(uint32x2_t simd) : simdInternal_(simd) {}
+
+    uint32x2_t simdInternal_;
+};
+
+static inline SimdDouble gmx_simdcall simdLoad(const double* m, SimdDoubleTag = {})
+{
+    assert(std::size_t(m) % 16 == 0);
+    return { vld1q_f64(m) };
+}
+
+static inline void gmx_simdcall store(double* m, SimdDouble a)
+{
+    assert(std::size_t(m) % 16 == 0);
+    vst1q_f64(m, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall simdLoadU(const double* m, SimdDoubleTag = {})
+{
+    return { vld1q_f64(m) };
+}
+
+static inline void gmx_simdcall storeU(double* m, SimdDouble a)
+{
+    vst1q_f64(m, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall setZeroD()
+{
+    return { vdupq_n_f64(0.0) };
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdDInt32Tag)
+{
+    assert(std::size_t(m) % 8 == 0);
+    return { vld1_s32(m) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdDInt32 a)
+{
+    assert(std::size_t(m) % 8 == 0);
+    vst1_s32(m, a.simdInternal_);
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdDInt32Tag)
+{
+    return { vld1_s32(m) };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdDInt32 a)
+{
+    vst1_s32(m, a.simdInternal_);
+}
+
+static inline SimdDInt32 gmx_simdcall setZeroDI()
+{
+    return { vdup_n_s32(0) };
+}
+
+template<int index>
+gmx_simdcall static inline std::int32_t extract(SimdDInt32 a)
+{
+    return vget_lane_s32(a.simdInternal_, index);
+}
+
+static inline SimdDouble gmx_simdcall operator&(SimdDouble a, SimdDouble b)
+{
+    return { float64x2_t(vandq_s64(int64x2_t(a.simdInternal_), int64x2_t(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall andNot(SimdDouble a, SimdDouble b)
+{
+    return { float64x2_t(vbicq_s64(int64x2_t(b.simdInternal_), int64x2_t(a.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall operator|(SimdDouble a, SimdDouble b)
+{
+    return { float64x2_t(vorrq_s64(int64x2_t(a.simdInternal_), int64x2_t(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall operator^(SimdDouble a, SimdDouble b)
+{
+    return { float64x2_t(veorq_s64(int64x2_t(a.simdInternal_), int64x2_t(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall operator+(SimdDouble a, SimdDouble b)
+{
+    return { vaddq_f64(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble a, SimdDouble b)
+{
+    return { vsubq_f64(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble x)
+{
+    return { vnegq_f64(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator*(SimdDouble a, SimdDouble b)
+{
+    return { vmulq_f64(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { vfmaq_f64(c.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { vnegq_f64(vfmsq_f64(c.simdInternal_, b.simdInternal_, a.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall fnma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { vfmsq_f64(c.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { vnegq_f64(vfmaq_f64(c.simdInternal_, b.simdInternal_, a.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall rsqrt(SimdDouble x)
+{
+    return { vrsqrteq_f64(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall rsqrtIter(SimdDouble lu, SimdDouble x)
+{
+    return { vmulq_f64(lu.simdInternal_,
+                       vrsqrtsq_f64(vmulq_f64(lu.simdInternal_, lu.simdInternal_), x.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall rcp(SimdDouble x)
+{
+    return { vrecpeq_f64(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall rcpIter(SimdDouble lu, SimdDouble x)
+{
+    return { vmulq_f64(lu.simdInternal_, vrecpsq_f64(lu.simdInternal_, x.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall maskAdd(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    float64x2_t addend = float64x2_t(vandq_u64(uint64x2_t(b.simdInternal_), m.simdInternal_));
+
+    return { vaddq_f64(a.simdInternal_, addend) };
+}
+
+static inline SimdDouble gmx_simdcall maskzMul(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    float64x2_t prod = vmulq_f64(a.simdInternal_, b.simdInternal_);
+    return { float64x2_t(vandq_u64(uint64x2_t(prod), m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall maskzFma(SimdDouble a, SimdDouble b, SimdDouble c, SimdDBool m)
+{
+    float64x2_t prod = vfmaq_f64(c.simdInternal_, b.simdInternal_, a.simdInternal_);
+
+    return { float64x2_t(vandq_u64(uint64x2_t(prod), m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall maskzRsqrt(SimdDouble x, SimdDBool m)
+{
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#ifndef NDEBUG
+    x.simdInternal_ = vbslq_f64(m.simdInternal_, x.simdInternal_, vdupq_n_f64(1.0));
+#endif
+    return { float64x2_t(vandq_u64(uint64x2_t(vrsqrteq_f64(x.simdInternal_)), m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall maskzRcp(SimdDouble x, SimdDBool m)
+{
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#ifndef NDEBUG
+    x.simdInternal_ = vbslq_f64(m.simdInternal_, x.simdInternal_, vdupq_n_f64(1.0));
+#endif
+    return { float64x2_t(vandq_u64(uint64x2_t(vrecpeq_f64(x.simdInternal_)), m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall abs(SimdDouble x)
+{
+    return { vabsq_f64(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall max(SimdDouble a, SimdDouble b)
+{
+    return { vmaxq_f64(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall min(SimdDouble a, SimdDouble b)
+{
+    return { vminq_f64(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall round(SimdDouble x)
+{
+    return { vrndnq_f64(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall trunc(SimdDouble x)
+{
+    return { vrndq_f64(x.simdInternal_) };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble frexp(SimdDouble value, SimdDInt32* exponent)
+{
+    const float64x2_t exponentMask = float64x2_t(vdupq_n_s64(0x7FF0000000000000LL));
+    const float64x2_t mantissaMask = float64x2_t(vdupq_n_s64(0x800FFFFFFFFFFFFFLL));
+
+    const int64x2_t exponentBias = vdupq_n_s64(1022); // add 1 to make our definition identical to frexp()
+    const float64x2_t half = vdupq_n_f64(0.5);
+    int64x2_t         iExponent;
+
+    iExponent = vandq_s64(int64x2_t(value.simdInternal_), int64x2_t(exponentMask));
+    iExponent = vsubq_s64(vshrq_n_s64(iExponent, 52), exponentBias);
+
+    float64x2_t result = float64x2_t(vorrq_s64(
+            vandq_s64(int64x2_t(value.simdInternal_), int64x2_t(mantissaMask)), int64x2_t(half)));
+
+    if (opt == MathOptimization::Safe)
+    {
+        uint64x2_t valueIsZero = vceqq_f64(value.simdInternal_, vdupq_n_f64(0.0));
+        iExponent              = vbicq_s64(iExponent, int64x2_t(valueIsZero));
+        result                 = vbslq_f64(valueIsZero, value.simdInternal_, result);
+    }
+
+    exponent->simdInternal_ = vmovn_s64(iExponent);
+
+    return { result };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble ldexp(SimdDouble value, SimdDInt32 exponent)
+{
+    const int32x2_t exponentBias = vdup_n_s32(1023);
+    int32x2_t       iExponent    = vadd_s32(exponent.simdInternal_, exponentBias);
+    int64x2_t       iExponent64;
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = vmax_s32(iExponent, vdup_n_s32(0));
+    }
+
+    iExponent64 = vmovl_s32(iExponent);
+    iExponent64 = vshlq_n_s64(iExponent64, 52);
+
+    return { vmulq_f64(value.simdInternal_, float64x2_t(iExponent64)) };
+}
+
+static inline double gmx_simdcall reduce(SimdDouble a)
+{
+    float64x2_t b = vpaddq_f64(a.simdInternal_, a.simdInternal_);
+    return vgetq_lane_f64(b, 0);
+}
+
+static inline SimdDBool gmx_simdcall operator==(SimdDouble a, SimdDouble b)
+{
+    return { vceqq_f64(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator!=(SimdDouble a, SimdDouble b)
+{
+    return { vreinterpretq_u64_u32(
+            vmvnq_u32(vreinterpretq_u32_u64(vceqq_f64(a.simdInternal_, b.simdInternal_)))) };
+}
+
+static inline SimdDBool gmx_simdcall operator<(SimdDouble a, SimdDouble b)
+{
+    return { vcltq_f64(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator<=(SimdDouble a, SimdDouble b)
+{
+    return { vcleq_f64(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall testBits(SimdDouble a)
+{
+    return { vtstq_s64(int64x2_t(a.simdInternal_), int64x2_t(a.simdInternal_)) };
+}
+
+static inline SimdDBool gmx_simdcall operator&&(SimdDBool a, SimdDBool b)
+{
+    return { vandq_u64(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator||(SimdDBool a, SimdDBool b)
+{
+    return { vorrq_u64(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDBool a)
+{
+    return (vmaxvq_u32((uint32x4_t)(a.simdInternal_)) != 0);
+}
+
+static inline SimdDouble gmx_simdcall selectByMask(SimdDouble a, SimdDBool m)
+{
+    return { float64x2_t(vandq_u64(uint64x2_t(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall selectByNotMask(SimdDouble a, SimdDBool m)
+{
+    return { float64x2_t(vbicq_u64(uint64x2_t(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall blend(SimdDouble a, SimdDouble b, SimdDBool sel)
+{
+    return { vbslq_f64(sel.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator&(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vand_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall andNot(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vbic_s32(b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator|(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vorr_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator^(SimdDInt32 a, SimdDInt32 b)
+{
+    return { veor_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator+(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vadd_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator-(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vsub_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator*(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vmul_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator==(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vceq_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall testBits(SimdDInt32 a)
+{
+    return { vtst_s32(a.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator<(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vclt_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator&&(SimdDIBool a, SimdDIBool b)
+{
+    return { vand_u32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator||(SimdDIBool a, SimdDIBool b)
+{
+    return { vorr_u32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDIBool a)
+{
+    return (vmaxv_u32(a.simdInternal_) != 0);
+}
+
+static inline SimdDInt32 gmx_simdcall selectByMask(SimdDInt32 a, SimdDIBool m)
+{
+    return { vand_s32(a.simdInternal_, vreinterpret_s32_u32(m.simdInternal_)) };
+}
+
+static inline SimdDInt32 gmx_simdcall selectByNotMask(SimdDInt32 a, SimdDIBool m)
+{
+    return { vbic_s32(a.simdInternal_, vreinterpret_s32_u32(m.simdInternal_)) };
+}
+
+static inline SimdDInt32 gmx_simdcall blend(SimdDInt32 a, SimdDInt32 b, SimdDIBool sel)
+{
+    return { vbsl_s32(sel.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall cvtR2I(SimdDouble a)
+{
+    return { vmovn_s64(vcvtnq_s64_f64(a.simdInternal_)) };
+}
+
+static inline SimdDInt32 gmx_simdcall cvttR2I(SimdDouble a)
+{
+    return { vmovn_s64(vcvtq_s64_f64(a.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall cvtI2R(SimdDInt32 a)
+{
+    return { vcvtq_f64_s64(vmovl_s32(a.simdInternal_)) };
+}
+
+static inline SimdDIBool gmx_simdcall cvtB2IB(SimdDBool a)
+{
+    return { vqmovn_u64(a.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall cvtIB2B(SimdDIBool a)
+{
+    return { vorrq_u64(vmovl_u32(a.simdInternal_), vshlq_n_u64(vmovl_u32(a.simdInternal_), 32)) };
+}
+
+static inline void gmx_simdcall cvtF2DD(SimdFloat f, SimdDouble* d0, SimdDouble* d1)
+{
+    d0->simdInternal_ = vcvt_f64_f32(vget_low_f32(f.simdInternal_));
+    d1->simdInternal_ = vcvt_high_f64_f32(f.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall cvtDD2F(SimdDouble d0, SimdDouble d1)
+{
+    return { vcvt_high_f32_f64(vcvt_f32_f64(d0.simdInternal_), d1.simdInternal_) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_NEON_ASIMD_SIMD_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_simd_float.h b/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_simd_float.h
new file mode 100644 (file)
index 0000000..aaaf80f
--- /dev/null
@@ -0,0 +1,547 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2019,2020, by the GROMACS development team.
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_IMPL_ARM_NEON_ASIMD_SIMD_FLOAT_H
+#define GMX_SIMD_IMPL_ARM_NEON_ASIMD_SIMD_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_neon.h>
+
+#include "gromacs/math/utilities.h"
+
+namespace gmx
+{
+
+class SimdFloat
+{
+public:
+    SimdFloat() {}
+
+    SimdFloat(float f) : simdInternal_(vdupq_n_f32(f)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFloat(float32x4_t simd) : simdInternal_(simd) {}
+
+    float32x4_t simdInternal_;
+};
+
+class SimdFInt32
+{
+public:
+    SimdFInt32() {}
+
+    SimdFInt32(std::int32_t i) : simdInternal_(vdupq_n_s32(i)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFInt32(int32x4_t simd) : simdInternal_(simd) {}
+
+    int32x4_t simdInternal_;
+};
+
+class SimdFBool
+{
+public:
+    SimdFBool() {}
+
+    SimdFBool(bool b) : simdInternal_(vdupq_n_u32(b ? 0xFFFFFFFF : 0)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFBool(uint32x4_t simd) : simdInternal_(simd) {}
+
+    uint32x4_t simdInternal_;
+};
+
+class SimdFIBool
+{
+public:
+    SimdFIBool() {}
+
+    SimdFIBool(bool b) : simdInternal_(vdupq_n_u32(b ? 0xFFFFFFFF : 0)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFIBool(uint32x4_t simd) : simdInternal_(simd) {}
+
+    uint32x4_t simdInternal_;
+};
+
+static inline SimdFloat gmx_simdcall simdLoad(const float* m, SimdFloatTag = {})
+{
+    assert(std::size_t(m) % 16 == 0);
+    return { vld1q_f32(m) };
+}
+
+static inline void gmx_simdcall store(float* m, SimdFloat a)
+{
+    assert(std::size_t(m) % 16 == 0);
+    vst1q_f32(m, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall simdLoadU(const float* m, SimdFloatTag = {})
+{
+    return { vld1q_f32(m) };
+}
+
+static inline void gmx_simdcall storeU(float* m, SimdFloat a)
+{
+    vst1q_f32(m, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall setZeroF()
+{
+    return { vdupq_n_f32(0.0F) };
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdFInt32Tag)
+{
+    assert(std::size_t(m) % 16 == 0);
+    return { vld1q_s32(m) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdFInt32 a)
+{
+    assert(std::size_t(m) % 16 == 0);
+    vst1q_s32(m, a.simdInternal_);
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdFInt32Tag)
+{
+    return { vld1q_s32(m) };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdFInt32 a)
+{
+    vst1q_s32(m, a.simdInternal_);
+}
+
+static inline SimdFInt32 gmx_simdcall setZeroFI()
+{
+    return { vdupq_n_s32(0) };
+}
+
+template<int index>
+gmx_simdcall static inline std::int32_t extract(SimdFInt32 a)
+{
+    return vgetq_lane_s32(a.simdInternal_, index);
+}
+
+static inline SimdFloat gmx_simdcall operator&(SimdFloat a, SimdFloat b)
+{
+    return { vreinterpretq_f32_s32(vandq_s32(vreinterpretq_s32_f32(a.simdInternal_),
+                                             vreinterpretq_s32_f32(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall andNot(SimdFloat a, SimdFloat b)
+{
+    return { vreinterpretq_f32_s32(vbicq_s32(vreinterpretq_s32_f32(b.simdInternal_),
+                                             vreinterpretq_s32_f32(a.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall operator|(SimdFloat a, SimdFloat b)
+{
+    return { vreinterpretq_f32_s32(vorrq_s32(vreinterpretq_s32_f32(a.simdInternal_),
+                                             vreinterpretq_s32_f32(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall operator^(SimdFloat a, SimdFloat b)
+{
+    return { vreinterpretq_f32_s32(veorq_s32(vreinterpretq_s32_f32(a.simdInternal_),
+                                             vreinterpretq_s32_f32(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall operator+(SimdFloat a, SimdFloat b)
+{
+    return { vaddq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat a, SimdFloat b)
+{
+    return { vsubq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat x)
+{
+    return { vnegq_f32(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator*(SimdFloat a, SimdFloat b)
+{
+    return { vmulq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall rsqrt(SimdFloat x)
+{
+    return { vrsqrteq_f32(x.simdInternal_) };
+}
+
+// The SIMD implementation seems to overflow when we square lu for
+// values close to FLOAT_MAX, so we fall back on the version in
+// simd_math.h, which is probably slightly slower.
+#if GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT
+static inline SimdFloat gmx_simdcall rsqrtIter(SimdFloat lu, SimdFloat x)
+{
+    return { vmulq_f32(lu.simdInternal_,
+                       vrsqrtsq_f32(vmulq_f32(lu.simdInternal_, lu.simdInternal_), x.simdInternal_)) };
+}
+#endif
+
+static inline SimdFloat gmx_simdcall rcp(SimdFloat x)
+{
+    return { vrecpeq_f32(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall rcpIter(SimdFloat lu, SimdFloat x)
+{
+    return { vmulq_f32(lu.simdInternal_, vrecpsq_f32(lu.simdInternal_, x.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall maskAdd(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    b.simdInternal_ =
+            vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(b.simdInternal_), m.simdInternal_));
+
+    return { vaddq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzMul(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    SimdFloat tmp = a * b;
+
+    return { vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(tmp.simdInternal_), m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall maskzFma(SimdFloat a, SimdFloat b, SimdFloat c, SimdFBool m)
+{
+#ifdef __ARM_FEATURE_FMA
+    float32x4_t tmp = vfmaq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_);
+#else
+    float32x4_t tmp = vmlaq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_);
+#endif
+
+    return { vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(tmp), m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall maskzRsqrt(SimdFloat x, SimdFBool m)
+{
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#ifndef NDEBUG
+    x.simdInternal_ = vbslq_f32(m.simdInternal_, x.simdInternal_, vdupq_n_f32(1.0F));
+#endif
+    return { vreinterpretq_f32_u32(
+            vandq_u32(vreinterpretq_u32_f32(vrsqrteq_f32(x.simdInternal_)), m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall maskzRcp(SimdFloat x, SimdFBool m)
+{
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#ifndef NDEBUG
+    x.simdInternal_ = vbslq_f32(m.simdInternal_, x.simdInternal_, vdupq_n_f32(1.0F));
+#endif
+    return { vreinterpretq_f32_u32(
+            vandq_u32(vreinterpretq_u32_f32(vrecpeq_f32(x.simdInternal_)), m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall abs(SimdFloat x)
+{
+    return { vabsq_f32(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall max(SimdFloat a, SimdFloat b)
+{
+    return { vmaxq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall min(SimdFloat a, SimdFloat b)
+{
+    return { vminq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+// Round and trunc operations are defined at the end of this file, since they
+// need to use float-to-integer and integer-to-float conversions.
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall frexp(SimdFloat value, SimdFInt32* exponent)
+{
+    const int32x4_t exponentMask = vdupq_n_s32(0x7F800000);
+    const int32x4_t mantissaMask = vdupq_n_s32(0x807FFFFF);
+    const int32x4_t exponentBias = vdupq_n_s32(126); // add 1 to make our definition identical to frexp()
+    const float32x4_t half       = vdupq_n_f32(0.5F);
+    int32x4_t         iExponent;
+
+    iExponent = vandq_s32(vreinterpretq_s32_f32(value.simdInternal_), exponentMask);
+    iExponent = vsubq_s32(vshrq_n_s32(iExponent, 23), exponentBias);
+
+    float32x4_t result = vreinterpretq_f32_s32(
+            vorrq_s32(vandq_s32(vreinterpretq_s32_f32(value.simdInternal_), mantissaMask),
+                      vreinterpretq_s32_f32(half)));
+
+    if (opt == MathOptimization::Safe)
+    {
+        uint32x4_t valueIsZero = vceqq_f32(value.simdInternal_, vdupq_n_f32(0.0F));
+        iExponent              = vbicq_s32(iExponent, vreinterpretq_s32_u32(valueIsZero));
+        result                 = vbslq_f32(valueIsZero, value.simdInternal_, result);
+    }
+
+    exponent->simdInternal_ = iExponent;
+    return { result };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall ldexp(SimdFloat value, SimdFInt32 exponent)
+{
+    const int32x4_t exponentBias = vdupq_n_s32(127);
+    int32x4_t       iExponent    = vaddq_s32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = vmaxq_s32(iExponent, vdupq_n_s32(0));
+    }
+
+    iExponent = vshlq_n_s32(iExponent, 23);
+
+    return { vmulq_f32(value.simdInternal_, vreinterpretq_f32_s32(iExponent)) };
+}
+
+static inline SimdFBool gmx_simdcall operator==(SimdFloat a, SimdFloat b)
+{
+    return { vceqq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator!=(SimdFloat a, SimdFloat b)
+{
+    return { vmvnq_u32(vceqq_f32(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline SimdFBool gmx_simdcall operator<(SimdFloat a, SimdFloat b)
+{
+    return { vcltq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator<=(SimdFloat a, SimdFloat b)
+{
+    return { vcleq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall testBits(SimdFloat a)
+{
+    uint32x4_t tmp = vreinterpretq_u32_f32(a.simdInternal_);
+
+    return { vtstq_u32(tmp, tmp) };
+}
+
+static inline SimdFBool gmx_simdcall operator&&(SimdFBool a, SimdFBool b)
+{
+
+    return { vandq_u32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator||(SimdFBool a, SimdFBool b)
+{
+    return { vorrq_u32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall selectByMask(SimdFloat a, SimdFBool m)
+{
+    return { vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall selectByNotMask(SimdFloat a, SimdFBool m)
+{
+    return { vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall blend(SimdFloat a, SimdFloat b, SimdFBool sel)
+{
+    return { vbslq_f32(sel.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator&(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vandq_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall andNot(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vbicq_s32(b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator|(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vorrq_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator^(SimdFInt32 a, SimdFInt32 b)
+{
+    return { veorq_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator+(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vaddq_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator-(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vsubq_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator*(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vmulq_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator==(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vceqq_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall testBits(SimdFInt32 a)
+{
+    return { vtstq_s32(a.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator<(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vcltq_s32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator&&(SimdFIBool a, SimdFIBool b)
+{
+    return { vandq_u32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator||(SimdFIBool a, SimdFIBool b)
+{
+    return { vorrq_u32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall selectByMask(SimdFInt32 a, SimdFIBool m)
+{
+    return { vandq_s32(a.simdInternal_, vreinterpretq_s32_u32(m.simdInternal_)) };
+}
+
+static inline SimdFInt32 gmx_simdcall selectByNotMask(SimdFInt32 a, SimdFIBool m)
+{
+    return { vbicq_s32(a.simdInternal_, vreinterpretq_s32_u32(m.simdInternal_)) };
+}
+
+static inline SimdFInt32 gmx_simdcall blend(SimdFInt32 a, SimdFInt32 b, SimdFIBool sel)
+{
+    return { vbslq_s32(sel.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvttR2I(SimdFloat a)
+{
+    return { vcvtq_s32_f32(a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall cvtI2R(SimdFInt32 a)
+{
+    return { vcvtq_f32_s32(a.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall cvtB2IB(SimdFBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdFBool gmx_simdcall cvtIB2B(SimdFIBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdFloat gmx_simdcall fma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { vfmaq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { vnegq_f32(vfmsq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall fnma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { vfmsq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { vnegq_f32(vfmaq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall round(SimdFloat x)
+{
+    return { vrndnq_f32(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall trunc(SimdFloat x)
+{
+    return { vrndq_f32(x.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvtR2I(SimdFloat a)
+{
+    return { vcvtnq_s32_f32(a.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFBool a)
+{
+    return (vmaxvq_u32(a.simdInternal_) != 0);
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFIBool a)
+{
+    return (vmaxvq_u32(a.simdInternal_) != 0);
+}
+
+static inline float gmx_simdcall reduce(SimdFloat a)
+{
+    float32x4_t b = a.simdInternal_;
+    b             = vpaddq_f32(b, b);
+    b             = vpaddq_f32(b, b);
+    return vgetq_lane_f32(b, 0);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_NEON_ASIMD_SIMD_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_util_double.h b/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_util_double.h
new file mode 100644 (file)
index 0000000..3e43635
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_NEON_ASIMD_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPL_ARM_NEON_ASIMD_UTIL_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_neon.h>
+
+#include "impl_arm_neon_asimd_simd_double.h"
+
+namespace gmx
+{
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const double*      base,
+                                                    const std::int32_t offset[],
+                                                    SimdDouble*        v0,
+                                                    SimdDouble*        v1,
+                                                    SimdDouble*        v2,
+                                                    SimdDouble*        v3)
+{
+    float64x2_t t1, t2, t3, t4;
+
+    assert(std::size_t(offset) % 8 == 0);
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 2 == 0);
+
+    t1                = vld1q_f64(base + align * offset[0]);
+    t2                = vld1q_f64(base + align * offset[1]);
+    t3                = vld1q_f64(base + align * offset[0] + 2);
+    t4                = vld1q_f64(base + align * offset[1] + 2);
+    v0->simdInternal_ = vuzp1q_f64(t1, t2);
+    v1->simdInternal_ = vuzp2q_f64(t1, t2);
+    v2->simdInternal_ = vuzp1q_f64(t3, t4);
+    v3->simdInternal_ = vuzp2q_f64(t3, t4);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadTranspose(const double* base, const std::int32_t offset[], SimdDouble* v0, SimdDouble* v1)
+{
+    float64x2_t t1, t2;
+
+    assert(std::size_t(offset) % 8 == 0);
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 2 == 0);
+
+    t1                = vld1q_f64(base + align * offset[0]);
+    t2                = vld1q_f64(base + align * offset[1]);
+    v0->simdInternal_ = vuzp1q_f64(t1, t2);
+    v1->simdInternal_ = vuzp2q_f64(t1, t2);
+}
+
+static const int c_simdBestPairAlignmentDouble = 2;
+
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const double*      base,
+                                                     const std::int32_t offset[],
+                                                     SimdDouble*        v0,
+                                                     SimdDouble*        v1,
+                                                     SimdDouble*        v2)
+{
+    float64x2_t t1, t2;
+    float64x1_t t3, t4;
+
+    assert(std::size_t(offset) % 8 == 0);
+
+    t1                = vld1q_f64(base + align * offset[0]);
+    t2                = vld1q_f64(base + align * offset[1]);
+    t3                = vld1_f64(base + align * offset[0] + 2);
+    t4                = vld1_f64(base + align * offset[1] + 2);
+    v0->simdInternal_ = vuzp1q_f64(t1, t2);
+    v1->simdInternal_ = vuzp2q_f64(t1, t2);
+    v2->simdInternal_ = vcombine_f64(t3, t4);
+}
+
+template<int align>
+static inline void gmx_simdcall transposeScatterStoreU(double*            base,
+                                                       const std::int32_t offset[],
+                                                       SimdDouble         v0,
+                                                       SimdDouble         v1,
+                                                       SimdDouble         v2)
+{
+    float64x2_t t0, t1;
+
+    assert(std::size_t(offset) % 8 == 0);
+
+    t0 = vuzp1q_f64(v0.simdInternal_, v1.simdInternal_);
+    t1 = vuzp2q_f64(v0.simdInternal_, v1.simdInternal_);
+    vst1q_f64(base + align * offset[0], t0);
+    vst1q_f64(base + align * offset[1], t1);
+    vst1_f64(base + align * offset[0] + 2, vget_low_f64(v2.simdInternal_));
+    vst1_f64(base + align * offset[1] + 2, vget_high_f64(v2.simdInternal_));
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    float64x2_t t0, t1, t2;
+    float64x1_t t3;
+
+    assert(std::size_t(offset) % 8 == 0);
+
+    t0 = vuzp1q_f64(v0.simdInternal_, v1.simdInternal_); // x0 y0
+    t1 = vuzp2q_f64(v0.simdInternal_, v1.simdInternal_); // x1 y1
+
+    t2 = vld1q_f64(base + align * offset[0]);
+    t2 = vaddq_f64(t2, t0);
+    vst1q_f64(base + align * offset[0], t2);
+
+    t3 = vld1_f64(base + align * offset[0] + 2);
+    t3 = vadd_f64(t3, vget_low_f64(v2.simdInternal_));
+    vst1_f64(base + align * offset[0] + 2, t3);
+
+    t2 = vld1q_f64(base + align * offset[1]);
+    t2 = vaddq_f64(t2, t1);
+    vst1q_f64(base + align * offset[1], t2);
+
+    t3 = vld1_f64(base + align * offset[1] + 2);
+    t3 = vadd_f64(t3, vget_high_f64(v2.simdInternal_));
+    vst1_f64(base + align * offset[1] + 2, t3);
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    float64x2_t t0, t1, t2;
+    float64x1_t t3;
+
+    assert(std::size_t(offset) % 8 == 0);
+
+    t0 = vuzp1q_f64(v0.simdInternal_, v1.simdInternal_); // x0 y0
+    t1 = vuzp2q_f64(v0.simdInternal_, v1.simdInternal_); // x1 y1
+
+    t2 = vld1q_f64(base + align * offset[0]);
+    t2 = vsubq_f64(t2, t0);
+    vst1q_f64(base + align * offset[0], t2);
+
+    t3 = vld1_f64(base + align * offset[0] + 2);
+    t3 = vsub_f64(t3, vget_low_f64(v2.simdInternal_));
+    vst1_f64(base + align * offset[0] + 2, t3);
+
+    t2 = vld1q_f64(base + align * offset[1]);
+    t2 = vsubq_f64(t2, t1);
+    vst1q_f64(base + align * offset[1], t2);
+
+    t3 = vld1_f64(base + align * offset[1] + 2);
+    t3 = vsub_f64(t3, vget_high_f64(v2.simdInternal_));
+    vst1_f64(base + align * offset[1] + 2, t3);
+}
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdDouble  scalar,
+                                                        SimdDouble* triplets0,
+                                                        SimdDouble* triplets1,
+                                                        SimdDouble* triplets2)
+{
+    triplets0->simdInternal_ = vuzp1q_f64(scalar.simdInternal_, scalar.simdInternal_);
+    triplets1->simdInternal_ = scalar.simdInternal_;
+    triplets2->simdInternal_ = vuzp2q_f64(scalar.simdInternal_, scalar.simdInternal_);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const double* base,
+                                                             SimdDInt32    offset,
+                                                             SimdDouble*   v0,
+                                                             SimdDouble*   v1,
+                                                             SimdDouble*   v2,
+                                                             SimdDouble*   v3)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_DINT32_WIDTH];
+
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 2 == 0);
+
+    vst1_s32(ioffset, offset.simdInternal_);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1, v2, v3);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_DINT32_WIDTH];
+
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 2 == 0);
+
+    vst1_s32(ioffset, offset.simdInternal_);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_DINT32_WIDTH];
+
+    vst1_s32(ioffset, offset.simdInternal_);
+
+    float64x2_t t1, t2;
+
+    t1                = vld1q_f64(base + align * ioffset[0]);
+    t2                = vld1q_f64(base + align * ioffset[1]);
+    v0->simdInternal_ = vuzp1q_f64(t1, t2);
+    v1->simdInternal_ = vuzp2q_f64(t1, t2);
+}
+
+
+static inline double gmx_simdcall
+reduceIncr4ReturnSum(double* m, SimdDouble v0, SimdDouble v1, SimdDouble v2, SimdDouble v3)
+{
+    float64x2_t t1, t2, t3, t4;
+
+    assert(std::size_t(m) % 8 == 0);
+
+    t1 = vuzp1q_f64(v0.simdInternal_, v1.simdInternal_);
+    t2 = vuzp2q_f64(v0.simdInternal_, v1.simdInternal_);
+    t3 = vuzp1q_f64(v2.simdInternal_, v3.simdInternal_);
+    t4 = vuzp2q_f64(v2.simdInternal_, v3.simdInternal_);
+
+    t1 = vaddq_f64(t1, t2);
+    t3 = vaddq_f64(t3, t4);
+
+    t2 = vaddq_f64(t1, vld1q_f64(m));
+    t4 = vaddq_f64(t3, vld1q_f64(m + 2));
+    vst1q_f64(m, t2);
+    vst1q_f64(m + 2, t4);
+
+    t1 = vaddq_f64(t1, t3);
+    t2 = vpaddq_f64(t1, t1);
+
+    return vgetq_lane_f64(t2, 0);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_NEON_ASIMD_UTIL_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_util_float.h b/src/include/gromacs/simd/impl_arm_neon_asimd/impl_arm_neon_asimd_util_float.h
new file mode 100644 (file)
index 0000000..34815cc
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_IMPL_ARM_NEON_ASIMD_UTIL_FLOAT_H
+#define GMX_SIMD_IMPL_ARM_NEON_ASIMD_UTIL_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_neon.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+
+namespace gmx
+{
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const float*       base,
+                                                    const std::int32_t offset[],
+                                                    SimdFloat*         v0,
+                                                    SimdFloat*         v1,
+                                                    SimdFloat*         v2,
+                                                    SimdFloat*         v3)
+{
+    assert(std::size_t(offset) % 16 == 0);
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 4 == 0);
+
+    // Unfortunately we cannot use the beautiful Neon structured load
+    // instructions since the data comes from four different memory locations.
+    float32x4x2_t t0 =
+            vuzpq_f32(vld1q_f32(base + align * offset[0]), vld1q_f32(base + align * offset[2]));
+    float32x4x2_t t1 =
+            vuzpq_f32(vld1q_f32(base + align * offset[1]), vld1q_f32(base + align * offset[3]));
+    float32x4x2_t t2  = vtrnq_f32(t0.val[0], t1.val[0]);
+    float32x4x2_t t3  = vtrnq_f32(t0.val[1], t1.val[1]);
+    v0->simdInternal_ = t2.val[0];
+    v1->simdInternal_ = t3.val[0];
+    v2->simdInternal_ = t2.val[1];
+    v3->simdInternal_ = t3.val[1];
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadTranspose(const float* base, const std::int32_t offset[], SimdFloat* v0, SimdFloat* v1)
+{
+    assert(std::size_t(offset) % 16 == 0);
+    assert(std::size_t(base) % 8 == 0);
+    assert(align % 2 == 0);
+
+    v0->simdInternal_ =
+            vcombine_f32(vld1_f32(base + align * offset[0]), vld1_f32(base + align * offset[2]));
+    v1->simdInternal_ =
+            vcombine_f32(vld1_f32(base + align * offset[1]), vld1_f32(base + align * offset[3]));
+
+    float32x4x2_t tmp = vtrnq_f32(v0->simdInternal_, v1->simdInternal_);
+
+    v0->simdInternal_ = tmp.val[0];
+    v1->simdInternal_ = tmp.val[1];
+}
+
+static const int c_simdBestPairAlignmentFloat = 2;
+
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const float*       base,
+                                                     const std::int32_t offset[],
+                                                     SimdFloat*         v0,
+                                                     SimdFloat*         v1,
+                                                     SimdFloat*         v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    float32x4x2_t t0 =
+            vuzpq_f32(vld1q_f32(base + align * offset[0]), vld1q_f32(base + align * offset[2]));
+    float32x4x2_t t1 =
+            vuzpq_f32(vld1q_f32(base + align * offset[1]), vld1q_f32(base + align * offset[3]));
+    float32x4x2_t t2  = vtrnq_f32(t0.val[0], t1.val[0]);
+    float32x4x2_t t3  = vtrnq_f32(t0.val[1], t1.val[1]);
+    v0->simdInternal_ = t2.val[0];
+    v1->simdInternal_ = t3.val[0];
+    v2->simdInternal_ = t2.val[1];
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterStoreU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    float32x4x2_t tmp = vtrnq_f32(v0.simdInternal_, v1.simdInternal_);
+
+    vst1_f32(base + align * offset[0], vget_low_f32(tmp.val[0]));
+    vst1_f32(base + align * offset[1], vget_low_f32(tmp.val[1]));
+    vst1_f32(base + align * offset[2], vget_high_f32(tmp.val[0]));
+    vst1_f32(base + align * offset[3], vget_high_f32(tmp.val[1]));
+
+    vst1q_lane_f32(base + align * offset[0] + 2, v2.simdInternal_, 0);
+    vst1q_lane_f32(base + align * offset[1] + 2, v2.simdInternal_, 1);
+    vst1q_lane_f32(base + align * offset[2] + 2, v2.simdInternal_, 2);
+    vst1q_lane_f32(base + align * offset[3] + 2, v2.simdInternal_, 3);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    if (align < 4)
+    {
+        float32x2_t   t0, t1, t2, t3;
+        float32x4x2_t tmp = vtrnq_f32(v0.simdInternal_, v1.simdInternal_);
+
+        t0 = vget_low_f32(tmp.val[0]);
+        t1 = vget_low_f32(tmp.val[1]);
+        t2 = vget_high_f32(tmp.val[0]);
+        t3 = vget_high_f32(tmp.val[1]);
+
+        t0 = vadd_f32(t0, vld1_f32(base + align * offset[0]));
+        vst1_f32(base + align * offset[0], t0);
+        base[align * offset[0] + 2] += vgetq_lane_f32(v2.simdInternal_, 0);
+
+        t1 = vadd_f32(t1, vld1_f32(base + align * offset[1]));
+        vst1_f32(base + align * offset[1], t1);
+        base[align * offset[1] + 2] += vgetq_lane_f32(v2.simdInternal_, 1);
+
+        t2 = vadd_f32(t2, vld1_f32(base + align * offset[2]));
+        vst1_f32(base + align * offset[2], t2);
+        base[align * offset[2] + 2] += vgetq_lane_f32(v2.simdInternal_, 2);
+
+        t3 = vadd_f32(t3, vld1_f32(base + align * offset[3]));
+        vst1_f32(base + align * offset[3], t3);
+        base[align * offset[3] + 2] += vgetq_lane_f32(v2.simdInternal_, 3);
+    }
+    else
+    {
+        // Extra elements means we can use full width-4 load/store operations
+        float32x4x2_t t0 = vuzpq_f32(v0.simdInternal_, v2.simdInternal_);
+        float32x4x2_t t1 = vuzpq_f32(v1.simdInternal_, vdupq_n_f32(0.0F));
+        float32x4x2_t t2 = vtrnq_f32(t0.val[0], t1.val[0]);
+        float32x4x2_t t3 = vtrnq_f32(t0.val[1], t1.val[1]);
+        float32x4_t   t4 = t2.val[0];
+        float32x4_t   t5 = t3.val[0];
+        float32x4_t   t6 = t2.val[1];
+        float32x4_t   t7 = t3.val[1];
+
+        vst1q_f32(base + align * offset[0], vaddq_f32(t4, vld1q_f32(base + align * offset[0])));
+        vst1q_f32(base + align * offset[1], vaddq_f32(t5, vld1q_f32(base + align * offset[1])));
+        vst1q_f32(base + align * offset[2], vaddq_f32(t6, vld1q_f32(base + align * offset[2])));
+        vst1q_f32(base + align * offset[3], vaddq_f32(t7, vld1q_f32(base + align * offset[3])));
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    if (align < 4)
+    {
+        float32x2_t   t0, t1, t2, t3;
+        float32x4x2_t tmp = vtrnq_f32(v0.simdInternal_, v1.simdInternal_);
+
+        t0 = vget_low_f32(tmp.val[0]);
+        t1 = vget_low_f32(tmp.val[1]);
+        t2 = vget_high_f32(tmp.val[0]);
+        t3 = vget_high_f32(tmp.val[1]);
+
+        t0 = vsub_f32(vld1_f32(base + align * offset[0]), t0);
+        vst1_f32(base + align * offset[0], t0);
+        base[align * offset[0] + 2] -= vgetq_lane_f32(v2.simdInternal_, 0);
+
+        t1 = vsub_f32(vld1_f32(base + align * offset[1]), t1);
+        vst1_f32(base + align * offset[1], t1);
+        base[align * offset[1] + 2] -= vgetq_lane_f32(v2.simdInternal_, 1);
+
+        t2 = vsub_f32(vld1_f32(base + align * offset[2]), t2);
+        vst1_f32(base + align * offset[2], t2);
+        base[align * offset[2] + 2] -= vgetq_lane_f32(v2.simdInternal_, 2);
+
+        t3 = vsub_f32(vld1_f32(base + align * offset[3]), t3);
+        vst1_f32(base + align * offset[3], t3);
+        base[align * offset[3] + 2] -= vgetq_lane_f32(v2.simdInternal_, 3);
+    }
+    else
+    {
+        // Extra elements means we can use full width-4 load/store operations
+        float32x4x2_t t0 = vuzpq_f32(v0.simdInternal_, v2.simdInternal_);
+        float32x4x2_t t1 = vuzpq_f32(v1.simdInternal_, vdupq_n_f32(0.0F));
+        float32x4x2_t t2 = vtrnq_f32(t0.val[0], t1.val[0]);
+        float32x4x2_t t3 = vtrnq_f32(t0.val[1], t1.val[1]);
+        float32x4_t   t4 = t2.val[0];
+        float32x4_t   t5 = t3.val[0];
+        float32x4_t   t6 = t2.val[1];
+        float32x4_t   t7 = t3.val[1];
+
+        vst1q_f32(base + align * offset[0], vsubq_f32(vld1q_f32(base + align * offset[0]), t4));
+        vst1q_f32(base + align * offset[1], vsubq_f32(vld1q_f32(base + align * offset[1]), t5));
+        vst1q_f32(base + align * offset[2], vsubq_f32(vld1q_f32(base + align * offset[2]), t6));
+        vst1q_f32(base + align * offset[3], vsubq_f32(vld1q_f32(base + align * offset[3]), t7));
+    }
+}
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdFloat  scalar,
+                                                        SimdFloat* triplets0,
+                                                        SimdFloat* triplets1,
+                                                        SimdFloat* triplets2)
+{
+    float32x2_t lo, hi;
+    float32x4_t t0, t1, t2, t3;
+
+    lo = vget_low_f32(scalar.simdInternal_);
+    hi = vget_high_f32(scalar.simdInternal_);
+
+    t0 = vdupq_lane_f32(lo, 0);
+    t1 = vdupq_lane_f32(lo, 1);
+    t2 = vdupq_lane_f32(hi, 0);
+    t3 = vdupq_lane_f32(hi, 1);
+
+    triplets0->simdInternal_ = vextq_f32(t0, t1, 1);
+    triplets1->simdInternal_ = vextq_f32(t1, t2, 2);
+    triplets2->simdInternal_ = vextq_f32(t2, t3, 3);
+}
+
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const float* base,
+                                                             SimdFInt32   offset,
+                                                             SimdFloat*   v0,
+                                                             SimdFloat*   v1,
+                                                             SimdFloat*   v2,
+                                                             SimdFloat*   v3)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_FINT32_WIDTH];
+
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 4 == 0);
+
+    store(ioffset, offset);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1, v2, v3);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v0, SimdFloat* v1)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_FINT32_WIDTH];
+
+    store(ioffset, offset);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v0, SimdFloat* v1)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_FINT32_WIDTH];
+
+    store(ioffset, offset);
+    v0->simdInternal_ =
+            vcombine_f32(vld1_f32(base + align * ioffset[0]), vld1_f32(base + align * ioffset[2]));
+    v1->simdInternal_ =
+            vcombine_f32(vld1_f32(base + align * ioffset[1]), vld1_f32(base + align * ioffset[3]));
+    float32x4x2_t tmp = vtrnq_f32(v0->simdInternal_, v1->simdInternal_);
+    v0->simdInternal_ = tmp.val[0];
+    v1->simdInternal_ = tmp.val[1];
+}
+
+static inline float gmx_simdcall reduceIncr4ReturnSum(float* m, SimdFloat v0, SimdFloat v1, SimdFloat v2, SimdFloat v3)
+{
+    assert(std::size_t(m) % 16 == 0);
+
+    float32x4x2_t t0 = vuzpq_f32(v0.simdInternal_, v2.simdInternal_);
+    float32x4x2_t t1 = vuzpq_f32(v1.simdInternal_, v3.simdInternal_);
+    float32x4x2_t t2 = vtrnq_f32(t0.val[0], t1.val[0]);
+    float32x4x2_t t3 = vtrnq_f32(t0.val[1], t1.val[1]);
+    v0.simdInternal_ = t2.val[0];
+    v1.simdInternal_ = t3.val[0];
+    v2.simdInternal_ = t2.val[1];
+    v3.simdInternal_ = t3.val[1];
+
+    v0 = v0 + v1;
+    v2 = v2 + v3;
+    v0 = v0 + v2;
+    v2 = v0 + simdLoad(m);
+    store(m, v2);
+
+    return reduce(v0);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_NEON_ASIMD_UTIL_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve.h b/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve.h
new file mode 100644 (file)
index 0000000..41240fc
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_H
+#define GMX_SIMD_IMPL_ARM_SVE_H
+
+#include "impl_arm_sve_definitions.h"
+#include "impl_arm_sve_general.h"
+#include "impl_arm_sve_simd4_double.h"
+#include "impl_arm_sve_simd4_float.h"
+#include "impl_arm_sve_simd_double.h"
+#include "impl_arm_sve_simd_float.h"
+#include "impl_arm_sve_util_double.h"
+#include "impl_arm_sve_util_float.h"
+
+/*
+ * armv8+sve support is implemented via the ARM C Language Extensions (ACLE)
+ * that introduces scalable/sizeless types such as svfloat32_t (vector of FP32),
+ * svint32_t (vector of int) or svbool_t (bit mask).
+ * These sizeless types are not a fit for GROMACS that needs to know the vector
+ * length at cmake time. GCC 10 and later have the -msve-vector-bits=<len> option
+ * in order to fix the vector length at compile time. This feature is currently
+ * planned in LLVM 12.
+ * Even with this option, svfloat32_t is considered as sizeless
+ * by the compiler. However, a float __attribute((vector_size(len/8))) variable is
+ * automagically converted as a svfloat32_t and can hence be used to invoke the SIMD
+ * intrinsics specified by ACLE.
+ * Unfortunately, there is no such thing for svbool_t, so a bit mask (SimdBool) is implemented
+ * as a vector of integers.
+ */
+#endif // GMX_SIMD_IMPL_ARM_SVE_H
diff --git a/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_definitions.h b/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_definitions.h
new file mode 100644 (file)
index 0000000..90443b7
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_DEFINITIONS_H
+#define GMX_SIMD_IMPL_ARM_SVE_DEFINITIONS_H
+
+#include "config.h"
+
+#define GMX_SIMD 1
+#define GMX_SIMD_HAVE_FLOAT 1
+#define GMX_SIMD_HAVE_DOUBLE 1
+#define GMX_SIMD_HAVE_LOADU 1
+#define GMX_SIMD_HAVE_STOREU 1
+#define GMX_SIMD_HAVE_LOGICAL 1
+#define GMX_SIMD_HAVE_FMA 1
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT \
+    0 // Although there is support, it is disabled in GROMACS, because rsqrtIter does not work correctly for inputs near MAX_FLOAT
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 1
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 1
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 1
+
+#define GMX_SIMD_ALIGNMENT 16
+
+// Implementation details
+#define GMX_SIMD_FLOAT_WIDTH (GMX_SIMD_ARM_SVE_LENGTH_VALUE / 32)
+#define GMX_SIMD_DOUBLE_WIDTH (GMX_SIMD_ARM_SVE_LENGTH_VALUE / 64)
+#define GMX_SIMD_FINT32_WIDTH GMX_SIMD_FLOAT_WIDTH
+#define GMX_SIMD_DINT32_WIDTH GMX_SIMD_DOUBLE_WIDTH
+#define GMX_SIMD4_WIDTH 4
+#define GMX_SIMD_RSQRT_BITS 8
+#define GMX_SIMD_RCP_BITS 8
+
+
+#if GMX_SIMD_FLOAT_WIDTH > 4
+#    define GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT 0
+#endif
+
+#if GMX_SIMD_DOUBLE_WIDTH > 4
+#    define GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE 0
+#endif
+
+#define GMX_SIMD4_HAVE_FLOAT 1
+#if GMX_SIMD_DOUBLE_WIDTH < 4
+#    define GMX_SIMD4_HAVE_DOUBLE 0
+#else
+#    define GMX_SIMD4_HAVE_DOUBLE 1
+#endif
+#endif // GMX_SIMD_IMPL_ARM_SVE_DEFINITIONS_H
diff --git a/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_general.h b/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_general.h
new file mode 100644 (file)
index 0000000..147155c
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2014,2015,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_GENERAL_H
+#define GMX_SIMD_IMPL_ARM_SVE_GENERAL_H
+
+namespace gmx
+{
+
+static inline void simdPrefetch(void* m)
+{
+#ifdef __GNUC__
+    __builtin_prefetch(m);
+#endif
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_GENERAL_H
diff --git a/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_simd4_double.h b/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_simd4_double.h
new file mode 100644 (file)
index 0000000..7834553
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_SIMD4_DOUBLE_H
+#define GMX_SIMD_IMPL_ARM_SVE_SIMD4_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_sve.h>
+
+#include "gromacs/math/utilities.h"
+
+namespace gmx
+{
+
+typedef struct Simd4Double
+{
+    float64_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH_VALUE / 8)));
+
+    Simd4Double(const double d) { this->simdInternal_ = svdup_f64(d); }
+
+    Simd4Double(svfloat64_t simd) : simdInternal_(simd) {}
+
+    Simd4Double() {}
+} Simd4Double;
+
+typedef struct Simd4DBool
+{
+    uint64_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH_VALUE / 8)));
+
+    Simd4DBool(const bool b) { this->simdInternal_ = svdup_n_u64_z(svptrue_b64(), b ? 1 : 0); }
+
+    Simd4DBool(svbool_t simd) { this->simdInternal_ = svdup_n_u64_z(simd, 1); }
+
+    Simd4DBool() {}
+} Simd4DBool;
+
+static inline svbool_t getMask(Simd4DBool m)
+{
+    return svcmpne_n_u64(svptrue_b64(), m.simdInternal_, 0);
+}
+
+static inline Simd4Double gmx_simdcall load4(const double* m)
+{
+    assert(std::size_t(m) % 32 == 0);
+    svbool_t pg = svwhilelt_b64(0, 4);
+    return { svld1_f64(pg, m) };
+}
+
+static inline void gmx_simdcall store4(double* m, Simd4Double a)
+{
+    assert(std::size_t(m) % 32 == 0);
+    svbool_t pg = svwhilelt_b64(0, 4);
+    svst1_f64(pg, m, a.simdInternal_);
+}
+
+static inline Simd4Double gmx_simdcall load4U(const double* m)
+{
+    svbool_t pg = svwhilelt_b64(0, 4);
+    return { svld1_f64(pg, m) };
+}
+
+static inline void gmx_simdcall store4U(double* m, Simd4Double a)
+{
+    svbool_t pg = svwhilelt_b64(0, 4);
+    svst1_f64(pg, m, a.simdInternal_);
+}
+
+static inline Simd4Double gmx_simdcall simd4SetZeroD()
+{
+    return { svdup_f64(0.0f) };
+}
+
+static inline Simd4Double gmx_simdcall operator&(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(svand_s64_z(
+            pg, svreinterpret_s64_f64(a.simdInternal_), svreinterpret_s64_f64(b.simdInternal_))) };
+}
+
+static inline Simd4Double gmx_simdcall andNot(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(svbic_s64_z(
+            pg, svreinterpret_s64_f64(b.simdInternal_), svreinterpret_s64_f64(a.simdInternal_))) };
+}
+
+static inline Simd4Double gmx_simdcall operator|(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(svorr_s64_z(
+            pg, svreinterpret_s64_f64(a.simdInternal_), svreinterpret_s64_f64(b.simdInternal_))) };
+}
+
+static inline Simd4Double gmx_simdcall operator^(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(sveor_s64_z(
+            pg, svreinterpret_s64_f64(a.simdInternal_), svreinterpret_s64_f64(b.simdInternal_))) };
+}
+
+static inline Simd4Double gmx_simdcall operator+(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svadd_f64_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator-(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svsub_f64_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator-(Simd4Double a)
+{
+    svbool_t pg = svptrue_b64();
+    return { svneg_f64_z(pg, a.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator*(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmul_f64_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmad_f64_z(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svnmsb_f64_z(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fnma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmsb_f64_z(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fnms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svnmad_f64_z(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall rsqrt(Simd4Double x)
+{
+    svbool_t    pg = svwhilelt_b64(0, 4);
+    svfloat64_t f  = svsplice_f64(pg, x.simdInternal_, svdup_n_f64(1.0f));
+    return { svrsqrte_f64(f) };
+}
+
+static inline Simd4Double gmx_simdcall abs(Simd4Double x)
+{
+    svbool_t pg = svptrue_b64();
+    return { svabs_f64_z(pg, x.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall max(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmax_f64_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall min(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmin_f64_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+// Round and trunc operations are defined at the end of this file, since they
+// need to use float-to-integer and integer-to-float conversions.
+
+static inline double gmx_simdcall reduce(Simd4Double a)
+{
+    svbool_t pg = svwhilelt_b64(0, 4);
+    return svadda_f64(pg, 0.0f, a.simdInternal_);
+}
+
+static inline Simd4DBool gmx_simdcall operator==(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpeq_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4DBool gmx_simdcall operator!=(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpne_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4DBool gmx_simdcall operator<(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmplt_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4DBool gmx_simdcall operator<=(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmple_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4DBool gmx_simdcall operator&&(Simd4DBool a, Simd4DBool b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svand_z(pg, getMask(a), getMask(b)) };
+}
+
+static inline Simd4DBool gmx_simdcall operator||(Simd4DBool a, Simd4DBool b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svorr_b_z(pg, getMask(a), getMask(b)) };
+}
+
+static inline bool gmx_simdcall anyTrue(Simd4DBool a)
+{
+    svbool_t pg = svwhilelt_b64(0, 4);
+    return svptest_any(pg, getMask(a));
+}
+
+static inline Simd4Double gmx_simdcall selectByMask(Simd4Double a, Simd4DBool m)
+{
+    return { svsel_f64(getMask(m), a.simdInternal_, svdup_f64(0.0f)) };
+}
+
+static inline Simd4Double gmx_simdcall selectByNotMask(Simd4Double a, Simd4DBool m)
+{
+    svbool_t pg = svptrue_b64();
+    return { svsel_f64(sveor_b_z(pg, getMask(m), pg), a.simdInternal_, svdup_f64(0.0f)) };
+}
+
+static inline Simd4Double gmx_simdcall blend(Simd4Double a, Simd4Double b, Simd4DBool sel)
+{
+    return { svsel_f64(getMask(sel), b.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall round(Simd4Double x)
+{
+    svbool_t pg = svptrue_b64();
+    return { svrinta_f64_z(pg, x.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall trunc(Simd4Double x)
+{
+    svbool_t pg = svwhilelt_b64(0, 4);
+    return { svcvt_f64_z(pg, svcvt_s64_z(pg, x.simdInternal_)) };
+}
+
+static inline double gmx_simdcall dotProduct(Simd4Double a, Simd4Double b)
+{
+    svbool_t pg = svwhilelt_b64(0, 3);
+    return svadda_f64(pg, 0.0f, svmul_f64_z(pg, a.simdInternal_, b.simdInternal_));
+}
+
+static inline void gmx_simdcall transpose(Simd4Double* v0, Simd4Double* v1, Simd4Double* v2, Simd4Double* v3)
+{
+    svbool_t pg = svwhilelt_b64(0, 4);
+    double   tmp[16];
+    svst1_f64(pg, tmp, v0->simdInternal_);
+    svst1_f64(pg, tmp + 4, v1->simdInternal_);
+    svst1_f64(pg, tmp + 8, v2->simdInternal_);
+    svst1_f64(pg, tmp + 12, v3->simdInternal_);
+
+    svfloat64x4_t vec = svld4_f64(pg, tmp);
+
+    v0->simdInternal_ = svget4_f64(vec, 0);
+    v1->simdInternal_ = svget4_f64(vec, 1);
+    v2->simdInternal_ = svget4_f64(vec, 2);
+    v3->simdInternal_ = svget4_f64(vec, 3);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_SIMD4_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_simd4_float.h b/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_simd4_float.h
new file mode 100644 (file)
index 0000000..959f55a
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPL_ARM_SVE_SIMD4_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_neon.h>
+
+#include "gromacs/math/utilities.h"
+
+namespace gmx
+{
+
+class Simd4Float
+{
+public:
+    Simd4Float() {}
+
+    Simd4Float(float f) : simdInternal_(vdupq_n_f32(f)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4Float(float32x4_t simd) : simdInternal_(simd) {}
+
+    float32x4_t simdInternal_;
+};
+
+class Simd4FBool
+{
+public:
+    Simd4FBool() {}
+
+    Simd4FBool(bool b) : simdInternal_(vdupq_n_u32(b ? 0xFFFFFFFF : 0)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4FBool(uint32x4_t simd) : simdInternal_(simd) {}
+
+    uint32x4_t simdInternal_;
+};
+
+static inline Simd4Float gmx_simdcall load4(const float* m)
+{
+    assert(std::size_t(m) % 16 == 0);
+    return { vld1q_f32(m) };
+}
+
+static inline void gmx_simdcall store4(float* m, Simd4Float a)
+{
+    assert(std::size_t(m) % 16 == 0);
+    vst1q_f32(m, a.simdInternal_);
+}
+
+static inline Simd4Float gmx_simdcall load4U(const float* m)
+{
+    return { vld1q_f32(m) };
+}
+
+static inline void gmx_simdcall store4U(float* m, Simd4Float a)
+{
+    vst1q_f32(m, a.simdInternal_);
+}
+
+static inline Simd4Float gmx_simdcall simd4SetZeroF()
+{
+    return { vdupq_n_f32(0.0f) };
+}
+
+static inline Simd4Float gmx_simdcall operator&(Simd4Float a, Simd4Float b)
+{
+    return { vreinterpretq_f32_s32(vandq_s32(vreinterpretq_s32_f32(a.simdInternal_),
+                                             vreinterpretq_s32_f32(b.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall andNot(Simd4Float a, Simd4Float b)
+{
+    return { vreinterpretq_f32_s32(vbicq_s32(vreinterpretq_s32_f32(b.simdInternal_),
+                                             vreinterpretq_s32_f32(a.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall operator|(Simd4Float a, Simd4Float b)
+{
+    return { vreinterpretq_f32_s32(vorrq_s32(vreinterpretq_s32_f32(a.simdInternal_),
+                                             vreinterpretq_s32_f32(b.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall operator^(Simd4Float a, Simd4Float b)
+{
+    return { vreinterpretq_f32_s32(veorq_s32(vreinterpretq_s32_f32(a.simdInternal_),
+                                             vreinterpretq_s32_f32(b.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall operator+(Simd4Float a, Simd4Float b)
+{
+    return { vaddq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float a, Simd4Float b)
+{
+    return { vsubq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float a)
+{
+    return { vnegq_f32(a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator*(Simd4Float a, Simd4Float b)
+{
+    return { vmulq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vfmaq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vnegq_f32(vfmsq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall fnma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vfmsq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vnegq_f32(vfmaq_f32(c.simdInternal_, b.simdInternal_, a.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall rsqrt(Simd4Float x)
+{
+    return { vrsqrteq_f32(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall abs(Simd4Float x)
+{
+    return { vabsq_f32(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall max(Simd4Float a, Simd4Float b)
+{
+    return { vmaxq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall min(Simd4Float a, Simd4Float b)
+{
+    return { vminq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+// Round and trunc operations are defined at the end of this file, since they
+// need to use float-to-integer and integer-to-float conversions.
+
+static inline float gmx_simdcall reduce(Simd4Float a)
+{
+    float32x4_t b = a.simdInternal_;
+    b             = vpaddq_f32(b, b);
+    b             = vpaddq_f32(b, b);
+    return vgetq_lane_f32(b, 0);
+}
+
+static inline Simd4FBool gmx_simdcall operator==(Simd4Float a, Simd4Float b)
+{
+    return { vceqq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator!=(Simd4Float a, Simd4Float b)
+{
+    return { vmvnq_u32(vceqq_f32(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<(Simd4Float a, Simd4Float b)
+{
+    return { vcltq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<=(Simd4Float a, Simd4Float b)
+{
+    return { vcleq_f32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator&&(Simd4FBool a, Simd4FBool b)
+{
+    return { vandq_u32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator||(Simd4FBool a, Simd4FBool b)
+{
+    return { vorrq_u32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(Simd4FBool a)
+{
+    return (vmaxvq_u32(a.simdInternal_) != 0);
+}
+
+static inline Simd4Float gmx_simdcall selectByMask(Simd4Float a, Simd4FBool m)
+{
+    return { vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall selectByNotMask(Simd4Float a, Simd4FBool m)
+{
+    return { vreinterpretq_f32_u32(vbicq_u32(vreinterpretq_u32_f32(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall blend(Simd4Float a, Simd4Float b, Simd4FBool sel)
+{
+    return { vbslq_f32(sel.simdInternal_, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall round(Simd4Float x)
+{
+    return { vrndnq_f32(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall trunc(Simd4Float x)
+{
+    return { vrndq_f32(x.simdInternal_) };
+}
+
+static inline float gmx_simdcall dotProduct(Simd4Float a, Simd4Float b)
+{
+    Simd4Float c;
+
+    c = a * b;
+    /* set 4th element to 0, then add all of them */
+    c.simdInternal_ = vsetq_lane_f32(0.0f, c.simdInternal_, 3);
+    return reduce(c);
+}
+
+static inline void gmx_simdcall transpose(Simd4Float* v0, Simd4Float* v1, Simd4Float* v2, Simd4Float* v3)
+{
+    float32x4x2_t t0  = vuzpq_f32(v0->simdInternal_, v2->simdInternal_);
+    float32x4x2_t t1  = vuzpq_f32(v1->simdInternal_, v3->simdInternal_);
+    float32x4x2_t t2  = vtrnq_f32(t0.val[0], t1.val[0]);
+    float32x4x2_t t3  = vtrnq_f32(t0.val[1], t1.val[1]);
+    v0->simdInternal_ = t2.val[0];
+    v1->simdInternal_ = t3.val[0];
+    v2->simdInternal_ = t2.val[1];
+    v3->simdInternal_ = t3.val[1];
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_SIMD4_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_simd_double.h b/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_simd_double.h
new file mode 100644 (file)
index 0000000..773a44e
--- /dev/null
@@ -0,0 +1,659 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPL_ARM_SVE_SIMD_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_sve.h>
+
+#include "gromacs/math/utilities.h"
+
+#include "impl_arm_sve_simd_float.h"
+
+#define SVE_DOUBLE_MASK svptrue_b64()
+#define SVE_DINT32_MASK svptrue_b64()
+
+namespace gmx
+{
+
+class SimdDouble
+{
+public:
+    SimdDouble() {}
+
+    SimdDouble(const double d) { this->simdInternal_ = svdup_f64(d); }
+
+    SimdDouble(svfloat64_t simd) : simdInternal_(simd) {}
+
+    float64_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH_VALUE / 8)));
+};
+
+class SimdDInt32
+{
+public:
+    SimdDInt32() {}
+
+    SimdDInt32(const int32_t i) { this->simdInternal_ = svdup_s64(i); }
+
+    SimdDInt32(svint64_t simd) : simdInternal_(simd) {}
+
+    int64_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH_VALUE / 8)));
+};
+
+class SimdDBool
+{
+public:
+    SimdDBool() {}
+
+    SimdDBool(const bool b)
+    {
+        this->simdInternal_ = svdup_n_u64_x(svptrue_b64(), b ? 0xFFFFFFFFFFFFFFFF : 0);
+    }
+
+    SimdDBool(svbool_t simd) { this->simdInternal_ = svdup_n_u64_z(simd, 0xFFFFFFFFFFFFFFFF); }
+
+    SimdDBool(svuint64_t simd) : simdInternal_(simd) {}
+
+    uint64_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH_VALUE / 8)));
+};
+
+class SimdDIBool
+{
+public:
+    SimdDIBool() {}
+
+    SimdDIBool(const bool b)
+    {
+        this->simdInternal_ = svdup_n_u64_x(svptrue_b64(), b ? 0xFFFFFFFFFFFFFFFF : 0);
+    }
+
+    SimdDIBool(svbool_t simd) { this->simdInternal_ = svdup_n_u64_z(simd, 0xFFFFFFFFFFFFFFFF); }
+
+    SimdDIBool(svuint64_t simd) : simdInternal_(simd) {}
+
+    uint64_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH_VALUE / 8)));
+};
+
+static inline SimdDouble gmx_simdcall simdLoad(const double* m, SimdDoubleTag = {})
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = SVE_DOUBLE_MASK;
+    return { svld1_f64(pg, m) };
+}
+
+static inline SimdDouble gmx_simdcall simdLoad(SimdDouble* m, int offset, SimdDoubleTag = {})
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = SVE_DOUBLE_MASK;
+    return { svld1_f64(pg, reinterpret_cast<double*>(m) + offset * svcntd()) };
+}
+
+static inline SimdDouble gmx_simdcall simdLoadDouble(const double* m)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = SVE_DOUBLE_MASK;
+    return { svld1_f64(pg, m) };
+}
+
+static inline void gmx_simdcall store(double* m, SimdDouble a)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = SVE_DOUBLE_MASK;
+    svst1_f64(pg, m, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall simdLoadU(const double* m, SimdDoubleTag = {})
+{
+    svbool_t pg = SVE_DOUBLE_MASK;
+    return { svld1_f64(pg, m) };
+}
+
+static inline void gmx_simdcall storeU(double* m, SimdDouble a)
+{
+    svbool_t pg = SVE_DOUBLE_MASK;
+    svst1_f64(pg, m, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall setZeroD()
+{
+    return { svdup_f64(0.0) };
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdDInt32Tag)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH);
+    return { svunpklo_s64(svld1_s32(pg, m)) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdDInt32 a)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH);
+    svst1_s32(pg, m, svuzp1(svreinterpret_s32_s64(a.simdInternal_), svreinterpret_s32_s64(a.simdInternal_)));
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdDInt32Tag)
+{
+    svbool_t pg = svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH);
+    return { svunpklo_s64(svld1_s32(pg, m)) };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdDInt32 a)
+{
+    svbool_t pg = svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH);
+    svst1_s32(pg, m, svuzp1(svreinterpret_s32_s64(a.simdInternal_), svreinterpret_s32_s64(a.simdInternal_)));
+}
+
+static inline SimdDInt32 gmx_simdcall setZeroDI()
+{
+    return { svdup_s64(0) };
+}
+
+template<int index>
+gmx_simdcall static inline std::int32_t extract(SimdDInt32 a)
+{
+    svbool_t pg = svwhilelt_b64(0, index);
+    return svlasta_s64(pg, a.simdInternal_);
+}
+
+template<int index>
+gmx_simdcall static inline double extract(SimdDouble a)
+{
+    svbool_t pg = svwhilelt_b64(0, index);
+    return svlasta_f64(pg, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall operator&(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(svand_s64_x(
+            pg, svreinterpret_s64_f64(a.simdInternal_), svreinterpret_s64_f64(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall andNot(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(svbic_s64_x(
+            pg, svreinterpret_s64_f64(b.simdInternal_), svreinterpret_s64_f64(a.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall operator|(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(svorr_s64_x(
+            pg, svreinterpret_s64_f64(a.simdInternal_), svreinterpret_s64_f64(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall operator^(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_s64(sveor_s64_x(
+            pg, svreinterpret_s64_f64(a.simdInternal_), svreinterpret_s64_f64(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall operator+(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svadd_f64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svsub_f64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble a)
+{
+    svbool_t pg = svptrue_b64();
+    return { svneg_f64_x(pg, a.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator*(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmul_f64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmad_f64_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svnmsb_f64_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmsb_f64_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    svbool_t pg = svptrue_b64();
+    return { svnmad_f64_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall rsqrt(SimdDouble x)
+{
+    return { svrsqrte_f64(x.simdInternal_) };
+}
+
+// The SIMD implementation seems to overflow when we square lu for
+// values close to FLOAT_MAX, so we fall back on the version in
+// simd_math.h, which is probably slightly slower.
+#if GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE
+static inline SimdDouble gmx_simdcall rsqrtIter(SimdDouble lu, SimdDouble x)
+{
+    return { vmulq_f64(lu.simdInternal_,
+                       vrsqrtsq_f32(vmulq_f32(lu.simdInternal_, lu.simdInternal_), x.simdInternal_)) };
+}
+#endif
+
+static inline SimdDouble gmx_simdcall rcp(SimdDouble x)
+{
+    return { svrecpe_f64(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall rcpIter(SimdDouble lu, SimdDouble x)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmul_f64_x(pg, lu.simdInternal_, svrecps_f64(lu.simdInternal_, x.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall maskAdd(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    return { svadd_f64_m(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzMul(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    return { svmul_f64_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzFma(SimdDouble a, SimdDouble b, SimdDouble c, SimdDBool m)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    return { svmad_f64_z(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzRsqrt(SimdDouble x, SimdDBool m)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#ifndef NDEBUG
+    x.simdInternal_ = svsel_f64(pg, x.simdInternal_, svdup_n_f64(1.0));
+#endif
+    return { svreinterpret_f64_u64(svand_n_u64_z(
+            pg, svreinterpret_u64_f64(svrsqrte_f64(x.simdInternal_)), 0xFFFFFFFFFFFFFFFF)) };
+}
+
+static inline SimdDouble gmx_simdcall maskzRcp(SimdDouble x, SimdDBool m)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#ifndef NDEBUG
+    x.simdInternal_ = svsel_f64(pg, x.simdInternal_, svdup_n_f64(1.0));
+#endif
+    return { svreinterpret_f64_u64(svand_n_u64_z(
+            pg, svreinterpret_u64_f64(svrecpe_f64(x.simdInternal_)), 0xFFFFFFFFFFFFFFFF)) };
+}
+
+static inline SimdDouble gmx_simdcall abs(SimdDouble x)
+{
+    svbool_t pg = svptrue_b64();
+    return { svabs_f64_x(pg, x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall max(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmax_f64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall min(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmin_f64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+// Round and trunc operations are defined at the end of this file, since they
+// need to use double-to-integer and integer-to-double conversions.
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall frexp(SimdDouble value, SimdDInt32* exponent)
+{
+    svbool_t        pg           = svptrue_b64();
+    const svint64_t exponentMask = svdup_n_s64(0x7FF0000000000000LL);
+    const svint64_t mantissaMask = svdup_n_s64(0x800FFFFFFFFFFFFFLL);
+    const svint64_t exponentBias = svdup_n_s64(1022LL); // add 1 to make our definition identical to frexp()
+    const svfloat64_t half = svdup_n_f64(0.5);
+    svint64_t         iExponent;
+
+    iExponent = svand_s64_x(pg, svreinterpret_s64_f64(value.simdInternal_), exponentMask);
+    iExponent = svsub_s64_x(
+            pg, svreinterpret_s64_u64(svlsr_n_u64_x(pg, svreinterpret_u64_s64(iExponent), 52)), exponentBias);
+
+
+    svfloat64_t result = svreinterpret_f64_s64(
+            svorr_s64_x(pg,
+                        svand_s64_x(pg, svreinterpret_s64_f64(value.simdInternal_), mantissaMask),
+                        svreinterpret_s64_f64(half)));
+
+    if (opt == MathOptimization::Safe)
+    {
+        svbool_t valueIsZero = svcmpeq_n_f64(pg, value.simdInternal_, 0.0);
+        iExponent            = svsel_s64(valueIsZero, svdup_s64(0), iExponent);
+        result               = svsel_f64(valueIsZero, value.simdInternal_, result);
+    }
+
+    exponent->simdInternal_ = iExponent;
+    return { result };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall ldexp(SimdDouble value, SimdDInt32 exponent)
+{
+    svbool_t        pg           = svptrue_b64();
+    const svint64_t exponentBias = svdup_n_s64(1023);
+    svint64_t       iExponent    = svadd_s64_x(pg, exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = svmax_n_s64_x(pg, iExponent, 0);
+    }
+
+    iExponent = svlsl_n_s64_x(pg, iExponent, 52);
+
+    return { svmul_f64_x(pg, value.simdInternal_, svreinterpret_f64_s64(iExponent)) };
+}
+
+static inline double gmx_simdcall reduce(SimdDouble a)
+{
+    svbool_t pg = svptrue_b64();
+    return svadda_f64(pg, 0.0f, a.simdInternal_);
+}
+
+static inline SimdDBool gmx_simdcall operator==(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpeq_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator!=(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpne_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator<(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmplt_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator<=(SimdDouble a, SimdDouble b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmple_f64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall testBits(SimdDouble a)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpne_n_s64(pg, svreinterpret_s64_f64(a.simdInternal_), 0) };
+}
+
+static inline SimdDBool gmx_simdcall operator&&(SimdDBool a, SimdDBool b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svand_u64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator||(SimdDBool a, SimdDBool b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svorr_u64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDBool a)
+{
+    svbool_t pg = svptrue_b64();
+    return svptest_any(pg, svcmpne_n_u64(pg, a.simdInternal_, 0));
+}
+
+static inline bool gmx_simdcall extractFirst(SimdDBool a)
+{
+    svbool_t pg = svptrue_b64();
+    return svptest_first(pg, svcmpne_n_u64(pg, a.simdInternal_, 0));
+}
+
+static inline SimdDouble gmx_simdcall selectByMask(SimdDouble a, SimdDBool m)
+{
+    svbool_t pg = svptrue_b64();
+    return { svreinterpret_f64_u64(svand_u64_x(pg, svreinterpret_u64_f64(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall selectByNotMask(SimdDouble a, SimdDBool m)
+{
+    svbool_t pg = svcmpeq_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    return { svsel_f64(pg, a.simdInternal_, svdup_f64(0.0f)) };
+}
+
+static inline SimdDouble gmx_simdcall blend(SimdDouble a, SimdDouble b, SimdDBool sel)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), sel.simdInternal_, 0);
+    return { svsel_f64(pg, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator&(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svand_s64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall andNot(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svbic_s64_x(pg, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator|(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svorr_s64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator^(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { sveor_s64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator+(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svadd_s64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator-(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svsub_s64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator*(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svmul_s64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator==(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpeq_s64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall testBits(SimdDInt32 a)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmpne_n_s64(pg, a.simdInternal_, (int64_t)0) };
+}
+
+static inline SimdDIBool gmx_simdcall operator<(SimdDInt32 a, SimdDInt32 b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcmplt_s64(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator&&(SimdDIBool a, SimdDIBool b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svand_u64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator||(SimdDIBool a, SimdDIBool b)
+{
+    svbool_t pg = svptrue_b64();
+    return { svorr_u64_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDIBool a)
+{
+    svbool_t pg = svptrue_b64();
+    return svptest_any(pg, svcmpne_n_u64(pg, a.simdInternal_, 0));
+}
+
+static inline SimdDInt32 gmx_simdcall selectByMask(SimdDInt32 a, SimdDIBool m)
+{
+    svbool_t pg = svptrue_b64();
+    return { svand_s64_x(pg, a.simdInternal_, svreinterpret_s64_u64(m.simdInternal_)) };
+}
+
+static inline SimdDInt32 gmx_simdcall selectByNotMask(SimdDInt32 a, SimdDIBool m)
+{
+    svbool_t pg = svcmpeq_n_u64(svptrue_b64(), m.simdInternal_, 0);
+    return { svadd_n_s64_z(pg, a.simdInternal_, 0) };
+}
+
+static inline SimdDInt32 gmx_simdcall blend(SimdDInt32 a, SimdDInt32 b, SimdDIBool sel)
+{
+    svbool_t pg = svcmpne_n_u64(svptrue_b64(), sel.simdInternal_, 0);
+    return { svsel_s64(pg, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall cvtR2I(SimdDouble a)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcvt_s64_x(pg, svrinta_f64_x(pg, a.simdInternal_)) };
+}
+
+static inline SimdDInt32 gmx_simdcall cvttR2I(SimdDouble a)
+{
+    // FIXME ???
+    svbool_t pg = svptrue_b64();
+    return { svcvt_s64_x(pg, a.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall cvtI2R(SimdDInt32 a)
+{
+    svbool_t pg = svptrue_b64();
+    return { svcvt_f64_x(pg, a.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall cvtB2IB(SimdDBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdDBool gmx_simdcall cvtIB2B(SimdDIBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdDouble gmx_simdcall round(SimdDouble x)
+{
+    svbool_t pg = svptrue_b64();
+    return { svrinta_f64_x(pg, x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall trunc(SimdDouble x)
+{
+    return cvtI2R(cvttR2I(x));
+}
+
+static inline void gmx_simdcall cvtF2DD(SimdFloat gmx_unused f,
+                                        SimdDouble gmx_unused* d0,
+                                        SimdDouble gmx_unused* d1)
+{
+    assert(GMX_SIMD_FLOAT_WIDTH == 2 * GMX_SIMD_DOUBLE_WIDTH);
+    svbool_t pg       = svptrue_b32();
+    d0->simdInternal_ = svcvt_f64_f32_x(pg, svzip1(f.simdInternal_, f.simdInternal_));
+    d1->simdInternal_ = svcvt_f64_f32_x(pg, svzip2(f.simdInternal_, f.simdInternal_));
+}
+
+static inline SimdFloat gmx_simdcall cvtDD2F(SimdDouble gmx_unused d0, SimdDouble gmx_unused d1)
+{
+    svbool_t pg = svptrue_b64();
+    assert(GMX_SIMD_FLOAT_WIDTH == 2 * GMX_SIMD_DOUBLE_WIDTH);
+    return { svuzp1_f32(svcvt_f32_f64_x(pg, d0.simdInternal_), svcvt_f32_f64_x(pg, d1.simdInternal_)) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_SIMD_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_simd_float.h b/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_simd_float.h
new file mode 100644 (file)
index 0000000..4af421f
--- /dev/null
@@ -0,0 +1,645 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_SIMD_FLOAT_H
+#define GMX_SIMD_IMPL_ARM_SVE_SIMD_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_sve.h>
+
+#include "gromacs/math/utilities.h"
+
+namespace gmx
+{
+
+class SimdFloat
+{
+public:
+    SimdFloat() {}
+
+    SimdFloat(const float f) { this->simdInternal_ = svdup_f32(f); }
+
+    SimdFloat(svfloat32_t simd) : simdInternal_(simd) {}
+
+    float32_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH_VALUE / 8)));
+};
+
+class SimdFInt32
+{
+public:
+    SimdFInt32() {}
+
+    SimdFInt32(const int32_t i) { this->simdInternal_ = svdup_s32(i); }
+
+    SimdFInt32(svint32_t simd) : simdInternal_(simd) {}
+
+    int32_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH_VALUE / 8)));
+};
+
+class SimdFBool
+{
+public:
+    SimdFBool() {}
+
+    SimdFBool(const bool b)
+    {
+        this->simdInternal_ = svdup_n_u32_x(svptrue_b32(), b ? 0xFFFFFFFF : 0);
+    }
+
+    SimdFBool(svbool_t simd) { this->simdInternal_ = svdup_n_u32_z(simd, 0xFFFFFFFF); }
+
+    SimdFBool(svuint32_t simd) : simdInternal_(simd) {}
+
+    uint32_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH_VALUE / 8)));
+};
+
+class SimdFIBool
+{
+public:
+    SimdFIBool() {}
+
+    SimdFIBool(const bool b)
+    {
+        this->simdInternal_ = svdup_n_u32_x(svptrue_b32(), b ? 0xFFFFFFFF : 0);
+    }
+
+    SimdFIBool(svbool_t simd) { this->simdInternal_ = svdup_n_u32_z(simd, 0xFFFFFFFF); }
+
+    SimdFIBool(svuint32_t simd) : simdInternal_(simd) {}
+
+    uint32_t simdInternal_ __attribute__((vector_size(GMX_SIMD_ARM_SVE_LENGTH_VALUE / 8)));
+};
+
+static inline SimdFloat gmx_simdcall simdLoad(const float* m, SimdFloatTag = {})
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svptrue_b32();
+    return { svld1_f32(pg, m) };
+}
+
+static inline SimdFloat gmx_simdcall simdLoad(SimdFloat* m, int offset, SimdFloatTag = {})
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svptrue_b32();
+    return { svld1_f32(pg, reinterpret_cast<float*>(m) + offset * svcntw()) };
+}
+
+static inline SimdFloat gmx_simdcall simdLoadFloat(const float* m)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svptrue_b32();
+    return { svld1_f32(pg, m) };
+}
+
+static inline void gmx_simdcall store(float* m, SimdFloat a)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svptrue_b32();
+    svst1_f32(pg, m, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall simdLoadU(const float* m, SimdFloatTag = {})
+{
+    svbool_t pg = svptrue_b32();
+    return { svld1_f32(pg, m) };
+}
+
+static inline void gmx_simdcall storeU(float* m, SimdFloat a)
+{
+    svbool_t pg = svptrue_b32();
+    svst1_f32(pg, m, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall setZeroF()
+{
+    return { svdup_f32(0.0f) };
+}
+
+static inline void gmx_simdcall simdIncr(SimdFloat*& p, SimdFloatTag)
+{
+    p = reinterpret_cast<SimdFloat*>(reinterpret_cast<uint64_t>(p) + svcntw());
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdFInt32Tag)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svptrue_b32();
+    return { svld1_s32(pg, m) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdFInt32 a)
+{
+    assert(0 == (std::size_t(m) % GMX_SIMD_ALIGNMENT));
+    svbool_t pg = svptrue_b32();
+    svst1_s32(pg, m, a.simdInternal_);
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdFInt32Tag)
+{
+    svbool_t pg = svptrue_b32();
+    return { svld1_s32(pg, m) };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdFInt32 a)
+{
+    svbool_t pg = svptrue_b32();
+    svst1_s32(pg, m, a.simdInternal_);
+}
+
+static inline SimdFInt32 gmx_simdcall setZeroFI()
+{
+    return { svdup_s32(0) };
+}
+
+template<int index>
+gmx_simdcall static inline std::int32_t extract(SimdFInt32 a)
+{
+    svbool_t pg = svwhilelt_b32(0, index);
+    return svlasta_s32(pg, a.simdInternal_);
+}
+
+template<int index>
+gmx_simdcall static inline float extract(SimdFloat a)
+{
+    svbool_t pg = svwhilelt_b32(0, index);
+    return svlasta_f32(pg, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall operator&(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svreinterpret_f32_s32(svand_s32_x(
+            pg, svreinterpret_s32_f32(a.simdInternal_), svreinterpret_s32_f32(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall andNot(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svreinterpret_f32_s32(svbic_s32_x(
+            pg, svreinterpret_s32_f32(b.simdInternal_), svreinterpret_s32_f32(a.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall operator|(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svreinterpret_f32_s32(svorr_s32_x(
+            pg, svreinterpret_s32_f32(a.simdInternal_), svreinterpret_s32_f32(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall operator^(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svreinterpret_f32_s32(sveor_s32_x(
+            pg, svreinterpret_s32_f32(a.simdInternal_), svreinterpret_s32_f32(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall operator+(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svadd_f32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svsub_f32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat a)
+{
+    svbool_t pg = svptrue_b32();
+    return { svneg_f32_x(pg, a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator*(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmul_f32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmad_f32_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    svbool_t pg = svptrue_b32();
+    return { svnmsb_f32_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmsb_f32_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    svbool_t pg = svptrue_b32();
+    return { svnmad_f32_x(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall rsqrt(SimdFloat x)
+{
+    return { svrsqrte_f32(x.simdInternal_) };
+}
+
+// The SIMD implementation seems to overflow when we square lu for
+// values close to FLOAT_MAX, so we fall back on the version in
+// simd_math.h, which is probably slightly slower.
+#if GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT
+static inline SimdFloat gmx_simdcall rsqrtIter(SimdFloat lu, SimdFloat x)
+{
+    svbool_t    pg = svptrue_b32();
+    svfloat32_t tmp1, tmp2;
+    tmp1 = svmul_f32_x(pg, x.simdInternal_, lu.simdInternal_);
+    tmp2 = svmul_n_f32_x(pg, lu.simdInternal_, -0.5f);
+    tmp1 = svmad_n_f32_x(pg, tmp1, lu.simdInternal_, -3.0f);
+    return { svmul_f32_x(pg, tmp1, tmp2) };
+}
+
+#endif
+
+static inline SimdFloat gmx_simdcall rcp(SimdFloat x)
+{
+    return { svrecpe_f32(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall rcpIter(SimdFloat lu, SimdFloat x)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmul_f32_x(pg, lu.simdInternal_, svrecps_f32(lu.simdInternal_, x.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall maskAdd(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    return { svadd_f32_m(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzMul(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    return { svmul_f32_z(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzFma(SimdFloat a, SimdFloat b, SimdFloat c, SimdFBool m)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    return { svmad_f32_z(pg, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzRsqrt(SimdFloat x, SimdFBool m)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#ifndef NDEBUG
+    x.simdInternal_ = svsel_f32(pg, x.simdInternal_, svdup_n_f32(1.0f));
+#endif
+    return { svreinterpret_f32_u32(
+            svand_n_u32_z(pg, svreinterpret_u32_f32(svrsqrte_f32(x.simdInternal_)), 0xFFFFFFFF)) };
+}
+
+static inline SimdFloat gmx_simdcall maskzRcp(SimdFloat x, SimdFBool m)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#ifndef NDEBUG
+    x.simdInternal_ = svsel_f32(pg, x.simdInternal_, svdup_n_f32(1.0f));
+#endif
+    return { svreinterpret_f32_u32(
+            svand_n_u32_z(pg, svreinterpret_u32_f32(svrecpe_f32(x.simdInternal_)), 0xFFFFFFFF)) };
+}
+
+static inline SimdFloat gmx_simdcall abs(SimdFloat x)
+{
+    svbool_t pg = svptrue_b32();
+    return { svabs_f32_x(pg, x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall max(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmax_f32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall min(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmin_f32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+// Round and trunc operations are defined at the end of this file, since they
+// need to use float-to-integer and integer-to-float conversions.
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall frexp(SimdFloat value, SimdFInt32* exponent)
+{
+    svbool_t        pg           = svptrue_b32();
+    const svint32_t exponentMask = svdup_n_s32(0x7F800000);
+    const svint32_t mantissaMask = svdup_n_s32(0x807FFFFF);
+    const svint32_t exponentBias = svdup_n_s32(126); // add 1 to make our definition identical to frexp()
+    const svfloat32_t half       = svdup_n_f32(0.5f);
+    svint32_t         iExponent;
+
+    iExponent = svand_s32_x(pg, svreinterpret_s32_f32(value.simdInternal_), exponentMask);
+    iExponent = svsub_s32_x(
+            pg, svreinterpret_s32_u32(svlsr_n_u32_x(pg, svreinterpret_u32_s32(iExponent), 23)), exponentBias);
+
+    svfloat32_t result = svreinterpret_f32_s32(
+            svorr_s32_x(pg,
+                        svand_s32_x(pg, svreinterpret_s32_f32(value.simdInternal_), mantissaMask),
+                        svreinterpret_s32_f32(half)));
+
+    if (opt == MathOptimization::Safe)
+    {
+        svbool_t valueIsZero = svcmpeq_n_f32(pg, value.simdInternal_, 0.0F);
+        iExponent            = svsel_s32(valueIsZero, svdup_s32(0), iExponent);
+        result               = svsel_f32(valueIsZero, value.simdInternal_, result);
+    }
+
+    exponent->simdInternal_ = iExponent;
+    return { result };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall ldexp(SimdFloat value, SimdFInt32 exponent)
+{
+    svbool_t        pg           = svptrue_b32();
+    const svint32_t exponentBias = svdup_n_s32(127);
+    svint32_t       iExponent    = svadd_s32_x(pg, exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = svmax_n_s32_x(pg, iExponent, 0);
+    }
+
+    iExponent = svlsl_n_s32_x(pg, iExponent, 23);
+
+    return { svmul_f32_x(pg, value.simdInternal_, svreinterpret_f32_s32(iExponent)) };
+}
+
+static inline float gmx_simdcall reduce(SimdFloat a)
+{
+    svbool_t pg = svptrue_b32();
+    return svaddv_f32(pg, a.simdInternal_);
+}
+
+static inline SimdFBool gmx_simdcall operator==(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmpeq_f32(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator!=(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmpne_f32(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator<(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmplt_f32(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator<=(SimdFloat a, SimdFloat b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmple_f32(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall testBits(SimdFloat a)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmpne_n_s32(pg, svreinterpret_s32_f32(a.simdInternal_), 0) };
+}
+
+static inline SimdFBool gmx_simdcall operator&&(SimdFBool a, SimdFBool b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svand_u32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator||(SimdFBool a, SimdFBool b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svorr_u32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFBool a)
+{
+    svbool_t pg = svptrue_b32();
+    return svptest_any(pg, svcmpne_n_u32(svptrue_b32(), a.simdInternal_, 0));
+}
+
+static inline bool gmx_simdcall extractFirst(SimdFBool a)
+{
+    svbool_t pg = svptrue_b32();
+    return svptest_first(pg, svcmpne_n_u32(svptrue_b32(), a.simdInternal_, 0));
+}
+
+static inline SimdFloat gmx_simdcall selectByMask(SimdFloat a, SimdFBool m)
+{
+    svbool_t pg = svptrue_b32();
+    return { svreinterpret_f32_u32(svand_u32_x(pg, svreinterpret_u32_f32(a.simdInternal_), m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall selectByNotMask(SimdFloat a, SimdFBool m)
+{
+    svbool_t pg = svcmpeq_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    return { svsel_f32(pg, a.simdInternal_, svdup_f32(0.0f)) };
+}
+
+static inline SimdFloat gmx_simdcall blend(SimdFloat a, SimdFloat b, SimdFBool sel)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), sel.simdInternal_, 0);
+    return { svsel_f32(pg, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator&(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svand_s32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall andNot(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svbic_s32_x(pg, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator|(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svorr_s32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator^(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { sveor_s32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator+(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svadd_s32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator-(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svsub_s32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator*(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svmul_s32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator==(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmpeq_s32(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall testBits(SimdFInt32 a)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmpne_n_s32(pg, a.simdInternal_, (int32_t)0) };
+}
+
+static inline SimdFIBool gmx_simdcall operator<(SimdFInt32 a, SimdFInt32 b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcmplt_s32(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator&&(SimdFIBool a, SimdFIBool b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svand_u32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator||(SimdFIBool a, SimdFIBool b)
+{
+    svbool_t pg = svptrue_b32();
+    return { svorr_u32_x(pg, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFIBool a)
+{
+    svbool_t pg = svptrue_b32();
+    return svptest_any(pg, svcmpne_n_u32(pg, a.simdInternal_, 0));
+}
+
+static inline SimdFInt32 gmx_simdcall selectByMask(SimdFInt32 a, SimdFIBool m)
+{
+    svbool_t pg = svptrue_b32();
+    return { svand_s32_x(pg, a.simdInternal_, svreinterpret_s32_u32(m.simdInternal_)) };
+}
+
+static inline SimdFInt32 gmx_simdcall selectByNotMask(SimdFInt32 a, SimdFIBool m)
+{
+    svbool_t pg = svcmpeq_n_u32(svptrue_b32(), m.simdInternal_, 0);
+    return { svadd_n_s32_z(pg, a.simdInternal_, 0) };
+}
+
+static inline SimdFInt32 gmx_simdcall blend(SimdFInt32 a, SimdFInt32 b, SimdFIBool sel)
+{
+    svbool_t pg = svcmpne_n_u32(svptrue_b32(), sel.simdInternal_, 0);
+    return { svsel_s32(pg, b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvtR2I(SimdFloat a)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcvt_s32_x(pg, svrinta_f32_x(pg, a.simdInternal_)) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvttR2I(SimdFloat a)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcvt_s32_x(pg, a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall cvtI2R(SimdFInt32 a)
+{
+    svbool_t pg = svptrue_b32();
+    return { svcvt_f32_x(pg, a.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall cvtB2IB(SimdFBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdFBool gmx_simdcall cvtIB2B(SimdFIBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdFloat gmx_simdcall round(SimdFloat x)
+{
+    svbool_t pg = svptrue_b32();
+    return { svrinta_f32_x(pg, x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall trunc(SimdFloat x)
+{
+    return cvtI2R(cvttR2I(x));
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_SIMD_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_util_double.h b/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_util_double.h
new file mode 100644 (file)
index 0000000..d7c8393
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPL_ARM_SVE_UTIL_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_sve.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_arm_sve_simd_double.h"
+
+
+namespace gmx
+{
+
+namespace
+{
+inline void gmx_simdcall decrHsimd(double* m, SimdDouble a)
+{
+    // Make sure the memory pointer is aligned to half float SIMD width
+    assert(std::size_t(m) % 32 == 0);
+
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    svfloat64_t v0, v1, v2, v3;
+    v0 = svld1_f64(pg, m);
+    v1 = svext_f64(a.simdInternal_, a.simdInternal_, GMX_SIMD_DOUBLE_WIDTH / 2);
+    v2 = svadd_f64_x(pg, a.simdInternal_, v1);
+    v3 = svsub_f64_x(pg, v0, v2);
+    svst1_f64(pg, m, v3);
+}
+} // namespace
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const double*      base,
+                                                    const std::int32_t offset[],
+                                                    SimdDouble*        v0,
+                                                    SimdDouble*        v1,
+                                                    SimdDouble*        v2,
+                                                    SimdDouble*        v3)
+{
+    assert(std::size_t(offset) % 16 == 0);
+    assert(std::size_t(base) % 64 == 0);
+    assert(align % 4 == 0);
+
+    svint64_t offsets;
+    svbool_t  pg = svptrue_b64();
+    offsets      = svmul_n_s64_x(
+            pg,
+            svunpklo_s64(svld1_s32(svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH), offset)),
+            align * sizeof(double));
+    v0->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v1->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v2->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v3->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    // Base pointer must be aligned to the smaller of 2 elements and float SIMD width
+    assert(std::size_t(base) % 8 == 0);
+    // align parameter must also be a multiple of the above alignment requirement
+    assert(align % 2 == 0);
+
+    svbool_t  pg = svptrue_b64();
+    svint64_t offsets;
+    offsets           = svmul_n_s64_x(pg, offset.simdInternal_, align * sizeof(double));
+    v0->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v1->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadTranspose(const double* base, const std::int32_t offset[], SimdDouble* v0, SimdDouble* v1)
+{
+    assert(std::size_t(offset) % 64 == 0);
+    assert(std::size_t(base) % 8 == 0);
+    assert(align % 2 == 0);
+
+    SimdDInt32 offsets;
+    svbool_t   pg         = svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH);
+    offsets.simdInternal_ = svunpklo_s64(svld1_s32(pg, offset));
+    gatherLoadBySimdIntTranspose<align>(base, offsets, v0, v1);
+}
+
+static const int c_simdBestPairAlignmentDouble = 2;
+
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const double*      base,
+                                                     const std::int32_t offset[],
+                                                     SimdDouble*        v0,
+                                                     SimdDouble*        v1,
+                                                     SimdDouble*        v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    svint64_t offsets;
+    svbool_t  pg = svptrue_b64();
+    offsets      = svmul_n_s64_x(
+            pg,
+            svunpklo_s64(svld1_s32(svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH), offset)),
+            align * sizeof(double));
+    v0->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v1->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v2->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+}
+
+
+template<int align>
+static inline void gmx_simdcall transposeScatterStoreU(double*            base,
+                                                       const std::int32_t offset[],
+                                                       SimdDouble         v0,
+                                                       SimdDouble         v1,
+                                                       SimdDouble         v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    svint64_t offsets;
+    svbool_t  pg = svptrue_b64();
+    offsets      = svmul_n_s64_x(
+            pg,
+            svunpklo_s64(svld1_s32(svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH), offset)),
+            align * sizeof(double));
+    svst1_scatter_s64offset_f64(pg, base, offsets, v0.simdInternal_);
+    offsets = svadd_n_s64_x(pg, offsets, sizeof(double));
+    svst1_scatter_s64offset_f64(pg, base, offsets, v1.simdInternal_);
+    offsets = svadd_n_s64_x(pg, offsets, sizeof(double));
+    svst1_scatter_s64offset_f64(pg, base, offsets, v2.simdInternal_);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    assert(std::size_t(offset) % 32 == 0);
+
+    svbool_t                           pg = svptrue_b64();
+    svfloat64x3_t                      v;
+    alignas(GMX_SIMD_ALIGNMENT) double tvec[3 * GMX_SIMD_DOUBLE_WIDTH];
+    v = svcreate3_f64(v0.simdInternal_, v1.simdInternal_, v2.simdInternal_);
+    svst3_f64(pg, tvec, v);
+#if GMX_SIMD_DOUBLE_WIDTH >= 3
+    pg = svwhilelt_b64(0, 3);
+    for (int i = 0; i < GMX_SIMD_DOUBLE_WIDTH; i++)
+    {
+        svfloat64_t t1 = svld1_f64(pg, base + align * offset[i]);
+        svfloat64_t t2 = svld1_f64(pg, tvec + 3 * i);
+        svfloat64_t t3 = svadd_f64_x(pg, t1, t2);
+        svst1_f64(pg, base + align * offset[i], t3);
+    }
+#else
+    for (std::size_t i = 0; i < GMX_SIMD_DOUBLE_WIDTH; i++)
+    {
+        for (int j = 0; j < 3; j++)
+        {
+            base[align * offset[i] + j] += tvec[i * 3 + j];
+        }
+    }
+#endif
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    svbool_t                           pg = svptrue_b64();
+    svfloat64x3_t                      v;
+    alignas(GMX_SIMD_ALIGNMENT) double tvec[3 * GMX_SIMD_DOUBLE_WIDTH];
+    v = svcreate3_f64(v0.simdInternal_, v1.simdInternal_, v2.simdInternal_);
+    svst3_f64(pg, tvec, v);
+#if GMX_SIMD_DOUBLE_WIDTH >= 3
+    pg = svwhilelt_b64(0, 3);
+    for (int i = 0; i < GMX_SIMD_DOUBLE_WIDTH; i++)
+    {
+        svfloat64_t t1 = svld1_f64(pg, base + align * offset[i]);
+        svfloat64_t t2 = svld1_f64(pg, tvec + 3 * i);
+        svfloat64_t t3 = svsub_f64_x(pg, t1, t2);
+        svst1_f64(pg, base + align * offset[i], t3);
+    }
+#else
+    for (std::size_t i = 0; i < GMX_SIMD_DOUBLE_WIDTH; i++)
+    {
+        for (int j = 0; j < 3; j++)
+        {
+            base[align * offset[i] + j] -= tvec[i * 3 + j];
+        }
+    }
+#endif
+}
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdDouble  scalar,
+                                                        SimdDouble* triplets0,
+                                                        SimdDouble* triplets1,
+                                                        SimdDouble* triplets2)
+{
+    assert(GMX_SIMD_DOUBLE_WIDTH <= 16);
+    uint64_t   ind[48] = { 0,  0,  0,  1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  5,
+                         5,  5,  6,  6,  6,  7,  7,  7,  8,  8,  8,  9,  9,  9,  10, 10,
+                         10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15 };
+    svbool_t   pg;
+    svuint64_t idx;
+
+    pg                       = svptrue_b64();
+    idx                      = svld1_u64(pg, ind);
+    triplets0->simdInternal_ = svtbl_f64(scalar.simdInternal_, idx);
+    idx                      = svld1_u64(pg, ind + GMX_SIMD_DOUBLE_WIDTH);
+    triplets1->simdInternal_ = svtbl_f64(scalar.simdInternal_, idx);
+    idx                      = svld1_u64(pg, ind + 2 * GMX_SIMD_DOUBLE_WIDTH);
+    triplets2->simdInternal_ = svtbl_f64(scalar.simdInternal_, idx);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const double* base,
+                                                             SimdDInt32    offset,
+                                                             SimdDouble*   v0,
+                                                             SimdDouble*   v1,
+                                                             SimdDouble*   v2,
+                                                             SimdDouble*   v3)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_FINT32_WIDTH];
+
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 4 == 0);
+
+    store(ioffset, offset);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1, v2, v3);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    svbool_t  pg      = svptrue_b64();
+    svint64_t offsets = svmul_n_s64_x(pg, offset.simdInternal_, align * sizeof(double));
+    v0->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    v1->simdInternal_ = svld1_gather_s64offset_f64(pg, base, offsets);
+}
+
+static inline double gmx_simdcall
+reduceIncr4ReturnSum(double* m, SimdDouble v0, SimdDouble v1, SimdDouble v2, SimdDouble v3)
+{
+    assert(std::size_t(m) % 16 == 0);
+    svbool_t    pg = svptrue_b64();
+    svfloat64_t _m, _s;
+    double      sum[4];
+    sum[0] = svadda_f64(pg, 0.0, v0.simdInternal_);
+    sum[1] = svadda_f64(pg, 0.0, v1.simdInternal_);
+    sum[2] = svadda_f64(pg, 0.0, v2.simdInternal_);
+    sum[3] = svadda_f64(pg, 0.0, v3.simdInternal_);
+#if GMX_SIMD_DOUBLE_WIDTH >= 4
+    pg = svwhilelt_b64(0, 4);
+    _m = svld1_f64(pg, m);
+    _s = svld1_f64(pg, sum);
+    svst1_f64(pg, m, svadd_f64_x(pg, _m, _s));
+    return svadda_f64(pg, 0.0, _s);
+#else
+    double res = 0;
+    for (int i = 0; i < 4; i++)
+    {
+        m[i] += sum[i];
+        res += sum[i];
+    }
+    return res;
+#endif
+}
+
+static inline SimdDouble gmx_simdcall loadDualHsimd(const double* m0, const double* m1)
+{
+    svfloat64_t v0, v1;
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    v0             = svld1_f64(pg, m0);
+    v1             = svld1_f64(pg, m1);
+    return { svsplice_f64(pg, v0, v1) };
+}
+
+static inline SimdDouble gmx_simdcall loadDuplicateHsimd(const double* m)
+{
+    svfloat64_t v;
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    v              = svld1_f64(pg, m);
+    return { svsplice_f64(pg, v, v) };
+}
+
+static inline SimdDouble gmx_simdcall loadU1DualHsimd(const double* m)
+{
+    svfloat64_t v0, v1;
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    v0             = svdup_f64(m[0]);
+    v1             = svdup_f64(m[1]);
+    return { svsplice_f64(pg, v0, v1) };
+}
+
+static inline void gmx_simdcall storeDualHsimd(double* m0, double* m1, SimdDouble a)
+{
+    svbool_t pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    svst1_f64(pg, m0, a.simdInternal_);
+    pg = sveor_b_z(svptrue_b64(), pg, svptrue_b64());
+    svst1_f64(pg, m1 - GMX_SIMD_DOUBLE_WIDTH / 2, a.simdInternal_);
+}
+
+static inline void gmx_simdcall incrDualHsimd(double* m0, double* m1, SimdDouble a)
+{
+    // Make sure the memory pointer is aligned to half float SIMD width
+    assert(std::size_t(m0) % 32 == 0);
+    assert(std::size_t(m1) % 32 == 0);
+
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    svfloat64_t v0, v2, v3;
+    v0 = svld1_f64(pg, m0);
+    v2 = svadd_f64_x(pg, v0, a.simdInternal_);
+    svst1_f64(pg, m0, v2);
+    v0 = svld1_f64(pg, m1);
+    v3 = svext_f64(a.simdInternal_, a.simdInternal_, GMX_SIMD_DOUBLE_WIDTH / 2);
+    v2 = svadd_f64_x(pg, v0, v3);
+    svst1_f64(pg, m1, v2);
+}
+
+static inline void gmx_simdcall decr3Hsimd(double* m, SimdDouble a0, SimdDouble a1, SimdDouble a2)
+{
+    decrHsimd(m, a0);
+    decrHsimd(m + GMX_SIMD_DOUBLE_WIDTH / 2, a1);
+    decrHsimd(m + GMX_SIMD_DOUBLE_WIDTH, a2);
+}
+
+static inline double gmx_simdcall reduceIncr4ReturnSumHsimd(double* m, SimdDouble v0, SimdDouble v1)
+{
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    svfloat64_t _m, _s;
+    double      sum[4];
+    sum[0] = svadda_f64(pg, 0.0, v0.simdInternal_);
+    sum[2] = svadda_f64(pg, 0.0, v1.simdInternal_);
+    pg     = sveor_b_z(svptrue_b64(), pg, svptrue_b64());
+    sum[1] = svadda_f64(pg, 0.0, v0.simdInternal_);
+    sum[3] = svadda_f64(pg, 0.0, v1.simdInternal_);
+
+#if GMX_SIMD_DOUBLE_WIDTH >= 4
+    pg = svwhilelt_b64(0, 4);
+    _m = svld1_f64(pg, m);
+    _s = svld1_f64(pg, sum);
+    svst1_f64(pg, m, svadd_f64_x(pg, _m, _s));
+    return svadda_f64(pg, 0.0, _s);
+#else
+    double res = 0;
+    for (int i = 0; i < 4; i++)
+    {
+        m[i] += sum[i];
+        res += sum[i];
+    }
+    return res;
+#endif
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTransposeHsimd(const double*      base0,
+                                                         const double*      base1,
+                                                         const std::int32_t offset[],
+                                                         SimdDouble*        v0,
+                                                         SimdDouble*        v1)
+{
+    svint64_t   offsets;
+    svbool_t    pg = svwhilelt_b64(0, (int32_t)GMX_SIMD_DOUBLE_WIDTH / 2);
+    svfloat64_t _v0, _v1;
+    offsets = svmul_n_s64_x(
+            pg,
+            svunpklo(svld1_s32(svwhilelt_b32(0, (int32_t)GMX_SIMD_DINT32_WIDTH / 2), offset)),
+            align * sizeof(double));
+    _v0               = svld1_gather_s64offset_f64(pg, base0, offsets);
+    _v1               = svld1_gather_s64offset_f64(pg, base1, offsets);
+    v0->simdInternal_ = svsplice_f64(pg, _v0, _v1);
+    offsets           = svadd_n_s64_x(pg, offsets, sizeof(double));
+    _v0               = svld1_gather_s64offset_f64(pg, base0, offsets);
+    _v1               = svld1_gather_s64offset_f64(pg, base1, offsets);
+    v1->simdInternal_ = svsplice_f64(pg, _v0, _v1);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_UTIL_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_util_float.h b/src/include/gromacs/simd/impl_arm_sve/impl_arm_sve_util_float.h
new file mode 100644 (file)
index 0000000..126e27b
--- /dev/null
@@ -0,0 +1,403 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020 Research Organization for Information Science and Technology (RIST).
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*
+ * armv8+sve support to GROMACS was contributed by the Research Organization for
+ * Information Science and Technology (RIST).
+ */
+
+#ifndef GMX_SIMD_IMPL_ARM_SVE_UTIL_FLOAT_H
+#define GMX_SIMD_IMPL_ARM_SVE_UTIL_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <arm_sve.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_arm_sve_simd_float.h"
+
+#define SVE_FLOAT_HALF_MASK svwhilelt_b32(0, GMX_SIMD_FLOAT_WIDTH / 2)
+#define SVE_FINT32_HALF_MASK svwhilelt_b32(0, GMX_SIMD_FLOAT_WIDTH / 2)
+
+#define SVE_FLOAT4_MASK svptrue_pat_b32(SV_VL4)
+#define SVE_FLOAT3_MASK svptrue_pat_b32(SV_VL3)
+
+namespace gmx
+{
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v0, SimdFloat* v1)
+{
+    // Base pointer must be aligned to the smaller of 2 elements and float SIMD width
+    assert(std::size_t(base) % 8 == 0);
+    // align parameter must also be a multiple of the above alignment requirement
+    assert(align % 2 == 0);
+
+    if (align < 2)
+    {
+        svbool_t  pg = svptrue_b32();
+        svint32_t offsets;
+        offsets           = svmul_n_s32_x(pg, offset.simdInternal_, align * 4);
+        v0->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+        offsets           = svadd_n_s32_x(pg, offsets, 4);
+        v1->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+    }
+    else if (2 == align)
+    {
+        svbool_t    pg    = svptrue_b32();
+        svfloat32_t t0    = svreinterpret_f32_u64(svld1_gather_s64index_u64(
+                svunpklo_b(pg), (uint64_t*)base, svunpklo_s64(offset.simdInternal_)));
+        svfloat32_t t1    = svreinterpret_f32_u64(svld1_gather_s64index_u64(
+                svunpkhi_b(pg), (uint64_t*)base, svunpkhi_s64(offset.simdInternal_)));
+        v0->simdInternal_ = svuzp1(t0, t1);
+        v1->simdInternal_ = svuzp2(t0, t1);
+    }
+    else
+    {
+        svbool_t    pg      = svptrue_b32();
+        svint32_t   offsets = svmul_n_s32_x(pg, offset.simdInternal_, align / 2);
+        svfloat32_t t0      = svreinterpret_f32_u64(
+                svld1_gather_s64index_u64(svunpklo_b(pg), (uint64_t*)base, svunpklo_s64(offsets)));
+        svfloat32_t t1 = svreinterpret_f32_u64(
+                svld1_gather_s64index_u64(svunpkhi_b(pg), (uint64_t*)base, svunpkhi_s64(offsets)));
+        v0->simdInternal_ = svuzp1(t0, t1);
+        v1->simdInternal_ = svuzp2(t0, t1);
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const float*       base,
+                                                    const std::int32_t offset[],
+                                                    SimdFloat*         v0,
+                                                    SimdFloat*         v1,
+                                                    SimdFloat*         v2,
+                                                    SimdFloat*         v3)
+{
+    assert(std::size_t(offset) % 16 == 0);
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 4 == 0);
+
+    svint32_t offsets;
+    offsets = svld1_s32(svptrue_b32(), offset);
+    gatherLoadBySimdIntTranspose<align>(base, offsets, v0, v1, v2, v3);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadTranspose(const float* base, const std::int32_t offset[], SimdFloat* v0, SimdFloat* v1)
+{
+    assert(std::size_t(offset) % 64 == 0);
+    assert(std::size_t(base) % 8 == 0);
+    assert(align % 2 == 0);
+
+    SimdFInt32 offsets;
+    svbool_t   pg         = svptrue_b32();
+    offsets.simdInternal_ = svld1(pg, offset);
+    gatherLoadBySimdIntTranspose<align>(base, offsets, v0, v1);
+}
+
+static const int c_simdBestPairAlignmentFloat = 2;
+
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const float*       base,
+                                                     const std::int32_t offset[],
+                                                     SimdFloat*         v0,
+                                                     SimdFloat*         v1,
+                                                     SimdFloat*         v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    svint32_t offsets;
+    svbool_t  pg      = svptrue_b32();
+    offsets           = svmul_n_s32_x(pg, svld1_s32(pg, offset), align * 4);
+    v0->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+    offsets           = svadd_n_s32_x(pg, offsets, 4);
+    v1->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+    offsets           = svadd_n_s32_x(pg, offsets, 4);
+    v2->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterStoreU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    svint32_t offsets;
+    svbool_t  pg = svptrue_b32();
+    offsets      = svmul_n_s32_x(pg, svld1_s32(pg, offset), align * 4);
+    svst1_scatter_s32offset_f32(pg, base, offsets, v0.simdInternal_);
+    offsets = svadd_n_s32_x(pg, offsets, 4);
+    svst1_scatter_s32offset_f32(pg, base, offsets, v1.simdInternal_);
+    offsets = svadd_n_s32_x(pg, offsets, 4);
+    svst1_scatter_s32offset_f32(pg, base, offsets, v2.simdInternal_);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    assert(std::size_t(offset) % 64 == 0);
+
+    svbool_t                          pg = svptrue_b32();
+    svfloat32x3_t                     v;
+    alignas(GMX_SIMD_ALIGNMENT) float tvec[3 * GMX_SIMD_FLOAT_WIDTH];
+    v = svcreate3_f32(v0.simdInternal_, v1.simdInternal_, v2.simdInternal_);
+    svst3_f32(pg, tvec, v);
+    pg = SVE_FLOAT3_MASK;
+    for (int i = 0; i < GMX_SIMD_FLOAT_WIDTH; i++)
+    {
+        svfloat32_t t1 = svld1_f32(pg, base + align * offset[i]);
+        svfloat32_t t2 = svld1_f32(pg, tvec + 3 * i);
+        svfloat32_t t3 = svadd_f32_x(pg, t1, t2);
+        svst1_f32(pg, base + align * offset[i], t3);
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    svbool_t                          pg = svptrue_b32();
+    svfloat32x3_t                     v;
+    alignas(GMX_SIMD_ALIGNMENT) float tvec[3 * GMX_SIMD_FLOAT_WIDTH];
+    v = svcreate3_f32(v0.simdInternal_, v1.simdInternal_, v2.simdInternal_);
+    svst3_f32(pg, tvec, v);
+    pg = SVE_FLOAT3_MASK;
+    for (int i = 0; i < GMX_SIMD_FLOAT_WIDTH; i++)
+    {
+        svfloat32_t t1 = svld1_f32(pg, base + align * offset[i]);
+        svfloat32_t t2 = svld1_f32(pg, tvec + 3 * i);
+        svfloat32_t t3 = svsub_f32_x(pg, t1, t2);
+        svst1_f32(pg, base + align * offset[i], t3);
+    }
+}
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdFloat  scalar,
+                                                        SimdFloat* triplets0,
+                                                        SimdFloat* triplets1,
+                                                        SimdFloat* triplets2)
+{
+    assert(GMX_SIMD_FLOAT_WIDTH <= 16);
+    uint32_t   ind[48] = { 0,  0,  0,  1,  1,  1,  2,  2,  2,  3,  3,  3,  4,  4,  4,  5,
+                         5,  5,  6,  6,  6,  7,  7,  7,  8,  8,  8,  9,  9,  9,  10, 10,
+                         10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15 };
+    svbool_t   pg;
+    svuint32_t idx;
+
+    pg                       = svptrue_b32();
+    idx                      = svld1_u32(pg, ind);
+    triplets0->simdInternal_ = svtbl_f32(scalar.simdInternal_, idx);
+    idx                      = svld1_u32(pg, ind + GMX_SIMD_FLOAT_WIDTH);
+    triplets1->simdInternal_ = svtbl_f32(scalar.simdInternal_, idx);
+    idx                      = svld1_u32(pg, ind + 2 * GMX_SIMD_FLOAT_WIDTH);
+    triplets2->simdInternal_ = svtbl_f32(scalar.simdInternal_, idx);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const float* base,
+                                                             SimdFInt32   offset,
+                                                             SimdFloat*   v0,
+                                                             SimdFloat*   v1,
+                                                             SimdFloat*   v2,
+                                                             SimdFloat*   v3)
+{
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 4 == 0);
+
+    svbool_t pg          = svptrue_b32();
+    offset.simdInternal_ = svmul_n_s32_x(pg, offset.simdInternal_, align);
+    v0->simdInternal_    = svld1_gather_s32index_f32(pg, base, offset.simdInternal_);
+    offset.simdInternal_ = svadd_n_s32_x(pg, offset.simdInternal_, 1);
+    v1->simdInternal_    = svld1_gather_s32index_f32(pg, base, offset.simdInternal_);
+    offset.simdInternal_ = svadd_n_s32_x(pg, offset.simdInternal_, 1);
+    v2->simdInternal_    = svld1_gather_s32index_f32(pg, base, offset.simdInternal_);
+    offset.simdInternal_ = svadd_n_s32_x(pg, offset.simdInternal_, 1);
+    v3->simdInternal_    = svld1_gather_s32index_f32(pg, base, offset.simdInternal_);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v0, SimdFloat* v1)
+{
+    svbool_t  pg      = svptrue_b32();
+    svint32_t offsets = svmul_n_s32_x(pg, offset.simdInternal_, align * 4);
+    v0->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+    offsets           = svadd_n_s32_x(pg, offsets, 4);
+    v1->simdInternal_ = svld1_gather_s32offset_f32(pg, base, offsets);
+}
+
+static inline float gmx_simdcall reduceIncr4ReturnSum(float* m, SimdFloat v0, SimdFloat v1, SimdFloat v2, SimdFloat v3)
+{
+    assert(std::size_t(m) % 16 == 0);
+    svbool_t    pg = svptrue_b32();
+    svfloat32_t _m, _s;
+    float32_t   sum[4];
+    sum[0] = svadda_f32(pg, 0.0f, v0.simdInternal_);
+    sum[1] = svadda_f32(pg, 0.0f, v1.simdInternal_);
+    sum[2] = svadda_f32(pg, 0.0f, v2.simdInternal_);
+    sum[3] = svadda_f32(pg, 0.0f, v3.simdInternal_);
+    pg     = SVE_FLOAT4_MASK;
+    _m     = svld1_f32(pg, m);
+    _s     = svld1_f32(pg, sum);
+    svst1_f32(pg, m, svadd_f32_x(pg, _m, _s));
+    return svadda_f32(pg, 0.0f, _s);
+}
+
+static inline SimdFloat gmx_simdcall loadDualHsimd(const float* m0, const float* m1)
+{
+    svfloat32_t v0, v1;
+    svbool_t    pg = SVE_FLOAT_HALF_MASK;
+    v0             = svld1_f32(pg, m0);
+    v1             = svld1_f32(pg, m1);
+    return { svsplice_f32(pg, v0, v1) };
+}
+
+static inline SimdFloat gmx_simdcall loadDuplicateHsimd(const float* m)
+{
+    svfloat32_t v;
+    svbool_t    pg = SVE_FLOAT_HALF_MASK;
+    v              = svld1_f32(pg, m);
+    return { svsplice_f32(pg, v, v) };
+}
+
+static inline SimdFloat gmx_simdcall loadU1DualHsimd(const float* m)
+{
+    svfloat32_t v0, v1;
+    svbool_t    pg = SVE_FLOAT_HALF_MASK;
+    v0             = svdup_f32(m[0]);
+    v1             = svdup_f32(m[1]);
+    return { svsplice_f32(pg, v0, v1) };
+}
+
+static inline void gmx_simdcall storeDualHsimd(float* m0, float* m1, SimdFloat a)
+{
+    svbool_t pg = SVE_FLOAT_HALF_MASK;
+    svst1_f32(pg, m0, a.simdInternal_);
+    svst1_f32(pg, m1, svext_f32(a.simdInternal_, a.simdInternal_, GMX_SIMD_FLOAT_WIDTH / 2));
+}
+
+static inline void gmx_simdcall incrDualHsimd(float* m0, float* m1, SimdFloat a)
+{
+    // Make sure the memory pointer is aligned to half float SIMD width
+    assert(std::size_t(m0) % (GMX_SIMD_FLOAT_WIDTH * sizeof(float) / 2) == 0);
+    assert(std::size_t(m1) % (GMX_SIMD_FLOAT_WIDTH * sizeof(float) / 2) == 0);
+
+    svbool_t    pg = SVE_FLOAT_HALF_MASK;
+    svfloat32_t v0, v2, v3;
+    v0 = svld1_f32(pg, m0);
+    v2 = svadd_f32_x(pg, v0, a.simdInternal_);
+    svst1_f32(pg, m0, v2);
+    v0 = svld1_f32(pg, m1);
+    v3 = svext_f32(a.simdInternal_, a.simdInternal_, GMX_SIMD_FLOAT_WIDTH / 2);
+    v2 = svadd_f32_x(pg, v0, v3);
+    svst1_f32(pg, m1, v2);
+}
+
+static inline void gmx_simdcall decr3Hsimd(float* m, SimdFloat a0, SimdFloat a1, SimdFloat a2)
+{
+    svbool_t    pg  = svptrue_b32();
+    svbool_t    pg2 = SVE_FLOAT_HALF_MASK;
+    svfloat32_t v0, v1, v2, v3;
+    v0 = svld1_f32(pg, m);
+    v1 = svext_f32(a0.simdInternal_, a1.simdInternal_, GMX_SIMD_FLOAT_WIDTH / 2);
+    v2 = svsel_f32(pg2, a0.simdInternal_, a1.simdInternal_);
+    v1 = svadd_f32_x(pg, v1, v2);
+    v0 = svsub_f32_z(pg, v0, v1);
+    svst1_f32(pg, m, v0);
+    v0 = svld1_f32(pg2, m + GMX_SIMD_FLOAT_WIDTH);
+    v1 = svext_f32(a2.simdInternal_, a0.simdInternal_, GMX_SIMD_FLOAT_WIDTH / 2);
+    v2 = svadd_f32_x(pg2, a2.simdInternal_, v1);
+    v3 = svsub_f32_x(pg2, v0, v2);
+    svst1_f32(pg2, m + GMX_SIMD_FLOAT_WIDTH, v3);
+}
+
+static inline float gmx_simdcall reduceIncr4ReturnSumHsimd(float* m, SimdFloat v0, SimdFloat v1)
+{
+    svbool_t    pg  = SVE_FLOAT_HALF_MASK;
+    svbool_t    pg2 = sveor_b_z(svptrue_b32(), pg, svptrue_b32());
+    svfloat32_t _m, _s;
+
+    _s = svdup_f32(0.0f);
+    _s = svinsr_n_f32(_s, svaddv_f32(pg2, v1.simdInternal_));
+    _s = svinsr_n_f32(_s, svaddv_f32(pg, v1.simdInternal_));
+    _s = svinsr_n_f32(_s, svaddv_f32(pg2, v0.simdInternal_));
+    _s = svinsr_n_f32(_s, svaddv_f32(pg, v0.simdInternal_));
+
+    pg = SVE_FLOAT4_MASK;
+    _m = svld1_f32(pg, m);
+    svst1_f32(pg, m, svadd_f32_x(pg, _m, _s));
+    return svaddv_f32(pg, _s);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTransposeHsimd(const float*       base0,
+                                                         const float*       base1,
+                                                         const std::int32_t offset[],
+                                                         SimdFloat*         v0,
+                                                         SimdFloat*         v1)
+{
+    svint64_t   offsets = svunpklo_s64(svld1_s32(svptrue_b32(), offset));
+    svfloat32_t _v0, _v1;
+    if (2 == align)
+    {
+        _v0 = svreinterpret_f32_f64(svld1_gather_s64index_f64(SVE_DOUBLE_MASK, (double*)base0, offsets));
+        _v1 = svreinterpret_f32_f64(svld1_gather_s64index_f64(SVE_DOUBLE_MASK, (double*)base1, offsets));
+    }
+    else
+    {
+        offsets = svmul_n_s64_x(svptrue_b64(), offsets, align * 4);
+        _v0 = svreinterpret_f32_f64(svld1_gather_s64offset_f64(SVE_DOUBLE_MASK, (double*)base0, offsets));
+        _v1 = svreinterpret_f32_f64(svld1_gather_s64offset_f64(SVE_DOUBLE_MASK, (double*)base1, offsets));
+    }
+    v0->simdInternal_ = svuzp1(_v0, _v1);
+    v1->simdInternal_ = svuzp2(_v0, _v1);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_ARM_SVE_UTIL_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx.h b/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx.h
new file mode 100644 (file)
index 0000000..e69fd33
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPLEMENTATION_IBM_VSX_H
+#define GMX_SIMD_IMPLEMENTATION_IBM_VSX_H
+
+// At high optimization levels, gcc 7.2 gives false
+// positives.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
+
+// While we do our best to also test VSX with Power7, that depends on having
+// access to big-endian hardware, so for the long term our focus will be
+// little-endian Power8.
+
+#include "impl_ibm_vsx_definitions.h"
+#include "impl_ibm_vsx_general.h"
+#include "impl_ibm_vsx_simd4_float.h"
+#include "impl_ibm_vsx_simd_double.h"
+#include "impl_ibm_vsx_simd_float.h"
+#include "impl_ibm_vsx_util_double.h"
+#include "impl_ibm_vsx_util_float.h"
+
+#pragma GCC diagnostic pop
+
+#endif // GMX_SIMD_IMPLEMENTATION_IBM_VSX_H
diff --git a/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_definitions.h b/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_definitions.h
new file mode 100644 (file)
index 0000000..fe67d4b
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPLEMENTATION_IBM_VSX_DEFINITIONS_H
+#define GMX_SIMD_IMPLEMENTATION_IBM_VSX_DEFINITIONS_H
+
+// IBM VSX SIMD instruction wrappers. Power7 and later.
+//
+// While this instruction set is similar to VMX, there are quite a few differences
+// that make it easier to understand if we start from scratch rather than by
+// including the VMX implementation and changing lots of things.
+
+#include <altivec.h>
+
+#if defined(__GNUC__) && !defined(__ibmxl__) && !defined(__xlC__)
+// According to G++ documentation, when using VSX in C++ we
+// must undefine vector & bool macros after including altivec.h
+#    undef vector
+#    undef bool
+#    define vsxBool __bool
+#else
+// We cannot undefine bool on xlc, but somehow it works anyway
+#    define vsxBool bool
+#endif
+
+#define GMX_SIMD 1
+#define GMX_SIMD_HAVE_FLOAT 1
+// GMX_SIMD_HAVE_DOUBLE is conditionally defined further down
+#define GMX_SIMD_HAVE_LOADU 1
+#define GMX_SIMD_HAVE_STOREU 1
+#define GMX_SIMD_HAVE_LOGICAL 1
+#define GMX_SIMD_HAVE_FMA 1
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 1
+// GMX_SIMD_HAVE_DINT32_EXTRACT is conditionally defined further down
+// GMX_SIMD_HAVE_DINT32_LOGICAL is conditionally defined further down
+// GMX_SIMD_HAVE_DINT32_ARITHMETICS is conditionally defined further down
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 1
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 1
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+// GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE is conditionally defined further down
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 0  // No need for half-simd, width is 4
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 0 // No need for half-simd, width is 2
+#define GMX_SIMD4_HAVE_FLOAT 1
+#define GMX_SIMD4_HAVE_DOUBLE 0
+
+// With GCC, only version 4.9 or later supports all parts of double precision VSX.
+// We check explicitly for xlc, since that compiler appears to like pretending it is gcc,
+// but there double precision seems to work fine.
+#if defined(__ibmxl__) || defined(__xlC__) \
+        || !(defined(__GNUC__) && ((__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ < 9))))
+#    define GMX_SIMD_HAVE_DOUBLE 1
+#    define GMX_SIMD_HAVE_DINT32_EXTRACT 1
+#    define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+#    define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+#    define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+#else
+#    define GMX_SIMD_HAVE_DOUBLE 0
+#    define GMX_SIMD_HAVE_DINT32_EXTRACT 0
+#    define GMX_SIMD_HAVE_DINT32_LOGICAL 0
+#    define GMX_SIMD_HAVE_DINT32_ARITHMETICS 0
+#    define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 0
+#endif
+
+// Implementation details
+#define GMX_SIMD_FLOAT_WIDTH 4
+#define GMX_SIMD_DOUBLE_WIDTH 2
+#define GMX_SIMD_FINT32_WIDTH 4
+#define GMX_SIMD_DINT32_WIDTH 2
+#define GMX_SIMD4_WIDTH 4
+#define GMX_SIMD_ALIGNMENT 16 // Bytes (4*single or 2*sdouble)
+#define GMX_SIMD_RSQRT_BITS 14
+#define GMX_SIMD_RCP_BITS 14
+
+
+#endif /* GMX_SIMD_IMPLEMENTATION_IBM_VSX_COMMON_H */
diff --git a/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_general.h b/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_general.h
new file mode 100644 (file)
index 0000000..03045e5
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPLEMENTATION_IBM_VSX_GENERAL_H
+#define GMX_SIMD_IMPLEMENTATION_IBM_VSX_GENERAL_H
+
+namespace gmx
+{
+
+static inline void simdPrefetch(const void* m)
+{
+#if defined(__ibmxl__) || defined(__xlC__)
+    __dcbt((void*)m);
+#elif defined __GNUC__
+    __builtin_prefetch(m);
+#endif
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPLEMENTATION_IBM_VSX_GENERAL_H
diff --git a/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd4_float.h b/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd4_float.h
new file mode 100644 (file)
index 0000000..b6957e2
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPLEMENTATION_IBM_VSX_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPLEMENTATION_IBM_VSX_SIMD4_FLOAT_H
+
+#include "config.h"
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_ibm_vsx_definitions.h"
+#include "impl_ibm_vsx_simd_float.h"
+
+namespace gmx
+{
+
+class Simd4Float
+{
+public:
+    Simd4Float() {}
+
+    // gcc-4.9 does not recognize that we use the parameter
+    Simd4Float(float gmx_unused f) : simdInternal_(vec_splats(f)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4Float(__vector float simd) : simdInternal_(simd) {}
+
+    __vector float simdInternal_;
+};
+
+class Simd4FBool
+{
+public:
+    Simd4FBool() {}
+
+    //! \brief Construct from scalar bool
+    Simd4FBool(bool b) :
+        simdInternal_(reinterpret_cast<__vector vsxBool int>(vec_splats(b ? 0xFFFFFFFF : 0)))
+    {
+    }
+
+    // Internal utility constructor to simplify return statements
+    Simd4FBool(__vector vsxBool int simd) : simdInternal_(simd) {}
+
+    __vector vsxBool int simdInternal_;
+};
+
+// The VSX load & store operations are a bit of a mess. The interface is different
+// for xlc version 12, xlc version 13, and gcc. Long-term IBM recommends
+// simply using pointer dereferencing both for aligned and unaligned loads.
+// That's nice, but unfortunately xlc still bugs out when the pointer is
+// not aligned. Sticking to vec_xl/vec_xst isn't a solution either, since
+// that appears to be buggy for some _aligned_ loads :-)
+//
+// For now, we use pointer dereferencing for all aligned load/stores, and
+// for unaligned ones with gcc. On xlc we use vec_xlw4/vec_xstw4 for
+// unaligned memory operations. The latest docs recommend using the overloaded
+// vec_xl/vec_xst, but that is not supported on xlc version 12. We'll
+// revisit things once xlc is a bit more stable - for now you probably want
+// to stick to gcc...
+
+static inline Simd4Float gmx_simdcall load4(const float* m)
+{
+    return { *reinterpret_cast<const __vector float*>(m) };
+}
+
+static inline void gmx_simdcall store4(float* m, Simd4Float a)
+{
+    *reinterpret_cast<__vector float*>(m) = a.simdInternal_;
+}
+
+static inline Simd4Float gmx_simdcall load4U(const float* m)
+{
+    return
+    {
+#if __GNUC__ < 7
+        *reinterpret_cast<const __vector float*>(m)
+#else
+        vec_xl(0, m)
+#endif
+    };
+}
+
+static inline void gmx_simdcall store4U(float* m, Simd4Float a)
+{
+#if __GNUC__ < 7
+    *reinterpret_cast<__vector float*>(m) = a.simdInternal_;
+#else
+    vec_xst(a.simdInternal_, 0, m);
+#endif
+}
+
+static inline Simd4Float gmx_simdcall simd4SetZeroF()
+{
+    return { vec_splats(0.0F) };
+}
+
+static inline Simd4Float gmx_simdcall operator&(Simd4Float a, Simd4Float b)
+{
+    return { vec_and(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall andNot(Simd4Float a, Simd4Float b)
+{
+    return { vec_andc(b.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator|(Simd4Float a, Simd4Float b)
+{
+    return { vec_or(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator^(Simd4Float a, Simd4Float b)
+{
+    return { vec_xor(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator+(Simd4Float a, Simd4Float b)
+{
+    return { vec_add(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float a, Simd4Float b)
+{
+    return { vec_sub(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float x)
+{
+    return { -x.simdInternal_ };
+}
+
+static inline Simd4Float gmx_simdcall operator*(Simd4Float a, Simd4Float b)
+{
+    return { vec_mul(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vec_madd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vec_msub(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vec_nmsub(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { vec_nmadd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall rsqrt(Simd4Float x)
+{
+    return { vec_rsqrte(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall abs(Simd4Float x)
+{
+    return { vec_abs(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall max(Simd4Float a, Simd4Float b)
+{
+    return { vec_max(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall min(Simd4Float a, Simd4Float b)
+{
+    return { vec_min(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall round(Simd4Float x)
+{
+    return { vec_round(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall trunc(Simd4Float x)
+{
+    return { vec_trunc(x.simdInternal_) };
+}
+
+static inline float gmx_simdcall dotProduct(Simd4Float a, Simd4Float b)
+{
+    const __vector unsigned char perm1 = { 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 };
+    const __vector unsigned char perm2 = { 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3 };
+    __vector float               c     = vec_mul(a.simdInternal_, b.simdInternal_);
+    __vector float               sum;
+    sum = vec_add(c, vec_perm(c, c, perm1));
+    sum = vec_add(sum, vec_perm(c, c, perm2));
+    return vec_extract(sum, 0);
+}
+
+static inline void gmx_simdcall transpose(Simd4Float* v0, Simd4Float* v1, Simd4Float* v2, Simd4Float* v3)
+{
+    __vector float t0 = vec_mergeh(v0->simdInternal_, v2->simdInternal_);
+    __vector float t1 = vec_mergel(v0->simdInternal_, v2->simdInternal_);
+    __vector float t2 = vec_mergeh(v1->simdInternal_, v3->simdInternal_);
+    __vector float t3 = vec_mergel(v1->simdInternal_, v3->simdInternal_);
+    v0->simdInternal_ = vec_mergeh(t0, t2);
+    v1->simdInternal_ = vec_mergel(t0, t2);
+    v2->simdInternal_ = vec_mergeh(t1, t3);
+    v3->simdInternal_ = vec_mergel(t1, t3);
+}
+
+static inline Simd4FBool gmx_simdcall operator==(Simd4Float a, Simd4Float b)
+{
+    return { vec_cmpeq(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator!=(Simd4Float a, Simd4Float b)
+{
+    return { vec_or(vec_cmpgt(a.simdInternal_, b.simdInternal_),
+                    vec_cmplt(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<(Simd4Float a, Simd4Float b)
+{
+    return { vec_cmplt(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<=(Simd4Float a, Simd4Float b)
+{
+    return { vec_cmple(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator&&(Simd4FBool a, Simd4FBool b)
+{
+    return { vec_and(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator||(Simd4FBool a, Simd4FBool b)
+{
+    return { vec_or(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(Simd4FBool a)
+{
+    return vec_any_ne(a.simdInternal_, reinterpret_cast<__vector vsxBool int>(vec_splats(0)));
+}
+
+static inline Simd4Float gmx_simdcall selectByMask(Simd4Float a, Simd4FBool m)
+{
+    return { vec_and(a.simdInternal_, reinterpret_cast<__vector float>(m.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall selectByNotMask(Simd4Float a, Simd4FBool m)
+{
+    return { vec_andc(a.simdInternal_, reinterpret_cast<__vector float>(m.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall blend(Simd4Float a, Simd4Float b, Simd4FBool sel)
+{
+    return { vec_sel(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline float gmx_simdcall reduce(Simd4Float x)
+{
+    const __vector unsigned char perm1 = { 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 };
+    const __vector unsigned char perm2 = { 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3 };
+
+    x.simdInternal_ = vec_add(x.simdInternal_, vec_perm(x.simdInternal_, x.simdInternal_, perm1));
+    x.simdInternal_ = vec_add(x.simdInternal_, vec_perm(x.simdInternal_, x.simdInternal_, perm2));
+    return vec_extract(x.simdInternal_, 0);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPLEMENTATION_IBM_VSX_SIMD4_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd_double.h b/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd_double.h
new file mode 100644 (file)
index 0000000..53981ed
--- /dev/null
@@ -0,0 +1,679 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPLEMENTATION_IBM_VSX_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPLEMENTATION_IBM_VSX_SIMD_DOUBLE_H
+
+#include "config.h"
+
+#include "gromacs/math/utilities.h"
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_ibm_vsx_definitions.h"
+
+namespace gmx
+{
+
+class SimdDouble
+{
+public:
+    SimdDouble() {}
+
+    // gcc-4.9 does not recognize that we use the parameter
+    SimdDouble(double gmx_unused d) : simdInternal_(vec_splats(d)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDouble(__vector double simd) : simdInternal_(simd) {}
+
+    __vector double simdInternal_;
+};
+
+class SimdDInt32
+{
+public:
+    SimdDInt32() {}
+
+    // gcc-4.9 does not recognize that we use the parameter
+    SimdDInt32(std::int32_t gmx_unused i) : simdInternal_(vec_splats(i)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDInt32(__vector signed int simd) : simdInternal_(simd) {}
+
+    __vector signed int simdInternal_;
+};
+
+class SimdDBool
+{
+public:
+    SimdDBool() {}
+
+    SimdDBool(bool b) :
+        simdInternal_(reinterpret_cast<__vector vsxBool long long>(vec_splats(b ? 0xFFFFFFFFFFFFFFFFULL : 0)))
+    {
+    }
+
+    // Internal utility constructor to simplify return statements
+    SimdDBool(__vector vsxBool long long simd) : simdInternal_(simd) {}
+
+    __vector vsxBool long long simdInternal_;
+};
+
+class SimdDIBool
+{
+public:
+    SimdDIBool() {}
+
+    SimdDIBool(bool b) :
+        simdInternal_(reinterpret_cast<__vector vsxBool int>(vec_splats(b ? 0xFFFFFFFF : 0)))
+    {
+    }
+
+    // Internal utility constructor to simplify return statements
+    SimdDIBool(__vector vsxBool int simd) : simdInternal_(simd) {}
+
+    __vector vsxBool int simdInternal_;
+};
+
+// Note that the interfaces we use here have been a mess in xlc;
+// currently version 13.1.5 is required.
+
+static inline SimdDouble gmx_simdcall simdLoad(const double* m, SimdDoubleTag = {})
+{
+    return
+    {
+#if defined(__ibmxl__)
+        vec_ld(0, m)
+#else
+#    if __GNUC__ < 7
+        *reinterpret_cast<const __vector double*>(m)
+#    else
+        vec_vsx_ld(0, m)
+#    endif
+#endif
+    };
+}
+
+static inline void gmx_simdcall store(double* m, SimdDouble a)
+{
+#if defined(__ibmxl__)
+    vec_st(a.simdInternal_, 0, m);
+#else
+#    if __GNUC__ < 7
+    *reinterpret_cast<__vector double*>(m) = a.simdInternal_;
+#    else
+    vec_vsx_st(a.simdInternal_, 0, m);
+#    endif
+#endif
+}
+
+static inline SimdDouble gmx_simdcall simdLoadU(const double* m, SimdDoubleTag = {})
+{
+    return
+    {
+#if defined(__ibmxl__)
+        vec_xl(0, m)
+#else
+#    if __GNUC__ < 7
+        *reinterpret_cast<const __vector double*>(m)
+#    else
+        vec_vsx_ld(0, m)
+#    endif
+#endif
+    };
+}
+
+static inline void gmx_simdcall storeU(double* m, SimdDouble a)
+{
+#if defined(__ibmxl__)
+    vec_xst(a.simdInternal_, 0, m);
+#else
+#    if __GNUC__ < 7
+    *reinterpret_cast<__vector double*>(m) = a.simdInternal_;
+#    else
+    vec_vsx_st(a.simdInternal_, 0, m);
+#    endif
+#endif
+}
+
+static inline SimdDouble gmx_simdcall setZeroD()
+{
+    return { vec_splats(0.0) };
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdDInt32Tag)
+{
+    __vector signed int          t0, t1;
+    const __vector unsigned char perm = { 0, 1, 2, 3, 0, 1, 2, 3, 16, 17, 18, 19, 16, 17, 18, 19 };
+    t0                                = vec_splats(m[0]);
+    t1                                = vec_splats(m[1]);
+    return { vec_perm(t0, t1, perm) };
+}
+
+// gcc-4.9 does not understand that arguments to vec_extract() are used
+static inline void gmx_simdcall store(std::int32_t* m, SimdDInt32 gmx_unused x)
+{
+    m[0] = vec_extract(x.simdInternal_, 0);
+    m[1] = vec_extract(x.simdInternal_, 2);
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdDInt32Tag)
+{
+    return simdLoad(m, SimdDInt32Tag());
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdDInt32 a)
+{
+    return store(m, a);
+}
+
+static inline SimdDInt32 gmx_simdcall setZeroDI()
+{
+    return { vec_splats(static_cast<int>(0)) };
+}
+
+// gcc-4.9 does not detect that vec_extract() uses its argument
+template<int index>
+static inline std::int32_t gmx_simdcall extract(SimdDInt32 gmx_unused a)
+{
+    return vec_extract(a.simdInternal_, 2 * index);
+}
+
+static inline SimdDouble gmx_simdcall operator&(SimdDouble a, SimdDouble b)
+{
+    return { vec_and(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall andNot(SimdDouble a, SimdDouble b)
+{
+    return { vec_andc(b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator|(SimdDouble a, SimdDouble b)
+{
+    return { vec_or(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator^(SimdDouble a, SimdDouble b)
+{
+    return { vec_xor(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator+(SimdDouble a, SimdDouble b)
+{
+    return { vec_add(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble a, SimdDouble b)
+{
+    return { vec_sub(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble x)
+{
+    return { -x.simdInternal_ };
+}
+
+static inline SimdDouble gmx_simdcall operator*(SimdDouble a, SimdDouble b)
+{
+    return { vec_mul(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { vec_madd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { vec_msub(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { vec_nmsub(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { vec_nmadd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall rsqrt(SimdDouble x)
+{
+    return { vec_rsqrte(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall rcp(SimdDouble x)
+{
+    return { vec_re(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskAdd(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    return { vec_add(a.simdInternal_,
+                     vec_and(b.simdInternal_, reinterpret_cast<__vector double>(m.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall maskzMul(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    SimdDouble prod = a * b;
+
+    return { vec_and(prod.simdInternal_, reinterpret_cast<__vector double>(m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall maskzFma(SimdDouble a, SimdDouble b, SimdDouble c, SimdDBool m)
+{
+    SimdDouble prod = fma(a, b, c);
+
+    return { vec_and(prod.simdInternal_, reinterpret_cast<__vector double>(m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall maskzRsqrt(SimdDouble x, SimdDBool m)
+{
+#ifndef NDEBUG
+    x.simdInternal_ = vec_sel(vec_splats(1.0), x.simdInternal_, m.simdInternal_);
+#endif
+    return { vec_and(vec_rsqrte(x.simdInternal_), reinterpret_cast<__vector double>(m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall maskzRcp(SimdDouble x, SimdDBool m)
+{
+#ifndef NDEBUG
+    x.simdInternal_ = vec_sel(vec_splats(1.0), x.simdInternal_, m.simdInternal_);
+#endif
+    return { vec_and(vec_re(x.simdInternal_), reinterpret_cast<__vector double>(m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall abs(SimdDouble x)
+{
+    return { vec_abs(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall max(SimdDouble a, SimdDouble b)
+{
+    return { vec_max(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall min(SimdDouble a, SimdDouble b)
+{
+    return { vec_min(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall round(SimdDouble x)
+{
+#if defined(__GNUC__) && !defined(__ibmxl__) && !defined(__xlC__)
+    // gcc up to at least version 4.9 does not have vec_round() in double precision - use inline asm
+    __vector double res;
+    __asm__("xvrdpi %x0,%x1" : "=wd"(res) : "wd"(x.simdInternal_));
+    return { res };
+#else
+    return { vec_round(x.simdInternal_) };
+#endif
+}
+
+static inline SimdDouble gmx_simdcall trunc(SimdDouble x)
+{
+    return { vec_trunc(x.simdInternal_) };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble frexp(SimdDouble value, SimdDInt32* exponent)
+{
+    const __vector double exponentMask =
+            reinterpret_cast<__vector double>(vec_splats(0x7FF0000000000000ULL));
+    const __vector signed int exponentBias = vec_splats(1022);
+    const __vector double     half         = vec_splats(0.5);
+    __vector signed int       iExponent;
+
+    __vector vsxBool long long valueIsZero =
+            vec_cmpeq(value.simdInternal_, reinterpret_cast<__vector double>(vec_splats(0.0)));
+
+    iExponent = reinterpret_cast<__vector signed int>(vec_and(value.simdInternal_, exponentMask));
+    // The data is in the upper half of each double (corresponding to elements 1 and 3).
+    // First shift 52-32=20bits, and then permute to swap element 0 with 1 and element 2 with 3
+    // For big endian they are in opposite order, so then we simply skip the swap.
+    iExponent = vec_sr(iExponent, vec_splats(20U));
+#ifndef __BIG_ENDIAN__
+    const __vector unsigned char perm = { 4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11 };
+    iExponent                         = vec_perm(iExponent, iExponent, perm);
+#endif
+    iExponent = vec_sub(iExponent, exponentBias);
+    iExponent = vec_andc(iExponent, reinterpret_cast<__vector int>(valueIsZero));
+
+    __vector double result = vec_or(vec_andc(value.simdInternal_, exponentMask), half);
+    result                 = vec_sel(result, value.simdInternal_, valueIsZero);
+
+    exponent->simdInternal_ = iExponent;
+
+    return { result };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble ldexp(SimdDouble value, SimdDInt32 exponent)
+{
+    const __vector signed int exponentBias = vec_splats(1023);
+    __vector signed int       iExponent;
+#ifdef __BIG_ENDIAN__
+    const __vector unsigned char perm = { 0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 16, 17, 18, 19 };
+#else
+    const __vector unsigned char perm = { 16, 17, 18, 19, 0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11 };
+#endif
+
+    iExponent = vec_add(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = vec_max(iExponent, vec_splat_s32(0));
+    }
+
+    // exponent is now present in pairs of integers; 0011.
+    // Elements 0/2 already correspond to the upper half of each double,
+    // so we only need to shift by another 52-32=20 bits.
+    // The remaining elements are set to zero.
+    iExponent = vec_sl(iExponent, vec_splats(20U));
+    iExponent = vec_perm(iExponent, vec_splats(0), perm);
+
+    return { vec_mul(value.simdInternal_, reinterpret_cast<__vector double>(iExponent)) };
+}
+
+static inline double gmx_simdcall reduce(SimdDouble x)
+{
+    const __vector unsigned char perm = { 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 };
+#ifdef __xlC__
+    /* old xlc version 12 does not understand vec_perm() with double arguments */
+    x.simdInternal_ = vec_add(x.simdInternal_,
+                              reinterpret_cast<__vector double>(vec_perm(
+                                      reinterpret_cast<__vector signed int>(x.simdInternal_),
+                                      reinterpret_cast<__vector signed int>(x.simdInternal_),
+                                      perm)));
+#else
+    x.simdInternal_ = vec_add(x.simdInternal_, vec_perm(x.simdInternal_, x.simdInternal_, perm));
+#endif
+    return vec_extract(x.simdInternal_, 0);
+}
+
+static inline SimdDBool gmx_simdcall operator==(SimdDouble a, SimdDouble b)
+{
+    return { vec_cmpeq(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator!=(SimdDouble a, SimdDouble b)
+{
+    return { reinterpret_cast<__vector vsxBool long long>(vec_or(
+            reinterpret_cast<__vector signed int>(vec_cmpgt(a.simdInternal_, b.simdInternal_)),
+            reinterpret_cast<__vector signed int>(vec_cmplt(a.simdInternal_, b.simdInternal_)))) };
+}
+
+static inline SimdDBool gmx_simdcall operator<(SimdDouble a, SimdDouble b)
+{
+    return { vec_cmplt(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator<=(SimdDouble a, SimdDouble b)
+{
+    return { vec_cmple(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall testBits(SimdDouble a)
+{
+#ifdef __POWER8_VECTOR__
+    // Power8 VSX has proper support for operations on long long integers
+    return { vec_cmpgt(reinterpret_cast<__vector unsigned long long>(a.simdInternal_), vec_splats(0ULL)) };
+#else
+    // No support for long long operations.
+    // Start with comparing 32-bit subfields bitwise by casting to integers
+    __vector vsxBool int tmp =
+            vec_cmpgt(reinterpret_cast<__vector unsigned int>(a.simdInternal_), vec_splats(0U));
+
+    // Shuffle low/high 32-bit fields of tmp into tmp2
+    const __vector unsigned char perm = { 4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11 };
+    __vector vsxBool int tmp2 = vec_perm(tmp, tmp, perm);
+
+    // Return the or:d parts of tmp & tmp2
+    return { reinterpret_cast<__vector vsxBool long long>(vec_or(tmp, tmp2)) };
+#endif
+}
+
+static inline SimdDBool gmx_simdcall operator&&(SimdDBool a, SimdDBool b)
+{
+    return { reinterpret_cast<__vector vsxBool long long>(
+            vec_and(reinterpret_cast<__vector signed int>(a.simdInternal_),
+                    reinterpret_cast<__vector signed int>(b.simdInternal_))) };
+}
+
+static inline SimdDBool gmx_simdcall operator||(SimdDBool a, SimdDBool b)
+{
+    return { reinterpret_cast<__vector vsxBool long long>(
+            vec_or(reinterpret_cast<__vector signed int>(a.simdInternal_),
+                   reinterpret_cast<__vector signed int>(b.simdInternal_))) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDBool a)
+{
+    return vec_any_ne(reinterpret_cast<__vector vsxBool int>(a.simdInternal_),
+                      reinterpret_cast<__vector vsxBool int>(vec_splats(0)));
+}
+
+static inline SimdDouble gmx_simdcall selectByMask(SimdDouble a, SimdDBool m)
+{
+    return { vec_and(a.simdInternal_, reinterpret_cast<__vector double>(m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall selectByNotMask(SimdDouble a, SimdDBool m)
+{
+    return { vec_andc(a.simdInternal_, reinterpret_cast<__vector double>(m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall blend(SimdDouble a, SimdDouble b, SimdDBool sel)
+{
+    return { vec_sel(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator&(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vec_and(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall andNot(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vec_andc(b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator|(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vec_or(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator^(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vec_xor(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator+(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vec_add(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator-(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vec_sub(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator*(SimdDInt32 a, SimdDInt32 b)
+{
+    return { a.simdInternal_ * b.simdInternal_ };
+}
+
+static inline SimdDIBool gmx_simdcall operator==(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vec_cmpeq(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall testBits(SimdDInt32 a)
+{
+    return { vec_cmpgt(reinterpret_cast<__vector unsigned int>(a.simdInternal_), vec_splats(0U)) };
+}
+
+static inline SimdDIBool gmx_simdcall operator<(SimdDInt32 a, SimdDInt32 b)
+{
+    return { vec_cmplt(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator&&(SimdDIBool a, SimdDIBool b)
+{
+    return { vec_and(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator||(SimdDIBool a, SimdDIBool b)
+{
+    return { vec_or(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDIBool a)
+{
+    return vec_any_ne(a.simdInternal_, reinterpret_cast<__vector vsxBool int>(vec_splats(0)));
+}
+
+static inline SimdDInt32 gmx_simdcall selectByMask(SimdDInt32 a, SimdDIBool m)
+{
+    return { vec_and(a.simdInternal_, reinterpret_cast<__vector signed int>(m.simdInternal_)) };
+}
+
+static inline SimdDInt32 gmx_simdcall selectByNotMask(SimdDInt32 a, SimdDIBool m)
+{
+    return { vec_andc(a.simdInternal_, reinterpret_cast<__vector signed int>(m.simdInternal_)) };
+}
+
+static inline SimdDInt32 gmx_simdcall blend(SimdDInt32 a, SimdDInt32 b, SimdDIBool sel)
+{
+    return { vec_sel(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall cvttR2I(SimdDouble a)
+{
+#if defined(__GNUC__) && !defined(__ibmxl__) && !defined(__xlC__)
+    // gcc up to at least version 6.1 is missing intrinsics for converting double to/from int - use inline asm
+    const __vector unsigned char perm = { 4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11 };
+    __vector double              ix;
+
+    __asm__("xvcvdpsxws %x0,%x1" : "=wa"(ix) : "wd"(a.simdInternal_));
+
+    return { reinterpret_cast<__vector signed int>(vec_perm(ix, ix, perm)) };
+#else
+    return { vec_cts(a.simdInternal_, 0) };
+#endif
+}
+
+static inline SimdDInt32 gmx_simdcall cvtR2I(SimdDouble a)
+{
+    return cvttR2I(round(a));
+}
+
+static inline SimdDouble gmx_simdcall cvtI2R(SimdDInt32 a)
+{
+#if defined(__GNUC__) && !defined(__ibmxl__) && !defined(__xlC__)
+    // gcc up to at least version 4.9 is missing intrinsics for converting double to/from int - use inline asm
+    __vector double x;
+#    ifndef __BIG_ENDIAN__
+    const __vector unsigned char perm = { 4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11 };
+    a.simdInternal_                   = vec_perm(a.simdInternal_, a.simdInternal_, perm);
+#    endif
+
+    __asm__("xvcvsxwdp %x0,%x1" : "=wd"(x) : "wa"(a.simdInternal_));
+
+    return { x };
+#else
+    return { vec_ctd(a.simdInternal_, 0) };
+#endif
+}
+
+static inline SimdDIBool gmx_simdcall cvtB2IB(SimdDBool a)
+{
+    return { reinterpret_cast<__vector vsxBool int>(a.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall cvtIB2B(SimdDIBool a)
+{
+    return { reinterpret_cast<__vector vsxBool long long>(a.simdInternal_) };
+}
+
+static inline void gmx_simdcall cvtF2DD(SimdFloat f, SimdDouble* d0, SimdDouble* d1)
+{
+    __vector float fA, fB;
+    fA = vec_mergeh(f.simdInternal_, f.simdInternal_); /* 0011 */
+    fB = vec_mergel(f.simdInternal_, f.simdInternal_); /* 2233 */
+#if defined(__GNUC__) && !defined(__ibmxl__) && !defined(__xlC__)
+    // gcc-4.9 is missing double-to-float/float-to-double conversions.
+    __asm__("xvcvspdp %x0,%x1" : "=wd"(d0->simdInternal_) : "wf"(fA));
+    __asm__("xvcvspdp %x0,%x1" : "=wd"(d1->simdInternal_) : "wf"(fB));
+#else
+    d0->simdInternal_ = vec_cvf(fA); /* 01 */
+    d1->simdInternal_ = vec_cvf(fB); /* 23 */
+#endif
+}
+
+static inline SimdFloat gmx_simdcall cvtDD2F(SimdDouble d0, SimdDouble d1)
+{
+    __vector float fA, fB, fC, fD, fE;
+#if defined(__GNUC__) && !defined(__ibmxl__) && !defined(__xlC__)
+    // gcc-4.9 is missing double-to-float/float-to-double conversions.
+    __asm__("xvcvdpsp %x0,%x1" : "=wf"(fA) : "wd"(d0.simdInternal_));
+    __asm__("xvcvdpsp %x0,%x1" : "=wf"(fB) : "wd"(d1.simdInternal_));
+#else
+    fA = vec_cvf(d0.simdInternal_);  /* 0x1x */
+    fB = vec_cvf(d1.simdInternal_);  /* 2x3x */
+#endif
+    fC = vec_mergeh(fA, fB); /* 02xx */
+    fD = vec_mergel(fA, fB); /* 13xx */
+    fE = vec_mergeh(fC, fD); /* 0123 */
+    return { fE };
+}
+
+static inline SimdDouble gmx_simdcall copysign(SimdDouble x, SimdDouble y)
+{
+#if defined(__GNUC__) && !defined(__ibmxl__) && !defined(__xlC__)
+    __vector double res;
+    __asm__("xvcpsgndp %x0,%x1,%x2" : "=wd"(res) : "wd"(y.simdInternal_), "wd"(x.simdInternal_));
+    return { res };
+#else
+    return { vec_cpsgn(y.simdInternal_, x.simdInternal_) };
+#endif
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPLEMENTATION_IBM_VSX_SIMD_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd_float.h b/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_simd_float.h
new file mode 100644 (file)
index 0000000..ab8932d
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPLEMENTATION_IBM_VSX_SIMD_FLOAT_H
+#define GMX_SIMD_IMPLEMENTATION_IBM_VSX_SIMD_FLOAT_H
+
+#include "config.h"
+
+#include "gromacs/math/utilities.h"
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_ibm_vsx_definitions.h"
+
+namespace gmx
+{
+
+class SimdFloat
+{
+public:
+    SimdFloat() {}
+
+    // gcc-4.9 does not recognize that we use the parameter
+    SimdFloat(float gmx_unused f) : simdInternal_(vec_splats(f)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFloat(__vector float simd) : simdInternal_(simd) {}
+
+    __vector float simdInternal_;
+};
+
+class SimdFInt32
+{
+public:
+    SimdFInt32() {}
+
+    // gcc-4.9 does not recognize that we use the parameter
+    SimdFInt32(std::int32_t gmx_unused i) : simdInternal_(vec_splats(i)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFInt32(__vector signed int simd) : simdInternal_(simd) {}
+
+    __vector signed int simdInternal_;
+};
+
+class SimdFBool
+{
+public:
+    SimdFBool() {}
+
+    SimdFBool(bool b) :
+        simdInternal_(reinterpret_cast<__vector vsxBool int>(vec_splats(b ? 0xFFFFFFFF : 0)))
+    {
+    }
+
+    // Internal utility constructor to simplify return statements
+    SimdFBool(__vector vsxBool int simd) : simdInternal_(simd) {}
+
+    __vector vsxBool int simdInternal_;
+};
+
+class SimdFIBool
+{
+public:
+    SimdFIBool() {}
+
+    SimdFIBool(bool b) :
+        simdInternal_(reinterpret_cast<__vector vsxBool int>(vec_splats(b ? 0xFFFFFFFF : 0)))
+    {
+    }
+
+    // Internal utility constructor to simplify return statements
+    SimdFIBool(__vector vsxBool int simd) : simdInternal_(simd) {}
+
+    __vector vsxBool int simdInternal_;
+};
+
+// Note that the interfaces we use here have been a mess in xlc;
+// currently version 13.1.5 is required.
+
+static inline SimdFloat gmx_simdcall simdLoad(const float* m, SimdFloatTag = {})
+{
+    return { *reinterpret_cast<const __vector float*>(m) };
+}
+
+static inline void gmx_simdcall store(float* m, SimdFloat a)
+{
+    *reinterpret_cast<__vector float*>(m) = a.simdInternal_;
+}
+
+static inline SimdFloat gmx_simdcall simdLoadU(const float* m, SimdFloatTag = {})
+{
+    return
+    {
+#if __GNUC__ < 7
+        *reinterpret_cast<const __vector float*>(m)
+#else
+        vec_xl(0, m)
+#endif
+    };
+}
+
+static inline void gmx_simdcall storeU(float* m, SimdFloat a)
+{
+#if __GNUC__ < 7
+    *reinterpret_cast<__vector float*>(m) = a.simdInternal_;
+#else
+    vec_xst(a.simdInternal_, 0, m);
+#endif
+}
+
+static inline SimdFloat gmx_simdcall setZeroF()
+{
+    return { vec_splats(0.0F) };
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdFInt32Tag)
+{
+    return { *reinterpret_cast<const __vector int*>(m) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdFInt32 a)
+{
+    *reinterpret_cast<__vector int*>(m) = a.simdInternal_;
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdFInt32Tag)
+{
+    return
+    {
+#if __GNUC__ < 7
+        *reinterpret_cast<const __vector int*>(m)
+#else
+        vec_xl(0, m)
+#endif
+    };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdFInt32 a)
+{
+#if __GNUC__ < 7
+    *reinterpret_cast<__vector int*>(m) = a.simdInternal_;
+#else
+    vec_xst(a.simdInternal_, 0, m);
+#endif
+}
+
+static inline SimdFInt32 gmx_simdcall setZeroFI()
+{
+    return { vec_splats(static_cast<int>(0)) };
+}
+
+// gcc-4.9 does not detect that vec_extract() uses its argument
+template<int index>
+static inline std::int32_t gmx_simdcall extract(SimdFInt32 gmx_unused a)
+{
+    return vec_extract(a.simdInternal_, index);
+}
+
+static inline SimdFloat gmx_simdcall operator&(SimdFloat a, SimdFloat b)
+{
+    return { vec_and(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall andNot(SimdFloat a, SimdFloat b)
+{
+    return { vec_andc(b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator|(SimdFloat a, SimdFloat b)
+{
+    return { vec_or(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator^(SimdFloat a, SimdFloat b)
+{
+    return { vec_xor(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator+(SimdFloat a, SimdFloat b)
+{
+    return { vec_add(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat a, SimdFloat b)
+{
+    return { vec_sub(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat x)
+{
+    return { -x.simdInternal_ };
+}
+
+static inline SimdFloat gmx_simdcall operator*(SimdFloat a, SimdFloat b)
+{
+    return { vec_mul(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { vec_madd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { vec_msub(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { vec_nmsub(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { vec_nmadd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall rsqrt(SimdFloat x)
+{
+    return { vec_rsqrte(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall rcp(SimdFloat x)
+{
+    return { vec_re(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskAdd(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    return { vec_add(a.simdInternal_,
+                     vec_and(b.simdInternal_, reinterpret_cast<__vector float>(m.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall maskzMul(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    SimdFloat prod = a * b;
+
+    return { vec_and(prod.simdInternal_, reinterpret_cast<__vector float>(m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall maskzFma(SimdFloat a, SimdFloat b, SimdFloat c, SimdFBool m)
+{
+    SimdFloat prod = fma(a, b, c);
+
+    return { vec_and(prod.simdInternal_, reinterpret_cast<__vector float>(m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall maskzRsqrt(SimdFloat x, SimdFBool m)
+{
+#ifndef NDEBUG
+    x.simdInternal_ = vec_sel(vec_splats(1.0F), x.simdInternal_, m.simdInternal_);
+#endif
+    return { vec_and(vec_rsqrte(x.simdInternal_), reinterpret_cast<__vector float>(m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall maskzRcp(SimdFloat x, SimdFBool m)
+{
+#ifndef NDEBUG
+    x.simdInternal_ = vec_sel(vec_splats(1.0F), x.simdInternal_, m.simdInternal_);
+#endif
+    return { vec_and(vec_re(x.simdInternal_), reinterpret_cast<__vector float>(m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall abs(SimdFloat x)
+{
+    return { vec_abs(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall max(SimdFloat a, SimdFloat b)
+{
+    return { vec_max(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall min(SimdFloat a, SimdFloat b)
+{
+    return { vec_min(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall round(SimdFloat x)
+{
+    return { vec_round(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall trunc(SimdFloat x)
+{
+    return { vec_trunc(x.simdInternal_) };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall frexp(SimdFloat value, SimdFInt32* exponent)
+{
+    const __vector float exponentMask = reinterpret_cast<__vector float>(vec_splats(0x7F800000U));
+    const __vector signed int exponentBias = vec_splats(126);
+    const __vector float      half         = vec_splats(0.5F);
+    __vector signed int       iExponent;
+
+    __vector vsxBool int valueIsZero =
+            vec_cmpeq(value.simdInternal_, reinterpret_cast<__vector float>(vec_splats(0.0)));
+
+    iExponent = reinterpret_cast<__vector signed int>(vec_and(value.simdInternal_, exponentMask));
+    iExponent = vec_sub(vec_sr(iExponent, vec_splats(23U)), exponentBias);
+    iExponent = vec_andc(iExponent, reinterpret_cast<__vector int>(valueIsZero));
+
+    __vector float result = vec_or(vec_andc(value.simdInternal_, exponentMask), half);
+    result                = vec_sel(result, value.simdInternal_, valueIsZero);
+
+    exponent->simdInternal_ = iExponent;
+
+    return { result };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall ldexp(SimdFloat value, SimdFInt32 exponent)
+{
+    const __vector signed int exponentBias = vec_splats(127);
+    __vector signed int       iExponent;
+
+    iExponent = vec_add(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = vec_max(iExponent, vec_splat_s32(0));
+    }
+
+    iExponent = vec_sl(iExponent, vec_splats(23U));
+
+    return { vec_mul(value.simdInternal_, reinterpret_cast<__vector float>(iExponent)) };
+}
+
+static inline float gmx_simdcall reduce(SimdFloat x)
+{
+    const __vector unsigned char perm1 = { 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 };
+    const __vector unsigned char perm2 = { 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3 };
+
+    x.simdInternal_ = vec_add(x.simdInternal_, vec_perm(x.simdInternal_, x.simdInternal_, perm1));
+    x.simdInternal_ = vec_add(x.simdInternal_, vec_perm(x.simdInternal_, x.simdInternal_, perm2));
+    return vec_extract(x.simdInternal_, 0);
+}
+
+static inline SimdFBool gmx_simdcall operator==(SimdFloat a, SimdFloat b)
+{
+    return { vec_cmpeq(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator!=(SimdFloat a, SimdFloat b)
+{
+    return { vec_or(vec_cmpgt(a.simdInternal_, b.simdInternal_),
+                    vec_cmplt(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline SimdFBool gmx_simdcall operator<(SimdFloat a, SimdFloat b)
+{
+    return { vec_cmplt(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator<=(SimdFloat a, SimdFloat b)
+{
+    return { vec_cmple(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall testBits(SimdFloat a)
+{
+    return { vec_cmpgt(reinterpret_cast<__vector unsigned int>(a.simdInternal_), vec_splats(0U)) };
+}
+
+static inline SimdFBool gmx_simdcall operator&&(SimdFBool a, SimdFBool b)
+{
+    return { vec_and(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator||(SimdFBool a, SimdFBool b)
+{
+    return { vec_or(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFBool a)
+{
+    return vec_any_ne(a.simdInternal_, reinterpret_cast<__vector vsxBool int>(vec_splats(0)));
+}
+
+static inline SimdFloat gmx_simdcall selectByMask(SimdFloat a, SimdFBool m)
+{
+    return { vec_and(a.simdInternal_, reinterpret_cast<__vector float>(m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall selectByNotMask(SimdFloat a, SimdFBool m)
+{
+    return { vec_andc(a.simdInternal_, reinterpret_cast<__vector float>(m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall blend(SimdFloat a, SimdFloat b, SimdFBool sel)
+{
+    return { vec_sel(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator&(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vec_and(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall andNot(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vec_andc(b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator|(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vec_or(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator^(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vec_xor(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator+(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vec_add(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator-(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vec_sub(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator*(SimdFInt32 a, SimdFInt32 b)
+{
+    return { a.simdInternal_ * b.simdInternal_ };
+}
+
+static inline SimdFIBool gmx_simdcall operator==(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vec_cmpeq(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall testBits(SimdFInt32 a)
+{
+    return { vec_cmpgt(reinterpret_cast<__vector unsigned int>(a.simdInternal_), vec_splats(0U)) };
+}
+
+static inline SimdFIBool gmx_simdcall operator<(SimdFInt32 a, SimdFInt32 b)
+{
+    return { vec_cmplt(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator&&(SimdFIBool a, SimdFIBool b)
+{
+    return { vec_and(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator||(SimdFIBool a, SimdFIBool b)
+{
+    return { vec_or(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFIBool a)
+{
+    return vec_any_ne(a.simdInternal_, reinterpret_cast<__vector vsxBool int>(vec_splats(0)));
+}
+
+static inline SimdFInt32 gmx_simdcall selectByMask(SimdFInt32 a, SimdFIBool m)
+{
+    return { vec_and(a.simdInternal_, reinterpret_cast<__vector signed int>(m.simdInternal_)) };
+}
+
+static inline SimdFInt32 gmx_simdcall selectByNotMask(SimdFInt32 a, SimdFIBool m)
+{
+    return { vec_andc(a.simdInternal_, reinterpret_cast<__vector signed int>(m.simdInternal_)) };
+}
+
+static inline SimdFInt32 gmx_simdcall blend(SimdFInt32 a, SimdFInt32 b, SimdFIBool sel)
+{
+    return { vec_sel(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvtR2I(SimdFloat a)
+{
+    return { vec_cts(vec_round(a.simdInternal_), 0) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvttR2I(SimdFloat a)
+{
+    return { vec_cts(a.simdInternal_, 0) };
+}
+
+static inline SimdFloat gmx_simdcall cvtI2R(SimdFInt32 a)
+{
+    return { vec_ctf(a.simdInternal_, 0) };
+}
+
+static inline SimdFIBool gmx_simdcall cvtB2IB(SimdFBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdFBool gmx_simdcall cvtIB2B(SimdFIBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdFloat gmx_simdcall copysign(SimdFloat x, SimdFloat y)
+{
+#if defined(__GNUC__) && !defined(__ibmxl__) && !defined(__xlC__)
+    __vector float res;
+    __asm__("xvcpsgnsp %x0,%x1,%x2" : "=wf"(res) : "wf"(y.simdInternal_), "wf"(x.simdInternal_));
+    return { res };
+#else
+    return { vec_cpsgn(y.simdInternal_, x.simdInternal_) };
+#endif
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPLEMENTATION_IBM_VSX_SIMD_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_util_double.h b/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_util_double.h
new file mode 100644 (file)
index 0000000..fda07d6
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPLEMENTATION_IBM_VSX_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPLEMENTATION_IBM_VSX_UTIL_DOUBLE_H
+
+#include "config.h"
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_ibm_vsx_definitions.h"
+#include "impl_ibm_vsx_simd_double.h"
+
+namespace gmx
+{
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const double*      base,
+                                                    const std::int32_t offset[],
+                                                    SimdDouble*        v0,
+                                                    SimdDouble*        v1,
+                                                    SimdDouble*        v2,
+                                                    SimdDouble*        v3)
+{
+    __vector double t1, t2, t3, t4;
+
+    t1                = *reinterpret_cast<const __vector double*>(base + align * offset[0]);
+    t2                = *reinterpret_cast<const __vector double*>(base + align * offset[1]);
+    t3                = *reinterpret_cast<const __vector double*>(base + align * offset[0] + 2);
+    t4                = *reinterpret_cast<const __vector double*>(base + align * offset[1] + 2);
+    v0->simdInternal_ = vec_mergeh(t1, t2);
+    v1->simdInternal_ = vec_mergel(t1, t2);
+    v2->simdInternal_ = vec_mergeh(t3, t4);
+    v3->simdInternal_ = vec_mergel(t3, t4);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadTranspose(const double* base, const std::int32_t offset[], SimdDouble* v0, SimdDouble* v1)
+{
+    __vector double t1, t2;
+
+    t1                = *reinterpret_cast<const __vector double*>(base + align * offset[0]);
+    t2                = *reinterpret_cast<const __vector double*>(base + align * offset[1]);
+    v0->simdInternal_ = vec_mergeh(t1, t2);
+    v1->simdInternal_ = vec_mergel(t1, t2);
+}
+
+static const int c_simdBestPairAlignmentDouble = 2;
+
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const double*      base,
+                                                     const std::int32_t offset[],
+                                                     SimdDouble*        v0,
+                                                     SimdDouble*        v1,
+                                                     SimdDouble*        v2)
+{
+    SimdDouble t1, t2;
+
+    t1 = simdLoad(base + align * offset[0]);
+    t2 = simdLoad(base + align * offset[1]);
+
+    v0->simdInternal_ = vec_mergeh(t1.simdInternal_, t2.simdInternal_);
+    v1->simdInternal_ = vec_mergel(t1.simdInternal_, t2.simdInternal_);
+    v2->simdInternal_ = vec_mergeh(vec_splats(*(base + align * offset[0] + 2)),
+                                   vec_splats(*(base + align * offset[1] + 2)));
+}
+
+// gcc-4.9 fails to recognize that the argument to vec_extract() is used
+template<int align>
+static inline void gmx_simdcall transposeScatterStoreU(double*               base,
+                                                       const std::int32_t    offset[],
+                                                       SimdDouble            v0,
+                                                       SimdDouble            v1,
+                                                       SimdDouble gmx_unused v2)
+{
+    SimdDouble t1, t2;
+
+    t1.simdInternal_ = vec_mergeh(v0.simdInternal_, v1.simdInternal_);
+    t2.simdInternal_ = vec_mergel(v0.simdInternal_, v1.simdInternal_);
+
+    store(base + align * offset[0], t1);
+    base[align * offset[0] + 2] = vec_extract(v2.simdInternal_, 0);
+    store(base + align * offset[1], t2);
+    base[align * offset[1] + 2] = vec_extract(v2.simdInternal_, 1);
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    if (align % 4 == 0)
+    {
+        __vector double t1, t2, t3, t4;
+        SimdDouble      t5, t6, t7, t8;
+
+        t1 = vec_mergeh(v0.simdInternal_, v1.simdInternal_);
+        t2 = vec_mergel(v0.simdInternal_, v1.simdInternal_);
+        t3 = vec_mergeh(v2.simdInternal_, vec_splats(0.0));
+        t4 = vec_mergel(v2.simdInternal_, vec_splats(0.0));
+
+        t5               = simdLoad(base + align * offset[0]);
+        t6               = simdLoad(base + align * offset[0] + 2);
+        t5.simdInternal_ = vec_add(t5.simdInternal_, t1);
+        t6.simdInternal_ = vec_add(t6.simdInternal_, t3);
+        store(base + align * offset[0], t5);
+        store(base + align * offset[0] + 2, t6);
+
+        t5               = simdLoad(base + align * offset[1]);
+        t6               = simdLoad(base + align * offset[1] + 2);
+        t5.simdInternal_ = vec_add(t5.simdInternal_, t2);
+        t6.simdInternal_ = vec_add(t6.simdInternal_, t4);
+        store(base + align * offset[1], t5);
+        store(base + align * offset[1] + 2, t6);
+    }
+    else
+    {
+        __vector double t1, t2;
+        SimdDouble      t3, t4;
+
+        t1 = vec_mergeh(v0.simdInternal_, v1.simdInternal_);
+        t2 = vec_mergel(v0.simdInternal_, v1.simdInternal_);
+
+        t3               = simdLoad(base + align * offset[0]);
+        t3.simdInternal_ = vec_add(t3.simdInternal_, t1);
+        store(base + align * offset[0], t3);
+        base[align * offset[0] + 2] += vec_extract(v2.simdInternal_, 0);
+
+        t4               = simdLoad(base + align * offset[1]);
+        t4.simdInternal_ = vec_add(t4.simdInternal_, t2);
+        store(base + align * offset[1], t4);
+        base[align * offset[1] + 2] += vec_extract(v2.simdInternal_, 1);
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    if (align % 4 == 0)
+    {
+        __vector double t1, t2, t3, t4;
+        SimdDouble      t5, t6, t7, t8;
+
+        t1 = vec_mergeh(v0.simdInternal_, v1.simdInternal_);
+        t2 = vec_mergel(v0.simdInternal_, v1.simdInternal_);
+        t3 = vec_mergeh(v2.simdInternal_, vec_splats(0.0));
+        t4 = vec_mergel(v2.simdInternal_, vec_splats(0.0));
+
+        t5               = simdLoad(base + align * offset[0]);
+        t6               = simdLoad(base + align * offset[0] + 2);
+        t5.simdInternal_ = vec_sub(t5.simdInternal_, t1);
+        t6.simdInternal_ = vec_sub(t6.simdInternal_, t3);
+        store(base + align * offset[0], t5);
+        store(base + align * offset[0] + 2, t6);
+
+        t5               = simdLoad(base + align * offset[1]);
+        t6               = simdLoad(base + align * offset[1] + 2);
+        t5.simdInternal_ = vec_sub(t5.simdInternal_, t2);
+        t6.simdInternal_ = vec_sub(t6.simdInternal_, t4);
+        store(base + align * offset[1], t5);
+        store(base + align * offset[1] + 2, t6);
+    }
+    else
+    {
+        __vector double t1, t2;
+        SimdDouble      t3, t4;
+
+        t1 = vec_mergeh(v0.simdInternal_, v1.simdInternal_);
+        t2 = vec_mergel(v0.simdInternal_, v1.simdInternal_);
+
+        t3               = simdLoad(base + align * offset[0]);
+        t3.simdInternal_ = vec_sub(t3.simdInternal_, t1);
+        store(base + align * offset[0], t3);
+        base[align * offset[0] + 2] -= vec_extract(v2.simdInternal_, 0);
+
+        t4               = simdLoad(base + align * offset[1]);
+        t4.simdInternal_ = vec_sub(t4.simdInternal_, t2);
+        store(base + align * offset[1], t4);
+        base[align * offset[1] + 2] -= vec_extract(v2.simdInternal_, 1);
+    }
+}
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdDouble  scalar,
+                                                        SimdDouble* triplets0,
+                                                        SimdDouble* triplets1,
+                                                        SimdDouble* triplets2)
+{
+    triplets0->simdInternal_ = vec_mergeh(scalar.simdInternal_, scalar.simdInternal_);
+    triplets1->simdInternal_ = scalar.simdInternal_;
+    triplets2->simdInternal_ = vec_mergel(scalar.simdInternal_, scalar.simdInternal_);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const double* base,
+                                                             SimdDInt32    offset,
+                                                             SimdDouble*   v0,
+                                                             SimdDouble*   v1,
+                                                             SimdDouble*   v2,
+                                                             SimdDouble*   v3)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_DINT32_WIDTH];
+
+    store(ioffset, offset);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1, v2, v3);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_DINT32_WIDTH];
+
+    store(ioffset, offset);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_DINT32_WIDTH];
+
+    store(ioffset, offset);
+
+    SimdDouble t1     = simdLoadU(base + align * ioffset[0]);
+    SimdDouble t2     = simdLoadU(base + align * ioffset[1]);
+    v0->simdInternal_ = vec_mergeh(t1.simdInternal_, t2.simdInternal_);
+    v1->simdInternal_ = vec_mergel(t1.simdInternal_, t2.simdInternal_);
+}
+
+static inline double gmx_simdcall
+reduceIncr4ReturnSum(double* m, SimdDouble v0, SimdDouble v1, SimdDouble v2, SimdDouble v3)
+{
+    __vector double t1, t2, t3, t4;
+
+    t1 = vec_mergeh(v0.simdInternal_, v1.simdInternal_);
+    t2 = vec_mergel(v0.simdInternal_, v1.simdInternal_);
+    t3 = vec_mergeh(v2.simdInternal_, v3.simdInternal_);
+    t4 = vec_mergel(v2.simdInternal_, v3.simdInternal_);
+
+    t1 = vec_add(t1, t2);
+    t3 = vec_add(t3, t4);
+
+    *reinterpret_cast<__vector double*>(m) += t1;
+    *reinterpret_cast<__vector double*>(m + 2) += t3;
+
+    t1 = vec_add(t1, t3);
+    return reduce(t1);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPLEMENTATION_IBM_VSX_UTIL_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_util_float.h b/src/include/gromacs/simd/impl_ibm_vsx/impl_ibm_vsx_util_float.h
new file mode 100644 (file)
index 0000000..17a5688
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPLEMENTATION_IBM_VSX_UTIL_FLOAT_H
+#define GMX_SIMD_IMPLEMENTATION_IBM_VSX_UTIL_FLOAT_H
+
+#include "config.h"
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_ibm_vsx_definitions.h"
+#include "impl_ibm_vsx_simd_float.h"
+
+namespace gmx
+{
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const float*       base,
+                                                    const std::int32_t offset[],
+                                                    SimdFloat*         v0,
+                                                    SimdFloat*         v1,
+                                                    SimdFloat*         v2,
+                                                    SimdFloat*         v3)
+{
+    __vector float l0, l1, l2, l3;
+
+    l0 = simdLoad(base + align * offset[0]).simdInternal_;
+    l1 = simdLoad(base + align * offset[1]).simdInternal_;
+    l2 = simdLoad(base + align * offset[2]).simdInternal_;
+    l3 = simdLoad(base + align * offset[3]).simdInternal_;
+
+    __vector float t0 = vec_mergeh(l0, l2);
+    __vector float t1 = vec_mergel(l0, l2);
+    __vector float t2 = vec_mergeh(l1, l3);
+    __vector float t3 = vec_mergel(l1, l3);
+    v0->simdInternal_ = vec_mergeh(t0, t2);
+    v1->simdInternal_ = vec_mergel(t0, t2);
+    v2->simdInternal_ = vec_mergeh(t1, t3);
+    v3->simdInternal_ = vec_mergel(t1, t3);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadTranspose(const float* base, const std::int32_t offset[], SimdFloat* v0, SimdFloat* v1)
+{
+    __vector float t0, t1, t2, t3;
+
+    t0 = reinterpret_cast<__vector float>(
+            vec_splats(*reinterpret_cast<const double*>(base + align * offset[0])));
+    t1 = reinterpret_cast<__vector float>(
+            vec_splats(*reinterpret_cast<const double*>(base + align * offset[1])));
+    t2 = reinterpret_cast<__vector float>(
+            vec_splats(*reinterpret_cast<const double*>(base + align * offset[2])));
+    t3 = reinterpret_cast<__vector float>(
+            vec_splats(*reinterpret_cast<const double*>(base + align * offset[3])));
+    t0                = vec_mergeh(t0, t2);
+    t1                = vec_mergeh(t1, t3);
+    v0->simdInternal_ = vec_mergeh(t0, t1);
+    v1->simdInternal_ = vec_mergel(t0, t1);
+}
+
+static const int c_simdBestPairAlignmentFloat = 2;
+
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const float*       base,
+                                                     const std::int32_t offset[],
+                                                     SimdFloat*         v0,
+                                                     SimdFloat*         v1,
+                                                     SimdFloat*         v2)
+{
+
+    if (align % 4 == 0)
+    {
+        SimdFloat t3;
+        gatherLoadTranspose<align>(base, offset, v0, v1, v2, &t3);
+    }
+    else
+    {
+        __vector float               t1, t2, t3, t4, t5, t6, t7, t8;
+        const __vector unsigned char perm_lo2hi = { 0,  1,  2,  3,  4,  5,  6,  7,
+                                                    16, 17, 18, 19, 20, 21, 22, 23 };
+        const __vector unsigned char perm_hi2lo = { 24, 25, 26, 27, 28, 29, 30, 31,
+                                                    8,  9,  10, 11, 12, 13, 14, 15 };
+
+        t1 = reinterpret_cast<__vector float>(
+                vec_splats(*reinterpret_cast<const double*>(base + align * offset[0])));
+        t2 = reinterpret_cast<__vector float>(
+                vec_splats(*reinterpret_cast<const double*>(base + align * offset[1])));
+        t3 = reinterpret_cast<__vector float>(
+                vec_splats(*reinterpret_cast<const double*>(base + align * offset[2])));
+        t4 = reinterpret_cast<__vector float>(
+                vec_splats(*reinterpret_cast<const double*>(base + align * offset[3])));
+        t5 = vec_splats(*(base + align * offset[0] + 2));
+        t6 = vec_splats(*(base + align * offset[1] + 2));
+        t7 = vec_splats(*(base + align * offset[2] + 2));
+        t8 = vec_splats(*(base + align * offset[3] + 2));
+
+        t1                = vec_mergeh(t1, t2);
+        t3                = vec_mergeh(t3, t4);
+        v0->simdInternal_ = vec_perm(t1, t3, perm_lo2hi);
+        v1->simdInternal_ = vec_perm(t3, t1, perm_hi2lo);
+        t5                = vec_mergeh(t5, t6);
+        t7                = vec_mergeh(t7, t8);
+        v2->simdInternal_ = vec_perm(t5, t7, perm_lo2hi);
+    }
+}
+
+
+// gcc-4.9 does not recognize that the argument to vec_extract() is used
+template<int align>
+static inline void gmx_simdcall transposeScatterStoreU(float*               base,
+                                                       const std::int32_t   offset[],
+                                                       SimdFloat            v0,
+                                                       SimdFloat            v1,
+                                                       SimdFloat gmx_unused v2)
+{
+    __vector float t1, t2;
+
+    t1 = vec_mergeh(v0.simdInternal_, v1.simdInternal_);
+    t2 = vec_mergel(v0.simdInternal_, v1.simdInternal_);
+    *reinterpret_cast<double*>(base + align * offset[0]) =
+            vec_extract(reinterpret_cast<__vector double>(t1), 0);
+    base[align * offset[0] + 2] = vec_extract(v2.simdInternal_, 0);
+    *reinterpret_cast<double*>(base + align * offset[1]) =
+            vec_extract(reinterpret_cast<__vector double>(t1), 1);
+    base[align * offset[1] + 2] = vec_extract(v2.simdInternal_, 1);
+    *reinterpret_cast<double*>(base + align * offset[2]) =
+            vec_extract(reinterpret_cast<__vector double>(t2), 0);
+    base[align * offset[2] + 2] = vec_extract(v2.simdInternal_, 2);
+    *reinterpret_cast<double*>(base + align * offset[3]) =
+            vec_extract(reinterpret_cast<__vector double>(t2), 1);
+    base[align * offset[3] + 2] = vec_extract(v2.simdInternal_, 3);
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    if (align < 4)
+    {
+        const __vector unsigned char perm_hi2lo = { 24, 25, 26, 27, 28, 29, 30, 31,
+                                                    8,  9,  10, 11, 12, 13, 14, 15 };
+        __vector float               t0, t1, t2, t3, t4, t5, t6, t7;
+
+        t0 = vec_mergeh(v0.simdInternal_, v1.simdInternal_); // x0 y0 x1 y1
+        t1 = vec_perm(t0, t0, perm_hi2lo);                   // x1 y1
+        t2 = vec_mergel(v0.simdInternal_, v1.simdInternal_); // x2 y2 x3 y3
+        t3 = vec_perm(t2, t2, perm_hi2lo);                   // x3 y3
+
+        t4 = reinterpret_cast<__vector float>(
+                vec_splats(*reinterpret_cast<double*>(base + align * offset[0])));
+        t4 = vec_add(t4, t0);
+        *reinterpret_cast<double*>(base + align * offset[0]) =
+                vec_extract(reinterpret_cast<__vector double>(t4), 0);
+        {
+            float extracted = vec_extract(v2.simdInternal_, 0);
+            base[align * offset[0] + 2] += extracted;
+        }
+
+        t5 = reinterpret_cast<__vector float>(
+                vec_splats(*reinterpret_cast<double*>(base + align * offset[1])));
+        t5 = vec_add(t5, t1);
+        *reinterpret_cast<double*>(base + align * offset[1]) =
+                vec_extract(reinterpret_cast<__vector double>(t5), 0);
+        {
+            float extracted = vec_extract(v2.simdInternal_, 1);
+            base[align * offset[1] + 2] += extracted;
+        }
+
+        t6 = reinterpret_cast<__vector float>(
+                vec_splats(*reinterpret_cast<double*>(base + align * offset[2])));
+        t6 = vec_add(t6, t2);
+        *reinterpret_cast<double*>(base + align * offset[2]) =
+                vec_extract(reinterpret_cast<__vector double>(t6), 0);
+        {
+            float extracted = vec_extract(v2.simdInternal_, 2);
+            base[align * offset[2] + 2] += extracted;
+        }
+
+        t7 = reinterpret_cast<__vector float>(
+                vec_splats(*reinterpret_cast<double*>(base + align * offset[3])));
+        t7 = vec_add(t7, t3);
+        *reinterpret_cast<double*>(base + align * offset[3]) =
+                vec_extract(reinterpret_cast<__vector double>(t7), 0);
+        {
+            float extracted = vec_extract(v2.simdInternal_, 3);
+            base[align * offset[3] + 2] += extracted;
+        }
+    }
+    else
+    {
+        // Extra elements means we can use full width-4 load/store operations
+        SimdFloat      v3;
+        __vector float t0 = vec_mergeh(v0.simdInternal_, v2.simdInternal_);
+        __vector float t1 = vec_mergel(v0.simdInternal_, v2.simdInternal_);
+        __vector float t2 = vec_mergeh(v1.simdInternal_, vec_splats(0.0F));
+        __vector float t3 = vec_mergel(v1.simdInternal_, vec_splats(0.0F));
+        v0.simdInternal_  = vec_mergeh(t0, t2);
+        v1.simdInternal_  = vec_mergel(t0, t2);
+        v2.simdInternal_  = vec_mergeh(t1, t3);
+        v3.simdInternal_  = vec_mergel(t1, t3);
+
+        store(base + align * offset[0], simdLoad(base + align * offset[0]) + v0);
+        store(base + align * offset[1], simdLoad(base + align * offset[1]) + v1);
+        store(base + align * offset[2], simdLoad(base + align * offset[2]) + v2);
+        store(base + align * offset[3], simdLoad(base + align * offset[3]) + v3);
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    if (align < 4)
+    {
+        const __vector unsigned char perm_hi2lo = { 24, 25, 26, 27, 28, 29, 30, 31,
+                                                    8,  9,  10, 11, 12, 13, 14, 15 };
+        __vector float               t0, t1, t2, t3, t4, t5, t6, t7;
+
+        t0 = vec_mergeh(v0.simdInternal_, v1.simdInternal_); // x0 y0 x1 y1
+        t1 = vec_perm(t0, t0, perm_hi2lo);                   // x1 y1
+        t2 = vec_mergel(v0.simdInternal_, v1.simdInternal_); // x2 y2 x3 y3
+        t3 = vec_perm(t2, t2, perm_hi2lo);                   // x3 y3
+
+        t4 = reinterpret_cast<__vector float>(
+                vec_splats(*reinterpret_cast<double*>(base + align * offset[0])));
+        t4 = vec_sub(t4, t0);
+        *reinterpret_cast<double*>(base + align * offset[0]) =
+                vec_extract(reinterpret_cast<__vector double>(t4), 0);
+        {
+            float extracted = vec_extract(v2.simdInternal_, 0);
+            base[align * offset[0] + 2] -= extracted;
+        }
+
+        t5 = reinterpret_cast<__vector float>(
+                vec_splats(*reinterpret_cast<double*>(base + align * offset[1])));
+        t5 = vec_sub(t5, t1);
+        *reinterpret_cast<double*>(base + align * offset[1]) =
+                vec_extract(reinterpret_cast<__vector double>(t5), 0);
+        {
+            float extracted = vec_extract(v2.simdInternal_, 1);
+            base[align * offset[1] + 2] -= extracted;
+        }
+
+        t6 = reinterpret_cast<__vector float>(
+                vec_splats(*reinterpret_cast<double*>(base + align * offset[2])));
+        t6 = vec_sub(t6, t2);
+        *reinterpret_cast<double*>(base + align * offset[2]) =
+                vec_extract(reinterpret_cast<__vector double>(t6), 0);
+        {
+            float extracted = vec_extract(v2.simdInternal_, 2);
+            base[align * offset[2] + 2] -= extracted;
+        }
+
+        t7 = reinterpret_cast<__vector float>(
+                vec_splats(*reinterpret_cast<double*>(base + align * offset[3])));
+        t7 = vec_sub(t7, t3);
+        *reinterpret_cast<double*>(base + align * offset[3]) =
+                vec_extract(reinterpret_cast<__vector double>(t7), 0);
+        {
+            float extracted = vec_extract(v2.simdInternal_, 3);
+            base[align * offset[3] + 2] -= extracted;
+        }
+    }
+    else
+    {
+        // Extra elements means we can use full width-4 load/store operations
+        SimdFloat      v3;
+        __vector float t0 = vec_mergeh(v0.simdInternal_, v2.simdInternal_);
+        __vector float t1 = vec_mergel(v0.simdInternal_, v2.simdInternal_);
+        __vector float t2 = vec_mergeh(v1.simdInternal_, vec_splats(0.0F));
+        __vector float t3 = vec_mergel(v1.simdInternal_, vec_splats(0.0F));
+        v0.simdInternal_  = vec_mergeh(t0, t2);
+        v1.simdInternal_  = vec_mergel(t0, t2);
+        v2.simdInternal_  = vec_mergeh(t1, t3);
+        v3.simdInternal_  = vec_mergel(t1, t3);
+
+        store(base + align * offset[0], simdLoad(base + align * offset[0]) - v0);
+        store(base + align * offset[1], simdLoad(base + align * offset[1]) - v1);
+        store(base + align * offset[2], simdLoad(base + align * offset[2]) - v2);
+        store(base + align * offset[3], simdLoad(base + align * offset[3]) - v3);
+    }
+}
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdFloat  scalar,
+                                                        SimdFloat* triplets0,
+                                                        SimdFloat* triplets1,
+                                                        SimdFloat* triplets2)
+{
+    // These permutes will be translated to immediate permutes (xxpermdi)
+    // since they operate on doublewords, which will be faster than loading
+    // the constants required for fully flexible permutes.
+    // (although the real reason was that the latter was buggy on xlc-13.1).
+    __vector unsigned char perm0 = { 0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23 };
+    __vector unsigned char perm1 = { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };
+    __vector unsigned char perm2 = { 8, 9, 10, 11, 12, 13, 14, 15, 24, 25, 26, 27, 28, 29, 30, 31 };
+    __vector float         t0, t1;
+
+    t0                       = vec_mergeh(scalar.simdInternal_, scalar.simdInternal_);
+    t1                       = vec_mergel(scalar.simdInternal_, scalar.simdInternal_);
+    triplets0->simdInternal_ = vec_perm(t0, scalar.simdInternal_, perm0);
+    triplets1->simdInternal_ = vec_perm(t0, t1, perm1);
+    triplets2->simdInternal_ = vec_perm(scalar.simdInternal_, t1, perm2);
+}
+
+/* TODO In debug mode, xlc 13.1.5 seems to overwrite v0 on the stack,
+   leading to segfaults. Possibly the calling convention doesn't
+   implement __vector int correctly. Release mode is OK. gcc is OK. */
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const float* base,
+                                                             SimdFInt32   offset,
+                                                             SimdFloat*   v0,
+                                                             SimdFloat*   v1,
+                                                             SimdFloat*   v2,
+                                                             SimdFloat*   v3)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_FINT32_WIDTH];
+
+    store(ioffset, offset);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1, v2, v3);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v0, SimdFloat* v1)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_FINT32_WIDTH];
+
+    store(ioffset, offset);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v0, SimdFloat* v1)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_FINT32_WIDTH];
+
+    store(ioffset, offset);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1);
+}
+
+static inline float gmx_simdcall reduceIncr4ReturnSum(float* m, SimdFloat v0, SimdFloat v1, SimdFloat v2, SimdFloat v3)
+{
+    __vector float t0 = vec_mergeh(v0.simdInternal_, v2.simdInternal_);
+    __vector float t1 = vec_mergel(v0.simdInternal_, v2.simdInternal_);
+    __vector float t2 = vec_mergeh(v1.simdInternal_, v3.simdInternal_);
+    __vector float t3 = vec_mergel(v1.simdInternal_, v3.simdInternal_);
+    v0.simdInternal_  = vec_mergeh(t0, t2);
+    v1.simdInternal_  = vec_mergel(t0, t2);
+    v2.simdInternal_  = vec_mergeh(t1, t3);
+    v3.simdInternal_  = vec_mergel(t1, t3);
+
+    v0 = v0 + v1;
+    v2 = v2 + v3;
+    v0 = v0 + v2;
+    v2 = v0 + simdLoad(m);
+    store(m, v2);
+
+    return reduce(v0);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPLEMENTATION_IBM_VSX_UTIL_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_none/impl_none.h b/src/include/gromacs/simd/impl_none/impl_none.h
new file mode 100644 (file)
index 0000000..e9ce083
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_NONE_H
+#define GMX_SIMD_IMPL_NONE_H
+
+/* No SIMD implementation - assign 0 to all defines */
+#define GMX_SIMD 0
+#define GMX_SIMD_HAVE_FLOAT 0
+#define GMX_SIMD_HAVE_DOUBLE 0
+#define GMX_SIMD_HAVE_LOADU 0
+#define GMX_SIMD_HAVE_STOREU 0
+#define GMX_SIMD_HAVE_LOGICAL 0
+#define GMX_SIMD_HAVE_FMA 0
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 0
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 0
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 0
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 0
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 0
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 0
+#define GMX_SIMD4_HAVE_FLOAT 0
+#define GMX_SIMD4_HAVE_DOUBLE 0
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 0
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 0
+
+#undef GMX_SIMD_FLOAT_WIDTH
+#undef GMX_SIMD_DOUBLE_WIDTH
+#undef GMX_SIMD_FINT32_WIDTH
+#undef GMX_SIMD_DINT32_WIDTH
+#undef GMX_SIMD4_WIDTH
+#define GMX_SIMD_ALIGNMENT 8 // 1*double
+#undef GMX_SIMD_RSQRT_BITS
+#undef GMX_SIMD_RCP_BITS
+
+#endif /* GMX_SIMD_IMPL_NONE_H */
diff --git a/src/include/gromacs/simd/impl_reference/impl_reference.h b/src/include/gromacs/simd/impl_reference/impl_reference.h
new file mode 100644 (file)
index 0000000..61e4ebb
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_REFERENCE_H
+#define GMX_SIMD_IMPL_REFERENCE_H
+
+/*! \libinternal \file
+ *
+ * \brief Reference SIMD implementation, including SIMD documentation.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \ingroup module_simd
+ */
+
+
+// Definitions for this architecture
+#include "impl_reference_definitions.h"
+
+// Functions not related to floating-point SIMD (mainly prefetching)
+#include "impl_reference_general.h"
+
+// Special width-4 double-precision SIMD
+#include "impl_reference_simd4_double.h"
+
+// Special width-4 single-precision SIMD
+#include "impl_reference_simd4_float.h"
+
+// General double-precision SIMD (and double/float conversions)
+#include "impl_reference_simd_double.h"
+
+// General single-precision SIMD
+#include "impl_reference_simd_float.h"
+
+// Higher-level utility functions for double precision SIMD
+#include "impl_reference_util_double.h"
+
+// Higher-level utility functions for single precision SIMD
+#include "impl_reference_util_float.h"
+
+#endif // GMX_SIMD_IMPL_REFERENCE_H
diff --git a/src/include/gromacs/simd/impl_reference/impl_reference_definitions.h b/src/include/gromacs/simd/impl_reference/impl_reference_definitions.h
new file mode 100644 (file)
index 0000000..29dd4ae
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_REFERENCE_DEFINITIONS_H
+#define GMX_SIMD_IMPL_REFERENCE_DEFINITIONS_H
+
+/*! \libinternal \file
+ *
+ * \brief Reference SIMD implementation, including SIMD documentation.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \ingroup module_simd
+ */
+namespace gmx
+{
+
+/*! \cond libapi */
+/*! \ingroup module_simd */
+/*! \{ */
+
+/*! \name SIMD implementation capability definitions
+ *  \{
+ */
+
+/* We list all the capability definitions in the main/wrapper SIMD header for
+ * each implementation, so we don't forget to set values for unsupported
+ * features to 0.
+ */
+
+//! \brief 1 if any SIMD support is present, otherwise 0.
+#define GMX_SIMD 1
+
+/*! \brief 1 when SIMD float support is present, otherwise 0
+ *
+ * You should only use this to specifically check for single precision SIMD,
+ * support, even when the rest of Gromacs uses double precision.
+ */
+#define GMX_SIMD_HAVE_FLOAT 1
+
+//! \brief 1 if SIMD double support is present, otherwise 0
+#define GMX_SIMD_HAVE_DOUBLE 1
+
+//! \brief 1 if the SIMD implementation supports unaligned loads, otherwise 0
+#define GMX_SIMD_HAVE_LOADU 1
+
+//! \brief 1 if the SIMD implementation supports unaligned stores, otherwise 0
+#define GMX_SIMD_HAVE_STOREU 1
+
+/*! \brief 1 if the SIMD implementation has fused-multiply add hardware
+ *
+ * \note All the fused multiply-add functions are always available and can be
+ *       used in any code (by executing separate multiply and add ops), but in
+ *       a few very tight loops you might be able to save a few instructions
+ *       with a separate non-FMA code path.
+ */
+#define GMX_SIMD_HAVE_FMA 0
+
+//! \brief 1 if SIMD impl has logical operations on floating-point data, otherwise 0
+#define GMX_SIMD_HAVE_LOGICAL 1
+
+//! \brief Support for extracting integers from \ref gmx::SimdFInt32 (1/0 for present/absent)
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 1
+
+//! \brief 1 if SIMD logical ops are supported for \ref gmx::SimdFInt32, otherwise 0
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 1
+
+//! \brief 1 if SIMD arithmetic ops are supported for \ref gmx::SimdFInt32, otherwise 0
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 1
+
+//! \brief Support for extracting integer from \ref gmx::SimdDInt32 (1/0 for present/absent)
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 1
+
+//! \brief 1 if logical operations are supported for \ref gmx::SimdDInt32, otherwise 0
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+
+//! \brief 1 if SIMD arithmetic ops are supported for \ref gmx::SimdDInt32, otherwise 0
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+
+/*! \brief 1 if implementation provides single precision copysign()
+ *
+ *  Only used in simd_math.h to selectively override the generic implementation.
+ */
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 0
+
+/*! \brief 1 if implementation provides single precision 1/sqrt(x) N-R iterations faster than simd_math.h
+ *
+ *  Only used in simd_math.h to selectively override the generic implementation.
+ */
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT 0
+
+/*! \brief 1 if implementation provides single precision 1/x N-R iterations faster than simd_math.h
+ *
+ *  Only used in simd_math.h to selectively override the generic implementation.
+ */
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 0
+
+/*! \brief 1 if implementation provides single precision log() faster than simd_math.h
+ *
+ *  Only used in simd_math.h to selectively override the generic implementation.
+ */
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+
+/*! \brief 1 if implementation provides single precision exp2() faster than simd_math.h
+ *
+ *  Only used in simd_math.h to selectively override the generic implementation.
+ */
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 0
+
+/*! \brief 1 if implementation provides single precision exp() faster than simd_math.h
+ *
+ *  Only used in simd_math.h to selectively override the generic implementation.
+ */
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 0
+
+/*! \brief 1 if implementation provides double precision copysign()
+ *
+ *  Only used in simd_math.h to selectively override the generic implementation.
+ */
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 0
+
+/*! \brief 1 if implementation provides double precision 1/sqrt(x) N-R iterations faster than simd_math.h
+ *
+ *  Only used in simd_math.h to selectively override the generic implementation.
+ */
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 0
+
+/*! \brief 1 if implementation provides double precision 1/x N-R iterations faster than simd_math.h
+ *
+ *  Only used in simd_math.h to selectively override the generic implementation.
+ */
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 0
+
+/*! \brief 1 if implementation provides double precision log() faster than simd_math.h
+ *
+ *  Only used in simd_math.h to selectively override the generic implementation.
+ */
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+
+/*! \brief 1 if implementation provides double precision exp2() faster than simd_math.h
+ *
+ *  Only used in simd_math.h to selectively override the generic implementation.
+ */
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+
+/*! \brief 1 if implementation provides double precision exp() faster than simd_math.h
+ *
+ *  Only used in simd_math.h to selectively override the generic implementation.
+ */
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+
+//! \brief 1 if \ref gmx::gatherLoadUBySimdIntTranspose is present, otherwise 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+
+//! \brief 1 if \ref gmx::gatherLoadUBySimdIntTranspose is present, otherwise 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+
+//! \brief 1 if float half-register load/store/reduce utils present, otherwise 0
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 1
+
+//! \brief 1 if double half-register load/store/reduce utils present, otherwise 0
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 1
+
+#ifdef GMX_SIMD_REF_FLOAT_WIDTH
+#    define GMX_SIMD_FLOAT_WIDTH GMX_SIMD_REF_FLOAT_WIDTH
+#else
+//! \brief Width of the \ref gmx::SimdFloat datatype
+#    define GMX_SIMD_FLOAT_WIDTH 4
+#endif
+
+#ifdef GMX_SIMD_REF_DOUBLE_WIDTH
+#    define GMX_SIMD_DOUBLE_WIDTH GMX_SIMD_REF_DOUBLE_WIDTH
+#else
+//! \brief Width of the \ref gmx::SimdDouble datatype
+#    define GMX_SIMD_DOUBLE_WIDTH 4
+#endif
+
+#if GMX_SIMD_FLOAT_WIDTH >= 8 || defined DOXYGEN // set in simd.h for GMX_SIMD_FLOAT_WIDTH<=4
+//! \brief 1 if float 4xN load utils present, otherwise 0
+#    define GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT 1
+#endif
+
+#if GMX_SIMD_DOUBLE_WIDTH >= 8 || defined DOXYGEN // set in simd.h for GMX_SIMD_DOUBLE_WIDTH<=4
+//! \brief 1 if double 4xN load utils present, otherwise 0
+#    define GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE 1
+#endif
+
+//! \brief 1 if implementation provides \ref gmx::Simd4Float, otherwise 0.
+#define GMX_SIMD4_HAVE_FLOAT 1
+
+//! \brief 1 if the implementation provides \ref gmx::Simd4Double, otherwise 0.
+#define GMX_SIMD4_HAVE_DOUBLE 1
+
+//! \brief Width of the \ref gmx::SimdFInt32 datatype.
+#define GMX_SIMD_FINT32_WIDTH GMX_SIMD_FLOAT_WIDTH
+
+//! \brief Width of the \ref gmx::SimdDInt32 datatype.
+#define GMX_SIMD_DINT32_WIDTH GMX_SIMD_DOUBLE_WIDTH
+
+//! \brief The SIMD4 type is always four units wide, but this makes code more explicit
+#define GMX_SIMD4_WIDTH 4
+
+/*! \brief Maximum required alignment in bytes for aligned load/store of multiple
+ * values (maximum required for either float or double). */
+#if GMX_SIMD_DOUBLE_WIDTH >= 2 * GMX_SIMD_FLOAT_WIDTH
+#    define GMX_SIMD_ALIGNMENT (GMX_SIMD_DOUBLE_WIDTH * 8)
+#else
+#    define GMX_SIMD_ALIGNMENT (GMX_SIMD_FLOAT_WIDTH * 4)
+#endif
+
+//! \brief Accuracy of SIMD 1/sqrt(x) lookup. Used to determine number of iterations.
+#define GMX_SIMD_RSQRT_BITS 23
+
+//! \brief Accuracy of SIMD 1/x lookup. Used to determine number of iterations.
+#define GMX_SIMD_RCP_BITS 23
+
+//! \}
+
+//! \}
+
+//! \endcond
+
+} // namespace gmx
+#endif // GMX_SIMD_IMPL_REFERENCE_DEFINITIONS_H
diff --git a/src/include/gromacs/simd/impl_reference/impl_reference_general.h b/src/include/gromacs/simd/impl_reference/impl_reference_general.h
new file mode 100644 (file)
index 0000000..99d113a
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_REFERENCE_GENERAL_H
+#define GMX_SIMD_IMPL_REFERENCE_GENERAL_H
+
+#include "gromacs/utility/basedefinitions.h"
+
+/*! \libinternal \file
+ *
+ * \brief Reference SIMD implementation, general utility functions
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \ingroup module_simd
+ */
+
+namespace gmx
+{
+
+/*! \brief Prefetch memory at address m
+ *
+ *  This typically prefetches one cache line of memory from address m,
+ *  usually 64bytes or more, but the exact amount will depend on the
+ *  implementation. On many platforms this is simply a no-op. Technically it
+ *  might not be part of the SIMD instruction set, but since it is a
+ *  hardware-specific function that is normally only used in tight loops where
+ *  we also apply SIMD, it fits well here.
+ *
+ *  There are no guarantees about the level of cache or temporality, but
+ *  usually we expect stuff to end up in level 2, and be used in a few hundred
+ *  clock cycles, after which it stays in cache until evicted (normal caching).
+ *
+ * \param m Pointer to location prefetch. There are no alignment requirements,
+ *        but if the pointer is not aligned the prefetch might start at the
+ *        lower cache line boundary (meaning fewer bytes are prefetched).
+ */
+static inline void gmx_unused simdPrefetch(void gmx_unused* m)
+{
+    // Do nothing for reference implementation
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_REFERENCE_GENERAL_H
diff --git a/src/include/gromacs/simd/impl_reference/impl_reference_simd4_double.h b/src/include/gromacs/simd/impl_reference/impl_reference_simd4_double.h
new file mode 100644 (file)
index 0000000..e1f9205
--- /dev/null
@@ -0,0 +1,783 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_REFERENCE_SIMD4_DOUBLE_H
+#define GMX_SIMD_IMPL_REFERENCE_SIMD4_DOUBLE_H
+
+/*! \libinternal \file
+ *
+ * \brief Reference implementation, SIMD4 single precision.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \ingroup module_simd
+ */
+
+#include "config.h"
+
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+
+#include <algorithm>
+#include <array>
+
+#include "impl_reference_definitions.h"
+
+namespace gmx
+{
+
+/*! \cond libapi */
+/*! \addtogroup module_simd */
+/*! \{ */
+
+/*! \name Constant width-4 double precision SIMD types and instructions
+ * \{
+ */
+
+/*! \libinternal \brief SIMD4 double type.
+ *
+ * Available if \ref GMX_SIMD4_HAVE_DOUBLE is 1.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+class Simd4Double
+{
+public:
+    Simd4Double() {}
+
+    //! \brief Construct from scalar
+    Simd4Double(double d) { simdInternal_.fill(d); }
+
+    /*! \brief Internal SIMD data. Implementation dependent, don't touch.
+     *
+     * This has to be public to enable usage in combination with static inline
+     * functions, but it should never, EVER, be accessed by any code outside
+     * the corresponding implementation directory since the type will depend
+     * on the architecture.
+     */
+    std::array<double, GMX_SIMD4_WIDTH> simdInternal_;
+};
+
+/*! \libinternal  \brief SIMD4 variable type to use for logical comparisons on doubles.
+ *
+ * Available if \ref GMX_SIMD4_HAVE_DOUBLE is 1.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+class Simd4DBool
+{
+public:
+    Simd4DBool() {}
+
+    //! \brief Construct from scalar
+    Simd4DBool(bool b) { simdInternal_.fill(b); }
+
+    /*! \brief Internal SIMD data. Implementation dependent, don't touch.
+     *
+     * This has to be public to enable usage in combination with static inline
+     * functions, but it should never, EVER, be accessed by any code outside
+     * the corresponding implementation directory since the type will depend
+     * on the architecture.
+     */
+    std::array<bool, GMX_SIMD4_WIDTH> simdInternal_;
+};
+
+/*! \brief Load 4 double values from aligned memory into SIMD4 variable.
+ *
+ * \param m Pointer to memory aligned to 4 elements.
+ * \return SIMD4 variable with data loaded.
+ */
+static inline Simd4Double gmx_simdcall load4(const double* m)
+{
+    Simd4Double a;
+
+    assert(std::size_t(m) % (a.simdInternal_.size() * sizeof(double)) == 0);
+
+    std::copy(m, m + a.simdInternal_.size(), a.simdInternal_.begin());
+    return a;
+}
+
+/*! \brief Store the contents of SIMD4 double to aligned memory m.
+ *
+ * \param[out] m Pointer to memory, aligned to 4 elements.
+ * \param a SIMD4 variable to store
+ */
+static inline void gmx_simdcall store4(double* m, Simd4Double a)
+{
+    assert(std::size_t(m) % (a.simdInternal_.size() * sizeof(double)) == 0);
+
+    std::copy(a.simdInternal_.begin(), a.simdInternal_.end(), m);
+}
+
+/*! \brief Load SIMD4 double from unaligned memory.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOADU is 1.
+ *
+ * \param m Pointer to memory, no alignment requirement.
+ * \return SIMD4 variable with data loaded.
+ */
+static inline Simd4Double gmx_simdcall load4U(const double* m)
+{
+    Simd4Double a;
+    std::copy(m, m + a.simdInternal_.size(), a.simdInternal_.begin());
+    return a;
+}
+
+/*! \brief Store SIMD4 double to unaligned memory.
+ *
+ * Available if \ref GMX_SIMD_HAVE_STOREU is 1.
+ *
+ * \param[out] m Pointer to memory, no alignment requirement.
+ * \param a SIMD4 variable to store.
+ */
+static inline void gmx_simdcall store4U(double* m, Simd4Double a)
+{
+    std::copy(a.simdInternal_.begin(), a.simdInternal_.end(), m);
+}
+
+/*! \brief Set all SIMD4 double elements to 0.
+ *
+ * You should typically just call \ref gmx::setZero(), which uses proxy objects
+ * internally to handle all types rather than adding the suffix used here.
+ *
+ * \return SIMD4 0.0
+ */
+static inline Simd4Double gmx_simdcall simd4SetZeroD()
+{
+    return Simd4Double(0.0);
+}
+
+
+/*! \brief Bitwise and for two SIMD4 double variables.
+ *
+ * Supported if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return data1 & data2
+ */
+static inline Simd4Double gmx_simdcall operator&(Simd4Double a, Simd4Double b)
+{
+    Simd4Double res;
+
+    union
+    {
+        double       r;
+        std::int64_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = conv1.i & conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+
+/*! \brief Bitwise andnot for two SIMD4 double variables. c=(~a) & b.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return (~data1) & data2
+ */
+static inline Simd4Double gmx_simdcall andNot(Simd4Double a, Simd4Double b)
+{
+    Simd4Double res;
+
+    union
+    {
+        double       r;
+        std::int64_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = ~conv1.i & conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+
+/*! \brief Bitwise or for two SIMD4 doubles.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return data1 | data2
+ */
+static inline Simd4Double gmx_simdcall operator|(Simd4Double a, Simd4Double b)
+{
+    Simd4Double res;
+
+    union
+    {
+        double       r;
+        std::int64_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = conv1.i | conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+/*! \brief Bitwise xor for two SIMD4 double variables.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return data1 ^ data2
+ */
+static inline Simd4Double gmx_simdcall operator^(Simd4Double a, Simd4Double b)
+{
+    Simd4Double res;
+
+    union
+    {
+        double       r;
+        std::int64_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = conv1.i ^ conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+/*! \brief Add two double SIMD4 variables.
+ *
+ * \param a term1
+ * \param b term2
+ * \return a+b
+ */
+static inline Simd4Double gmx_simdcall operator+(Simd4Double a, Simd4Double b)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] + b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Subtract two SIMD4 variables.
+ *
+ * \param a term1
+ * \param b term2
+ * \return a-b
+ */
+static inline Simd4Double gmx_simdcall operator-(Simd4Double a, Simd4Double b)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] - b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief SIMD4 floating-point negate.
+ *
+ * \param a SIMD4 floating-point value
+ * \return -a
+ */
+static inline Simd4Double gmx_simdcall operator-(Simd4Double a)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = -a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Multiply two SIMD4 variables.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \return a*b.
+ */
+static inline Simd4Double gmx_simdcall operator*(Simd4Double a, Simd4Double b)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] * b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief SIMD4 Fused-multiply-add. Result is a*b+c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return a*b+c
+ */
+static inline Simd4Double gmx_simdcall fma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return a * b + c;
+}
+
+/*! \brief SIMD4 Fused-multiply-subtract. Result is a*b-c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return a*b-c
+ */
+static inline Simd4Double gmx_simdcall fms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return a * b - c;
+}
+
+/*! \brief SIMD4 Fused-negated-multiply-add. Result is -a*b+c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return -a*b+c
+ */
+static inline Simd4Double gmx_simdcall fnma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return c - a * b;
+}
+
+/*! \brief SIMD4 Fused-negated-multiply-subtract. Result is -a*b-c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return -a*b-c
+ */
+static inline Simd4Double gmx_simdcall fnms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return -a * b - c;
+}
+
+/*! \brief SIMD4 1.0/sqrt(x) lookup.
+ *
+ * This is a low-level instruction that should only be called from routines
+ * implementing the inverse square root in simd_math.h.
+ *
+ * \param x Argument, x>0
+ * \return Approximation of 1/sqrt(x), accuracy is \ref GMX_SIMD_RSQRT_BITS.
+ */
+static inline Simd4Double gmx_simdcall rsqrt(Simd4Double x)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        // sic - we only use single precision for the lookup
+        res.simdInternal_[i] = 1.0F / std::sqrt(static_cast<float>(x.simdInternal_[i]));
+    }
+    return res;
+};
+
+
+/*! \brief SIMD4 Floating-point abs().
+ *
+ * \param a any floating point values
+ * \return fabs(a) for each element.
+ */
+static inline Simd4Double gmx_simdcall abs(Simd4Double a)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::abs(a.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Set each SIMD4 element to the largest from two variables.
+ *
+ * \param a Any floating-point value
+ * \param b Any floating-point value
+ * \return max(a,b) for each element.
+ */
+static inline Simd4Double gmx_simdcall max(Simd4Double a, Simd4Double b)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::max(a.simdInternal_[i], b.simdInternal_[i]);
+    }
+    return res;
+}
+
+
+/*! \brief Set each SIMD4 element to the largest from two variables.
+ *
+ * \param a Any floating-point value
+ * \param b Any floating-point value
+ * \return max(a,b) for each element.
+ */
+static inline Simd4Double gmx_simdcall min(Simd4Double a, Simd4Double b)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::min(a.simdInternal_[i], b.simdInternal_[i]);
+    }
+    return res;
+}
+
+
+/*! \brief SIMD4 Round to nearest integer value (in floating-point format).
+ *
+ * \param a Any floating-point value
+ * \return The nearest integer, represented in floating-point format.
+ */
+static inline Simd4Double gmx_simdcall round(Simd4Double a)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::round(a.simdInternal_[i]);
+    }
+    return res;
+}
+
+
+/*! \brief Truncate SIMD4, i.e. round towards zero - common hardware instruction.
+ *
+ * \param a Any floating-point value
+ * \return Integer rounded towards zero, represented in floating-point format.
+ *
+ * \note This is truncation towards zero, not floor(). The reason for this
+ * is that truncation is virtually always present as a dedicated hardware
+ * instruction, but floor() frequently isn't.
+ */
+static inline Simd4Double gmx_simdcall trunc(Simd4Double a)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::trunc(a.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Return dot product of two double precision SIMD4 variables.
+ *
+ * The dot product is calculated between the first three elements in the two
+ * vectors, while the fourth is ignored. The result is returned as a scalar.
+ *
+ * \param a vector1
+ * \param b vector2
+ * \result a[0]*b[0]+a[1]*b[1]+a[2]*b[2], returned as scalar. Last element is ignored.
+ */
+static inline double gmx_simdcall dotProduct(Simd4Double a, Simd4Double b)
+{
+    return (a.simdInternal_[0] * b.simdInternal_[0] + a.simdInternal_[1] * b.simdInternal_[1]
+            + a.simdInternal_[2] * b.simdInternal_[2]);
+}
+
+/*! \brief SIMD4 double transpose
+ *
+ * \param[in,out] v0  Row 0 on input, column 0 on output
+ * \param[in,out] v1  Row 1 on input, column 1 on output
+ * \param[in,out] v2  Row 2 on input, column 2 on output
+ * \param[in,out] v3  Row 3 on input, column 3 on output
+ */
+static inline void gmx_simdcall transpose(Simd4Double* v0, Simd4Double* v1, Simd4Double* v2, Simd4Double* v3)
+{
+    Simd4Double t0       = *v0;
+    Simd4Double t1       = *v1;
+    Simd4Double t2       = *v2;
+    Simd4Double t3       = *v3;
+    v0->simdInternal_[0] = t0.simdInternal_[0];
+    v0->simdInternal_[1] = t1.simdInternal_[0];
+    v0->simdInternal_[2] = t2.simdInternal_[0];
+    v0->simdInternal_[3] = t3.simdInternal_[0];
+    v1->simdInternal_[0] = t0.simdInternal_[1];
+    v1->simdInternal_[1] = t1.simdInternal_[1];
+    v1->simdInternal_[2] = t2.simdInternal_[1];
+    v1->simdInternal_[3] = t3.simdInternal_[1];
+    v2->simdInternal_[0] = t0.simdInternal_[2];
+    v2->simdInternal_[1] = t1.simdInternal_[2];
+    v2->simdInternal_[2] = t2.simdInternal_[2];
+    v2->simdInternal_[3] = t3.simdInternal_[2];
+    v3->simdInternal_[0] = t0.simdInternal_[3];
+    v3->simdInternal_[1] = t1.simdInternal_[3];
+    v3->simdInternal_[2] = t2.simdInternal_[3];
+    v3->simdInternal_[3] = t3.simdInternal_[3];
+}
+
+/*! \brief a==b for SIMD4 double
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a==b.
+ */
+static inline Simd4DBool gmx_simdcall operator==(Simd4Double a, Simd4Double b)
+{
+    Simd4DBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] == b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief a!=b for SIMD4 double
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a!=b.
+ */
+static inline Simd4DBool gmx_simdcall operator!=(Simd4Double a, Simd4Double b)
+{
+    Simd4DBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] != b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief a<b for SIMD4 double
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a<b.
+ */
+static inline Simd4DBool gmx_simdcall operator<(Simd4Double a, Simd4Double b)
+{
+    Simd4DBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] < b.simdInternal_[i]);
+    }
+    return res;
+}
+
+
+/*! \brief a<=b for SIMD4 double.
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a<=b.
+ */
+static inline Simd4DBool gmx_simdcall operator<=(Simd4Double a, Simd4Double b)
+{
+    Simd4DBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] <= b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Logical \a and on single precision SIMD4 booleans.
+ *
+ * \param a logical vars 1
+ * \param b logical vars 2
+ * \return For each element, the result boolean is true if a \& b are true.
+ *
+ * \note This is not necessarily a bitwise operation - the storage format
+ * of booleans is implementation-dependent.
+ */
+static inline Simd4DBool gmx_simdcall operator&&(Simd4DBool a, Simd4DBool b)
+{
+    Simd4DBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] && b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Logical \a or on single precision SIMD4 booleans.
+ *
+ * \param a logical vars 1
+ * \param b logical vars 2
+ * \return For each element, the result boolean is true if a or b is true.
+ *
+ * Note that this is not necessarily a bitwise operation - the storage format
+ * of booleans is implementation-dependent.
+ */
+static inline Simd4DBool gmx_simdcall operator||(Simd4DBool a, Simd4DBool b)
+{
+    Simd4DBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] || b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Returns non-zero if any of the boolean in SIMD4 a is True, otherwise 0.
+ *
+ * \param a Logical variable.
+ * \return true if any element in a is true, otherwise false.
+ *
+ * The actual return value for truth will depend on the architecture,
+ * so any non-zero value is considered truth.
+ */
+static inline bool gmx_simdcall anyTrue(Simd4DBool a)
+{
+    bool res = false;
+
+    for (std::size_t i = 0; i < a.simdInternal_.size(); i++)
+    {
+        res = res || a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Select from single precision SIMD4 variable where boolean is true.
+ *
+ * \param a Floating-point variable to select from
+ * \param mask Boolean selector
+ * \return  For each element, a is selected for true, 0 for false.
+ */
+static inline Simd4Double gmx_simdcall selectByMask(Simd4Double a, Simd4DBool mask)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = mask.simdInternal_[i] ? a.simdInternal_[i] : 0.0;
+    }
+    return res;
+}
+
+/*! \brief Select from single precision SIMD4 variable where boolean is false.
+ *
+ * \param a Floating-point variable to select from
+ * \param mask Boolean selector
+ * \return  For each element, a is selected for false, 0 for true (sic).
+ */
+static inline Simd4Double gmx_simdcall selectByNotMask(Simd4Double a, Simd4DBool mask)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = mask.simdInternal_[i] ? 0.0 : a.simdInternal_[i];
+    }
+    return res;
+}
+
+
+/*! \brief Vector-blend SIMD4 selection.
+ *
+ * \param a First source
+ * \param b Second source
+ * \param sel Boolean selector
+ * \return For each element, select b if sel is true, a otherwise.
+ */
+static inline Simd4Double gmx_simdcall blend(Simd4Double a, Simd4Double b, Simd4DBool sel)
+{
+    Simd4Double res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = sel.simdInternal_[i] ? b.simdInternal_[i] : a.simdInternal_[i];
+    }
+    return res;
+}
+
+
+/*! \brief Return sum of all elements in SIMD4 double variable.
+ *
+ * \param a SIMD4 variable to reduce/sum.
+ * \return The sum of all elements in the argument variable.
+ *
+ */
+static inline double gmx_simdcall reduce(Simd4Double a)
+{
+    double sum = 0.0;
+
+    for (std::size_t i = 0; i < a.simdInternal_.size(); i++)
+    {
+        sum += a.simdInternal_[i];
+    }
+    return sum;
+}
+
+//! \}
+
+//! \}
+
+//! \endcond
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_REFERENCE_SIMD4_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_reference/impl_reference_simd4_float.h b/src/include/gromacs/simd/impl_reference/impl_reference_simd4_float.h
new file mode 100644 (file)
index 0000000..bd6d240
--- /dev/null
@@ -0,0 +1,781 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_REFERENCE_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPL_REFERENCE_SIMD4_FLOAT_H
+
+/*! \libinternal \file
+ *
+ * \brief Reference implementation, SIMD4 single precision.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \ingroup module_simd
+ */
+
+#include "config.h"
+
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+
+#include <algorithm>
+#include <array>
+
+#include "impl_reference_definitions.h"
+
+namespace gmx
+{
+
+/*! \cond libapi */
+/*! \addtogroup module_simd */
+/*! \{ */
+
+/*! \name Constant width-4 single precision SIMD types and instructions
+ * \{
+ */
+
+/*! \libinternal \brief SIMD4 float type.
+ *
+ * Available if \ref GMX_SIMD4_HAVE_FLOAT is 1.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+class Simd4Float
+{
+public:
+    Simd4Float() {}
+
+    //! \brief Construct from scalar
+    Simd4Float(float f) { simdInternal_.fill(f); }
+
+    /*! \brief Internal SIMD data. Implementation dependent, don't touch.
+     *
+     * This has to be public to enable usage in combination with static inline
+     * functions, but it should never, EVER, be accessed by any code outside
+     * the corresponding implementation directory since the type will depend
+     * on the architecture.
+     */
+    std::array<float, GMX_SIMD4_WIDTH> simdInternal_;
+};
+
+/*! \libinternal  \brief SIMD4 variable type to use for logical comparisons on floats.
+ *
+ * Available if \ref GMX_SIMD4_HAVE_FLOAT is 1.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+class Simd4FBool
+{
+public:
+    Simd4FBool() {}
+
+    //! \brief Construct from scalar bool
+    Simd4FBool(bool b) { simdInternal_.fill(b); }
+
+    /*! \brief Internal SIMD data. Implementation dependent, don't touch.
+     *
+     * This has to be public to enable usage in combination with static inline
+     * functions, but it should never, EVER, be accessed by any code outside
+     * the corresponding implementation directory since the type will depend
+     * on the architecture.
+     */
+    std::array<bool, GMX_SIMD4_WIDTH> simdInternal_;
+};
+
+/*! \brief Load 4 float values from aligned memory into SIMD4 variable.
+ *
+ * \param m Pointer to memory aligned to 4 elements.
+ * \return SIMD4 variable with data loaded.
+ */
+static inline Simd4Float gmx_simdcall load4(const float* m)
+{
+    Simd4Float a;
+
+    assert(std::size_t(m) % (a.simdInternal_.size() * sizeof(float)) == 0);
+
+    std::copy(m, m + a.simdInternal_.size(), a.simdInternal_.begin());
+    return a;
+}
+
+/*! \brief Store the contents of SIMD4 float to aligned memory m.
+ *
+ * \param[out] m Pointer to memory, aligned to 4 elements.
+ * \param a SIMD4 variable to store
+ */
+static inline void gmx_simdcall store4(float* m, Simd4Float a)
+{
+    assert(std::size_t(m) % (a.simdInternal_.size() * sizeof(float)) == 0);
+
+    std::copy(a.simdInternal_.begin(), a.simdInternal_.end(), m);
+}
+
+/*! \brief Load SIMD4 float from unaligned memory.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOADU is 1.
+ *
+ * \param m Pointer to memory, no alignment requirement.
+ * \return SIMD4 variable with data loaded.
+ */
+static inline Simd4Float gmx_simdcall load4U(const float* m)
+{
+    Simd4Float a;
+    std::copy(m, m + a.simdInternal_.size(), a.simdInternal_.begin());
+    return a;
+}
+
+/*! \brief Store SIMD4 float to unaligned memory.
+ *
+ * Available if \ref GMX_SIMD_HAVE_STOREU is 1.
+ *
+ * \param[out] m Pointer to memory, no alignment requirement.
+ * \param a SIMD4 variable to store.
+ */
+static inline void gmx_simdcall store4U(float* m, Simd4Float a)
+{
+    std::copy(a.simdInternal_.begin(), a.simdInternal_.end(), m);
+}
+
+/*! \brief Set all SIMD4 float elements to 0.
+ *
+ * You should typically just call \ref gmx::setZero(), which uses proxy objects
+ * internally to handle all types rather than adding the suffix used here.
+ *
+ * \return SIMD4 0.0
+ */
+static inline Simd4Float gmx_simdcall simd4SetZeroF()
+{
+    return Simd4Float(0.0F);
+}
+
+
+/*! \brief Bitwise and for two SIMD4 float variables.
+ *
+ * Supported if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return data1 & data2
+ */
+static inline Simd4Float gmx_simdcall operator&(Simd4Float a, Simd4Float b)
+{
+    Simd4Float res;
+
+    union
+    {
+        float        r;
+        std::int32_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = conv1.i & conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+
+/*! \brief Bitwise andnot for two SIMD4 float variables. c=(~a) & b.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return (~data1) & data2
+ */
+static inline Simd4Float gmx_simdcall andNot(Simd4Float a, Simd4Float b)
+{
+    Simd4Float res;
+
+    union
+    {
+        float        r;
+        std::int32_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = ~conv1.i & conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+
+/*! \brief Bitwise or for two SIMD4 floats.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return data1 | data2
+ */
+static inline Simd4Float gmx_simdcall operator|(Simd4Float a, Simd4Float b)
+{
+    Simd4Float res;
+
+    union
+    {
+        float        r;
+        std::int32_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = conv1.i | conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+/*! \brief Bitwise xor for two SIMD4 float variables.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return data1 ^ data2
+ */
+static inline Simd4Float gmx_simdcall operator^(Simd4Float a, Simd4Float b)
+{
+    Simd4Float res;
+
+    union
+    {
+        float        r;
+        std::int32_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = conv1.i ^ conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+/*! \brief Add two float SIMD4 variables.
+ *
+ * \param a term1
+ * \param b term2
+ * \return a+b
+ */
+static inline Simd4Float gmx_simdcall operator+(Simd4Float a, Simd4Float b)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] + b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Subtract two SIMD4 variables.
+ *
+ * \param a term1
+ * \param b term2
+ * \return a-b
+ */
+static inline Simd4Float gmx_simdcall operator-(Simd4Float a, Simd4Float b)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] - b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief SIMD4 floating-point negate.
+ *
+ * \param a SIMD4 floating-point value
+ * \return -a
+ */
+static inline Simd4Float gmx_simdcall operator-(Simd4Float a)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = -a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Multiply two SIMD4 variables.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \return a*b.
+ */
+static inline Simd4Float gmx_simdcall operator*(Simd4Float a, Simd4Float b)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] * b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief SIMD4 Fused-multiply-add. Result is a*b+c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return a*b+c
+ */
+static inline Simd4Float gmx_simdcall fma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return a * b + c;
+}
+
+/*! \brief SIMD4 Fused-multiply-subtract. Result is a*b-c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return a*b-c
+ */
+static inline Simd4Float gmx_simdcall fms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return a * b - c;
+}
+
+/*! \brief SIMD4 Fused-negated-multiply-add. Result is -a*b+c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return -a*b+c
+ */
+static inline Simd4Float gmx_simdcall fnma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return c - a * b;
+}
+
+/*! \brief SIMD4 Fused-negated-multiply-subtract. Result is -a*b-c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return -a*b-c
+ */
+static inline Simd4Float gmx_simdcall fnms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return -a * b - c;
+}
+
+/*! \brief SIMD4 1.0/sqrt(x) lookup.
+ *
+ * This is a low-level instruction that should only be called from routines
+ * implementing the inverse square root in simd_math.h.
+ *
+ * \param x Argument, x>0
+ * \return Approximation of 1/sqrt(x), accuracy is \ref GMX_SIMD_RSQRT_BITS.
+ */
+static inline Simd4Float gmx_simdcall rsqrt(Simd4Float x)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = 1.0F / std::sqrt(x.simdInternal_[i]);
+    }
+    return res;
+};
+
+
+/*! \brief SIMD4 Floating-point fabs().
+ *
+ * \param a any floating point values
+ * \return fabs(a) for each element.
+ */
+static inline Simd4Float gmx_simdcall abs(Simd4Float a)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::abs(a.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Set each SIMD4 element to the largest from two variables.
+ *
+ * \param a Any floating-point value
+ * \param b Any floating-point value
+ * \return max(a,b) for each element.
+ */
+static inline Simd4Float gmx_simdcall max(Simd4Float a, Simd4Float b)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::max(a.simdInternal_[i], b.simdInternal_[i]);
+    }
+    return res;
+}
+
+
+/*! \brief Set each SIMD4 element to the largest from two variables.
+ *
+ * \param a Any floating-point value
+ * \param b Any floating-point value
+ * \return max(a,b) for each element.
+ */
+static inline Simd4Float gmx_simdcall min(Simd4Float a, Simd4Float b)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::min(a.simdInternal_[i], b.simdInternal_[i]);
+    }
+    return res;
+}
+
+
+/*! \brief SIMD4 Round to nearest integer value (in floating-point format).
+ *
+ * \param a Any floating-point value
+ * \return The nearest integer, represented in floating-point format.
+ */
+static inline Simd4Float gmx_simdcall round(Simd4Float a)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::round(a.simdInternal_[i]);
+    }
+    return res;
+}
+
+
+/*! \brief Truncate SIMD4, i.e. round towards zero - common hardware instruction.
+ *
+ * \param a Any floating-point value
+ * \return Integer rounded towards zero, represented in floating-point format.
+ *
+ * \note This is truncation towards zero, not floor(). The reason for this
+ * is that truncation is virtually always present as a dedicated hardware
+ * instruction, but floor() frequently isn't.
+ */
+static inline Simd4Float gmx_simdcall trunc(Simd4Float a)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::trunc(a.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Return dot product of two single precision SIMD4 variables.
+ *
+ * The dot product is calculated between the first three elements in the two
+ * vectors, while the fourth is ignored. The result is returned as a scalar.
+ *
+ * \param a vector1
+ * \param b vector2
+ * \result a[0]*b[0]+a[1]*b[1]+a[2]*b[2], returned as scalar. Last element is ignored.
+ */
+static inline float gmx_simdcall dotProduct(Simd4Float a, Simd4Float b)
+{
+    return (a.simdInternal_[0] * b.simdInternal_[0] + a.simdInternal_[1] * b.simdInternal_[1]
+            + a.simdInternal_[2] * b.simdInternal_[2]);
+}
+
+/*! \brief SIMD4 float transpose
+ *
+ * \param[in,out] v0  Row 0 on input, column 0 on output
+ * \param[in,out] v1  Row 1 on input, column 1 on output
+ * \param[in,out] v2  Row 2 on input, column 2 on output
+ * \param[in,out] v3  Row 3 on input, column 3 on output
+ */
+static inline void gmx_simdcall transpose(Simd4Float* v0, Simd4Float* v1, Simd4Float* v2, Simd4Float* v3)
+{
+    Simd4Float t0        = *v0;
+    Simd4Float t1        = *v1;
+    Simd4Float t2        = *v2;
+    Simd4Float t3        = *v3;
+    v0->simdInternal_[0] = t0.simdInternal_[0];
+    v0->simdInternal_[1] = t1.simdInternal_[0];
+    v0->simdInternal_[2] = t2.simdInternal_[0];
+    v0->simdInternal_[3] = t3.simdInternal_[0];
+    v1->simdInternal_[0] = t0.simdInternal_[1];
+    v1->simdInternal_[1] = t1.simdInternal_[1];
+    v1->simdInternal_[2] = t2.simdInternal_[1];
+    v1->simdInternal_[3] = t3.simdInternal_[1];
+    v2->simdInternal_[0] = t0.simdInternal_[2];
+    v2->simdInternal_[1] = t1.simdInternal_[2];
+    v2->simdInternal_[2] = t2.simdInternal_[2];
+    v2->simdInternal_[3] = t3.simdInternal_[2];
+    v3->simdInternal_[0] = t0.simdInternal_[3];
+    v3->simdInternal_[1] = t1.simdInternal_[3];
+    v3->simdInternal_[2] = t2.simdInternal_[3];
+    v3->simdInternal_[3] = t3.simdInternal_[3];
+}
+
+/*! \brief a==b for SIMD4 float
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a==b.
+ */
+static inline Simd4FBool gmx_simdcall operator==(Simd4Float a, Simd4Float b)
+{
+    Simd4FBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] == b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief a!=b for SIMD4 float
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a!=b.
+ */
+static inline Simd4FBool gmx_simdcall operator!=(Simd4Float a, Simd4Float b)
+{
+    Simd4FBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] != b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief a<b for SIMD4 float
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a<b.
+ */
+static inline Simd4FBool gmx_simdcall operator<(Simd4Float a, Simd4Float b)
+{
+    Simd4FBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] < b.simdInternal_[i]);
+    }
+    return res;
+}
+
+
+/*! \brief a<=b for SIMD4 float.
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a<=b.
+ */
+static inline Simd4FBool gmx_simdcall operator<=(Simd4Float a, Simd4Float b)
+{
+    Simd4FBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] <= b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Logical \a and on single precision SIMD4 booleans.
+ *
+ * \param a logical vars 1
+ * \param b logical vars 2
+ * \return For each element, the result boolean is true if a \& b are true.
+ *
+ * \note This is not necessarily a bitwise operation - the storage format
+ * of booleans is implementation-dependent.
+ */
+static inline Simd4FBool gmx_simdcall operator&&(Simd4FBool a, Simd4FBool b)
+{
+    Simd4FBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] && b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Logical \a or on single precision SIMD4 booleans.
+ *
+ * \param a logical vars 1
+ * \param b logical vars 2
+ * \return For each element, the result boolean is true if a or b is true.
+ *
+ * Note that this is not necessarily a bitwise operation - the storage format
+ * of booleans is implementation-dependent.
+ */
+static inline Simd4FBool gmx_simdcall operator||(Simd4FBool a, Simd4FBool b)
+{
+    Simd4FBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] || b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Returns non-zero if any of the boolean in SIMD4 a is True, otherwise 0.
+ *
+ * \param a Logical variable.
+ * \return true if any element in a is true, otherwise false.
+ *
+ * The actual return value for truth will depend on the architecture,
+ * so any non-zero value is considered truth.
+ */
+static inline bool gmx_simdcall anyTrue(Simd4FBool a)
+{
+    bool res = false;
+
+    for (std::size_t i = 0; i < a.simdInternal_.size(); i++)
+    {
+        res = res || a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Select from single precision SIMD4 variable where boolean is true.
+ *
+ * \param a Floating-point variable to select from
+ * \param mask Boolean selector
+ * \return  For each element, a is selected for true, 0 for false.
+ */
+static inline Simd4Float gmx_simdcall selectByMask(Simd4Float a, Simd4FBool mask)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = mask.simdInternal_[i] ? a.simdInternal_[i] : 0.0F;
+    }
+    return res;
+}
+
+/*! \brief Select from single precision SIMD4 variable where boolean is false.
+ *
+ * \param a Floating-point variable to select from
+ * \param mask Boolean selector
+ * \return  For each element, a is selected for false, 0 for true (sic).
+ */
+static inline Simd4Float gmx_simdcall selectByNotMask(Simd4Float a, Simd4FBool mask)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = mask.simdInternal_[i] ? 0.0F : a.simdInternal_[i];
+    }
+    return res;
+}
+
+
+/*! \brief Vector-blend SIMD4 selection.
+ *
+ * \param a First source
+ * \param b Second source
+ * \param sel Boolean selector
+ * \return For each element, select b if sel is true, a otherwise.
+ */
+static inline Simd4Float gmx_simdcall blend(Simd4Float a, Simd4Float b, Simd4FBool sel)
+{
+    Simd4Float res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = sel.simdInternal_[i] ? b.simdInternal_[i] : a.simdInternal_[i];
+    }
+    return res;
+}
+
+
+/*! \brief Return sum of all elements in SIMD4 float variable.
+ *
+ * \param a SIMD4 variable to reduce/sum.
+ * \return The sum of all elements in the argument variable.
+ *
+ */
+static inline float gmx_simdcall reduce(Simd4Float a)
+{
+    float sum = 0.0F;
+
+    for (std::size_t i = 0; i < a.simdInternal_.size(); i++)
+    {
+        sum += a.simdInternal_[i];
+    }
+    return sum;
+}
+
+/*! \} */
+
+/*! \} */
+/*! \endcond */
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_REFERENCE_SIMD4_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_reference/impl_reference_simd_double.h b/src/include/gromacs/simd/impl_reference/impl_reference_simd_double.h
new file mode 100644 (file)
index 0000000..a2377f8
--- /dev/null
@@ -0,0 +1,1644 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_REFERENCE_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPL_REFERENCE_SIMD_DOUBLE_H
+
+/*! \libinternal \file
+ *
+ * \brief Reference implementation, SIMD double precision.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \ingroup module_simd
+ */
+
+#include "config.h"
+
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+
+#include <algorithm>
+#include <array>
+
+#include "gromacs/math/utilities.h"
+#include "gromacs/utility/fatalerror.h"
+
+#include "impl_reference_definitions.h"
+#include "impl_reference_simd_float.h"
+
+namespace gmx
+{
+
+/*! \cond libapi */
+/*! \addtogroup module_simd */
+/*! \{ */
+
+/*! \name SIMD implementation data types
+ * \{
+ */
+
+/*! \libinternal \brief Double SIMD variable. Available if GMX_SIMD_HAVE_DOUBLE is 1.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+class SimdDouble
+{
+public:
+    SimdDouble() {}
+
+    //! \brief Construct from scalar
+    SimdDouble(double d) { simdInternal_.fill(d); }
+
+    /*! \brief Internal SIMD data. Implementation dependent, don't touch.
+     *
+     * This has to be public to enable usage in combination with static inline
+     * functions, but it should never, EVER, be accessed by any code outside
+     * the corresponding implementation directory since the type will depend
+     * on the architecture.
+     */
+    std::array<double, GMX_SIMD_DOUBLE_WIDTH> simdInternal_;
+};
+
+/*! \libinternal \brief Integer SIMD variable type to use for conversions to/from double.
+ *
+ * Available if GMX_SIMD_HAVE_DOUBLE is 1.
+ *
+ * \note The integer SIMD type will always be available, but on architectures
+ * that do not have any real integer SIMD support it might be defined as the
+ * floating-point type. This will work fine, since there are separate defines
+ * for whether the implementation can actually do any operations on integer
+ * SIMD types.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+class SimdDInt32
+{
+public:
+    SimdDInt32() {}
+
+    //! \brief Construct from scalar
+    SimdDInt32(std::int32_t i) { simdInternal_.fill(i); }
+
+    /*! \brief Internal SIMD data. Implementation dependent, don't touch.
+     *
+     * This has to be public to enable usage in combination with static inline
+     * functions, but it should never, EVER, be accessed by any code outside
+     * the corresponding implementation directory since the type will depend
+     * on the architecture.
+     */
+    std::array<std::int32_t, GMX_SIMD_DINT32_WIDTH> simdInternal_;
+};
+
+/*! \libinternal \brief Boolean type for double SIMD data.
+ *
+ *  Available if GMX_SIMD_HAVE_DOUBLE is 1.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+class SimdDBool
+{
+public:
+    SimdDBool() {}
+
+    //! \brief Construct from scalar bool
+    SimdDBool(bool b) { simdInternal_.fill(b); }
+
+    /*! \brief Internal SIMD data. Implementation dependent, don't touch.
+     *
+     * This has to be public to enable usage in combination with static inline
+     * functions, but it should never, EVER, be accessed by any code outside
+     * the corresponding implementation directory since the type will depend
+     * on the architecture.
+     */
+    std::array<bool, GMX_SIMD_DOUBLE_WIDTH> simdInternal_;
+};
+
+/*! \libinternal \brief Boolean type for integer datatypes corresponding to double SIMD.
+ *
+ * Available if GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+class SimdDIBool
+{
+public:
+    SimdDIBool() {}
+
+    //! \brief Construct from scalar
+    SimdDIBool(bool b) { simdInternal_.fill(b); }
+
+    /*! \brief Internal SIMD data. Implementation dependent, don't touch.
+     *
+     * This has to be public to enable usage in combination with static inline
+     * functions, but it should never, EVER, be accessed by any code outside
+     * the corresponding implementation directory since the type will depend
+     * on the architecture.
+     */
+    std::array<bool, GMX_SIMD_DINT32_WIDTH> simdInternal_;
+};
+
+/*! \}
+ *
+ * \name SIMD implementation load/store operations for double precision floating point
+ * \{
+ */
+
+/*! \brief Load \ref GMX_SIMD_DOUBLE_WIDTH numbers from aligned memory.
+ *
+ * \param m Pointer to memory aligned to the SIMD width.
+ * \return SIMD variable with data loaded.
+ */
+static inline SimdDouble gmx_simdcall simdLoad(const double* m, SimdDoubleTag = {})
+{
+    SimdDouble a;
+
+    assert(std::size_t(m) % (a.simdInternal_.size() * sizeof(double)) == 0);
+
+    std::copy(m, m + a.simdInternal_.size(), a.simdInternal_.begin());
+    return a;
+}
+
+/*! \brief Store the contents of SIMD double variable to aligned memory m.
+ *
+ * \param[out] m Pointer to memory, aligned to SIMD width.
+ * \param a SIMD variable to store
+ */
+static inline void gmx_simdcall store(double* m, SimdDouble a)
+{
+    assert(std::size_t(m) % (a.simdInternal_.size() * sizeof(double)) == 0);
+
+    std::copy(a.simdInternal_.begin(), a.simdInternal_.end(), m);
+}
+
+/*! \brief Load SIMD double from unaligned memory.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOADU is 1.
+ *
+ * \param m Pointer to memory, no alignment requirement.
+ * \return SIMD variable with data loaded.
+ */
+static inline SimdDouble gmx_simdcall simdLoadU(const double* m, SimdDoubleTag = {})
+{
+    SimdDouble a;
+    std::copy(m, m + a.simdInternal_.size(), a.simdInternal_.begin());
+    return a;
+}
+
+/*! \brief Store SIMD double to unaligned memory.
+ *
+ * Available if \ref GMX_SIMD_HAVE_STOREU is 1.
+ *
+ * \param[out] m Pointer to memory, no alignment requirement.
+ * \param a SIMD variable to store.
+ */
+static inline void gmx_simdcall storeU(double* m, SimdDouble a)
+{
+    std::copy(a.simdInternal_.begin(), a.simdInternal_.end(), m);
+}
+
+/*! \brief Set all SIMD double variable elements to 0.0.
+ *
+ * You should typically just call \ref gmx::setZero(), which uses proxy objects
+ * internally to handle all types rather than adding the suffix used here.
+ *
+ * \return SIMD 0.0
+ */
+static inline SimdDouble gmx_simdcall setZeroD()
+{
+    return SimdDouble(0.0);
+}
+
+/*! \}
+ *
+ * \name SIMD implementation load/store operations for integers (corresponding to double)
+ * \{
+ */
+
+/*! \brief Load aligned SIMD integer data, width corresponds to \ref gmx::SimdDouble.
+ *
+ * You should typically just call \ref gmx::load(), which uses proxy objects
+ * internally to handle all types rather than adding the suffix used here.
+ *
+ * \param m Pointer to memory, aligned to (double) integer SIMD width.
+ * \return SIMD integer variable.
+ */
+static inline SimdDInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdDInt32Tag)
+{
+    SimdDInt32 a;
+
+    assert(std::size_t(m) % (a.simdInternal_.size() * sizeof(std::int32_t)) == 0);
+
+    std::copy(m, m + a.simdInternal_.size(), a.simdInternal_.begin());
+    return a;
+};
+
+/*! \brief Store aligned SIMD integer data, width corresponds to \ref gmx::SimdDouble.
+ *
+ * \param m Memory aligned to (double) integer SIMD width.
+ * \param a SIMD (double) integer variable to store.
+ */
+static inline void gmx_simdcall store(std::int32_t* m, SimdDInt32 a)
+{
+    assert(std::size_t(m) % (a.simdInternal_.size() * sizeof(std::int32_t)) == 0);
+
+    std::copy(a.simdInternal_.begin(), a.simdInternal_.end(), m);
+};
+
+/*! \brief Load unaligned integer SIMD data, width corresponds to \ref gmx::SimdDouble.
+ *
+ * You should typically just call \ref gmx::loadU(), which uses proxy objects
+ * internally to handle all types rather than adding the suffix used here.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOADU is 1.
+ *
+ * \param m Pointer to memory, no alignment requirements.
+ * \return SIMD integer variable.
+ */
+static inline SimdDInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdDInt32Tag)
+{
+    SimdDInt32 a;
+    std::copy(m, m + a.simdInternal_.size(), a.simdInternal_.begin());
+    return a;
+}
+
+/*! \brief Store unaligned SIMD integer data, width corresponds to \ref gmx::SimdDouble.
+ *
+ * Available if \ref GMX_SIMD_HAVE_STOREU is 1.
+ *
+ * \param m Memory pointer, no alignment requirements.
+ * \param a SIMD (double) integer variable to store.
+ */
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdDInt32 a)
+{
+    std::copy(a.simdInternal_.begin(), a.simdInternal_.end(), m);
+}
+
+/*! \brief Set all SIMD (double) integer variable elements to 0.
+ *
+ * You should typically just call \ref gmx::setZero(), which uses proxy objects
+ * internally to handle all types rather than adding the suffix used here.
+ *
+ * \return SIMD 0
+ */
+static inline SimdDInt32 gmx_simdcall setZeroDI()
+{
+    return SimdDInt32(0);
+}
+
+/*! \brief Extract element with index i from \ref gmx::SimdDInt32.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_EXTRACT is 1.
+ *
+ * \tparam index Compile-time constant, position to extract (first position is 0)
+ * \param  a     SIMD variable from which to extract value.
+ * \return Single integer from position index in SIMD variable.
+ */
+template<int index>
+static inline std::int32_t gmx_simdcall extract(SimdDInt32 a)
+{
+    return a.simdInternal_[index];
+}
+
+/*! \}
+ *
+ * \name SIMD implementation double precision floating-point bitwise logical operations
+ * \{
+ */
+
+/*! \brief Bitwise and for two SIMD double variables.
+ *
+ * Supported if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return data1 & data2
+ */
+static inline SimdDouble gmx_simdcall operator&(SimdDouble a, SimdDouble b)
+{
+    SimdDouble res;
+
+    union
+    {
+        double       r;
+        std::int64_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = conv1.i & conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+/*! \brief Bitwise andnot for SIMD double.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return (~data1) & data2
+ */
+static inline SimdDouble gmx_simdcall andNot(SimdDouble a, SimdDouble b)
+{
+    SimdDouble res;
+
+    union
+    {
+        double       r;
+        std::int64_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = ~conv1.i & conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+/*! \brief Bitwise or for SIMD double.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return data1 | data2
+ */
+static inline SimdDouble gmx_simdcall operator|(SimdDouble a, SimdDouble b)
+{
+    SimdDouble res;
+
+    union
+    {
+        double       r;
+        std::int64_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = conv1.i | conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+/*! \brief Bitwise xor for SIMD double.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return data1 ^ data2
+ */
+static inline SimdDouble gmx_simdcall operator^(SimdDouble a, SimdDouble b)
+{
+    SimdDouble res;
+
+    union
+    {
+        double       r;
+        std::int64_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = conv1.i ^ conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+/*! \}
+ *
+ * \name SIMD implementation double precision floating-point arithmetics
+ * \{
+ */
+
+/*! \brief Add two double SIMD variables.
+ *
+ * \param a term1
+ * \param b term2
+ * \return a+b
+ */
+static inline SimdDouble gmx_simdcall operator+(SimdDouble a, SimdDouble b)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] + b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Subtract two double SIMD variables.
+ *
+ * \param a term1
+ * \param b term2
+ * \return a-b
+ */
+static inline SimdDouble gmx_simdcall operator-(SimdDouble a, SimdDouble b)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] - b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief SIMD double precision negate.
+ *
+ * \param a SIMD double precision value
+ * \return -a
+ */
+static inline SimdDouble gmx_simdcall operator-(SimdDouble a)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = -a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Multiply two double SIMD variables.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \return a*b.
+ */
+static inline SimdDouble gmx_simdcall operator*(SimdDouble a, SimdDouble b)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] * b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief SIMD double Fused-multiply-add. Result is a*b+c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return a*b+c
+ */
+static inline SimdDouble gmx_simdcall fma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return a * b + c;
+}
+
+/*! \brief SIMD double Fused-multiply-subtract. Result is a*b-c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return a*b-c
+ */
+static inline SimdDouble gmx_simdcall fms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return a * b - c;
+}
+
+/*! \brief SIMD double Fused-negated-multiply-add. Result is -a*b+c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return -a*b+c
+ */
+static inline SimdDouble gmx_simdcall fnma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return c - a * b;
+}
+
+/*! \brief SIMD double Fused-negated-multiply-subtract. Result is -a*b-c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return -a*b-c
+ */
+static inline SimdDouble gmx_simdcall fnms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return -a * b - c;
+}
+
+/*! \brief double SIMD 1.0/sqrt(x) lookup.
+ *
+ * This is a low-level instruction that should only be called from routines
+ * implementing the inverse square root in simd_math.h.
+ *
+ * \param x Argument, x>0
+ * \return Approximation of 1/sqrt(x), accuracy is \ref GMX_SIMD_RSQRT_BITS.
+ */
+static inline SimdDouble gmx_simdcall rsqrt(SimdDouble x)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        // sic - we only use single precision for the lookup
+        res.simdInternal_[i] = 1.0F / std::sqrt(static_cast<float>(x.simdInternal_[i]));
+    }
+    return res;
+};
+
+/*! \brief SIMD double 1.0/x lookup.
+ *
+ * This is a low-level instruction that should only be called from routines
+ * implementing the reciprocal in simd_math.h.
+ *
+ * \param x Argument, x!=0
+ * \return Approximation of 1/x, accuracy is \ref GMX_SIMD_RCP_BITS.
+ */
+static inline SimdDouble gmx_simdcall rcp(SimdDouble x)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        // sic - we only use single precision for the lookup
+        res.simdInternal_[i] = 1.0F / static_cast<float>(x.simdInternal_[i]);
+    }
+    return res;
+};
+
+/*! \brief Add two double SIMD variables, masked version.
+ *
+ * \param a term1
+ * \param b term2
+ * \param m mask
+ * \return a+b where mask is true, 0.0 otherwise.
+ */
+static inline SimdDouble gmx_simdcall maskAdd(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] + (m.simdInternal_[i] ? b.simdInternal_[i] : 0.0);
+    }
+    return res;
+}
+
+/*! \brief Multiply two double SIMD variables, masked version.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param m mask
+ * \return a*b where mask is true, 0.0 otherwise.
+ */
+static inline SimdDouble gmx_simdcall maskzMul(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = m.simdInternal_[i] ? (a.simdInternal_[i] * b.simdInternal_[i]) : 0.0;
+    }
+    return res;
+}
+
+/*! \brief SIMD double fused multiply-add, masked version.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \param m mask
+ * \return a*b+c where mask is true, 0.0 otherwise.
+ */
+static inline SimdDouble gmx_simdcall maskzFma(SimdDouble a, SimdDouble b, SimdDouble c, SimdDBool m)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] =
+                m.simdInternal_[i] ? (a.simdInternal_[i] * b.simdInternal_[i] + c.simdInternal_[i]) : 0.0;
+    }
+    return res;
+}
+
+/*! \brief SIMD double 1.0/sqrt(x) lookup, masked version.
+ *
+ * This is a low-level instruction that should only be called from routines
+ * implementing the inverse square root in simd_math.h.
+ *
+ * \param x Argument, x>0 for entries where mask is true.
+ * \param m Mask
+ * \return Approximation of 1/sqrt(x), accuracy is \ref GMX_SIMD_RSQRT_BITS.
+ *         The result for masked-out entries will be 0.0.
+ */
+static inline SimdDouble gmx_simdcall maskzRsqrt(SimdDouble x, SimdDBool m)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        // sic - we only use single precision for the lookup
+        res.simdInternal_[i] = (m.simdInternal_[i] != 0)
+                                       ? 1.0F / std::sqrt(static_cast<float>(x.simdInternal_[i]))
+                                       : 0.0;
+    }
+    return res;
+}
+
+/*! \brief SIMD double 1.0/x lookup, masked version.
+ *
+ * This is a low-level instruction that should only be called from routines
+ * implementing the reciprocal in simd_math.h.
+ *
+ * \param x Argument, x>0 for entries where mask is true.
+ * \param m Mask
+ * \return Approximation of 1/x, accuracy is \ref GMX_SIMD_RCP_BITS.
+ *         The result for masked-out entries will be 0.0.
+ */
+static inline SimdDouble gmx_simdcall maskzRcp(SimdDouble x, SimdDBool m)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] =
+                (m.simdInternal_[i] != 0) ? 1.0F / static_cast<float>(x.simdInternal_[i]) : 0.0;
+    }
+    return res;
+}
+
+/*! \brief SIMD double floating-point fabs().
+ *
+ * \param a any floating point values
+ * \return fabs(a) for each element.
+ */
+static inline SimdDouble gmx_simdcall abs(SimdDouble a)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::abs(a.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Set each SIMD double element to the largest from two variables.
+ *
+ * \param a Any floating-point value
+ * \param b Any floating-point value
+ * \return max(a,b) for each element.
+ */
+static inline SimdDouble gmx_simdcall max(SimdDouble a, SimdDouble b)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::max(a.simdInternal_[i], b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Set each SIMD double element to the smallest from two variables.
+ *
+ * \param a Any floating-point value
+ * \param b Any floating-point value
+ * \return min(a,b) for each element.
+ */
+static inline SimdDouble gmx_simdcall min(SimdDouble a, SimdDouble b)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::min(a.simdInternal_[i], b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief SIMD double round to nearest integer value (in floating-point format).
+ *
+ * \param a Any floating-point value
+ * \return The nearest integer, represented in floating-point format.
+ *
+ * \note Round mode is implementation defined. The only guarantee is that it
+ * is consistent between rounding functions (round, cvtR2I).
+ */
+static inline SimdDouble gmx_simdcall round(SimdDouble a)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::round(a.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Truncate SIMD double, i.e. round towards zero - common hardware instruction.
+ *
+ * \param a Any floating-point value
+ * \return Integer rounded towards zero, represented in floating-point format.
+ *
+ * \note This is truncation towards zero, not floor(). The reason for this
+ * is that truncation is virtually always present as a dedicated hardware
+ * instruction, but floor() frequently isn't.
+ */
+static inline SimdDouble gmx_simdcall trunc(SimdDouble a)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::trunc(a.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Extract (integer) exponent and fraction from double precision SIMD.
+ *
+ * \tparam      opt       By default this function behaves like the standard
+ *                        library such that frexp(+-0,exp) returns +-0 and
+ *                        stores 0 in the exponent when value is 0. If you
+ *                        know the argument is always nonzero, you can set
+ *                        the template parameter to MathOptimization::Unsafe
+ *                        to make it slightly faster.
+ *
+ * \param       value     Floating-point value to extract from
+ * \param[out]  exponent  Returned exponent of value, integer SIMD format.
+ * \return      Fraction of value, floating-point SIMD format.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall frexp(SimdDouble value, SimdDInt32* exponent)
+{
+    SimdDouble fraction;
+
+    for (std::size_t i = 0; i < fraction.simdInternal_.size(); i++)
+    {
+        fraction.simdInternal_[i] = std::frexp(value.simdInternal_[i], &exponent->simdInternal_[i]);
+    }
+    return fraction;
+}
+
+/*! \brief Multiply a SIMD double value by the number 2 raised to an exp power.
+ *
+ * \tparam opt By default, this routine will return zero for input arguments
+ *             that are so small they cannot be reproduced in the current
+ *             precision. If the unsafe math optimization template parameter
+ *             setting is used, these tests are skipped, and the result will
+ *             be undefined (possible even NaN). This might happen below -127
+ *             in single precision or -1023 in double, although some
+ *             might use denormal support to extend the range.
+ *
+ * \param value Floating-point number to multiply with new exponent
+ * \param exponent Integer that will not overflow as 2^exponent.
+ * \return value*2^exponent
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall ldexp(SimdDouble value, SimdDInt32 exponent)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        // std::ldexp already takes care of clamping arguments, so we do not
+        // need to do anything in the reference implementation
+        res.simdInternal_[i] = std::ldexp(value.simdInternal_[i], exponent.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Return sum of all elements in SIMD double variable.
+ *
+ * \param a SIMD variable to reduce/sum.
+ * \return The sum of all elements in the argument variable.
+ *
+ */
+static inline double gmx_simdcall reduce(SimdDouble a)
+{
+    double sum = 0.0;
+
+    for (std::size_t i = 0; i < a.simdInternal_.size(); i++)
+    {
+        sum += a.simdInternal_[i];
+    }
+    return sum;
+}
+
+/*! \}
+ *
+ * \name SIMD implementation double precision floating-point comparison, boolean, selection.
+ * \{
+ */
+
+/*! \brief SIMD a==b for double SIMD.
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a==b.
+ *
+ * Beware that exact floating-point comparisons are difficult.
+ */
+static inline SimdDBool gmx_simdcall operator==(SimdDouble a, SimdDouble b)
+{
+    SimdDBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] == b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief SIMD a!=b for double SIMD.
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a!=b.
+ *
+ * Beware that exact floating-point comparisons are difficult.
+ */
+static inline SimdDBool gmx_simdcall operator!=(SimdDouble a, SimdDouble b)
+{
+    SimdDBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] != b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief SIMD a<b for double SIMD.
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a<b.
+ */
+static inline SimdDBool gmx_simdcall operator<(SimdDouble a, SimdDouble b)
+{
+    SimdDBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] < b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief SIMD a<=b for double SIMD.
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a<=b.
+ */
+static inline SimdDBool gmx_simdcall operator<=(SimdDouble a, SimdDouble b)
+{
+    SimdDBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] <= b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Return true if any bits are set in the single precision SIMD.
+ *
+ * This function is used to handle bitmasks, mainly for exclusions in the
+ * inner kernels. Note that it will return true even for -0.0 (sign bit set),
+ * so it is not identical to not-equal.
+ *
+ * \param a value
+ * \return Each element of the boolean will be true if any bit in a is nonzero.
+ */
+static inline SimdDBool gmx_simdcall testBits(SimdDouble a)
+{
+    SimdDBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        union
+        {
+            std::uint64_t i;
+            double        d;
+        } conv;
+
+        conv.d               = a.simdInternal_[i];
+        res.simdInternal_[i] = (conv.i != 0);
+    }
+    return res;
+}
+
+/*! \brief Logical \a and on double precision SIMD booleans.
+ *
+ * \param a logical vars 1
+ * \param b logical vars 2
+ * \return For each element, the result boolean is true if a \& b are true.
+ *
+ * \note This is not necessarily a bitwise operation - the storage format
+ * of booleans is implementation-dependent.
+ */
+static inline SimdDBool gmx_simdcall operator&&(SimdDBool a, SimdDBool b)
+{
+    SimdDBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] && b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Logical \a or on double precision SIMD booleans.
+ *
+ * \param a logical vars 1
+ * \param b logical vars 2
+ * \return For each element, the result boolean is true if a or b is true.
+ *
+ * Note that this is not necessarily a bitwise operation - the storage format
+ * of booleans is implementation-dependent.
+ *
+ \ */
+static inline SimdDBool gmx_simdcall operator||(SimdDBool a, SimdDBool b)
+{
+    SimdDBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] || b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Returns non-zero if any of the boolean in SIMD a is True, otherwise 0.
+ *
+ * \param a Logical variable.
+ * \return true if any element in a is true, otherwise false.
+ *
+ * The actual return value for truth will depend on the architecture,
+ * so any non-zero value is considered truth.
+ */
+static inline bool gmx_simdcall anyTrue(SimdDBool a)
+{
+    bool res = false;
+
+    for (std::size_t i = 0; i < a.simdInternal_.size(); i++)
+    {
+        res = res || a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Select from double precision SIMD variable where boolean is true.
+ *
+ * \param a Floating-point variable to select from
+ * \param mask Boolean selector
+ * \return  For each element, a is selected for true, 0 for false.
+ */
+static inline SimdDouble gmx_simdcall selectByMask(SimdDouble a, SimdDBool mask)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = mask.simdInternal_[i] ? a.simdInternal_[i] : 0.0;
+    }
+    return res;
+}
+
+/*! \brief Select from double precision SIMD variable where boolean is false.
+ *
+ * \param a Floating-point variable to select from
+ * \param mask Boolean selector
+ * \return  For each element, a is selected for false, 0 for true (sic).
+ */
+static inline SimdDouble gmx_simdcall selectByNotMask(SimdDouble a, SimdDBool mask)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = mask.simdInternal_[i] ? 0.0 : a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Vector-blend SIMD double selection.
+ *
+ * \param a First source
+ * \param b Second source
+ * \param sel Boolean selector
+ * \return For each element, select b if sel is true, a otherwise.
+ */
+static inline SimdDouble gmx_simdcall blend(SimdDouble a, SimdDouble b, SimdDBool sel)
+{
+    SimdDouble res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = sel.simdInternal_[i] ? b.simdInternal_[i] : a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \}
+ *
+ * \name SIMD implementation integer (corresponding to double) bitwise logical operations
+ * \{
+ */
+
+/*! \brief Integer SIMD bitwise and.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_LOGICAL is 1.
+ *
+ * \note You can \a not use this operation directly to select based on a boolean
+ * SIMD variable, since booleans are separate from integer SIMD. If that
+ * is what you need, have a look at \ref gmx::selectByMask instead.
+ *
+ * \param a first integer SIMD
+ * \param b second integer SIMD
+ * \return a \& b (bitwise and)
+ */
+static inline SimdDInt32 gmx_simdcall operator&(SimdDInt32 a, SimdDInt32 b)
+{
+    SimdDInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] & b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Integer SIMD bitwise not/complement.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_LOGICAL is 1.
+ *
+ * \note You can \a not use this operation directly to select based on a boolean
+ * SIMD variable, since booleans are separate from integer SIMD. If that
+ * is what you need, have a look at \ref gmx::selectByMask instead.
+ *
+ * \param a integer SIMD
+ * \param b integer SIMD
+ * \return (~a) & b
+ */
+static inline SimdDInt32 gmx_simdcall andNot(SimdDInt32 a, SimdDInt32 b)
+{
+    SimdDInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = ~a.simdInternal_[i] & b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Integer SIMD bitwise or.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_LOGICAL is 1.
+ *
+ * \param a first integer SIMD
+ * \param b second integer SIMD
+ * \return a \| b (bitwise or)
+ */
+static inline SimdDInt32 gmx_simdcall operator|(SimdDInt32 a, SimdDInt32 b)
+{
+    SimdDInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] | b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Integer SIMD bitwise xor.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_LOGICAL is 1.
+ *
+ * \param a first integer SIMD
+ * \param b second integer SIMD
+ * \return a ^ b (bitwise xor)
+ */
+static inline SimdDInt32 gmx_simdcall operator^(SimdDInt32 a, SimdDInt32 b)
+{
+    SimdDInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] ^ b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \}
+ *
+ * \name SIMD implementation integer (corresponding to double) arithmetics
+ * \{
+ */
+
+/*! \brief Add SIMD integers.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * \param a term1
+ * \param b term2
+ * \return a+b
+ */
+static inline SimdDInt32 gmx_simdcall operator+(SimdDInt32 a, SimdDInt32 b)
+{
+    SimdDInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] + b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Subtract SIMD integers.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * \param a term1
+ * \param b term2
+ * \return a-b
+ */
+static inline SimdDInt32 gmx_simdcall operator-(SimdDInt32 a, SimdDInt32 b)
+{
+    SimdDInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] - b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Multiply SIMD integers.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \return a*b.
+ *
+ * \note Only the low 32 bits are retained, so this can overflow.
+ */
+static inline SimdDInt32 gmx_simdcall operator*(SimdDInt32 a, SimdDInt32 b)
+{
+    SimdDInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] * b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \}
+ *
+ * \name SIMD implementation integer (corresponding to double) comparisons, boolean selection
+ * \{
+ */
+
+/*! \brief Equality comparison of two integers corresponding to double values.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD integer1
+ * \param b SIMD integer2
+ * \return SIMD integer boolean with true for elements where a==b
+ */
+static inline SimdDIBool gmx_simdcall operator==(SimdDInt32 a, SimdDInt32 b)
+{
+    SimdDIBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] == b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Less-than comparison of two SIMD integers corresponding to double values.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD integer1
+ * \param b SIMD integer2
+ * \return SIMD integer boolean with true for elements where a<b
+ */
+static inline SimdDIBool gmx_simdcall operator<(SimdDInt32 a, SimdDInt32 b)
+{
+    SimdDIBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] < b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Check if any bit is set in each element
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD integer
+ * \return SIMD integer boolean with true for elements where any bit is set
+ */
+static inline SimdDIBool gmx_simdcall testBits(SimdDInt32 a)
+{
+    SimdDIBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] != 0);
+    }
+    return res;
+}
+
+/*! \brief Logical AND on SimdDIBool.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD boolean 1
+ * \param b SIMD boolean 2
+ * \return True for elements where both a and b are true.
+ */
+static inline SimdDIBool gmx_simdcall operator&&(SimdDIBool a, SimdDIBool b)
+{
+    SimdDIBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] && b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Logical OR on SimdDIBool.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD boolean 1
+ * \param b SIMD boolean 2
+ * \return True for elements where both a and b are true.
+ */
+static inline SimdDIBool gmx_simdcall operator||(SimdDIBool a, SimdDIBool b)
+{
+    SimdDIBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] || b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Returns true if any of the boolean in x is True, otherwise 0.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * The actual return value for "any true" will depend on the architecture.
+ * Any non-zero value should be considered truth.
+ *
+ * \param a SIMD boolean
+ * \return True if any of the elements in a is true, otherwise 0.
+ */
+static inline bool gmx_simdcall anyTrue(SimdDIBool a)
+{
+    bool res = false;
+
+    for (std::size_t i = 0; i < a.simdInternal_.size(); i++)
+    {
+        res = res || a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Select from \ref gmx::SimdDInt32 variable where boolean is true.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD integer to select from
+ * \param mask Boolean selector
+ * \return Elements from a where sel is true, 0 otherwise.
+ */
+static inline SimdDInt32 gmx_simdcall selectByMask(SimdDInt32 a, SimdDIBool mask)
+{
+    SimdDInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = mask.simdInternal_[i] ? a.simdInternal_[i] : 0;
+    }
+    return res;
+}
+
+/*! \brief Select from \ref gmx::SimdDInt32 variable where boolean is false.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD integer to select from
+ * \param mask Boolean selector
+ * \return Elements from a where sel is false, 0 otherwise (sic).
+ */
+static inline SimdDInt32 gmx_simdcall selectByNotMask(SimdDInt32 a, SimdDIBool mask)
+{
+    SimdDInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = mask.simdInternal_[i] ? 0 : a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Vector-blend SIMD integer selection.
+ *
+ * Available if \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS is 1.
+ *
+ * \param a First source
+ * \param b Second source
+ * \param sel Boolean selector
+ * \return For each element, select b if sel is true, a otherwise.
+ */
+static inline SimdDInt32 gmx_simdcall blend(SimdDInt32 a, SimdDInt32 b, SimdDIBool sel)
+{
+    SimdDInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = sel.simdInternal_[i] ? b.simdInternal_[i] : a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \}
+ *
+ * \name SIMD implementation conversion operations
+ * \{
+ */
+
+/*! \brief Round double precision floating point to integer.
+ *
+ * \param a SIMD floating-point
+ * \return SIMD integer, rounded to nearest integer.
+ *
+ * \note Round mode is implementation defined. The only guarantee is that it
+ * is consistent between rounding functions (round, cvtR2I).
+ */
+static inline SimdDInt32 gmx_simdcall cvtR2I(SimdDouble a)
+{
+    SimdDInt32 b;
+
+    for (std::size_t i = 0; i < b.simdInternal_.size(); i++)
+    {
+        b.simdInternal_[i] = std::round(a.simdInternal_[i]);
+    }
+    return b;
+};
+
+/*! \brief Truncate double precision floating point to integer.
+ *
+ * \param a SIMD floating-point
+ * \return SIMD integer, truncated to nearest integer.
+ */
+static inline SimdDInt32 gmx_simdcall cvttR2I(SimdDouble a)
+{
+    SimdDInt32 b;
+
+    for (std::size_t i = 0; i < b.simdInternal_.size(); i++)
+    {
+        b.simdInternal_[i] = std::trunc(a.simdInternal_[i]);
+    }
+    return b;
+};
+
+/*! \brief Convert integer to double precision floating point.
+ *
+ * \param a SIMD integer
+ * \return SIMD floating-point
+ */
+static inline SimdDouble gmx_simdcall cvtI2R(SimdDInt32 a)
+{
+    SimdDouble b;
+
+    for (std::size_t i = 0; i < b.simdInternal_.size(); i++)
+    {
+        b.simdInternal_[i] = a.simdInternal_[i];
+    }
+    return b;
+};
+
+/*! \brief Convert from double precision boolean to corresponding integer boolean
+ *
+ * \param a SIMD floating-point boolean
+ * \return SIMD integer boolean
+ */
+static inline SimdDIBool gmx_simdcall cvtB2IB(SimdDBool a)
+{
+    SimdDIBool b;
+
+    for (std::size_t i = 0; i < b.simdInternal_.size(); i++)
+    {
+        b.simdInternal_[i] = a.simdInternal_[i];
+    }
+    return b;
+};
+
+/*! \brief Convert from integer boolean to corresponding double precision boolean
+ *
+ * \param a SIMD integer boolean
+ * \return SIMD floating-point boolean
+ */
+static inline SimdDBool gmx_simdcall cvtIB2B(SimdDIBool a)
+{
+    SimdDBool b;
+
+    for (std::size_t i = 0; i < b.simdInternal_.size(); i++)
+    {
+        b.simdInternal_[i] = a.simdInternal_[i];
+    }
+    return b;
+};
+
+/*! \brief Convert SIMD float to double.
+ *
+ * This version is available if \ref GMX_SIMD_FLOAT_WIDTH is identical to
+ * \ref GMX_SIMD_DOUBLE_WIDTH.
+ *
+ * Float/double conversions are complex since the SIMD width could either
+ * be different (e.g. on x86) or identical (e.g. IBM QPX). This means you will
+ * need to check for the width in the code, and have different code paths.
+ *
+ * \param f Single-precision SIMD variable
+ * \return Double-precision SIMD variable of the same width
+ */
+static inline SimdDouble gmx_simdcall cvtF2D(SimdFloat gmx_unused f)
+{
+#if (GMX_SIMD_FLOAT_WIDTH == GMX_SIMD_DOUBLE_WIDTH)
+    SimdDouble d;
+    for (std::size_t i = 0; i < d.simdInternal_.size(); i++)
+    {
+        d.simdInternal_[i] = f.simdInternal_[i];
+    }
+    return d;
+#else
+    gmx_fatal(FARGS, "cvtF2D() requires GMX_SIMD_FLOAT_WIDTH==GMX_SIMD_DOUBLE_WIDTH");
+#endif
+}
+
+/*! \brief Convert SIMD double to float.
+ *
+ * This version is available if \ref GMX_SIMD_FLOAT_WIDTH is identical to
+ * \ref GMX_SIMD_DOUBLE_WIDTH.
+ *
+ * Float/double conversions are complex since the SIMD width could either
+ * be different (e.g. on x86) or identical (e.g. IBM QPX). This means you will
+ * need to check for the width in the code, and have different code paths.
+ *
+ * \param d Double-precision SIMD variable
+ * \return Single-precision SIMD variable of the same width
+ */
+static inline SimdFloat gmx_simdcall cvtD2F(SimdDouble gmx_unused d)
+{
+#if (GMX_SIMD_FLOAT_WIDTH == GMX_SIMD_DOUBLE_WIDTH)
+    SimdFloat f;
+    for (std::size_t i = 0; i < f.simdInternal_.size(); i++)
+    {
+        f.simdInternal_[i] = d.simdInternal_[i];
+    }
+    return f;
+#else
+    gmx_fatal(FARGS, "cvtD2F() requires GMX_SIMD_FLOAT_WIDTH==GMX_SIMD_DOUBLE_WIDTH");
+#endif
+}
+
+/*! \brief Convert SIMD float to double.
+ *
+ * This version is available if \ref GMX_SIMD_FLOAT_WIDTH is twice as large
+ * as \ref GMX_SIMD_DOUBLE_WIDTH.
+ *
+ * Float/double conversions are complex since the SIMD width could either
+ * be different (e.g. on x86) or identical (e.g. IBM QPX). This means you will
+ * need to check for the width in the code, and have different code paths.
+ *
+ * \param f Single-precision SIMD variable
+ * \param[out] d0 Double-precision SIMD variable, first half of values from f.
+ * \param[out] d1 Double-precision SIMD variable, second half of values from f.
+ */
+static inline void gmx_simdcall cvtF2DD(SimdFloat gmx_unused f,
+                                        SimdDouble gmx_unused* d0,
+                                        SimdDouble gmx_unused* d1)
+{
+#if (GMX_SIMD_FLOAT_WIDTH == 2 * GMX_SIMD_DOUBLE_WIDTH)
+    for (std::size_t i = 0; i < d0->simdInternal_.size(); i++)
+    {
+        d0->simdInternal_[i] = f.simdInternal_[i];
+        d1->simdInternal_[i] = f.simdInternal_[f.simdInternal_.size() / 2 + i];
+    }
+#else
+    gmx_fatal(FARGS, "simdCvtF2DD() requires GMX_SIMD_FLOAT_WIDTH==2*GMX_SIMD_DOUBLE_WIDTH");
+#endif
+}
+
+/*! \brief Convert SIMD double to float.
+ *
+ * This version is available if \ref GMX_SIMD_FLOAT_WIDTH is twice as large
+ * as \ref GMX_SIMD_DOUBLE_WIDTH.
+ *
+ * Float/double conversions are complex since the SIMD width could either
+ * be different (e.g. on x86) or identical (e.g. IBM QPX). This means you will
+ * need to check for the width in the code, and have different code paths.
+ *
+ * \param d0 Double-precision SIMD variable, first half of values to put in f.
+ * \param d1 Double-precision SIMD variable, second half of values to put in f.
+ * \return Single-precision SIMD variable with all values.
+ */
+static inline SimdFloat gmx_simdcall cvtDD2F(SimdDouble gmx_unused d0, SimdDouble gmx_unused d1)
+{
+#if (GMX_SIMD_FLOAT_WIDTH == 2 * GMX_SIMD_DOUBLE_WIDTH)
+    SimdFloat f;
+    for (std::size_t i = 0; i < d0.simdInternal_.size(); i++)
+    {
+        f.simdInternal_[i]                              = d0.simdInternal_[i];
+        f.simdInternal_[f.simdInternal_.size() / 2 + i] = d1.simdInternal_[i];
+    }
+    return f;
+#else
+    gmx_fatal(FARGS, "simdCvtDD2F() requires GMX_SIMD_FLOAT_WIDTH==2*GMX_SIMD_DOUBLE_WIDTH");
+#endif
+}
+
+/*! \} */
+
+/*! \} */
+/*! \endcond */
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_REFERENCE_SIMD_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_reference/impl_reference_simd_float.h b/src/include/gromacs/simd/impl_reference/impl_reference_simd_float.h
new file mode 100644 (file)
index 0000000..4e05599
--- /dev/null
@@ -0,0 +1,1532 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_REFERENCE_SIMD_FLOAT_H
+#define GMX_SIMD_IMPL_REFERENCE_SIMD_FLOAT_H
+
+/*! \libinternal \file
+ *
+ * \brief Reference implementation, SIMD single precision.
+
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \ingroup module_simd
+ */
+
+#include "config.h"
+
+#include <cassert>
+#include <cmath>
+#include <cstddef>
+#include <cstdint>
+
+#include <algorithm>
+#include <array>
+
+#include "gromacs/math/utilities.h"
+
+#include "impl_reference_definitions.h"
+
+namespace gmx
+{
+
+/*! \cond libapi */
+/*! \addtogroup module_simd */
+/*! \{ */
+
+/*! \name SIMD implementation data types and built-in conversions between types
+ * \{
+ */
+
+/*! \libinternal \brief Float SIMD variable. Available if GMX_SIMD_HAVE_FLOAT is 1.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+class SimdFloat
+{
+public:
+    SimdFloat() {}
+
+    //! \brief Construct from scalar
+    SimdFloat(float f) { simdInternal_.fill(f); }
+
+    /*! \brief Internal SIMD data. Implementation dependent, don't touch.
+     *
+     * This has to be public to enable usage in combination with static inline
+     * functions, but it should never, EVER, be accessed by any code outside
+     * the corresponding implementation directory since the type will depend
+     * on the architecture.
+     */
+    std::array<float, GMX_SIMD_FLOAT_WIDTH> simdInternal_;
+};
+
+/*! \libinternal \brief Integer SIMD variable type to use for conversions to/from float.
+ *
+ * This is also the widest integer SIMD type. Available if GMX_SIMD_HAVE_FLOAT is 1.
+ *
+ * \note The integer SIMD type will always be available, but on architectures
+ * that do not have any real integer SIMD support it might be defined as the
+ * floating-point type. This will work fine, since there are separate defines
+ * for whether the implementation can actually do any operations on integer
+ * SIMD types.
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+class SimdFInt32
+{
+public:
+    SimdFInt32() {}
+
+    //! \brief Construct from scalar
+    SimdFInt32(std::int32_t i) { simdInternal_.fill(i); }
+
+    /*! \brief Internal SIMD data. Implementation dependent, don't touch.
+     *
+     * This has to be public to enable usage in combination with static inline
+     * functions, but it should never, EVER, be accessed by any code outside
+     * the corresponding implementation directory since the type will depend
+     * on the architecture.
+     */
+    std::array<std::int32_t, GMX_SIMD_FINT32_WIDTH> simdInternal_;
+};
+
+/*! \libinternal \brief Boolean type for float SIMD data.
+ *
+ *  Available if GMX_SIMD_HAVE_FLOAT is 1.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+class SimdFBool
+{
+public:
+    SimdFBool() {}
+
+    //! \brief Construct from scalar
+    SimdFBool(bool b) { simdInternal_.fill(b); }
+
+    /*! \brief Internal SIMD data. Implementation dependent, don't touch.
+     *
+     * This has to be public to enable usage in combination with static inline
+     * functions, but it should never, EVER, be accessed by any code outside
+     * the corresponding implementation directory since the type will depend
+     * on the architecture.
+     */
+    std::array<bool, GMX_SIMD_FLOAT_WIDTH> simdInternal_;
+};
+
+/*! \libinternal \brief Boolean type for integer datatypes corresponding to float SIMD.
+ *
+ * Available if GMX_SIMD_HAVE_FINT32_ARITHMETICS is 1.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+class SimdFIBool
+{
+public:
+    SimdFIBool() {}
+
+    //! \brief Construct from scalar
+    SimdFIBool(bool b) { simdInternal_.fill(b); }
+
+    /*! \brief Internal SIMD data. Implementation dependent, don't touch.
+     *
+     * This has to be public to enable usage in combination with static inline
+     * functions, but it should never, EVER, be accessed by any code outside
+     * the corresponding implementation directory since the type will depend
+     * on the architecture.
+     */
+    std::array<bool, GMX_SIMD_FINT32_WIDTH> simdInternal_;
+};
+
+/*! \}
+ *
+ * \name SIMD implementation load/store operations for single precision floating point
+ * \{
+ */
+
+/*! \brief Load \ref GMX_SIMD_FLOAT_WIDTH float numbers from aligned memory.
+ *
+ * \param m Pointer to memory aligned to the SIMD width.
+ * \return SIMD variable with data loaded.
+ */
+static inline SimdFloat gmx_simdcall simdLoad(const float* m, SimdFloatTag = {})
+{
+    SimdFloat a;
+
+    assert(std::size_t(m) % (a.simdInternal_.size() * sizeof(float)) == 0);
+
+    std::copy(m, m + a.simdInternal_.size(), a.simdInternal_.begin());
+    return a;
+}
+
+/*! \brief Store the contents of SIMD float variable to aligned memory m.
+ *
+ * \param[out] m Pointer to memory, aligned to SIMD width.
+ * \param a SIMD variable to store
+ */
+static inline void gmx_simdcall store(float* m, SimdFloat a)
+{
+    assert(std::size_t(m) % (a.simdInternal_.size() * sizeof(float)) == 0);
+
+    std::copy(a.simdInternal_.begin(), a.simdInternal_.end(), m);
+}
+
+/*! \brief Load SIMD float from unaligned memory.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOADU is 1.
+ *
+ * \param m Pointer to memory, no alignment requirement.
+ * \return SIMD variable with data loaded.
+ */
+static inline SimdFloat gmx_simdcall simdLoadU(const float* m, SimdFloatTag = {})
+{
+    SimdFloat a;
+    std::copy(m, m + a.simdInternal_.size(), a.simdInternal_.begin());
+    return a;
+}
+
+/*! \brief Store SIMD float to unaligned memory.
+ *
+ * Available if \ref GMX_SIMD_HAVE_STOREU is 1.
+ *
+ * \param[out] m Pointer to memory, no alignment requirement.
+ * \param a SIMD variable to store.
+ */
+static inline void gmx_simdcall storeU(float* m, SimdFloat a)
+{
+    std::copy(a.simdInternal_.begin(), a.simdInternal_.end(), m);
+}
+
+/*! \brief Set all SIMD float variable elements to 0.0.
+ *
+ * You should typically just call \ref gmx::setZero(), which uses proxy objects
+ * internally to handle all types rather than adding the suffix used here.
+ *
+ * \return SIMD 0.0F
+ */
+static inline SimdFloat gmx_simdcall setZeroF()
+{
+    return SimdFloat(0.0F);
+}
+
+/*! \} */
+
+
+/*!
+ * \name SIMD implementation load/store operations for integers (corresponding to float)
+ * \{
+ */
+
+/*! \brief Load aligned SIMD integer data, width corresponds to \ref gmx::SimdFloat.
+ *
+ * You should typically just call \ref gmx::load(), which uses proxy objects
+ * internally to handle all types rather than adding the suffix used here.
+ *
+ * \param m Pointer to memory, aligned to (float) integer SIMD width.
+ * \return SIMD integer variable.
+ */
+static inline SimdFInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdFInt32Tag)
+{
+    SimdFInt32 a;
+
+    assert(std::size_t(m) % (a.simdInternal_.size() * sizeof(std::int32_t)) == 0);
+
+    std::copy(m, m + a.simdInternal_.size(), a.simdInternal_.begin());
+    return a;
+};
+
+/*! \brief Store aligned SIMD integer data, width corresponds to \ref gmx::SimdFloat.
+ *
+ * \param m Memory aligned to (float) integer SIMD width.
+ * \param a SIMD variable to store.
+ */
+static inline void gmx_simdcall store(std::int32_t* m, SimdFInt32 a)
+{
+    assert(std::size_t(m) % (a.simdInternal_.size() * sizeof(std::int32_t)) == 0);
+
+    std::copy(a.simdInternal_.begin(), a.simdInternal_.end(), m);
+};
+
+/*! \brief Load unaligned integer SIMD data, width corresponds to \ref gmx::SimdFloat.
+ *
+ * You should typically just call \ref gmx::loadU(), which uses proxy objects
+ * internally to handle all types rather than adding the suffix used here.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOADU is 1.
+ *
+ * \param m Pointer to memory, no alignment requirements.
+ * \return SIMD integer variable.
+ */
+static inline SimdFInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdFInt32Tag)
+{
+    SimdFInt32 a;
+    std::copy(m, m + a.simdInternal_.size(), a.simdInternal_.begin());
+    return a;
+}
+
+/*! \brief Store unaligned SIMD integer data, width corresponds to \ref gmx::SimdFloat.
+ *
+ * Available if \ref GMX_SIMD_HAVE_STOREU is 1.
+ *
+ * \param m Memory pointer, no alignment requirements.
+ * \param a SIMD variable to store.
+ */
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdFInt32 a)
+{
+    std::copy(a.simdInternal_.begin(), a.simdInternal_.end(), m);
+}
+
+/*! \brief Set all SIMD (float) integer variable elements to 0.
+ *
+ * You should typically just call \ref gmx::setZero(), which uses proxy objects
+ * internally to handle all types rather than adding the suffix used here.
+ *
+ * \return SIMD 0
+ */
+static inline SimdFInt32 gmx_simdcall setZeroFI()
+{
+    return SimdFInt32(0);
+}
+
+/*! \brief Extract element with index i from \ref gmx::SimdFInt32.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_EXTRACT is 1.
+ *
+ * \tparam index Compile-time constant, position to extract (first position is 0)
+ * \param  a     SIMD variable from which to extract value.
+ * \return Single integer from position index in SIMD variable.
+ */
+template<int index>
+static inline std::int32_t gmx_simdcall extract(SimdFInt32 a)
+{
+    return a.simdInternal_[index];
+}
+
+/*! \}
+ *
+ * \name SIMD implementation single precision floating-point bitwise logical operations
+ * \{
+ */
+
+/*! \brief Bitwise and for two SIMD float variables.
+ *
+ * Supported if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return data1 & data2
+ */
+static inline SimdFloat gmx_simdcall operator&(SimdFloat a, SimdFloat b)
+{
+    SimdFloat res;
+
+    union
+    {
+        float        r;
+        std::int32_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = conv1.i & conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+/*! \brief Bitwise andnot for SIMD float.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return (~data1) & data2
+ */
+static inline SimdFloat gmx_simdcall andNot(SimdFloat a, SimdFloat b)
+{
+    SimdFloat res;
+
+    union
+    {
+        float        r;
+        std::int32_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = ~conv1.i & conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+/*! \brief Bitwise or for SIMD float.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return data1 | data2
+ */
+static inline SimdFloat gmx_simdcall operator|(SimdFloat a, SimdFloat b)
+{
+    SimdFloat res;
+
+    union
+    {
+        float        r;
+        std::int32_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = conv1.i | conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+/*! \brief Bitwise xor for SIMD float.
+ *
+ * Available if \ref GMX_SIMD_HAVE_LOGICAL is 1.
+ *
+ * \param a data1
+ * \param b data2
+ * \return data1 ^ data2
+ */
+static inline SimdFloat gmx_simdcall operator^(SimdFloat a, SimdFloat b)
+{
+    SimdFloat res;
+
+    union
+    {
+        float        r;
+        std::int32_t i;
+    } conv1, conv2;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        conv1.r              = a.simdInternal_[i];
+        conv2.r              = b.simdInternal_[i];
+        conv1.i              = conv1.i ^ conv2.i;
+        res.simdInternal_[i] = conv1.r;
+    }
+    return res;
+}
+
+/*! \}
+ *
+ * \name SIMD implementation single precision floating-point arithmetics
+ * \{
+ */
+
+/*! \brief Add two float SIMD variables.
+ *
+ * \param a term1
+ * \param b term2
+ * \return a+b
+ */
+static inline SimdFloat gmx_simdcall operator+(SimdFloat a, SimdFloat b)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] + b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Subtract two float SIMD variables.
+ *
+ * \param a term1
+ * \param b term2
+ * \return a-b
+ */
+static inline SimdFloat gmx_simdcall operator-(SimdFloat a, SimdFloat b)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] - b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief SIMD single precision negate.
+ *
+ * \param a SIMD double precision value
+ * \return -a
+ */
+static inline SimdFloat gmx_simdcall operator-(SimdFloat a)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = -a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Multiply two float SIMD variables.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \return a*b.
+ */
+static inline SimdFloat gmx_simdcall operator*(SimdFloat a, SimdFloat b)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] * b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief SIMD float Fused-multiply-add. Result is a*b+c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return a*b+c
+ */
+static inline SimdFloat gmx_simdcall fma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return a * b + c;
+}
+
+/*! \brief SIMD float Fused-multiply-subtract. Result is a*b-c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return a*b-c
+ */
+static inline SimdFloat gmx_simdcall fms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return a * b - c;
+}
+
+/*! \brief SIMD float Fused-negated-multiply-add. Result is -a*b+c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return -a*b+c
+ */
+static inline SimdFloat gmx_simdcall fnma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return c - a * b;
+}
+
+/*! \brief SIMD float Fused-negated-multiply-subtract. Result is -a*b-c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return -a*b-c
+ */
+static inline SimdFloat gmx_simdcall fnms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return -a * b - c;
+}
+
+/*! \brief SIMD float 1.0/sqrt(x) lookup.
+ *
+ * This is a low-level instruction that should only be called from routines
+ * implementing the inverse square root in simd_math.h.
+ *
+ * \param x Argument, x>0
+ * \return Approximation of 1/sqrt(x), accuracy is \ref GMX_SIMD_RSQRT_BITS.
+ */
+static inline SimdFloat gmx_simdcall rsqrt(SimdFloat x)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = 1.0F / std::sqrt(x.simdInternal_[i]);
+    }
+    return res;
+};
+
+/*! \brief SIMD float 1.0/x lookup.
+ *
+ * This is a low-level instruction that should only be called from routines
+ * implementing the reciprocal in simd_math.h.
+ *
+ * \param x Argument, x!=0
+ * \return Approximation of 1/x, accuracy is \ref GMX_SIMD_RCP_BITS.
+ */
+static inline SimdFloat gmx_simdcall rcp(SimdFloat x)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = 1.0F / x.simdInternal_[i];
+    }
+    return res;
+};
+
+/*! \brief Add two float SIMD variables, masked version.
+ *
+ * \param a term1
+ * \param b term2
+ * \param m mask
+ * \return a+b where mask is true, a otherwise.
+ */
+static inline SimdFloat gmx_simdcall maskAdd(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] + (m.simdInternal_[i] ? b.simdInternal_[i] : 0.0F);
+    }
+    return res;
+}
+
+/*! \brief Multiply two float SIMD variables, masked version.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param m mask
+ * \return a*b where mask is true, 0.0 otherwise.
+ */
+static inline SimdFloat gmx_simdcall maskzMul(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = m.simdInternal_[i] ? (a.simdInternal_[i] * b.simdInternal_[i]) : 0.0F;
+    }
+    return res;
+}
+
+/*! \brief SIMD float fused multiply-add, masked version.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \param m mask
+ * \return a*b+c where mask is true, 0.0 otherwise.
+ */
+static inline SimdFloat gmx_simdcall maskzFma(SimdFloat a, SimdFloat b, SimdFloat c, SimdFBool m)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] =
+                m.simdInternal_[i] ? (a.simdInternal_[i] * b.simdInternal_[i] + c.simdInternal_[i]) : 0.0F;
+    }
+    return res;
+}
+
+/*! \brief SIMD float 1.0/sqrt(x) lookup, masked version.
+ *
+ * This is a low-level instruction that should only be called from routines
+ * implementing the inverse square root in simd_math.h.
+ *
+ * \param x Argument, x>0 for entries where mask is true.
+ * \param m Mask
+ * \return Approximation of 1/sqrt(x), accuracy is \ref GMX_SIMD_RSQRT_BITS.
+ *         The result for masked-out entries will be 0.0.
+ */
+static inline SimdFloat gmx_simdcall maskzRsqrt(SimdFloat x, SimdFBool m)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (m.simdInternal_[i] != 0) ? 1.0F / std::sqrt(x.simdInternal_[i]) : 0.0F;
+    }
+    return res;
+}
+
+/*! \brief SIMD float 1.0/x lookup, masked version.
+ *
+ * This is a low-level instruction that should only be called from routines
+ * implementing the reciprocal in simd_math.h.
+ *
+ * \param x Argument, x>0 for entries where mask is true.
+ * \param m Mask
+ * \return Approximation of 1/x, accuracy is \ref GMX_SIMD_RCP_BITS.
+ *         The result for masked-out entries will be 0.0.
+ */
+static inline SimdFloat gmx_simdcall maskzRcp(SimdFloat x, SimdFBool m)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (m.simdInternal_[i] != 0) ? 1.0F / x.simdInternal_[i] : 0.0F;
+    }
+    return res;
+}
+
+/*! \brief SIMD float Floating-point abs().
+ *
+ * \param a any floating point values
+ * \return abs(a) for each element.
+ */
+static inline SimdFloat gmx_simdcall abs(SimdFloat a)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::abs(a.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Set each SIMD float element to the largest from two variables.
+ *
+ * \param a Any floating-point value
+ * \param b Any floating-point value
+ * \return max(a,b) for each element.
+ */
+static inline SimdFloat gmx_simdcall max(SimdFloat a, SimdFloat b)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::max(a.simdInternal_[i], b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Set each SIMD float element to the smallest from two variables.
+ *
+ * \param a Any floating-point value
+ * \param b Any floating-point value
+ * \return min(a,b) for each element.
+ */
+static inline SimdFloat gmx_simdcall min(SimdFloat a, SimdFloat b)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::min(a.simdInternal_[i], b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief SIMD float round to nearest integer value (in floating-point format).
+ *
+ * \param a Any floating-point value
+ * \return The nearest integer, represented in floating-point format.
+ *
+ * \note Round mode is implementation defined. The only guarantee is that it
+ * is consistent between rounding functions (round, cvtR2I).
+ */
+static inline SimdFloat gmx_simdcall round(SimdFloat a)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::round(a.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Truncate SIMD float, i.e. round towards zero - common hardware instruction.
+ *
+ * \param a Any floating-point value
+ * \return Integer rounded towards zero, represented in floating-point format.
+ *
+ * \note This is truncation towards zero, not floor(). The reason for this
+ * is that truncation is virtually always present as a dedicated hardware
+ * instruction, but floor() frequently isn't.
+ */
+static inline SimdFloat gmx_simdcall trunc(SimdFloat a)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = std::trunc(a.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Extract (integer) exponent and fraction from single precision SIMD.
+ *
+ * \tparam      opt       By default this function behaves like the standard
+ *                        library such that frexp(+-0,exp) returns +-0 and
+ *                        stores 0 in the exponent when value is 0. If you
+ *                        know the argument is always nonzero, you can set
+ *                        the template parameter to MathOptimization::Unsafe
+ *                        to make it slightly faster.
+ *
+ * \param       value     Floating-point value to extract from
+ * \param[out]  exponent  Returned exponent of value, integer SIMD format.
+ * \return      Fraction of value, floating-point SIMD format.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall frexp(SimdFloat value, SimdFInt32* exponent)
+{
+    SimdFloat fraction;
+
+    for (std::size_t i = 0; i < fraction.simdInternal_.size(); i++)
+    {
+        fraction.simdInternal_[i] = std::frexp(value.simdInternal_[i], &exponent->simdInternal_[i]);
+    }
+    return fraction;
+}
+
+/*! \brief Multiply a SIMD float value by the number 2 raised to an exp power.
+ *
+ * \tparam opt By default, this routine will return zero for input arguments
+ *             that are so small they cannot be reproduced in the current
+ *             precision. If the unsafe math optimization template parameter
+ *             setting is used, these tests are skipped, and the result will
+ *             be undefined (possible even NaN). This might happen below -127
+ *             in single precision or -1023 in double, although some
+ *             might use denormal support to extend the range.
+ *
+ * \param value Floating-point number to multiply with new exponent
+ * \param exponent Integer that will not overflow as 2^exponent.
+ * \return value*2^exponent
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall ldexp(SimdFloat value, SimdFInt32 exponent)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        // std::ldexp already takes care of clamping arguments, so we do not
+        // need to do anything in the reference implementation
+        res.simdInternal_[i] = std::ldexp(value.simdInternal_[i], exponent.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Return sum of all elements in SIMD float variable.
+ *
+ * \param a SIMD variable to reduce/sum.
+ * \return The sum of all elements in the argument variable.
+ *
+ */
+static inline float gmx_simdcall reduce(SimdFloat a)
+{
+    float sum = 0.0F;
+
+    for (std::size_t i = 0; i < a.simdInternal_.size(); i++)
+    {
+        sum += a.simdInternal_[i];
+    }
+    return sum;
+}
+
+/*! \}
+ *
+ * \name SIMD implementation single precision floating-point comparisons, boolean, selection.
+ * \{
+ */
+
+/*! \brief SIMD a==b for single SIMD.
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a==b.
+ *
+ * Beware that exact floating-point comparisons are difficult.
+ */
+static inline SimdFBool gmx_simdcall operator==(SimdFloat a, SimdFloat b)
+{
+    SimdFBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] == b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief SIMD a!=b for single SIMD.
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a!=b.
+ *
+ * Beware that exact floating-point comparisons are difficult.
+ */
+static inline SimdFBool gmx_simdcall operator!=(SimdFloat a, SimdFloat b)
+{
+    SimdFBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] != b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief SIMD a<b for single SIMD.
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a<b.
+ */
+static inline SimdFBool gmx_simdcall operator<(SimdFloat a, SimdFloat b)
+{
+    SimdFBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] < b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief SIMD a<=b for single SIMD.
+ *
+ * \param a value1
+ * \param b value2
+ * \return Each element of the boolean will be set to true if a<=b.
+ */
+static inline SimdFBool gmx_simdcall operator<=(SimdFloat a, SimdFloat b)
+{
+    SimdFBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] <= b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Return true if any bits are set in the single precision SIMD.
+ *
+ * This function is used to handle bitmasks, mainly for exclusions in the
+ * inner kernels. Note that it will return true even for -0.0F (sign bit set),
+ * so it is not identical to not-equal.
+ *
+ * \param a value
+ * \return Each element of the boolean will be true if any bit in a is nonzero.
+ */
+static inline SimdFBool gmx_simdcall testBits(SimdFloat a)
+{
+    SimdFBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        union
+        {
+            std::uint32_t i;
+            float         f;
+        } conv;
+
+        conv.f               = a.simdInternal_[i];
+        res.simdInternal_[i] = (conv.i != 0);
+    }
+    return res;
+}
+
+/*! \brief Logical \a and on single precision SIMD booleans.
+ *
+ * \param a logical vars 1
+ * \param b logical vars 2
+ * \return For each element, the result boolean is true if a \& b are true.
+ *
+ * \note This is not necessarily a bitwise operation - the storage format
+ * of booleans is implementation-dependent.
+ */
+static inline SimdFBool gmx_simdcall operator&&(SimdFBool a, SimdFBool b)
+{
+    SimdFBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] && b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Logical \a or on single precision SIMD booleans.
+ *
+ * \param a logical vars 1
+ * \param b logical vars 2
+ * \return For each element, the result boolean is true if a or b is true.
+ *
+ * Note that this is not necessarily a bitwise operation - the storage format
+ * of booleans is implementation-dependent.
+ *
+ \ */
+static inline SimdFBool gmx_simdcall operator||(SimdFBool a, SimdFBool b)
+{
+    SimdFBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] || b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Returns non-zero if any of the boolean in SIMD a is True, otherwise 0.
+ *
+ * \param a Logical variable.
+ * \return true if any element in a is true, otherwise false.
+ *
+ * The actual return value for truth will depend on the architecture,
+ * so any non-zero value is considered truth.
+ */
+static inline bool gmx_simdcall anyTrue(SimdFBool a)
+{
+    bool res = false;
+
+    for (std::size_t i = 0; i < a.simdInternal_.size(); i++)
+    {
+        res = res || a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Select from single precision SIMD variable where boolean is true.
+ *
+ * \param a Floating-point variable to select from
+ * \param mask Boolean selector
+ * \return  For each element, a is selected for true, 0 for false.
+ */
+static inline SimdFloat gmx_simdcall selectByMask(SimdFloat a, SimdFBool mask)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = mask.simdInternal_[i] ? a.simdInternal_[i] : 0.0F;
+    }
+    return res;
+}
+
+/*! \brief Select from single precision SIMD variable where boolean is false.
+ *
+ * \param a Floating-point variable to select from
+ * \param mask Boolean selector
+ * \return  For each element, a is selected for false, 0 for true (sic).
+ */
+static inline SimdFloat gmx_simdcall selectByNotMask(SimdFloat a, SimdFBool mask)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = mask.simdInternal_[i] ? 0.0F : a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Vector-blend SIMD float selection.
+ *
+ * \param a First source
+ * \param b Second source
+ * \param sel Boolean selector
+ * \return For each element, select b if sel is true, a otherwise.
+ */
+static inline SimdFloat gmx_simdcall blend(SimdFloat a, SimdFloat b, SimdFBool sel)
+{
+    SimdFloat res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = sel.simdInternal_[i] ? b.simdInternal_[i] : a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \}
+ *
+ * \name SIMD implementation integer (corresponding to float) bitwise logical operations
+ * \{
+ */
+
+/*! \brief Integer SIMD bitwise and.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_LOGICAL is 1.
+ *
+ * \note You can \a not use this operation directly to select based on a boolean
+ * SIMD variable, since booleans are separate from integer SIMD. If that
+ * is what you need, have a look at \ref gmx::selectByMask instead.
+ *
+ * \param a first integer SIMD
+ * \param b second integer SIMD
+ * \return a \& b (bitwise and)
+ */
+static inline SimdFInt32 gmx_simdcall operator&(SimdFInt32 a, SimdFInt32 b)
+{
+    SimdFInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] & b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Integer SIMD bitwise not/complement.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_LOGICAL is 1.
+ *
+ * \note You can \a not use this operation directly to select based on a boolean
+ * SIMD variable, since booleans are separate from integer SIMD. If that
+ * is what you need, have a look at \ref gmx::selectByMask instead.
+ *
+ * \param a integer SIMD
+ * \param b integer SIMD
+ * \return (~a) & b
+ */
+static inline SimdFInt32 gmx_simdcall andNot(SimdFInt32 a, SimdFInt32 b)
+{
+    SimdFInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = ~a.simdInternal_[i] & b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Integer SIMD bitwise or.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_LOGICAL is 1.
+ *
+ * \param a first integer SIMD
+ * \param b second integer SIMD
+ * \return a \| b (bitwise or)
+ */
+static inline SimdFInt32 gmx_simdcall operator|(SimdFInt32 a, SimdFInt32 b)
+{
+    SimdFInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] | b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Integer SIMD bitwise xor.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_LOGICAL is 1.
+ *
+ * \param a first integer SIMD
+ * \param b second integer SIMD
+ * \return a ^ b (bitwise xor)
+ */
+static inline SimdFInt32 gmx_simdcall operator^(SimdFInt32 a, SimdFInt32 b)
+{
+    SimdFInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] ^ b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \}
+ *
+ * \name SIMD implementation integer (corresponding to float) arithmetics
+ * \{
+ */
+
+/*! \brief Add SIMD integers.
+ *
+ * This routine is only available if \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS (single)
+ *  or \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS (double) is 1.
+ *
+ * \param a term1
+ * \param b term2
+ * \return a+b
+ */
+static inline SimdFInt32 gmx_simdcall operator+(SimdFInt32 a, SimdFInt32 b)
+{
+    SimdFInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] + b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Subtract SIMD integers.
+ *
+ * This routine is only available if \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS (single)
+ *  or \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS (double) is 1.
+ *
+ * \param a term1
+ * \param b term2
+ * \return a-b
+ */
+static inline SimdFInt32 gmx_simdcall operator-(SimdFInt32 a, SimdFInt32 b)
+{
+    SimdFInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] - b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Multiply SIMD integers.
+ *
+ * This routine is only available if \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS (single)
+ *  or \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS (double) is 1.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \return a*b.
+ *
+ * \note Only the low 32 bits are retained, so this can overflow.
+ */
+static inline SimdFInt32 gmx_simdcall operator*(SimdFInt32 a, SimdFInt32 b)
+{
+    SimdFInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = a.simdInternal_[i] * b.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \}
+ *
+ * \name SIMD implementation integer (corresponding to float) comparisons, boolean, selection
+ * \{
+ */
+
+/*! \brief Equality comparison of two integers corresponding to float values.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD integer1
+ * \param b SIMD integer2
+ * \return SIMD integer boolean with true for elements where a==b
+ */
+static inline SimdFIBool gmx_simdcall operator==(SimdFInt32 a, SimdFInt32 b)
+{
+    SimdFIBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] == b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Less-than comparison of two SIMD integers corresponding to float values.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD integer1
+ * \param b SIMD integer2
+ * \return SIMD integer boolean with true for elements where a<b
+ */
+static inline SimdFIBool gmx_simdcall operator<(SimdFInt32 a, SimdFInt32 b)
+{
+    SimdFIBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] < b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Check if any bit is set in each element
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD integer
+ * \return SIMD integer boolean with true for elements where any bit is set
+ */
+static inline SimdFIBool gmx_simdcall testBits(SimdFInt32 a)
+{
+    SimdFIBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] != 0);
+    }
+    return res;
+}
+
+/*! \brief Logical AND on SimdFIBool.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD boolean 1
+ * \param b SIMD boolean 2
+ * \return True for elements where both a and b are true.
+ */
+static inline SimdFIBool gmx_simdcall operator&&(SimdFIBool a, SimdFIBool b)
+{
+    SimdFIBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] && b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Logical OR on SimdFIBool.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD boolean 1
+ * \param b SIMD boolean 2
+ * \return True for elements where both a and b are true.
+ */
+static inline SimdFIBool gmx_simdcall operator||(SimdFIBool a, SimdFIBool b)
+{
+    SimdFIBool res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = (a.simdInternal_[i] || b.simdInternal_[i]);
+    }
+    return res;
+}
+
+/*! \brief Returns true if any of the boolean in x is True, otherwise 0.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS is 1.
+ *
+ * The actual return value for "any true" will depend on the architecture.
+ * Any non-zero value should be considered truth.
+ *
+ * \param a SIMD boolean
+ * \return True if any of the elements in a is true, otherwise 0.
+ */
+static inline bool gmx_simdcall anyTrue(SimdFIBool a)
+{
+    bool res = false;
+
+    for (std::size_t i = 0; i < a.simdInternal_.size(); i++)
+    {
+        res = res || a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Select from \ref gmx::SimdFInt32 variable where boolean is true.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD integer to select from
+ * \param mask Boolean selector
+ * \return Elements from a where sel is true, 0 otherwise.
+ */
+static inline SimdFInt32 gmx_simdcall selectByMask(SimdFInt32 a, SimdFIBool mask)
+{
+    SimdFInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = mask.simdInternal_[i] ? a.simdInternal_[i] : 0.0F;
+    }
+    return res;
+}
+
+/*! \brief Select from \ref gmx::SimdFInt32 variable where boolean is false.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS is 1.
+ *
+ * \param a SIMD integer to select from
+ * \param mask Boolean selector
+ * \return Elements from a where sel is false, 0 otherwise (sic).
+ */
+static inline SimdFInt32 gmx_simdcall selectByNotMask(SimdFInt32 a, SimdFIBool mask)
+{
+    SimdFInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = mask.simdInternal_[i] ? 0.0F : a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \brief Vector-blend SIMD integer selection.
+ *
+ * Available if \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS is 1.
+ *
+ * \param a First source
+ * \param b Second source
+ * \param sel Boolean selector
+ * \return For each element, select b if sel is true, a otherwise.
+ */
+static inline SimdFInt32 gmx_simdcall blend(SimdFInt32 a, SimdFInt32 b, SimdFIBool sel)
+{
+    SimdFInt32 res;
+
+    for (std::size_t i = 0; i < res.simdInternal_.size(); i++)
+    {
+        res.simdInternal_[i] = sel.simdInternal_[i] ? b.simdInternal_[i] : a.simdInternal_[i];
+    }
+    return res;
+}
+
+/*! \}
+ *
+ * \name SIMD implementation conversion operations
+ * \{
+ */
+
+/*! \brief Round single precision floating point to integer.
+ *
+ * \param a SIMD floating-point
+ * \return SIMD integer, rounded to nearest integer.
+ *
+ * \note Round mode is implementation defined. The only guarantee is that it
+ * is consistent between rounding functions (round, cvtR2I).
+ */
+static inline SimdFInt32 gmx_simdcall cvtR2I(SimdFloat a)
+{
+    SimdFInt32 b;
+
+    for (std::size_t i = 0; i < b.simdInternal_.size(); i++)
+    {
+        b.simdInternal_[i] = std::round(a.simdInternal_[i]);
+    }
+    return b;
+};
+
+/*! \brief Truncate single precision floating point to integer.
+ *
+ * \param a SIMD floating-point
+ * \return SIMD integer, truncated to nearest integer.
+ */
+static inline SimdFInt32 gmx_simdcall cvttR2I(SimdFloat a)
+{
+    SimdFInt32 b;
+
+    for (std::size_t i = 0; i < b.simdInternal_.size(); i++)
+    {
+        b.simdInternal_[i] = std::trunc(a.simdInternal_[i]);
+    }
+    return b;
+};
+
+/*! \brief Convert integer to single precision floating point.
+ *
+ * \param a SIMD integer
+ * \return SIMD floating-point
+ */
+static inline SimdFloat gmx_simdcall cvtI2R(SimdFInt32 a)
+{
+    SimdFloat b;
+
+    for (std::size_t i = 0; i < b.simdInternal_.size(); i++)
+    {
+        b.simdInternal_[i] = a.simdInternal_[i];
+    }
+    return b;
+};
+
+/*! \brief Convert from single precision boolean to corresponding integer boolean
+ *
+ * \param a SIMD floating-point boolean
+ * \return SIMD integer boolean
+ */
+static inline SimdFIBool gmx_simdcall cvtB2IB(SimdFBool a)
+{
+    SimdFIBool b;
+
+    for (std::size_t i = 0; i < b.simdInternal_.size(); i++)
+    {
+        b.simdInternal_[i] = a.simdInternal_[i];
+    }
+    return b;
+};
+
+/*! \brief Convert from integer boolean to corresponding single precision boolean
+ *
+ * \param a SIMD integer boolean
+ * \return SIMD floating-point boolean
+ */
+static inline SimdFBool gmx_simdcall cvtIB2B(SimdFIBool a)
+{
+    SimdFBool b;
+
+    for (std::size_t i = 0; i < b.simdInternal_.size(); i++)
+    {
+        b.simdInternal_[i] = a.simdInternal_[i];
+    }
+    return b;
+};
+
+/*! \} */
+
+/*! \} */
+/*! \endcond */
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_REFERENCE_SIMD_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_reference/impl_reference_util_double.h b/src/include/gromacs/simd/impl_reference/impl_reference_util_double.h
new file mode 100644 (file)
index 0000000..777af5b
--- /dev/null
@@ -0,0 +1,963 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_REFERENCE_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPL_REFERENCE_UTIL_DOUBLE_H
+
+/*! \libinternal \file
+ *
+ * \brief Reference impl., higher-level double prec. SIMD utility functions
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \ingroup module_simd
+ */
+
+/* Avoid adding dependencies on the rest of GROMACS here (e.g. gmxassert.h)
+ * since we want to be able run the low-level SIMD implementations independently
+ * in simulators for new hardware.
+ */
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <algorithm>
+
+#include "impl_reference_definitions.h"
+#include "impl_reference_simd_double.h"
+
+namespace gmx
+{
+
+/*! \cond libapi */
+/*! \addtogroup module_simd */
+/*! \{ */
+
+/*! \name Higher-level SIMD utility functions, double precision.
+ *
+ * These include generic functions to work with triplets of data, typically
+ * coordinates, and a few utility functions to load and update data in the
+ * nonbonded kernels. These functions should be available on all implementations.
+ *
+ * \{
+ */
+
+/*! \brief Load 4 consecutive double from each of GMX_SIMD_DOUBLE_WIDTH offsets,
+ *         and transpose into 4 SIMD double variables.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 4 for this routine) the input data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are loaded.
+ * \param      base   Pointer to the start of the memory area
+ * \param      offset Array with offsets to the start of each data point.
+ * \param[out] v0     1st component of data, base[align*offset[i]] for each i.
+ * \param[out] v1     2nd component of data, base[align*offset[i] + 1] for each i.
+ * \param[out] v2     3rd component of data, base[align*offset[i] + 2] for each i.
+ * \param[out] v3     4th component of data, base[align*offset[i] + 3] for each i.
+ *
+ * The floating-point memory locations must be aligned, but only to the smaller
+ * of four elements and the floating-point SIMD width.
+ *
+ * The offset memory must be aligned to GMX_SIMD_DINT32_WIDTH.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ */
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const double*      base,
+                                                    const std::int32_t offset[],
+                                                    SimdDouble*        v0,
+                                                    SimdDouble*        v1,
+                                                    SimdDouble*        v2,
+                                                    SimdDouble*        v3)
+{
+    // Offset list must be aligned for SIMD DINT32
+    assert(std::size_t(offset) % (GMX_SIMD_DINT32_WIDTH * sizeof(std::int32_t)) == 0);
+    // Base pointer must be aligned to the smaller of 4 elements and double SIMD width
+    assert(std::size_t(base) % (std::min(GMX_SIMD_DOUBLE_WIDTH, 4) * sizeof(double)) == 0);
+    // align parameter must also be a multiple of the above alignment requirement
+    assert(align % std::min(GMX_SIMD_DOUBLE_WIDTH, 4) == 0);
+
+    for (std::size_t i = 0; i < v0->simdInternal_.size(); i++)
+    {
+        v0->simdInternal_[i] = base[align * offset[i]];
+        v1->simdInternal_[i] = base[align * offset[i] + 1];
+        v2->simdInternal_[i] = base[align * offset[i] + 2];
+        v3->simdInternal_[i] = base[align * offset[i] + 3];
+    }
+}
+
+
+/*! \brief Load 2 consecutive double from each of GMX_SIMD_DOUBLE_WIDTH offsets,
+ *         and transpose into 2 SIMD double variables.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 2 for this routine) the input data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are loaded.
+ * \param      base   Pointer to the start of the memory area
+ * \param      offset Array with offsets to the start of each data point.
+ * \param[out] v0     1st component of data, base[align*offset[i]] for each i.
+ * \param[out] v1     2nd component of data, base[align*offset[i] + 1] for each i.
+ *
+ * The floating-point memory locations must be aligned, but only to the smaller
+ * of two elements and the floating-point SIMD width.
+ *
+ * The offset memory must be aligned to GMX_SIMD_DINT32_WIDTH.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ */
+template<int align>
+static inline void gmx_simdcall
+gatherLoadTranspose(const double* base, const std::int32_t offset[], SimdDouble* v0, SimdDouble* v1)
+{
+    // Offset list must be aligned for SIMD DINT32
+    assert(std::size_t(offset) % (GMX_SIMD_DINT32_WIDTH * sizeof(std::int32_t)) == 0);
+    // Base pointer must be aligned to the smaller of 2 elements and double SIMD width
+    assert(std::size_t(base) % (std::min(GMX_SIMD_DOUBLE_WIDTH, 2) * sizeof(double)) == 0);
+    // align parameter must also be a multiple of the above alignment requirement
+    assert(align % std::min(GMX_SIMD_DOUBLE_WIDTH, 2) == 0);
+
+    for (std::size_t i = 0; i < v0->simdInternal_.size(); i++)
+    {
+        v0->simdInternal_[i] = base[align * offset[i]];
+        v1->simdInternal_[i] = base[align * offset[i] + 1];
+    }
+}
+
+
+/*! \brief Best alignment to use for aligned pairs of double data.
+ *
+ * \copydetails c_simdBestPairAlignmentFloat
+ */
+static const int c_simdBestPairAlignmentDouble = 2;
+
+
+/*! \brief Load 3 consecutive doubles from each of GMX_SIMD_DOUBLE_WIDTH offsets,
+ *         and transpose into 3 SIMD double variables.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 3 for this routine) the input data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are loaded.
+ * \param      base   Pointer to the start of the memory area
+ * \param      offset Array with offsets to the start of each data point.
+ * \param[out] v0     1st component of data, base[align*offset[i]] for each i.
+ * \param[out] v1     2nd component of data, base[align*offset[i] + 1] for each i.
+ * \param[out] v2     3rd component of data, base[align*offset[i] + 2] for each i.
+ *
+ * This function can work with both aligned (better performance) and unaligned
+ * memory. When the align parameter is not a power-of-two (align==3 would be normal
+ * for packed atomic coordinates) the memory obviously cannot be aligned, and
+ * we account for this.
+ * However, in the case where align is a power-of-two, we assume the base pointer
+ * also has the same alignment, which will enable many platforms to use faster
+ * aligned memory load operations.
+ * An easy way to think of this is that each triplet of data in memory must be
+ * aligned to the align parameter you specify when it's a power-of-two.
+ *
+ * The offset memory must always be aligned to GMX_SIMD_FINT32_WIDTH, since this
+ * enables us to use SIMD loads and gather operations on platforms that support it.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This routine uses a normal array for the offsets, since we typically
+ *       load this data from memory. On the architectures we have tested this
+ *       is faster even when a SIMD integer datatype is present.
+ * \note To improve performance, this function might use full-SIMD-width
+ *       unaligned loads. This means you need to ensure the memory is padded
+ *       at the end, so we always can load GMX_SIMD_REAL_WIDTH elements
+ *       starting at the last offset. If you use the Gromacs aligned memory
+ *       allocation routines this will always be the case.
+ */
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const double*      base,
+                                                     const std::int32_t offset[],
+                                                     SimdDouble*        v0,
+                                                     SimdDouble*        v1,
+                                                     SimdDouble*        v2)
+{
+    // Offset list must be aligned for SIMD DINT32
+    assert(std::size_t(offset) % (GMX_SIMD_DINT32_WIDTH * sizeof(std::int32_t)) == 0);
+
+    for (std::size_t i = 0; i < v0->simdInternal_.size(); i++)
+    {
+        v0->simdInternal_[i] = base[align * offset[i]];
+        v1->simdInternal_[i] = base[align * offset[i] + 1];
+        v2->simdInternal_[i] = base[align * offset[i] + 2];
+    }
+}
+
+/*! \brief Transpose and store 3 SIMD doubles to 3 consecutive addresses at
+ *         GMX_SIMD_DOUBLE_WIDTH offsets.
+ *
+ * \tparam     align  Alignment of the memory to which we write, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 3 for this routine) the output data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are written.
+ * \param[out] base   Pointer to the start of the memory area
+ * \param      offset Aligned array with offsets to the start of each triplet.
+ * \param      v0     1st component of triplets, written to base[align*offset[i]].
+ * \param      v1     2nd component of triplets, written to base[align*offset[i] + 1].
+ * \param      v2     3rd component of triplets, written to base[align*offset[i] + 2].
+ *
+ * This function can work with both aligned (better performance) and unaligned
+ * memory. When the align parameter is not a power-of-two (align==3 would be normal
+ * for packed atomic coordinates) the memory obviously cannot be aligned, and
+ * we account for this.
+ * However, in the case where align is a power-of-two, we assume the base pointer
+ * also has the same alignment, which will enable many platforms to use faster
+ * aligned memory store operations.
+ * An easy way to think of this is that each triplet of data in memory must be
+ * aligned to the align parameter you specify when it's a power-of-two.
+ *
+ * The offset memory must always be aligned to GMX_SIMD_FINT32_WIDTH, since this
+ * enables us to use SIMD loads and gather operations on platforms that support it.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This routine uses a normal array for the offsets, since we typically
+ *       load the data from memory. On the architectures we have tested this
+ *       is faster even when a SIMD integer datatype is present.
+ */
+template<int align>
+static inline void gmx_simdcall transposeScatterStoreU(double*            base,
+                                                       const std::int32_t offset[],
+                                                       SimdDouble         v0,
+                                                       SimdDouble         v1,
+                                                       SimdDouble         v2)
+{
+    // Offset list must be aligned for SIMD DINT32
+    assert(std::size_t(offset) % (GMX_SIMD_DINT32_WIDTH * sizeof(std::int32_t)) == 0);
+
+    for (std::size_t i = 0; i < v0.simdInternal_.size(); i++)
+    {
+        base[align * offset[i]]     = v0.simdInternal_[i];
+        base[align * offset[i] + 1] = v1.simdInternal_[i];
+        base[align * offset[i] + 2] = v2.simdInternal_[i];
+    }
+}
+
+
+/*! \brief Transpose and add 3 SIMD doubles to 3 consecutive addresses at
+ *         GMX_SIMD_DOUBLE_WIDTH offsets.
+ *
+ * \tparam     align  Alignment of the memory to which we write, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 3 for this routine) the output data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are incremented.
+ * \param[out] base   Pointer to the start of the memory area
+ * \param      offset Aligned array with offsets to the start of each triplet.
+ * \param      v0     1st component of triplets, added to base[align*offset[i]].
+ * \param      v1     2nd component of triplets, added to base[align*offset[i] + 1].
+ * \param      v2     3rd component of triplets, added to base[align*offset[i] + 2].
+ *
+ * This function can work with both aligned (better performance) and unaligned
+ * memory. When the align parameter is not a power-of-two (align==3 would be normal
+ * for packed atomic coordinates) the memory obviously cannot be aligned, and
+ * we account for this.
+ * However, in the case where align is a power-of-two, we assume the base pointer
+ * also has the same alignment, which will enable many platforms to use faster
+ * aligned memory load/store operations.
+ * An easy way to think of this is that each triplet of data in memory must be
+ * aligned to the align parameter you specify when it's a power-of-two.
+ *
+ * The offset memory must always be aligned to GMX_SIMD_FINT32_WIDTH, since this
+ * enables us to use SIMD loads and gather operations on platforms that support it.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This routine uses a normal array for the offsets, since we typically
+ *       load the data from memory. On the architectures we have tested this
+ *       is faster even when a SIMD integer datatype is present.
+ * \note To improve performance, this function might use full-SIMD-width
+ *       unaligned load/store, and add 0.0 to the extra elements.
+ *       This means you need to ensure the memory is padded
+ *       at the end, so we always can load GMX_SIMD_REAL_WIDTH elements
+ *       starting at the last offset. If you use the Gromacs aligned memory
+ *       allocation routines this will always be the case.
+ */
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    // Offset list must be aligned for SIMD DINT32
+    assert(std::size_t(offset) % (GMX_SIMD_DINT32_WIDTH * sizeof(std::int32_t)) == 0);
+
+    for (std::size_t i = 0; i < v0.simdInternal_.size(); i++)
+    {
+        base[align * offset[i]] += v0.simdInternal_[i];
+        base[align * offset[i] + 1] += v1.simdInternal_[i];
+        base[align * offset[i] + 2] += v2.simdInternal_[i];
+    }
+}
+
+/*! \brief Transpose and subtract 3 SIMD doubles to 3 consecutive addresses at
+ *         GMX_SIMD_DOUBLE_WIDTH offsets.
+ *
+ * \tparam     align  Alignment of the memory to which we write, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 3 for this routine) the output data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are decremented.
+ * \param[out] base    Pointer to start of memory.
+ * \param      offset  Aligned array with offsets to the start of each triplet.
+ * \param      v0      1st component, subtracted from base[align*offset[i]]
+ * \param      v1      2nd component, subtracted from base[align*offset[i]+1]
+ * \param      v2      3rd component, subtracted from base[align*offset[i]+2]
+ *
+ * This function can work with both aligned (better performance) and unaligned
+ * memory. When the align parameter is not a power-of-two (align==3 would be normal
+ * for packed atomic coordinates) the memory obviously cannot be aligned, and
+ * we account for this.
+ * However, in the case where align is a power-of-two, we assume the base pointer
+ * also has the same alignment, which will enable many platforms to use faster
+ * aligned memory load/store operations.
+ * An easy way to think of this is that each triplet of data in memory must be
+ * aligned to the align parameter you specify when it's a power-of-two.
+ *
+ * The offset memory must always be aligned to GMX_SIMD_FINT32_WIDTH, since this
+ * enables us to use SIMD loads and gather operations on platforms that support it.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This routine uses a normal array for the offsets, since we typically
+ *       load the data from memory. On the architectures we have tested this
+ *       is faster even when a SIMD integer datatype is present.
+ * \note To improve performance, this function might use full-SIMD-width
+ *       unaligned load/store, and subtract 0.0 from the extra elements.
+ *       This means you need to ensure the memory is padded
+ *       at the end, so we always can load GMX_SIMD_REAL_WIDTH elements
+ *       starting at the last offset. If you use the Gromacs aligned memory
+ *       allocation routines this will always be the case.
+ */
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    // Offset list must be aligned for SIMD DINT32
+    assert(std::size_t(offset) % (GMX_SIMD_DINT32_WIDTH * sizeof(std::int32_t)) == 0);
+
+    for (std::size_t i = 0; i < v0.simdInternal_.size(); i++)
+    {
+        base[align * offset[i]] -= v0.simdInternal_[i];
+        base[align * offset[i] + 1] -= v1.simdInternal_[i];
+        base[align * offset[i] + 2] -= v2.simdInternal_[i];
+    }
+}
+
+
+/*! \brief Expand each element of double SIMD variable into three identical
+ *         consecutive elements in three SIMD outputs.
+ *
+ * \param      scalar    Floating-point input, e.g. [s0 s1 s2 s3] if width=4.
+ * \param[out] triplets0 First output, e.g. [s0 s0 s0 s1] if width=4.
+ * \param[out] triplets1 Second output, e.g. [s1 s1 s2 s2] if width=4.
+ * \param[out] triplets2 Third output, e.g. [s2 s3 s3 s3] if width=4.
+ *
+ * This routine is meant to use for things like scalar-vector multiplication,
+ * where the vectors are stored in a merged format like [x0 y0 z0 x1 y1 z1 ...],
+ * while the scalars are stored as [s0 s1 s2...], and the data cannot easily
+ * be changed to SIMD-friendly layout.
+ *
+ * In this case, load 3 full-width SIMD variables from the vector array (This
+ * will always correspond to GMX_SIMD_DOUBLE_WIDTH triplets),
+ * load a single full-width variable from the scalar array, and
+ * call this routine to expand the data. You can then simply multiply the
+ * first, second and third pair of SIMD variables, and store the three
+ * results back into a suitable vector-format array.
+ */
+static inline void gmx_simdcall expandScalarsToTriplets(SimdDouble  scalar,
+                                                        SimdDouble* triplets0,
+                                                        SimdDouble* triplets1,
+                                                        SimdDouble* triplets2)
+{
+    for (std::size_t i = 0; i < scalar.simdInternal_.size(); i++)
+    {
+        triplets0->simdInternal_[i] = scalar.simdInternal_[i / 3];
+        triplets1->simdInternal_[i] = scalar.simdInternal_[(i + scalar.simdInternal_.size()) / 3];
+        triplets2->simdInternal_[i] = scalar.simdInternal_[(i + 2 * scalar.simdInternal_.size()) / 3];
+    }
+}
+
+
+/*! \brief Load 4 consecutive doubles from each of GMX_SIMD_DOUBLE_WIDTH offsets
+ *         specified by a SIMD integer, transpose into 4 SIMD double variables.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 4 for this routine) the input data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are loaded.
+ * \param      base   Aligned pointer to the start of the memory.
+ * \param      offset SIMD integer type with offsets to the start of each triplet.
+ * \param[out] v0     First component, base[align*offset[i]] for each i.
+ * \param[out] v1     Second component, base[align*offset[i] + 1] for each i.
+ * \param[out] v2     Third component, base[align*offset[i] + 2] for each i.
+ * \param[out] v3     Fourth component, base[align*offset[i] + 3] for each i.
+ *
+ * The floating-point memory locations must be aligned, but only to the smaller
+ * of four elements and the floating-point SIMD width.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This is a special routine primarily intended for loading Gromacs
+ *       table data as efficiently as possible - this is the reason for using
+ *       a SIMD offset index, since the result of the  real-to-integer conversion
+ *       is present in a SIMD register just before calling this routine.
+ */
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const double* base,
+                                                             SimdDInt32    offset,
+                                                             SimdDouble*   v0,
+                                                             SimdDouble*   v1,
+                                                             SimdDouble*   v2,
+                                                             SimdDouble*   v3)
+{
+    // Base pointer must be aligned to the smaller of 4 elements and double SIMD width
+    assert(std::size_t(base) % (std::min(GMX_SIMD_DOUBLE_WIDTH, 4) * sizeof(double)) == 0);
+    // align parameter must also be a multiple of the above alignment requirement
+    assert(align % std::min(GMX_SIMD_DOUBLE_WIDTH, 4) == 0);
+
+    for (std::size_t i = 0; i < v0->simdInternal_.size(); i++)
+    {
+        v0->simdInternal_[i] = base[align * offset.simdInternal_[i]];
+        v1->simdInternal_[i] = base[align * offset.simdInternal_[i] + 1];
+        v2->simdInternal_[i] = base[align * offset.simdInternal_[i] + 2];
+        v3->simdInternal_[i] = base[align * offset.simdInternal_[i] + 3];
+    }
+}
+
+
+/*! \brief Load 2 consecutive doubles from each of GMX_SIMD_DOUBLE_WIDTH offsets
+ *         (unaligned) specified by SIMD integer, transpose into 2 SIMD doubles.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 2 for this routine) the input data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are loaded.
+ * \param      base   Pointer to the start of the memory.
+ * \param      offset SIMD integer type with offsets to the start of each triplet.
+ * \param[out] v0     First component, base[align*offset[i]] for each i.
+ * \param[out] v1     Second component, base[align*offset[i] + 1] for each i.
+ *
+ * Since some SIMD architectures cannot handle any unaligned loads, this routine
+ * is only available if GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE is 1.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This is a special routine primarily intended for loading Gromacs
+ *       table data as efficiently as possible - this is the reason for using
+ *       a SIMD offset index, since the result of the  real-to-integer conversion
+ *       is present in a SIMD register just before calling this routine.
+ */
+template<int align>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    for (std::size_t i = 0; i < v0->simdInternal_.size(); i++)
+    {
+        v0->simdInternal_[i] = base[align * offset.simdInternal_[i]];
+        v1->simdInternal_[i] = base[align * offset.simdInternal_[i] + 1];
+    }
+}
+
+/*! \brief Load 2 consecutive doubles from each of GMX_SIMD_DOUBLE_WIDTH offsets
+ *         specified by a SIMD integer, transpose into 2 SIMD double variables.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 2 for this routine) the input data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are loaded.
+ * \param      base   Aligned pointer to the start of the memory.
+ * \param      offset SIMD integer type with offsets to the start of each triplet.
+ * \param[out] v0     First component, base[align*offset[i]] for each i.
+ * \param[out] v1     Second component, base[align*offset[i] + 1] for each i.
+ *
+ * The floating-point memory locations must be aligned, but only to the smaller
+ * of two elements and the floating-point SIMD width.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This is a special routine primarily intended for loading Gromacs
+ *       table data as efficiently as possible - this is the reason for using
+ *       a SIMD offset index, since the result of the  real-to-integer conversion
+ *       is present in a SIMD register just before calling this routine.
+ */
+template<int align>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    // Base pointer must be aligned to the smaller of 2 elements and double SIMD width
+    assert(std::size_t(base) % (std::min(GMX_SIMD_DOUBLE_WIDTH, 2) * sizeof(double)) == 0);
+    // align parameter must also be a multiple of the above alignment requirement
+    assert(align % std::min(GMX_SIMD_DOUBLE_WIDTH, 2) == 0);
+
+    for (std::size_t i = 0; i < v0->simdInternal_.size(); i++)
+    {
+        v0->simdInternal_[i] = base[align * offset.simdInternal_[i]];
+        v1->simdInternal_[i] = base[align * offset.simdInternal_[i] + 1];
+    }
+}
+
+
+/*! \brief Reduce each of four SIMD doubles, add those values to four consecutive
+ *         doubles in memory, return sum.
+ *
+ * \param m   Pointer to memory where four doubles should be incremented
+ * \param v0  SIMD variable whose sum should be added to m[0]
+ * \param v1  SIMD variable whose sum should be added to m[1]
+ * \param v2  SIMD variable whose sum should be added to m[2]
+ * \param v3  SIMD variable whose sum should be added to m[3]
+ *
+ * \return Sum of all elements in the four SIMD variables.
+ *
+ * The pointer m must be aligned to the smaller of four elements and the
+ * floating-point SIMD width.
+ *
+ * \note This is a special routine intended for the Gromacs nonbonded kernels.
+ * It is used in the epilogue of the outer loop, where the variables will
+ * contain unrolled forces for one outer-loop-particle each, corresponding to
+ * a single coordinate (i.e, say, four x-coordinate force variables). These
+ * should be summed and added to the force array in memory. Since we always work
+ * with contiguous SIMD-layout , we can use efficient aligned loads/stores.
+ * When calculating the virial, we also need the total sum of all forces for
+ * each coordinate. This is provided as the return value. For routines that
+ * do not need these, this extra code will be optimized away completely if you
+ * just ignore the return value (Checked with gcc-4.9.1 and clang-3.6 for AVX).
+ */
+static inline double gmx_simdcall
+reduceIncr4ReturnSum(double* m, SimdDouble v0, SimdDouble v1, SimdDouble v2, SimdDouble v3)
+{
+    double sum[4]; // Note that the 4 here corresponds to the 4 m-elements, not any SIMD width
+
+    // Make sure the memory pointer is aligned to the smaller of 4 elements and double SIMD width
+    assert(std::size_t(m) % (std::min(GMX_SIMD_DOUBLE_WIDTH, 4) * sizeof(double)) == 0);
+
+    sum[0] = reduce(v0);
+    sum[1] = reduce(v1);
+    sum[2] = reduce(v2);
+    sum[3] = reduce(v3);
+
+    m[0] += sum[0];
+    m[1] += sum[1];
+    m[2] += sum[2];
+    m[3] += sum[3];
+
+    return sum[0] + sum[1] + sum[2] + sum[3];
+}
+
+
+/*! \}
+ *
+ * \name Higher-level SIMD utilities accessing partial (half-width) SIMD doubles.
+ *
+ * See the single-precision versions for documentation. Since double precision
+ * is typically half the width of single, this double version is likely only
+ * useful with 512-bit and larger implementations.
+ *
+ * \{
+ */
+
+/*! \brief Load low & high parts of SIMD double from different locations.
+ *
+ * \param m0 Pointer to memory aligned to half SIMD width.
+ * \param m1 Pointer to memory aligned to half SIMD width.
+ *
+ * \return SIMD variable with low part loaded from m0, high from m1.
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE is 1.
+ */
+static inline SimdDouble gmx_simdcall loadDualHsimd(const double* m0, const double* m1)
+{
+    SimdDouble a;
+
+    // Make sure the memory pointers are aligned to half double SIMD width
+    assert(std::size_t(m0) % (GMX_SIMD_DOUBLE_WIDTH / 2 * sizeof(double)) == 0);
+    assert(std::size_t(m1) % (GMX_SIMD_DOUBLE_WIDTH / 2 * sizeof(double)) == 0);
+
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    {
+        a.simdInternal_[i]                              = m0[i];
+        a.simdInternal_[a.simdInternal_.size() / 2 + i] = m1[i];
+    }
+    return a;
+}
+
+/*! \brief Load half-SIMD-width double data, spread to both halves.
+ *
+ * \param m Pointer to memory aligned to half SIMD width.
+ *
+ * \return SIMD variable with both halves loaded from m..
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE is 1.
+ */
+static inline SimdDouble gmx_simdcall loadDuplicateHsimd(const double* m)
+{
+    SimdDouble a;
+
+    // Make sure the memory pointer is aligned
+    assert(std::size_t(m) % (GMX_SIMD_DOUBLE_WIDTH / 2 * sizeof(double)) == 0);
+
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    {
+        a.simdInternal_[i]                              = m[i];
+        a.simdInternal_[a.simdInternal_.size() / 2 + i] = a.simdInternal_[i];
+    }
+    return a;
+}
+
+/*! \brief Load two doubles, spread 1st in low half, 2nd in high half.
+ *
+ * \param m Pointer to two adjacent double values.
+ *
+ * \return SIMD variable where all elements in the low half have been set
+ *         to m[0], and all elements in high half to m[1].
+ *
+ * \note This routine always loads two values and sets the halves separately.
+ *       If you want to set all elements to the same value, simply use
+ *       the standard (non-half-SIMD) operations.
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE is 1.
+ */
+static inline SimdDouble gmx_simdcall loadU1DualHsimd(const double* m)
+{
+    SimdDouble a;
+
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    {
+        a.simdInternal_[i]                              = m[0];
+        a.simdInternal_[a.simdInternal_.size() / 2 + i] = m[1];
+    }
+    return a;
+}
+
+
+/*! \brief Store low & high parts of SIMD double to different locations.
+ *
+ * \param m0 Pointer to memory aligned to half SIMD width.
+ * \param m1 Pointer to memory aligned to half SIMD width.
+ * \param a  SIMD variable. Low half should be stored to m0, high to m1.
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE is 1.
+ */
+static inline void gmx_simdcall storeDualHsimd(double* m0, double* m1, SimdDouble a)
+{
+    // Make sure the memory pointers are aligned to half double SIMD width
+    assert(std::size_t(m0) % (GMX_SIMD_DOUBLE_WIDTH / 2 * sizeof(double)) == 0);
+    assert(std::size_t(m1) % (GMX_SIMD_DOUBLE_WIDTH / 2 * sizeof(double)) == 0);
+
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    {
+        m0[i] = a.simdInternal_[i];
+        m1[i] = a.simdInternal_[a.simdInternal_.size() / 2 + i];
+    }
+}
+
+/*! \brief Add each half of SIMD variable to separate memory adresses
+ *
+ * \param m0 Pointer to memory aligned to half SIMD width.
+ * \param m1 Pointer to memory aligned to half SIMD width.
+ * \param a  SIMD variable. Lower half will be added to m0, upper half to m1.
+ *
+ * The memory must be aligned to half SIMD width.
+ *
+ * \note The updated m0 value is written before m1 is read from memory, so
+ *       the result will be correct even if the memory regions overlap.
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE is 1.
+ */
+static inline void gmx_simdcall incrDualHsimd(double* m0, double* m1, SimdDouble a)
+{
+    // Make sure the memory pointer is aligned to half double SIMD width
+    assert(std::size_t(m0) % (GMX_SIMD_DOUBLE_WIDTH / 2 * sizeof(double)) == 0);
+    assert(std::size_t(m1) % (GMX_SIMD_DOUBLE_WIDTH / 2 * sizeof(double)) == 0);
+
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    {
+        m0[i] += a.simdInternal_[i];
+    }
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    {
+        m1[i] += a.simdInternal_[a.simdInternal_.size() / 2 + i];
+    }
+}
+
+/*! \brief Add the two halves of three SIMD doubles, subtract the sum from
+ *         three half-SIMD-width consecutive doubles in memory.
+ *
+ * \param m  half-width aligned memory, from which sum of the halves will be subtracted.
+ * \param a0 SIMD variable. Upper & lower halves will first be added.
+ * \param a1 SIMD variable. Upper & lower halves will second be added.
+ * \param a2 SIMD variable. Upper & lower halves will third be added.
+ *
+ * If the SIMD width is 8 and the vectors contain [a0 b0 c0 d0 e0 f0 g0 h0],
+ * [a1 b1 c1 d1 e1 f1 g1 g1] and [a2 b2 c2 d2 e2 f2 g2 h2], the
+ * memory will be modified to [m[0]-(a0+e0) m[1]-(b0+f0) m[2]-(c0+g0) m[3]-(d0+h0)
+ *                             m[4]-(a1+e1) m[5]-(b1+f1) m[6]-(c1+g1) m[7]-(d1+h1)
+ *                             m[8]-(a2+e2) m[9]-(b2+f2) m[10]-(c2+g2) m[11]-(d2+h2)].
+ *
+ * The memory must be aligned to half SIMD width.
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE is 1.
+ */
+static inline void gmx_simdcall decr3Hsimd(double* m, SimdDouble a0, SimdDouble a1, SimdDouble a2)
+{
+    assert(std::size_t(m) % (GMX_SIMD_DOUBLE_WIDTH / 2 * sizeof(double)) == 0);
+    for (std::size_t i = 0; i < a0.simdInternal_.size() / 2; i++)
+    {
+        m[i] -= a0.simdInternal_[i] + a0.simdInternal_[a0.simdInternal_.size() / 2 + i];
+    }
+    for (std::size_t i = 0; i < a1.simdInternal_.size() / 2; i++)
+    {
+        m[a1.simdInternal_.size() / 2 + i] -=
+                a1.simdInternal_[i] + a1.simdInternal_[a1.simdInternal_.size() / 2 + i];
+    }
+    for (std::size_t i = 0; i < a2.simdInternal_.size() / 2; i++)
+    {
+        m[a2.simdInternal_.size() + i] -=
+                a2.simdInternal_[i] + a2.simdInternal_[a2.simdInternal_.size() / 2 + i];
+    }
+}
+
+
+/*! \brief Load 2 consecutive doubles from each of GMX_SIMD_DOUBLE_WIDTH/2 offsets,
+ *         transpose into SIMD double (low half from base0, high from base1).
+ *
+ * \tparam     align  Alignment of the storage, i.e. the distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of output components
+ *                    the data is packed without padding. This must be a
+ *                    multiple of the alignment to keep all data aligned.
+ * \param      base0  Pointer to base of first aligned memory
+ * \param      base1  Pointer to base of second aligned memory
+ * \param      offset Offset to the start of each pair
+ * \param[out] v0     1st element in each pair, base0 in low and base1 in high half.
+ * \param[out] v1     2nd element in each pair, base0 in low and base1 in high half.
+ *
+ * The offset array should be of half the SIMD width length, so it corresponds
+ * to the half-SIMD-register operations. This also means it must be aligned
+ * to half the integer SIMD width (i.e., GMX_SIMD_DINT32_WIDTH/2).
+ *
+ * The floating-point memory locations must be aligned, but only to the smaller
+ * of two elements and the floating-point SIMD width.
+ *
+ * This routine is primarily designed to load nonbonded parameters in the
+ * kernels. It is the equivalent of the full-width routine
+ * gatherLoadTranspose(), but just
+ * as the other hsimd routines it will pick half-SIMD-width data from base0
+ * and put in the lower half, while the upper half comes from base1.
+ *
+ * For an example, assume the SIMD width is 8, align is 2, that
+ * base0 is [A0 A1 B0 B1 C0 C1 D0 D1 ...], and base1 [E0 E1 F0 F1 G0 G1 H0 H1...].
+ *
+ * Then we will get v0 as [A0 B0 C0 D0 E0 F0 G0 H0] and v1 as [A1 B1 C1 D1 E1 F1 G1 H1].
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE is 1.
+ */
+template<int align>
+static inline void gmx_simdcall gatherLoadTransposeHsimd(const double* base0,
+                                                         const double* base1,
+                                                         std::int32_t  offset[],
+                                                         SimdDouble*   v0,
+                                                         SimdDouble*   v1)
+{
+    // Offset list must be aligned for half SIMD DINT32 width
+    assert(std::size_t(offset) % (GMX_SIMD_DINT32_WIDTH / 2 * sizeof(std::int32_t)) == 0);
+    // base pointers must be aligned to the smaller of 2 elements and double SIMD width
+    assert(std::size_t(base0) % (std::min(GMX_SIMD_DOUBLE_WIDTH, 2) * sizeof(double)) == 0);
+    assert(std::size_t(base1) % (std::min(GMX_SIMD_DOUBLE_WIDTH, 2) * sizeof(double)) == 0);
+    // alignment parameter must be also be multiple of the above required alignment
+    assert(align % std::min(GMX_SIMD_DOUBLE_WIDTH, 2) == 0);
+
+    for (std::size_t i = 0; i < v0->simdInternal_.size() / 2; i++)
+    {
+        v0->simdInternal_[i]                                = base0[align * offset[i]];
+        v1->simdInternal_[i]                                = base0[align * offset[i] + 1];
+        v0->simdInternal_[v0->simdInternal_.size() / 2 + i] = base1[align * offset[i]];
+        v1->simdInternal_[v1->simdInternal_.size() / 2 + i] = base1[align * offset[i] + 1];
+    }
+}
+
+
+/*! \brief Reduce the 4 half-SIMD-with doubles in 2 SIMD variables (sum halves),
+ *         increment four consecutive doubles in memory, return sum.
+ *
+ * \param m    Pointer to memory where the four values should be incremented
+ * \param v0   Variable whose half-SIMD sums should be added to m[0]/m[1], respectively.
+ * \param v1   Variable whose half-SIMD sums should be added to m[2]/m[3], respectively.
+ *
+ * \return Sum of all elements in the four SIMD variables.
+ *
+ * The pointer m must be aligned, but only to the smaller
+ * of four elements and the floating-point SIMD width.
+ *
+ * \note This is the half-SIMD-width version of
+ *      reduceIncr4ReturnSum(). The only difference is that the
+ *      four half-SIMD inputs needed are present in the low/high halves of the
+ *      two SIMD arguments.
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE is 1.
+ */
+static inline double gmx_simdcall reduceIncr4ReturnSumHsimd(double* m, SimdDouble v0, SimdDouble v1)
+{
+    // The 4 here corresponds to the 4 elements in memory, not any SIMD width
+    double sum[4] = { 0.0, 0.0, 0.0, 0.0 };
+
+    for (std::size_t i = 0; i < v0.simdInternal_.size() / 2; i++)
+    {
+        sum[0] += v0.simdInternal_[i];
+        sum[1] += v0.simdInternal_[v0.simdInternal_.size() / 2 + i];
+        sum[2] += v1.simdInternal_[i];
+        sum[3] += v1.simdInternal_[v1.simdInternal_.size() / 2 + i];
+    }
+
+    // Make sure the memory pointer is aligned to the smaller of 4 elements and double SIMD width
+    assert(std::size_t(m) % (std::min(GMX_SIMD_DOUBLE_WIDTH, 4) * sizeof(double)) == 0);
+
+    m[0] += sum[0];
+    m[1] += sum[1];
+    m[2] += sum[2];
+    m[3] += sum[3];
+
+    return sum[0] + sum[1] + sum[2] + sum[3];
+}
+
+#if GMX_SIMD_DOUBLE_WIDTH > 8 || defined DOXYGEN
+/*! \brief Load N doubles and duplicate them 4 times each.
+ *
+ * \param m Pointer to unaligned memory
+ *
+ * \return SIMD variable with N doubles from m duplicated 4x.
+ *
+ * Available if \ref GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE is 1.
+ * N is GMX_SIMD_DOUBLE_WIDTH/4. Duplicated values are
+ * contigous and different values are 4 positions in SIMD
+ * apart.
+ */
+static inline SimdDouble gmx_simdcall loadUNDuplicate4(const double* m)
+{
+    SimdDouble a;
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 4; i++)
+    {
+        a.simdInternal_[i * 4]     = m[i];
+        a.simdInternal_[i * 4 + 1] = m[i];
+        a.simdInternal_[i * 4 + 2] = m[i];
+        a.simdInternal_[i * 4 + 3] = m[i];
+    }
+    return a;
+}
+
+/*! \brief Load 4 doubles and duplicate them N times each.
+ *
+ * \param m Pointer to memory aligned to 4 doubles
+ *
+ * \return SIMD variable with 4 doubles from m duplicated Nx.
+ *
+ * Available if \ref GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE is 1.
+ * N is GMX_SIMD_DOUBLE_WIDTH/4. Different values are
+ * contigous and same values are 4 positions in SIMD
+ * apart.
+ */
+static inline SimdDouble gmx_simdcall load4DuplicateN(const double* m)
+{
+    SimdDouble a;
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 4; i++)
+    {
+        a.simdInternal_[i * 4]     = m[0];
+        a.simdInternal_[i * 4 + 1] = m[1];
+        a.simdInternal_[i * 4 + 2] = m[2];
+        a.simdInternal_[i * 4 + 3] = m[3];
+    }
+    return a;
+}
+#endif
+
+#if GMX_SIMD_DOUBLE_WIDTH >= 8 || defined DOXYGEN
+/*! \brief Load doubles in blocks of 4 at fixed offsets
+ *
+ * \param m Pointer to unaligned memory
+ * \param offset Offset in memory between input blocks of 4
+ *
+ * \return SIMD variable with doubles from m.
+ *
+ * Available if \ref GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE is 1.
+ * Blocks of 4 doubles are loaded from m+n*offset where n
+ * is the n-th block of 4 doubles.
+ */
+static inline SimdDouble gmx_simdcall loadU4NOffset(const double* m, int offset)
+{
+    SimdDouble a;
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 4; i++)
+    {
+        a.simdInternal_[i * 4]     = m[offset * i + 0];
+        a.simdInternal_[i * 4 + 1] = m[offset * i + 1];
+        a.simdInternal_[i * 4 + 2] = m[offset * i + 2];
+        a.simdInternal_[i * 4 + 3] = m[offset * i + 3];
+    }
+    return a;
+}
+#endif
+
+
+/*! \} */
+
+/*! \} */
+/*! \endcond */
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_REFERENCE_UTIL_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_reference/impl_reference_util_float.h b/src/include/gromacs/simd/impl_reference/impl_reference_util_float.h
new file mode 100644 (file)
index 0000000..c7e52fb
--- /dev/null
@@ -0,0 +1,1005 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_REFERENCE_UTIL_FLOAT_H
+#define GMX_SIMD_IMPL_REFERENCE_UTIL_FLOAT_H
+
+/*! \libinternal \file
+ *
+ * \brief Reference impl., higher-level single prec. SIMD utility functions
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \ingroup module_simd
+ */
+
+/* Avoid adding dependencies on the rest of GROMACS here (e.g. gmxassert.h)
+ * since we want to be able run the low-level SIMD implementations independently
+ * in simulators for new hardware.
+ */
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <algorithm>
+
+#include "impl_reference_definitions.h"
+#include "impl_reference_simd_float.h"
+
+namespace gmx
+{
+
+/*! \cond libapi */
+/*! \addtogroup module_simd */
+/*! \{ */
+
+/*! \name Higher-level SIMD utility functions, single precision.
+ *
+ * These include generic functions to work with triplets of data, typically
+ * coordinates, and a few utility functions to load and update data in the
+ * nonbonded kernels.
+ * These functions should be available on all implementations, although
+ * some wide SIMD implementations (width>=8) also provide special optional
+ * versions to work with half or quarter registers to improve the performance
+ * in the nonbonded kernels.
+ *
+ * \{
+ */
+
+/*! \brief Load 4 consecutive floats from each of GMX_SIMD_FLOAT_WIDTH offsets,
+ *         and transpose into 4 SIMD float variables.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 4 for this routine) the input data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are loaded.
+ * \param      base   Pointer to the start of the memory area
+ * \param      offset Array with offsets to the start of each data point.
+ * \param[out] v0     1st component of data, base[align*offset[i]] for each i.
+ * \param[out] v1     2nd component of data, base[align*offset[i] + 1] for each i.
+ * \param[out] v2     3rd component of data, base[align*offset[i] + 2] for each i.
+ * \param[out] v3     4th component of data, base[align*offset[i] + 3] for each i.
+ *
+ * The floating-point memory locations must be aligned, but only to the smaller
+ * of four elements and the floating-point SIMD width.
+ *
+ * The offset memory must be aligned to GMX_SIMD_DINT32_WIDTH.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ */
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const float*       base,
+                                                    const std::int32_t offset[],
+                                                    SimdFloat*         v0,
+                                                    SimdFloat*         v1,
+                                                    SimdFloat*         v2,
+                                                    SimdFloat*         v3)
+{
+    // Offset list must be aligned for SIMD FINT32
+    assert(std::size_t(offset) % (GMX_SIMD_FINT32_WIDTH * sizeof(std::int32_t)) == 0);
+    // Base pointer must be aligned to the smaller of 4 elements and float SIMD width
+    assert(std::size_t(base) % (std::min(GMX_SIMD_FLOAT_WIDTH, 4) * sizeof(float)) == 0);
+    // align parameter must also be a multiple of the above alignment requirement
+    assert(align % std::min(GMX_SIMD_FLOAT_WIDTH, 4) == 0);
+
+    for (std::size_t i = 0; i < v0->simdInternal_.size(); i++)
+    {
+        v0->simdInternal_[i] = base[align * offset[i]];
+        v1->simdInternal_[i] = base[align * offset[i] + 1];
+        v2->simdInternal_[i] = base[align * offset[i] + 2];
+        v3->simdInternal_[i] = base[align * offset[i] + 3];
+    }
+}
+
+/*! \brief Load 2 consecutive floats from each of GMX_SIMD_FLOAT_WIDTH offsets,
+ *         and transpose into 2 SIMD float variables.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 2 for this routine) the input data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are loaded.
+ * \param      base   Pointer to the start of the memory area
+ * \param      offset Array with offsets to the start of each data point.
+ * \param[out] v0     1st component of data, base[align*offset[i]] for each i.
+ * \param[out] v1     2nd component of data, base[align*offset[i] + 1] for each i.
+ *
+ * The floating-point memory locations must be aligned, but only to the smaller
+ * of two elements and the floating-point SIMD width.
+ *
+ * The offset memory must be aligned to GMX_SIMD_FINT32_WIDTH.
+ *
+ * To achieve the best possible performance, you should store your data with
+ * alignment \ref c_simdBestPairAlignmentFloat in single, or
+ * \ref c_simdBestPairAlignmentDouble in double.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ */
+template<int align>
+static inline void gmx_simdcall
+gatherLoadTranspose(const float* base, const std::int32_t offset[], SimdFloat* v0, SimdFloat* v1)
+{
+    // Offset list must be aligned for SIMD FINT32
+    assert(std::size_t(offset) % (GMX_SIMD_FINT32_WIDTH * sizeof(std::int32_t)) == 0);
+    // Base pointer must be aligned to the smaller of 2 elements and float SIMD width
+    assert(std::size_t(base) % (std::min(GMX_SIMD_FLOAT_WIDTH, 2) * sizeof(float)) == 0);
+    // align parameter must also be a multiple of the above alignment requirement
+    assert(align % std::min(GMX_SIMD_FLOAT_WIDTH, 2) == 0);
+
+    for (std::size_t i = 0; i < v0->simdInternal_.size(); i++)
+    {
+        v0->simdInternal_[i] = base[align * offset[i]];
+        v1->simdInternal_[i] = base[align * offset[i] + 1];
+    }
+}
+
+
+/*! \brief Best alignment to use for aligned pairs of float data.
+ *
+ *  The routines to load and transpose data will work with a wide range of
+ *  alignments, but some might be faster than others, depending on the load
+ *  instructions available in the hardware. This specifies the best
+ *  alignment for each implementation when working with pairs of data.
+ *
+ *  To allow each architecture to use the most optimal form, we use a constant
+ *  that code outside the SIMD module should use to store things properly. It
+ *  must be at least 2. For example, a value of 2 means the two parameters A & B
+ *  are stored as [A0 B0 A1 B1] while align-4 means [A0 B0 - - A1 B1 - -].
+ *
+ *  This alignment depends on the efficiency of partial-register load/store
+ *  operations, and will depend on the architecture.
+ */
+static const int c_simdBestPairAlignmentFloat = 2;
+
+
+/*! \brief Load 3 consecutive floats from each of GMX_SIMD_FLOAT_WIDTH offsets,
+ *         and transpose into 3 SIMD float variables.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 3 for this routine) the input data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are loaded.
+ * \param      base   Pointer to the start of the memory area
+ * \param      offset Array with offsets to the start of each data point.
+ * \param[out] v0     1st component of data, base[align*offset[i]] for each i.
+ * \param[out] v1     2nd component of data, base[align*offset[i] + 1] for each i.
+ * \param[out] v2     3rd component of data, base[align*offset[i] + 2] for each i.
+ *
+ * This function can work with both aligned (better performance) and unaligned
+ * memory. When the align parameter is not a power-of-two (align==3 would be normal
+ * for packed atomic coordinates) the memory obviously cannot be aligned, and
+ * we account for this.
+ * However, in the case where align is a power-of-two, we assume the base pointer
+ * also has the same alignment, which will enable many platforms to use faster
+ * aligned memory load operations.
+ * An easy way to think of this is that each triplet of data in memory must be
+ * aligned to the align parameter you specify when it's a power-of-two.
+ *
+ * The offset memory must always be aligned to GMX_SIMD_FINT32_WIDTH, since this
+ * enables us to use SIMD loads and gather operations on platforms that support it.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This routine uses a normal array for the offsets, since we typically
+ *       load this data from memory. On the architectures we have tested this
+ *       is faster even when a SIMD integer datatype is present.
+ * \note To improve performance, this function might use full-SIMD-width
+ *       unaligned loads. This means you need to ensure the memory is padded
+ *       at the end, so we always can load GMX_SIMD_REAL_WIDTH elements
+ *       starting at the last offset. If you use the Gromacs aligned memory
+ *       allocation routines this will always be the case.
+ */
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const float*       base,
+                                                     const std::int32_t offset[],
+                                                     SimdFloat*         v0,
+                                                     SimdFloat*         v1,
+                                                     SimdFloat*         v2)
+{
+    // Offset list must be aligned for SIMD FINT32
+    assert(std::size_t(offset) % (GMX_SIMD_FINT32_WIDTH * sizeof(std::int32_t)) == 0);
+
+    for (std::size_t i = 0; i < v0->simdInternal_.size(); i++)
+    {
+        v0->simdInternal_[i] = base[align * offset[i]];
+        v1->simdInternal_[i] = base[align * offset[i] + 1];
+        v2->simdInternal_[i] = base[align * offset[i] + 2];
+    }
+}
+
+
+/*! \brief Transpose and store 3 SIMD floats to 3 consecutive addresses at
+ *         GMX_SIMD_FLOAT_WIDTH offsets.
+ *
+ * \tparam     align  Alignment of the memory to which we write, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 3 for this routine) the output data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are written.
+ * \param[out] base   Pointer to the start of the memory area
+ * \param      offset Aligned array with offsets to the start of each triplet.
+ * \param      v0     1st component of triplets, written to base[align*offset[i]].
+ * \param      v1     2nd component of triplets, written to base[align*offset[i] + 1].
+ * \param      v2     3rd component of triplets, written to base[align*offset[i] + 2].
+ *
+ * This function can work with both aligned (better performance) and unaligned
+ * memory. When the align parameter is not a power-of-two (align==3 would be normal
+ * for packed atomic coordinates) the memory obviously cannot be aligned, and
+ * we account for this.
+ * However, in the case where align is a power-of-two, we assume the base pointer
+ * also has the same alignment, which will enable many platforms to use faster
+ * aligned memory store operations.
+ * An easy way to think of this is that each triplet of data in memory must be
+ * aligned to the align parameter you specify when it's a power-of-two.
+ *
+ * The offset memory must always be aligned to GMX_SIMD_FINT32_WIDTH, since this
+ * enables us to use SIMD loads and gather operations on platforms that support it.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This routine uses a normal array for the offsets, since we typically
+ *       load the data from memory. On the architectures we have tested this
+ *       is faster even when a SIMD integer datatype is present.
+ */
+template<int align>
+static inline void gmx_simdcall
+transposeScatterStoreU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    // Offset list must be aligned for SIMD FINT32
+    assert(std::size_t(offset) % (GMX_SIMD_FINT32_WIDTH * sizeof(std::int32_t)) == 0);
+
+    for (std::size_t i = 0; i < v0.simdInternal_.size(); i++)
+    {
+        base[align * offset[i]]     = v0.simdInternal_[i];
+        base[align * offset[i] + 1] = v1.simdInternal_[i];
+        base[align * offset[i] + 2] = v2.simdInternal_[i];
+    }
+}
+
+
+/*! \brief Transpose and add 3 SIMD floats to 3 consecutive addresses at
+ *         GMX_SIMD_FLOAT_WIDTH offsets.
+ *
+ * \tparam     align  Alignment of the memory to which we write, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 3 for this routine) the output data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are incremented.
+ * \param[out] base   Pointer to the start of the memory area
+ * \param      offset Aligned array with offsets to the start of each triplet.
+ * \param      v0     1st component of triplets, added to base[align*offset[i]].
+ * \param      v1     2nd component of triplets, added to base[align*offset[i] + 1].
+ * \param      v2     3rd component of triplets, added to base[align*offset[i] + 2].
+ *
+ * This function can work with both aligned (better performance) and unaligned
+ * memory. When the align parameter is not a power-of-two (align==3 would be normal
+ * for packed atomic coordinates) the memory obviously cannot be aligned, and
+ * we account for this.
+ * However, in the case where align is a power-of-two, we assume the base pointer
+ * also has the same alignment, which will enable many platforms to use faster
+ * aligned memory load/store operations.
+ * An easy way to think of this is that each triplet of data in memory must be
+ * aligned to the align parameter you specify when it's a power-of-two.
+ *
+ * The offset memory must always be aligned to GMX_SIMD_FINT32_WIDTH, since this
+ * enables us to use SIMD loads and gather operations on platforms that support it.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This routine uses a normal array for the offsets, since we typically
+ *       load the data from memory. On the architectures we have tested this
+ *       is faster even when a SIMD integer datatype is present.
+ * \note To improve performance, this function might use full-SIMD-width
+ *       unaligned load/store, and add 0.0 to the extra elements.
+ *       This means you need to ensure the memory is padded
+ *       at the end, so we always can load GMX_SIMD_REAL_WIDTH elements
+ *       starting at the last offset. If you use the Gromacs aligned memory
+ *       allocation routines this will always be the case.
+ */
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    // Offset list must be aligned for SIMD FINT32
+    assert(std::size_t(offset) % (GMX_SIMD_FINT32_WIDTH * sizeof(std::int32_t)) == 0);
+
+    for (std::size_t i = 0; i < v0.simdInternal_.size(); i++)
+    {
+        base[align * offset[i]] += v0.simdInternal_[i];
+        base[align * offset[i] + 1] += v1.simdInternal_[i];
+        base[align * offset[i] + 2] += v2.simdInternal_[i];
+    }
+}
+
+
+/*! \brief Transpose and subtract 3 SIMD floats to 3 consecutive addresses at
+ *         GMX_SIMD_FLOAT_WIDTH offsets.
+ *
+ * \tparam     align  Alignment of the memory to which we write, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 3 for this routine) the output data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are decremented.
+ * \param[out] base    Pointer to start of memory.
+ * \param      offset  Aligned array with offsets to the start of each triplet.
+ * \param      v0      1st component, subtracted from base[align*offset[i]]
+ * \param      v1      2nd component, subtracted from base[align*offset[i]+1]
+ * \param      v2      3rd component, subtracted from base[align*offset[i]+2]
+ *
+ * This function can work with both aligned (better performance) and unaligned
+ * memory. When the align parameter is not a power-of-two (align==3 would be normal
+ * for packed atomic coordinates) the memory obviously cannot be aligned, and
+ * we account for this.
+ * However, in the case where align is a power-of-two, we assume the base pointer
+ * also has the same alignment, which will enable many platforms to use faster
+ * aligned memory load/store operations.
+ * An easy way to think of this is that each triplet of data in memory must be
+ * aligned to the align parameter you specify when it's a power-of-two.
+ *
+ * The offset memory must always be aligned to GMX_SIMD_FINT32_WIDTH, since this
+ * enables us to use SIMD loads and gather operations on platforms that support it.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This routine uses a normal array for the offsets, since we typically
+ *       load the data from memory. On the architectures we have tested this
+ *       is faster even when a SIMD integer datatype is present.
+ * \note To improve performance, this function might use full-SIMD-width
+ *       unaligned load/store, and subtract 0.0 from the extra elements.
+ *       This means you need to ensure the memory is padded
+ *       at the end, so we always can load GMX_SIMD_REAL_WIDTH elements
+ *       starting at the last offset. If you use the Gromacs aligned memory
+ *       allocation routines this will always be the case.
+ */
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    // Offset list must be aligned for SIMD FINT32
+    assert(std::size_t(offset) % (GMX_SIMD_FINT32_WIDTH * sizeof(std::int32_t)) == 0);
+
+    for (std::size_t i = 0; i < v0.simdInternal_.size(); i++)
+    {
+        base[align * offset[i]] -= v0.simdInternal_[i];
+        base[align * offset[i] + 1] -= v1.simdInternal_[i];
+        base[align * offset[i] + 2] -= v2.simdInternal_[i];
+    }
+}
+
+
+/*! \brief Expand each element of float SIMD variable into three identical
+ *         consecutive elements in three SIMD outputs.
+ *
+ * \param      scalar    Floating-point input, e.g. [s0 s1 s2 s3] if width=4.
+ * \param[out] triplets0 First output, e.g. [s0 s0 s0 s1] if width=4.
+ * \param[out] triplets1 Second output, e.g. [s1 s1 s2 s2] if width=4.
+ * \param[out] triplets2 Third output, e.g. [s2 s3 s3 s3] if width=4.
+ *
+ * This routine is meant to use for things like scalar-vector multiplication,
+ * where the vectors are stored in a merged format like [x0 y0 z0 x1 y1 z1 ...],
+ * while the scalars are stored as [s0 s1 s2...], and the data cannot easily
+ * be changed to SIMD-friendly layout.
+ *
+ * In this case, load 3 full-width SIMD variables from the vector array (This
+ * will always correspond to GMX_SIMD_FLOAT_WIDTH triplets),
+ * load a single full-width variable from the scalar array, and
+ * call this routine to expand the data. You can then simply multiply the
+ * first, second and third pair of SIMD variables, and store the three
+ * results back into a suitable vector-format array.
+ */
+static inline void gmx_simdcall expandScalarsToTriplets(SimdFloat  scalar,
+                                                        SimdFloat* triplets0,
+                                                        SimdFloat* triplets1,
+                                                        SimdFloat* triplets2)
+{
+    for (std::size_t i = 0; i < scalar.simdInternal_.size(); i++)
+    {
+        triplets0->simdInternal_[i] = scalar.simdInternal_[i / 3];
+        triplets1->simdInternal_[i] = scalar.simdInternal_[(i + scalar.simdInternal_.size()) / 3];
+        triplets2->simdInternal_[i] = scalar.simdInternal_[(i + 2 * scalar.simdInternal_.size()) / 3];
+    }
+}
+
+/*! \brief Load 4 consecutive floats from each of GMX_SIMD_FLOAT_WIDTH offsets
+ *         specified by a SIMD integer, transpose into 4 SIMD float variables.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 4 for this routine) the input data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are loaded.
+ * \param      base   Aligned pointer to the start of the memory.
+ * \param      offset SIMD integer type with offsets to the start of each triplet.
+ * \param[out] v0     First component, base[align*offset[i]] for each i.
+ * \param[out] v1     Second component, base[align*offset[i] + 1] for each i.
+ * \param[out] v2     Third component, base[align*offset[i] + 2] for each i.
+ * \param[out] v3     Fourth component, base[align*offset[i] + 3] for each i.
+ *
+ * The floating-point memory locations must be aligned, but only to the smaller
+ * of four elements and the floating-point SIMD width.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This is a special routine primarily intended for loading Gromacs
+ *       table data as efficiently as possible - this is the reason for using
+ *       a SIMD offset index, since the result of the  real-to-integer conversion
+ *       is present in a SIMD register just before calling this routine.
+ */
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const float* base,
+                                                             SimdFInt32   offset,
+                                                             SimdFloat*   v0,
+                                                             SimdFloat*   v1,
+                                                             SimdFloat*   v2,
+                                                             SimdFloat*   v3)
+{
+    // Base pointer must be aligned to the smaller of 4 elements and float SIMD width
+    assert(std::size_t(base) % (std::min(GMX_SIMD_FLOAT_WIDTH, 4) * sizeof(float)) == 0);
+    // align parameter must also be a multiple of the above alignment requirement
+    assert(align % std::min(GMX_SIMD_FLOAT_WIDTH, 4) == 0);
+
+    for (std::size_t i = 0; i < v0->simdInternal_.size(); i++)
+    {
+        v0->simdInternal_[i] = base[align * offset.simdInternal_[i]];
+        v1->simdInternal_[i] = base[align * offset.simdInternal_[i] + 1];
+        v2->simdInternal_[i] = base[align * offset.simdInternal_[i] + 2];
+        v3->simdInternal_[i] = base[align * offset.simdInternal_[i] + 3];
+    }
+}
+
+
+/*! \brief Load 2 consecutive floats from each of GMX_SIMD_FLOAT_WIDTH offsets
+ *         (unaligned) specified by SIMD integer, transpose into 2 SIMD floats.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 2 for this routine) the input data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are loaded.
+ * \param      base   Pointer to the start of the memory.
+ * \param      offset SIMD integer type with offsets to the start of each triplet.
+ * \param[out] v0     First component, base[align*offset[i]] for each i.
+ * \param[out] v1     Second component, base[align*offset[i] + 1] for each i.
+ *
+ * Since some SIMD architectures cannot handle any unaligned loads, this routine
+ * is only available if GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE is 1.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This is a special routine primarily intended for loading Gromacs
+ *       table data as efficiently as possible - this is the reason for using
+ *       a SIMD offset index, since the result of the  real-to-integer conversion
+ *       is present in a SIMD register just before calling this routine.
+ */
+template<int align>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v0, SimdFloat* v1)
+{
+    for (std::size_t i = 0; i < v0->simdInternal_.size(); i++)
+    {
+        v0->simdInternal_[i] = base[align * offset.simdInternal_[i]];
+        v1->simdInternal_[i] = base[align * offset.simdInternal_[i] + 1];
+    }
+}
+
+/*! \brief Load 2 consecutive floats from each of GMX_SIMD_FLOAT_WIDTH offsets
+ *         specified by a SIMD integer, transpose into 2 SIMD float variables.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of SIMD variables
+ *                    (i.e., 2 for this routine) the input data is packed without
+ *                    padding in memory. See the SIMD parameters for exactly
+ *                    what memory positions are loaded.
+ * \param      base   Aligned pointer to the start of the memory.
+ * \param      offset SIMD integer type with offsets to the start of each triplet.
+ * \param[out] v0     First component, base[align*offset[i]] for each i.
+ * \param[out] v1     Second component, base[align*offset[i] + 1] for each i.
+ *
+ * The floating-point memory locations must be aligned, but only to the smaller
+ * of two elements and the floating-point SIMD width.
+ *
+ * \note You should NOT scale offsets before calling this routine; it is
+ *       done internally by using the alignment template parameter instead.
+ * \note This is a special routine primarily intended for loading Gromacs
+ *       table data as efficiently as possible - this is the reason for using
+ *       a SIMD offset index, since the result of the  real-to-integer conversion
+ *       is present in a SIMD register just before calling this routine.
+ */
+template<int align>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v0, SimdFloat* v1)
+{
+    // Base pointer must be aligned to the smaller of 2 elements and float SIMD width
+    assert(std::size_t(base) % (std::min(GMX_SIMD_FLOAT_WIDTH, 2) * sizeof(float)) == 0);
+    // align parameter must also be a multiple of the above alignment requirement
+    assert(align % std::min(GMX_SIMD_FLOAT_WIDTH, 2) == 0);
+
+    for (std::size_t i = 0; i < v0->simdInternal_.size(); i++)
+    {
+        v0->simdInternal_[i] = base[align * offset.simdInternal_[i]];
+        v1->simdInternal_[i] = base[align * offset.simdInternal_[i] + 1];
+    }
+}
+
+
+/*! \brief Reduce each of four SIMD floats, add those values to four consecutive
+ *         floats in memory, return sum.
+ *
+ * \param m   Pointer to memory where four floats should be incremented
+ * \param v0  SIMD variable whose sum should be added to m[0]
+ * \param v1  SIMD variable whose sum should be added to m[1]
+ * \param v2  SIMD variable whose sum should be added to m[2]
+ * \param v3  SIMD variable whose sum should be added to m[3]
+ *
+ * \return Sum of all elements in the four SIMD variables.
+ *
+ * The pointer m must be aligned to the smaller of four elements and the
+ * floating-point SIMD width.
+ *
+ * \note This is a special routine intended for the Gromacs nonbonded kernels.
+ * It is used in the epilogue of the outer loop, where the variables will
+ * contain unrolled forces for one outer-loop-particle each, corresponding to
+ * a single coordinate (i.e, say, four x-coordinate force variables). These
+ * should be summed and added to the force array in memory. Since we always work
+ * with contiguous SIMD-layout , we can use efficient aligned loads/stores.
+ * When calculating the virial, we also need the total sum of all forces for
+ * each coordinate. This is provided as the return value. For routines that
+ * do not need these, this extra code will be optimized away completely if you
+ * just ignore the return value (Checked with gcc-4.9.1 and clang-3.6 for AVX).
+ */
+static inline float gmx_simdcall reduceIncr4ReturnSum(float* m, SimdFloat v0, SimdFloat v1, SimdFloat v2, SimdFloat v3)
+{
+    float sum[4]; // Note that the 4 here corresponds to the 4 m-elements, not any SIMD width
+
+    // Make sure the memory pointer is aligned to the smaller of 4 elements and float SIMD width
+    assert(std::size_t(m) % (std::min(GMX_SIMD_FLOAT_WIDTH, 4) * sizeof(float)) == 0);
+
+    sum[0] = reduce(v0);
+    sum[1] = reduce(v1);
+    sum[2] = reduce(v2);
+    sum[3] = reduce(v3);
+
+    m[0] += sum[0];
+    m[1] += sum[1];
+    m[2] += sum[2];
+    m[3] += sum[3];
+
+    return sum[0] + sum[1] + sum[2] + sum[3];
+}
+
+
+/*! \}
+ *
+ * \name Higher-level SIMD utilities accessing partial (half-width) SIMD floats.
+ *
+ * These functions are optional. The are only useful for SIMD implementation
+ * where the width is 8 or larger, and where it would be inefficient
+ * to process 4*8, 8*8, or more, interactions in parallel.
+ *
+ * Currently, only Intel provides very wide SIMD implementations, but these
+ * also come with excellent support for loading, storing, accessing and
+ * shuffling parts of the register in so-called 'lanes' of 4 bytes each.
+ * We can use this to load separate parts into the low/high halves of the
+ * register in the inner loop of the nonbonded kernel, which e.g. makes it
+ * possible to process 4*4 nonbonded interactions as a pattern of 2*8. We
+ * can also use implementations with width 16 or greater.
+ *
+ * To make this more generic, when \ref GMX_SIMD_HAVE_HSIMD_UTIL_REAL is 1,
+ * the SIMD implementation provides seven special routines that:
+ *
+ * - Load the low/high parts of a SIMD variable from different pointers
+ * - Load half the SIMD width from one pointer, and duplicate in low/high parts
+ * - Load two reals, put 1st one in all low elements, and 2nd in all high ones.
+ * - Store the low/high parts of a SIMD variable to different pointers
+ * - Subtract both SIMD halves from a single half-SIMD-width memory location.
+ * - Load aligned pairs (LJ parameters) from two base pointers, with a common
+ *   offset list, and put these in the low/high SIMD halves.
+ * - Reduce each half of two SIMD registers (i.e., 4 parts in total), increment
+ *   four adjacent memory positions, and return the total sum.
+ *
+ * Remember: this is ONLY used when the native SIMD width is large. You will
+ * just waste time if you implement it for normal 16-byte SIMD architectures.
+ *
+ * This is part of the new C++ SIMD interface, so these functions are only
+ * available when using C++. Since some Gromacs code reliying on the SIMD
+ * module is still C (not C++), we have kept the C-style naming for now - this
+ * will change once we are entirely C++.
+ *
+ * \{
+ */
+
+
+/*! \brief Load low & high parts of SIMD float from different locations.
+ *
+ * \param m0 Pointer to memory aligned to half SIMD width.
+ * \param m1 Pointer to memory aligned to half SIMD width.
+ *
+ * \return SIMD variable with low part loaded from m0, high from m1.
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT is 1.
+ */
+static inline SimdFloat gmx_simdcall loadDualHsimd(const float* m0, const float* m1)
+{
+    SimdFloat a;
+
+    // Make sure the memory pointers are aligned to half float SIMD width
+    assert(std::size_t(m0) % (GMX_SIMD_FLOAT_WIDTH / 2 * sizeof(float)) == 0);
+    assert(std::size_t(m1) % (GMX_SIMD_FLOAT_WIDTH / 2 * sizeof(float)) == 0);
+
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    {
+        a.simdInternal_[i]                              = m0[i];
+        a.simdInternal_[a.simdInternal_.size() / 2 + i] = m1[i];
+    }
+    return a;
+}
+
+/*! \brief Load half-SIMD-width float data, spread to both halves.
+ *
+ * \param m Pointer to memory aligned to half SIMD width.
+ *
+ * \return SIMD variable with both halves loaded from m..
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT is 1.
+ */
+static inline SimdFloat gmx_simdcall loadDuplicateHsimd(const float* m)
+{
+    SimdFloat a;
+
+    // Make sure the memory pointer is aligned
+    assert(std::size_t(m) % (GMX_SIMD_FLOAT_WIDTH / 2 * sizeof(float)) == 0);
+
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    {
+        a.simdInternal_[i]                              = m[i];
+        a.simdInternal_[a.simdInternal_.size() / 2 + i] = a.simdInternal_[i];
+    }
+    return a;
+}
+
+/*! \brief Load two floats, spread 1st in low half, 2nd in high half.
+ *
+ * \param m Pointer to two adjacent float values.
+ *
+ * \return SIMD variable where all elements in the low half have been set
+ *         to m[0], and all elements in high half to m[1].
+ *
+ * \note This routine always loads two values and sets the halves separately.
+ *       If you want to set all elements to the same value, simply use
+ *       the standard (non-half-SIMD) operations.
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT is 1.
+ */
+static inline SimdFloat gmx_simdcall loadU1DualHsimd(const float* m)
+{
+    SimdFloat a;
+
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    {
+        a.simdInternal_[i]                              = m[0];
+        a.simdInternal_[a.simdInternal_.size() / 2 + i] = m[1];
+    }
+    return a;
+}
+
+
+/*! \brief Store low & high parts of SIMD float to different locations.
+ *
+ * \param m0 Pointer to memory aligned to half SIMD width.
+ * \param m1 Pointer to memory aligned to half SIMD width.
+ * \param a  SIMD variable. Low half should be stored to m0, high to m1.
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT is 1.
+ */
+static inline void gmx_simdcall storeDualHsimd(float* m0, float* m1, SimdFloat a)
+{
+    // Make sure the memory pointers are aligned to half float SIMD width
+    assert(std::size_t(m0) % (GMX_SIMD_FLOAT_WIDTH / 2 * sizeof(float)) == 0);
+    assert(std::size_t(m1) % (GMX_SIMD_FLOAT_WIDTH / 2 * sizeof(float)) == 0);
+
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    {
+        m0[i] = a.simdInternal_[i];
+        m1[i] = a.simdInternal_[a.simdInternal_.size() / 2 + i];
+    }
+}
+
+/*! \brief Add each half of SIMD variable to separate memory adresses
+ *
+ * \param m0 Pointer to memory aligned to half SIMD width.
+ * \param m1 Pointer to memory aligned to half SIMD width.
+ * \param a  SIMD variable. Lower half will be added to m0, upper half to m1.
+ *
+ * The memory must be aligned to half SIMD width.
+ *
+ * \note The updated m0 value is written before m1 is read from memory, so
+ *       the result will be correct even if the memory regions overlap.
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT is 1.
+ */
+static inline void gmx_simdcall incrDualHsimd(float* m0, float* m1, SimdFloat a)
+{
+    // Make sure the memory pointer is aligned to half float SIMD width
+    assert(std::size_t(m0) % (GMX_SIMD_FLOAT_WIDTH / 2 * sizeof(float)) == 0);
+    assert(std::size_t(m1) % (GMX_SIMD_FLOAT_WIDTH / 2 * sizeof(float)) == 0);
+
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    {
+        m0[i] += a.simdInternal_[i];
+    }
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 2; i++)
+    {
+        m1[i] += a.simdInternal_[a.simdInternal_.size() / 2 + i];
+    }
+}
+
+/*! \brief Add the two halves of three SIMD floats, subtract the sum from
+ *         three half-SIMD-width consecutive floats in memory.
+ *
+ * \param m  half-width aligned memory, from which sum of the halves will be subtracted.
+ * \param a0 SIMD variable. Upper & lower halves will first be added.
+ * \param a1 SIMD variable. Upper & lower halves will second be added.
+ * \param a2 SIMD variable. Upper & lower halves will third be added.
+ *
+ * If the SIMD width is 8 and the vectors contain [a0 b0 c0 d0 e0 f0 g0 h0],
+ * [a1 b1 c1 d1 e1 f1 g1 g1] and [a2 b2 c2 d2 e2 f2 g2 h2], the
+ * memory will be modified to [m[0]-(a0+e0) m[1]-(b0+f0) m[2]-(c0+g0) m[3]-(d0+h0)
+ *                             m[4]-(a1+e1) m[5]-(b1+f1) m[6]-(c1+g1) m[7]-(d1+h1)
+ *                             m[8]-(a2+e2) m[9]-(b2+f2) m[10]-(c2+g2) m[11]-(d2+h2)].
+ *
+ * The memory must be aligned to half SIMD width.
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT is 1.
+ */
+static inline void gmx_simdcall decr3Hsimd(float* m, SimdFloat a0, SimdFloat a1, SimdFloat a2)
+{
+    assert(std::size_t(m) % (GMX_SIMD_FLOAT_WIDTH / 2 * sizeof(float)) == 0);
+    for (std::size_t i = 0; i < a0.simdInternal_.size() / 2; i++)
+    {
+        m[i] -= a0.simdInternal_[i] + a0.simdInternal_[a0.simdInternal_.size() / 2 + i];
+    }
+    for (std::size_t i = 0; i < a1.simdInternal_.size() / 2; i++)
+    {
+        m[a1.simdInternal_.size() / 2 + i] -=
+                a1.simdInternal_[i] + a1.simdInternal_[a1.simdInternal_.size() / 2 + i];
+    }
+    for (std::size_t i = 0; i < a2.simdInternal_.size() / 2; i++)
+    {
+        m[a2.simdInternal_.size() + i] -=
+                a2.simdInternal_[i] + a2.simdInternal_[a2.simdInternal_.size() / 2 + i];
+    }
+}
+
+/*! \brief Load 2 consecutive floats from each of GMX_SIMD_FLOAT_WIDTH/2 offsets,
+ *         transpose into SIMD float (low half from base0, high from base1).
+ *
+ * \tparam     align  Alignment of the storage, i.e. the distance
+ *                    (measured in elements, not bytes) between index points.
+ *                    When this is identical to the number of output components
+ *                    the data is packed without padding. This must be a
+ *                    multiple of the alignment to keep all data aligned.
+ * \param      base0  Pointer to base of first aligned memory
+ * \param      base1  Pointer to base of second aligned memory
+ * \param      offset Offset to the start of each pair
+ * \param[out] v0     1st element in each pair, base0 in low and base1 in high half.
+ * \param[out] v1     2nd element in each pair, base0 in low and base1 in high half.
+ *
+ * The offset array should be of half the SIMD width length, so it corresponds
+ * to the half-SIMD-register operations. This also means it must be aligned
+ * to half the integer SIMD width (i.e., GMX_SIMD_FINT32_WIDTH/2).
+ *
+ * The floating-point memory locations must be aligned, but only to the smaller
+ * of two elements and the floating-point SIMD width.
+ *
+ * This routine is primarily designed to load nonbonded parameters in the
+ * kernels. It is the equivalent of the full-width routine
+ * gatherLoadTranspose(), but just
+ * as the other hsimd routines it will pick half-SIMD-width data from base0
+ * and put in the lower half, while the upper half comes from base1.
+ *
+ * For an example, assume the SIMD width is 8, align is 2, that
+ * base0 is [A0 A1 B0 B1 C0 C1 D0 D1 ...], and base1 [E0 E1 F0 F1 G0 G1 H0 H1...].
+ *
+ * Then we will get v0 as [A0 B0 C0 D0 E0 F0 G0 H0] and v1 as [A1 B1 C1 D1 E1 F1 G1 H1].
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT is 1.
+ */
+template<int align>
+static inline void gmx_simdcall gatherLoadTransposeHsimd(const float*       base0,
+                                                         const float*       base1,
+                                                         const std::int32_t offset[],
+                                                         SimdFloat*         v0,
+                                                         SimdFloat*         v1)
+{
+    // Offset list must be aligned for half SIMD FINT32 width
+    assert(std::size_t(offset) % (GMX_SIMD_FINT32_WIDTH / 2 * sizeof(std::int32_t)) == 0);
+    // base pointers must be aligned to the smaller of 2 elements and float SIMD width
+    assert(std::size_t(base0) % (std::min(GMX_SIMD_FLOAT_WIDTH, 2) * sizeof(float)) == 0);
+    assert(std::size_t(base1) % (std::min(GMX_SIMD_FLOAT_WIDTH, 2) * sizeof(float)) == 0);
+    // alignment parameter must be also be multiple of the above required alignment
+    assert(align % std::min(GMX_SIMD_FLOAT_WIDTH, 2) == 0);
+
+    for (std::size_t i = 0; i < v0->simdInternal_.size() / 2; i++)
+    {
+        v0->simdInternal_[i]                                = base0[align * offset[i]];
+        v1->simdInternal_[i]                                = base0[align * offset[i] + 1];
+        v0->simdInternal_[v0->simdInternal_.size() / 2 + i] = base1[align * offset[i]];
+        v1->simdInternal_[v1->simdInternal_.size() / 2 + i] = base1[align * offset[i] + 1];
+    }
+}
+
+/*! \brief Reduce the 4 half-SIMD-with floats in 2 SIMD variables (sum halves),
+ *         increment four consecutive floats in memory, return sum.
+ *
+ * \param m    Pointer to memory where the four values should be incremented
+ * \param v0   Variable whose half-SIMD sums should be added to m[0]/m[1], respectively.
+ * \param v1   Variable whose half-SIMD sums should be added to m[2]/m[3], respectively.
+ *
+ * \return Sum of all elements in the four SIMD variables.
+ *
+ * The pointer m must be aligned, but only to the smaller
+ * of four elements and the floating-point SIMD width.
+ *
+ * \note This is the half-SIMD-width version of
+ *      reduceIncr4ReturnSum(). The only difference is that the
+ *      four half-SIMD inputs needed are present in the low/high halves of the
+ *      two SIMD arguments.
+ *
+ * Available if \ref GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT is 1.
+ */
+static inline float gmx_simdcall reduceIncr4ReturnSumHsimd(float* m, SimdFloat v0, SimdFloat v1)
+{
+    // The 4 here corresponds to the 4 elements in memory, not any SIMD width
+    float sum[4] = { 0.0F, 0.0F, 0.0F, 0.0F };
+
+    for (std::size_t i = 0; i < v0.simdInternal_.size() / 2; i++)
+    {
+        sum[0] += v0.simdInternal_[i];
+        sum[1] += v0.simdInternal_[v0.simdInternal_.size() / 2 + i];
+        sum[2] += v1.simdInternal_[i];
+        sum[3] += v1.simdInternal_[v1.simdInternal_.size() / 2 + i];
+    }
+
+    // Make sure the memory pointer is aligned to the smaller of 4 elements and float SIMD width
+    assert(std::size_t(m) % (std::min(GMX_SIMD_FLOAT_WIDTH, 4) * sizeof(float)) == 0);
+
+    m[0] += sum[0];
+    m[1] += sum[1];
+    m[2] += sum[2];
+    m[3] += sum[3];
+
+    return sum[0] + sum[1] + sum[2] + sum[3];
+}
+
+#if GMX_SIMD_FLOAT_WIDTH > 8 || defined DOXYGEN
+/*! \brief Load N floats and duplicate them 4 times each.
+ *
+ * \param m Pointer to unaligned memory
+ *
+ * \return SIMD variable with N floats from m duplicated 4x.
+ *
+ * Available if \ref GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT is 1.
+ * N is GMX_SIMD_FLOAT_WIDTH/4. Duplicated values are
+ * contigous and different values are 4 positions in SIMD
+ * apart.
+ */
+static inline SimdFloat gmx_simdcall loadUNDuplicate4(const float* m)
+{
+    SimdFloat a;
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 4; i++)
+    {
+        a.simdInternal_[i * 4]     = m[i];
+        a.simdInternal_[i * 4 + 1] = m[i];
+        a.simdInternal_[i * 4 + 2] = m[i];
+        a.simdInternal_[i * 4 + 3] = m[i];
+    }
+    return a;
+}
+
+/*! \brief Load 4 floats and duplicate them N times each.
+ *
+ * \param m Pointer to memory aligned to 4 floats
+ *
+ * \return SIMD variable with 4 floats from m duplicated Nx.
+ *
+ * Available if \ref GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT is 1.
+ * N is GMX_SIMD_FLOAT_WIDTH/4. Different values are
+ * contigous and same values are 4 positions in SIMD
+ * apart.
+ */
+static inline SimdFloat gmx_simdcall load4DuplicateN(const float* m)
+{
+    SimdFloat a;
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 4; i++)
+    {
+        a.simdInternal_[i * 4]     = m[0];
+        a.simdInternal_[i * 4 + 1] = m[1];
+        a.simdInternal_[i * 4 + 2] = m[2];
+        a.simdInternal_[i * 4 + 3] = m[3];
+    }
+    return a;
+}
+#endif
+
+#if GMX_SIMD_FLOAT_WIDTH >= 8 || defined DOXYGEN
+/*! \brief Load floats in blocks of 4 at fixed offsets
+ *
+ * \param m Pointer to unaligned memory
+ * \param offset Offset in memory between input blocks of 4
+ *
+ * \return SIMD variable with floats from m.
+ *
+ * Available if \ref GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT is 1.
+ * Blocks of 4 floats are loaded from m+n*offset where n
+ * is the n-th block of 4 floats.
+ */
+static inline SimdFloat gmx_simdcall loadU4NOffset(const float* m, int offset)
+{
+    SimdFloat a;
+    for (std::size_t i = 0; i < a.simdInternal_.size() / 4; i++)
+    {
+        a.simdInternal_[i * 4]     = m[offset * i + 0];
+        a.simdInternal_[i * 4 + 1] = m[offset * i + 1];
+        a.simdInternal_[i * 4 + 2] = m[offset * i + 2];
+        a.simdInternal_[i * 4 + 3] = m[offset * i + 3];
+    }
+    return a;
+}
+#endif
+
+/*! \} */
+
+/*! \} */
+/*! \endcond */
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_REFERENCE_UTIL_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128.h b/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128.h
new file mode 100644 (file)
index 0000000..2ee27a3
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_128_H
+#define GMX_SIMD_IMPL_X86_AVX2_128_H
+
+#include "impl_x86_avx2_128_definitions.h"
+#include "impl_x86_avx2_128_general.h"
+#include "impl_x86_avx2_128_simd4_double.h"
+#include "impl_x86_avx2_128_simd4_float.h"
+#include "impl_x86_avx2_128_simd_double.h"
+#include "impl_x86_avx2_128_simd_float.h"
+#include "impl_x86_avx2_128_util_double.h"
+#include "impl_x86_avx2_128_util_float.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_128_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_definitions.h b/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_definitions.h
new file mode 100644 (file)
index 0000000..cffb4c6
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_128_DEFINITIONS_H
+#define GMX_SIMD_IMPL_X86_AVX2_128_DEFINITIONS_H
+
+// Capability definitions for (mostly) 128-bit AVX2
+#define GMX_SIMD 1
+#define GMX_SIMD_HAVE_FLOAT 1
+#define GMX_SIMD_HAVE_DOUBLE 1
+#define GMX_SIMD_HAVE_LOADU 1
+#define GMX_SIMD_HAVE_STOREU 1
+#define GMX_SIMD_HAVE_LOGICAL 1
+#define GMX_SIMD_HAVE_FMA 1
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 0  // No need for half-simd, width is 4
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 0 // No need for half-simd, width is 2
+
+#define GMX_SIMD4_HAVE_FLOAT 1
+#define GMX_SIMD4_HAVE_DOUBLE 1
+
+// Implementation details
+#define GMX_SIMD_FLOAT_WIDTH 4
+#define GMX_SIMD_DOUBLE_WIDTH 2
+#define GMX_SIMD_FINT32_WIDTH 4
+#define GMX_SIMD_DINT32_WIDTH 2
+#define GMX_SIMD4_WIDTH 4
+#define GMX_SIMD_ALIGNMENT 32 // Bytes (4*double for SIMD4)
+#define GMX_SIMD_RSQRT_BITS 11
+#define GMX_SIMD_RCP_BITS 11
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_128_DEFINITIONS_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_general.h b/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_general.h
new file mode 100644 (file)
index 0000000..788fe87
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_128_GENERAL_H
+#define GMX_SIMD_IMPL_X86_AVX2_128_GENERAL_H
+
+#include "gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_general.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_128_GENERAL_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_simd4_double.h b/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_simd4_double.h
new file mode 100644 (file)
index 0000000..dbf743b
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_128_SIMD4_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX2_128_SIMD4_DOUBLE_H
+
+#include "gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd4_double.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_128_SIMD4_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_simd4_float.h b/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_simd4_float.h
new file mode 100644 (file)
index 0000000..69a6f66
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_128_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX2_128_SIMD4_FLOAT_H
+
+#include "gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd4_float.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_128_SIMD4_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_simd_double.h b/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_simd_double.h
new file mode 100644 (file)
index 0000000..38abfa5
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_128_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX2_128_SIMD_DOUBLE_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd_double.h"
+
+namespace gmx
+{
+
+static inline double gmx_simdcall reduce(SimdDouble a)
+{
+    __m128d b = _mm_add_sd(a.simdInternal_, _mm_permute_pd(a.simdInternal_, _MM_SHUFFLE2(1, 1)));
+    return *reinterpret_cast<double*>(&b);
+}
+
+static inline SimdDouble gmx_simdcall fma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm_fmadd_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm_fmsub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm_fnmadd_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm_fnmsub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_128_SIMD_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_simd_float.h b/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_simd_float.h
new file mode 100644 (file)
index 0000000..4103efb
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_128_SIMD_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX2_128_SIMD_FLOAT_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd_float.h"
+
+namespace gmx
+{
+
+static inline float gmx_simdcall reduce(SimdFloat a)
+{
+    a.simdInternal_ =
+            _mm_add_ps(a.simdInternal_, _mm_permute_ps(a.simdInternal_, _MM_SHUFFLE(1, 0, 3, 2)));
+    a.simdInternal_ =
+            _mm_add_ss(a.simdInternal_, _mm_permute_ps(a.simdInternal_, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&a);
+}
+
+static inline SimdFloat gmx_simdcall fma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm_fmadd_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm_fmsub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm_fnmadd_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm_fnmsub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_128_SIMD_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_util_double.h b/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_util_double.h
new file mode 100644 (file)
index 0000000..131532b
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_128_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX2_128_UTIL_DOUBLE_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_util_double.h"
+
+namespace gmx
+{
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdDouble  scalar,
+                                                        SimdDouble* triplets0,
+                                                        SimdDouble* triplets1,
+                                                        SimdDouble* triplets2)
+{
+    triplets0->simdInternal_ = _mm_permute_pd(scalar.simdInternal_, _MM_SHUFFLE2(0, 0));
+    triplets1->simdInternal_ = _mm_permute_pd(scalar.simdInternal_, _MM_SHUFFLE2(1, 0));
+    triplets2->simdInternal_ = _mm_permute_pd(scalar.simdInternal_, _MM_SHUFFLE2(1, 1));
+}
+
+static inline double reduceIncr4ReturnSum(double* m, SimdDouble v0, SimdDouble v1, SimdDouble v2, SimdDouble v3)
+{
+    __m128d t1, t2, t3, t4;
+
+    t1 = _mm_unpacklo_pd(v0.simdInternal_, v1.simdInternal_);
+    t2 = _mm_unpackhi_pd(v0.simdInternal_, v1.simdInternal_);
+    t3 = _mm_unpacklo_pd(v2.simdInternal_, v3.simdInternal_);
+    t4 = _mm_unpackhi_pd(v2.simdInternal_, v3.simdInternal_);
+
+    t1 = _mm_add_pd(t1, t2);
+    t3 = _mm_add_pd(t3, t4);
+
+    assert(std::size_t(m) % 16 == 0);
+
+    t2 = _mm_add_pd(t1, _mm_load_pd(m));
+    t4 = _mm_add_pd(t3, _mm_load_pd(m + 2));
+    _mm_store_pd(m, t2);
+    _mm_store_pd(m + 2, t4);
+
+    t1 = _mm_add_pd(t1, t3);
+
+    t2 = _mm_add_sd(t1, _mm_permute_pd(t1, _MM_SHUFFLE2(1, 1)));
+    return *reinterpret_cast<double*>(&t2);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_128_UTIL_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_util_float.h b/src/include/gromacs/simd/impl_x86_avx2_128/impl_x86_avx2_128_util_float.h
new file mode 100644 (file)
index 0000000..acc3445
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_128_UTIL_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX2_128_UTIL_FLOAT_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_util_float.h"
+
+namespace gmx
+{
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdFloat  scalar,
+                                                        SimdFloat* triplets0,
+                                                        SimdFloat* triplets1,
+                                                        SimdFloat* triplets2)
+{
+    triplets0->simdInternal_ = _mm_permute_ps(scalar.simdInternal_, _MM_SHUFFLE(1, 0, 0, 0));
+    triplets1->simdInternal_ = _mm_permute_ps(scalar.simdInternal_, _MM_SHUFFLE(2, 2, 1, 1));
+    triplets2->simdInternal_ = _mm_permute_ps(scalar.simdInternal_, _MM_SHUFFLE(3, 3, 3, 2));
+}
+
+static inline float gmx_simdcall reduceIncr4ReturnSum(float* m, SimdFloat v0, SimdFloat v1, SimdFloat v2, SimdFloat v3)
+{
+    _MM_TRANSPOSE4_PS(v0.simdInternal_, v1.simdInternal_, v2.simdInternal_, v3.simdInternal_);
+    v0.simdInternal_ = _mm_add_ps(v0.simdInternal_, v1.simdInternal_);
+    v2.simdInternal_ = _mm_add_ps(v2.simdInternal_, v3.simdInternal_);
+    v0.simdInternal_ = _mm_add_ps(v0.simdInternal_, v2.simdInternal_);
+
+    assert(std::size_t(m) % 16 == 0);
+
+    v2.simdInternal_ = _mm_add_ps(v0.simdInternal_, _mm_load_ps(m));
+    _mm_store_ps(m, v2.simdInternal_);
+
+    __m128 b = _mm_add_ps(v0.simdInternal_, _mm_permute_ps(v0.simdInternal_, _MM_SHUFFLE(1, 0, 3, 2)));
+    b        = _mm_add_ss(b, _mm_permute_ps(b, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&b);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_128_UTIL_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256.h b/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256.h
new file mode 100644 (file)
index 0000000..e525ec9
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_256_H
+#define GMX_SIMD_IMPL_X86_AVX2_256_H
+
+#include "impl_x86_avx2_256_definitions.h"
+#include "impl_x86_avx2_256_general.h"
+#include "impl_x86_avx2_256_simd4_double.h"
+#include "impl_x86_avx2_256_simd4_float.h"
+#include "impl_x86_avx2_256_simd_double.h"
+#include "impl_x86_avx2_256_simd_float.h"
+#include "impl_x86_avx2_256_util_double.h"
+#include "impl_x86_avx2_256_util_float.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_256_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_definitions.h b/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_definitions.h
new file mode 100644 (file)
index 0000000..7c2a6b1
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_256_DEFINITIONS_H
+#define GMX_SIMD_IMPL_X86_AVX2_256_DEFINITIONS_H
+
+// Capability definitions for 256-bit AVX2
+#define GMX_SIMD 1
+#define GMX_SIMD_HAVE_FLOAT 1
+#define GMX_SIMD_HAVE_DOUBLE 1
+#define GMX_SIMD_HAVE_LOADU 1
+#define GMX_SIMD_HAVE_STOREU 1
+#define GMX_SIMD_HAVE_LOGICAL 1
+#define GMX_SIMD_HAVE_FMA 1
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 0 // Not needed for width 4
+#define GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT 1
+
+#define GMX_SIMD4_HAVE_FLOAT 1
+#define GMX_SIMD4_HAVE_DOUBLE 1
+
+// Implementation details
+#define GMX_SIMD_FLOAT_WIDTH 8
+#define GMX_SIMD_DOUBLE_WIDTH 4
+#define GMX_SIMD_FINT32_WIDTH 8
+#define GMX_SIMD_DINT32_WIDTH 4
+#define GMX_SIMD4_WIDTH 4
+#define GMX_SIMD_ALIGNMENT 32 // Bytes (8*single or 4*double)
+#define GMX_SIMD_RSQRT_BITS 11
+#define GMX_SIMD_RCP_BITS 11
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_256_DEFINITIONS_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_general.h b/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_general.h
new file mode 100644 (file)
index 0000000..633bb0d
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_256_GENERAL_H
+#define GMX_SIMD_IMPL_X86_AVX2_256_GENERAL_H
+
+#include "gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_general.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_256_GENERAL_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd4_double.h b/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd4_double.h
new file mode 100644 (file)
index 0000000..dcf467a
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_256_SIMD4_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX2_256_SIMD4_DOUBLE_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd4_double.h"
+
+namespace gmx
+{
+
+static inline Simd4Double gmx_simdcall fma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_fmadd_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_fmsub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fnma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_fnmadd_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fnms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_fnmsub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_256_SIMD4_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd4_float.h b/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd4_float.h
new file mode 100644 (file)
index 0000000..8472605
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_256_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX2_256_SIMD4_FLOAT_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd4_float.h"
+
+namespace gmx
+{
+
+static inline Simd4Float gmx_simdcall fma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_fmadd_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_fmsub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_fnmadd_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_fnmsub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_256_SIMD4_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd_double.h b/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd_double.h
new file mode 100644 (file)
index 0000000..8689654
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_256_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX2_256_SIMD_DOUBLE_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/math/utilities.h"
+#include "gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_double.h"
+
+namespace gmx
+{
+
+static inline SimdDouble gmx_simdcall fma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm256_fmadd_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm256_fmsub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm256_fnmadd_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm256_fnmsub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall testBits(SimdDouble a)
+{
+    __m256i ia  = _mm256_castpd_si256(a.simdInternal_);
+    __m256i res = _mm256_andnot_si256(_mm256_cmpeq_epi64(ia, _mm256_setzero_si256()),
+                                      _mm256_cmpeq_epi64(ia, ia));
+
+    return { _mm256_castsi256_pd(res) };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble frexp(SimdDouble value, SimdDInt32* exponent)
+{
+    const __m256d exponentMask = _mm256_castsi256_pd(_mm256_set1_epi64x(0x7FF0000000000000LL));
+    const __m256d mantissaMask = _mm256_castsi256_pd(_mm256_set1_epi64x(0x800FFFFFFFFFFFFFLL));
+    const __m256i exponentBias =
+            _mm256_set1_epi64x(1022LL); // add 1 to make our definition identical to frexp()
+    const __m256d half = _mm256_set1_pd(0.5);
+
+    __m256i iExponent = _mm256_castpd_si256(_mm256_and_pd(value.simdInternal_, exponentMask));
+    iExponent         = _mm256_sub_epi64(_mm256_srli_epi64(iExponent, 52), exponentBias);
+
+    __m256d result = _mm256_or_pd(_mm256_and_pd(value.simdInternal_, mantissaMask), half);
+
+    if (opt == MathOptimization::Safe)
+    {
+        __m256d valueIsZero = _mm256_cmp_pd(_mm256_setzero_pd(), value.simdInternal_, _CMP_EQ_OQ);
+        // Set the 64-bit-fields of "iExponent" to 0-bits if the corresponding input value was +-0.0
+        iExponent = _mm256_andnot_si256(_mm256_castpd_si256(valueIsZero), iExponent);
+        // Set result to +-0 if the corresponding input value was +-0
+        result = _mm256_blendv_pd(result, value.simdInternal_, valueIsZero);
+    }
+
+    // Shuffle exponent so that 32-bit-fields 0 & 1 contain the relevant exponent values to return
+    iExponent               = _mm256_shuffle_epi32(iExponent, _MM_SHUFFLE(3, 1, 2, 0));
+    __m128i iExponent128    = _mm256_extractf128_si256(iExponent, 1);
+    exponent->simdInternal_ = _mm_unpacklo_epi64(_mm256_castsi256_si128(iExponent), iExponent128);
+
+    return { result };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble ldexp(SimdDouble value, SimdDInt32 exponent)
+{
+    const __m128i exponentBias = _mm_set1_epi32(1023);
+    __m128i       iExponent    = _mm_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm_max_epi32(iExponent, _mm_setzero_si128());
+    }
+
+    __m256i iExponent256 = _mm256_slli_epi64(_mm256_cvtepi32_epi64(iExponent), 52);
+    return { _mm256_mul_pd(value.simdInternal_, _mm256_castsi256_pd(iExponent256)) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_256_SIMD_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd_float.h b/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_simd_float.h
new file mode 100644 (file)
index 0000000..1c88090
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_256_SIMD_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX2_256_SIMD_FLOAT_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/math/utilities.h"
+#include "gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_float.h"
+
+namespace gmx
+{
+
+class SimdFIBool
+{
+public:
+    MSVC_DIAGNOSTIC_IGNORE(26495) // simdInternal_ is not being initialized!
+    SimdFIBool() {}
+    MSVC_DIAGNOSTIC_RESET
+    SimdFIBool(bool b) : simdInternal_(_mm256_set1_epi32(b ? 0xFFFFFFFF : 0)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFIBool(__m256i simd) : simdInternal_(simd) {}
+
+    __m256i simdInternal_;
+};
+
+static inline SimdFloat gmx_simdcall fma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm256_fmadd_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm256_fmsub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm256_fnmadd_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm256_fnmsub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall testBits(SimdFloat a)
+{
+    __m256i ia  = _mm256_castps_si256(a.simdInternal_);
+    __m256i res = _mm256_andnot_si256(_mm256_cmpeq_epi32(ia, _mm256_setzero_si256()),
+                                      _mm256_cmpeq_epi32(ia, ia));
+
+    return { _mm256_castsi256_ps(res) };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall frexp(SimdFloat value, SimdFInt32* exponent)
+{
+    const __m256 exponentMask = _mm256_castsi256_ps(_mm256_set1_epi32(0x7F800000));
+    const __m256 mantissaMask = _mm256_castsi256_ps(_mm256_set1_epi32(0x807FFFFF));
+    const __m256i exponentBias = _mm256_set1_epi32(126); // add 1 to make our definition identical to frexp()
+    const __m256 half = _mm256_set1_ps(0.5);
+
+    __m256i iExponent = _mm256_castps_si256(_mm256_and_ps(value.simdInternal_, exponentMask));
+    iExponent         = _mm256_sub_epi32(_mm256_srli_epi32(iExponent, 23), exponentBias);
+
+    // Combine mantissa and exponent for result
+    __m256 result = _mm256_or_ps(_mm256_and_ps(value.simdInternal_, mantissaMask), half);
+
+    if (opt == MathOptimization::Safe)
+    {
+        __m256 valueIsZero = _mm256_cmp_ps(_mm256_setzero_ps(), value.simdInternal_, _CMP_EQ_OQ);
+        // If value was non-zero, use the exponent we calculated, otherwise set return-value exponent to zero.
+        iExponent = _mm256_andnot_si256(_mm256_castps_si256(valueIsZero), iExponent);
+        // set the result to zero for all elements where the input value was zero.
+        result = _mm256_blendv_ps(result, value.simdInternal_, valueIsZero);
+    }
+
+    exponent->simdInternal_ = iExponent;
+
+    return { result };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall ldexp(SimdFloat value, SimdFInt32 exponent)
+{
+    const __m256i exponentBias = _mm256_set1_epi32(127);
+    __m256i       iExponent    = _mm256_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm256_max_epi32(iExponent, _mm256_setzero_si256());
+    }
+
+    iExponent = _mm256_slli_epi32(iExponent, 23);
+    return { _mm256_mul_ps(value.simdInternal_, _mm256_castsi256_ps(iExponent)) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator&(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm256_and_si256(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall andNot(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm256_andnot_si256(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator|(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm256_or_si256(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator^(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm256_xor_si256(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator+(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm256_add_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator-(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm256_sub_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator*(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm256_mullo_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator==(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm256_cmpeq_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall testBits(SimdFInt32 a)
+{
+    return { _mm256_andnot_si256(_mm256_cmpeq_epi32(a.simdInternal_, _mm256_setzero_si256()),
+                                 _mm256_cmpeq_epi32(a.simdInternal_, a.simdInternal_)) };
+}
+
+static inline SimdFIBool gmx_simdcall operator<(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm256_cmpgt_epi32(b.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator&&(SimdFIBool a, SimdFIBool b)
+{
+    return { _mm256_and_si256(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator||(SimdFIBool a, SimdFIBool b)
+{
+    return { _mm256_or_si256(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFIBool a)
+{
+    return _mm256_movemask_epi8(a.simdInternal_) != 0;
+}
+
+static inline SimdFInt32 gmx_simdcall selectByMask(SimdFInt32 a, SimdFIBool mask)
+{
+    return { _mm256_and_si256(a.simdInternal_, mask.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall selectByNotMask(SimdFInt32 a, SimdFIBool mask)
+{
+    return { _mm256_andnot_si256(mask.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall blend(SimdFInt32 a, SimdFInt32 b, SimdFIBool sel)
+{
+    return { _mm256_blendv_epi8(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall cvtB2IB(SimdFBool a)
+{
+    return { _mm256_castps_si256(a.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall cvtIB2B(SimdFIBool a)
+{
+    return { _mm256_castsi256_ps(a.simdInternal_) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_256_SIMD_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_util_double.h b/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_util_double.h
new file mode 100644 (file)
index 0000000..c8fe810
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_256_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX2_256_UTIL_DOUBLE_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_util_double.h"
+
+namespace gmx
+{
+
+// This version is marginally slower than the AVX 4-wide component load
+// version on Intel Skylake. On older Intel architectures this version
+// is significantly slower.
+template<int align>
+static inline void gmx_simdcall gatherLoadUTransposeSafe(const double*      base,
+                                                         const std::int32_t offset[],
+                                                         SimdDouble*        v0,
+                                                         SimdDouble*        v1,
+                                                         SimdDouble*        v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    const SimdDInt32 alignSimd = SimdDInt32(align);
+
+    SimdDInt32 vindex = simdLoad(offset, SimdDInt32Tag());
+    vindex            = vindex * alignSimd;
+
+    *v0 = _mm256_i32gather_pd(base + 0, vindex.simdInternal_, sizeof(double));
+    *v1 = _mm256_i32gather_pd(base + 1, vindex.simdInternal_, sizeof(double));
+    *v2 = _mm256_i32gather_pd(base + 2, vindex.simdInternal_, sizeof(double));
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_256_UTIL_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_util_float.h b/src/include/gromacs/simd/impl_x86_avx2_256/impl_x86_avx2_256_util_float.h
new file mode 100644 (file)
index 0000000..7bdc509
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX2_256_UTIL_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX2_256_UTIL_FLOAT_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_util_float.h"
+
+namespace gmx
+{
+
+// This version is marginally slower than the AVX 4-wide component load
+// version on Intel Skylake. On older Intel architectures this version
+// is significantly slower.
+template<int align>
+static inline void gmx_simdcall gatherLoadUTransposeSafe(const float*       base,
+                                                         const std::int32_t offset[],
+                                                         SimdFloat*         v0,
+                                                         SimdFloat*         v1,
+                                                         SimdFloat*         v2)
+{
+    assert(std::size_t(offset) % 32 == 0);
+
+    const SimdFInt32 alignSimd = SimdFInt32(align);
+
+    SimdFInt32 vindex = simdLoad(offset, SimdFInt32Tag());
+    vindex            = vindex * alignSimd;
+
+    *v0 = _mm256_i32gather_ps(base + 0, vindex.simdInternal_, sizeof(float));
+    *v1 = _mm256_i32gather_ps(base + 1, vindex.simdInternal_, sizeof(float));
+    *v2 = _mm256_i32gather_ps(base + 2, vindex.simdInternal_, sizeof(float));
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX2_256_UTIL_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma.h b/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma.h
new file mode 100644 (file)
index 0000000..3d68554
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_128_FMA_H
+#define GMX_SIMD_IMPL_X86_AVX_128_FMA_H
+
+#include "impl_x86_avx_128_fma_definitions.h"
+#include "impl_x86_avx_128_fma_general.h"
+#include "impl_x86_avx_128_fma_simd4_double.h"
+#include "impl_x86_avx_128_fma_simd4_float.h"
+#include "impl_x86_avx_128_fma_simd_double.h"
+#include "impl_x86_avx_128_fma_simd_float.h"
+#include "impl_x86_avx_128_fma_util_double.h"
+#include "impl_x86_avx_128_fma_util_float.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX_128_FMA_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_definitions.h b/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_definitions.h
new file mode 100644 (file)
index 0000000..9b65882
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_128_FMA_DEFINITIONS_H
+#define GMX_SIMD_IMPL_X86_AVX_128_FMA_DEFINITIONS_H
+
+// Capability definitions for AVX-128-FMA
+#define GMX_SIMD 1
+#define GMX_SIMD_HAVE_FLOAT 1
+#define GMX_SIMD_HAVE_DOUBLE 1
+#define GMX_SIMD_HAVE_LOADU 1
+#define GMX_SIMD_HAVE_STOREU 1
+#define GMX_SIMD_HAVE_LOGICAL 1
+#define GMX_SIMD_HAVE_FMA 1
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 0  // No need for half-simd, width is 4
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 0 // No need for half-simd, width is 2
+
+#define GMX_SIMD4_HAVE_FLOAT 1
+#define GMX_SIMD4_HAVE_DOUBLE 1 // Uses 256-bit avx for SIMD4-double
+
+// Implementation details
+#define GMX_SIMD_FLOAT_WIDTH 4
+#define GMX_SIMD_DOUBLE_WIDTH 2
+#define GMX_SIMD_FINT32_WIDTH 4
+#define GMX_SIMD_DINT32_WIDTH 2
+#define GMX_SIMD4_WIDTH 4
+#define GMX_SIMD_ALIGNMENT 32 // Bytes (4*double for SIMD4)
+#define GMX_SIMD_RSQRT_BITS 11
+#define GMX_SIMD_RCP_BITS 11
+
+#define gmx_mm_maskload_ps(mem, mask) _mm_maskload_ps((mem), (mask))
+#define gmx_mm_maskstore_ps(mem, mask, x) _mm_maskstore_ps((mem), (mask), (x))
+
+#endif // GMX_SIMD_IMPL_X86_AVX_128_FMA_DEFINITIONS_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_general.h b/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_general.h
new file mode 100644 (file)
index 0000000..63a262d
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_128_FMA_GENERAL_H
+#define GMX_SIMD_IMPL_X86_AVX_128_FMA_GENERAL_H
+
+#include "gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_general.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX_128_FMA_GENERAL_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_simd4_double.h b/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_simd4_double.h
new file mode 100644 (file)
index 0000000..3649362
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_128_FMA_SIMD4_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX_128_FMA_SIMD4_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+
+#include <immintrin.h>
+#include <x86intrin.h>
+
+namespace gmx
+{
+
+class Simd4Double
+{
+public:
+    Simd4Double() {}
+
+    Simd4Double(double d) : simdInternal_(_mm256_set1_pd(d)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4Double(__m256d simd) : simdInternal_(simd) {}
+
+    __m256d simdInternal_;
+};
+
+class Simd4DBool
+{
+public:
+    Simd4DBool() {}
+
+    //! \brief Construct from scalar bool
+    Simd4DBool(bool b) : simdInternal_(_mm256_castsi256_pd(_mm256_set1_epi32(b ? 0xFFFFFFFF : 0)))
+    {
+    }
+
+    // Internal utility constructor to simplify return statements
+    Simd4DBool(__m256d simd) : simdInternal_(simd) {}
+
+    __m256d simdInternal_;
+};
+
+static inline Simd4Double gmx_simdcall load4(const double* m)
+{
+    assert(size_t(m) % 32 == 0);
+    return { _mm256_load_pd(m) };
+}
+
+static inline void gmx_simdcall store4(double* m, Simd4Double a)
+{
+    assert(size_t(m) % 32 == 0);
+    _mm256_store_pd(m, a.simdInternal_);
+}
+
+static inline Simd4Double gmx_simdcall load4U(const double* m)
+{
+    return { _mm256_loadu_pd(m) };
+}
+
+static inline void gmx_simdcall store4U(double* m, Simd4Double a)
+{
+    _mm256_storeu_pd(m, a.simdInternal_);
+}
+
+static inline Simd4Double gmx_simdcall simd4SetZeroD()
+{
+    return { _mm256_setzero_pd() };
+}
+
+static inline Simd4Double gmx_simdcall operator&(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_and_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall andNot(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_andnot_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator|(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_or_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator^(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_xor_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator+(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_add_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator-(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_sub_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator-(Simd4Double x)
+{
+    return { _mm256_xor_pd(x.simdInternal_, _mm256_set1_pd(GMX_DOUBLE_NEGZERO)) };
+}
+
+static inline Simd4Double gmx_simdcall operator*(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_mul_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_macc_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_msub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fnma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_nmacc_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fnms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_nmsub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall rsqrt(Simd4Double x)
+{
+    return { _mm256_cvtps_pd(_mm_rsqrt_ps(_mm256_cvtpd_ps(x.simdInternal_))) };
+}
+
+static inline Simd4Double gmx_simdcall abs(Simd4Double x)
+{
+    return { _mm256_andnot_pd(_mm256_set1_pd(GMX_DOUBLE_NEGZERO), x.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall max(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_max_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall min(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_min_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall round(Simd4Double x)
+{
+    return { _mm256_round_pd(x.simdInternal_, _MM_FROUND_NINT) };
+}
+
+static inline Simd4Double gmx_simdcall trunc(Simd4Double x)
+{
+    return { _mm256_round_pd(x.simdInternal_, _MM_FROUND_TRUNC) };
+}
+
+static inline double gmx_simdcall dotProduct(Simd4Double a, Simd4Double b)
+{
+    __m128d tmp1, tmp2;
+    a.simdInternal_ = _mm256_mul_pd(a.simdInternal_, b.simdInternal_);
+    tmp1            = _mm256_castpd256_pd128(a.simdInternal_);
+    tmp2            = _mm256_extractf128_pd(a.simdInternal_, 0x1);
+
+    tmp1 = _mm_add_pd(tmp1, _mm_permute_pd(tmp1, _MM_SHUFFLE2(0, 1)));
+    tmp1 = _mm_add_pd(tmp1, tmp2);
+    return *reinterpret_cast<double*>(&tmp1);
+}
+
+static inline void gmx_simdcall transpose(Simd4Double* v0, Simd4Double* v1, Simd4Double* v2, Simd4Double* v3)
+{
+    __m256d t1, t2, t3, t4;
+    t1                = _mm256_unpacklo_pd(v0->simdInternal_, v1->simdInternal_);
+    t2                = _mm256_unpackhi_pd(v0->simdInternal_, v1->simdInternal_);
+    t3                = _mm256_unpacklo_pd(v2->simdInternal_, v3->simdInternal_);
+    t4                = _mm256_unpackhi_pd(v2->simdInternal_, v3->simdInternal_);
+    v0->simdInternal_ = _mm256_permute2f128_pd(t1, t3, 0x20);
+    v1->simdInternal_ = _mm256_permute2f128_pd(t2, t4, 0x20);
+    v2->simdInternal_ = _mm256_permute2f128_pd(t1, t3, 0x31);
+    v3->simdInternal_ = _mm256_permute2f128_pd(t2, t4, 0x31);
+}
+
+static inline Simd4DBool gmx_simdcall operator==(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_EQ_OQ) };
+}
+
+static inline Simd4DBool gmx_simdcall operator!=(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_NEQ_OQ) };
+}
+
+static inline Simd4DBool gmx_simdcall operator<(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_LT_OQ) };
+}
+
+static inline Simd4DBool gmx_simdcall operator<=(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_LE_OQ) };
+}
+
+static inline Simd4DBool gmx_simdcall operator&&(Simd4DBool a, Simd4DBool b)
+{
+    return { _mm256_and_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4DBool gmx_simdcall operator||(Simd4DBool a, Simd4DBool b)
+{
+    return { _mm256_or_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(Simd4DBool a)
+{
+    return _mm256_movemask_pd(a.simdInternal_) != 0;
+}
+
+static inline Simd4Double gmx_simdcall selectByMask(Simd4Double a, Simd4DBool mask)
+{
+    return { _mm256_and_pd(a.simdInternal_, mask.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall selectByNotMask(Simd4Double a, Simd4DBool mask)
+{
+    return { _mm256_andnot_pd(mask.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall blend(Simd4Double a, Simd4Double b, Simd4DBool sel)
+{
+    return { _mm256_blendv_pd(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline double gmx_simdcall reduce(Simd4Double a)
+{
+    __m128d a0, a1;
+    /* test with shuffle & add as an alternative to hadd later */
+    a.simdInternal_ = _mm256_hadd_pd(a.simdInternal_, a.simdInternal_);
+    a0              = _mm256_castpd256_pd128(a.simdInternal_);
+    a1              = _mm256_extractf128_pd(a.simdInternal_, 0x1);
+    a0              = _mm_add_sd(a0, a1);
+    return *reinterpret_cast<double*>(&a0);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_128_FMA_SIMD4_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_simd4_float.h b/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_simd4_float.h
new file mode 100644 (file)
index 0000000..ffa3311
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_128_FMA_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX_128_FMA_SIMD4_FLOAT_H
+
+#include "config.h"
+
+#include <immintrin.h>
+#include <x86intrin.h>
+
+#include "gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd4_float.h"
+
+namespace gmx
+{
+
+static inline Simd4Float gmx_simdcall fma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_macc_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_msub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_nmacc_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_nmsub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline float gmx_simdcall reduce(Simd4Float a)
+{
+    __m128 b;
+    b = _mm_add_ps(a.simdInternal_, _mm_permute_ps(a.simdInternal_, _MM_SHUFFLE(1, 0, 3, 2)));
+    b = _mm_add_ss(b, _mm_permute_ps(b, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&b);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_128_FMA_SIMD4_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_simd_double.h b/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_simd_double.h
new file mode 100644 (file)
index 0000000..ce34d19
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_128_FMA_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX_128_FMA_SIMD_DOUBLE_H
+
+#include "config.h"
+
+#include <immintrin.h>
+#include <x86intrin.h>
+
+#include "gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd_double.h"
+
+namespace gmx
+{
+
+static inline double gmx_simdcall reduce(SimdDouble a)
+{
+    __m128d b = _mm_add_sd(a.simdInternal_, _mm_permute_pd(a.simdInternal_, _MM_SHUFFLE2(1, 1)));
+    return *reinterpret_cast<double*>(&b);
+}
+
+static inline SimdDouble gmx_simdcall fma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm_macc_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm_msub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm_nmacc_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm_nmsub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+} // namespace gmx
+
+#endif /* GMX_SIMD_IMPL_X86_AVX_128_FMA_SIMD_DOUBLE_H */
diff --git a/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_simd_float.h b/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_simd_float.h
new file mode 100644 (file)
index 0000000..b9b2209
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_128_FMA_SIMD_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX_128_FMA_SIMD_FLOAT_H
+
+#include "config.h"
+
+#include <immintrin.h>
+#include <x86intrin.h>
+
+#include "gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd_float.h"
+
+namespace gmx
+{
+
+static inline float gmx_simdcall reduce(SimdFloat a)
+{
+    a.simdInternal_ =
+            _mm_add_ps(a.simdInternal_, _mm_permute_ps(a.simdInternal_, _MM_SHUFFLE(1, 0, 3, 2)));
+    a.simdInternal_ =
+            _mm_add_ss(a.simdInternal_, _mm_permute_ps(a.simdInternal_, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&a);
+}
+
+static inline SimdFloat gmx_simdcall fma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm_macc_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm_msub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm_nmacc_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm_nmsub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_128_FMA_SIMD_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_util_double.h b/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_util_double.h
new file mode 100644 (file)
index 0000000..1eaf881
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_128_FMA_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX_128_FMA_UTIL_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+
+#include <immintrin.h>
+#include <x86intrin.h>
+
+#include "gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_util_double.h"
+
+namespace gmx
+{
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdDouble  scalar,
+                                                        SimdDouble* triplets0,
+                                                        SimdDouble* triplets1,
+                                                        SimdDouble* triplets2)
+{
+    triplets0->simdInternal_ = _mm_permute_pd(scalar.simdInternal_, _MM_SHUFFLE2(0, 0));
+    triplets1->simdInternal_ = _mm_permute_pd(scalar.simdInternal_, _MM_SHUFFLE2(1, 0));
+    triplets2->simdInternal_ = _mm_permute_pd(scalar.simdInternal_, _MM_SHUFFLE2(1, 1));
+}
+
+static inline double reduceIncr4ReturnSum(double* m, SimdDouble v0, SimdDouble v1, SimdDouble v2, SimdDouble v3)
+{
+    __m128d t1, t2, t3, t4;
+
+    t1 = _mm_unpacklo_pd(v0.simdInternal_, v1.simdInternal_);
+    t2 = _mm_unpackhi_pd(v0.simdInternal_, v1.simdInternal_);
+    t3 = _mm_unpacklo_pd(v2.simdInternal_, v3.simdInternal_);
+    t4 = _mm_unpackhi_pd(v2.simdInternal_, v3.simdInternal_);
+
+    t1 = _mm_add_pd(t1, t2);
+    t3 = _mm_add_pd(t3, t4);
+
+    assert(std::size_t(m) % 16 == 0);
+
+    t2 = _mm_add_pd(t1, _mm_load_pd(m));
+    t4 = _mm_add_pd(t3, _mm_load_pd(m + 2));
+    _mm_store_pd(m, t2);
+    _mm_store_pd(m + 2, t4);
+
+    t1 = _mm_add_pd(t1, t3);
+
+    t2 = _mm_add_sd(t1, _mm_permute_pd(t1, _MM_SHUFFLE2(1, 1)));
+    return *reinterpret_cast<double*>(&t2);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_128_FMA_UTIL_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_util_float.h b/src/include/gromacs/simd/impl_x86_avx_128_fma/impl_x86_avx_128_fma_util_float.h
new file mode 100644 (file)
index 0000000..61bbdc8
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_128_FMA_UTIL_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX_128_FMA_UTIL_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <immintrin.h>
+#include <x86intrin.h>
+
+#include "gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_util_float.h"
+
+namespace gmx
+{
+
+/* In the old group kernels we found it more efficient to transpose the data to store rather
+ * than using maskload and maskstore. It might be worth to test again, but for now we assume
+ * this is still the case, and rely on those version inherited from the SSE2 header.
+ *
+ * It is also worth testing if changing _mm_shuffle_ps() to _mm_permute_ps() could improve
+ * throughput just-so-slightly.
+ */
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdFloat  scalar,
+                                                        SimdFloat* triplets0,
+                                                        SimdFloat* triplets1,
+                                                        SimdFloat* triplets2)
+{
+    triplets0->simdInternal_ = _mm_permute_ps(scalar.simdInternal_, _MM_SHUFFLE(1, 0, 0, 0));
+    triplets1->simdInternal_ = _mm_permute_ps(scalar.simdInternal_, _MM_SHUFFLE(2, 2, 1, 1));
+    triplets2->simdInternal_ = _mm_permute_ps(scalar.simdInternal_, _MM_SHUFFLE(3, 3, 3, 2));
+}
+
+static inline float gmx_simdcall reduceIncr4ReturnSum(float* m, SimdFloat v0, SimdFloat v1, SimdFloat v2, SimdFloat v3)
+{
+    _MM_TRANSPOSE4_PS(v0.simdInternal_, v1.simdInternal_, v2.simdInternal_, v3.simdInternal_);
+    v0.simdInternal_ = _mm_add_ps(v0.simdInternal_, v1.simdInternal_);
+    v2.simdInternal_ = _mm_add_ps(v2.simdInternal_, v3.simdInternal_);
+    v0.simdInternal_ = _mm_add_ps(v0.simdInternal_, v2.simdInternal_);
+
+    assert(std::size_t(m) % 16 == 0);
+
+    v2.simdInternal_ = _mm_add_ps(v0.simdInternal_, _mm_load_ps(m));
+    _mm_store_ps(m, v2.simdInternal_);
+
+    __m128 b = _mm_add_ps(v0.simdInternal_, _mm_permute_ps(v0.simdInternal_, _MM_SHUFFLE(1, 0, 3, 2)));
+    b        = _mm_add_ss(b, _mm_permute_ps(b, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&b);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_128_FMA_UTIL_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256.h b/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256.h
new file mode 100644 (file)
index 0000000..bae0001
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_256_H
+#define GMX_SIMD_IMPL_X86_AVX_256_H
+
+#include "impl_x86_avx_256_definitions.h"
+#include "impl_x86_avx_256_general.h"
+#include "impl_x86_avx_256_simd4_double.h"
+#include "impl_x86_avx_256_simd4_float.h"
+#include "impl_x86_avx_256_simd_double.h"
+#include "impl_x86_avx_256_simd_float.h"
+#include "impl_x86_avx_256_util_double.h"
+#include "impl_x86_avx_256_util_float.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX_256_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_definitions.h b/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_definitions.h
new file mode 100644 (file)
index 0000000..aa633c2
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_256_DEFINITIONS_H
+#define GMX_SIMD_IMPL_X86_AVX_256_DEFINITIONS_H
+
+// Capability definitions for 256-bit AVX
+#define GMX_SIMD 1
+#define GMX_SIMD_HAVE_FLOAT 1
+#define GMX_SIMD_HAVE_DOUBLE 1
+#define GMX_SIMD_HAVE_LOADU 1
+#define GMX_SIMD_HAVE_STOREU 1
+#define GMX_SIMD_HAVE_LOGICAL 1
+#define GMX_SIMD_HAVE_FMA 0
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 1     // Emulated
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 0     // AVX1 cannot do 256-bit int shifts
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 0 // AVX1 cannot do 256-bit int +,-,*
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 1     // Native, since we use __m128i
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 0 // Not needed for width 4
+#define GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT 1
+
+#define GMX_SIMD4_HAVE_FLOAT 1
+#define GMX_SIMD4_HAVE_DOUBLE 1
+
+// Implementation details
+#define GMX_SIMD_FLOAT_WIDTH 8
+#define GMX_SIMD_DOUBLE_WIDTH 4
+#define GMX_SIMD_FINT32_WIDTH 8
+#define GMX_SIMD_DINT32_WIDTH 4
+#define GMX_SIMD4_WIDTH 4
+#define GMX_SIMD_ALIGNMENT 32 // Bytes (8*single or 4*double)
+#define GMX_SIMD_RSQRT_BITS 11
+#define GMX_SIMD_RCP_BITS 11
+
+#endif // GMX_SIMD_IMPL_X86_AVX_256_DEFINITIONS_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_general.h b/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_general.h
new file mode 100644 (file)
index 0000000..57999e7
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_256_GENERAL_H
+#define GMX_SIMD_IMPL_X86_AVX_256_GENERAL_H
+
+#include <immintrin.h>
+
+namespace gmx
+{
+
+static inline void simdPrefetch(void* m)
+{
+    _mm_prefetch(reinterpret_cast<const char*>(m), _MM_HINT_T0);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_256_GENERAL_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd4_double.h b/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd4_double.h
new file mode 100644 (file)
index 0000000..9969e03
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_256_SIMD4_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX_256_SIMD4_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+
+#include <immintrin.h>
+
+namespace gmx
+{
+
+class Simd4Double
+{
+public:
+    MSVC_DIAGNOSTIC_IGNORE(26495) // simdInternal_ is not being initialized!
+    Simd4Double() {}
+    MSVC_DIAGNOSTIC_RESET
+
+    Simd4Double(double d) : simdInternal_(_mm256_set1_pd(d)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4Double(__m256d simd) : simdInternal_(simd) {}
+
+    __m256d simdInternal_;
+};
+
+class Simd4DBool
+{
+public:
+    MSVC_DIAGNOSTIC_IGNORE(26495) // simdInternal_ is not being initialized!
+    Simd4DBool() {}
+    MSVC_DIAGNOSTIC_RESET
+
+    //! \brief Construct from scalar bool
+    Simd4DBool(bool b) : simdInternal_(_mm256_castsi256_pd(_mm256_set1_epi32(b ? 0xFFFFFFFF : 0)))
+    {
+    }
+
+    // Internal utility constructor to simplify return statements
+    Simd4DBool(__m256d simd) : simdInternal_(simd) {}
+
+    __m256d simdInternal_;
+};
+
+static inline Simd4Double gmx_simdcall load4(const double* m)
+{
+    assert(std::size_t(m) % 32 == 0);
+    return { _mm256_load_pd(m) };
+}
+
+static inline void gmx_simdcall store4(double* m, Simd4Double a)
+{
+    assert(std::size_t(m) % 32 == 0);
+    _mm256_store_pd(m, a.simdInternal_);
+}
+
+static inline Simd4Double gmx_simdcall load4U(const double* m)
+{
+    return { _mm256_loadu_pd(m) };
+}
+
+static inline void gmx_simdcall store4U(double* m, Simd4Double a)
+{
+    _mm256_storeu_pd(m, a.simdInternal_);
+}
+
+static inline Simd4Double gmx_simdcall simd4SetZeroD()
+{
+    return { _mm256_setzero_pd() };
+}
+
+static inline Simd4Double gmx_simdcall operator&(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_and_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall andNot(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_andnot_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator|(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_or_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator^(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_xor_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator+(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_add_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator-(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_sub_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator-(Simd4Double x)
+{
+    return { _mm256_xor_pd(x.simdInternal_, _mm256_set1_pd(GMX_DOUBLE_NEGZERO)) };
+}
+
+static inline Simd4Double gmx_simdcall operator*(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_mul_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for AVX2 and higher
+#if GMX_SIMD_X86_AVX_256
+static inline Simd4Double gmx_simdcall fma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_add_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_sub_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fnma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_sub_pd(c.simdInternal_, _mm256_mul_pd(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline Simd4Double gmx_simdcall fnms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_sub_pd(_mm256_setzero_pd(),
+                           _mm256_add_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_)) };
+}
+#endif
+
+static inline Simd4Double gmx_simdcall rsqrt(Simd4Double x)
+{
+    return { _mm256_cvtps_pd(_mm_rsqrt_ps(_mm256_cvtpd_ps(x.simdInternal_))) };
+}
+
+static inline Simd4Double gmx_simdcall abs(Simd4Double x)
+{
+    return { _mm256_andnot_pd(_mm256_set1_pd(GMX_DOUBLE_NEGZERO), x.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall max(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_max_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall min(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_min_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall round(Simd4Double x)
+{
+    return { _mm256_round_pd(x.simdInternal_, _MM_FROUND_NINT) };
+}
+
+static inline Simd4Double gmx_simdcall trunc(Simd4Double x)
+{
+    return { _mm256_round_pd(x.simdInternal_, _MM_FROUND_TRUNC) };
+}
+
+static inline double gmx_simdcall dotProduct(Simd4Double a, Simd4Double b)
+{
+    __m128d tmp1, tmp2;
+    a.simdInternal_ = _mm256_mul_pd(a.simdInternal_, b.simdInternal_);
+    tmp1            = _mm256_castpd256_pd128(a.simdInternal_);
+    tmp2            = _mm256_extractf128_pd(a.simdInternal_, 0x1);
+
+    tmp1 = _mm_add_pd(tmp1, _mm_permute_pd(tmp1, _MM_SHUFFLE2(0, 1)));
+    tmp1 = _mm_add_pd(tmp1, tmp2);
+    return *reinterpret_cast<double*>(&tmp1);
+}
+
+static inline void gmx_simdcall transpose(Simd4Double* v0, Simd4Double* v1, Simd4Double* v2, Simd4Double* v3)
+{
+    __m256d t1, t2, t3, t4;
+    t1                = _mm256_unpacklo_pd(v0->simdInternal_, v1->simdInternal_);
+    t2                = _mm256_unpackhi_pd(v0->simdInternal_, v1->simdInternal_);
+    t3                = _mm256_unpacklo_pd(v2->simdInternal_, v3->simdInternal_);
+    t4                = _mm256_unpackhi_pd(v2->simdInternal_, v3->simdInternal_);
+    v0->simdInternal_ = _mm256_permute2f128_pd(t1, t3, 0x20);
+    v1->simdInternal_ = _mm256_permute2f128_pd(t2, t4, 0x20);
+    v2->simdInternal_ = _mm256_permute2f128_pd(t1, t3, 0x31);
+    v3->simdInternal_ = _mm256_permute2f128_pd(t2, t4, 0x31);
+}
+
+static inline Simd4DBool gmx_simdcall operator==(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_EQ_OQ) };
+}
+
+static inline Simd4DBool gmx_simdcall operator!=(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_NEQ_OQ) };
+}
+
+static inline Simd4DBool gmx_simdcall operator<(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_LT_OQ) };
+}
+
+static inline Simd4DBool gmx_simdcall operator<=(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_LE_OQ) };
+}
+
+static inline Simd4DBool gmx_simdcall operator&&(Simd4DBool a, Simd4DBool b)
+{
+    return { _mm256_and_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4DBool gmx_simdcall operator||(Simd4DBool a, Simd4DBool b)
+{
+    return { _mm256_or_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(Simd4DBool a)
+{
+    return _mm256_movemask_pd(a.simdInternal_) != 0;
+}
+
+static inline Simd4Double gmx_simdcall selectByMask(Simd4Double a, Simd4DBool mask)
+{
+    return { _mm256_and_pd(a.simdInternal_, mask.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall selectByNotMask(Simd4Double a, Simd4DBool mask)
+{
+    return { _mm256_andnot_pd(mask.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall blend(Simd4Double a, Simd4Double b, Simd4DBool sel)
+{
+    return { _mm256_blendv_pd(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline double gmx_simdcall reduce(Simd4Double a)
+{
+    __m128d a0, a1;
+    // test with shuffle & add as an alternative to hadd later
+    a.simdInternal_ = _mm256_hadd_pd(a.simdInternal_, a.simdInternal_);
+    a0              = _mm256_castpd256_pd128(a.simdInternal_);
+    a1              = _mm256_extractf128_pd(a.simdInternal_, 0x1);
+    a0              = _mm_add_sd(a0, a1);
+    return *reinterpret_cast<double*>(&a0);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_256_SIMD4_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd4_float.h b/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd4_float.h
new file mode 100644 (file)
index 0000000..e1b9027
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_256_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX_256_SIMD4_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+
+#include <immintrin.h>
+
+namespace gmx
+{
+
+class Simd4Float
+{
+public:
+    MSVC_DIAGNOSTIC_IGNORE(26495) // simdInternal_ is not being initialized!
+    Simd4Float() {}
+    MSVC_DIAGNOSTIC_RESET
+    Simd4Float(float f) : simdInternal_(_mm_set1_ps(f)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4Float(__m128 simd) : simdInternal_(simd) {}
+
+    __m128 simdInternal_;
+};
+
+class Simd4FBool
+{
+public:
+    MSVC_DIAGNOSTIC_IGNORE(26495) // simdInternal_ is not being initialized!
+    Simd4FBool() {}
+    MSVC_DIAGNOSTIC_RESET
+    //! \brief Construct from scalar bool
+    Simd4FBool(bool b) : simdInternal_(_mm_castsi128_ps(_mm_set1_epi32(b ? 0xFFFFFFFF : 0))) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4FBool(__m128 simd) : simdInternal_(simd) {}
+
+    __m128 simdInternal_;
+};
+
+static inline Simd4Float gmx_simdcall load4(const float* m)
+{
+    assert(std::size_t(m) % 16 == 0);
+    return { _mm_load_ps(m) };
+}
+
+static inline void gmx_simdcall store4(float* m, Simd4Float a)
+{
+    assert(std::size_t(m) % 16 == 0);
+    _mm_store_ps(m, a.simdInternal_);
+}
+
+static inline Simd4Float gmx_simdcall load4U(const float* m)
+{
+    return { _mm_loadu_ps(m) };
+}
+
+static inline void gmx_simdcall store4U(float* m, Simd4Float a)
+{
+    _mm_storeu_ps(m, a.simdInternal_);
+}
+
+static inline Simd4Float gmx_simdcall simd4SetZeroF()
+{
+    return { _mm_setzero_ps() };
+}
+
+static inline Simd4Float gmx_simdcall operator&(Simd4Float a, Simd4Float b)
+{
+    return { _mm_and_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall andNot(Simd4Float a, Simd4Float b)
+{
+    return { _mm_andnot_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator|(Simd4Float a, Simd4Float b)
+{
+    return { _mm_or_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator^(Simd4Float a, Simd4Float b)
+{
+    return { _mm_xor_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator+(Simd4Float a, Simd4Float b)
+{
+    return { _mm_add_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float a, Simd4Float b)
+{
+    return { _mm_sub_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float x)
+{
+    return { _mm_xor_ps(x.simdInternal_, _mm_set1_ps(GMX_FLOAT_NEGZERO)) };
+}
+
+static inline Simd4Float gmx_simdcall operator*(Simd4Float a, Simd4Float b)
+{
+    return { _mm_mul_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for AVX2 and higher
+#if GMX_SIMD_X86_AVX_256
+static inline Simd4Float gmx_simdcall fma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_add_ps(_mm_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_sub_ps(_mm_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_sub_ps(c.simdInternal_, _mm_mul_ps(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall fnms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_sub_ps(_mm_setzero_ps(),
+                        _mm_add_ps(_mm_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_)) };
+}
+#endif
+
+static inline Simd4Float gmx_simdcall rsqrt(Simd4Float x)
+{
+    return { _mm_rsqrt_ps(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall abs(Simd4Float x)
+{
+    return { _mm_andnot_ps(_mm_set1_ps(GMX_FLOAT_NEGZERO), x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall max(Simd4Float a, Simd4Float b)
+{
+    return { _mm_max_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall min(Simd4Float a, Simd4Float b)
+{
+    return { _mm_min_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall round(Simd4Float x)
+{
+    return { _mm_round_ps(x.simdInternal_, _MM_FROUND_NINT) };
+}
+
+static inline Simd4Float gmx_simdcall trunc(Simd4Float x)
+{
+    return { _mm_round_ps(x.simdInternal_, _MM_FROUND_TRUNC) };
+}
+
+static inline float gmx_simdcall dotProduct(Simd4Float a, Simd4Float b)
+{
+    __m128 c, d;
+    c = _mm_mul_ps(a.simdInternal_, b.simdInternal_);
+    d = _mm_add_ps(c, _mm_permute_ps(c, _MM_SHUFFLE(2, 1, 2, 1)));
+    d = _mm_add_ps(d, _mm_permute_ps(c, _MM_SHUFFLE(3, 2, 3, 2)));
+    return *reinterpret_cast<float*>(&d);
+}
+
+static inline void gmx_simdcall transpose(Simd4Float* v0, Simd4Float* v1, Simd4Float* v2, Simd4Float* v3)
+{
+    _MM_TRANSPOSE4_PS(v0->simdInternal_, v1->simdInternal_, v2->simdInternal_, v3->simdInternal_);
+}
+
+static inline Simd4FBool gmx_simdcall operator==(Simd4Float a, Simd4Float b)
+{
+    return { _mm_cmp_ps(a.simdInternal_, b.simdInternal_, _CMP_EQ_OQ) };
+}
+
+static inline Simd4FBool gmx_simdcall operator!=(Simd4Float a, Simd4Float b)
+{
+    return { _mm_cmp_ps(a.simdInternal_, b.simdInternal_, _CMP_NEQ_OQ) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<(Simd4Float a, Simd4Float b)
+{
+    return { _mm_cmp_ps(a.simdInternal_, b.simdInternal_, _CMP_LT_OQ) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<=(Simd4Float a, Simd4Float b)
+{
+    return { _mm_cmp_ps(a.simdInternal_, b.simdInternal_, _CMP_LE_OQ) };
+}
+
+static inline Simd4FBool gmx_simdcall operator&&(Simd4FBool a, Simd4FBool b)
+{
+    return { _mm_and_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator||(Simd4FBool a, Simd4FBool b)
+{
+    return { _mm_or_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(Simd4FBool a)
+{
+    return _mm_movemask_ps(a.simdInternal_) != 0;
+}
+
+static inline Simd4Float gmx_simdcall selectByMask(Simd4Float a, Simd4FBool mask)
+{
+    return { _mm_and_ps(a.simdInternal_, mask.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall selectByNotMask(Simd4Float a, Simd4FBool mask)
+{
+    return { _mm_andnot_ps(mask.simdInternal_, a.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall blend(Simd4Float a, Simd4Float b, Simd4FBool sel)
+{
+    return { _mm_blendv_ps(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline float gmx_simdcall reduce(Simd4Float a)
+{
+    __m128 b;
+    b = _mm_add_ps(a.simdInternal_, _mm_permute_ps(a.simdInternal_, _MM_SHUFFLE(1, 0, 3, 2)));
+    b = _mm_add_ss(b, _mm_permute_ps(b, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&b);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_256_SIMD4_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_double.h b/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_double.h
new file mode 100644 (file)
index 0000000..d60a11c
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_256_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX_256_SIMD_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <immintrin.h>
+
+#include "gromacs/math/utilities.h"
+
+#include "impl_x86_avx_256_simd_float.h"
+
+
+namespace gmx
+{
+
+class SimdDouble
+{
+public:
+    MSVC_DIAGNOSTIC_IGNORE(26495) // simdInternal_ is not being initialized!
+    SimdDouble() {}
+    MSVC_DIAGNOSTIC_RESET
+    SimdDouble(double d) : simdInternal_(_mm256_set1_pd(d)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDouble(__m256d simd) : simdInternal_(simd) {}
+
+    __m256d simdInternal_;
+};
+
+class SimdDInt32
+{
+public:
+    MSVC_DIAGNOSTIC_IGNORE(26495) // simdInternal_ is not being initialized!
+    SimdDInt32() {}
+    MSVC_DIAGNOSTIC_RESET
+    SimdDInt32(std::int32_t i) : simdInternal_(_mm_set1_epi32(i)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDInt32(__m128i simd) : simdInternal_(simd) {}
+
+    __m128i simdInternal_;
+};
+
+class SimdDBool
+{
+public:
+    MSVC_DIAGNOSTIC_IGNORE(26495) // simdInternal_ is not being initialized!
+    SimdDBool() {}
+    MSVC_DIAGNOSTIC_RESET
+    SimdDBool(bool b) : simdInternal_(_mm256_castsi256_pd(_mm256_set1_epi32(b ? 0xFFFFFFFF : 0))) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDBool(__m256d simd) : simdInternal_(simd) {}
+
+    __m256d simdInternal_;
+};
+
+class SimdDIBool
+{
+public:
+    MSVC_DIAGNOSTIC_IGNORE(26495) // simdInternal_ is not being initialized!
+    SimdDIBool() {}
+    MSVC_DIAGNOSTIC_RESET
+    SimdDIBool(bool b) : simdInternal_(_mm_set1_epi32(b ? 0xFFFFFFFF : 0)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDIBool(__m128i simd) : simdInternal_(simd) {}
+
+    __m128i simdInternal_;
+};
+
+
+static inline SimdDouble gmx_simdcall simdLoad(const double* m, SimdDoubleTag /*unused*/ = {})
+{
+    assert(std::size_t(m) % 32 == 0);
+    return { _mm256_load_pd(m) };
+}
+
+static inline void gmx_simdcall store(double* m, SimdDouble a)
+{
+    assert(std::size_t(m) % 32 == 0);
+    _mm256_store_pd(m, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall simdLoadU(const double* m, SimdDoubleTag /*unused*/ = {})
+{
+    return { _mm256_loadu_pd(m) };
+}
+
+static inline void gmx_simdcall storeU(double* m, SimdDouble a)
+{
+    _mm256_storeu_pd(m, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall setZeroD()
+{
+    return { _mm256_setzero_pd() };
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdDInt32Tag /*unused*/)
+{
+    assert(std::size_t(m) % 16 == 0);
+    return { _mm_load_si128(reinterpret_cast<const __m128i*>(m)) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdDInt32 a)
+{
+    assert(std::size_t(m) % 16 == 0);
+    _mm_store_si128(reinterpret_cast<__m128i*>(m), a.simdInternal_);
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdDInt32Tag /*unused*/)
+{
+    return { _mm_loadu_si128(reinterpret_cast<const __m128i*>(m)) };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdDInt32 a)
+{
+    _mm_storeu_si128(reinterpret_cast<__m128i*>(m), a.simdInternal_);
+}
+
+static inline SimdDInt32 gmx_simdcall setZeroDI()
+{
+    return { _mm_setzero_si128() };
+}
+
+template<int index>
+static inline std::int32_t gmx_simdcall extract(SimdDInt32 a)
+{
+    return _mm_extract_epi32(a.simdInternal_, index);
+}
+
+static inline SimdDouble gmx_simdcall operator&(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_and_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall andNot(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_andnot_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator|(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_or_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator^(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_xor_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator+(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_add_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_sub_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble x)
+{
+    return { _mm256_xor_pd(x.simdInternal_, _mm256_set1_pd(GMX_DOUBLE_NEGZERO)) };
+}
+
+static inline SimdDouble gmx_simdcall operator*(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_mul_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for AVX2 and higher
+#if GMX_SIMD_X86_AVX_256
+static inline SimdDouble gmx_simdcall fma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm256_add_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm256_sub_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm256_sub_pd(c.simdInternal_, _mm256_mul_pd(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall fnms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm256_sub_pd(_mm256_setzero_pd(),
+                           _mm256_add_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_)) };
+}
+#endif
+
+static inline SimdDouble gmx_simdcall rsqrt(SimdDouble x)
+{
+    return { _mm256_cvtps_pd(_mm_rsqrt_ps(_mm256_cvtpd_ps(x.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall rcp(SimdDouble x)
+{
+    return { _mm256_cvtps_pd(_mm_rcp_ps(_mm256_cvtpd_ps(x.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall maskAdd(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    return { _mm256_add_pd(a.simdInternal_, _mm256_and_pd(b.simdInternal_, m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall maskzMul(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    return { _mm256_and_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), m.simdInternal_) };
+}
+
+static inline SimdDouble maskzFma(SimdDouble a, SimdDouble b, SimdDouble c, SimdDBool m)
+{
+    return { _mm256_and_pd(_mm256_add_pd(_mm256_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_),
+                           m.simdInternal_) };
+}
+
+static inline SimdDouble maskzRsqrt(SimdDouble x, SimdDBool m)
+{
+#ifndef NDEBUG
+    x.simdInternal_ = _mm256_blendv_pd(_mm256_set1_pd(1.0), x.simdInternal_, m.simdInternal_);
+#endif
+    return { _mm256_and_pd(_mm256_cvtps_pd(_mm_rsqrt_ps(_mm256_cvtpd_ps(x.simdInternal_))), m.simdInternal_) };
+}
+
+static inline SimdDouble maskzRcp(SimdDouble x, SimdDBool m)
+{
+#ifndef NDEBUG
+    x.simdInternal_ = _mm256_blendv_pd(_mm256_set1_pd(1.0), x.simdInternal_, m.simdInternal_);
+#endif
+    return { _mm256_and_pd(_mm256_cvtps_pd(_mm_rcp_ps(_mm256_cvtpd_ps(x.simdInternal_))), m.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall abs(SimdDouble x)
+{
+    return { _mm256_andnot_pd(_mm256_set1_pd(GMX_DOUBLE_NEGZERO), x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall max(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_max_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall min(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_min_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall round(SimdDouble x)
+{
+    return { _mm256_round_pd(x.simdInternal_, _MM_FROUND_NINT) };
+}
+
+static inline SimdDouble gmx_simdcall trunc(SimdDouble x)
+{
+    return { _mm256_round_pd(x.simdInternal_, _MM_FROUND_TRUNC) };
+}
+
+// Override for AVX2 and higher
+#if GMX_SIMD_X86_AVX_256
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble frexp(SimdDouble value, SimdDInt32* exponent)
+{
+    const __m256d exponentMask = _mm256_castsi256_pd(_mm256_set1_epi64x(0x7FF0000000000000LL));
+    const __m256d mantissaMask = _mm256_castsi256_pd(_mm256_set1_epi64x(0x800FFFFFFFFFFFFFLL));
+    const __m256d half         = _mm256_set1_pd(0.5);
+    const __m128i exponentBias = _mm_set1_epi32(1022); // add 1 to make our definition identical to frexp()
+
+    __m256i iExponent     = _mm256_castpd_si256(_mm256_and_pd(value.simdInternal_, exponentMask));
+    __m128i iExponentHigh = _mm256_extractf128_si256(iExponent, 0x1);
+    __m128i iExponentLow  = _mm256_castsi256_si128(iExponent);
+    iExponentLow          = _mm_srli_epi64(iExponentLow, 52);
+    iExponentHigh         = _mm_srli_epi64(iExponentHigh, 52);
+    iExponentLow          = _mm_shuffle_epi32(iExponentLow, _MM_SHUFFLE(1, 1, 2, 0));
+    iExponentHigh         = _mm_shuffle_epi32(iExponentHigh, _MM_SHUFFLE(2, 0, 1, 1));
+    // We need to store the return in a 128-bit integer variable, so reuse iExponentLow for both
+    iExponentLow = _mm_or_si128(iExponentLow, iExponentHigh);
+    iExponentLow = _mm_sub_epi32(iExponentLow, exponentBias);
+
+    __m256d result = _mm256_or_pd(_mm256_and_pd(value.simdInternal_, mantissaMask), half);
+
+    if (opt == MathOptimization::Safe)
+    {
+        __m256d valueIsZero = _mm256_cmp_pd(_mm256_setzero_pd(), value.simdInternal_, _CMP_EQ_OQ);
+        // This looks more complex than it is: the valueIsZero variable contains 4x 64-bit double
+        // precision fields, but a bit below we'll need a corresponding integer variable with 4x
+        // 32-bit fields. Since AVX1 does not support shuffling across the upper/lower 128-bit
+        // lanes, we need to extract those first, and then shuffle between two 128-bit variables.
+        __m128i iValueIsZero = _mm_castps_si128(
+                _mm_shuffle_ps(_mm256_extractf128_ps(_mm256_castpd_ps(valueIsZero), 0x0),
+                               _mm256_extractf128_ps(_mm256_castpd_ps(valueIsZero), 0x1),
+                               _MM_SHUFFLE(2, 0, 2, 0)));
+
+        // Set exponent to 0 when input value was zero
+        iExponentLow = _mm_andnot_si128(iValueIsZero, iExponentLow);
+
+        // Set result to +-0 if the corresponding input value was +-0
+        result = _mm256_blendv_pd(result, value.simdInternal_, valueIsZero);
+    }
+    exponent->simdInternal_ = iExponentLow;
+
+    return { result };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble ldexp(SimdDouble value, SimdDInt32 exponent)
+{
+    const __m128i exponentBias = _mm_set1_epi32(1023);
+    __m128i       iExponentLow, iExponentHigh;
+    __m256d       fExponent;
+
+    iExponentLow = _mm_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponentLow = _mm_max_epi32(iExponentLow, _mm_setzero_si128());
+    }
+
+    iExponentHigh = _mm_shuffle_epi32(iExponentLow, _MM_SHUFFLE(3, 3, 2, 2));
+    iExponentLow  = _mm_shuffle_epi32(iExponentLow, _MM_SHUFFLE(1, 1, 0, 0));
+    iExponentHigh = _mm_slli_epi64(iExponentHigh, 52);
+    iExponentLow  = _mm_slli_epi64(iExponentLow, 52);
+    fExponent     = _mm256_castsi256_pd(
+            _mm256_insertf128_si256(_mm256_castsi128_si256(iExponentLow), iExponentHigh, 0x1));
+    return { _mm256_mul_pd(value.simdInternal_, fExponent) };
+}
+#endif
+
+static inline double gmx_simdcall reduce(SimdDouble a)
+{
+    __m128d a0, a1;
+    a.simdInternal_ = _mm256_add_pd(a.simdInternal_, _mm256_permute_pd(a.simdInternal_, 0b0101));
+    a0              = _mm256_castpd256_pd128(a.simdInternal_);
+    a1              = _mm256_extractf128_pd(a.simdInternal_, 0x1);
+    a0              = _mm_add_sd(a0, a1);
+
+    return *reinterpret_cast<double*>(&a0);
+}
+
+static inline SimdDBool gmx_simdcall operator==(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_EQ_OQ) };
+}
+
+static inline SimdDBool gmx_simdcall operator!=(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_NEQ_OQ) };
+}
+
+static inline SimdDBool gmx_simdcall operator<(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_LT_OQ) };
+}
+
+static inline SimdDBool gmx_simdcall operator<=(SimdDouble a, SimdDouble b)
+{
+    return { _mm256_cmp_pd(a.simdInternal_, b.simdInternal_, _CMP_LE_OQ) };
+}
+
+// Override for AVX2 and higher
+#if GMX_SIMD_X86_AVX_256
+static inline SimdDBool gmx_simdcall testBits(SimdDouble a)
+{
+    // Do an or of the low/high 32 bits of each double (so the data is replicated),
+    // and then use the same algorithm as we use for single precision.
+    __m256 tst = _mm256_castpd_ps(a.simdInternal_);
+
+    tst = _mm256_or_ps(tst, _mm256_permute_ps(tst, _MM_SHUFFLE(2, 3, 0, 1)));
+    tst = _mm256_cvtepi32_ps(_mm256_castps_si256(tst));
+
+    return { _mm256_castps_pd(_mm256_cmp_ps(tst, _mm256_setzero_ps(), _CMP_NEQ_OQ)) };
+}
+#endif
+
+static inline SimdDBool gmx_simdcall operator&&(SimdDBool a, SimdDBool b)
+{
+    return { _mm256_and_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator||(SimdDBool a, SimdDBool b)
+{
+    return { _mm256_or_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDBool a)
+{
+    return _mm256_movemask_pd(a.simdInternal_) != 0;
+}
+
+static inline SimdDouble gmx_simdcall selectByMask(SimdDouble a, SimdDBool mask)
+{
+    return { _mm256_and_pd(a.simdInternal_, mask.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall selectByNotMask(SimdDouble a, SimdDBool mask)
+{
+    return { _mm256_andnot_pd(mask.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall blend(SimdDouble a, SimdDouble b, SimdDBool sel)
+{
+    return { _mm256_blendv_pd(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator&(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_and_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall andNot(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_andnot_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator|(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_or_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator^(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_xor_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator+(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_add_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator-(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_sub_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator*(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_mullo_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator==(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_cmpeq_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator<(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_cmplt_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall testBits(SimdDInt32 a)
+{
+    __m128i x   = a.simdInternal_;
+    __m128i res = _mm_andnot_si128(_mm_cmpeq_epi32(x, _mm_setzero_si128()), _mm_cmpeq_epi32(x, x));
+
+    return { res };
+}
+
+static inline SimdDIBool gmx_simdcall operator&&(SimdDIBool a, SimdDIBool b)
+{
+    return { _mm_and_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator||(SimdDIBool a, SimdDIBool b)
+{
+    return { _mm_or_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDIBool a)
+{
+    return _mm_movemask_epi8(a.simdInternal_) != 0;
+}
+
+static inline SimdDInt32 gmx_simdcall selectByMask(SimdDInt32 a, SimdDIBool mask)
+{
+    return { _mm_and_si128(a.simdInternal_, mask.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall selectByNotMask(SimdDInt32 a, SimdDIBool mask)
+{
+    return { _mm_andnot_si128(mask.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall blend(SimdDInt32 a, SimdDInt32 b, SimdDIBool sel)
+{
+    return { _mm_blendv_epi8(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall cvtR2I(SimdDouble a)
+{
+    return { _mm256_cvtpd_epi32(a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall cvttR2I(SimdDouble a)
+{
+    return { _mm256_cvttpd_epi32(a.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall cvtI2R(SimdDInt32 a)
+{
+    return { _mm256_cvtepi32_pd(a.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall cvtB2IB(SimdDBool a)
+{
+    __m128i a1 = _mm256_extractf128_si256(_mm256_castpd_si256(a.simdInternal_), 0x1);
+    __m128i a0 = _mm256_castsi256_si128(_mm256_castpd_si256(a.simdInternal_));
+    a0         = _mm_shuffle_epi32(a0, _MM_SHUFFLE(2, 0, 2, 0));
+    a1         = _mm_shuffle_epi32(a1, _MM_SHUFFLE(2, 0, 2, 0));
+
+    return { _mm_blend_epi16(a0, a1, 0xF0) };
+}
+
+static inline SimdDBool gmx_simdcall cvtIB2B(SimdDIBool a)
+{
+    __m128d lo = _mm_castsi128_pd(_mm_unpacklo_epi32(a.simdInternal_, a.simdInternal_));
+    __m128d hi = _mm_castsi128_pd(_mm_unpackhi_epi32(a.simdInternal_, a.simdInternal_));
+
+    return { _mm256_insertf128_pd(_mm256_castpd128_pd256(lo), hi, 0x1) };
+}
+
+static inline void gmx_simdcall cvtF2DD(SimdFloat f, SimdDouble* d0, SimdDouble* d1)
+{
+    d0->simdInternal_ = _mm256_cvtps_pd(_mm256_castps256_ps128(f.simdInternal_));
+    d1->simdInternal_ = _mm256_cvtps_pd(_mm256_extractf128_ps(f.simdInternal_, 0x1));
+}
+
+static inline SimdFloat gmx_simdcall cvtDD2F(SimdDouble d0, SimdDouble d1)
+{
+    __m128 f0 = _mm256_cvtpd_ps(d0.simdInternal_);
+    __m128 f1 = _mm256_cvtpd_ps(d1.simdInternal_);
+    return { _mm256_insertf128_ps(_mm256_castps128_ps256(f0), f1, 0x1) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_256_SIMD_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_float.h b/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_simd_float.h
new file mode 100644 (file)
index 0000000..8bccb00
--- /dev/null
@@ -0,0 +1,438 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_256_SIMD_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX_256_SIMD_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <immintrin.h>
+
+#include "gromacs/math/utilities.h"
+
+namespace gmx
+{
+
+class SimdFloat
+{
+public:
+    MSVC_DIAGNOSTIC_IGNORE(26495) // simdInternal_ is not being initialized!
+    SimdFloat() {}
+    MSVC_DIAGNOSTIC_RESET
+    SimdFloat(float f) : simdInternal_(_mm256_set1_ps(f)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFloat(__m256 simd) : simdInternal_(simd) {}
+
+    __m256 simdInternal_;
+};
+
+class SimdFInt32
+{
+public:
+    MSVC_DIAGNOSTIC_IGNORE(26495) // simdInternal_ is not being initialized!
+    SimdFInt32() {}
+    MSVC_DIAGNOSTIC_RESET
+    SimdFInt32(std::int32_t i) : simdInternal_(_mm256_set1_epi32(i)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFInt32(__m256i simd) : simdInternal_(simd) {}
+
+    __m256i simdInternal_;
+};
+
+class SimdFBool
+{
+public:
+    MSVC_DIAGNOSTIC_IGNORE(26495) // simdInternal_ is not being initialized!
+    SimdFBool() {}
+    MSVC_DIAGNOSTIC_RESET
+    SimdFBool(bool b) : simdInternal_(_mm256_castsi256_ps(_mm256_set1_epi32(b ? 0xFFFFFFFF : 0))) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFBool(__m256 simd) : simdInternal_(simd) {}
+
+    __m256 simdInternal_;
+};
+
+static inline SimdFloat gmx_simdcall simdLoad(const float* m, SimdFloatTag /*unused*/ = {})
+{
+    assert(std::size_t(m) % 32 == 0);
+    return { _mm256_load_ps(m) };
+}
+
+static inline void gmx_simdcall store(float* m, SimdFloat a)
+{
+    assert(std::size_t(m) % 32 == 0);
+    _mm256_store_ps(m, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall simdLoadU(const float* m, SimdFloatTag /*unused*/ = {})
+{
+    return { _mm256_loadu_ps(m) };
+}
+
+static inline void gmx_simdcall storeU(float* m, SimdFloat a)
+{
+    _mm256_storeu_ps(m, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall setZeroF()
+{
+    return { _mm256_setzero_ps() };
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdFInt32Tag /*unused*/)
+{
+    assert(std::size_t(m) % 32 == 0);
+    return { _mm256_load_si256(reinterpret_cast<const __m256i*>(m)) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdFInt32 a)
+{
+    assert(std::size_t(m) % 32 == 0);
+    _mm256_store_si256(reinterpret_cast<__m256i*>(m), a.simdInternal_);
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdFInt32Tag /*unused*/)
+{
+    return { _mm256_loadu_si256(reinterpret_cast<const __m256i*>(m)) };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdFInt32 a)
+{
+    _mm256_storeu_si256(reinterpret_cast<__m256i*>(m), a.simdInternal_);
+}
+
+static inline SimdFInt32 gmx_simdcall setZeroFI()
+{
+    return { _mm256_setzero_si256() };
+}
+
+template<int index>
+static inline std::int32_t gmx_simdcall extract(SimdFInt32 a)
+{
+    return _mm_extract_epi32(_mm256_extractf128_si256(a.simdInternal_, index >> 2), index & 0x3);
+}
+
+static inline SimdFloat gmx_simdcall operator&(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_and_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall andNot(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_andnot_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator|(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_or_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator^(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_xor_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator+(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_add_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_sub_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat x)
+{
+    return { _mm256_xor_ps(x.simdInternal_, _mm256_set1_ps(GMX_FLOAT_NEGZERO)) };
+}
+
+static inline SimdFloat gmx_simdcall operator*(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_mul_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for AVX2 and higher
+#if GMX_SIMD_X86_AVX_256
+static inline SimdFloat gmx_simdcall fma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm256_add_ps(_mm256_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm256_sub_ps(_mm256_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm256_sub_ps(c.simdInternal_, _mm256_mul_ps(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall fnms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm256_sub_ps(_mm256_setzero_ps(),
+                           _mm256_add_ps(_mm256_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_)) };
+}
+#endif
+
+static inline SimdFloat gmx_simdcall rsqrt(SimdFloat x)
+{
+    return { _mm256_rsqrt_ps(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall rcp(SimdFloat x)
+{
+    return { _mm256_rcp_ps(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskAdd(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    return { _mm256_add_ps(a.simdInternal_, _mm256_and_ps(b.simdInternal_, m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall maskzMul(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    return { _mm256_and_ps(_mm256_mul_ps(a.simdInternal_, b.simdInternal_), m.simdInternal_) };
+}
+
+static inline SimdFloat maskzFma(SimdFloat a, SimdFloat b, SimdFloat c, SimdFBool m)
+{
+    return { _mm256_and_ps(_mm256_add_ps(_mm256_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_),
+                           m.simdInternal_) };
+}
+
+static inline SimdFloat maskzRsqrt(SimdFloat x, SimdFBool m)
+{
+#ifndef NDEBUG
+    x.simdInternal_ = _mm256_blendv_ps(_mm256_set1_ps(1.0F), x.simdInternal_, m.simdInternal_);
+#endif
+    return { _mm256_and_ps(_mm256_rsqrt_ps(x.simdInternal_), m.simdInternal_) };
+}
+
+static inline SimdFloat maskzRcp(SimdFloat x, SimdFBool m)
+{
+#ifndef NDEBUG
+    x.simdInternal_ = _mm256_blendv_ps(_mm256_set1_ps(1.0F), x.simdInternal_, m.simdInternal_);
+#endif
+    return { _mm256_and_ps(_mm256_rcp_ps(x.simdInternal_), m.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall abs(SimdFloat x)
+{
+    return { _mm256_andnot_ps(_mm256_set1_ps(GMX_FLOAT_NEGZERO), x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall max(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_max_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall min(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_min_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall round(SimdFloat x)
+{
+    return { _mm256_round_ps(x.simdInternal_, _MM_FROUND_NINT) };
+}
+
+static inline SimdFloat gmx_simdcall trunc(SimdFloat x)
+{
+    return { _mm256_round_ps(x.simdInternal_, _MM_FROUND_TRUNC) };
+}
+
+// Override for AVX2 and higher
+#if GMX_SIMD_X86_AVX_256
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall frexp(SimdFloat value, SimdFInt32* exponent)
+{
+    const __m256 exponentMask = _mm256_castsi256_ps(_mm256_set1_epi32(0x7F800000));
+    const __m256 mantissaMask = _mm256_castsi256_ps(_mm256_set1_epi32(0x807FFFFF));
+    const __m256 half         = _mm256_set1_ps(0.5);
+    const __m128i exponentBias = _mm_set1_epi32(126); // add 1 to make our definition identical to frexp()
+
+    __m256i iExponent     = _mm256_castps_si256(_mm256_and_ps(value.simdInternal_, exponentMask));
+    __m128i iExponentHigh = _mm256_extractf128_si256(iExponent, 0x1);
+    __m128i iExponentLow  = _mm256_castsi256_si128(iExponent);
+    iExponentLow          = _mm_srli_epi32(iExponentLow, 23);
+    iExponentHigh         = _mm_srli_epi32(iExponentHigh, 23);
+    iExponentLow          = _mm_sub_epi32(iExponentLow, exponentBias);
+    iExponentHigh         = _mm_sub_epi32(iExponentHigh, exponentBias);
+    iExponent             = _mm256_castsi128_si256(iExponentLow);
+    iExponent             = _mm256_insertf128_si256(iExponent, iExponentHigh, 0x1);
+
+    __m256 result = _mm256_or_ps(_mm256_and_ps(value.simdInternal_, mantissaMask), half);
+
+    if (opt == MathOptimization::Safe)
+    {
+        __m256 valueIsZero = _mm256_cmp_ps(_mm256_setzero_ps(), value.simdInternal_, _CMP_EQ_OQ);
+        // Set the upper/lower 64-bit-fields of "iExponent" to 0-bits if the corresponding input value was +-0.0
+        // ... but we need to do the actual andnot operation as float, since AVX1 does not support integer ops.
+        iExponent = _mm256_castps_si256(_mm256_andnot_ps(valueIsZero, _mm256_castsi256_ps(iExponent)));
+
+        // Set result to +-0 if the corresponding input value was +-0
+        result = _mm256_blendv_ps(result, value.simdInternal_, valueIsZero);
+    }
+
+    exponent->simdInternal_ = iExponent;
+
+    return { result };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall ldexp(SimdFloat value, SimdFInt32 exponent)
+{
+    const __m128i exponentBias = _mm_set1_epi32(127);
+    __m256i       iExponent;
+    __m128i       iExponentLow, iExponentHigh;
+
+    iExponentHigh = _mm256_extractf128_si256(exponent.simdInternal_, 0x1);
+    iExponentLow  = _mm256_castsi256_si128(exponent.simdInternal_);
+
+    iExponentLow  = _mm_add_epi32(iExponentLow, exponentBias);
+    iExponentHigh = _mm_add_epi32(iExponentHigh, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponentLow  = _mm_max_epi32(iExponentLow, _mm_setzero_si128());
+        iExponentHigh = _mm_max_epi32(iExponentHigh, _mm_setzero_si128());
+    }
+
+    iExponentLow  = _mm_slli_epi32(iExponentLow, 23);
+    iExponentHigh = _mm_slli_epi32(iExponentHigh, 23);
+    iExponent     = _mm256_castsi128_si256(iExponentLow);
+    iExponent     = _mm256_insertf128_si256(iExponent, iExponentHigh, 0x1);
+    return { _mm256_mul_ps(value.simdInternal_, _mm256_castsi256_ps(iExponent)) };
+}
+#endif
+
+static inline float gmx_simdcall reduce(SimdFloat a)
+{
+    __m128 t0;
+    t0 = _mm_add_ps(_mm256_castps256_ps128(a.simdInternal_), _mm256_extractf128_ps(a.simdInternal_, 0x1));
+    t0 = _mm_add_ps(t0, _mm_permute_ps(t0, _MM_SHUFFLE(1, 0, 3, 2)));
+    t0 = _mm_add_ss(t0, _mm_permute_ps(t0, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&t0);
+}
+
+static inline SimdFBool gmx_simdcall operator==(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_cmp_ps(a.simdInternal_, b.simdInternal_, _CMP_EQ_OQ) };
+}
+
+static inline SimdFBool gmx_simdcall operator!=(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_cmp_ps(a.simdInternal_, b.simdInternal_, _CMP_NEQ_OQ) };
+}
+
+static inline SimdFBool gmx_simdcall operator<(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_cmp_ps(a.simdInternal_, b.simdInternal_, _CMP_LT_OQ) };
+}
+
+static inline SimdFBool gmx_simdcall operator<=(SimdFloat a, SimdFloat b)
+{
+    return { _mm256_cmp_ps(a.simdInternal_, b.simdInternal_, _CMP_LE_OQ) };
+}
+
+// Override for AVX2 and higher
+#if GMX_SIMD_X86_AVX_256
+static inline SimdFBool gmx_simdcall testBits(SimdFloat a)
+{
+    __m256 tst = _mm256_cvtepi32_ps(_mm256_castps_si256(a.simdInternal_));
+
+    return { _mm256_cmp_ps(tst, _mm256_setzero_ps(), _CMP_NEQ_OQ) };
+}
+#endif
+
+static inline SimdFBool gmx_simdcall operator&&(SimdFBool a, SimdFBool b)
+{
+    return { _mm256_and_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator||(SimdFBool a, SimdFBool b)
+{
+    return { _mm256_or_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFBool a)
+{
+    return _mm256_movemask_ps(a.simdInternal_) != 0;
+}
+
+static inline SimdFloat gmx_simdcall selectByMask(SimdFloat a, SimdFBool mask)
+{
+    return { _mm256_and_ps(a.simdInternal_, mask.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall selectByNotMask(SimdFloat a, SimdFBool mask)
+{
+    return { _mm256_andnot_ps(mask.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall blend(SimdFloat a, SimdFloat b, SimdFBool sel)
+{
+    return { _mm256_blendv_ps(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvtR2I(SimdFloat a)
+{
+    return { _mm256_cvtps_epi32(a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvttR2I(SimdFloat a)
+{
+    return { _mm256_cvttps_epi32(a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall cvtI2R(SimdFInt32 a)
+{
+    return { _mm256_cvtepi32_ps(a.simdInternal_) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_256_SIMD_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_util_double.h b/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_util_double.h
new file mode 100644 (file)
index 0000000..19e6bc8
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_256_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX_256_UTIL_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <immintrin.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_x86_avx_256_simd_double.h"
+
+namespace gmx
+{
+
+// Internal utility function: Full 4x4 transpose of __m256d
+static inline void gmx_simdcall avx256Transpose4By4(__m256d* v0, __m256d* v1, __m256d* v2, __m256d* v3)
+{
+    __m256d t1 = _mm256_unpacklo_pd(*v0, *v1);
+    __m256d t2 = _mm256_unpackhi_pd(*v0, *v1);
+    __m256d t3 = _mm256_unpacklo_pd(*v2, *v3);
+    __m256d t4 = _mm256_unpackhi_pd(*v2, *v3);
+    *v0        = _mm256_permute2f128_pd(t1, t3, 0x20);
+    *v1        = _mm256_permute2f128_pd(t2, t4, 0x20);
+    *v2        = _mm256_permute2f128_pd(t1, t3, 0x31);
+    *v3        = _mm256_permute2f128_pd(t2, t4, 0x31);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const double*      base,
+                                                    const std::int32_t offset[],
+                                                    SimdDouble*        v0,
+                                                    SimdDouble*        v1,
+                                                    SimdDouble*        v2,
+                                                    SimdDouble*        v3)
+{
+    assert(std::size_t(offset) % 16 == 0);
+    assert(std::size_t(base) % 32 == 0);
+    assert(align % 4 == 0);
+
+    v0->simdInternal_ = _mm256_load_pd(base + align * offset[0]);
+    v1->simdInternal_ = _mm256_load_pd(base + align * offset[1]);
+    v2->simdInternal_ = _mm256_load_pd(base + align * offset[2]);
+    v3->simdInternal_ = _mm256_load_pd(base + align * offset[3]);
+    avx256Transpose4By4(&v0->simdInternal_, &v1->simdInternal_, &v2->simdInternal_, &v3->simdInternal_);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadTranspose(const double* base, const std::int32_t offset[], SimdDouble* v0, SimdDouble* v1)
+{
+    __m128d t1, t2, t3, t4;
+    __m256d tA, tB;
+
+    assert(std::size_t(offset) % 16 == 0);
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 2 == 0);
+
+    t1 = _mm_load_pd(base + align * offset[0]);
+    t2 = _mm_load_pd(base + align * offset[1]);
+    t3 = _mm_load_pd(base + align * offset[2]);
+    t4 = _mm_load_pd(base + align * offset[3]);
+    tA = _mm256_insertf128_pd(_mm256_castpd128_pd256(t1), t3, 0x1);
+    tB = _mm256_insertf128_pd(_mm256_castpd128_pd256(t2), t4, 0x1);
+
+    v0->simdInternal_ = _mm256_unpacklo_pd(tA, tB);
+    v1->simdInternal_ = _mm256_unpackhi_pd(tA, tB);
+}
+
+static const int c_simdBestPairAlignmentDouble = 2;
+
+// With the implementation below, thread-sanitizer can detect false positives.
+// For loading a triplet, we load 4 floats and ignore the last. Another thread
+// might write to this element, but that will not affect the result.
+// On AVX2 we can use a gather intrinsic instead.
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const double*      base,
+                                                     const std::int32_t offset[],
+                                                     SimdDouble*        v0,
+                                                     SimdDouble*        v1,
+                                                     SimdDouble*        v2)
+{
+    assert(std::size_t(offset) % 16 == 0);
+
+    __m256d t1, t2, t3, t4, t5, t6, t7, t8;
+    if (align % 4 == 0)
+    {
+        t1 = _mm256_load_pd(base + align * offset[0]);
+        t2 = _mm256_load_pd(base + align * offset[1]);
+        t3 = _mm256_load_pd(base + align * offset[2]);
+        t4 = _mm256_load_pd(base + align * offset[3]);
+    }
+    else
+    {
+        t1 = _mm256_loadu_pd(base + align * offset[0]);
+        t2 = _mm256_loadu_pd(base + align * offset[1]);
+        t3 = _mm256_loadu_pd(base + align * offset[2]);
+        t4 = _mm256_loadu_pd(base + align * offset[3]);
+    }
+    t5                = _mm256_unpacklo_pd(t1, t2);
+    t6                = _mm256_unpackhi_pd(t1, t2);
+    t7                = _mm256_unpacklo_pd(t3, t4);
+    t8                = _mm256_unpackhi_pd(t3, t4);
+    v0->simdInternal_ = _mm256_permute2f128_pd(t5, t7, 0x20);
+    v1->simdInternal_ = _mm256_permute2f128_pd(t6, t8, 0x20);
+    v2->simdInternal_ = _mm256_permute2f128_pd(t5, t7, 0x31);
+}
+
+template<int align>
+static inline void gmx_simdcall transposeScatterStoreU(double*            base,
+                                                       const std::int32_t offset[],
+                                                       SimdDouble         v0,
+                                                       SimdDouble         v1,
+                                                       SimdDouble         v2)
+{
+    __m256d t0, t1, t2;
+
+
+    assert(std::size_t(offset) % 16 == 0);
+
+    // v0: x0 x1 | x2 x3
+    // v1: y0 y1 | y2 y3
+    // v2: z0 z1 | z2 z3
+
+    t0 = _mm256_unpacklo_pd(v0.simdInternal_, v1.simdInternal_); // x0 y0 | x2 y2
+    t1 = _mm256_unpackhi_pd(v0.simdInternal_, v1.simdInternal_); // x1 y1 | x3 y3
+    t2 = _mm256_unpackhi_pd(v2.simdInternal_, v2.simdInternal_); // z1 z1 | z3 z3
+
+    _mm_storeu_pd(base + align * offset[0], _mm256_castpd256_pd128(t0));
+    _mm_storeu_pd(base + align * offset[1], _mm256_castpd256_pd128(t1));
+    _mm_storeu_pd(base + align * offset[2], _mm256_extractf128_pd(t0, 0x1));
+    _mm_storeu_pd(base + align * offset[3], _mm256_extractf128_pd(t1, 0x1));
+    _mm_store_sd(base + align * offset[0] + 2, _mm256_castpd256_pd128(v2.simdInternal_));
+    _mm_store_sd(base + align * offset[1] + 2, _mm256_castpd256_pd128(t2));
+    _mm_store_sd(base + align * offset[2] + 2, _mm256_extractf128_pd(v2.simdInternal_, 0x1));
+    _mm_store_sd(base + align * offset[3] + 2, _mm256_extractf128_pd(t2, 0x1));
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    __m256d t0, t1;
+    __m128d t2, tA, tB;
+
+    assert(std::size_t(offset) % 16 == 0);
+
+    if (align % 4 == 0)
+    {
+        // we can use aligned load/store
+        t0 = _mm256_setzero_pd();
+        avx256Transpose4By4(&v0.simdInternal_, &v1.simdInternal_, &v2.simdInternal_, &t0);
+        _mm256_store_pd(base + align * offset[0],
+                        _mm256_add_pd(_mm256_load_pd(base + align * offset[0]), v0.simdInternal_));
+        _mm256_store_pd(base + align * offset[1],
+                        _mm256_add_pd(_mm256_load_pd(base + align * offset[1]), v1.simdInternal_));
+        _mm256_store_pd(base + align * offset[2],
+                        _mm256_add_pd(_mm256_load_pd(base + align * offset[2]), v2.simdInternal_));
+        _mm256_store_pd(base + align * offset[3],
+                        _mm256_add_pd(_mm256_load_pd(base + align * offset[3]), t0));
+    }
+    else
+    {
+        // v0: x0 x1 | x2 x3
+        // v1: y0 y1 | y2 y3
+        // v2: z0 z1 | z2 z3
+
+        t0 = _mm256_unpacklo_pd(v0.simdInternal_, v1.simdInternal_); // x0 y0 | x2 y2
+        t1 = _mm256_unpackhi_pd(v0.simdInternal_, v1.simdInternal_); // x1 y1 | x3 y3
+        t2 = _mm256_extractf128_pd(v2.simdInternal_, 0x1);           // z2 z3
+
+        tA = _mm_loadu_pd(base + align * offset[0]);
+        tB = _mm_load_sd(base + align * offset[0] + 2);
+        tA = _mm_add_pd(tA, _mm256_castpd256_pd128(t0));
+        tB = _mm_add_pd(tB, _mm256_castpd256_pd128(v2.simdInternal_));
+        _mm_storeu_pd(base + align * offset[0], tA);
+        _mm_store_sd(base + align * offset[0] + 2, tB);
+
+        tA = _mm_loadu_pd(base + align * offset[1]);
+        tB = _mm_loadh_pd(_mm_setzero_pd(), base + align * offset[1] + 2);
+        tA = _mm_add_pd(tA, _mm256_castpd256_pd128(t1));
+        tB = _mm_add_pd(tB, _mm256_castpd256_pd128(v2.simdInternal_));
+        _mm_storeu_pd(base + align * offset[1], tA);
+        _mm_storeh_pd(base + align * offset[1] + 2, tB);
+
+        tA = _mm_loadu_pd(base + align * offset[2]);
+        tB = _mm_load_sd(base + align * offset[2] + 2);
+        tA = _mm_add_pd(tA, _mm256_extractf128_pd(t0, 0x1));
+        tB = _mm_add_pd(tB, t2);
+        _mm_storeu_pd(base + align * offset[2], tA);
+        _mm_store_sd(base + align * offset[2] + 2, tB);
+
+        tA = _mm_loadu_pd(base + align * offset[3]);
+        tB = _mm_loadh_pd(_mm_setzero_pd(), base + align * offset[3] + 2);
+        tA = _mm_add_pd(tA, _mm256_extractf128_pd(t1, 0x1));
+        tB = _mm_add_pd(tB, t2);
+        _mm_storeu_pd(base + align * offset[3], tA);
+        _mm_storeh_pd(base + align * offset[3] + 2, tB);
+    }
+}
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    __m256d t0, t1;
+    __m128d t2, tA, tB;
+
+    assert(std::size_t(offset) % 16 == 0);
+
+    if (align % 4 == 0)
+    {
+        // we can use aligned load/store
+        t0 = _mm256_setzero_pd();
+        avx256Transpose4By4(&v0.simdInternal_, &v1.simdInternal_, &v2.simdInternal_, &t0);
+        _mm256_store_pd(base + align * offset[0],
+                        _mm256_sub_pd(_mm256_load_pd(base + align * offset[0]), v0.simdInternal_));
+        _mm256_store_pd(base + align * offset[1],
+                        _mm256_sub_pd(_mm256_load_pd(base + align * offset[1]), v1.simdInternal_));
+        _mm256_store_pd(base + align * offset[2],
+                        _mm256_sub_pd(_mm256_load_pd(base + align * offset[2]), v2.simdInternal_));
+        _mm256_store_pd(base + align * offset[3],
+                        _mm256_sub_pd(_mm256_load_pd(base + align * offset[3]), t0));
+    }
+    else
+    {
+        // v0: x0 x1 | x2 x3
+        // v1: y0 y1 | y2 y3
+        // v2: z0 z1 | z2 z3
+
+        t0 = _mm256_unpacklo_pd(v0.simdInternal_, v1.simdInternal_); // x0 y0 | x2 y2
+        t1 = _mm256_unpackhi_pd(v0.simdInternal_, v1.simdInternal_); // x1 y1 | x3 y3
+        t2 = _mm256_extractf128_pd(v2.simdInternal_, 0x1);           // z2 z3
+
+        tA = _mm_loadu_pd(base + align * offset[0]);
+        tB = _mm_load_sd(base + align * offset[0] + 2);
+        tA = _mm_sub_pd(tA, _mm256_castpd256_pd128(t0));
+        tB = _mm_sub_pd(tB, _mm256_castpd256_pd128(v2.simdInternal_));
+        _mm_storeu_pd(base + align * offset[0], tA);
+        _mm_store_sd(base + align * offset[0] + 2, tB);
+
+        tA = _mm_loadu_pd(base + align * offset[1]);
+        tB = _mm_loadh_pd(_mm_setzero_pd(), base + align * offset[1] + 2);
+        tA = _mm_sub_pd(tA, _mm256_castpd256_pd128(t1));
+        tB = _mm_sub_pd(tB, _mm256_castpd256_pd128(v2.simdInternal_));
+        _mm_storeu_pd(base + align * offset[1], tA);
+        _mm_storeh_pd(base + align * offset[1] + 2, tB);
+
+        tA = _mm_loadu_pd(base + align * offset[2]);
+        tB = _mm_load_sd(base + align * offset[2] + 2);
+        tA = _mm_sub_pd(tA, _mm256_extractf128_pd(t0, 0x1));
+        tB = _mm_sub_pd(tB, t2);
+        _mm_storeu_pd(base + align * offset[2], tA);
+        _mm_store_sd(base + align * offset[2] + 2, tB);
+
+        tA = _mm_loadu_pd(base + align * offset[3]);
+        tB = _mm_loadh_pd(_mm_setzero_pd(), base + align * offset[3] + 2);
+        tA = _mm_sub_pd(tA, _mm256_extractf128_pd(t1, 0x1));
+        tB = _mm_sub_pd(tB, t2);
+        _mm_storeu_pd(base + align * offset[3], tA);
+        _mm_storeh_pd(base + align * offset[3] + 2, tB);
+    }
+}
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdDouble  scalar,
+                                                        SimdDouble* triplets0,
+                                                        SimdDouble* triplets1,
+                                                        SimdDouble* triplets2)
+{
+    __m256d t0 = _mm256_permute2f128_pd(scalar.simdInternal_, scalar.simdInternal_, 0x21);
+    __m256d t1 = _mm256_permute_pd(scalar.simdInternal_, 0b0000);
+    __m256d t2 = _mm256_permute_pd(scalar.simdInternal_, 0b1111);
+    triplets0->simdInternal_ = _mm256_blend_pd(t1, t0, 0b1100);
+    triplets1->simdInternal_ = _mm256_blend_pd(t2, t1, 0b1100);
+    triplets2->simdInternal_ = _mm256_blend_pd(t0, t2, 0b1100);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const double* base,
+                                                             SimdDInt32    offset,
+                                                             SimdDouble*   v0,
+                                                             SimdDouble*   v1,
+                                                             SimdDouble*   v2,
+                                                             SimdDouble*   v3)
+{
+    assert(std::size_t(base) % 32 == 0);
+    assert(align % 4 == 0);
+
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_DINT32_WIDTH];
+    _mm_store_si128(reinterpret_cast<__m128i*>(ioffset), offset.simdInternal_);
+
+    v0->simdInternal_ = _mm256_load_pd(base + align * ioffset[0]);
+    v1->simdInternal_ = _mm256_load_pd(base + align * ioffset[1]);
+    v2->simdInternal_ = _mm256_load_pd(base + align * ioffset[2]);
+    v3->simdInternal_ = _mm256_load_pd(base + align * ioffset[3]);
+
+    avx256Transpose4By4(&v0->simdInternal_, &v1->simdInternal_, &v2->simdInternal_, &v3->simdInternal_);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    __m128d t1, t2, t3, t4;
+    __m256d tA, tB;
+
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 2 == 0);
+
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_DINT32_WIDTH];
+    _mm_store_si128(reinterpret_cast<__m128i*>(ioffset), offset.simdInternal_);
+
+    t1 = _mm_load_pd(base + align * ioffset[0]);
+    t2 = _mm_load_pd(base + align * ioffset[1]);
+    t3 = _mm_load_pd(base + align * ioffset[2]);
+    t4 = _mm_load_pd(base + align * ioffset[3]);
+
+    tA                = _mm256_insertf128_pd(_mm256_castpd128_pd256(t1), t3, 0x1);
+    tB                = _mm256_insertf128_pd(_mm256_castpd128_pd256(t2), t4, 0x1);
+    v0->simdInternal_ = _mm256_unpacklo_pd(tA, tB);
+    v1->simdInternal_ = _mm256_unpackhi_pd(tA, tB);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    __m128d t1, t2, t3, t4;
+    __m256d tA, tB;
+
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_DINT32_WIDTH];
+    _mm_store_si128(reinterpret_cast<__m128i*>(ioffset), offset.simdInternal_);
+
+    t1 = _mm_loadu_pd(base + align * ioffset[0]);
+    t2 = _mm_loadu_pd(base + align * ioffset[1]);
+    t3 = _mm_loadu_pd(base + align * ioffset[2]);
+    t4 = _mm_loadu_pd(base + align * ioffset[3]);
+
+    tA = _mm256_insertf128_pd(_mm256_castpd128_pd256(t1), t3, 0x1);
+    tB = _mm256_insertf128_pd(_mm256_castpd128_pd256(t2), t4, 0x1);
+
+    v0->simdInternal_ = _mm256_unpacklo_pd(tA, tB);
+    v1->simdInternal_ = _mm256_unpackhi_pd(tA, tB);
+}
+
+static inline double gmx_simdcall
+reduceIncr4ReturnSum(double* m, SimdDouble v0, SimdDouble v1, SimdDouble v2, SimdDouble v3)
+{
+    __m256d t0, t1, t2;
+    __m128d a0, a1;
+
+    assert(std::size_t(m) % 32 == 0);
+
+    t0 = _mm256_hadd_pd(v0.simdInternal_, v1.simdInternal_);
+    t1 = _mm256_hadd_pd(v2.simdInternal_, v3.simdInternal_);
+    t2 = _mm256_permute2f128_pd(t0, t1, 0x21);
+    t0 = _mm256_add_pd(t0, t2);
+    t1 = _mm256_add_pd(t1, t2);
+    t0 = _mm256_blend_pd(t0, t1, 0b1100);
+
+    t1 = _mm256_add_pd(t0, _mm256_load_pd(m));
+    _mm256_store_pd(m, t1);
+
+    t0 = _mm256_add_pd(t0, _mm256_permute_pd(t0, 0b0101));
+    a0 = _mm256_castpd256_pd128(t0);
+    a1 = _mm256_extractf128_pd(t0, 0x1);
+    a0 = _mm_add_sd(a0, a1);
+
+    return *reinterpret_cast<double*>(&a0);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_256_UTIL_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_util_float.h b/src/include/gromacs/simd/impl_x86_avx_256/impl_x86_avx_256_util_float.h
new file mode 100644 (file)
index 0000000..606bbcc
--- /dev/null
@@ -0,0 +1,706 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_256_UTIL_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX_256_UTIL_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <immintrin.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_x86_avx_256_simd_float.h"
+
+namespace gmx
+{
+
+/* This is an internal helper function used by decr3Hsimd(...).
+ */
+static inline void gmx_simdcall decrHsimd(float* m, SimdFloat a)
+{
+    assert(std::size_t(m) % 16 == 0);
+    __m128 asum = _mm_add_ps(_mm256_castps256_ps128(a.simdInternal_),
+                             _mm256_extractf128_ps(a.simdInternal_, 0x1));
+    _mm_store_ps(m, _mm_sub_ps(_mm_load_ps(m), asum));
+}
+
+/* This is an internal helper function used by the three functions storing,
+ * incrementing, or decrementing data. Do NOT use it outside this file.
+ *
+ * Input v0: [x0 x1 x2 x3 x4 x5 x6 x7]
+ * Input v1: [y0 y1 y2 y3 y4 y5 y6 y7]
+ * Input v2: [z0 z1 z2 z3 z4 z5 z6 z7]
+ * Input v3: Unused
+ *
+ * Output v0: [x0 y0 z0 -  x4 y4 z4 - ]
+ * Output v1: [x1 y1 z1 -  x5 y5 z5 - ]
+ * Output v2: [x2 y2 z2 -  x6 y6 z6 - ]
+ * Output v3: [x3 y3 z3 -  x7 y7 z7 - ]
+ *
+ * Here, - means undefined. Note that such values will not be zero!
+ */
+static inline void gmx_simdcall avx256Transpose3By4InLanes(__m256* v0, __m256* v1, __m256* v2, __m256* v3)
+{
+    __m256 t1 = _mm256_unpacklo_ps(*v0, *v1);
+    __m256 t2 = _mm256_unpackhi_ps(*v0, *v1);
+    *v0       = _mm256_shuffle_ps(t1, *v2, _MM_SHUFFLE(0, 0, 1, 0));
+    *v1       = _mm256_shuffle_ps(t1, *v2, _MM_SHUFFLE(0, 1, 3, 2));
+    *v3       = _mm256_shuffle_ps(t2, *v2, _MM_SHUFFLE(0, 3, 3, 2));
+    *v2       = _mm256_shuffle_ps(t2, *v2, _MM_SHUFFLE(0, 2, 1, 0));
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const float*       base,
+                                                    const std::int32_t offset[],
+                                                    SimdFloat*         v0,
+                                                    SimdFloat*         v1,
+                                                    SimdFloat*         v2,
+                                                    SimdFloat*         v3)
+{
+    __m128 t1, t2, t3, t4, t5, t6, t7, t8;
+    __m256 tA, tB, tC, tD;
+
+    assert(std::size_t(offset) % 32 == 0);
+    assert(std::size_t(base) % 16 == 0);
+    assert(align % 4 == 0);
+
+    t1 = _mm_load_ps(base + align * offset[0]);
+    t2 = _mm_load_ps(base + align * offset[1]);
+    t3 = _mm_load_ps(base + align * offset[2]);
+    t4 = _mm_load_ps(base + align * offset[3]);
+    t5 = _mm_load_ps(base + align * offset[4]);
+    t6 = _mm_load_ps(base + align * offset[5]);
+    t7 = _mm_load_ps(base + align * offset[6]);
+    t8 = _mm_load_ps(base + align * offset[7]);
+
+    v0->simdInternal_ = _mm256_insertf128_ps(_mm256_castps128_ps256(t1), t5, 0x1);
+    v1->simdInternal_ = _mm256_insertf128_ps(_mm256_castps128_ps256(t2), t6, 0x1);
+    v2->simdInternal_ = _mm256_insertf128_ps(_mm256_castps128_ps256(t3), t7, 0x1);
+    v3->simdInternal_ = _mm256_insertf128_ps(_mm256_castps128_ps256(t4), t8, 0x1);
+
+    tA = _mm256_unpacklo_ps(v0->simdInternal_, v1->simdInternal_);
+    tB = _mm256_unpacklo_ps(v2->simdInternal_, v3->simdInternal_);
+    tC = _mm256_unpackhi_ps(v0->simdInternal_, v1->simdInternal_);
+    tD = _mm256_unpackhi_ps(v2->simdInternal_, v3->simdInternal_);
+
+    v0->simdInternal_ = _mm256_shuffle_ps(tA, tB, _MM_SHUFFLE(1, 0, 1, 0));
+    v1->simdInternal_ = _mm256_shuffle_ps(tA, tB, _MM_SHUFFLE(3, 2, 3, 2));
+    v2->simdInternal_ = _mm256_shuffle_ps(tC, tD, _MM_SHUFFLE(1, 0, 1, 0));
+    v3->simdInternal_ = _mm256_shuffle_ps(tC, tD, _MM_SHUFFLE(3, 2, 3, 2));
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadTranspose(const float* base, const std::int32_t offset[], SimdFloat* v0, SimdFloat* v1)
+{
+    __m128 t1, t2, t3, t4, t5, t6, t7, t8;
+    __m256 tA, tB, tC, tD;
+
+    assert(std::size_t(offset) % 32 == 0);
+    assert(std::size_t(base) % 8 == 0);
+    assert(align % 2 == 0);
+
+    t1 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[0]));
+    t2 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[1]));
+    t3 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[2]));
+    t4 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[3]));
+    t5 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[4]));
+    t6 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[5]));
+    t7 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[6]));
+    t8 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[7]));
+
+    tA = _mm256_insertf128_ps(_mm256_castps128_ps256(t1), t5, 0x1);
+    tB = _mm256_insertf128_ps(_mm256_castps128_ps256(t2), t6, 0x1);
+    tC = _mm256_insertf128_ps(_mm256_castps128_ps256(t3), t7, 0x1);
+    tD = _mm256_insertf128_ps(_mm256_castps128_ps256(t4), t8, 0x1);
+
+    tA                = _mm256_unpacklo_ps(tA, tC);
+    tB                = _mm256_unpacklo_ps(tB, tD);
+    v0->simdInternal_ = _mm256_unpacklo_ps(tA, tB);
+    v1->simdInternal_ = _mm256_unpackhi_ps(tA, tB);
+}
+
+static const int c_simdBestPairAlignmentFloat = 2;
+
+// With the implementation below, thread-sanitizer can detect false positives.
+// For loading a triplet, we load 4 floats and ignore the last. Another thread
+// might write to this element, but that will not affect the result.
+// On AVX2 we can use a gather intrinsic instead.
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const float*       base,
+                                                     const std::int32_t offset[],
+                                                     SimdFloat*         v0,
+                                                     SimdFloat*         v1,
+                                                     SimdFloat*         v2)
+{
+    __m256 t1, t2, t3, t4, t5, t6, t7, t8;
+
+    assert(std::size_t(offset) % 32 == 0);
+
+    if (align % 4 == 0)
+    {
+        // we can use aligned loads since base should also be aligned in this case
+        assert(std::size_t(base) % 16 == 0);
+        t1 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(base + align * offset[0])),
+                                  _mm_load_ps(base + align * offset[4]),
+                                  0x1);
+        t2 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(base + align * offset[1])),
+                                  _mm_load_ps(base + align * offset[5]),
+                                  0x1);
+        t3 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(base + align * offset[2])),
+                                  _mm_load_ps(base + align * offset[6]),
+                                  0x1);
+        t4 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(base + align * offset[3])),
+                                  _mm_load_ps(base + align * offset[7]),
+                                  0x1);
+    }
+    else
+    {
+        // Use unaligned loads
+        t1 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(base + align * offset[0])),
+                                  _mm_loadu_ps(base + align * offset[4]),
+                                  0x1);
+        t2 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(base + align * offset[1])),
+                                  _mm_loadu_ps(base + align * offset[5]),
+                                  0x1);
+        t3 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(base + align * offset[2])),
+                                  _mm_loadu_ps(base + align * offset[6]),
+                                  0x1);
+        t4 = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(base + align * offset[3])),
+                                  _mm_loadu_ps(base + align * offset[7]),
+                                  0x1);
+    }
+
+    t5                = _mm256_unpacklo_ps(t1, t2);
+    t6                = _mm256_unpacklo_ps(t3, t4);
+    t7                = _mm256_unpackhi_ps(t1, t2);
+    t8                = _mm256_unpackhi_ps(t3, t4);
+    v0->simdInternal_ = _mm256_shuffle_ps(t5, t6, _MM_SHUFFLE(1, 0, 1, 0));
+    v1->simdInternal_ = _mm256_shuffle_ps(t5, t6, _MM_SHUFFLE(3, 2, 3, 2));
+    v2->simdInternal_ = _mm256_shuffle_ps(t7, t8, _MM_SHUFFLE(1, 0, 1, 0));
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterStoreU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    __m256  tv3;
+    __m128i mask = _mm_set_epi32(0, -1, -1, -1);
+
+    assert(std::size_t(offset) % 32 == 0);
+
+    avx256Transpose3By4InLanes(&v0.simdInternal_, &v1.simdInternal_, &v2.simdInternal_, &tv3);
+    _mm_maskstore_ps(base + align * offset[0], mask, _mm256_castps256_ps128(v0.simdInternal_));
+    _mm_maskstore_ps(base + align * offset[1], mask, _mm256_castps256_ps128(v1.simdInternal_));
+    _mm_maskstore_ps(base + align * offset[2], mask, _mm256_castps256_ps128(v2.simdInternal_));
+    _mm_maskstore_ps(base + align * offset[3], mask, _mm256_castps256_ps128(tv3));
+    _mm_maskstore_ps(base + align * offset[4], mask, _mm256_extractf128_ps(v0.simdInternal_, 0x1));
+    _mm_maskstore_ps(base + align * offset[5], mask, _mm256_extractf128_ps(v1.simdInternal_, 0x1));
+    _mm_maskstore_ps(base + align * offset[6], mask, _mm256_extractf128_ps(v2.simdInternal_, 0x1));
+    _mm_maskstore_ps(base + align * offset[7], mask, _mm256_extractf128_ps(tv3, 0x1));
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    __m256 t1, t2, t3, t4, t5, t6, t7, t8, t9, t10;
+    __m128 tA, tB, tC, tD, tE, tF, tG, tH, tX;
+
+    if (align < 4)
+    {
+        t5  = _mm256_unpacklo_ps(v1.simdInternal_, v2.simdInternal_);
+        t6  = _mm256_unpackhi_ps(v1.simdInternal_, v2.simdInternal_);
+        t7  = _mm256_shuffle_ps(v0.simdInternal_, t5, _MM_SHUFFLE(1, 0, 0, 0));
+        t8  = _mm256_shuffle_ps(v0.simdInternal_, t5, _MM_SHUFFLE(3, 2, 0, 1));
+        t9  = _mm256_shuffle_ps(v0.simdInternal_, t6, _MM_SHUFFLE(1, 0, 0, 2));
+        t10 = _mm256_shuffle_ps(v0.simdInternal_, t6, _MM_SHUFFLE(3, 2, 0, 3));
+
+        tA = _mm256_castps256_ps128(t7);
+        tB = _mm256_castps256_ps128(t8);
+        tC = _mm256_castps256_ps128(t9);
+        tD = _mm256_castps256_ps128(t10);
+        tE = _mm256_extractf128_ps(t7, 0x1);
+        tF = _mm256_extractf128_ps(t8, 0x1);
+        tG = _mm256_extractf128_ps(t9, 0x1);
+        tH = _mm256_extractf128_ps(t10, 0x1);
+
+        tX = _mm_load_ss(base + align * offset[0]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[0] + 1));
+        tX = _mm_add_ps(tX, tA);
+        _mm_store_ss(base + align * offset[0], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[0] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[1]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[1] + 1));
+        tX = _mm_add_ps(tX, tB);
+        _mm_store_ss(base + align * offset[1], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[1] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[2]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[2] + 1));
+        tX = _mm_add_ps(tX, tC);
+        _mm_store_ss(base + align * offset[2], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[2] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[3]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[3] + 1));
+        tX = _mm_add_ps(tX, tD);
+        _mm_store_ss(base + align * offset[3], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[3] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[4]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[4] + 1));
+        tX = _mm_add_ps(tX, tE);
+        _mm_store_ss(base + align * offset[4], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[4] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[5]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[5] + 1));
+        tX = _mm_add_ps(tX, tF);
+        _mm_store_ss(base + align * offset[5], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[5] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[6]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[6] + 1));
+        tX = _mm_add_ps(tX, tG);
+        _mm_store_ss(base + align * offset[6], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[6] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[7]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[7] + 1));
+        tX = _mm_add_ps(tX, tH);
+        _mm_store_ss(base + align * offset[7], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[7] + 1), tX);
+    }
+    else
+    {
+        // Extra elements means we can use full width-4 load/store operations
+        t1 = _mm256_unpacklo_ps(v0.simdInternal_, v2.simdInternal_);
+        t2 = _mm256_unpackhi_ps(v0.simdInternal_, v2.simdInternal_);
+        t3 = _mm256_unpacklo_ps(v1.simdInternal_, _mm256_setzero_ps());
+        t4 = _mm256_unpackhi_ps(v1.simdInternal_, _mm256_setzero_ps());
+        t5 = _mm256_unpacklo_ps(t1, t3); // x0 y0 z0  0 | x4 y4 z4 0
+        t6 = _mm256_unpackhi_ps(t1, t3); // x1 y1 z1  0 | x5 y5 z5 0
+        t7 = _mm256_unpacklo_ps(t2, t4); // x2 y2 z2  0 | x6 y6 z6 0
+        t8 = _mm256_unpackhi_ps(t2, t4); // x3 y3 z3  0 | x7 y7 z7 0
+
+        if (align % 4 == 0)
+        {
+            // We can use aligned load & store
+            _mm_store_ps(base + align * offset[0],
+                         _mm_add_ps(_mm_load_ps(base + align * offset[0]), _mm256_castps256_ps128(t5)));
+            _mm_store_ps(base + align * offset[1],
+                         _mm_add_ps(_mm_load_ps(base + align * offset[1]), _mm256_castps256_ps128(t6)));
+            _mm_store_ps(base + align * offset[2],
+                         _mm_add_ps(_mm_load_ps(base + align * offset[2]), _mm256_castps256_ps128(t7)));
+            _mm_store_ps(base + align * offset[3],
+                         _mm_add_ps(_mm_load_ps(base + align * offset[3]), _mm256_castps256_ps128(t8)));
+            _mm_store_ps(base + align * offset[4],
+                         _mm_add_ps(_mm_load_ps(base + align * offset[4]), _mm256_extractf128_ps(t5, 0x1)));
+            _mm_store_ps(base + align * offset[5],
+                         _mm_add_ps(_mm_load_ps(base + align * offset[5]), _mm256_extractf128_ps(t6, 0x1)));
+            _mm_store_ps(base + align * offset[6],
+                         _mm_add_ps(_mm_load_ps(base + align * offset[6]), _mm256_extractf128_ps(t7, 0x1)));
+            _mm_store_ps(base + align * offset[7],
+                         _mm_add_ps(_mm_load_ps(base + align * offset[7]), _mm256_extractf128_ps(t8, 0x1)));
+        }
+        else
+        {
+            // alignment >=5, but not a multiple of 4
+            _mm_storeu_ps(base + align * offset[0],
+                          _mm_add_ps(_mm_loadu_ps(base + align * offset[0]), _mm256_castps256_ps128(t5)));
+            _mm_storeu_ps(base + align * offset[1],
+                          _mm_add_ps(_mm_loadu_ps(base + align * offset[1]), _mm256_castps256_ps128(t6)));
+            _mm_storeu_ps(base + align * offset[2],
+                          _mm_add_ps(_mm_loadu_ps(base + align * offset[2]), _mm256_castps256_ps128(t7)));
+            _mm_storeu_ps(base + align * offset[3],
+                          _mm_add_ps(_mm_loadu_ps(base + align * offset[3]), _mm256_castps256_ps128(t8)));
+            _mm_storeu_ps(
+                    base + align * offset[4],
+                    _mm_add_ps(_mm_loadu_ps(base + align * offset[4]), _mm256_extractf128_ps(t5, 0x1)));
+            _mm_storeu_ps(
+                    base + align * offset[5],
+                    _mm_add_ps(_mm_loadu_ps(base + align * offset[5]), _mm256_extractf128_ps(t6, 0x1)));
+            _mm_storeu_ps(
+                    base + align * offset[6],
+                    _mm_add_ps(_mm_loadu_ps(base + align * offset[6]), _mm256_extractf128_ps(t7, 0x1)));
+            _mm_storeu_ps(
+                    base + align * offset[7],
+                    _mm_add_ps(_mm_loadu_ps(base + align * offset[7]), _mm256_extractf128_ps(t8, 0x1)));
+        }
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    __m256 t1, t2, t3, t4, t5, t6, t7, t8, t9, t10;
+    __m128 tA, tB, tC, tD, tE, tF, tG, tH, tX;
+
+    if (align < 4)
+    {
+        t5  = _mm256_unpacklo_ps(v1.simdInternal_, v2.simdInternal_);
+        t6  = _mm256_unpackhi_ps(v1.simdInternal_, v2.simdInternal_);
+        t7  = _mm256_shuffle_ps(v0.simdInternal_, t5, _MM_SHUFFLE(1, 0, 0, 0));
+        t8  = _mm256_shuffle_ps(v0.simdInternal_, t5, _MM_SHUFFLE(3, 2, 0, 1));
+        t9  = _mm256_shuffle_ps(v0.simdInternal_, t6, _MM_SHUFFLE(1, 0, 0, 2));
+        t10 = _mm256_shuffle_ps(v0.simdInternal_, t6, _MM_SHUFFLE(3, 2, 0, 3));
+
+        tA = _mm256_castps256_ps128(t7);
+        tB = _mm256_castps256_ps128(t8);
+        tC = _mm256_castps256_ps128(t9);
+        tD = _mm256_castps256_ps128(t10);
+        tE = _mm256_extractf128_ps(t7, 0x1);
+        tF = _mm256_extractf128_ps(t8, 0x1);
+        tG = _mm256_extractf128_ps(t9, 0x1);
+        tH = _mm256_extractf128_ps(t10, 0x1);
+
+        tX = _mm_load_ss(base + align * offset[0]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[0] + 1));
+        tX = _mm_sub_ps(tX, tA);
+        _mm_store_ss(base + align * offset[0], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[0] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[1]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[1] + 1));
+        tX = _mm_sub_ps(tX, tB);
+        _mm_store_ss(base + align * offset[1], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[1] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[2]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[2] + 1));
+        tX = _mm_sub_ps(tX, tC);
+        _mm_store_ss(base + align * offset[2], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[2] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[3]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[3] + 1));
+        tX = _mm_sub_ps(tX, tD);
+        _mm_store_ss(base + align * offset[3], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[3] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[4]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[4] + 1));
+        tX = _mm_sub_ps(tX, tE);
+        _mm_store_ss(base + align * offset[4], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[4] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[5]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[5] + 1));
+        tX = _mm_sub_ps(tX, tF);
+        _mm_store_ss(base + align * offset[5], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[5] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[6]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[6] + 1));
+        tX = _mm_sub_ps(tX, tG);
+        _mm_store_ss(base + align * offset[6], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[6] + 1), tX);
+
+        tX = _mm_load_ss(base + align * offset[7]);
+        tX = _mm_loadh_pi(tX, reinterpret_cast<__m64*>(base + align * offset[7] + 1));
+        tX = _mm_sub_ps(tX, tH);
+        _mm_store_ss(base + align * offset[7], tX);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[7] + 1), tX);
+    }
+    else
+    {
+        // Extra elements means we can use full width-4 load/store operations
+        t1 = _mm256_unpacklo_ps(v0.simdInternal_, v2.simdInternal_);
+        t2 = _mm256_unpackhi_ps(v0.simdInternal_, v2.simdInternal_);
+        t3 = _mm256_unpacklo_ps(v1.simdInternal_, _mm256_setzero_ps());
+        t4 = _mm256_unpackhi_ps(v1.simdInternal_, _mm256_setzero_ps());
+        t5 = _mm256_unpacklo_ps(t1, t3); // x0 y0 z0  0 | x4 y4 z4 0
+        t6 = _mm256_unpackhi_ps(t1, t3); // x1 y1 z1  0 | x5 y5 z5 0
+        t7 = _mm256_unpacklo_ps(t2, t4); // x2 y2 z2  0 | x6 y6 z6 0
+        t8 = _mm256_unpackhi_ps(t2, t4); // x3 y3 z3  0 | x7 y7 z7 0
+
+        if (align % 4 == 0)
+        {
+            // We can use aligned load & store
+            _mm_store_ps(base + align * offset[0],
+                         _mm_sub_ps(_mm_load_ps(base + align * offset[0]), _mm256_castps256_ps128(t5)));
+            _mm_store_ps(base + align * offset[1],
+                         _mm_sub_ps(_mm_load_ps(base + align * offset[1]), _mm256_castps256_ps128(t6)));
+            _mm_store_ps(base + align * offset[2],
+                         _mm_sub_ps(_mm_load_ps(base + align * offset[2]), _mm256_castps256_ps128(t7)));
+            _mm_store_ps(base + align * offset[3],
+                         _mm_sub_ps(_mm_load_ps(base + align * offset[3]), _mm256_castps256_ps128(t8)));
+            _mm_store_ps(base + align * offset[4],
+                         _mm_sub_ps(_mm_load_ps(base + align * offset[4]), _mm256_extractf128_ps(t5, 0x1)));
+            _mm_store_ps(base + align * offset[5],
+                         _mm_sub_ps(_mm_load_ps(base + align * offset[5]), _mm256_extractf128_ps(t6, 0x1)));
+            _mm_store_ps(base + align * offset[6],
+                         _mm_sub_ps(_mm_load_ps(base + align * offset[6]), _mm256_extractf128_ps(t7, 0x1)));
+            _mm_store_ps(base + align * offset[7],
+                         _mm_sub_ps(_mm_load_ps(base + align * offset[7]), _mm256_extractf128_ps(t8, 0x1)));
+        }
+        else
+        {
+            // alignment >=5, but not a multiple of 4
+            _mm_storeu_ps(base + align * offset[0],
+                          _mm_sub_ps(_mm_loadu_ps(base + align * offset[0]), _mm256_castps256_ps128(t5)));
+            _mm_storeu_ps(base + align * offset[1],
+                          _mm_sub_ps(_mm_loadu_ps(base + align * offset[1]), _mm256_castps256_ps128(t6)));
+            _mm_storeu_ps(base + align * offset[2],
+                          _mm_sub_ps(_mm_loadu_ps(base + align * offset[2]), _mm256_castps256_ps128(t7)));
+            _mm_storeu_ps(base + align * offset[3],
+                          _mm_sub_ps(_mm_loadu_ps(base + align * offset[3]), _mm256_castps256_ps128(t8)));
+            _mm_storeu_ps(
+                    base + align * offset[4],
+                    _mm_sub_ps(_mm_loadu_ps(base + align * offset[4]), _mm256_extractf128_ps(t5, 0x1)));
+            _mm_storeu_ps(
+                    base + align * offset[5],
+                    _mm_sub_ps(_mm_loadu_ps(base + align * offset[5]), _mm256_extractf128_ps(t6, 0x1)));
+            _mm_storeu_ps(
+                    base + align * offset[6],
+                    _mm_sub_ps(_mm_loadu_ps(base + align * offset[6]), _mm256_extractf128_ps(t7, 0x1)));
+            _mm_storeu_ps(
+                    base + align * offset[7],
+                    _mm_sub_ps(_mm_loadu_ps(base + align * offset[7]), _mm256_extractf128_ps(t8, 0x1)));
+        }
+    }
+}
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdFloat  scalar,
+                                                        SimdFloat* triplets0,
+                                                        SimdFloat* triplets1,
+                                                        SimdFloat* triplets2)
+{
+    __m256 t0 = _mm256_permute2f128_ps(scalar.simdInternal_, scalar.simdInternal_, 0x21);
+    __m256 t1 = _mm256_permute_ps(scalar.simdInternal_, _MM_SHUFFLE(1, 0, 0, 0));
+    __m256 t2 = _mm256_permute_ps(t0, _MM_SHUFFLE(2, 2, 1, 1));
+    __m256 t3 = _mm256_permute_ps(scalar.simdInternal_, _MM_SHUFFLE(3, 3, 3, 2));
+    triplets0->simdInternal_ = _mm256_blend_ps(t1, t2, 0xF0);
+    triplets1->simdInternal_ = _mm256_blend_ps(t3, t1, 0xF0);
+    triplets2->simdInternal_ = _mm256_blend_ps(t2, t3, 0xF0);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const float* base,
+                                                             SimdFInt32   simdoffset,
+                                                             SimdFloat*   v0,
+                                                             SimdFloat*   v1,
+                                                             SimdFloat*   v2,
+                                                             SimdFloat*   v3)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset[GMX_SIMD_FLOAT_WIDTH];
+    _mm256_store_si256(reinterpret_cast<__m256i*>(offset), simdoffset.simdInternal_);
+    gatherLoadTranspose<align>(base, offset, v0, v1, v2, v3);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const float* base, SimdFInt32 simdoffset, SimdFloat* v0, SimdFloat* v1)
+{
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset[GMX_SIMD_FLOAT_WIDTH];
+    _mm256_store_si256(reinterpret_cast<__m256i*>(offset), simdoffset.simdInternal_);
+    gatherLoadTranspose<align>(base, offset, v0, v1);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const float* base, SimdFInt32 simdoffset, SimdFloat* v0, SimdFloat* v1)
+{
+    __m128 t1, t2, t3, t4, t5, t6, t7, t8;
+    __m256 tA, tB, tC, tD;
+
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t offset[GMX_SIMD_FLOAT_WIDTH];
+    _mm256_store_si256(reinterpret_cast<__m256i*>(offset), simdoffset.simdInternal_);
+
+    t1 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[0]));
+    t2 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[1]));
+    t3 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[2]));
+    t4 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[3]));
+    t5 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[4]));
+    t6 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[5]));
+    t7 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[6]));
+    t8 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base + align * offset[7]));
+
+    tA = _mm256_insertf128_ps(_mm256_castps128_ps256(t1), t5, 0x1);
+    tB = _mm256_insertf128_ps(_mm256_castps128_ps256(t2), t6, 0x1);
+    tC = _mm256_insertf128_ps(_mm256_castps128_ps256(t3), t7, 0x1);
+    tD = _mm256_insertf128_ps(_mm256_castps128_ps256(t4), t8, 0x1);
+
+    tA                = _mm256_unpacklo_ps(tA, tC);
+    tB                = _mm256_unpacklo_ps(tB, tD);
+    v0->simdInternal_ = _mm256_unpacklo_ps(tA, tB);
+    v1->simdInternal_ = _mm256_unpackhi_ps(tA, tB);
+}
+
+static inline float gmx_simdcall reduceIncr4ReturnSum(float* m, SimdFloat v0, SimdFloat v1, SimdFloat v2, SimdFloat v3)
+{
+    __m128 t0, t2;
+
+    assert(std::size_t(m) % 16 == 0);
+
+    v0.simdInternal_ = _mm256_hadd_ps(v0.simdInternal_, v1.simdInternal_);
+    v2.simdInternal_ = _mm256_hadd_ps(v2.simdInternal_, v3.simdInternal_);
+    v0.simdInternal_ = _mm256_hadd_ps(v0.simdInternal_, v2.simdInternal_);
+    t0               = _mm_add_ps(_mm256_castps256_ps128(v0.simdInternal_), _mm256_extractf128_ps(v0.simdInternal_, 0x1));
+
+    t2 = _mm_add_ps(t0, _mm_load_ps(m));
+    _mm_store_ps(m, t2);
+
+    t0 = _mm_add_ps(t0, _mm_permute_ps(t0, _MM_SHUFFLE(1, 0, 3, 2)));
+    t0 = _mm_add_ss(t0, _mm_permute_ps(t0, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&t0);
+}
+
+
+/*************************************
+ * Half-simd-width utility functions *
+ *************************************/
+static inline SimdFloat gmx_simdcall loadDualHsimd(const float* m0, const float* m1)
+{
+    assert(std::size_t(m0) % 16 == 0);
+    assert(std::size_t(m1) % 16 == 0);
+
+    return { _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_load_ps(m0)), _mm_load_ps(m1), 0x1) };
+}
+
+static inline SimdFloat gmx_simdcall loadDuplicateHsimd(const float* m)
+{
+    assert(std::size_t(m) % 16 == 0);
+
+    return { _mm256_broadcast_ps(reinterpret_cast<const __m128*>(m)) };
+}
+
+static inline SimdFloat gmx_simdcall loadU1DualHsimd(const float* m)
+{
+    __m128 t0, t1;
+    t0 = _mm_broadcast_ss(m);
+    t1 = _mm_broadcast_ss(m + 1);
+    return { _mm256_insertf128_ps(_mm256_castps128_ps256(t0), t1, 0x1) };
+}
+
+
+static inline void gmx_simdcall storeDualHsimd(float* m0, float* m1, SimdFloat a)
+{
+    assert(std::size_t(m0) % 16 == 0);
+    assert(std::size_t(m1) % 16 == 0);
+    _mm_store_ps(m0, _mm256_castps256_ps128(a.simdInternal_));
+    _mm_store_ps(m1, _mm256_extractf128_ps(a.simdInternal_, 0x1));
+}
+
+static inline void gmx_simdcall incrDualHsimd(float* m0, float* m1, SimdFloat a)
+{
+    assert(std::size_t(m0) % 16 == 0);
+    assert(std::size_t(m1) % 16 == 0);
+    _mm_store_ps(m0, _mm_add_ps(_mm256_castps256_ps128(a.simdInternal_), _mm_load_ps(m0)));
+    _mm_store_ps(m1, _mm_add_ps(_mm256_extractf128_ps(a.simdInternal_, 0x1), _mm_load_ps(m1)));
+}
+
+static inline void gmx_simdcall decr3Hsimd(float* m, SimdFloat a0, SimdFloat a1, SimdFloat a2)
+{
+    assert(std::size_t(m) % 16 == 0);
+    decrHsimd(m, a0);
+    decrHsimd(m + GMX_SIMD_FLOAT_WIDTH / 2, a1);
+    decrHsimd(m + GMX_SIMD_FLOAT_WIDTH, a2);
+}
+
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTransposeHsimd(const float*       base0,
+                                                         const float*       base1,
+                                                         const std::int32_t offset[],
+                                                         SimdFloat*         v0,
+                                                         SimdFloat*         v1)
+{
+    __m128 t0, t1, t2, t3, t4, t5, t6, t7;
+    __m256 tA, tB, tC, tD;
+
+    assert(std::size_t(offset) % 16 == 0);
+    assert(std::size_t(base0) % 8 == 0);
+    assert(std::size_t(base1) % 8 == 0);
+    assert(align % 2 == 0);
+
+    t0 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base0 + align * offset[0]));
+    t1 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base0 + align * offset[1]));
+    t2 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base0 + align * offset[2]));
+    t3 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base0 + align * offset[3]));
+    t4 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base1 + align * offset[0]));
+    t5 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base1 + align * offset[1]));
+    t6 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base1 + align * offset[2]));
+    t7 = _mm_loadl_pi(_mm_setzero_ps(), reinterpret_cast<const __m64*>(base1 + align * offset[3]));
+
+    tA = _mm256_insertf128_ps(_mm256_castps128_ps256(t0), t4, 0x1);
+    tB = _mm256_insertf128_ps(_mm256_castps128_ps256(t1), t5, 0x1);
+    tC = _mm256_insertf128_ps(_mm256_castps128_ps256(t2), t6, 0x1);
+    tD = _mm256_insertf128_ps(_mm256_castps128_ps256(t3), t7, 0x1);
+
+    tA                = _mm256_unpacklo_ps(tA, tC);
+    tB                = _mm256_unpacklo_ps(tB, tD);
+    v0->simdInternal_ = _mm256_unpacklo_ps(tA, tB);
+    v1->simdInternal_ = _mm256_unpackhi_ps(tA, tB);
+}
+
+
+static inline float gmx_simdcall reduceIncr4ReturnSumHsimd(float* m, SimdFloat v0, SimdFloat v1)
+{
+    __m128 t0, t1;
+
+    v0.simdInternal_ = _mm256_hadd_ps(v0.simdInternal_, v1.simdInternal_);
+    t0               = _mm256_extractf128_ps(v0.simdInternal_, 0x1);
+    t0               = _mm_hadd_ps(_mm256_castps256_ps128(v0.simdInternal_), t0);
+    t0               = _mm_permute_ps(t0, _MM_SHUFFLE(3, 1, 2, 0));
+
+    assert(std::size_t(m) % 16 == 0);
+
+    t1 = _mm_add_ps(t0, _mm_load_ps(m));
+    _mm_store_ps(m, t1);
+
+    t0 = _mm_add_ps(t0, _mm_permute_ps(t0, _MM_SHUFFLE(1, 0, 3, 2)));
+    t0 = _mm_add_ss(t0, _mm_permute_ps(t0, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&t0);
+}
+
+static inline SimdFloat gmx_simdcall loadU4NOffset(const float* m, int offset)
+{
+    return { _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_loadu_ps(m)), _mm_loadu_ps(m + offset), 0x1) };
+}
+
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_256_UTIL_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512.h b/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512.h
new file mode 100644 (file)
index 0000000..c88d816
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_H
+#define GMX_SIMD_IMPL_X86_AVX_512_H
+
+#include "impl_x86_avx_512_definitions.h"
+#include "impl_x86_avx_512_general.h"
+#include "impl_x86_avx_512_simd4_double.h"
+#include "impl_x86_avx_512_simd4_float.h"
+#include "impl_x86_avx_512_simd_double.h"
+#include "impl_x86_avx_512_simd_float.h"
+#include "impl_x86_avx_512_util_double.h"
+#include "impl_x86_avx_512_util_float.h"
+
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_definitions.h b/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_definitions.h
new file mode 100644 (file)
index 0000000..75e4a97
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_DEFINITIONS_H
+#define GMX_SIMD_IMPL_X86_AVX_512_DEFINITIONS_H
+
+// A couple of implementation notes:
+//
+// - We avoid using the reduce calls provided by the intel compiler, since they
+//   are not instructions but software routines, and not implemented on gcc.
+// - gcc-4.9 through 5.2 do not provide int2mask and mask2int. However, since both
+//   compilers are fine when we just use hexadecimal literals, we use our
+//   own routines avx512Mask2Int() & avx512Int2Mask() that simply return
+//   the argument. If some compiler needs a different solution in the future,
+//   just write some conditional code for these routines.
+// - gcc does not provide _mm512_trunc_ps(), and all alternative ops where we
+//   can specify a rounding mode also appear to be buggy. We conditionally work
+//   around this by using conversions to/from integer instead.
+
+#define GMX_SIMD 1
+#define GMX_SIMD_HAVE_FLOAT 1
+#define GMX_SIMD_HAVE_DOUBLE 1
+#define GMX_SIMD_HAVE_LOADU 1
+#define GMX_SIMD_HAVE_STOREU 1
+#define GMX_SIMD_HAVE_LOGICAL 1
+#define GMX_SIMD_HAVE_FMA 1
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 0
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 1
+// Technically it is straightforward to emulate extract on AVX-512 through
+// memory operations, but when applied to 16 elements as part of a table lookup
+// it will be faster to just store the entire vector once, so we avoid setting it.
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 0
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 1
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 1
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 1
+#define GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT 1
+#define GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE 1
+
+#define GMX_SIMD4_HAVE_FLOAT 1
+#define GMX_SIMD4_HAVE_DOUBLE 1
+
+// Implementation details
+#define GMX_SIMD_FLOAT_WIDTH 16
+#define GMX_SIMD_DOUBLE_WIDTH 8
+#define GMX_SIMD_FINT32_WIDTH 16
+#define GMX_SIMD_DINT32_WIDTH 8
+#define GMX_SIMD4_WIDTH 4
+#define GMX_SIMD_ALIGNMENT 64 // Bytes (16*single or 8*double)
+#define GMX_SIMD_RSQRT_BITS 14
+#define GMX_SIMD_RCP_BITS 14
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_DEFINITIONS_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_general.h b/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_general.h
new file mode 100644 (file)
index 0000000..f1a0e8e
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_GENERAL_H
+#define GMX_SIMD_IMPL_X86_AVX_512_GENERAL_H
+
+#include <immintrin.h>
+
+namespace gmx
+{
+
+static inline void simdPrefetch(const void* m)
+{
+    _mm_prefetch(reinterpret_cast<const char*>(m), _MM_HINT_T0);
+}
+
+/*! \brief Return integer from AVX-512 mask
+ *
+ *  \param m  Mask suitable for use with AVX-512 instructions
+ *
+ *  \return Short integer representation of mask
+ */
+static inline short avx512Mask2Int(__mmask16 m)
+{
+    return static_cast<short>(m);
+}
+
+/*! \brief Return AVX-512 mask from integer
+ *
+ *  \param m  Short integer
+ *
+ *  \return Mask suitable for use with AVX-512 instructions.
+ */
+static inline __mmask16 avx512Int2Mask(short i)
+{
+    return static_cast<__mmask16>(i);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_GENERAL_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd4_double.h b/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd4_double.h
new file mode 100644 (file)
index 0000000..165717e
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_SIMD4_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX_512_SIMD4_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+
+#include <immintrin.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_x86_avx_512_general.h"
+
+namespace gmx
+{
+
+class Simd4Double
+{
+public:
+    Simd4Double() {}
+
+    Simd4Double(double d) : simdInternal_(_mm256_set1_pd(d)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4Double(__m256d simd) : simdInternal_(simd) {}
+
+    __m256d simdInternal_;
+};
+
+class Simd4DBool
+{
+public:
+    Simd4DBool() {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4DBool(__mmask8 simd) : simdInternal_(simd) {}
+
+    __mmask8 simdInternal_;
+};
+
+static inline Simd4Double gmx_simdcall load4(const double* m)
+{
+    assert(size_t(m) % 32 == 0);
+    return { _mm256_load_pd(m) };
+}
+
+static inline void gmx_simdcall store4(double* m, Simd4Double a)
+{
+    assert(size_t(m) % 32 == 0);
+    _mm256_store_pd(m, a.simdInternal_);
+}
+
+static inline Simd4Double gmx_simdcall load4U(const double* m)
+{
+    return { _mm256_loadu_pd(m) };
+}
+
+static inline void gmx_simdcall store4U(double* m, Simd4Double a)
+{
+    _mm256_storeu_pd(m, a.simdInternal_);
+}
+
+static inline Simd4Double gmx_simdcall simd4SetZeroD()
+{
+    return { _mm256_setzero_pd() };
+}
+
+static inline Simd4Double gmx_simdcall operator&(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_and_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall andNot(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_andnot_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator|(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_or_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator^(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_xor_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator+(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_add_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator-(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_sub_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall operator-(Simd4Double x)
+{
+    return { _mm256_xor_pd(x.simdInternal_, _mm256_set1_pd(GMX_DOUBLE_NEGZERO)) };
+}
+
+static inline Simd4Double gmx_simdcall operator*(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_mul_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_fmadd_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_fmsub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fnma(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_fnmadd_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall fnms(Simd4Double a, Simd4Double b, Simd4Double c)
+{
+    return { _mm256_fnmsub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+// Override for AVX-512-KNL
+#if GMX_SIMD_X86_AVX_512
+static inline Simd4Double gmx_simdcall rsqrt(Simd4Double x)
+{
+    return { _mm512_castpd512_pd256(_mm512_rsqrt14_pd(_mm512_castpd256_pd512(x.simdInternal_))) };
+}
+#endif
+
+static inline Simd4Double gmx_simdcall abs(Simd4Double x)
+{
+    return { _mm256_andnot_pd(_mm256_set1_pd(GMX_DOUBLE_NEGZERO), x.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall max(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_max_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall min(Simd4Double a, Simd4Double b)
+{
+    return { _mm256_min_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Double gmx_simdcall round(Simd4Double x)
+{
+    return { _mm256_round_pd(x.simdInternal_, _MM_FROUND_NINT) };
+}
+
+static inline Simd4Double gmx_simdcall trunc(Simd4Double x)
+{
+    return { _mm256_round_pd(x.simdInternal_, _MM_FROUND_TRUNC) };
+}
+
+static inline double gmx_simdcall dotProduct(Simd4Double a, Simd4Double b)
+{
+    __m128d tmp1, tmp2;
+    a.simdInternal_ = _mm256_mul_pd(a.simdInternal_, b.simdInternal_);
+    tmp1            = _mm256_castpd256_pd128(a.simdInternal_);
+    tmp2            = _mm256_extractf128_pd(a.simdInternal_, 0x1);
+
+    tmp1 = _mm_add_pd(tmp1, _mm_permute_pd(tmp1, _MM_SHUFFLE2(0, 1)));
+    tmp1 = _mm_add_pd(tmp1, tmp2);
+    return *reinterpret_cast<double*>(&tmp1);
+}
+
+static inline void gmx_simdcall transpose(Simd4Double* v0, Simd4Double* v1, Simd4Double* v2, Simd4Double* v3)
+{
+    __m256d t1, t2, t3, t4;
+    t1                = _mm256_unpacklo_pd(v0->simdInternal_, v1->simdInternal_);
+    t2                = _mm256_unpackhi_pd(v0->simdInternal_, v1->simdInternal_);
+    t3                = _mm256_unpacklo_pd(v2->simdInternal_, v3->simdInternal_);
+    t4                = _mm256_unpackhi_pd(v2->simdInternal_, v3->simdInternal_);
+    v0->simdInternal_ = _mm256_permute2f128_pd(t1, t3, 0x20);
+    v1->simdInternal_ = _mm256_permute2f128_pd(t2, t4, 0x20);
+    v2->simdInternal_ = _mm256_permute2f128_pd(t1, t3, 0x31);
+    v3->simdInternal_ = _mm256_permute2f128_pd(t2, t4, 0x31);
+}
+
+static inline Simd4DBool gmx_simdcall operator==(Simd4Double a, Simd4Double b)
+{
+    return { _mm512_mask_cmp_pd_mask(avx512Int2Mask(0xF),
+                                     _mm512_castpd256_pd512(a.simdInternal_),
+                                     _mm512_castpd256_pd512(b.simdInternal_),
+                                     _CMP_EQ_OQ) };
+}
+
+static inline Simd4DBool gmx_simdcall operator!=(Simd4Double a, Simd4Double b)
+{
+    return { _mm512_mask_cmp_pd_mask(avx512Int2Mask(0xF),
+                                     _mm512_castpd256_pd512(a.simdInternal_),
+                                     _mm512_castpd256_pd512(b.simdInternal_),
+                                     _CMP_NEQ_OQ) };
+}
+
+static inline Simd4DBool gmx_simdcall operator<(Simd4Double a, Simd4Double b)
+{
+    return { _mm512_mask_cmp_pd_mask(avx512Int2Mask(0xF),
+                                     _mm512_castpd256_pd512(a.simdInternal_),
+                                     _mm512_castpd256_pd512(b.simdInternal_),
+                                     _CMP_LT_OQ) };
+}
+
+static inline Simd4DBool gmx_simdcall operator<=(Simd4Double a, Simd4Double b)
+{
+    return { _mm512_mask_cmp_pd_mask(avx512Int2Mask(0xF),
+                                     _mm512_castpd256_pd512(a.simdInternal_),
+                                     _mm512_castpd256_pd512(b.simdInternal_),
+                                     _CMP_LE_OQ) };
+}
+
+static inline Simd4DBool gmx_simdcall operator&&(Simd4DBool a, Simd4DBool b)
+{
+    return { static_cast<__mmask8>(_mm512_kand(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline Simd4DBool gmx_simdcall operator||(Simd4DBool a, Simd4DBool b)
+{
+    return { static_cast<__mmask8>(_mm512_kor(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline bool gmx_simdcall anyTrue(Simd4DBool x)
+{
+    return (avx512Mask2Int(x.simdInternal_) & 0xF) != 0;
+}
+
+static inline Simd4Double gmx_simdcall selectByMask(Simd4Double a, Simd4DBool m)
+{
+    return { _mm512_castpd512_pd256(_mm512_mask_mov_pd(
+            _mm512_setzero_pd(), m.simdInternal_, _mm512_castpd256_pd512(a.simdInternal_))) };
+}
+
+static inline Simd4Double gmx_simdcall selectByNotMask(Simd4Double a, Simd4DBool m)
+{
+    return { _mm512_castpd512_pd256(_mm512_mask_mov_pd(
+            _mm512_castpd256_pd512(a.simdInternal_), m.simdInternal_, _mm512_setzero_pd())) };
+}
+
+static inline Simd4Double gmx_simdcall blend(Simd4Double a, Simd4Double b, Simd4DBool sel)
+{
+    return { _mm512_castpd512_pd256(_mm512_mask_blend_pd(sel.simdInternal_,
+                                                         _mm512_castpd256_pd512(a.simdInternal_),
+                                                         _mm512_castpd256_pd512(b.simdInternal_))) };
+}
+
+static inline double gmx_simdcall reduce(Simd4Double a)
+{
+    __m128d a0, a1;
+
+    a.simdInternal_ = _mm256_hadd_pd(a.simdInternal_, a.simdInternal_);
+    a0              = _mm256_castpd256_pd128(a.simdInternal_);
+    a1              = _mm256_extractf128_pd(a.simdInternal_, 0x1);
+    a0              = _mm_add_sd(a0, a1);
+    return *reinterpret_cast<double*>(&a0);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_SIMD4_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd4_float.h b/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd4_float.h
new file mode 100644 (file)
index 0000000..4692885
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX_512_SIMD4_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+
+#include <immintrin.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_x86_avx_512_general.h"
+
+namespace gmx
+{
+
+class Simd4Float
+{
+public:
+    Simd4Float() {}
+
+    Simd4Float(float f) : simdInternal_(_mm_set1_ps(f)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4Float(__m128 simd) : simdInternal_(simd) {}
+
+    __m128 simdInternal_;
+};
+
+class Simd4FBool
+{
+public:
+    Simd4FBool() {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4FBool(__mmask16 simd) : simdInternal_(simd) {}
+
+    __mmask16 simdInternal_;
+};
+
+static inline Simd4Float gmx_simdcall load4(const float* m)
+{
+    assert(size_t(m) % 16 == 0);
+    return { _mm_load_ps(m) };
+}
+
+static inline void gmx_simdcall store4(float* m, Simd4Float a)
+{
+    assert(size_t(m) % 16 == 0);
+    _mm_store_ps(m, a.simdInternal_);
+}
+
+static inline Simd4Float gmx_simdcall load4U(const float* m)
+{
+    return { _mm_loadu_ps(m) };
+}
+
+static inline void gmx_simdcall store4U(float* m, Simd4Float a)
+{
+    _mm_storeu_ps(m, a.simdInternal_);
+}
+
+static inline Simd4Float gmx_simdcall simd4SetZeroF()
+{
+    return { _mm_setzero_ps() };
+}
+
+static inline Simd4Float gmx_simdcall operator&(Simd4Float a, Simd4Float b)
+{
+    return { _mm_and_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall andNot(Simd4Float a, Simd4Float b)
+{
+    return { _mm_andnot_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator|(Simd4Float a, Simd4Float b)
+{
+    return { _mm_or_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator^(Simd4Float a, Simd4Float b)
+{
+    return { _mm_xor_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator+(Simd4Float a, Simd4Float b)
+{
+    return { _mm_add_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float a, Simd4Float b)
+{
+    return { _mm_sub_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float x)
+{
+    return { _mm_xor_ps(x.simdInternal_, _mm_set1_ps(GMX_FLOAT_NEGZERO)) };
+}
+
+static inline Simd4Float gmx_simdcall operator*(Simd4Float a, Simd4Float b)
+{
+    return { _mm_mul_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_fmadd_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_fmsub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_fnmadd_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_fnmsub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+// Override for AVX-512-KNL
+#if GMX_SIMD_X86_AVX_512
+static inline Simd4Float gmx_simdcall rsqrt(Simd4Float x)
+{
+    return { _mm512_castps512_ps128(_mm512_rsqrt14_ps(_mm512_castps128_ps512(x.simdInternal_))) };
+}
+#endif
+
+static inline Simd4Float gmx_simdcall abs(Simd4Float x)
+{
+    return { _mm_andnot_ps(_mm_set1_ps(GMX_FLOAT_NEGZERO), x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall max(Simd4Float a, Simd4Float b)
+{
+    return { _mm_max_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall min(Simd4Float a, Simd4Float b)
+{
+    return { _mm_min_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall round(Simd4Float x)
+{
+    return { _mm_round_ps(x.simdInternal_, _MM_FROUND_NINT) };
+}
+
+static inline Simd4Float gmx_simdcall trunc(Simd4Float x)
+{
+    return { _mm_round_ps(x.simdInternal_, _MM_FROUND_TRUNC) };
+}
+
+static inline float gmx_simdcall dotProduct(Simd4Float a, Simd4Float b)
+{
+    __m128 c, d;
+    c = _mm_mul_ps(a.simdInternal_, b.simdInternal_);
+    d = _mm_add_ps(c, _mm_permute_ps(c, _MM_SHUFFLE(0, 3, 2, 1)));
+    d = _mm_add_ps(d, _mm_permute_ps(c, _MM_SHUFFLE(1, 0, 3, 2)));
+    return *reinterpret_cast<float*>(&d);
+}
+
+static inline void gmx_simdcall transpose(Simd4Float* v0, Simd4Float* v1, Simd4Float* v2, Simd4Float* v3)
+{
+    __m128 t0, t1, t2, t3;
+
+    t0                = _mm_unpacklo_ps(v0->simdInternal_, v2->simdInternal_);
+    t1                = _mm_unpackhi_ps(v0->simdInternal_, v2->simdInternal_);
+    t2                = _mm_unpacklo_ps(v1->simdInternal_, v3->simdInternal_);
+    t3                = _mm_unpackhi_ps(v1->simdInternal_, v3->simdInternal_);
+    v0->simdInternal_ = _mm_unpacklo_ps(t0, t2);
+    v1->simdInternal_ = _mm_unpackhi_ps(t0, t2);
+    v2->simdInternal_ = _mm_unpacklo_ps(t1, t3);
+    v3->simdInternal_ = _mm_unpackhi_ps(t1, t3);
+}
+
+static inline Simd4FBool gmx_simdcall operator==(Simd4Float a, Simd4Float b)
+{
+    return { _mm512_mask_cmp_ps_mask(avx512Int2Mask(0xF),
+                                     _mm512_castps128_ps512(a.simdInternal_),
+                                     _mm512_castps128_ps512(b.simdInternal_),
+                                     _CMP_EQ_OQ) };
+}
+
+static inline Simd4FBool gmx_simdcall operator!=(Simd4Float a, Simd4Float b)
+{
+    return { _mm512_mask_cmp_ps_mask(avx512Int2Mask(0xF),
+                                     _mm512_castps128_ps512(a.simdInternal_),
+                                     _mm512_castps128_ps512(b.simdInternal_),
+                                     _CMP_NEQ_OQ) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<(Simd4Float a, Simd4Float b)
+{
+    return { _mm512_mask_cmp_ps_mask(avx512Int2Mask(0xF),
+                                     _mm512_castps128_ps512(a.simdInternal_),
+                                     _mm512_castps128_ps512(b.simdInternal_),
+                                     _CMP_LT_OQ) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<=(Simd4Float a, Simd4Float b)
+{
+    return { _mm512_mask_cmp_ps_mask(avx512Int2Mask(0xF),
+                                     _mm512_castps128_ps512(a.simdInternal_),
+                                     _mm512_castps128_ps512(b.simdInternal_),
+                                     _CMP_LE_OQ) };
+}
+
+static inline Simd4FBool gmx_simdcall operator&&(Simd4FBool a, Simd4FBool b)
+{
+    return { _mm512_kand(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator||(Simd4FBool a, Simd4FBool b)
+{
+    return { _mm512_kor(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(Simd4FBool a)
+{
+    return (avx512Mask2Int(a.simdInternal_) & 0xF) != 0;
+}
+
+static inline Simd4Float gmx_simdcall selectByMask(Simd4Float a, Simd4FBool m)
+{
+    return { _mm512_castps512_ps128(_mm512_mask_mov_ps(
+            _mm512_setzero_ps(), m.simdInternal_, _mm512_castps128_ps512(a.simdInternal_))) };
+}
+
+static inline Simd4Float gmx_simdcall selectByNotMask(Simd4Float a, Simd4FBool m)
+{
+    return { _mm512_castps512_ps128(_mm512_mask_mov_ps(
+            _mm512_castps128_ps512(a.simdInternal_), m.simdInternal_, _mm512_setzero_ps())) };
+}
+
+static inline Simd4Float gmx_simdcall blend(Simd4Float a, Simd4Float b, Simd4FBool sel)
+{
+    return { _mm512_castps512_ps128(_mm512_mask_blend_ps(sel.simdInternal_,
+                                                         _mm512_castps128_ps512(a.simdInternal_),
+                                                         _mm512_castps128_ps512(b.simdInternal_))) };
+}
+
+static inline float gmx_simdcall reduce(Simd4Float a)
+{
+    __m128 b;
+    b = _mm_add_ps(a.simdInternal_, _mm_permute_ps(a.simdInternal_, _MM_SHUFFLE(1, 0, 3, 2)));
+    b = _mm_add_ss(b, _mm_permute_ps(b, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&b);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_SIMD4_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd_double.h b/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd_double.h
new file mode 100644 (file)
index 0000000..669c2aa
--- /dev/null
@@ -0,0 +1,563 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX_512_SIMD_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstdint>
+
+#include <immintrin.h>
+
+#include "gromacs/math/utilities.h"
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_x86_avx_512_general.h"
+#include "impl_x86_avx_512_simd_float.h"
+
+namespace gmx
+{
+
+class SimdDouble
+{
+public:
+    SimdDouble() {}
+
+    SimdDouble(double d) : simdInternal_(_mm512_set1_pd(d)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDouble(__m512d simd) : simdInternal_(simd) {}
+
+    __m512d simdInternal_;
+};
+
+class SimdDInt32
+{
+public:
+    SimdDInt32() {}
+
+    SimdDInt32(std::int32_t i) : simdInternal_(_mm256_set1_epi32(i)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDInt32(__m256i simd) : simdInternal_(simd) {}
+
+    __m256i simdInternal_;
+};
+
+class SimdDBool
+{
+public:
+    SimdDBool() {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDBool(__mmask8 simd) : simdInternal_(simd) {}
+
+    __mmask8 simdInternal_;
+};
+
+class SimdDIBool
+{
+public:
+    SimdDIBool() {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDIBool(__mmask16 simd) : simdInternal_(simd) {}
+
+    __mmask16 simdInternal_;
+};
+
+static inline SimdDouble gmx_simdcall simdLoad(const double* m, SimdDoubleTag = {})
+{
+    assert(std::size_t(m) % 64 == 0);
+    return { _mm512_load_pd(m) };
+}
+
+static inline void gmx_simdcall store(double* m, SimdDouble a)
+{
+    assert(std::size_t(m) % 64 == 0);
+    _mm512_store_pd(m, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall simdLoadU(const double* m, SimdDoubleTag = {})
+{
+    return { _mm512_loadu_pd(m) };
+}
+
+static inline void gmx_simdcall storeU(double* m, SimdDouble a)
+{
+    _mm512_storeu_pd(m, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall setZeroD()
+{
+    return { _mm512_setzero_pd() };
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdDInt32Tag)
+{
+    assert(std::size_t(m) % 32 == 0);
+    return { _mm256_load_si256(reinterpret_cast<const __m256i*>(m)) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdDInt32 a)
+{
+    assert(std::size_t(m) % 32 == 0);
+    _mm256_store_si256(reinterpret_cast<__m256i*>(m), a.simdInternal_);
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdDInt32Tag)
+{
+    return { _mm256_loadu_si256(reinterpret_cast<const __m256i*>(m)) };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdDInt32 a)
+{
+    _mm256_storeu_si256(reinterpret_cast<__m256i*>(m), a.simdInternal_);
+}
+
+static inline SimdDInt32 gmx_simdcall setZeroDI()
+{
+    return { _mm256_setzero_si256() };
+}
+
+static inline SimdDouble gmx_simdcall operator&(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_castsi512_pd(_mm512_and_epi32(_mm512_castpd_si512(a.simdInternal_),
+                                                  _mm512_castpd_si512(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall andNot(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_castsi512_pd(_mm512_andnot_epi32(_mm512_castpd_si512(a.simdInternal_),
+                                                     _mm512_castpd_si512(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall operator|(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_castsi512_pd(_mm512_or_epi32(_mm512_castpd_si512(a.simdInternal_),
+                                                 _mm512_castpd_si512(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall operator^(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_castsi512_pd(_mm512_xor_epi32(_mm512_castpd_si512(a.simdInternal_),
+                                                  _mm512_castpd_si512(b.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall operator+(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_add_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_sub_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble x)
+{
+    return { _mm512_castsi512_pd(_mm512_xor_epi32(_mm512_castpd_si512(x.simdInternal_),
+                                                  _mm512_castpd_si512(_mm512_set1_pd(GMX_DOUBLE_NEGZERO)))) };
+}
+
+static inline SimdDouble gmx_simdcall operator*(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_mul_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm512_fmadd_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm512_fmsub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm512_fnmadd_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm512_fnmsub_pd(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+// Override for AVX-512-KNL
+#if GMX_SIMD_X86_AVX_512
+static inline SimdDouble gmx_simdcall rsqrt(SimdDouble x)
+{
+    return { _mm512_rsqrt14_pd(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall rcp(SimdDouble x)
+{
+    return { _mm512_rcp14_pd(x.simdInternal_) };
+}
+#endif
+
+static inline SimdDouble gmx_simdcall maskAdd(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    return { _mm512_mask_add_pd(a.simdInternal_, m.simdInternal_, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzMul(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    return { _mm512_maskz_mul_pd(m.simdInternal_, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzFma(SimdDouble a, SimdDouble b, SimdDouble c, SimdDBool m)
+{
+    return { _mm512_maskz_fmadd_pd(m.simdInternal_, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+// Override for AVX-512-KNL
+#if GMX_SIMD_X86_AVX_512
+static inline SimdDouble gmx_simdcall maskzRsqrt(SimdDouble x, SimdDBool m)
+{
+    return { _mm512_maskz_rsqrt14_pd(m.simdInternal_, x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzRcp(SimdDouble x, SimdDBool m)
+{
+    return { _mm512_maskz_rcp14_pd(m.simdInternal_, x.simdInternal_) };
+}
+#endif
+
+static inline SimdDouble gmx_simdcall abs(SimdDouble x)
+{
+    return { _mm512_castsi512_pd(_mm512_andnot_epi32(_mm512_castpd_si512(_mm512_set1_pd(GMX_DOUBLE_NEGZERO)),
+                                                     _mm512_castpd_si512(x.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall max(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_max_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall min(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_min_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall round(SimdDouble x)
+{
+    return { _mm512_roundscale_pd(x.simdInternal_, 0) };
+}
+
+static inline SimdDouble gmx_simdcall trunc(SimdDouble x)
+{
+#if defined(__INTEL_COMPILER) || defined(__ECC)
+    return { _mm512_trunc_pd(x.simdInternal_) };
+#else
+    return { _mm512_cvtepi32_pd(_mm512_cvttpd_epi32(x.simdInternal_)) };
+#endif
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble frexp(SimdDouble value, SimdDInt32* exponent)
+{
+    __m512d rExponent;
+    __m256i iExponent;
+    __m512d result;
+
+    if (opt == MathOptimization::Safe)
+    {
+        // For the safe branch, we use the masked operations to only assign results if the
+        // input value was nonzero, and otherwise set exponent to 0, and the fraction to the input (+-0).
+        __mmask8 valueIsNonZero =
+                _mm512_cmp_pd_mask(_mm512_setzero_pd(), value.simdInternal_, _CMP_NEQ_OQ);
+        rExponent = _mm512_maskz_getexp_pd(valueIsNonZero, value.simdInternal_);
+
+        // AVX512F does not contain any function to use masking when adding 1 to a 256-bit register
+        // (that comes with AVX512VL), so to work around this we create an integer -1 value, and
+        // use masking in the _conversion_ instruction where it is supported. When we later add
+        // 1 to all fields, the files that were formerly -1 (corresponding to zero exponent)
+        // will be assigned -1 + 1 = 0.
+        iExponent = _mm512_mask_cvtpd_epi32(_mm256_set1_epi32(-1), valueIsNonZero, rExponent);
+        iExponent = _mm256_add_epi32(iExponent, _mm256_set1_epi32(1));
+
+        // Set result to value (+-0) when it is zero.
+        result = _mm512_mask_getmant_pd(
+                value.simdInternal_, valueIsNonZero, value.simdInternal_, _MM_MANT_NORM_p5_1, _MM_MANT_SIGN_src);
+    }
+    else
+    {
+        // For the fast branch, it's the user's responsibility to make sure never to call the
+        // function with input values of +-0.0
+        rExponent = _mm512_getexp_pd(value.simdInternal_);
+        iExponent = _mm512_cvtpd_epi32(rExponent);
+        iExponent = _mm256_add_epi32(iExponent, _mm256_set1_epi32(1));
+
+        result = _mm512_getmant_pd(value.simdInternal_, _MM_MANT_NORM_p5_1, _MM_MANT_SIGN_src);
+    }
+
+    exponent->simdInternal_ = iExponent;
+    return { result };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble ldexp(SimdDouble value, SimdDInt32 exponent)
+{
+    const __m256i exponentBias = _mm256_set1_epi32(1023);
+    __m256i       iExponent    = _mm256_add_epi32(exponent.simdInternal_, exponentBias);
+    __m512i       iExponent512;
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm256_max_epi32(iExponent, _mm256_setzero_si256());
+    }
+
+    iExponent512 =
+            _mm512_permutexvar_epi32(_mm512_set_epi32(7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0),
+                                     _mm512_castsi256_si512(iExponent));
+    iExponent512 =
+            _mm512_mask_slli_epi32(_mm512_setzero_epi32(), avx512Int2Mask(0xAAAA), iExponent512, 20);
+    return _mm512_mul_pd(_mm512_castsi512_pd(iExponent512), value.simdInternal_);
+}
+
+static inline double gmx_simdcall reduce(SimdDouble a)
+{
+    __m512d x = a.simdInternal_;
+    x         = _mm512_add_pd(x, _mm512_shuffle_f64x2(x, x, 0xEE));
+    x         = _mm512_add_pd(x, _mm512_shuffle_f64x2(x, x, 0x11));
+    x         = _mm512_add_pd(x, _mm512_permute_pd(x, 0x01));
+    return *reinterpret_cast<double*>(&x);
+}
+
+static inline SimdDBool gmx_simdcall operator==(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_cmp_pd_mask(a.simdInternal_, b.simdInternal_, _CMP_EQ_OQ) };
+}
+
+static inline SimdDBool gmx_simdcall operator!=(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_cmp_pd_mask(a.simdInternal_, b.simdInternal_, _CMP_NEQ_OQ) };
+}
+
+static inline SimdDBool gmx_simdcall operator<(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_cmp_pd_mask(a.simdInternal_, b.simdInternal_, _CMP_LT_OQ) };
+}
+
+static inline SimdDBool gmx_simdcall operator<=(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_cmp_pd_mask(a.simdInternal_, b.simdInternal_, _CMP_LE_OQ) };
+}
+
+static inline SimdDBool gmx_simdcall testBits(SimdDouble a)
+{
+    return { _mm512_test_epi64_mask(_mm512_castpd_si512(a.simdInternal_),
+                                    _mm512_castpd_si512(a.simdInternal_)) };
+}
+
+static inline SimdDBool gmx_simdcall operator&&(SimdDBool a, SimdDBool b)
+{
+    return { static_cast<__mmask8>(_mm512_kand(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline SimdDBool gmx_simdcall operator||(SimdDBool a, SimdDBool b)
+{
+    return { static_cast<__mmask8>(_mm512_kor(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDBool a)
+{
+    return (avx512Mask2Int(a.simdInternal_) != 0);
+}
+
+static inline SimdDouble gmx_simdcall selectByMask(SimdDouble a, SimdDBool m)
+{
+    return { _mm512_mask_mov_pd(_mm512_setzero_pd(), m.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall selectByNotMask(SimdDouble a, SimdDBool m)
+{
+    return { _mm512_mask_mov_pd(a.simdInternal_, m.simdInternal_, _mm512_setzero_pd()) };
+}
+
+static inline SimdDouble gmx_simdcall blend(SimdDouble a, SimdDouble b, SimdDBool sel)
+{
+    return { _mm512_mask_blend_pd(sel.simdInternal_, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall copysign(SimdDouble a, SimdDouble b)
+{
+    return { _mm512_castsi512_pd(_mm512_ternarylogic_epi64(_mm512_castpd_si512(a.simdInternal_),
+                                                           _mm512_castpd_si512(b.simdInternal_),
+                                                           _mm512_set1_epi64(INT64_MIN),
+                                                           0xD8)) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator&(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm256_and_si256(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall andNot(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm256_andnot_si256(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator|(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm256_or_si256(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator^(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm256_xor_si256(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator+(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm256_add_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator-(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm256_sub_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator*(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm256_mullo_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator==(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm512_mask_cmp_epi32_mask(avx512Int2Mask(0xFF),
+                                        _mm512_castsi256_si512(a.simdInternal_),
+                                        _mm512_castsi256_si512(b.simdInternal_),
+                                        _MM_CMPINT_EQ) };
+}
+
+static inline SimdDIBool gmx_simdcall testBits(SimdDInt32 a)
+{
+    return { _mm512_mask_test_epi32_mask(avx512Int2Mask(0xFF),
+                                         _mm512_castsi256_si512(a.simdInternal_),
+                                         _mm512_castsi256_si512(a.simdInternal_)) };
+}
+
+static inline SimdDIBool gmx_simdcall operator<(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm512_mask_cmp_epi32_mask(avx512Int2Mask(0xFF),
+                                        _mm512_castsi256_si512(a.simdInternal_),
+                                        _mm512_castsi256_si512(b.simdInternal_),
+                                        _MM_CMPINT_LT) };
+}
+
+static inline SimdDIBool gmx_simdcall operator&&(SimdDIBool a, SimdDIBool b)
+{
+    return { _mm512_kand(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator||(SimdDIBool a, SimdDIBool b)
+{
+    return { _mm512_kor(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDIBool a)
+{
+    return (avx512Mask2Int(a.simdInternal_) & 0xFF) != 0;
+}
+
+static inline SimdDInt32 gmx_simdcall selectByMask(SimdDInt32 a, SimdDIBool m)
+{
+    return { _mm512_castsi512_si256(_mm512_mask_mov_epi32(
+            _mm512_setzero_si512(), m.simdInternal_, _mm512_castsi256_si512(a.simdInternal_))) };
+}
+
+static inline SimdDInt32 gmx_simdcall selectByNotMask(SimdDInt32 a, SimdDIBool m)
+{
+    return { _mm512_castsi512_si256(_mm512_mask_mov_epi32(
+            _mm512_castsi256_si512(a.simdInternal_), m.simdInternal_, _mm512_setzero_si512())) };
+}
+
+static inline SimdDInt32 gmx_simdcall blend(SimdDInt32 a, SimdDInt32 b, SimdDIBool sel)
+{
+    return { _mm512_castsi512_si256(_mm512_mask_blend_epi32(sel.simdInternal_,
+                                                            _mm512_castsi256_si512(a.simdInternal_),
+                                                            _mm512_castsi256_si512(b.simdInternal_))) };
+}
+
+static inline SimdDInt32 gmx_simdcall cvtR2I(SimdDouble a)
+{
+    return { _mm512_cvtpd_epi32(a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall cvttR2I(SimdDouble a)
+{
+    return { _mm512_cvttpd_epi32(a.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall cvtI2R(SimdDInt32 a)
+{
+    return { _mm512_cvtepi32_pd(a.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall cvtB2IB(SimdDBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdDBool gmx_simdcall cvtIB2B(SimdDIBool a)
+{
+    return { static_cast<__mmask8>(a.simdInternal_) };
+}
+
+static inline void gmx_simdcall cvtF2DD(SimdFloat f, SimdDouble* d0, SimdDouble* d1)
+{
+    d0->simdInternal_ = _mm512_cvtps_pd(_mm512_castps512_ps256(f.simdInternal_));
+    d1->simdInternal_ = _mm512_cvtps_pd(
+            _mm512_castps512_ps256(_mm512_shuffle_f32x4(f.simdInternal_, f.simdInternal_, 0xEE)));
+}
+
+static inline SimdFloat gmx_simdcall cvtDD2F(SimdDouble d0, SimdDouble d1)
+{
+    __m512 f0 = _mm512_castps256_ps512(_mm512_cvtpd_ps(d0.simdInternal_));
+    __m512 f1 = _mm512_castps256_ps512(_mm512_cvtpd_ps(d1.simdInternal_));
+    return { _mm512_shuffle_f32x4(f0, f1, 0x44) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_SIMD_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd_float.h b/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd_float.h
new file mode 100644 (file)
index 0000000..8659448
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_SIMD_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX_512_SIMD_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstdint>
+
+#include <immintrin.h>
+
+#include "gromacs/math/utilities.h"
+#include "gromacs/utility/real.h"
+
+#include "impl_x86_avx_512_general.h"
+
+namespace gmx
+{
+
+class SimdFloat
+{
+public:
+    SimdFloat() {}
+
+    SimdFloat(float f) : simdInternal_(_mm512_set1_ps(f)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFloat(__m512 simd) : simdInternal_(simd) {}
+
+    __m512 simdInternal_;
+};
+
+class SimdFInt32
+{
+public:
+    SimdFInt32() {}
+
+    SimdFInt32(std::int32_t i) : simdInternal_(_mm512_set1_epi32(i)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFInt32(__m512i simd) : simdInternal_(simd) {}
+
+    __m512i simdInternal_;
+};
+
+class SimdFBool
+{
+public:
+    SimdFBool() {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFBool(__mmask16 simd) : simdInternal_(simd) {}
+
+    __mmask16 simdInternal_;
+};
+
+class SimdFIBool
+{
+public:
+    SimdFIBool() {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFIBool(__mmask16 simd) : simdInternal_(simd) {}
+
+    __mmask16 simdInternal_;
+};
+
+static inline SimdFloat gmx_simdcall simdLoad(const float* m, SimdFloatTag = {})
+{
+    assert(std::size_t(m) % 64 == 0);
+    return { _mm512_load_ps(m) };
+}
+
+static inline void gmx_simdcall store(float* m, SimdFloat a)
+{
+    assert(std::size_t(m) % 64 == 0);
+    _mm512_store_ps(m, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall simdLoadU(const float* m, SimdFloatTag = {})
+{
+    return { _mm512_loadu_ps(m) };
+}
+
+static inline void gmx_simdcall storeU(float* m, SimdFloat a)
+{
+    _mm512_storeu_ps(m, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall setZeroF()
+{
+    return { _mm512_setzero_ps() };
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdFInt32Tag)
+{
+    assert(std::size_t(m) % 64 == 0);
+    return { _mm512_load_si512(m) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdFInt32 a)
+{
+    assert(std::size_t(m) % 64 == 0);
+    _mm512_store_si512(m, a.simdInternal_);
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdFInt32Tag)
+{
+    return { _mm512_loadu_si512(m) };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdFInt32 a)
+{
+    _mm512_storeu_si512(m, a.simdInternal_);
+}
+
+static inline SimdFInt32 gmx_simdcall setZeroFI()
+{
+    return { _mm512_setzero_si512() };
+}
+
+
+static inline SimdFloat gmx_simdcall operator&(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_castsi512_ps(_mm512_and_epi32(_mm512_castps_si512(a.simdInternal_),
+                                                  _mm512_castps_si512(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall andNot(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_castsi512_ps(_mm512_andnot_epi32(_mm512_castps_si512(a.simdInternal_),
+                                                     _mm512_castps_si512(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall operator|(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_castsi512_ps(_mm512_or_epi32(_mm512_castps_si512(a.simdInternal_),
+                                                 _mm512_castps_si512(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall operator^(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_castsi512_ps(_mm512_xor_epi32(_mm512_castps_si512(a.simdInternal_),
+                                                  _mm512_castps_si512(b.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall operator+(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_add_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_sub_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat x)
+{
+    return { _mm512_castsi512_ps(_mm512_xor_epi32(_mm512_castps_si512(x.simdInternal_),
+                                                  _mm512_castps_si512(_mm512_set1_ps(GMX_FLOAT_NEGZERO)))) };
+}
+
+static inline SimdFloat gmx_simdcall operator*(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_mul_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm512_fmadd_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm512_fmsub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm512_fnmadd_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm512_fnmsub_ps(a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+// Override for AVX-512-KNL
+#if GMX_SIMD_X86_AVX_512
+static inline SimdFloat gmx_simdcall rsqrt(SimdFloat x)
+{
+    return { _mm512_rsqrt14_ps(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall rcp(SimdFloat x)
+{
+    return { _mm512_rcp14_ps(x.simdInternal_) };
+}
+#endif
+
+static inline SimdFloat gmx_simdcall maskAdd(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    return { _mm512_mask_add_ps(a.simdInternal_, m.simdInternal_, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzMul(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    return { _mm512_maskz_mul_ps(m.simdInternal_, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzFma(SimdFloat a, SimdFloat b, SimdFloat c, SimdFBool m)
+{
+    return { _mm512_maskz_fmadd_ps(m.simdInternal_, a.simdInternal_, b.simdInternal_, c.simdInternal_) };
+}
+
+// Override for AVX-512-KNL
+#if GMX_SIMD_X86_AVX_512
+static inline SimdFloat gmx_simdcall maskzRsqrt(SimdFloat x, SimdFBool m)
+{
+    return { _mm512_maskz_rsqrt14_ps(m.simdInternal_, x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzRcp(SimdFloat x, SimdFBool m)
+{
+    return { _mm512_maskz_rcp14_ps(m.simdInternal_, x.simdInternal_) };
+}
+#endif
+
+static inline SimdFloat gmx_simdcall abs(SimdFloat x)
+{
+    return { _mm512_castsi512_ps(_mm512_andnot_epi32(_mm512_castps_si512(_mm512_set1_ps(GMX_FLOAT_NEGZERO)),
+                                                     _mm512_castps_si512(x.simdInternal_))) };
+}
+
+static inline SimdFloat gmx_simdcall max(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_max_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall min(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_min_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall round(SimdFloat x)
+{
+    return { _mm512_roundscale_ps(x.simdInternal_, 0) };
+}
+
+static inline SimdFloat gmx_simdcall trunc(SimdFloat x)
+{
+#if defined(__INTEL_COMPILER) || defined(__ECC)
+    return { _mm512_trunc_ps(x.simdInternal_) };
+#else
+    return { _mm512_cvtepi32_ps(_mm512_cvttps_epi32(x.simdInternal_)) };
+#endif
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall frexp(SimdFloat value, SimdFInt32* exponent)
+{
+    __m512  rExponent;
+    __m512i iExponent;
+    __m512  result;
+
+    if (opt == MathOptimization::Safe)
+    {
+        // For the safe branch, we use the masked operations to only assign results if the
+        // input value was nonzero, and otherwise set exponent to 0, and the fraction to the input (+-0).
+        __mmask16 valueIsNonZero =
+                _mm512_cmp_ps_mask(_mm512_setzero_ps(), value.simdInternal_, _CMP_NEQ_OQ);
+        rExponent = _mm512_mask_getexp_ps(_mm512_setzero_ps(), valueIsNonZero, value.simdInternal_);
+        iExponent = _mm512_cvtps_epi32(rExponent);
+        iExponent = _mm512_mask_add_epi32(iExponent, valueIsNonZero, iExponent, _mm512_set1_epi32(1));
+
+        // Set result to input value when the latter is +-0
+        result = _mm512_mask_getmant_ps(
+                value.simdInternal_, valueIsNonZero, value.simdInternal_, _MM_MANT_NORM_p5_1, _MM_MANT_SIGN_src);
+    }
+    else
+    {
+        // For the fast branch, it's the user's responsibility to make sure never to call the
+        // function with input values of +-0.0
+        rExponent = _mm512_getexp_ps(value.simdInternal_);
+        iExponent = _mm512_cvtps_epi32(rExponent);
+        iExponent = _mm512_add_epi32(iExponent, _mm512_set1_epi32(1));
+
+        result = _mm512_getmant_ps(value.simdInternal_, _MM_MANT_NORM_p5_1, _MM_MANT_SIGN_src);
+    }
+
+    exponent->simdInternal_ = iExponent;
+
+    return { result };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall ldexp(SimdFloat value, SimdFInt32 exponent)
+{
+    const __m512i exponentBias = _mm512_set1_epi32(127);
+    __m512i       iExponent    = _mm512_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm512_max_epi32(iExponent, _mm512_setzero_epi32());
+    }
+
+    iExponent = _mm512_slli_epi32(iExponent, 23);
+
+    return { _mm512_mul_ps(value.simdInternal_, _mm512_castsi512_ps(iExponent)) };
+}
+
+static inline float gmx_simdcall reduce(SimdFloat a)
+{
+    __m512 x = a.simdInternal_;
+    x        = _mm512_add_ps(x, _mm512_shuffle_f32x4(x, x, 0xEE));
+    x        = _mm512_add_ps(x, _mm512_shuffle_f32x4(x, x, 0x11));
+    x        = _mm512_add_ps(x, _mm512_permute_ps(x, 0xEE));
+    x        = _mm512_add_ps(x, _mm512_permute_ps(x, 0x11));
+    return *reinterpret_cast<float*>(&x);
+}
+
+static inline SimdFBool gmx_simdcall operator==(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_cmp_ps_mask(a.simdInternal_, b.simdInternal_, _CMP_EQ_OQ) };
+}
+
+static inline SimdFBool gmx_simdcall operator!=(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_cmp_ps_mask(a.simdInternal_, b.simdInternal_, _CMP_NEQ_OQ) };
+}
+
+static inline SimdFBool gmx_simdcall operator<(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_cmp_ps_mask(a.simdInternal_, b.simdInternal_, _CMP_LT_OQ) };
+}
+
+static inline SimdFBool gmx_simdcall operator<=(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_cmp_ps_mask(a.simdInternal_, b.simdInternal_, _CMP_LE_OQ) };
+}
+
+static inline SimdFBool gmx_simdcall testBits(SimdFloat a)
+{
+    return { _mm512_test_epi32_mask(_mm512_castps_si512(a.simdInternal_),
+                                    _mm512_castps_si512(a.simdInternal_)) };
+}
+
+static inline SimdFBool gmx_simdcall operator&&(SimdFBool a, SimdFBool b)
+{
+    return { _mm512_kand(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator||(SimdFBool a, SimdFBool b)
+{
+    return { _mm512_kor(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFBool a)
+{
+    return (avx512Mask2Int(a.simdInternal_) != 0);
+}
+
+static inline SimdFloat gmx_simdcall selectByMask(SimdFloat a, SimdFBool m)
+{
+    return { _mm512_mask_mov_ps(_mm512_setzero_ps(), m.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall selectByNotMask(SimdFloat a, SimdFBool m)
+{
+    return { _mm512_mask_mov_ps(a.simdInternal_, m.simdInternal_, _mm512_setzero_ps()) };
+}
+
+static inline SimdFloat gmx_simdcall blend(SimdFloat a, SimdFloat b, SimdFBool sel)
+{
+    return { _mm512_mask_blend_ps(sel.simdInternal_, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall copysign(SimdFloat a, SimdFloat b)
+{
+    return { _mm512_castsi512_ps(_mm512_ternarylogic_epi32(_mm512_castps_si512(a.simdInternal_),
+                                                           _mm512_castps_si512(b.simdInternal_),
+                                                           _mm512_set1_epi32(INT32_MIN),
+                                                           0xD8)) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator&(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm512_and_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall andNot(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm512_andnot_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator|(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm512_or_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator^(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm512_xor_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator+(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm512_add_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator-(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm512_sub_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator*(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm512_mullo_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator==(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm512_cmp_epi32_mask(a.simdInternal_, b.simdInternal_, _MM_CMPINT_EQ) };
+}
+
+static inline SimdFIBool gmx_simdcall testBits(SimdFInt32 a)
+{
+    return { _mm512_test_epi32_mask(a.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator<(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm512_cmp_epi32_mask(a.simdInternal_, b.simdInternal_, _MM_CMPINT_LT) };
+}
+
+static inline SimdFIBool gmx_simdcall operator&&(SimdFIBool a, SimdFIBool b)
+{
+    return { _mm512_kand(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator||(SimdFIBool a, SimdFIBool b)
+{
+    return { _mm512_kor(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFIBool a)
+{
+    return (avx512Mask2Int(a.simdInternal_) != 0);
+}
+
+static inline SimdFInt32 gmx_simdcall selectByMask(SimdFInt32 a, SimdFIBool m)
+{
+    return { _mm512_mask_mov_epi32(_mm512_setzero_epi32(), m.simdInternal_, a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall selectByNotMask(SimdFInt32 a, SimdFIBool m)
+{
+    return { _mm512_mask_mov_epi32(a.simdInternal_, m.simdInternal_, _mm512_setzero_epi32()) };
+}
+
+static inline SimdFInt32 gmx_simdcall blend(SimdFInt32 a, SimdFInt32 b, SimdFIBool sel)
+{
+    return { _mm512_mask_blend_epi32(sel.simdInternal_, a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvtR2I(SimdFloat a)
+{
+    return { _mm512_cvtps_epi32(a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvttR2I(SimdFloat a)
+{
+    return { _mm512_cvttps_epi32(a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall cvtI2R(SimdFInt32 a)
+{
+    return { _mm512_cvtepi32_ps(a.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall cvtB2IB(SimdFBool a)
+{
+    return { a.simdInternal_ };
+}
+
+static inline SimdFBool gmx_simdcall cvtIB2B(SimdFIBool a)
+{
+    return { a.simdInternal_ };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_SIMD_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_util_double.h b/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_util_double.h
new file mode 100644 (file)
index 0000000..9ae6999
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014-2018, The GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX_512_UTIL_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstdint>
+
+#include <immintrin.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_x86_avx_512_general.h"
+#include "impl_x86_avx_512_simd_double.h"
+
+namespace gmx
+{
+
+static const int c_simdBestPairAlignmentDouble = 2;
+
+namespace
+{
+// Multiply function optimized for powers of 2, for which it is done by
+// shifting. Currently up to 8 is accelerated. Could be accelerated for any
+// number with a constexpr log2 function.
+template<int n>
+static inline SimdDInt32 fastMultiply(SimdDInt32 x)
+{
+    if (n == 2)
+    {
+        return _mm256_slli_epi32(x.simdInternal_, 1);
+    }
+    else if (n == 4)
+    {
+        return _mm256_slli_epi32(x.simdInternal_, 2);
+    }
+    else if (n == 8)
+    {
+        return _mm256_slli_epi32(x.simdInternal_, 3);
+    }
+    else
+    {
+        return x * n;
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const double*, SimdDInt32)
+{
+    // Nothing to do. Termination of recursion.
+}
+
+/* This is an internal helper function used by decr3Hsimd(...).
+ */
+inline void gmx_simdcall decrHsimd(double* m, SimdDouble a)
+{
+    __m256d t;
+
+    assert(std::size_t(m) % 32 == 0);
+
+    a.simdInternal_ = _mm512_add_pd(a.simdInternal_,
+                                    _mm512_shuffle_f64x2(a.simdInternal_, a.simdInternal_, 0xEE));
+    t               = _mm256_load_pd(m);
+    t               = _mm256_sub_pd(t, _mm512_castpd512_pd256(a.simdInternal_));
+    _mm256_store_pd(m, t);
+}
+} // namespace
+
+
+template<int align, typename... Targs>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v, Targs... Fargs)
+{
+    if (align > 1)
+    {
+        offset = fastMultiply<align>(offset);
+    }
+    constexpr size_t scale = sizeof(double);
+    v->simdInternal_       = _mm512_i32gather_pd(offset.simdInternal_, base, scale);
+    gatherLoadBySimdIntTranspose<1>(base + 1, offset, Fargs...);
+}
+
+template<int align, typename... Targs>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v, Targs... Fargs)
+{
+    gatherLoadBySimdIntTranspose<align>(base, offset, v, Fargs...);
+}
+
+template<int align, typename... Targs>
+static inline void gmx_simdcall
+gatherLoadTranspose(const double* base, const std::int32_t offset[], SimdDouble* v, Targs... Fargs)
+{
+    gatherLoadBySimdIntTranspose<align>(base, simdLoad(offset, SimdDInt32Tag()), v, Fargs...);
+}
+
+template<int align, typename... Targs>
+static inline void gmx_simdcall
+gatherLoadUTranspose(const double* base, const std::int32_t offset[], SimdDouble* v, Targs... Fargs)
+{
+    gatherLoadBySimdIntTranspose<align>(base, simdLoad(offset, SimdDInt32Tag()), v, Fargs...);
+}
+
+template<int align>
+static inline void gmx_simdcall transposeScatterStoreU(double*            base,
+                                                       const std::int32_t offset[],
+                                                       SimdDouble         v0,
+                                                       SimdDouble         v1,
+                                                       SimdDouble         v2)
+{
+    SimdDInt32 simdoffset = simdLoad(offset, SimdDInt32Tag());
+
+    if (align > 1)
+    {
+        simdoffset = fastMultiply<align>(simdoffset);
+        ;
+    }
+    constexpr size_t scale = sizeof(double);
+    _mm512_i32scatter_pd(base, simdoffset.simdInternal_, v0.simdInternal_, scale);
+    _mm512_i32scatter_pd(&(base[1]), simdoffset.simdInternal_, v1.simdInternal_, scale);
+    _mm512_i32scatter_pd(&(base[2]), simdoffset.simdInternal_, v2.simdInternal_, scale);
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    __m512d                                  t[4], t5, t6, t7, t8;
+    alignas(GMX_SIMD_ALIGNMENT) std::int64_t o[8];
+    // TODO: should use fastMultiply
+    _mm512_store_epi64(o,
+                       _mm512_cvtepi32_epi64(_mm256_mullo_epi32(
+                               _mm256_load_si256((const __m256i*)(offset)), _mm256_set1_epi32(align))));
+    t5   = _mm512_unpacklo_pd(v0.simdInternal_, v1.simdInternal_);
+    t6   = _mm512_unpackhi_pd(v0.simdInternal_, v1.simdInternal_);
+    t7   = _mm512_unpacklo_pd(v2.simdInternal_, _mm512_setzero_pd());
+    t8   = _mm512_unpackhi_pd(v2.simdInternal_, _mm512_setzero_pd());
+    t[0] = _mm512_mask_permutex_pd(t5, avx512Int2Mask(0xCC), t7, 0x4E);
+    t[1] = _mm512_mask_permutex_pd(t6, avx512Int2Mask(0xCC), t8, 0x4E);
+    t[2] = _mm512_mask_permutex_pd(t7, avx512Int2Mask(0x33), t5, 0x4E);
+    t[3] = _mm512_mask_permutex_pd(t8, avx512Int2Mask(0x33), t6, 0x4E);
+    if (align < 4)
+    {
+        for (int i = 0; i < 4; i++)
+        {
+            _mm512_mask_storeu_pd(base + o[0 + i],
+                                  avx512Int2Mask(7),
+                                  _mm512_castpd256_pd512(_mm256_add_pd(_mm256_loadu_pd(base + o[0 + i]),
+                                                                       _mm512_castpd512_pd256(t[i]))));
+            _mm512_mask_storeu_pd(
+                    base + o[4 + i],
+                    avx512Int2Mask(7),
+                    _mm512_castpd256_pd512(_mm256_add_pd(_mm256_loadu_pd(base + o[4 + i]),
+                                                         _mm512_extractf64x4_pd(t[i], 1))));
+        }
+    }
+    else
+    {
+        if (align % 4 == 0)
+        {
+            for (int i = 0; i < 4; i++)
+            {
+                _mm256_store_pd(
+                        base + o[0 + i],
+                        _mm256_add_pd(_mm256_load_pd(base + o[0 + i]), _mm512_castpd512_pd256(t[i])));
+                _mm256_store_pd(base + o[4 + i],
+                                _mm256_add_pd(_mm256_load_pd(base + o[4 + i]),
+                                              _mm512_extractf64x4_pd(t[i], 1)));
+            }
+        }
+        else
+        {
+            for (int i = 0; i < 4; i++)
+            {
+                _mm256_storeu_pd(
+                        base + o[0 + i],
+                        _mm256_add_pd(_mm256_loadu_pd(base + o[0 + i]), _mm512_castpd512_pd256(t[i])));
+                _mm256_storeu_pd(base + o[4 + i],
+                                 _mm256_add_pd(_mm256_loadu_pd(base + o[4 + i]),
+                                               _mm512_extractf64x4_pd(t[i], 1)));
+            }
+        }
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    __m512d                                  t[4], t5, t6, t7, t8;
+    alignas(GMX_SIMD_ALIGNMENT) std::int64_t o[8];
+    // TODO: should use fastMultiply
+    _mm512_store_epi64(o,
+                       _mm512_cvtepi32_epi64(_mm256_mullo_epi32(
+                               _mm256_load_si256((const __m256i*)(offset)), _mm256_set1_epi32(align))));
+    t5   = _mm512_unpacklo_pd(v0.simdInternal_, v1.simdInternal_);
+    t6   = _mm512_unpackhi_pd(v0.simdInternal_, v1.simdInternal_);
+    t7   = _mm512_unpacklo_pd(v2.simdInternal_, _mm512_setzero_pd());
+    t8   = _mm512_unpackhi_pd(v2.simdInternal_, _mm512_setzero_pd());
+    t[0] = _mm512_mask_permutex_pd(t5, avx512Int2Mask(0xCC), t7, 0x4E);
+    t[2] = _mm512_mask_permutex_pd(t7, avx512Int2Mask(0x33), t5, 0x4E);
+    t[1] = _mm512_mask_permutex_pd(t6, avx512Int2Mask(0xCC), t8, 0x4E);
+    t[3] = _mm512_mask_permutex_pd(t8, avx512Int2Mask(0x33), t6, 0x4E);
+    if (align < 4)
+    {
+        for (int i = 0; i < 4; i++)
+        {
+            _mm512_mask_storeu_pd(base + o[0 + i],
+                                  avx512Int2Mask(7),
+                                  _mm512_castpd256_pd512(_mm256_sub_pd(_mm256_loadu_pd(base + o[0 + i]),
+                                                                       _mm512_castpd512_pd256(t[i]))));
+            _mm512_mask_storeu_pd(
+                    base + o[4 + i],
+                    avx512Int2Mask(7),
+                    _mm512_castpd256_pd512(_mm256_sub_pd(_mm256_loadu_pd(base + o[4 + i]),
+                                                         _mm512_extractf64x4_pd(t[i], 1))));
+        }
+    }
+    else
+    {
+        if (align % 4 == 0)
+        {
+            for (int i = 0; i < 4; i++)
+            {
+                _mm256_store_pd(
+                        base + o[0 + i],
+                        _mm256_sub_pd(_mm256_load_pd(base + o[0 + i]), _mm512_castpd512_pd256(t[i])));
+                _mm256_store_pd(base + o[4 + i],
+                                _mm256_sub_pd(_mm256_load_pd(base + o[4 + i]),
+                                              _mm512_extractf64x4_pd(t[i], 1)));
+            }
+        }
+        else
+        {
+            for (int i = 0; i < 4; i++)
+            {
+                _mm256_storeu_pd(
+                        base + o[0 + i],
+                        _mm256_sub_pd(_mm256_loadu_pd(base + o[0 + i]), _mm512_castpd512_pd256(t[i])));
+                _mm256_storeu_pd(base + o[4 + i],
+                                 _mm256_sub_pd(_mm256_loadu_pd(base + o[4 + i]),
+                                               _mm512_extractf64x4_pd(t[i], 1)));
+            }
+        }
+    }
+}
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdDouble  scalar,
+                                                        SimdDouble* triplets0,
+                                                        SimdDouble* triplets1,
+                                                        SimdDouble* triplets2)
+{
+    triplets0->simdInternal_ = _mm512_castsi512_pd(_mm512_permutexvar_epi32(
+            _mm512_set_epi32(5, 4, 5, 4, 3, 2, 3, 2, 3, 2, 1, 0, 1, 0, 1, 0),
+            _mm512_castpd_si512(scalar.simdInternal_)));
+    triplets1->simdInternal_ = _mm512_castsi512_pd(_mm512_permutexvar_epi32(
+            _mm512_set_epi32(11, 10, 9, 8, 9, 8, 9, 8, 7, 6, 7, 6, 7, 6, 5, 4),
+            _mm512_castpd_si512(scalar.simdInternal_)));
+    triplets2->simdInternal_ = _mm512_castsi512_pd(_mm512_permutexvar_epi32(
+            _mm512_set_epi32(15, 14, 15, 14, 15, 14, 13, 12, 13, 12, 13, 12, 11, 10, 11, 10),
+            _mm512_castpd_si512(scalar.simdInternal_)));
+}
+
+
+static inline double gmx_simdcall
+reduceIncr4ReturnSum(double* m, SimdDouble v0, SimdDouble v1, SimdDouble v2, SimdDouble v3)
+{
+    __m512d t0, t2;
+    __m256d t3, t4;
+
+    assert(std::size_t(m) % 32 == 0);
+
+    t0 = _mm512_add_pd(v0.simdInternal_, _mm512_permute_pd(v0.simdInternal_, 0x55));
+    t2 = _mm512_add_pd(v2.simdInternal_, _mm512_permute_pd(v2.simdInternal_, 0x55));
+    t0 = _mm512_mask_add_pd(
+            t0, avx512Int2Mask(0xAA), v1.simdInternal_, _mm512_permute_pd(v1.simdInternal_, 0x55));
+    t2 = _mm512_mask_add_pd(
+            t2, avx512Int2Mask(0xAA), v3.simdInternal_, _mm512_permute_pd(v3.simdInternal_, 0x55));
+    t0 = _mm512_add_pd(t0, _mm512_shuffle_f64x2(t0, t0, 0x4E));
+    t0 = _mm512_mask_add_pd(t0, avx512Int2Mask(0xF0), t2, _mm512_shuffle_f64x2(t2, t2, 0x4E));
+    t0 = _mm512_add_pd(t0, _mm512_shuffle_f64x2(t0, t0, 0xB1));
+    t0 = _mm512_mask_shuffle_f64x2(t0, avx512Int2Mask(0x0C), t0, t0, 0xEE);
+
+    t3 = _mm512_castpd512_pd256(t0);
+    t4 = _mm256_load_pd(m);
+    t4 = _mm256_add_pd(t4, t3);
+    _mm256_store_pd(m, t4);
+
+    t0 = _mm512_add_pd(t0, _mm512_permutex_pd(t0, 0x4E));
+    t0 = _mm512_add_pd(t0, _mm512_permutex_pd(t0, 0xB1));
+
+    return _mm_cvtsd_f64(_mm512_castpd512_pd128(t0));
+}
+
+static inline SimdDouble gmx_simdcall loadDualHsimd(const double* m0, const double* m1)
+{
+    assert(std::size_t(m0) % 32 == 0);
+    assert(std::size_t(m1) % 32 == 0);
+
+    return { _mm512_insertf64x4(_mm512_castpd256_pd512(_mm256_load_pd(m0)), _mm256_load_pd(m1), 1) };
+}
+
+static inline SimdDouble gmx_simdcall loadDuplicateHsimd(const double* m)
+{
+    assert(std::size_t(m) % 32 == 0);
+
+    return { _mm512_broadcast_f64x4(_mm256_load_pd(m)) };
+}
+
+static inline SimdDouble gmx_simdcall loadU1DualHsimd(const double* m)
+{
+    return { _mm512_insertf64x4(
+            _mm512_broadcastsd_pd(_mm_load_sd(m)), _mm256_broadcastsd_pd(_mm_load_sd(m + 1)), 1) };
+}
+
+
+static inline void gmx_simdcall storeDualHsimd(double* m0, double* m1, SimdDouble a)
+{
+    assert(std::size_t(m0) % 32 == 0);
+    assert(std::size_t(m1) % 32 == 0);
+
+    _mm256_store_pd(m0, _mm512_castpd512_pd256(a.simdInternal_));
+    _mm256_store_pd(m1, _mm512_extractf64x4_pd(a.simdInternal_, 1));
+}
+
+static inline void gmx_simdcall incrDualHsimd(double* m0, double* m1, SimdDouble a)
+{
+    assert(std::size_t(m0) % 32 == 0);
+    assert(std::size_t(m1) % 32 == 0);
+
+    __m256d x;
+
+    // Lower half
+    x = _mm256_load_pd(m0);
+    x = _mm256_add_pd(x, _mm512_castpd512_pd256(a.simdInternal_));
+    _mm256_store_pd(m0, x);
+
+    // Upper half
+    x = _mm256_load_pd(m1);
+    x = _mm256_add_pd(x, _mm512_extractf64x4_pd(a.simdInternal_, 1));
+    _mm256_store_pd(m1, x);
+}
+
+static inline void gmx_simdcall decr3Hsimd(double* m, SimdDouble a0, SimdDouble a1, SimdDouble a2)
+{
+    decrHsimd(m, a0);
+    decrHsimd(m + GMX_SIMD_DOUBLE_WIDTH / 2, a1);
+    decrHsimd(m + GMX_SIMD_DOUBLE_WIDTH, a2);
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTransposeHsimd(const double*      base0,
+                                                         const double*      base1,
+                                                         const std::int32_t offset[],
+                                                         SimdDouble*        v0,
+                                                         SimdDouble*        v1)
+{
+    __m128i idx0, idx1;
+    __m256i idx;
+    __m512d tmp1, tmp2;
+
+    assert(std::size_t(offset) % 16 == 0);
+    assert(std::size_t(base0) % 16 == 0);
+    assert(std::size_t(base1) % 16 == 0);
+
+    idx0 = _mm_load_si128(reinterpret_cast<const __m128i*>(offset));
+
+    static_assert(align == 2 || align == 4, "If more are needed use fastMultiply");
+    idx0 = _mm_slli_epi32(idx0, align == 2 ? 1 : 2);
+
+    idx1 = _mm_add_epi32(idx0, _mm_set1_epi32(1));
+
+    idx = _mm256_inserti128_si256(_mm256_castsi128_si256(idx0), idx1, 1);
+
+    constexpr size_t scale = sizeof(double);
+    tmp1 = _mm512_i32gather_pd(idx, base0, scale); // TODO: Might be faster to use invidual loads
+    tmp2 = _mm512_i32gather_pd(idx, base1, scale);
+
+    v0->simdInternal_ = _mm512_shuffle_f64x2(tmp1, tmp2, 0x44);
+    v1->simdInternal_ = _mm512_shuffle_f64x2(tmp1, tmp2, 0xEE);
+}
+
+static inline double gmx_simdcall reduceIncr4ReturnSumHsimd(double* m, SimdDouble v0, SimdDouble v1)
+{
+    __m512d t0;
+    __m256d t2, t3;
+
+    assert(std::size_t(m) % 32 == 0);
+
+    t0 = _mm512_add_pd(v0.simdInternal_, _mm512_permutex_pd(v0.simdInternal_, 0x4E));
+    t0 = _mm512_mask_add_pd(
+            t0, avx512Int2Mask(0xCC), v1.simdInternal_, _mm512_permutex_pd(v1.simdInternal_, 0x4E));
+    t0 = _mm512_add_pd(t0, _mm512_permutex_pd(t0, 0xB1));
+    t0 = _mm512_mask_shuffle_f64x2(t0, avx512Int2Mask(0xAA), t0, t0, 0xEE);
+
+    t2 = _mm512_castpd512_pd256(t0);
+    t3 = _mm256_load_pd(m);
+    t3 = _mm256_add_pd(t3, t2);
+    _mm256_store_pd(m, t3);
+
+    t0 = _mm512_add_pd(t0, _mm512_permutex_pd(t0, 0x4E));
+    t0 = _mm512_add_pd(t0, _mm512_permutex_pd(t0, 0xB1));
+
+    return _mm_cvtsd_f64(_mm512_castpd512_pd128(t0));
+}
+
+static inline SimdDouble gmx_simdcall loadU4NOffset(const double* m, int offset)
+{
+    return { _mm512_insertf64x4(
+            _mm512_castpd256_pd512(_mm256_loadu_pd(m)), _mm256_loadu_pd(m + offset), 1) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_UTIL_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_util_float.h b/src/include/gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_util_float.h
new file mode 100644 (file)
index 0000000..06c648d
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_UTIL_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX_512_UTIL_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstdint>
+
+#include <immintrin.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_x86_avx_512_general.h"
+#include "impl_x86_avx_512_simd_float.h"
+
+namespace gmx
+{
+
+static const int c_simdBestPairAlignmentFloat = 2;
+
+namespace
+{
+// Multiply function optimized for powers of 2, for which it is done by
+// shifting. Currently up to 8 is accelerated. Could be accelerated for any
+// number with a constexpr log2 function.
+template<int n>
+static inline SimdFInt32 fastMultiply(SimdFInt32 x)
+{
+    if (n == 2)
+    {
+        return _mm512_slli_epi32(x.simdInternal_, 1);
+    }
+    else if (n == 4)
+    {
+        return _mm512_slli_epi32(x.simdInternal_, 2);
+    }
+    else if (n == 8)
+    {
+        return _mm512_slli_epi32(x.simdInternal_, 3);
+    }
+    else
+    {
+        return x * n;
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const float*, SimdFInt32)
+{
+    // Nothing to do. Termination of recursion.
+}
+
+/* This is an internal helper function used by decr3Hsimd(...).
+ */
+inline void gmx_simdcall decrHsimd(float* m, SimdFloat a)
+{
+    __m256 t;
+
+    assert(std::size_t(m) % 32 == 0);
+
+    a.simdInternal_ = _mm512_add_ps(a.simdInternal_,
+                                    _mm512_shuffle_f32x4(a.simdInternal_, a.simdInternal_, 0xEE));
+    t               = _mm256_load_ps(m);
+    t               = _mm256_sub_ps(t, _mm512_castps512_ps256(a.simdInternal_));
+    _mm256_store_ps(m, t);
+}
+} // namespace
+
+template<int align, typename... Targs>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v, Targs... Fargs)
+{
+    // For align 1 or 2: No multiplication of offset is needed
+    if (align > 2)
+    {
+        offset = fastMultiply<align>(offset);
+    }
+    // For align 2: Scale of 2*sizeof(float) is used (maximum supported scale)
+    constexpr int align_ = (align > 2) ? 1 : align;
+    v->simdInternal_     = _mm512_i32gather_ps(offset.simdInternal_, base, sizeof(float) * align_);
+    // Gather remaining elements. Avoid extra multiplication (new align is 1 or 2).
+    gatherLoadBySimdIntTranspose<align_>(base + 1, offset, Fargs...);
+}
+
+template<int align, typename... Targs>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v, Targs... Fargs)
+{
+    gatherLoadBySimdIntTranspose<align>(base, offset, v, Fargs...);
+}
+
+template<int align, typename... Targs>
+static inline void gmx_simdcall
+gatherLoadTranspose(const float* base, const std::int32_t offset[], SimdFloat* v, Targs... Fargs)
+{
+    gatherLoadBySimdIntTranspose<align>(base, simdLoad(offset, SimdFInt32Tag()), v, Fargs...);
+}
+
+template<int align, typename... Targs>
+static inline void gmx_simdcall
+gatherLoadUTranspose(const float* base, const std::int32_t offset[], SimdFloat* v, Targs... Fargs)
+{
+    gatherLoadBySimdIntTranspose<align>(base, simdLoad(offset, SimdFInt32Tag()), v, Fargs...);
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterStoreU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    SimdFInt32 simdoffset = simdLoad(offset, SimdFInt32Tag());
+    if (align > 2)
+    {
+        simdoffset = fastMultiply<align>(simdoffset);
+    }
+    constexpr size_t scale = (align > 2) ? sizeof(float) : sizeof(float) * align;
+
+    _mm512_i32scatter_ps(base, simdoffset.simdInternal_, v0.simdInternal_, scale);
+    _mm512_i32scatter_ps(&(base[1]), simdoffset.simdInternal_, v1.simdInternal_, scale);
+    _mm512_i32scatter_ps(&(base[2]), simdoffset.simdInternal_, v2.simdInternal_, scale);
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    __m512                                   t[4], t5, t6, t7, t8;
+    int                                      i;
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t o[16];
+    store(o, fastMultiply<align>(simdLoad(offset, SimdFInt32Tag())));
+    if (align < 4)
+    {
+        t5   = _mm512_unpacklo_ps(v0.simdInternal_, v1.simdInternal_);
+        t6   = _mm512_unpackhi_ps(v0.simdInternal_, v1.simdInternal_);
+        t[0] = _mm512_shuffle_ps(t5, v2.simdInternal_, _MM_SHUFFLE(0, 0, 1, 0));
+        t[1] = _mm512_shuffle_ps(t5, v2.simdInternal_, _MM_SHUFFLE(1, 1, 3, 2));
+        t[2] = _mm512_shuffle_ps(t6, v2.simdInternal_, _MM_SHUFFLE(2, 2, 1, 0));
+        t[3] = _mm512_shuffle_ps(t6, v2.simdInternal_, _MM_SHUFFLE(3, 3, 3, 2));
+        for (i = 0; i < 4; i++)
+        {
+            _mm512_mask_storeu_ps(base + o[i],
+                                  avx512Int2Mask(7),
+                                  _mm512_castps128_ps512(_mm_add_ps(_mm_loadu_ps(base + o[i]),
+                                                                    _mm512_castps512_ps128(t[i]))));
+            _mm512_mask_storeu_ps(base + o[4 + i],
+                                  avx512Int2Mask(7),
+                                  _mm512_castps128_ps512(_mm_add_ps(_mm_loadu_ps(base + o[4 + i]),
+                                                                    _mm512_extractf32x4_ps(t[i], 1))));
+            _mm512_mask_storeu_ps(base + o[8 + i],
+                                  avx512Int2Mask(7),
+                                  _mm512_castps128_ps512(_mm_add_ps(_mm_loadu_ps(base + o[8 + i]),
+                                                                    _mm512_extractf32x4_ps(t[i], 2))));
+            _mm512_mask_storeu_ps(base + o[12 + i],
+                                  avx512Int2Mask(7),
+                                  _mm512_castps128_ps512(_mm_add_ps(_mm_loadu_ps(base + o[12 + i]),
+                                                                    _mm512_extractf32x4_ps(t[i], 3))));
+        }
+    }
+    else
+    {
+        // One could use shuffle here too if it is OK to overwrite the padded elements for alignment
+        t5   = _mm512_unpacklo_ps(v0.simdInternal_, v2.simdInternal_);
+        t6   = _mm512_unpackhi_ps(v0.simdInternal_, v2.simdInternal_);
+        t7   = _mm512_unpacklo_ps(v1.simdInternal_, _mm512_setzero_ps());
+        t8   = _mm512_unpackhi_ps(v1.simdInternal_, _mm512_setzero_ps());
+        t[0] = _mm512_unpacklo_ps(t5, t7); // x0 y0 z0  0 | x4 y4 z4 0
+        t[1] = _mm512_unpackhi_ps(t5, t7); // x1 y1 z1  0 | x5 y5 z5 0
+        t[2] = _mm512_unpacklo_ps(t6, t8); // x2 y2 z2  0 | x6 y6 z6 0
+        t[3] = _mm512_unpackhi_ps(t6, t8); // x3 y3 z3  0 | x7 y7 z7 0
+        if (align % 4 == 0)
+        {
+            for (i = 0; i < 4; i++)
+            {
+                _mm_store_ps(base + o[i],
+                             _mm_add_ps(_mm_load_ps(base + o[i]), _mm512_castps512_ps128(t[i])));
+                _mm_store_ps(base + o[4 + i],
+                             _mm_add_ps(_mm_load_ps(base + o[4 + i]), _mm512_extractf32x4_ps(t[i], 1)));
+                _mm_store_ps(base + o[8 + i],
+                             _mm_add_ps(_mm_load_ps(base + o[8 + i]), _mm512_extractf32x4_ps(t[i], 2)));
+                _mm_store_ps(base + o[12 + i],
+                             _mm_add_ps(_mm_load_ps(base + o[12 + i]), _mm512_extractf32x4_ps(t[i], 3)));
+            }
+        }
+        else
+        {
+            for (i = 0; i < 4; i++)
+            {
+                _mm_storeu_ps(base + o[i],
+                              _mm_add_ps(_mm_loadu_ps(base + o[i]), _mm512_castps512_ps128(t[i])));
+                _mm_storeu_ps(base + o[4 + i],
+                              _mm_add_ps(_mm_loadu_ps(base + o[4 + i]), _mm512_extractf32x4_ps(t[i], 1)));
+                _mm_storeu_ps(base + o[8 + i],
+                              _mm_add_ps(_mm_loadu_ps(base + o[8 + i]), _mm512_extractf32x4_ps(t[i], 2)));
+                _mm_storeu_ps(base + o[12 + i],
+                              _mm_add_ps(_mm_loadu_ps(base + o[12 + i]), _mm512_extractf32x4_ps(t[i], 3)));
+            }
+        }
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    __m512                                   t[4], t5, t6, t7, t8;
+    int                                      i;
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t o[16];
+    store(o, fastMultiply<align>(simdLoad(offset, SimdFInt32Tag())));
+    if (align < 4)
+    {
+        t5   = _mm512_unpacklo_ps(v0.simdInternal_, v1.simdInternal_);
+        t6   = _mm512_unpackhi_ps(v0.simdInternal_, v1.simdInternal_);
+        t[0] = _mm512_shuffle_ps(t5, v2.simdInternal_, _MM_SHUFFLE(0, 0, 1, 0));
+        t[1] = _mm512_shuffle_ps(t5, v2.simdInternal_, _MM_SHUFFLE(1, 1, 3, 2));
+        t[2] = _mm512_shuffle_ps(t6, v2.simdInternal_, _MM_SHUFFLE(2, 2, 1, 0));
+        t[3] = _mm512_shuffle_ps(t6, v2.simdInternal_, _MM_SHUFFLE(3, 3, 3, 2));
+        for (i = 0; i < 4; i++)
+        {
+            _mm512_mask_storeu_ps(base + o[i],
+                                  avx512Int2Mask(7),
+                                  _mm512_castps128_ps512(_mm_sub_ps(_mm_loadu_ps(base + o[i]),
+                                                                    _mm512_castps512_ps128(t[i]))));
+            _mm512_mask_storeu_ps(base + o[4 + i],
+                                  avx512Int2Mask(7),
+                                  _mm512_castps128_ps512(_mm_sub_ps(_mm_loadu_ps(base + o[4 + i]),
+                                                                    _mm512_extractf32x4_ps(t[i], 1))));
+            _mm512_mask_storeu_ps(base + o[8 + i],
+                                  avx512Int2Mask(7),
+                                  _mm512_castps128_ps512(_mm_sub_ps(_mm_loadu_ps(base + o[8 + i]),
+                                                                    _mm512_extractf32x4_ps(t[i], 2))));
+            _mm512_mask_storeu_ps(base + o[12 + i],
+                                  avx512Int2Mask(7),
+                                  _mm512_castps128_ps512(_mm_sub_ps(_mm_loadu_ps(base + o[12 + i]),
+                                                                    _mm512_extractf32x4_ps(t[i], 3))));
+        }
+    }
+    else
+    {
+        // One could use shuffle here too if it is OK to overwrite the padded elements for alignment
+        t5   = _mm512_unpacklo_ps(v0.simdInternal_, v2.simdInternal_);
+        t6   = _mm512_unpackhi_ps(v0.simdInternal_, v2.simdInternal_);
+        t7   = _mm512_unpacklo_ps(v1.simdInternal_, _mm512_setzero_ps());
+        t8   = _mm512_unpackhi_ps(v1.simdInternal_, _mm512_setzero_ps());
+        t[0] = _mm512_unpacklo_ps(t5, t7); // x0 y0 z0  0 | x4 y4 z4 0
+        t[1] = _mm512_unpackhi_ps(t5, t7); // x1 y1 z1  0 | x5 y5 z5 0
+        t[2] = _mm512_unpacklo_ps(t6, t8); // x2 y2 z2  0 | x6 y6 z6 0
+        t[3] = _mm512_unpackhi_ps(t6, t8); // x3 y3 z3  0 | x7 y7 z7 0
+        if (align % 4 == 0)
+        {
+            for (i = 0; i < 4; i++)
+            {
+                _mm_store_ps(base + o[i],
+                             _mm_sub_ps(_mm_load_ps(base + o[i]), _mm512_castps512_ps128(t[i])));
+                _mm_store_ps(base + o[4 + i],
+                             _mm_sub_ps(_mm_load_ps(base + o[4 + i]), _mm512_extractf32x4_ps(t[i], 1)));
+                _mm_store_ps(base + o[8 + i],
+                             _mm_sub_ps(_mm_load_ps(base + o[8 + i]), _mm512_extractf32x4_ps(t[i], 2)));
+                _mm_store_ps(base + o[12 + i],
+                             _mm_sub_ps(_mm_load_ps(base + o[12 + i]), _mm512_extractf32x4_ps(t[i], 3)));
+            }
+        }
+        else
+        {
+            for (i = 0; i < 4; i++)
+            {
+                _mm_storeu_ps(base + o[i],
+                              _mm_sub_ps(_mm_loadu_ps(base + o[i]), _mm512_castps512_ps128(t[i])));
+                _mm_storeu_ps(base + o[4 + i],
+                              _mm_sub_ps(_mm_loadu_ps(base + o[4 + i]), _mm512_extractf32x4_ps(t[i], 1)));
+                _mm_storeu_ps(base + o[8 + i],
+                              _mm_sub_ps(_mm_loadu_ps(base + o[8 + i]), _mm512_extractf32x4_ps(t[i], 2)));
+                _mm_storeu_ps(base + o[12 + i],
+                              _mm_sub_ps(_mm_loadu_ps(base + o[12 + i]), _mm512_extractf32x4_ps(t[i], 3)));
+            }
+        }
+    }
+}
+
+static inline void gmx_simdcall expandScalarsToTriplets(SimdFloat  scalar,
+                                                        SimdFloat* triplets0,
+                                                        SimdFloat* triplets1,
+                                                        SimdFloat* triplets2)
+{
+    triplets0->simdInternal_ = _mm512_permutexvar_ps(
+            _mm512_set_epi32(5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 1, 1, 1, 0, 0, 0), scalar.simdInternal_);
+    triplets1->simdInternal_ = _mm512_permutexvar_ps(
+            _mm512_set_epi32(10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 5, 5), scalar.simdInternal_);
+    triplets2->simdInternal_ = _mm512_permutexvar_ps(
+            _mm512_set_epi32(15, 15, 15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, 10),
+            scalar.simdInternal_);
+}
+
+
+static inline float gmx_simdcall reduceIncr4ReturnSum(float* m, SimdFloat v0, SimdFloat v1, SimdFloat v2, SimdFloat v3)
+{
+    __m512 t0, t1, t2;
+    __m128 t3, t4;
+
+    assert(std::size_t(m) % 16 == 0);
+
+    t0 = _mm512_add_ps(v0.simdInternal_, _mm512_permute_ps(v0.simdInternal_, 0x4E));
+    t0 = _mm512_mask_add_ps(
+            t0, avx512Int2Mask(0xCCCC), v2.simdInternal_, _mm512_permute_ps(v2.simdInternal_, 0x4E));
+    t1 = _mm512_add_ps(v1.simdInternal_, _mm512_permute_ps(v1.simdInternal_, 0x4E));
+    t1 = _mm512_mask_add_ps(
+            t1, avx512Int2Mask(0xCCCC), v3.simdInternal_, _mm512_permute_ps(v3.simdInternal_, 0x4E));
+    t2 = _mm512_add_ps(t0, _mm512_permute_ps(t0, 0xB1));
+    t2 = _mm512_mask_add_ps(t2, avx512Int2Mask(0xAAAA), t1, _mm512_permute_ps(t1, 0xB1));
+
+    t2 = _mm512_add_ps(t2, _mm512_shuffle_f32x4(t2, t2, 0x4E));
+    t2 = _mm512_add_ps(t2, _mm512_shuffle_f32x4(t2, t2, 0xB1));
+
+    t3 = _mm512_castps512_ps128(t2);
+    t4 = _mm_load_ps(m);
+    t4 = _mm_add_ps(t4, t3);
+    _mm_store_ps(m, t4);
+
+    t3 = _mm_add_ps(t3, _mm_permute_ps(t3, 0x4E));
+    t3 = _mm_add_ps(t3, _mm_permute_ps(t3, 0xB1));
+
+    return _mm_cvtss_f32(t3);
+}
+
+static inline SimdFloat gmx_simdcall loadDualHsimd(const float* m0, const float* m1)
+{
+    assert(std::size_t(m0) % 32 == 0);
+    assert(std::size_t(m1) % 32 == 0);
+
+    return { _mm512_castpd_ps(_mm512_insertf64x4(
+            _mm512_castpd256_pd512(_mm256_load_pd(reinterpret_cast<const double*>(m0))),
+            _mm256_load_pd(reinterpret_cast<const double*>(m1)),
+            1)) };
+}
+
+static inline SimdFloat gmx_simdcall loadDuplicateHsimd(const float* m)
+{
+    assert(std::size_t(m) % 32 == 0);
+    return { _mm512_castpd_ps(_mm512_broadcast_f64x4(_mm256_load_pd(reinterpret_cast<const double*>(m)))) };
+}
+
+static inline SimdFloat gmx_simdcall loadU1DualHsimd(const float* m)
+{
+    return { _mm512_shuffle_f32x4(
+            _mm512_broadcastss_ps(_mm_load_ss(m)), _mm512_broadcastss_ps(_mm_load_ss(m + 1)), 0x44) };
+}
+
+
+static inline void gmx_simdcall storeDualHsimd(float* m0, float* m1, SimdFloat a)
+{
+    assert(std::size_t(m0) % 32 == 0);
+    assert(std::size_t(m1) % 32 == 0);
+
+    _mm256_store_ps(m0, _mm512_castps512_ps256(a.simdInternal_));
+    _mm256_store_pd(reinterpret_cast<double*>(m1),
+                    _mm512_extractf64x4_pd(_mm512_castps_pd(a.simdInternal_), 1));
+}
+
+static inline void gmx_simdcall incrDualHsimd(float* m0, float* m1, SimdFloat a)
+{
+    assert(std::size_t(m0) % 32 == 0);
+    assert(std::size_t(m1) % 32 == 0);
+
+    __m256 x;
+
+    // Lower half
+    x = _mm256_load_ps(m0);
+    x = _mm256_add_ps(x, _mm512_castps512_ps256(a.simdInternal_));
+    _mm256_store_ps(m0, x);
+
+    // Upper half
+    x = _mm256_load_ps(m1);
+    x = _mm256_add_ps(x, _mm256_castpd_ps(_mm512_extractf64x4_pd(_mm512_castps_pd(a.simdInternal_), 1)));
+    _mm256_store_ps(m1, x);
+}
+
+static inline void gmx_simdcall decr3Hsimd(float* m, SimdFloat a0, SimdFloat a1, SimdFloat a2)
+{
+    decrHsimd(m, a0);
+    decrHsimd(m + GMX_SIMD_FLOAT_WIDTH / 2, a1);
+    decrHsimd(m + GMX_SIMD_FLOAT_WIDTH, a2);
+}
+
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTransposeHsimd(const float*       base0,
+                                                         const float*       base1,
+                                                         const std::int32_t offset[],
+                                                         SimdFloat*         v0,
+                                                         SimdFloat*         v1)
+{
+    __m256i idx;
+    __m512  tmp1, tmp2;
+
+    assert(std::size_t(offset) % 32 == 0);
+    assert(std::size_t(base0) % 8 == 0);
+    assert(std::size_t(base1) % 8 == 0);
+
+    idx = _mm256_load_si256(reinterpret_cast<const __m256i*>(offset));
+
+    static_assert(align == 2 || align == 4, "If more are needed use fastMultiply");
+    if (align == 4)
+    {
+        idx = _mm256_slli_epi32(idx, 1);
+    }
+
+    tmp1 = _mm512_castpd_ps(
+            _mm512_i32gather_pd(idx, reinterpret_cast<const double*>(base0), sizeof(double)));
+    tmp2 = _mm512_castpd_ps(
+            _mm512_i32gather_pd(idx, reinterpret_cast<const double*>(base1), sizeof(double)));
+
+    v0->simdInternal_ = _mm512_mask_moveldup_ps(tmp1, 0xAAAA, tmp2);
+    v1->simdInternal_ = _mm512_mask_movehdup_ps(tmp2, 0x5555, tmp1);
+
+    v0->simdInternal_ = _mm512_permutexvar_ps(
+            _mm512_set_epi32(15, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 0), v0->simdInternal_);
+    v1->simdInternal_ = _mm512_permutexvar_ps(
+            _mm512_set_epi32(15, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 0), v1->simdInternal_);
+}
+
+static inline float gmx_simdcall reduceIncr4ReturnSumHsimd(float* m, SimdFloat v0, SimdFloat v1)
+{
+    __m512 t0, t1;
+    __m128 t2, t3;
+
+    assert(std::size_t(m) % 16 == 0);
+
+    t0 = _mm512_shuffle_f32x4(v0.simdInternal_, v1.simdInternal_, 0x88);
+    t1 = _mm512_shuffle_f32x4(v0.simdInternal_, v1.simdInternal_, 0xDD);
+    t0 = _mm512_add_ps(t0, t1);
+    t0 = _mm512_add_ps(t0, _mm512_permute_ps(t0, 0x4E));
+    t0 = _mm512_add_ps(t0, _mm512_permute_ps(t0, 0xB1));
+    t0 = _mm512_maskz_compress_ps(avx512Int2Mask(0x1111), t0);
+
+    t3 = _mm512_castps512_ps128(t0);
+    t2 = _mm_load_ps(m);
+    t2 = _mm_add_ps(t2, t3);
+    _mm_store_ps(m, t2);
+
+    t3 = _mm_add_ps(t3, _mm_permute_ps(t3, 0x4E));
+    t3 = _mm_add_ps(t3, _mm_permute_ps(t3, 0xB1));
+
+    return _mm_cvtss_f32(t3);
+}
+
+static inline SimdFloat gmx_simdcall loadUNDuplicate4(const float* f)
+{
+    return { _mm512_permute_ps(_mm512_maskz_expandloadu_ps(0x1111, f), 0) };
+}
+
+static inline SimdFloat gmx_simdcall load4DuplicateN(const float* f)
+{
+    return { _mm512_broadcast_f32x4(_mm_load_ps(f)) };
+}
+
+static inline SimdFloat gmx_simdcall loadU4NOffset(const float* f, int offset)
+{
+    const __m256i idx = _mm256_setr_epi32(0, 0, 1, 1, 2, 2, 3, 3);
+    const __m256i gdx = _mm256_add_epi32(_mm256_setr_epi32(0, 2, 0, 2, 0, 2, 0, 2),
+                                         _mm256_mullo_epi32(idx, _mm256_set1_epi32(offset)));
+    return { _mm512_castpd_ps(_mm512_i32gather_pd(gdx, reinterpret_cast<const double*>(f), sizeof(float))) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_UTIL_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl.h b/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl.h
new file mode 100644 (file)
index 0000000..1e7ad54
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_KNL_H
+#define GMX_SIMD_IMPL_X86_AVX_512_KNL_H
+
+#include "impl_x86_avx_512_knl_definitions.h"
+#include "impl_x86_avx_512_knl_general.h"
+#include "impl_x86_avx_512_knl_simd4_double.h"
+#include "impl_x86_avx_512_knl_simd4_float.h"
+#include "impl_x86_avx_512_knl_simd_double.h"
+#include "impl_x86_avx_512_knl_simd_float.h"
+#include "impl_x86_avx_512_knl_util_double.h"
+#include "impl_x86_avx_512_knl_util_float.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_KNL_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_definitions.h b/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_definitions.h
new file mode 100644 (file)
index 0000000..13e924d
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_KNL_DEFINITIONS_H
+#define GMX_SIMD_IMPL_X86_AVX_512_KNL_DEFINITIONS_H
+
+#include <math.h>
+
+#define GMX_SIMD 1
+#define GMX_SIMD_HAVE_FLOAT 1
+#define GMX_SIMD_HAVE_DOUBLE 1
+#define GMX_SIMD_HAVE_LOADU 1
+#define GMX_SIMD_HAVE_STOREU 1
+#define GMX_SIMD_HAVE_LOGICAL 1
+#define GMX_SIMD_HAVE_FMA 1
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 0
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 1
+// Technically it is straightforward to emulate extract on AVX-512F through
+// memory operations, but when applied to 16 elements as part of a table lookup
+// it will be faster to just store the entire vector once, so we avoid setting it.
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 0
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 1
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 1
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 1
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 1
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 1
+#define GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT 1
+#define GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE 1
+
+#define GMX_SIMD4_HAVE_FLOAT 1
+#define GMX_SIMD4_HAVE_DOUBLE 1
+
+// Implementation details
+#define GMX_SIMD_FLOAT_WIDTH 16
+#define GMX_SIMD_DOUBLE_WIDTH 8
+#define GMX_SIMD_FINT32_WIDTH 16
+#define GMX_SIMD_DINT32_WIDTH 8
+#define GMX_SIMD4_WIDTH 4
+#define GMX_SIMD_ALIGNMENT 64 // Bytes (16*single or 8*double)
+#define GMX_SIMD_RSQRT_BITS 28
+#define GMX_SIMD_RCP_BITS 28
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_KNL_DEFINITIONS_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_general.h b/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_general.h
new file mode 100644 (file)
index 0000000..14035fd
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_KNL_GENERAL_H
+#define GMX_SIMD_IMPL_X86_AVX_512_KNL_GENERAL_H
+
+#include "gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_general.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_KNL_GENERAL_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_simd4_double.h b/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_simd4_double.h
new file mode 100644 (file)
index 0000000..9163a9a
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_KNL_SIMD4_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX_512_KNL_SIMD4_DOUBLE_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd4_double.h"
+
+namespace gmx
+{
+
+static inline Simd4Double gmx_simdcall rsqrt(Simd4Double x)
+{
+    return {
+#ifndef NDEBUG // for debug mask to the 4 actually used elements to not trigger 1/0 fp exception
+        _mm512_castpd512_pd256(_mm512_maskz_rsqrt28_pd(avx512Int2Mask(0xF),
+                                                       _mm512_castpd256_pd512(x.simdInternal_)))
+#else
+        _mm512_castpd512_pd256(_mm512_rsqrt28_pd(_mm512_castpd256_pd512(x.simdInternal_)))
+#endif
+    };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_KNL_SIMD4_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_simd4_float.h b/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_simd4_float.h
new file mode 100644 (file)
index 0000000..ff493ea
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_KNL_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX_512_KNL_SIMD4_FLOAT_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd4_float.h"
+
+namespace gmx
+{
+
+static inline Simd4Float gmx_simdcall rsqrt(Simd4Float x)
+{
+    return {
+#ifndef NDEBUG // for debug mask to the 4 actually used elements to not trigger 1/0 fp exception
+        _mm512_castps512_ps128(_mm512_maskz_rsqrt28_ps(avx512Int2Mask(0xF),
+                                                       _mm512_castps128_ps512(x.simdInternal_)))
+#else
+        _mm512_castps512_ps128(_mm512_rsqrt28_ps(_mm512_castps128_ps512(x.simdInternal_)))
+#endif
+    };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_KNL_SIMD4_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_simd_double.h b/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_simd_double.h
new file mode 100644 (file)
index 0000000..2bca167
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_KNL_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX_512_KNL_SIMD_DOUBLE_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd_double.h"
+
+namespace gmx
+{
+
+static inline SimdDouble gmx_simdcall rsqrt(SimdDouble x)
+{
+    return { _mm512_rsqrt28_pd(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall rcp(SimdDouble x)
+{
+    return { _mm512_rcp28_pd(x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzRsqrt(SimdDouble x, SimdDBool m)
+{
+    return { _mm512_maskz_rsqrt28_pd(m.simdInternal_, x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzRcp(SimdDouble x, SimdDBool m)
+{
+    return { _mm512_maskz_rcp28_pd(m.simdInternal_, x.simdInternal_) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_KNL_SIMD_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_simd_float.h b/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_simd_float.h
new file mode 100644 (file)
index 0000000..52f8623
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_KNL_SIMD_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX_512_KNL_SIMD_FLOAT_H
+
+#include "config.h"
+
+#include <immintrin.h>
+
+#include "gromacs/math/utilities.h"
+#include "gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_simd_float.h"
+
+namespace gmx
+{
+
+static inline SimdFloat gmx_simdcall rsqrt(SimdFloat x)
+{
+    return { _mm512_rsqrt28_ps(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall rcp(SimdFloat x)
+{
+    return { _mm512_rcp28_ps(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzRsqrt(SimdFloat x, SimdFBool m)
+{
+    return { _mm512_maskz_rsqrt28_ps(m.simdInternal_, x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzRcp(SimdFloat x, SimdFBool m)
+{
+    return { _mm512_maskz_rcp28_ps(m.simdInternal_, x.simdInternal_) };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall exp2(SimdFloat x)
+{
+    return { _mm512_exp2a23_ps(x.simdInternal_) };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall exp(SimdFloat x)
+{
+    const __m512 argscale    = _mm512_set1_ps(1.44269504088896341F);
+    const __m512 invargscale = _mm512_set1_ps(-0.69314718055994528623F);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Set the limit to gurantee flush to zero
+        const SimdFloat smallArgLimit(-88.f);
+        // Since we multiply the argument by 1.44, for the safe version we need to make
+        // sure this doesn't result in overflow
+        x = max(x, smallArgLimit);
+    }
+
+    __m512 xscaled = _mm512_mul_ps(x.simdInternal_, argscale);
+    __m512 r       = _mm512_exp2a23_ps(xscaled);
+
+    // exp2a23_ps provides 23 bits of accuracy, but we ruin some of that with our argument
+    // scaling. To correct this, we find the difference between the scaled argument and
+    // the true one (extended precision arithmetics does not appear to be necessary to
+    // fulfill our accuracy requirements) and then multiply by the exponent of this
+    // correction since exp(a+b)=exp(a)*exp(b).
+    // Note that this only adds two instructions (and maybe some constant loads).
+
+    // find the difference
+    x = _mm512_fmadd_ps(invargscale, xscaled, x.simdInternal_);
+    // x will now be a _very_ small number, so approximate exp(x)=1+x.
+    // We should thus apply the correction as r'=r*(1+x)=r+r*x
+    r = _mm512_fmadd_ps(r, x.simdInternal_, r);
+    return { r };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_KNL_SIMD_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_util_double.h b/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_util_double.h
new file mode 100644 (file)
index 0000000..92674ce
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_KNL_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_AVX_512_KNL_UTIL_DOUBLE_H
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_util_double.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_KNL_UTIL_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_util_float.h b/src/include/gromacs/simd/impl_x86_avx_512_knl/impl_x86_avx_512_knl_util_float.h
new file mode 100644 (file)
index 0000000..a77a373
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_AVX_512_KNL_UTIL_FLOAT_H
+#define GMX_SIMD_IMPL_X86_AVX_512_KNL_UTIL_FLOAT_H
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <immintrin.h>
+
+#include "gromacs/simd/impl_x86_avx_512/impl_x86_avx_512_util_float.h"
+
+#endif // GMX_SIMD_IMPL_X86_AVX_512_KNL_UTIL_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2.h b/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2.h
new file mode 100644 (file)
index 0000000..3c8d403
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_SSE2_H
+#define GMX_SIMD_IMPL_X86_SSE2_H
+
+#include "impl_x86_sse2_definitions.h"
+#include "impl_x86_sse2_general.h"
+// SSE2 cannot do double precision SIMD4
+#include "impl_x86_sse2_simd4_float.h"
+#include "impl_x86_sse2_simd_double.h"
+#include "impl_x86_sse2_simd_float.h"
+#include "impl_x86_sse2_util_double.h"
+#include "impl_x86_sse2_util_float.h"
+
+#endif // GMX_SIMD_IMPL_X86_SSE2_H
diff --git a/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_definitions.h b/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_definitions.h
new file mode 100644 (file)
index 0000000..f47f56b
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_SSE2_DEFINITIONS_H
+#define GMX_SIMD_IMPL_X86_SSE2_DEFINITIONS_H
+
+// Capability definitions for SSE2.
+#define GMX_SIMD 1
+#define GMX_SIMD_HAVE_FLOAT 1
+#define GMX_SIMD_HAVE_DOUBLE 1
+#define GMX_SIMD_HAVE_LOADU 1
+#define GMX_SIMD_HAVE_STOREU 1
+#define GMX_SIMD_HAVE_LOGICAL 1
+#define GMX_SIMD_HAVE_FMA 0
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 1 // No SSE2 instruction, but use shifts
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 1 // No SSE2 instruction, but use shifts
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 0  // No need for half-simd, width is 4
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 0 // No need for half-simd, width is 2
+
+#define GMX_SIMD4_HAVE_FLOAT 1
+#define GMX_SIMD4_HAVE_DOUBLE 0
+
+// Implementation details
+#define GMX_SIMD_FLOAT_WIDTH 4
+#define GMX_SIMD_DOUBLE_WIDTH 2
+#define GMX_SIMD_FINT32_WIDTH 4
+#define GMX_SIMD_DINT32_WIDTH 2
+#define GMX_SIMD4_WIDTH 4
+#define GMX_SIMD_ALIGNMENT 16 // Bytes (4*single or 2*double)
+#define GMX_SIMD_RSQRT_BITS 11
+#define GMX_SIMD_RCP_BITS 11
+
+#endif // GMX_SIMD_IMPL_X86_SSE2_DEFINITIONS_H
diff --git a/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_general.h b/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_general.h
new file mode 100644 (file)
index 0000000..7eefa2f
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_IMPL_X86_SSE2_GENERAL_H
+#define GMX_SIMD_IMPL_X86_SSE2_GENERAL_H
+
+#include <emmintrin.h>
+
+namespace gmx
+{
+
+static inline void simdPrefetch(void* m)
+{
+    _mm_prefetch(reinterpret_cast<const char*>(m), _MM_HINT_T0);
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_SSE2_GENERAL_H
diff --git a/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd4_float.h b/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd4_float.h
new file mode 100644 (file)
index 0000000..0cff9bd
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_IMPL_X86_SSE2_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPL_X86_SSE2_SIMD4_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+
+#include <emmintrin.h>
+
+namespace gmx
+{
+
+class Simd4Float
+{
+public:
+    Simd4Float() {}
+
+    Simd4Float(float f) : simdInternal_(_mm_set1_ps(f)) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4Float(__m128 simd) : simdInternal_(simd) {}
+
+    __m128 simdInternal_;
+};
+
+class Simd4FBool
+{
+public:
+    Simd4FBool() {}
+
+    //! \brief Construct from scalar bool
+    Simd4FBool(bool b) : simdInternal_(_mm_castsi128_ps(_mm_set1_epi32(b ? 0xFFFFFFFF : 0))) {}
+
+    // Internal utility constructor to simplify return statements
+    Simd4FBool(__m128 simd) : simdInternal_(simd) {}
+
+    __m128 simdInternal_;
+};
+
+static inline Simd4Float gmx_simdcall load4(const float* m)
+{
+    assert(size_t(m) % 16 == 0);
+    return { _mm_load_ps(m) };
+}
+
+static inline void gmx_simdcall store4(float* m, Simd4Float a)
+{
+    assert(size_t(m) % 16 == 0);
+    _mm_store_ps(m, a.simdInternal_);
+}
+
+static inline Simd4Float gmx_simdcall load4U(const float* m)
+{
+    return { _mm_loadu_ps(m) };
+}
+
+static inline void gmx_simdcall store4U(float* m, Simd4Float a)
+{
+    _mm_storeu_ps(m, a.simdInternal_);
+}
+
+static inline Simd4Float gmx_simdcall simd4SetZeroF()
+{
+    return { _mm_setzero_ps() };
+}
+
+static inline Simd4Float gmx_simdcall operator&(Simd4Float a, Simd4Float b)
+{
+    return { _mm_and_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall andNot(Simd4Float a, Simd4Float b)
+{
+    return { _mm_andnot_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator|(Simd4Float a, Simd4Float b)
+{
+    return { _mm_or_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator^(Simd4Float a, Simd4Float b)
+{
+    return { _mm_xor_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator+(Simd4Float a, Simd4Float b)
+{
+    return { _mm_add_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float a, Simd4Float b)
+{
+    return { _mm_sub_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall operator-(Simd4Float x)
+{
+    return { _mm_xor_ps(x.simdInternal_, _mm_set1_ps(GMX_FLOAT_NEGZERO)) };
+}
+
+static inline Simd4Float gmx_simdcall operator*(Simd4Float a, Simd4Float b)
+{
+    return { _mm_mul_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for AVX-128-FMA and higher
+#if GMX_SIMD_X86_SSE2 || GMX_SIMD_X86_SSE4_1
+static inline Simd4Float gmx_simdcall fma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_add_ps(_mm_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_sub_ps(_mm_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall fnma(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_sub_ps(c.simdInternal_, _mm_mul_ps(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall fnms(Simd4Float a, Simd4Float b, Simd4Float c)
+{
+    return { _mm_sub_ps(_mm_setzero_ps(),
+                        _mm_add_ps(_mm_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_)) };
+}
+#endif
+
+static inline Simd4Float gmx_simdcall rsqrt(Simd4Float x)
+{
+    return { _mm_rsqrt_ps(x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall abs(Simd4Float x)
+{
+    return { _mm_andnot_ps(_mm_set1_ps(GMX_FLOAT_NEGZERO), x.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall max(Simd4Float a, Simd4Float b)
+{
+    return { _mm_max_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall min(Simd4Float a, Simd4Float b)
+{
+    return { _mm_min_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline Simd4Float gmx_simdcall round(Simd4Float x)
+{
+    return { _mm_cvtepi32_ps(_mm_cvtps_epi32(x.simdInternal_)) };
+}
+
+static inline Simd4Float gmx_simdcall trunc(Simd4Float x)
+{
+    return { _mm_cvtepi32_ps(_mm_cvttps_epi32(x.simdInternal_)) };
+}
+
+static inline float gmx_simdcall dotProduct(Simd4Float a, Simd4Float b)
+{
+    __m128 c, d;
+    c = _mm_mul_ps(a.simdInternal_, b.simdInternal_);
+    d = _mm_add_ps(c, _mm_shuffle_ps(c, c, _MM_SHUFFLE(2, 1, 2, 1)));
+    d = _mm_add_ps(d, _mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 2, 3, 2)));
+    return *reinterpret_cast<float*>(&d);
+}
+#endif
+
+static inline void gmx_simdcall transpose(Simd4Float* v0, Simd4Float* v1, Simd4Float* v2, Simd4Float* v3)
+{
+    _MM_TRANSPOSE4_PS(v0->simdInternal_, v1->simdInternal_, v2->simdInternal_, v3->simdInternal_);
+}
+
+static inline Simd4FBool gmx_simdcall operator==(Simd4Float a, Simd4Float b)
+{
+    return { _mm_cmpeq_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator!=(Simd4Float a, Simd4Float b)
+{
+    return { _mm_cmpneq_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<(Simd4Float a, Simd4Float b)
+{
+    return { _mm_cmplt_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator<=(Simd4Float a, Simd4Float b)
+{
+    return { _mm_cmple_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator&&(Simd4FBool a, Simd4FBool b)
+{
+    return { _mm_and_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline Simd4FBool gmx_simdcall operator||(Simd4FBool a, Simd4FBool b)
+{
+    return { _mm_or_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(Simd4FBool a)
+{
+    return _mm_movemask_ps(a.simdInternal_) != 0;
+}
+
+static inline Simd4Float gmx_simdcall selectByMask(Simd4Float a, Simd4FBool mask)
+{
+    return { _mm_and_ps(a.simdInternal_, mask.simdInternal_) };
+}
+
+static inline Simd4Float gmx_simdcall selectByNotMask(Simd4Float a, Simd4FBool mask)
+{
+    return { _mm_andnot_ps(mask.simdInternal_, a.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline Simd4Float gmx_simdcall blend(Simd4Float a, Simd4Float b, Simd4FBool sel)
+{
+    return { _mm_or_ps(_mm_andnot_ps(sel.simdInternal_, a.simdInternal_),
+                       _mm_and_ps(sel.simdInternal_, b.simdInternal_)) };
+}
+#endif
+
+// Override for AVX-128-FMA and higher
+#if GMX_SIMD_X86_SSE2 || GMX_SIMD_X86_SSE4_1
+static inline float gmx_simdcall reduce(Simd4Float a)
+{
+    __m128 b;
+    b = _mm_add_ps(a.simdInternal_,
+                   _mm_shuffle_ps(a.simdInternal_, a.simdInternal_, _MM_SHUFFLE(1, 0, 3, 2)));
+    b = _mm_add_ss(b, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&b);
+}
+#endif
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_SSE2_SIMD4_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd_double.h b/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd_double.h
new file mode 100644 (file)
index 0000000..e610bf2
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_IMPL_X86_SSE2_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_SSE2_SIMD_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <emmintrin.h>
+
+#include "gromacs/math/utilities.h"
+
+#include "impl_x86_sse2_simd_float.h"
+
+namespace gmx
+{
+
+class SimdDouble
+{
+public:
+    SimdDouble() {}
+
+    SimdDouble(double d) : simdInternal_(_mm_set1_pd(d)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDouble(__m128d simd) : simdInternal_(simd) {}
+
+    __m128d simdInternal_;
+};
+
+class SimdDInt32
+{
+public:
+    SimdDInt32() {}
+
+    SimdDInt32(std::int32_t i) : simdInternal_(_mm_set1_epi32(i)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDInt32(__m128i simd) : simdInternal_(simd) {}
+
+    __m128i simdInternal_;
+};
+
+class SimdDBool
+{
+public:
+    SimdDBool() {}
+
+    SimdDBool(bool b) : simdInternal_(_mm_castsi128_pd(_mm_set1_epi32(b ? 0xFFFFFFFF : 0))) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDBool(__m128d simd) : simdInternal_(simd) {}
+
+    __m128d simdInternal_;
+};
+
+class SimdDIBool
+{
+public:
+    SimdDIBool() {}
+
+    SimdDIBool(bool b) : simdInternal_(_mm_set1_epi32(b ? 0xFFFFFFFF : 0)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdDIBool(__m128i simd) : simdInternal_(simd) {}
+
+    __m128i simdInternal_;
+};
+
+static inline SimdDouble gmx_simdcall simdLoad(const double* m, SimdDoubleTag = {})
+{
+    assert(std::size_t(m) % 16 == 0);
+    return { _mm_load_pd(m) };
+}
+
+static inline void gmx_simdcall store(double* m, SimdDouble a)
+{
+    assert(std::size_t(m) % 16 == 0);
+    _mm_store_pd(m, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall simdLoadU(const double* m, SimdDoubleTag = {})
+{
+    return { _mm_loadu_pd(m) };
+}
+
+static inline void gmx_simdcall storeU(double* m, SimdDouble a)
+{
+    _mm_storeu_pd(m, a.simdInternal_);
+}
+
+static inline SimdDouble gmx_simdcall setZeroD()
+{
+    return { _mm_setzero_pd() };
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdDInt32Tag)
+{
+    assert(std::size_t(m) % 8 == 0);
+    return { _mm_loadl_epi64(reinterpret_cast<const __m128i*>(m)) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdDInt32 a)
+{
+    assert(std::size_t(m) % 8 == 0);
+    _mm_storel_epi64(reinterpret_cast<__m128i*>(m), a.simdInternal_);
+}
+
+static inline SimdDInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdDInt32Tag)
+{
+    return { _mm_loadl_epi64(reinterpret_cast<const __m128i*>(m)) };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdDInt32 a)
+{
+    _mm_storel_epi64(reinterpret_cast<__m128i*>(m), a.simdInternal_);
+}
+
+static inline SimdDInt32 gmx_simdcall setZeroDI()
+{
+    return { _mm_setzero_si128() };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+template<int index>
+static inline std::int32_t gmx_simdcall extract(SimdDInt32 a)
+{
+    return _mm_cvtsi128_si32(_mm_srli_si128(a.simdInternal_, 4 * index));
+}
+#endif
+
+static inline SimdDouble gmx_simdcall operator&(SimdDouble a, SimdDouble b)
+{
+    return { _mm_and_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall andNot(SimdDouble a, SimdDouble b)
+{
+    return { _mm_andnot_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator|(SimdDouble a, SimdDouble b)
+{
+    return { _mm_or_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator^(SimdDouble a, SimdDouble b)
+{
+    return { _mm_xor_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator+(SimdDouble a, SimdDouble b)
+{
+    return { _mm_add_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble a, SimdDouble b)
+{
+    return { _mm_sub_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall operator-(SimdDouble x)
+{
+    return { _mm_xor_pd(x.simdInternal_, _mm_set1_pd(GMX_DOUBLE_NEGZERO)) };
+}
+
+static inline SimdDouble gmx_simdcall operator*(SimdDouble a, SimdDouble b)
+{
+    return { _mm_mul_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for AVX-128-FMA and higher
+#if GMX_SIMD_X86_SSE2 || GMX_SIMD_X86_SSE4_1
+static inline SimdDouble gmx_simdcall fma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm_add_pd(_mm_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm_sub_pd(_mm_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall fnma(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm_sub_pd(c.simdInternal_, _mm_mul_pd(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall fnms(SimdDouble a, SimdDouble b, SimdDouble c)
+{
+    return { _mm_sub_pd(_mm_setzero_pd(),
+                        _mm_add_pd(_mm_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_)) };
+}
+#endif
+
+static inline SimdDouble gmx_simdcall rsqrt(SimdDouble x)
+{
+    return { _mm_cvtps_pd(_mm_rsqrt_ps(_mm_cvtpd_ps(x.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall rcp(SimdDouble x)
+{
+    return { _mm_cvtps_pd(_mm_rcp_ps(_mm_cvtpd_ps(x.simdInternal_))) };
+}
+
+static inline SimdDouble gmx_simdcall maskAdd(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    return { _mm_add_pd(a.simdInternal_, _mm_and_pd(b.simdInternal_, m.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall maskzMul(SimdDouble a, SimdDouble b, SimdDBool m)
+{
+    return { _mm_and_pd(_mm_mul_pd(a.simdInternal_, b.simdInternal_), m.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzFma(SimdDouble a, SimdDouble b, SimdDouble c, SimdDBool m)
+{
+    return { _mm_and_pd(_mm_add_pd(_mm_mul_pd(a.simdInternal_, b.simdInternal_), c.simdInternal_),
+                        m.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline SimdDouble gmx_simdcall maskzRsqrt(SimdDouble x, SimdDBool m)
+{
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#    ifndef NDEBUG
+    x.simdInternal_ = _mm_or_pd(_mm_andnot_pd(m.simdInternal_, _mm_set1_pd(1.0)),
+                                _mm_and_pd(m.simdInternal_, x.simdInternal_));
+#    endif
+    return { _mm_and_pd(_mm_cvtps_pd(_mm_rsqrt_ps(_mm_cvtpd_ps(x.simdInternal_))), m.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall maskzRcp(SimdDouble x, SimdDBool m)
+{
+    // The result will always be correct since we mask the result with m, but
+    // for debug builds we also want to make sure not to generate FP exceptions
+#    ifndef NDEBUG
+    x.simdInternal_ = _mm_or_pd(_mm_andnot_pd(m.simdInternal_, _mm_set1_pd(1.0)),
+                                _mm_and_pd(m.simdInternal_, x.simdInternal_));
+#    endif
+    return { _mm_and_pd(_mm_cvtps_pd(_mm_rcp_ps(_mm_cvtpd_ps(x.simdInternal_))), m.simdInternal_) };
+}
+#endif
+
+static inline SimdDouble gmx_simdcall abs(SimdDouble x)
+{
+    return { _mm_andnot_pd(_mm_set1_pd(GMX_DOUBLE_NEGZERO), x.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall max(SimdDouble a, SimdDouble b)
+{
+    return { _mm_max_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall min(SimdDouble a, SimdDouble b)
+{
+    return { _mm_min_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline SimdDouble gmx_simdcall round(SimdDouble x)
+{
+    return { _mm_cvtepi32_pd(_mm_cvtpd_epi32(x.simdInternal_)) };
+}
+
+static inline SimdDouble gmx_simdcall trunc(SimdDouble x)
+{
+    return { _mm_cvtepi32_pd(_mm_cvttpd_epi32(x.simdInternal_)) };
+}
+
+#endif
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble frexp(SimdDouble value, SimdDInt32* exponent)
+{
+    // Don't use _mm_set1_epi64x() - on MSVC it is only supported for 64-bit builds
+    const __m128d exponentMask =
+            _mm_castsi128_pd(_mm_set_epi32(0x7FF00000, 0x00000000, 0x7FF00000, 0x00000000));
+    const __m128d mantissaMask =
+            _mm_castsi128_pd(_mm_set_epi32(0x800FFFFF, 0xFFFFFFFF, 0x800FFFFF, 0xFFFFFFFF));
+    const __m128i exponentBias = _mm_set1_epi32(1022); // add 1 to make our definition identical to frexp()
+    const __m128d half         = _mm_set1_pd(0.5);
+
+    __m128i iExponent = _mm_castpd_si128(_mm_and_pd(value.simdInternal_, exponentMask));
+    iExponent         = _mm_sub_epi32(_mm_srli_epi64(iExponent, 52), exponentBias);
+
+    __m128d result = _mm_or_pd(_mm_and_pd(value.simdInternal_, mantissaMask), half);
+
+    if (opt == MathOptimization::Safe)
+    {
+        __m128d valueIsZero = _mm_cmpeq_pd(_mm_setzero_pd(), value.simdInternal_);
+        // Set the upper/lower 64-bit-fields of "iExponent" to 0-bits if the corresponding input value was +-0.0
+        iExponent = _mm_andnot_si128(_mm_castpd_si128(valueIsZero), iExponent);
+        // Set result to +-0 if the corresponding input value was +-0
+        result = _mm_or_pd(_mm_andnot_pd(valueIsZero, result),
+                           _mm_and_pd(valueIsZero, value.simdInternal_));
+    }
+
+    // Shuffle exponent so that 32-bit-fields 0 & 1 contain the relevant exponent values to return
+    exponent->simdInternal_ = _mm_shuffle_epi32(iExponent, _MM_SHUFFLE(3, 1, 2, 0));
+
+    return { result };
+}
+
+// Override for SSE4.1
+#if GMX_SIMD_X86_SSE2
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble ldexp(SimdDouble value, SimdDInt32 exponent)
+{
+    const __m128i exponentBias = _mm_set1_epi32(1023);
+    __m128i       iExponent    = _mm_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm_and_si128(iExponent, _mm_cmpgt_epi32(iExponent, _mm_setzero_si128()));
+    }
+
+    // After conversion integers will be in slot 0,1. Move them to 0,2 so
+    // we can do a 64-bit shift and get them to the dp exponents.
+    iExponent = _mm_shuffle_epi32(iExponent, _MM_SHUFFLE(3, 1, 2, 0));
+    iExponent = _mm_slli_epi64(iExponent, 52);
+
+    return { _mm_mul_pd(value.simdInternal_, _mm_castsi128_pd(iExponent)) };
+}
+#endif
+
+// Override for AVX-128-FMA and higher
+#if GMX_SIMD_X86_SSE2 || GMX_SIMD_X86_SSE4_1
+static inline double gmx_simdcall reduce(SimdDouble a)
+{
+    __m128d b = _mm_add_sd(a.simdInternal_,
+                           _mm_shuffle_pd(a.simdInternal_, a.simdInternal_, _MM_SHUFFLE2(1, 1)));
+    return *reinterpret_cast<double*>(&b);
+}
+#endif
+
+static inline SimdDBool gmx_simdcall operator==(SimdDouble a, SimdDouble b)
+{
+    return { _mm_cmpeq_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator!=(SimdDouble a, SimdDouble b)
+{
+    return { _mm_cmpneq_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator<(SimdDouble a, SimdDouble b)
+{
+    return { _mm_cmplt_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator<=(SimdDouble a, SimdDouble b)
+{
+    return { _mm_cmple_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline SimdDBool gmx_simdcall testBits(SimdDouble a)
+{
+    __m128i ia = _mm_castpd_si128(a.simdInternal_);
+    __m128i res = _mm_andnot_si128(_mm_cmpeq_epi32(ia, _mm_setzero_si128()), _mm_cmpeq_epi32(ia, ia));
+
+    // set each 64-bit element if low or high 32-bit part is set
+    res = _mm_or_si128(res, _mm_shuffle_epi32(res, _MM_SHUFFLE(2, 3, 0, 1)));
+
+    return { _mm_castsi128_pd(res) };
+}
+#endif
+
+static inline SimdDBool gmx_simdcall operator&&(SimdDBool a, SimdDBool b)
+{
+    return { _mm_and_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDBool gmx_simdcall operator||(SimdDBool a, SimdDBool b)
+{
+    return { _mm_or_pd(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDBool a)
+{
+    return _mm_movemask_pd(a.simdInternal_) != 0;
+}
+
+static inline SimdDouble gmx_simdcall selectByMask(SimdDouble a, SimdDBool mask)
+{
+    return { _mm_and_pd(a.simdInternal_, mask.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall selectByNotMask(SimdDouble a, SimdDBool mask)
+{
+    return { _mm_andnot_pd(mask.simdInternal_, a.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline SimdDouble gmx_simdcall blend(SimdDouble a, SimdDouble b, SimdDBool sel)
+{
+    return { _mm_or_pd(_mm_andnot_pd(sel.simdInternal_, a.simdInternal_),
+                       _mm_and_pd(sel.simdInternal_, b.simdInternal_)) };
+}
+#endif
+
+static inline SimdDInt32 gmx_simdcall operator&(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_and_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall andNot(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_andnot_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator|(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_or_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator^(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_xor_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator+(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_add_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator-(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_sub_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline SimdDInt32 gmx_simdcall operator*(SimdDInt32 a, SimdDInt32 b)
+{
+
+    __m128i tmpA = _mm_unpacklo_epi32(a.simdInternal_, _mm_setzero_si128()); // 0 a[1] 0 a[0]
+    __m128i tmpB = _mm_unpacklo_epi32(b.simdInternal_, _mm_setzero_si128()); // 0 b[1] 0 b[0]
+
+    __m128i tmpC = _mm_mul_epu32(tmpA, tmpB); // 0 a[1]*b[1] 0 a[0]*b[0]
+
+    return { _mm_shuffle_epi32(tmpC, _MM_SHUFFLE(3, 1, 2, 0)) };
+}
+#endif
+
+static inline SimdDIBool gmx_simdcall operator==(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_cmpeq_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall testBits(SimdDInt32 a)
+{
+    __m128i x   = a.simdInternal_;
+    __m128i res = _mm_andnot_si128(_mm_cmpeq_epi32(x, _mm_setzero_si128()), _mm_cmpeq_epi32(x, x));
+
+    return { res };
+}
+
+static inline SimdDIBool gmx_simdcall operator<(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_cmplt_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator&&(SimdDIBool a, SimdDIBool b)
+{
+    return { _mm_and_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall operator||(SimdDIBool a, SimdDIBool b)
+{
+    return { _mm_or_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdDIBool a)
+{
+    return _mm_movemask_epi8(_mm_shuffle_epi32(a.simdInternal_, _MM_SHUFFLE(1, 0, 1, 0))) != 0;
+}
+
+static inline SimdDInt32 gmx_simdcall selectByMask(SimdDInt32 a, SimdDIBool mask)
+{
+    return { _mm_and_si128(a.simdInternal_, mask.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall selectByNotMask(SimdDInt32 a, SimdDIBool mask)
+{
+    return { _mm_andnot_si128(mask.simdInternal_, a.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline SimdDInt32 gmx_simdcall blend(SimdDInt32 a, SimdDInt32 b, SimdDIBool sel)
+{
+    return { _mm_or_si128(_mm_andnot_si128(sel.simdInternal_, a.simdInternal_),
+                          _mm_and_si128(sel.simdInternal_, b.simdInternal_)) };
+}
+#endif
+
+static inline SimdDInt32 gmx_simdcall cvtR2I(SimdDouble a)
+{
+    return { _mm_cvtpd_epi32(a.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall cvttR2I(SimdDouble a)
+{
+    return { _mm_cvttpd_epi32(a.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall cvtI2R(SimdDInt32 a)
+{
+    return { _mm_cvtepi32_pd(a.simdInternal_) };
+}
+
+static inline SimdDIBool gmx_simdcall cvtB2IB(SimdDBool a)
+{
+    return { _mm_shuffle_epi32(_mm_castpd_si128(a.simdInternal_), _MM_SHUFFLE(2, 0, 2, 0)) };
+}
+
+static inline SimdDBool gmx_simdcall cvtIB2B(SimdDIBool a)
+{
+    return { _mm_castsi128_pd(_mm_shuffle_epi32(a.simdInternal_, _MM_SHUFFLE(1, 1, 0, 0))) };
+}
+
+static inline void gmx_simdcall cvtF2DD(SimdFloat f, SimdDouble* d0, SimdDouble* d1)
+{
+    d0->simdInternal_ = _mm_cvtps_pd(f.simdInternal_);
+    d1->simdInternal_ = _mm_cvtps_pd(_mm_movehl_ps(f.simdInternal_, f.simdInternal_));
+}
+
+static inline SimdFloat gmx_simdcall cvtDD2F(SimdDouble d0, SimdDouble d1)
+{
+    return { _mm_movelh_ps(_mm_cvtpd_ps(d0.simdInternal_), _mm_cvtpd_ps(d1.simdInternal_)) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_SSE2_SIMD_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd_float.h b/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd_float.h
new file mode 100644 (file)
index 0000000..dd8c6c4
--- /dev/null
@@ -0,0 +1,558 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_IMPL_X86_SSE2_SIMD_FLOAT_H
+#define GMX_SIMD_IMPL_X86_SSE2_SIMD_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <emmintrin.h>
+
+#include "gromacs/math/utilities.h"
+
+namespace gmx
+{
+
+class SimdFloat
+{
+public:
+    SimdFloat() {}
+
+    SimdFloat(float f) : simdInternal_(_mm_set1_ps(f)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFloat(__m128 simd) : simdInternal_(simd) {}
+
+    __m128 simdInternal_;
+};
+
+class SimdFInt32
+{
+public:
+    SimdFInt32() {}
+
+    SimdFInt32(std::int32_t i) : simdInternal_(_mm_set1_epi32(i)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFInt32(__m128i simd) : simdInternal_(simd) {}
+
+    __m128i simdInternal_;
+};
+
+class SimdFBool
+{
+public:
+    SimdFBool() {}
+
+    SimdFBool(bool b) : simdInternal_(_mm_castsi128_ps(_mm_set1_epi32(b ? 0xFFFFFFFF : 0))) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFBool(__m128 simd) : simdInternal_(simd) {}
+
+    __m128 simdInternal_;
+};
+
+class SimdFIBool
+{
+public:
+    SimdFIBool() {}
+
+    SimdFIBool(bool b) : simdInternal_(_mm_set1_epi32(b ? 0xFFFFFFFF : 0)) {}
+
+    // Internal utility constructor to simplify return statements
+    SimdFIBool(__m128i simd) : simdInternal_(simd) {}
+
+    __m128i simdInternal_;
+};
+
+static inline SimdFloat gmx_simdcall simdLoad(const float* m, SimdFloatTag = {})
+{
+    assert(std::size_t(m) % 16 == 0);
+    return { _mm_load_ps(m) };
+}
+
+static inline void gmx_simdcall store(float* m, SimdFloat a)
+{
+    assert(std::size_t(m) % 16 == 0);
+    _mm_store_ps(m, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall simdLoadU(const float* m, SimdFloatTag = {})
+{
+    return { _mm_loadu_ps(m) };
+}
+
+static inline void gmx_simdcall storeU(float* m, SimdFloat a)
+{
+    _mm_storeu_ps(m, a.simdInternal_);
+}
+
+static inline SimdFloat gmx_simdcall setZeroF()
+{
+    return { _mm_setzero_ps() };
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoad(const std::int32_t* m, SimdFInt32Tag)
+{
+    assert(std::size_t(m) % 16 == 0);
+    return { _mm_load_si128(reinterpret_cast<const __m128i*>(m)) };
+}
+
+static inline void gmx_simdcall store(std::int32_t* m, SimdFInt32 a)
+{
+    assert(std::size_t(m) % 16 == 0);
+    _mm_store_si128(reinterpret_cast<__m128i*>(m), a.simdInternal_);
+}
+
+static inline SimdFInt32 gmx_simdcall simdLoadU(const std::int32_t* m, SimdFInt32Tag)
+{
+    return { _mm_loadu_si128(reinterpret_cast<const __m128i*>(m)) };
+}
+
+static inline void gmx_simdcall storeU(std::int32_t* m, SimdFInt32 a)
+{
+    _mm_storeu_si128(reinterpret_cast<__m128i*>(m), a.simdInternal_);
+}
+
+static inline SimdFInt32 gmx_simdcall setZeroFI()
+{
+    return { _mm_setzero_si128() };
+}
+
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+template<int index>
+static inline std::int32_t gmx_simdcall extract(SimdFInt32 a)
+{
+    return _mm_cvtsi128_si32(_mm_srli_si128(a.simdInternal_, 4 * index));
+}
+#endif
+
+static inline SimdFloat gmx_simdcall operator&(SimdFloat a, SimdFloat b)
+{
+    return { _mm_and_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall andNot(SimdFloat a, SimdFloat b)
+{
+    return { _mm_andnot_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator|(SimdFloat a, SimdFloat b)
+{
+    return { _mm_or_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator^(SimdFloat a, SimdFloat b)
+{
+    return { _mm_xor_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator+(SimdFloat a, SimdFloat b)
+{
+    return { _mm_add_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat a, SimdFloat b)
+{
+    return { _mm_sub_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall operator-(SimdFloat x)
+{
+    return { _mm_xor_ps(x.simdInternal_, _mm_set1_ps(GMX_FLOAT_NEGZERO)) };
+}
+
+static inline SimdFloat gmx_simdcall operator*(SimdFloat a, SimdFloat b)
+{
+    return { _mm_mul_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for AVX-128-FMA and higher
+#if GMX_SIMD_X86_SSE2 || GMX_SIMD_X86_SSE4_1
+static inline SimdFloat gmx_simdcall fma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm_add_ps(_mm_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm_sub_ps(_mm_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall fnma(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm_sub_ps(c.simdInternal_, _mm_mul_ps(a.simdInternal_, b.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall fnms(SimdFloat a, SimdFloat b, SimdFloat c)
+{
+    return { _mm_sub_ps(_mm_setzero_ps(),
+                        _mm_add_ps(_mm_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_)) };
+}
+#endif
+
+static inline SimdFloat gmx_simdcall rsqrt(SimdFloat x)
+{
+    return { _mm_rsqrt_ps(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall rcp(SimdFloat x)
+{
+    return { _mm_rcp_ps(x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskAdd(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    return { _mm_add_ps(a.simdInternal_, _mm_and_ps(b.simdInternal_, m.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall maskzMul(SimdFloat a, SimdFloat b, SimdFBool m)
+{
+    return { _mm_and_ps(_mm_mul_ps(a.simdInternal_, b.simdInternal_), m.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzFma(SimdFloat a, SimdFloat b, SimdFloat c, SimdFBool m)
+{
+    return { _mm_and_ps(_mm_add_ps(_mm_mul_ps(a.simdInternal_, b.simdInternal_), c.simdInternal_),
+                        m.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline SimdFloat gmx_simdcall maskzRsqrt(SimdFloat x, SimdFBool m)
+{
+#    ifndef NDEBUG
+    x.simdInternal_ = _mm_or_ps(_mm_andnot_ps(m.simdInternal_, _mm_set1_ps(1.0F)),
+                                _mm_and_ps(m.simdInternal_, x.simdInternal_));
+#    endif
+    return { _mm_and_ps(_mm_rsqrt_ps(x.simdInternal_), m.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall maskzRcp(SimdFloat x, SimdFBool m)
+{
+#    ifndef NDEBUG
+    x.simdInternal_ = _mm_or_ps(_mm_andnot_ps(m.simdInternal_, _mm_set1_ps(1.0F)),
+                                _mm_and_ps(m.simdInternal_, x.simdInternal_));
+#    endif
+    return { _mm_and_ps(_mm_rcp_ps(x.simdInternal_), m.simdInternal_) };
+}
+#endif
+
+static inline SimdFloat gmx_simdcall abs(SimdFloat x)
+{
+    return { _mm_andnot_ps(_mm_set1_ps(GMX_FLOAT_NEGZERO), x.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall max(SimdFloat a, SimdFloat b)
+{
+    return { _mm_max_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall min(SimdFloat a, SimdFloat b)
+{
+    return { _mm_min_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline SimdFloat gmx_simdcall round(SimdFloat x)
+{
+    return { _mm_cvtepi32_ps(_mm_cvtps_epi32(x.simdInternal_)) };
+}
+
+static inline SimdFloat gmx_simdcall trunc(SimdFloat x)
+{
+    return { _mm_cvtepi32_ps(_mm_cvttps_epi32(x.simdInternal_)) };
+}
+
+#endif
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall frexp(SimdFloat value, SimdFInt32* exponent)
+{
+    const __m128 exponentMask = _mm_castsi128_ps(_mm_set1_epi32(0x7F800000));
+    const __m128 mantissaMask = _mm_castsi128_ps(_mm_set1_epi32(0x807FFFFF));
+    const __m128i exponentBias = _mm_set1_epi32(126); // add 1 to make our definition identical to frexp()
+    const __m128 half          = _mm_set1_ps(0.5F);
+
+    __m128i iExponent = _mm_castps_si128(_mm_and_ps(value.simdInternal_, exponentMask));
+    iExponent         = _mm_sub_epi32(_mm_srli_epi32(iExponent, 23), exponentBias);
+
+    // Combine mantissa and exponent for result
+    __m128 result = _mm_or_ps(_mm_and_ps(value.simdInternal_, mantissaMask), half);
+
+    if (opt == MathOptimization::Safe)
+    {
+        __m128 valueIsZero = _mm_cmpeq_ps(_mm_setzero_ps(), value.simdInternal_);
+        // If value was non-zero, use the exponent we calculated, otherwise set return-value exponent to zero.
+        iExponent = _mm_andnot_si128(_mm_castps_si128(valueIsZero), iExponent);
+        // set the fraction result to zero for all elements where the input value was zero.
+        result = _mm_or_ps(_mm_andnot_ps(valueIsZero, result),
+                           _mm_and_ps(valueIsZero, value.simdInternal_));
+    }
+
+    exponent->simdInternal_ = iExponent;
+    return { result };
+}
+
+// Override for SSE4.1
+#if GMX_SIMD_X86_SSE2
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall ldexp(SimdFloat value, SimdFInt32 exponent)
+{
+    const __m128i exponentBias = _mm_set1_epi32(127);
+    __m128i       iExponent;
+
+    iExponent = _mm_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm_and_si128(iExponent, _mm_cmpgt_epi32(iExponent, _mm_setzero_si128()));
+    }
+
+    iExponent = _mm_slli_epi32(iExponent, 23);
+
+    return { _mm_mul_ps(value.simdInternal_, _mm_castsi128_ps(iExponent)) };
+}
+#endif
+
+// Override for AVX-128-FMA and higher
+#if GMX_SIMD_X86_SSE2 || GMX_SIMD_X86_SSE4_1
+static inline float gmx_simdcall reduce(SimdFloat a)
+{
+    // Shuffle has latency 1/throughput 1, followed by add with latency 3, t-put 1.
+    // This is likely faster than using _mm_hadd_ps, which has latency 5, t-put 2.
+    a.simdInternal_ = _mm_add_ps(
+            a.simdInternal_, _mm_shuffle_ps(a.simdInternal_, a.simdInternal_, _MM_SHUFFLE(1, 0, 3, 2)));
+    a.simdInternal_ = _mm_add_ss(
+            a.simdInternal_, _mm_shuffle_ps(a.simdInternal_, a.simdInternal_, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&a);
+}
+#endif
+
+static inline SimdFBool gmx_simdcall operator==(SimdFloat a, SimdFloat b)
+{
+    return { _mm_cmpeq_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator!=(SimdFloat a, SimdFloat b)
+{
+    return { _mm_cmpneq_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator<(SimdFloat a, SimdFloat b)
+{
+    return { _mm_cmplt_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator<=(SimdFloat a, SimdFloat b)
+{
+    return { _mm_cmple_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall testBits(SimdFloat a)
+{
+    __m128i ia = _mm_castps_si128(a.simdInternal_);
+    __m128i res = _mm_andnot_si128(_mm_cmpeq_epi32(ia, _mm_setzero_si128()), _mm_cmpeq_epi32(ia, ia));
+
+    return { _mm_castsi128_ps(res) };
+}
+
+static inline SimdFBool gmx_simdcall operator&&(SimdFBool a, SimdFBool b)
+{
+    return { _mm_and_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall operator||(SimdFBool a, SimdFBool b)
+{
+    return { _mm_or_ps(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFBool a)
+{
+    return _mm_movemask_ps(a.simdInternal_) != 0;
+}
+
+static inline SimdFloat gmx_simdcall selectByMask(SimdFloat a, SimdFBool mask)
+{
+    return { _mm_and_ps(a.simdInternal_, mask.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall selectByNotMask(SimdFloat a, SimdFBool mask)
+{
+    return { _mm_andnot_ps(mask.simdInternal_, a.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline SimdFloat gmx_simdcall blend(SimdFloat a, SimdFloat b, SimdFBool sel)
+{
+    return { _mm_or_ps(_mm_andnot_ps(sel.simdInternal_, a.simdInternal_),
+                       _mm_and_ps(sel.simdInternal_, b.simdInternal_)) };
+}
+#endif
+
+static inline SimdFInt32 gmx_simdcall operator&(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm_and_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall andNot(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm_andnot_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator|(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm_or_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator^(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm_xor_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator+(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm_add_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator-(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm_sub_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline SimdFInt32 gmx_simdcall operator*(SimdFInt32 a, SimdFInt32 b)
+{
+    __m128i a1 = _mm_srli_si128(a.simdInternal_, 4); // - a[3] a[2] a[1]
+    __m128i b1 = _mm_srli_si128(b.simdInternal_, 4); // - b[3] b[2] b[1]
+    __m128i c  = _mm_mul_epu32(a.simdInternal_, b.simdInternal_);
+    __m128i c1 = _mm_mul_epu32(a1, b1);
+
+    c  = _mm_shuffle_epi32(c, _MM_SHUFFLE(3, 1, 2, 0));  // - - a[2]*b[2] a[0]*b[0]
+    c1 = _mm_shuffle_epi32(c1, _MM_SHUFFLE(3, 1, 2, 0)); // - - a[3]*b[3] a[1]*b[1]
+
+    return { _mm_unpacklo_epi32(c, c1) };
+}
+#endif
+
+static inline SimdFIBool gmx_simdcall operator==(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm_cmpeq_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall testBits(SimdFInt32 a)
+{
+    __m128i x   = a.simdInternal_;
+    __m128i res = _mm_andnot_si128(_mm_cmpeq_epi32(x, _mm_setzero_si128()), _mm_cmpeq_epi32(x, x));
+
+    return { res };
+}
+
+static inline SimdFIBool gmx_simdcall operator<(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm_cmplt_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator&&(SimdFIBool a, SimdFIBool b)
+{
+    return { _mm_and_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall operator||(SimdFIBool a, SimdFIBool b)
+{
+    return { _mm_or_si128(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline bool gmx_simdcall anyTrue(SimdFIBool a)
+{
+    return _mm_movemask_epi8(a.simdInternal_) != 0;
+}
+
+static inline SimdFInt32 gmx_simdcall selectByMask(SimdFInt32 a, SimdFIBool mask)
+{
+    return { _mm_and_si128(a.simdInternal_, mask.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall selectByNotMask(SimdFInt32 a, SimdFIBool mask)
+{
+    return { _mm_andnot_si128(mask.simdInternal_, a.simdInternal_) };
+}
+
+// Override for SSE4.1 and higher
+#if GMX_SIMD_X86_SSE2
+static inline SimdFInt32 gmx_simdcall blend(SimdFInt32 a, SimdFInt32 b, SimdFIBool sel)
+{
+    return { _mm_or_si128(_mm_andnot_si128(sel.simdInternal_, a.simdInternal_),
+                          _mm_and_si128(sel.simdInternal_, b.simdInternal_)) };
+}
+#endif
+
+static inline SimdFInt32 gmx_simdcall cvtR2I(SimdFloat a)
+{
+    return { _mm_cvtps_epi32(a.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall cvttR2I(SimdFloat a)
+{
+    return { _mm_cvttps_epi32(a.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall cvtI2R(SimdFInt32 a)
+{
+    return { _mm_cvtepi32_ps(a.simdInternal_) };
+}
+
+static inline SimdFIBool gmx_simdcall cvtB2IB(SimdFBool a)
+{
+    return { _mm_castps_si128(a.simdInternal_) };
+}
+
+static inline SimdFBool gmx_simdcall cvtIB2B(SimdFIBool a)
+{
+    return { _mm_castsi128_ps(a.simdInternal_) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_SSE2_SIMD_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_util_double.h b/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_util_double.h
new file mode 100644 (file)
index 0000000..6ffb8ad
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_IMPL_X86_SSE2_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_SSE2_UTIL_DOUBLE_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <emmintrin.h>
+
+#include "impl_x86_sse2_simd_double.h"
+
+namespace gmx
+{
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const double*      base,
+                                                    const std::int32_t offset[],
+                                                    SimdDouble*        v0,
+                                                    SimdDouble*        v1,
+                                                    SimdDouble*        v2,
+                                                    SimdDouble*        v3)
+{
+    __m128d t1, t2, t3, t4;
+
+    assert(std::size_t(base + align * offset[0]) % 16 == 0);
+    assert(std::size_t(base + align * offset[1]) % 16 == 0);
+
+    t1                = _mm_load_pd(base + align * offset[0]);
+    t2                = _mm_load_pd(base + align * offset[1]);
+    t3                = _mm_load_pd(base + align * offset[0] + 2);
+    t4                = _mm_load_pd(base + align * offset[1] + 2);
+    v0->simdInternal_ = _mm_unpacklo_pd(t1, t2);
+    v1->simdInternal_ = _mm_unpackhi_pd(t1, t2);
+    v2->simdInternal_ = _mm_unpacklo_pd(t3, t4);
+    v3->simdInternal_ = _mm_unpackhi_pd(t3, t4);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadTranspose(const double* base, const std::int32_t offset[], SimdDouble* v0, SimdDouble* v1)
+{
+    __m128d t1, t2;
+
+    assert(std::size_t(base + align * offset[0]) % 16 == 0);
+    assert(std::size_t(base + align * offset[1]) % 16 == 0);
+
+    t1                = _mm_load_pd(base + align * offset[0]);
+    t2                = _mm_load_pd(base + align * offset[1]);
+    v0->simdInternal_ = _mm_unpacklo_pd(t1, t2);
+    v1->simdInternal_ = _mm_unpackhi_pd(t1, t2);
+}
+
+static const int c_simdBestPairAlignmentDouble = 2;
+
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const double*      base,
+                                                     const std::int32_t offset[],
+                                                     SimdDouble*        v0,
+                                                     SimdDouble*        v1,
+                                                     SimdDouble*        v2)
+{
+    __m128d t1, t2, t3, t4;
+    t1                = _mm_loadu_pd(base + align * offset[0]);
+    t2                = _mm_loadu_pd(base + align * offset[1]);
+    t3                = _mm_load_sd(base + align * offset[0] + 2);
+    t4                = _mm_load_sd(base + align * offset[1] + 2);
+    v0->simdInternal_ = _mm_unpacklo_pd(t1, t2);
+    v1->simdInternal_ = _mm_unpackhi_pd(t1, t2);
+    v2->simdInternal_ = _mm_unpacklo_pd(t3, t4);
+}
+
+template<int align>
+static inline void gmx_simdcall transposeScatterStoreU(double*            base,
+                                                       const std::int32_t offset[],
+                                                       SimdDouble         v0,
+                                                       SimdDouble         v1,
+                                                       SimdDouble         v2)
+{
+    __m128d t1, t2;
+    t1 = _mm_unpacklo_pd(v0.simdInternal_, v1.simdInternal_);
+    t2 = _mm_unpackhi_pd(v0.simdInternal_, v1.simdInternal_);
+    _mm_storeu_pd(base + align * offset[0], t1);
+    _mm_store_sd(base + align * offset[0] + 2, v2.simdInternal_);
+    _mm_storeu_pd(base + align * offset[1], t2);
+    _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[1] + 2), _mm_castpd_ps(v2.simdInternal_));
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    __m128d t1, t2, t3, t4, t5, t6, t7;
+
+    t5 = _mm_unpacklo_pd(v0.simdInternal_, v1.simdInternal_);
+    t6 = _mm_unpackhi_pd(v0.simdInternal_, v1.simdInternal_);
+    t7 = _mm_unpackhi_pd(v2.simdInternal_, v2.simdInternal_);
+
+    t1 = _mm_loadu_pd(base + align * offset[0]);
+    t2 = _mm_load_sd(base + align * offset[0] + 2);
+    t1 = _mm_add_pd(t1, t5);
+    t2 = _mm_add_sd(t2, v2.simdInternal_);
+    _mm_storeu_pd(base + align * offset[0], t1);
+    _mm_store_sd(base + align * offset[0] + 2, t2);
+
+    t3 = _mm_loadu_pd(base + align * offset[1]);
+    t4 = _mm_load_sd(base + align * offset[1] + 2);
+    t3 = _mm_add_pd(t3, t6);
+    t4 = _mm_add_sd(t4, t7);
+    _mm_storeu_pd(base + align * offset[1], t3);
+    _mm_store_sd(base + align * offset[1] + 2, t4);
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(double* base, const std::int32_t offset[], SimdDouble v0, SimdDouble v1, SimdDouble v2)
+{
+    // This implementation is identical to the increment version, apart from using subtraction instead
+    __m128d t1, t2, t3, t4, t5, t6, t7;
+
+    t5 = _mm_unpacklo_pd(v0.simdInternal_, v1.simdInternal_);
+    t6 = _mm_unpackhi_pd(v0.simdInternal_, v1.simdInternal_);
+    t7 = _mm_unpackhi_pd(v2.simdInternal_, v2.simdInternal_);
+
+    t1 = _mm_loadu_pd(base + align * offset[0]);
+    t2 = _mm_load_sd(base + align * offset[0] + 2);
+    t1 = _mm_sub_pd(t1, t5);
+    t2 = _mm_sub_sd(t2, v2.simdInternal_);
+    _mm_storeu_pd(base + align * offset[0], t1);
+    _mm_store_sd(base + align * offset[0] + 2, t2);
+
+    t3 = _mm_loadu_pd(base + align * offset[1]);
+    t4 = _mm_load_sd(base + align * offset[1] + 2);
+    t3 = _mm_sub_pd(t3, t6);
+    t4 = _mm_sub_sd(t4, t7);
+    _mm_storeu_pd(base + align * offset[1], t3);
+    _mm_store_sd(base + align * offset[1] + 2, t4);
+}
+
+// Override for AVX-128-FMA and higher
+#if GMX_SIMD_X86_SSE2 || GMX_SIMD_X86_SSE4_1
+static inline void gmx_simdcall expandScalarsToTriplets(SimdDouble  scalar,
+                                                        SimdDouble* triplets0,
+                                                        SimdDouble* triplets1,
+                                                        SimdDouble* triplets2)
+{
+    triplets0->simdInternal_ =
+            _mm_shuffle_pd(scalar.simdInternal_, scalar.simdInternal_, _MM_SHUFFLE2(0, 0));
+    triplets1->simdInternal_ =
+            _mm_shuffle_pd(scalar.simdInternal_, scalar.simdInternal_, _MM_SHUFFLE2(1, 0));
+    triplets2->simdInternal_ =
+            _mm_shuffle_pd(scalar.simdInternal_, scalar.simdInternal_, _MM_SHUFFLE2(1, 1));
+}
+#endif
+
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const double* base,
+                                                             SimdDInt32    offset,
+                                                             SimdDouble*   v0,
+                                                             SimdDouble*   v1,
+                                                             SimdDouble*   v2,
+                                                             SimdDouble*   v3)
+{
+    __m128d t1, t2, t3, t4;
+    // Use optimized bit-shift multiply for the most common alignments
+    if (align == 4)
+    {
+        offset.simdInternal_ = _mm_slli_epi32(offset.simdInternal_, 2);
+    }
+    else if (align == 8)
+    {
+        offset.simdInternal_ = _mm_slli_epi32(offset.simdInternal_, 3);
+    }
+    else if (align == 12)
+    {
+        /* multiply by 3, then by 4 */
+        offset.simdInternal_ =
+                _mm_add_epi32(offset.simdInternal_, _mm_slli_epi32(offset.simdInternal_, 1));
+        offset.simdInternal_ = _mm_slli_epi32(offset.simdInternal_, 2);
+    }
+    else if (align == 16)
+    {
+        offset.simdInternal_ = _mm_slli_epi32(offset.simdInternal_, 4);
+    }
+
+    if (align == 4 || align == 8 || align == 12 || align == 16)
+    {
+        assert(std::size_t(base + extract<0>(offset)) % 16 == 0);
+        assert(std::size_t(base + extract<1>(offset)) % 16 == 0);
+
+        t1 = _mm_load_pd(base + extract<0>(offset));
+        t2 = _mm_load_pd(base + extract<1>(offset));
+        t3 = _mm_load_pd(base + extract<0>(offset) + 2);
+        t4 = _mm_load_pd(base + extract<1>(offset) + 2);
+    }
+    else
+    {
+        assert(std::size_t(base + align * extract<0>(offset)) % 16 == 0);
+        assert(std::size_t(base + align * extract<1>(offset)) % 16 == 0);
+
+        t1 = _mm_load_pd(base + align * extract<0>(offset));
+        t2 = _mm_load_pd(base + align * extract<1>(offset));
+        t3 = _mm_load_pd(base + align * extract<0>(offset) + 2);
+        t4 = _mm_load_pd(base + align * extract<1>(offset) + 2);
+    }
+    v0->simdInternal_ = _mm_unpacklo_pd(t1, t2);
+    v1->simdInternal_ = _mm_unpackhi_pd(t1, t2);
+    v2->simdInternal_ = _mm_unpacklo_pd(t3, t4);
+    v3->simdInternal_ = _mm_unpackhi_pd(t3, t4);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    __m128d t1, t2;
+
+    // Use optimized bit-shift multiply for the most common alignments
+    if (align == 2)
+    {
+        offset.simdInternal_ = _mm_slli_epi32(offset.simdInternal_, 1);
+    }
+    else if (align == 4)
+    {
+        offset.simdInternal_ = _mm_slli_epi32(offset.simdInternal_, 2);
+    }
+    else if (align == 6)
+    {
+        // multiply by 3, then by 2
+        offset.simdInternal_ =
+                _mm_add_epi32(offset.simdInternal_, _mm_slli_epi32(offset.simdInternal_, 1));
+        offset.simdInternal_ = _mm_slli_epi32(offset.simdInternal_, 1);
+    }
+    else if (align == 8)
+    {
+        offset.simdInternal_ = _mm_slli_epi32(offset.simdInternal_, 3);
+    }
+    else if (align == 12)
+    {
+        // multiply by 3, then by 4
+        offset.simdInternal_ =
+                _mm_add_epi32(offset.simdInternal_, _mm_slli_epi32(offset.simdInternal_, 1));
+        offset.simdInternal_ = _mm_slli_epi32(offset.simdInternal_, 2);
+    }
+    else if (align == 16)
+    {
+        offset.simdInternal_ = _mm_slli_epi32(offset.simdInternal_, 4);
+    }
+
+    if (align == 2 || align == 4 || align == 6 || align == 8 || align == 12 || align == 16)
+    {
+        assert(std::size_t(base + extract<0>(offset)) % 16 == 0);
+        assert(std::size_t(base + extract<1>(offset)) % 16 == 0);
+
+        t1 = _mm_load_pd(base + extract<0>(offset));
+        t2 = _mm_load_pd(base + extract<1>(offset));
+    }
+    else
+    {
+        assert(std::size_t(base + align * extract<0>(offset)) % 16 == 0);
+        assert(std::size_t(base + align * extract<1>(offset)) % 16 == 0);
+
+        t1 = _mm_load_pd(base + align * extract<0>(offset));
+        t2 = _mm_load_pd(base + align * extract<1>(offset));
+    }
+    v0->simdInternal_ = _mm_unpacklo_pd(t1, t2);
+    v1->simdInternal_ = _mm_unpackhi_pd(t1, t2);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const double* base, SimdDInt32 offset, SimdDouble* v0, SimdDouble* v1)
+{
+    __m128d t1, t2;
+    // Use optimized bit-shift multiply for the most common alignments.
+
+    // Do nothing for align == 1
+    if (align == 2)
+    {
+        offset.simdInternal_ = _mm_slli_epi32(offset.simdInternal_, 1);
+    }
+    else if (align == 4)
+    {
+        offset.simdInternal_ = _mm_slli_epi32(offset.simdInternal_, 2);
+    }
+
+    if (align == 1 || align == 2 || align == 4)
+    {
+        t1 = _mm_loadu_pd(base + extract<0>(offset));
+        t2 = _mm_loadu_pd(base + extract<1>(offset));
+    }
+    else
+    {
+        t1 = _mm_loadu_pd(base + align * extract<0>(offset));
+        t2 = _mm_loadu_pd(base + align * extract<1>(offset));
+    }
+    v0->simdInternal_ = _mm_unpacklo_pd(t1, t2);
+    v1->simdInternal_ = _mm_unpackhi_pd(t1, t2);
+}
+
+// Override for AVX-128-FMA and higher
+#if GMX_SIMD_X86_SSE2 || GMX_SIMD_X86_SSE4_1
+static inline double gmx_simdcall
+reduceIncr4ReturnSum(double* m, SimdDouble v0, SimdDouble v1, SimdDouble v2, SimdDouble v3)
+{
+    __m128d t1, t2, t3, t4;
+
+    t1 = _mm_unpacklo_pd(v0.simdInternal_, v1.simdInternal_);
+    t2 = _mm_unpackhi_pd(v0.simdInternal_, v1.simdInternal_);
+    t3 = _mm_unpacklo_pd(v2.simdInternal_, v3.simdInternal_);
+    t4 = _mm_unpackhi_pd(v2.simdInternal_, v3.simdInternal_);
+
+    t1 = _mm_add_pd(t1, t2);
+    t3 = _mm_add_pd(t3, t4);
+
+    t2 = _mm_add_pd(t1, _mm_load_pd(m));
+    t4 = _mm_add_pd(t3, _mm_load_pd(m + 2));
+
+    assert(std::size_t(m) % 16 == 0);
+
+    _mm_store_pd(m, t2);
+    _mm_store_pd(m + 2, t4);
+
+    t1 = _mm_add_pd(t1, t3);
+
+    t2 = _mm_add_sd(t1, _mm_shuffle_pd(t1, t1, _MM_SHUFFLE2(1, 1)));
+    return *reinterpret_cast<double*>(&t2);
+}
+#endif
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_SSE2_UTIL_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_util_float.h b/src/include/gromacs/simd/impl_x86_sse2/impl_x86_sse2_util_float.h
new file mode 100644 (file)
index 0000000..7d7be0b
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_IMPL_X86_SSE2_UTIL_FLOAT_H
+#define GMX_SIMD_IMPL_X86_SSE2_UTIL_FLOAT_H
+
+#include "config.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+
+#include <emmintrin.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+#include "impl_x86_sse2_simd_float.h"
+
+
+namespace gmx
+{
+
+template<int align>
+static inline void gmx_simdcall gatherLoadTranspose(const float*       base,
+                                                    const std::int32_t offset[],
+                                                    SimdFloat*         v0,
+                                                    SimdFloat*         v1,
+                                                    SimdFloat*         v2,
+                                                    SimdFloat*         v3)
+{
+    assert(std::size_t(base + align * offset[0]) % 16 == 0);
+    assert(std::size_t(base + align * offset[1]) % 16 == 0);
+    assert(std::size_t(base + align * offset[2]) % 16 == 0);
+    assert(std::size_t(base + align * offset[3]) % 16 == 0);
+
+    v0->simdInternal_ = _mm_load_ps(base + align * offset[0]);
+    v1->simdInternal_ = _mm_load_ps(base + align * offset[1]);
+    v2->simdInternal_ = _mm_load_ps(base + align * offset[2]);
+    v3->simdInternal_ = _mm_load_ps(base + align * offset[3]);
+
+    _MM_TRANSPOSE4_PS(v0->simdInternal_, v1->simdInternal_, v2->simdInternal_, v3->simdInternal_);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadTranspose(const float* base, const std::int32_t offset[], SimdFloat* v0, SimdFloat* v1)
+{
+    __m128 t1, t2;
+
+    v0->simdInternal_ =
+            _mm_castpd_ps(_mm_load_sd(reinterpret_cast<const double*>(base + align * offset[0])));
+    v1->simdInternal_ =
+            _mm_castpd_ps(_mm_load_sd(reinterpret_cast<const double*>(base + align * offset[1])));
+    t1 = _mm_castpd_ps(_mm_load_sd(reinterpret_cast<const double*>(base + align * offset[2])));
+    t2 = _mm_castpd_ps(_mm_load_sd(reinterpret_cast<const double*>(base + align * offset[3])));
+    t1 = _mm_unpacklo_ps(v0->simdInternal_, t1);
+    t2 = _mm_unpacklo_ps(v1->simdInternal_, t2);
+    v0->simdInternal_ = _mm_unpacklo_ps(t1, t2);
+    v1->simdInternal_ = _mm_unpackhi_ps(t1, t2);
+}
+
+static const int c_simdBestPairAlignmentFloat = 2;
+
+template<int align>
+static inline void gmx_simdcall gatherLoadUTranspose(const float*       base,
+                                                     const std::int32_t offset[],
+                                                     SimdFloat*         v0,
+                                                     SimdFloat*         v1,
+                                                     SimdFloat*         v2)
+{
+    __m128 t1, t2, t3, t4, t5, t6, t7, t8;
+
+    if (align % 4 != 0)
+    {
+        // general case, not aligned to 4-byte boundary
+        t1 = _mm_loadu_ps(base + align * offset[0]);
+        t2 = _mm_loadu_ps(base + align * offset[1]);
+        t3 = _mm_loadu_ps(base + align * offset[2]);
+        t4 = _mm_loadu_ps(base + align * offset[3]);
+    }
+    else
+    {
+        // aligned to 4-byte boundary or more
+        t1 = _mm_load_ps(base + align * offset[0]);
+        t2 = _mm_load_ps(base + align * offset[1]);
+        t3 = _mm_load_ps(base + align * offset[2]);
+        t4 = _mm_load_ps(base + align * offset[3]);
+    }
+    t5  = _mm_unpacklo_ps(t1, t2);
+    t6  = _mm_unpacklo_ps(t3, t4);
+    t7  = _mm_unpackhi_ps(t1, t2);
+    t8  = _mm_unpackhi_ps(t3, t4);
+    *v0 = _mm_movelh_ps(t5, t6);
+    *v1 = _mm_movehl_ps(t6, t5);
+    *v2 = _mm_movelh_ps(t7, t8);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterStoreU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    __m128 t1, t2;
+
+    // general case, not aligned to 4-byte boundary
+    t1 = _mm_unpacklo_ps(v0.simdInternal_, v1.simdInternal_);
+    t2 = _mm_unpackhi_ps(v0.simdInternal_, v1.simdInternal_);
+    _mm_storel_pi(reinterpret_cast<__m64*>(base + align * offset[0]), t1);
+    _mm_store_ss(base + align * offset[0] + 2, v2.simdInternal_);
+    _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[1]), t1);
+    _mm_store_ss(base + align * offset[1] + 2,
+                 _mm_shuffle_ps(v2.simdInternal_, v2.simdInternal_, _MM_SHUFFLE(1, 1, 1, 1)));
+    _mm_storel_pi(reinterpret_cast<__m64*>(base + align * offset[2]), t2);
+    _mm_store_ss(base + align * offset[2] + 2,
+                 _mm_shuffle_ps(v2.simdInternal_, v2.simdInternal_, _MM_SHUFFLE(2, 2, 2, 2)));
+    _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[3]), t2);
+    _mm_store_ss(base + align * offset[3] + 2,
+                 _mm_shuffle_ps(v2.simdInternal_, v2.simdInternal_, _MM_SHUFFLE(3, 3, 3, 3)));
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterIncrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    __m128 t1, t2, t3, t4, t5, t6, t7, t8, t9, t10;
+
+    if (align < 4)
+    {
+        t5  = _mm_unpacklo_ps(v1.simdInternal_, v2.simdInternal_);
+        t6  = _mm_unpackhi_ps(v1.simdInternal_, v2.simdInternal_);
+        t7  = _mm_shuffle_ps(v0.simdInternal_, t5, _MM_SHUFFLE(1, 0, 0, 0));
+        t8  = _mm_shuffle_ps(v0.simdInternal_, t5, _MM_SHUFFLE(3, 2, 0, 1));
+        t9  = _mm_shuffle_ps(v0.simdInternal_, t6, _MM_SHUFFLE(1, 0, 0, 2));
+        t10 = _mm_shuffle_ps(v0.simdInternal_, t6, _MM_SHUFFLE(3, 2, 0, 3));
+
+        t1 = _mm_load_ss(base + align * offset[0]);
+        t1 = _mm_loadh_pi(t1, reinterpret_cast<__m64*>(base + align * offset[0] + 1));
+        t1 = _mm_add_ps(t1, t7);
+        _mm_store_ss(base + align * offset[0], t1);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[0] + 1), t1);
+
+        t2 = _mm_load_ss(base + align * offset[1]);
+        t2 = _mm_loadh_pi(t2, reinterpret_cast<__m64*>(base + align * offset[1] + 1));
+        t2 = _mm_add_ps(t2, t8);
+        _mm_store_ss(base + align * offset[1], t2);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[1] + 1), t2);
+
+        t3 = _mm_load_ss(base + align * offset[2]);
+        t3 = _mm_loadh_pi(t3, reinterpret_cast<__m64*>(base + align * offset[2] + 1));
+        t3 = _mm_add_ps(t3, t9);
+        _mm_store_ss(base + align * offset[2], t3);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[2] + 1), t3);
+
+        t4 = _mm_load_ss(base + align * offset[3]);
+        t4 = _mm_loadh_pi(t4, reinterpret_cast<__m64*>(base + align * offset[3] + 1));
+        t4 = _mm_add_ps(t4, t10);
+        _mm_store_ss(base + align * offset[3], t4);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[3] + 1), t4);
+    }
+    else
+    {
+        // Extra elements means we can use full width-4 load/store operations
+
+        t1 = _mm_unpacklo_ps(v0.simdInternal_, v2.simdInternal_); // x0 z0 x1 z1
+        t2 = _mm_unpackhi_ps(v0.simdInternal_, v2.simdInternal_); // x2 z2 x3 z3
+        t3 = _mm_unpacklo_ps(v1.simdInternal_, _mm_setzero_ps()); // y0  0 y1  0
+        t4 = _mm_unpackhi_ps(v1.simdInternal_, _mm_setzero_ps()); // y2  0 y3  0
+        t5 = _mm_unpacklo_ps(t1, t3);                             // x0 y0 z0  0
+        t6 = _mm_unpackhi_ps(t1, t3);                             // x1 y1 z1  0
+        t7 = _mm_unpacklo_ps(t2, t4);                             // x2 y2 z2  0
+        t8 = _mm_unpackhi_ps(t2, t4);                             // x3 y3 z3  0
+
+        if (align % 4 == 0)
+        {
+            // alignment is a multiple of 4
+            _mm_store_ps(base + align * offset[0], _mm_add_ps(_mm_load_ps(base + align * offset[0]), t5));
+            _mm_store_ps(base + align * offset[1], _mm_add_ps(_mm_load_ps(base + align * offset[1]), t6));
+            _mm_store_ps(base + align * offset[2], _mm_add_ps(_mm_load_ps(base + align * offset[2]), t7));
+            _mm_store_ps(base + align * offset[3], _mm_add_ps(_mm_load_ps(base + align * offset[3]), t8));
+        }
+        else
+        {
+            // alignment >=5, but not a multiple of 4
+            _mm_storeu_ps(base + align * offset[0],
+                          _mm_add_ps(_mm_loadu_ps(base + align * offset[0]), t5));
+            _mm_storeu_ps(base + align * offset[1],
+                          _mm_add_ps(_mm_loadu_ps(base + align * offset[1]), t6));
+            _mm_storeu_ps(base + align * offset[2],
+                          _mm_add_ps(_mm_loadu_ps(base + align * offset[2]), t7));
+            _mm_storeu_ps(base + align * offset[3],
+                          _mm_add_ps(_mm_loadu_ps(base + align * offset[3]), t8));
+        }
+    }
+}
+
+template<int align>
+static inline void gmx_simdcall
+transposeScatterDecrU(float* base, const std::int32_t offset[], SimdFloat v0, SimdFloat v1, SimdFloat v2)
+{
+    // This implementation is identical to the increment version, apart from using subtraction instead
+    __m128 t1, t2, t3, t4, t5, t6, t7, t8, t9, t10;
+
+    if (align < 4)
+    {
+        t5  = _mm_unpacklo_ps(v1.simdInternal_, v2.simdInternal_);
+        t6  = _mm_unpackhi_ps(v1.simdInternal_, v2.simdInternal_);
+        t7  = _mm_shuffle_ps(v0.simdInternal_, t5, _MM_SHUFFLE(1, 0, 0, 0));
+        t8  = _mm_shuffle_ps(v0.simdInternal_, t5, _MM_SHUFFLE(3, 2, 0, 1));
+        t9  = _mm_shuffle_ps(v0.simdInternal_, t6, _MM_SHUFFLE(1, 0, 0, 2));
+        t10 = _mm_shuffle_ps(v0.simdInternal_, t6, _MM_SHUFFLE(3, 2, 0, 3));
+
+        t1 = _mm_load_ss(base + align * offset[0]);
+        t1 = _mm_loadh_pi(t1, reinterpret_cast<__m64*>(base + align * offset[0] + 1));
+        t1 = _mm_sub_ps(t1, t7);
+        _mm_store_ss(base + align * offset[0], t1);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[0] + 1), t1);
+
+        t2 = _mm_load_ss(base + align * offset[1]);
+        t2 = _mm_loadh_pi(t2, reinterpret_cast<__m64*>(base + align * offset[1] + 1));
+        t2 = _mm_sub_ps(t2, t8);
+        _mm_store_ss(base + align * offset[1], t2);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[1] + 1), t2);
+
+        t3 = _mm_load_ss(base + align * offset[2]);
+        t3 = _mm_loadh_pi(t3, reinterpret_cast<__m64*>(base + align * offset[2] + 1));
+        t3 = _mm_sub_ps(t3, t9);
+        _mm_store_ss(base + align * offset[2], t3);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[2] + 1), t3);
+
+        t4 = _mm_load_ss(base + align * offset[3]);
+        t4 = _mm_loadh_pi(t4, reinterpret_cast<__m64*>(base + align * offset[3] + 1));
+        t4 = _mm_sub_ps(t4, t10);
+        _mm_store_ss(base + align * offset[3], t4);
+        _mm_storeh_pi(reinterpret_cast<__m64*>(base + align * offset[3] + 1), t4);
+    }
+    else
+    {
+        // Extra elements means we can use full width-4 load/store operations
+
+        t1 = _mm_unpacklo_ps(v0.simdInternal_, v2.simdInternal_); // x0 z0 x1 z1
+        t2 = _mm_unpackhi_ps(v0.simdInternal_, v2.simdInternal_); // x2 z2 x3 z3
+        t3 = _mm_unpacklo_ps(v1.simdInternal_, _mm_setzero_ps()); // y0  0 y1  0
+        t4 = _mm_unpackhi_ps(v1.simdInternal_, _mm_setzero_ps()); // y2  0 y3  0
+        t5 = _mm_unpacklo_ps(t1, t3);                             // x0 y0 z0  0
+        t6 = _mm_unpackhi_ps(t1, t3);                             // x1 y1 z1  0
+        t7 = _mm_unpacklo_ps(t2, t4);                             // x2 y2 z2  0
+        t8 = _mm_unpackhi_ps(t2, t4);                             // x3 y3 z3  0
+
+        if (align % 4 == 0)
+        {
+            // alignment is a multiple of 4
+            _mm_store_ps(base + align * offset[0], _mm_sub_ps(_mm_load_ps(base + align * offset[0]), t5));
+            _mm_store_ps(base + align * offset[1], _mm_sub_ps(_mm_load_ps(base + align * offset[1]), t6));
+            _mm_store_ps(base + align * offset[2], _mm_sub_ps(_mm_load_ps(base + align * offset[2]), t7));
+            _mm_store_ps(base + align * offset[3], _mm_sub_ps(_mm_load_ps(base + align * offset[3]), t8));
+        }
+        else
+        {
+            // alignment >=5, but not a multiple of 4
+            _mm_storeu_ps(base + align * offset[0],
+                          _mm_sub_ps(_mm_loadu_ps(base + align * offset[0]), t5));
+            _mm_storeu_ps(base + align * offset[1],
+                          _mm_sub_ps(_mm_loadu_ps(base + align * offset[1]), t6));
+            _mm_storeu_ps(base + align * offset[2],
+                          _mm_sub_ps(_mm_loadu_ps(base + align * offset[2]), t7));
+            _mm_storeu_ps(base + align * offset[3],
+                          _mm_sub_ps(_mm_loadu_ps(base + align * offset[3]), t8));
+        }
+    }
+}
+
+// Override for AVX-128-FMA and higher
+#if GMX_SIMD_X86_SSE2 || GMX_SIMD_X86_SSE4_1
+static inline void gmx_simdcall expandScalarsToTriplets(SimdFloat  scalar,
+                                                        SimdFloat* triplets0,
+                                                        SimdFloat* triplets1,
+                                                        SimdFloat* triplets2)
+{
+    triplets0->simdInternal_ =
+            _mm_shuffle_ps(scalar.simdInternal_, scalar.simdInternal_, _MM_SHUFFLE(1, 0, 0, 0));
+    triplets1->simdInternal_ =
+            _mm_shuffle_ps(scalar.simdInternal_, scalar.simdInternal_, _MM_SHUFFLE(2, 2, 1, 1));
+    triplets2->simdInternal_ =
+            _mm_shuffle_ps(scalar.simdInternal_, scalar.simdInternal_, _MM_SHUFFLE(3, 3, 3, 2));
+}
+#endif
+
+
+template<int align>
+static inline void gmx_simdcall gatherLoadBySimdIntTranspose(const float* base,
+                                                             SimdFInt32   offset,
+                                                             SimdFloat*   v0,
+                                                             SimdFloat*   v1,
+                                                             SimdFloat*   v2,
+                                                             SimdFloat*   v3)
+{
+    // For present-generation x86 CPUs it appears to be faster to simply
+    // store the SIMD integer to memory and then use the normal load operations.
+    // This is likely because (a) the extract function is expensive, and (b)
+    // the alignment scaling can often be done as part of the load instruction
+    // (which is even cheaper than doing it in SIMD registers).
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_FINT32_WIDTH];
+    _mm_store_si128((__m128i*)ioffset, offset.simdInternal_);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1, v2, v3);
+}
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v0, SimdFloat* v1)
+{
+    // For present-generation x86 CPUs it appears to be faster to simply
+    // store the SIMD integer to memory and then use the normal load operations.
+    // This is likely because (a) the extract function is expensive, and (b)
+    // the alignment scaling can often be done as part of the load instruction
+    // (which is even cheaper than doing it in SIMD registers).
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_FINT32_WIDTH];
+    _mm_store_si128((__m128i*)ioffset, offset.simdInternal_);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1);
+}
+
+
+template<int align>
+static inline void gmx_simdcall
+gatherLoadUBySimdIntTranspose(const float* base, SimdFInt32 offset, SimdFloat* v0, SimdFloat* v1)
+{
+    // For present-generation x86 CPUs it appears to be faster to simply
+    // store the SIMD integer to memory and then use the normal load operations.
+    // This is likely because (a) the extract function is expensive, and (b)
+    // the alignment scaling can often be done as part of the load instruction
+    // (which is even cheaper than doing it in SIMD registers).
+    alignas(GMX_SIMD_ALIGNMENT) std::int32_t ioffset[GMX_SIMD_FINT32_WIDTH];
+    _mm_store_si128((__m128i*)ioffset, offset.simdInternal_);
+    gatherLoadTranspose<align>(base, ioffset, v0, v1);
+}
+
+// Override for AVX-128-FMA and higher
+#if GMX_SIMD_X86_SSE2 || GMX_SIMD_X86_SSE4_1
+static inline float gmx_simdcall reduceIncr4ReturnSum(float* m, SimdFloat v0, SimdFloat v1, SimdFloat v2, SimdFloat v3)
+{
+    _MM_TRANSPOSE4_PS(v0.simdInternal_, v1.simdInternal_, v2.simdInternal_, v3.simdInternal_);
+    v0.simdInternal_ = _mm_add_ps(v0.simdInternal_, v1.simdInternal_);
+    v2.simdInternal_ = _mm_add_ps(v2.simdInternal_, v3.simdInternal_);
+    v0.simdInternal_ = _mm_add_ps(v0.simdInternal_, v2.simdInternal_);
+    v2.simdInternal_ = _mm_add_ps(v0.simdInternal_, _mm_load_ps(m));
+
+    assert(std::size_t(m) % 16 == 0);
+    _mm_store_ps(m, v2.simdInternal_);
+
+    __m128 b = _mm_add_ps(v0.simdInternal_,
+                          _mm_shuffle_ps(v0.simdInternal_, v0.simdInternal_, _MM_SHUFFLE(1, 0, 3, 2)));
+    b        = _mm_add_ss(b, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 3, 2, 1)));
+    return *reinterpret_cast<float*>(&b);
+}
+#endif
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_SSE2_UTIL_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1.h b/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1.h
new file mode 100644 (file)
index 0000000..769532d
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_SSE4_1_H
+#define GMX_SIMD_IMPL_X86_SSE4_1_H
+
+#include "impl_x86_sse4_1_definitions.h"
+#include "impl_x86_sse4_1_general.h"
+// SSE4.1 cannot do double precision SIMD4
+#include "impl_x86_sse4_1_simd4_float.h"
+#include "impl_x86_sse4_1_simd_double.h"
+#include "impl_x86_sse4_1_simd_float.h"
+#include "impl_x86_sse4_1_util_double.h"
+#include "impl_x86_sse4_1_util_float.h"
+
+#endif // GMX_SIMD_IMPL_X86_SSE4_1_H
diff --git a/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_definitions.h b/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_definitions.h
new file mode 100644 (file)
index 0000000..0e50fb0
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_SSE4_1_DEFINITIONS_H
+#define GMX_SIMD_IMPL_X86_SSE4_1_DEFINITIONS_H
+
+// Capability definitions for SSE4.1
+#define GMX_SIMD 1
+#define GMX_SIMD_HAVE_FLOAT 1
+#define GMX_SIMD_HAVE_DOUBLE 1
+#define GMX_SIMD_HAVE_LOADU 1
+#define GMX_SIMD_HAVE_STOREU 1
+#define GMX_SIMD_HAVE_LOGICAL 1
+#define GMX_SIMD_HAVE_FMA 0
+#define GMX_SIMD_HAVE_FINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_FINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_FINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_DINT32_EXTRACT 1
+#define GMX_SIMD_HAVE_DINT32_LOGICAL 1
+#define GMX_SIMD_HAVE_DINT32_ARITHMETICS 1
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_FLOAT 0
+#define GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE 0
+#define GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE 0
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT 1
+#define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE 1
+#define GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT 0  // No need for half-simd, width is 4
+#define GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE 0 // No need for half-simd, width is 2
+
+#define GMX_SIMD4_HAVE_FLOAT 1
+#define GMX_SIMD4_HAVE_DOUBLE 0
+
+// Implementation details
+#define GMX_SIMD_FLOAT_WIDTH 4
+#define GMX_SIMD_DOUBLE_WIDTH 2
+#define GMX_SIMD_FINT32_WIDTH 4
+#define GMX_SIMD_DINT32_WIDTH 2
+#define GMX_SIMD4_WIDTH 4
+#define GMX_SIMD_ALIGNMENT 16 // Bytes (4*single or 2*double)
+#define GMX_SIMD_RSQRT_BITS 11
+#define GMX_SIMD_RCP_BITS 11
+
+#endif // GMX_SIMD_IMPL_X86_SSE4_1_DEFINITIONS_H
diff --git a/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_general.h b/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_general.h
new file mode 100644 (file)
index 0000000..6636e3a
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_SSE4_1_GENERAL_H
+#define GMX_SIMD_IMPL_X86_SSE4_1_GENERAL_H
+
+#include "gromacs/simd/impl_x86_sse2/impl_x86_sse2_general.h"
+
+#endif // GMX_SIMD_IMPL_X86_SSE4_1_GENERAL_H
diff --git a/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd4_float.h b/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd4_float.h
new file mode 100644 (file)
index 0000000..d214816
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_SSE4_1_SIMD4_FLOAT_H
+#define GMX_SIMD_IMPL_X86_SSE4_1_SIMD4_FLOAT_H
+
+#include "config.h"
+
+#include <smmintrin.h>
+
+#include "gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd4_float.h"
+
+namespace gmx
+{
+
+static inline Simd4Float gmx_simdcall round(Simd4Float x)
+{
+    return { _mm_round_ps(x.simdInternal_, _MM_FROUND_NINT) };
+}
+
+static inline Simd4Float gmx_simdcall trunc(Simd4Float x)
+{
+    return { _mm_round_ps(x.simdInternal_, _MM_FROUND_TRUNC) };
+}
+
+static inline float gmx_simdcall dotProduct(Simd4Float a, Simd4Float b)
+{
+    __m128 res = _mm_dp_ps(a.simdInternal_, b.simdInternal_, 0x71);
+    return *reinterpret_cast<float*>(&res);
+}
+
+static inline Simd4Float gmx_simdcall blend(Simd4Float a, Simd4Float b, Simd4FBool sel)
+{
+    return { _mm_blendv_ps(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_SSE4_1_SIMD4_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd_double.h b/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd_double.h
new file mode 100644 (file)
index 0000000..3adf63d
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_SSE4_1_SIMD_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_SSE4_1_SIMD_DOUBLE_H
+
+#include "config.h"
+
+#include <smmintrin.h>
+
+#include "gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd_double.h"
+
+namespace gmx
+{
+
+template<int index>
+static inline std::int32_t gmx_simdcall extract(SimdDInt32 a)
+{
+    return _mm_extract_epi32(a.simdInternal_, index);
+}
+
+static inline SimdDouble maskzRsqrt(SimdDouble x, SimdDBool m)
+{
+#ifndef NDEBUG
+    x.simdInternal_ = _mm_blendv_pd(_mm_set1_pd(1.0), x.simdInternal_, m.simdInternal_);
+#endif
+    return { _mm_and_pd(_mm_cvtps_pd(_mm_rsqrt_ps(_mm_cvtpd_ps(x.simdInternal_))), m.simdInternal_) };
+}
+
+static inline SimdDouble maskzRcp(SimdDouble x, SimdDBool m)
+{
+#ifndef NDEBUG
+    x.simdInternal_ = _mm_blendv_pd(_mm_set1_pd(1.0), x.simdInternal_, m.simdInternal_);
+#endif
+    return { _mm_and_pd(_mm_cvtps_pd(_mm_rcp_ps(_mm_cvtpd_ps(x.simdInternal_))), m.simdInternal_) };
+}
+
+static inline SimdDouble gmx_simdcall round(SimdDouble x)
+{
+    return { _mm_round_pd(x.simdInternal_, _MM_FROUND_NINT) };
+}
+
+static inline SimdDouble gmx_simdcall trunc(SimdDouble x)
+{
+    return { _mm_round_pd(x.simdInternal_, _MM_FROUND_TRUNC) };
+}
+
+static inline SimdDBool gmx_simdcall testBits(SimdDouble a)
+{
+    __m128i ia = _mm_castpd_si128(a.simdInternal_);
+    __m128i res = _mm_andnot_si128(_mm_cmpeq_epi64(ia, _mm_setzero_si128()), _mm_cmpeq_epi64(ia, ia));
+
+    return { _mm_castsi128_pd(res) };
+}
+
+static inline SimdDouble gmx_simdcall blend(SimdDouble a, SimdDouble b, SimdDBool sel)
+{
+    return { _mm_blendv_pd(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall operator*(SimdDInt32 a, SimdDInt32 b)
+{
+    return { _mm_mullo_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdDInt32 gmx_simdcall blend(SimdDInt32 a, SimdDInt32 b, SimdDIBool sel)
+{
+    return { _mm_blendv_epi8(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble ldexp(SimdDouble value, SimdDInt32 exponent)
+{
+    const __m128i exponentBias = _mm_set1_epi32(1023);
+    __m128i       iExponent    = _mm_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm_max_epi32(iExponent, _mm_setzero_si128());
+    }
+
+    // After conversion integers will be in slot 0,1. Move them to 0,2 so
+    // we can do a 64-bit shift and get them to the dp exponents.
+    iExponent = _mm_shuffle_epi32(iExponent, _MM_SHUFFLE(3, 1, 2, 0));
+    iExponent = _mm_slli_epi64(iExponent, 52);
+
+    return { _mm_mul_pd(value.simdInternal_, _mm_castsi128_pd(iExponent)) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_SSE4_1_SIMD_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd_float.h b/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_simd_float.h
new file mode 100644 (file)
index 0000000..6d42a44
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_SSE4_1_SIMD_FLOAT_H
+#define GMX_SIMD_IMPL_X86_SSE4_1_SIMD_FLOAT_H
+
+#include "config.h"
+
+#include <smmintrin.h>
+
+#include "gromacs/simd/impl_x86_sse2/impl_x86_sse2_simd_float.h"
+
+namespace gmx
+{
+
+template<int index>
+static inline std::int32_t gmx_simdcall extract(SimdFInt32 a)
+{
+    return _mm_extract_epi32(a.simdInternal_, index);
+}
+
+static inline SimdFloat maskzRsqrt(SimdFloat x, SimdFBool m)
+{
+#ifndef NDEBUG
+    x.simdInternal_ = _mm_blendv_ps(_mm_set1_ps(1.0F), x.simdInternal_, m.simdInternal_);
+#endif
+    return { _mm_and_ps(_mm_rsqrt_ps(x.simdInternal_), m.simdInternal_) };
+}
+
+static inline SimdFloat maskzRcp(SimdFloat x, SimdFBool m)
+{
+#ifndef NDEBUG
+    x.simdInternal_ = _mm_blendv_ps(_mm_set1_ps(1.0F), x.simdInternal_, m.simdInternal_);
+#endif
+    return { _mm_and_ps(_mm_rcp_ps(x.simdInternal_), m.simdInternal_) };
+}
+
+static inline SimdFloat gmx_simdcall round(SimdFloat x)
+{
+    return { _mm_round_ps(x.simdInternal_, _MM_FROUND_NINT) };
+}
+
+static inline SimdFloat gmx_simdcall trunc(SimdFloat x)
+{
+    return { _mm_round_ps(x.simdInternal_, _MM_FROUND_TRUNC) };
+}
+
+static inline SimdFloat gmx_simdcall blend(SimdFloat a, SimdFloat b, SimdFBool sel)
+{
+    return { _mm_blendv_ps(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall operator*(SimdFInt32 a, SimdFInt32 b)
+{
+    return { _mm_mullo_epi32(a.simdInternal_, b.simdInternal_) };
+}
+
+static inline SimdFInt32 gmx_simdcall blend(SimdFInt32 a, SimdFInt32 b, SimdFIBool sel)
+{
+    return { _mm_blendv_epi8(a.simdInternal_, b.simdInternal_, sel.simdInternal_) };
+}
+
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall ldexp(SimdFloat value, SimdFInt32 exponent)
+{
+    const __m128i exponentBias = _mm_set1_epi32(127);
+    __m128i       iExponent;
+
+    iExponent = _mm_add_epi32(exponent.simdInternal_, exponentBias);
+
+    if (opt == MathOptimization::Safe)
+    {
+        // Make sure biased argument is not negative
+        iExponent = _mm_max_epi32(iExponent, _mm_setzero_si128());
+    }
+
+    iExponent = _mm_slli_epi32(iExponent, 23);
+
+    return { _mm_mul_ps(value.simdInternal_, _mm_castsi128_ps(iExponent)) };
+}
+
+} // namespace gmx
+
+#endif // GMX_SIMD_IMPL_X86_SSE4_1_SIMD_FLOAT_H
diff --git a/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_util_double.h b/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_util_double.h
new file mode 100644 (file)
index 0000000..3f33217
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_SSE4_1_UTIL_DOUBLE_H
+#define GMX_SIMD_IMPL_X86_SSE4_1_UTIL_DOUBLE_H
+
+#include <smmintrin.h>
+
+#include "gromacs/simd/impl_x86_sse2/impl_x86_sse2_util_double.h"
+
+#endif // GMX_SIMD_IMPL_X86_SSE4_1_UTIL_DOUBLE_H
diff --git a/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_util_float.h b/src/include/gromacs/simd/impl_x86_sse4_1/impl_x86_sse4_1_util_float.h
new file mode 100644 (file)
index 0000000..f50d0e3
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_IMPL_X86_SSE4_1_UTIL_FLOAT_H
+#define GMX_SIMD_IMPL_X86_SSE4_1_UTIL_FLOAT_H
+
+#include <smmintrin.h>
+
+#include "gromacs/simd/impl_x86_sse2/impl_x86_sse2_util_float.h"
+
+#endif // GMX_SIMD_IMPL_X86_SSE4_1_UTIL_FLOAT_H
diff --git a/src/include/gromacs/simd/scalar/scalar.h b/src/include/gromacs/simd/scalar/scalar.h
new file mode 100644 (file)
index 0000000..9343602
--- /dev/null
@@ -0,0 +1,1023 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_SCALAR_H
+#define GMX_SIMD_SCALAR_H
+
+#include <cmath>
+#include <cstdint>
+#include <cstdlib>
+
+#include <algorithm>
+
+/*! \libinternal \file
+ *
+ * \brief Scalar float functions corresponding to GROMACS SIMD functions
+ *
+ * These versions make it possible to write functions that are templated with
+ * either a SIMD or scalar type. While some of these functions might not appear
+ * SIMD-specific, we have placed them here because the only reason to use these
+ * instead of generic function is in templated combined SIMD/non-SIMD code.
+ *
+ * There are a handful of limitations, in particular that it is impossible
+ * to overload the bitwise logical operators on built-in types.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_simd
+ */
+
+namespace gmx
+{
+
+/************************************************************************
+ *   Single-precision floating point functions mimicking SIMD versions  *
+ ************************************************************************/
+
+/*! \brief Store contents of float variable to aligned memory m.
+ *
+ * \param[out] m Pointer to memory.
+ * \param a float variable to store
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void store(float* m, float a)
+{
+    *m = a;
+}
+
+/*! \brief Store contents of float variable to unaligned memory m.
+ *
+ * \param[out] m Pointer to memory, no alignment requirement.
+ * \param a float variable to store.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void storeU(float* m, float a)
+{
+    *m = a;
+}
+
+// We cannot overload the logical operators and, or, andNot, xor for
+// built-in types.
+
+/*! \brief Float Fused-multiply-add. Result is a*b + c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return a*b + c
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float fma(float a, float b, float c)
+{
+    // Note that we purposely do not use the single-rounding std::fma
+    // as that can be very slow without hardware support
+    return a * b + c;
+}
+
+/*! \brief Float Fused-multiply-subtract. Result is a*b - c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return a*b - c
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float fms(float a, float b, float c)
+{
+    return a * b - c;
+}
+
+/*! \brief Float Fused-negated-multiply-add. Result is -a*b + c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return -a*b + c
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float fnma(float a, float b, float c)
+{
+    return c - a * b;
+}
+
+/*! \brief Float Fused-negated-multiply-subtract. Result is -a*b - c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return -a*b - c
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float fnms(float a, float b, float c)
+{
+    return -a * b - c;
+}
+
+/*! \brief Add two float variables, masked version.
+ *
+ * \param a term1
+ * \param b term2
+ * \param m mask
+ * \return a+b where mask is true, a otherwise.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float maskAdd(float a, float b, float m)
+{
+    return a + (m != 0.0F ? b : 0.0F);
+}
+
+/*! \brief Multiply two float variables, masked version.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param m mask
+ * \return a*b where mask is true, 0.0 otherwise.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float maskzMul(float a, float b, float m)
+{
+    return m != 0.0F ? (a * b) : 0.0F;
+}
+
+/*! \brief Float fused multiply-add, masked version.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \param m mask
+ * \return a*b+c where mask is true, 0.0 otherwise.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float maskzFma(float a, float b, float c, float m)
+{
+    return m != 0.0F ? (a * b + c) : 0.0F;
+}
+
+/*! \brief Float 1.0/x, masked version.
+ *
+ * \param x Argument, x>0 for entries where mask is true.
+ * \param m Mask
+ * \return 1/x. The result for masked-out entries will be 0.0.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float gmx_simdcall maskzRcp(float x, float m)
+{
+    return m != 0.0F ? 1.0F / x : 0.0F;
+}
+
+/*! \brief Float Floating-point abs().
+ *
+ * \param a any floating point values
+ * \return abs(a) for each element.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float abs(float a)
+{
+    return std::abs(a);
+}
+
+/*! \brief Set each float element to the largest from two variables.
+ *
+ * \param a Any floating-point value
+ * \param b Any floating-point value
+ * \return max(a,b) for each element.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float max(float a, float b)
+{
+    return std::max(a, b);
+}
+
+/*! \brief Set each float element to the smallest from two variables.
+ *
+ * \param a Any floating-point value
+ * \param b Any floating-point value
+ * \return min(a,b) for each element.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float min(float a, float b)
+{
+    return std::min(a, b);
+}
+
+/*! \brief Float round to nearest integer value (in floating-point format).
+ *
+ * \param a Any floating-point value
+ * \return The nearest integer, represented in floating-point format.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float round(float a)
+{
+    return std::round(a);
+}
+
+/*! \brief Truncate float, i.e. round towards zero - common hardware instruction.
+ *
+ * \param a Any floating-point value
+ * \return Integer rounded towards zero, represented in floating-point format.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float trunc(float a)
+{
+    return std::trunc(a);
+}
+
+/*! \brief Return sum of all elements in float variable (i.e., the variable itself).
+ *
+ * \param a variable to reduce/sum.
+ * \return The argument variable itself.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float reduce(float a)
+{
+    return a;
+}
+
+/*! \brief Bitwise andnot for two scalar float variables.
+ *
+ * \param a data1
+ * \param b data2
+ * \return (~data1) & data2
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float andNot(float a, float b)
+{
+    union
+    {
+        float         r;
+        std::uint32_t i;
+    } conv1, conv2;
+
+    conv1.r = a;
+    conv2.r = b;
+
+    conv1.i = (~conv1.i) & conv2.i;
+
+    return conv1.r;
+}
+
+/*! \brief Return true if any bits are set in the float variable.
+ *
+ * This function is used to handle bitmasks, mainly for exclusions in the
+ * inner kernels. Note that it will return true even for -0.0f (sign bit set),
+ * so it is not identical to not-equal.
+ *
+ * \param a value
+ * \return True if any bit in a is nonzero.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline bool testBits(float a)
+{
+    union
+    {
+        std::uint32_t i;
+        float         f;
+    } conv;
+
+    conv.f = a;
+    return (conv.i != 0);
+}
+
+/*! \brief Returns if the boolean is true.
+ *
+ * \param a Logical variable.
+ * \return true if a is true, otherwise false.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline bool anyTrue(bool a)
+{
+    return a;
+}
+
+/*! \brief Select from single precision variable where boolean is true.
+ *
+ * \param a Floating-point variable to select from
+ * \param mask Boolean selector
+ * \return  a is selected for true, 0 for false.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float selectByMask(float a, bool mask)
+{
+    return mask ? a : 0.0F;
+}
+
+/*! \brief Select from single precision variable where boolean is false.
+ *
+ * \param a Floating-point variable to select from
+ * \param mask Boolean selector
+ * \return  a is selected for false, 0 for true.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float selectByNotMask(float a, bool mask)
+{
+    return mask ? 0.0F : a;
+}
+
+/*! \brief Blend float selection.
+ *
+ * \param a First source
+ * \param b Second source
+ * \param sel Boolean selector
+ * \return Select b if sel is true, a otherwise.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float blend(float a, float b, bool sel)
+{
+    return sel ? b : a;
+}
+
+/*! \brief Round single precision floating point to integer.
+ *
+ * \param a float
+ * \return Integer format, a rounded to nearest integer.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline std::int32_t cvtR2I(float a)
+{
+    return static_cast<std::int32_t>(std::round(a));
+};
+
+/*! \brief Truncate single precision floating point to integer.
+ *
+ * \param a float
+ * \return Integer format, a truncated to integer.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline std::int32_t cvttR2I(float a)
+{
+    return static_cast<std::int32_t>(std::trunc(a));
+};
+
+/*! \brief Return integer.
+ *
+ * This function mimicks the SIMD integer-to-real conversion routines. By
+ * simply returning an integer, we let the compiler sort out whether the
+ * conversion should be to float or double rather than using proxy objects.
+ *
+ * \param a integer
+ * \return same value (a)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline std::int32_t cvtI2R(std::int32_t a)
+{
+    return a;
+}
+
+/************************************************************************
+ *   Double-precision floating point functions mimicking SIMD versions  *
+ ************************************************************************/
+
+/*! \brief Store contents of double variable to aligned memory m.
+ *
+ * \param[out] m Pointer to memory.
+ * \param a double variable to store
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void store(double* m, double a)
+{
+    *m = a;
+}
+
+/*! \brief Store contents of double variable to unaligned memory m.
+ *
+ * \param[out] m Pointer to memory, no alignment requirement.
+ * \param a double variable to store.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void storeU(double* m, double a)
+{
+    *m = a;
+}
+
+// We cannot overload the logical operators and, or, andNot, xor for
+// built-in types.
+
+/*! \brief double Fused-multiply-add. Result is a*b + c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return a*b + c
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double fma(double a, double b, double c)
+{
+    // Note that we purposely do not use the single-rounding std::fma
+    // as that can be very slow without hardware support
+    return a * b + c;
+}
+
+/*! \brief double Fused-multiply-subtract. Result is a*b - c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return a*b - c
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double fms(double a, double b, double c)
+{
+    return a * b - c;
+}
+
+/*! \brief double Fused-negated-multiply-add. Result is - a*b + c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return -a*b + c
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double fnma(double a, double b, double c)
+{
+    return c - a * b;
+}
+
+/*! \brief double Fused-negated-multiply-subtract. Result is -a*b - c.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \return -a*b - c
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double fnms(double a, double b, double c)
+{
+    return -a * b - c;
+}
+
+/*! \brief Add two double variables, masked version.
+ *
+ * \param a term1
+ * \param b term2
+ * \param m mask
+ * \return a+b where mask is true, a otherwise.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double maskAdd(double a, double b, double m)
+{
+    return a + (m != 0.0 ? b : 0.0);
+}
+
+/*! \brief Multiply two double variables, masked version.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param m mask
+ * \return a*b where mask is true, 0.0 otherwise.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double maskzMul(double a, double b, double m)
+{
+    return m != 0.0 ? (a * b) : 0.0;
+}
+
+/*! \brief double fused multiply-add, masked version.
+ *
+ * \param a factor1
+ * \param b factor2
+ * \param c term
+ * \param m mask
+ * \return a*b+c where mask is true, 0.0 otherwise.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double maskzFma(double a, double b, double c, double m)
+{
+    return m != 0.0 ? (a * b + c) : 0.0;
+}
+
+/*! \brief Double 1.0/x, masked version.
+ *
+ * \param x Argument, x>0 for entries where mask is true.
+ * \param m Mask
+ * \return Approximation of 1/x. The result for masked-out entries will be 0.0.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double gmx_simdcall maskzRcp(double x, double m)
+{
+    return m != 0.0 ? 1.0 / x : 0.0;
+}
+
+/*! \brief double doubleing-point abs().
+ *
+ * \param a any doubleing point values
+ * \return abs(a) for each element.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double abs(double a)
+{
+    return std::abs(a);
+}
+
+/*! \brief Set each double element to the largest from two variables.
+ *
+ * \param a Any doubleing-point value
+ * \param b Any doubleing-point value
+ * \return max(a,b) for each element.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double max(double a, double b)
+{
+    return std::max(a, b);
+}
+
+/*! \brief Set each double element to the smallest from two variables.
+ *
+ * \param a Any doubleing-point value
+ * \param b Any doubleing-point value
+ * \return min(a,b) for each element.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double min(double a, double b)
+{
+    return std::min(a, b);
+}
+
+/*! \brief double round to nearest integer value (in doubleing-point format).
+ *
+ * \param a Any doubleing-point value
+ * \return The nearest integer, represented in doubleing-point format.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double round(double a)
+{
+    return std::round(a);
+}
+
+/*! \brief Truncate double, i.e. round towards zero - common hardware instruction.
+ *
+ * \param a Any doubleing-point value
+ * \return Integer rounded towards zero, represented in doubleing-point format.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double trunc(double a)
+{
+    return std::trunc(a);
+}
+
+/*! \brief Return sum of all elements in double variable (i.e., the variable itself).
+ *
+ * \param a variable to reduce/sum.
+ * \return The argument variable itself.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double reduce(double a)
+{
+    return a;
+}
+
+/*! \brief Bitwise andnot for two scalar double variables.
+ *
+ * \param a data1
+ * \param b data2
+ * \return (~data1) & data2
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double andNot(double a, double b)
+{
+    union
+    {
+        double        r;
+        std::uint64_t i;
+    } conv1, conv2;
+
+    conv1.r = a;
+    conv2.r = b;
+
+    conv1.i = (~conv1.i) & conv2.i;
+
+    return conv1.r;
+}
+
+/*! \brief Return true if any bits are set in the double variable.
+ *
+ * This function is used to handle bitmasks, mainly for exclusions in the
+ * inner kernels. Note that it will return true even for -0.0 (sign bit set),
+ * so it is not identical to not-equal.
+ *
+ * \param a value
+ * \return True if any bit in a is nonzero.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline bool testBits(double a)
+{
+    union
+    {
+        std::uint64_t i;
+        double        f;
+    } conv;
+
+    conv.f = a;
+    return (conv.i != 0);
+}
+
+/*! \brief Select from double precision variable where boolean is true.
+ *
+ * \param a double variable to select from
+ * \param mask Boolean selector
+ * \return  a is selected for true, 0 for false.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double selectByMask(double a, bool mask)
+{
+    return mask ? a : 0.0;
+}
+
+/*! \brief Select from double precision variable where boolean is false.
+ *
+ * \param a double variable to select from
+ * \param mask Boolean selector
+ * \return  a is selected for false, 0 for true.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double selectByNotMask(double a, bool mask)
+{
+    return mask ? 0.0 : a;
+}
+
+/*! \brief Blend double selection.
+ *
+ * \param a First source
+ * \param b Second source
+ * \param sel Boolean selector
+ * \return Select b if sel is true, a otherwise.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double blend(double a, double b, bool sel)
+{
+    return sel ? b : a;
+}
+
+/*! \brief Round single precision doubleing point to integer.
+ *
+ * \param a double
+ * \return Integer format, a rounded to nearest integer.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline std::int32_t cvtR2I(double a)
+{
+    return static_cast<std::int32_t>(std::round(a));
+};
+
+/*! \brief Truncate single precision doubleing point to integer.
+ *
+ * \param a double
+ * \return Integer format, a truncated to integer.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline std::int32_t cvttR2I(double a)
+{
+    return static_cast<std::int32_t>(std::trunc(a));
+};
+
+// We do not have a separate cvtI2R for double, since that would require
+// proxy objects. Instead, the float version returns an integer and lets the
+// compiler sort out the conversion type.
+
+
+/*! \brief Convert float to double (mimicks SIMD conversion)
+ *
+ * \param a float
+ * \return a, as double double
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double cvtF2D(float a)
+{
+    return a;
+}
+
+/*! \brief Convert double to float (mimicks SIMD conversion)
+ *
+ * \param a double
+ * \return a, as float
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float cvtD2F(double a)
+{
+    return a;
+}
+
+/************************************************
+ *   Integer functions mimicking SIMD versions  *
+ ************************************************/
+
+/*! \brief Store contents of integer variable to aligned memory m.
+ *
+ * \param[out] m Pointer to memory.
+ * \param a integer variable to store
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void store(std::int32_t* m, std::int32_t a)
+{
+    *m = a;
+}
+
+/*! \brief Store contents of integer variable to unaligned memory m.
+ *
+ * \param[out] m Pointer to memory, no alignment requirement.
+ * \param a integer variable to store.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void storeU(std::int32_t* m, std::int32_t a)
+{
+    *m = a;
+}
+
+/*! \brief Bitwise andnot for two scalar integer variables.
+ *
+ * \param a data1
+ * \param b data2
+ * \return (~data1) & data2
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline std::int32_t andNot(std::int32_t a, std::int32_t b)
+{
+    return ~a & b;
+}
+
+/*! \brief Return true if any bits are set in the integer variable.
+ *
+ * This function is used to handle bitmasks, mainly for exclusions in the
+ * inner kernels.
+ *
+ * \param a value
+ * \return True if any bit in a is nonzero.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline bool testBits(std::int32_t a)
+{
+    return (a != 0);
+}
+
+/*! \brief Select from integer variable where boolean is true.
+ *
+ * \param a Integer variable to select from
+ * \param mask Boolean selector
+ * \return  a is selected for true, 0 for false.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline std::int32_t selectByMask(std::int32_t a, bool mask)
+{
+    return mask ? a : 0;
+}
+
+/*! \brief Select from integer variable where boolean is false.
+ *
+ * \param a Integer variable to select from
+ * \param mask Boolean selector
+ * \return  a is selected for false, 0 for true.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline std::int32_t selectByNotMask(std::int32_t a, bool mask)
+{
+    return mask ? 0 : a;
+}
+
+/*! \brief Blend integer selection.
+ *
+ * \param a First source
+ * \param b Second source
+ * \param sel Boolean selector
+ * \return Select b if sel is true, a otherwise.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline std::int32_t blend(std::int32_t a, std::int32_t b, bool sel)
+{
+    return sel ? b : a;
+}
+
+/*! \brief Just return a boolean (mimicks SIMD real-to-int bool conversions)
+ *
+ * \param a  boolean
+ * \return same boolean
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline bool cvtB2IB(bool a)
+{
+    return a;
+}
+
+/*! \brief Just return a boolean (mimicks SIMD int-to-real bool conversions)
+ *
+ * \param a  boolean
+ * \return same boolean
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline bool cvtIB2B(bool a)
+{
+    return a;
+}
+
+} // namespace gmx
+
+
+#endif // GMX_SIMD_SCALAR_FLOAT_H
diff --git a/src/include/gromacs/simd/scalar/scalar_math.h b/src/include/gromacs/simd/scalar/scalar_math.h
new file mode 100644 (file)
index 0000000..a523985
--- /dev/null
@@ -0,0 +1,1289 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_SCALAR_MATH_H
+#define GMX_SIMD_SCALAR_MATH_H
+
+#include <cmath>
+
+#include "gromacs/math/functions.h"
+#include "gromacs/math/utilities.h"
+#include "gromacs/simd/scalar/scalar.h"
+
+/*! \libinternal \file
+ *
+ * \brief Scalar math functions mimicking GROMACS SIMD math functions
+ *
+ * These versions make it possible to write functions that are templated with
+ * either a SIMD or scalar type. While some of these functions might not appear
+ * SIMD-specific, we have placed them here because the only reason to use these
+ * instead of generic function is in templated combined SIMD/non-SIMD code.
+ * It is important that these functions match the SIMD versions exactly in their
+ * arguments and template arguments so that overload resolution works correctly.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_simd
+ */
+
+namespace gmx
+{
+
+/*****************************************************************************
+ *   Single-precision floating point math functions mimicking SIMD versions  *
+ *****************************************************************************/
+
+
+/*! \brief Composes single value with the magnitude of x and the sign of y.
+ *
+ * \param x Value to set sign for
+ * \param y Value used to set sign
+ * \return  Magnitude of x, sign of y
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float copysign(float x, float y)
+{
+    return std::copysign(x, y);
+}
+
+// invsqrt(x) is already defined in math/functions.h
+
+/*! \brief Calculate 1/sqrt(x) for two floats.
+ *
+ * \param x0  First argument, x0 must be positive - no argument checking.
+ * \param x1  Second argument, x1 must be positive - no argument checking.
+ * \param[out] out0  Result 1/sqrt(x0)
+ * \param[out] out1  Result 1/sqrt(x1)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void invsqrtPair(float x0, float x1, float* out0, float* out1)
+{
+    *out0 = invsqrt(x0);
+    *out1 = invsqrt(x1);
+}
+
+/*! \brief Calculate 1/x for float.
+ *
+ *  \param x Argument that must be nonzero. This routine does not check arguments.
+ *  \return 1/x. Result is undefined if your argument was invalid.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float inv(float x)
+{
+    return 1.0F / x;
+}
+
+/*! \brief Calculate 1/sqrt(x) for masked entry of float.
+ *
+ *  This routine only evaluates 1/sqrt(x) if mask is true.
+ *  Illegal values for a masked-out float will not lead to
+ *  floating-point exceptions.
+ *
+ *  \param x Argument that must be >0 if masked-in.
+ *  \param m Mask
+ *  \return 1/sqrt(x). Result is undefined if your argument was invalid or
+ *          entry was not masked, and 0.0 for masked-out entries.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float maskzInvsqrt(float x, bool m)
+{
+    return m ? invsqrt(x) : 0.0F;
+}
+
+/*! \brief Calculate 1/x for masked entry of float.
+ *
+ *  This routine only evaluates 1/x if mask is true.
+ *  Illegal values for a masked-out float will not lead to
+ *  floating-point exceptions.
+ *
+ *  \param x Argument that must be nonzero if masked-in.
+ *  \param m Mask
+ *  \return 1/x. Result is undefined if your argument was invalid or
+ *          entry was not masked, and 0.0 for masked-out entries.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float maskzInv(float x, bool m)
+{
+    return m ? inv(x) : 0.0F;
+}
+
+/*! \brief Float sqrt(x). This is the square root.
+ *
+ * \param x Argument, should be >= 0.
+ * \result The square root of x. Undefined if argument is invalid.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline float sqrt(float x)
+{
+    return std::sqrt(x);
+}
+
+/*! \brief Float cbrt(x). This is the cubic root.
+ *
+ * \param x Argument, should be >= 0.
+ * \result The cubic root of x. Undefined if argument is invalid.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline float cbrt(float x)
+{
+    return std::cbrt(x);
+}
+
+/*! \brief Float log(x). This is the natural logarithm.
+ *
+ * \param x Argument, should be >0.
+ * \result The natural logarithm of x. Undefined if argument is invalid.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float log(float x)
+{
+    return std::log(x);
+}
+
+/*! \brief Float 2^x.
+ *
+ * \param x Argument.
+ * \result 2^x. Undefined if input argument caused overflow.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline float exp2(float x)
+{
+    return std::exp2(x);
+}
+
+/*! \brief Float exp(x).
+ *
+ * \param x Argument.
+ * \result exp(x). Undefined if input argument caused overflow.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline float exp(float x)
+{
+    return std::exp(x);
+}
+
+/*! \brief Float erf(x).
+ *
+ * \param x Argument.
+ * \result erf(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float erf(float x)
+{
+    return std::erf(x);
+}
+
+/*! \brief Float erfc(x).
+ *
+ * \param x Argument.
+ * \result erfc(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float erfc(float x)
+{
+    return std::erfc(x);
+}
+
+
+/*! \brief Float sin \& cos.
+ *
+ * \param x The argument to evaluate sin/cos for
+ * \param[out] sinval Sin(x)
+ * \param[out] cosval Cos(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void sincos(float x, float* sinval, float* cosval)
+{
+    *sinval = std::sin(x);
+    *cosval = std::cos(x);
+}
+
+/*! \brief Float sin.
+ *
+ * \param x The argument to evaluate sin for
+ * \result Sin(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float sin(float x)
+{
+    return std::sin(x);
+}
+
+/*! \brief Float cos.
+ *
+ * \param x The argument to evaluate cos for
+ * \result Cos(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float cos(float x)
+{
+    return std::cos(x);
+}
+
+/*! \brief Float tan.
+ *
+ * \param x The argument to evaluate tan for
+ * \result Tan(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float tan(float x)
+{
+    return std::tan(x);
+}
+
+/*! \brief float asin.
+ *
+ * \param x The argument to evaluate asin for
+ * \result Asin(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float asin(float x)
+{
+    return std::asin(x);
+}
+
+/*! \brief Float acos.
+ *
+ * \param x The argument to evaluate acos for
+ * \result Acos(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float acos(float x)
+{
+    return std::acos(x);
+}
+
+/*! \brief Float atan.
+ *
+ * \param x The argument to evaluate atan for
+ * \result Atan(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float atan(float x)
+{
+    return std::atan(x);
+}
+
+/*! \brief Float atan2(y,x).
+ *
+ * \param y Y component of vector, any quartile
+ * \param x X component of vector, any quartile
+ * \result Atan(y,x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float atan2(float y, float x)
+{
+    return std::atan2(y, x);
+}
+
+/*! \brief Calculate the force correction due to PME analytically in float.
+ *
+ * See the SIMD version of this function for details.
+ *
+ * \param z2 input parameter
+ * \returns Correction to use on force
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float pmeForceCorrection(float z2)
+{
+    const float FN6(-1.7357322914161492954e-8F);
+    const float FN5(1.4703624142580877519e-6F);
+    const float FN4(-0.000053401640219807709149F);
+    const float FN3(0.0010054721316683106153F);
+    const float FN2(-0.019278317264888380590F);
+    const float FN1(0.069670166153766424023F);
+    const float FN0(-0.75225204789749321333F);
+
+    const float FD4(0.0011193462567257629232F);
+    const float FD3(0.014866955030185295499F);
+    const float FD2(0.11583842382862377919F);
+    const float FD1(0.50736591960530292870F);
+    const float FD0(1.0F);
+
+    float z4;
+    float polyFN0, polyFN1, polyFD0, polyFD1;
+
+    z4 = z2 * z2;
+
+    polyFD0 = fma(FD4, z4, FD2);
+    polyFD1 = fma(FD3, z4, FD1);
+    polyFD0 = fma(polyFD0, z4, FD0);
+    polyFD0 = fma(polyFD1, z2, polyFD0);
+
+    polyFN0 = fma(FN6, z4, FN4);
+    polyFN1 = fma(FN5, z4, FN3);
+    polyFN0 = fma(polyFN0, z4, FN2);
+    polyFN1 = fma(polyFN1, z4, FN1);
+    polyFN0 = fma(polyFN0, z4, FN0);
+    polyFN0 = fma(polyFN1, z2, polyFN0);
+
+    return polyFN0 / polyFD0;
+}
+
+/*! \brief Calculate the potential correction due to PME analytically in float.
+ *
+ * See the SIMD version of this function for details.
+ *
+ * \param z2 input parameter
+ * \returns Correction to use on potential.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float pmePotentialCorrection(float z2)
+{
+    const float VN6(1.9296833005951166339e-8F);
+    const float VN5(-1.4213390571557850962e-6F);
+    const float VN4(0.000041603292906656984871F);
+    const float VN3(-0.00013134036773265025626F);
+    const float VN2(0.038657983986041781264F);
+    const float VN1(0.11285044772717598220F);
+    const float VN0(1.1283802385263030286F);
+
+    const float VD3(0.0066752224023576045451F);
+    const float VD2(0.078647795836373922256F);
+    const float VD1(0.43336185284710920150F);
+    const float VD0(1.0F);
+
+    float z4;
+    float polyVN0, polyVN1, polyVD0, polyVD1;
+
+    z4 = z2 * z2;
+
+    polyVD1 = fma(VD3, z4, VD1);
+    polyVD0 = fma(VD2, z4, VD0);
+    polyVD0 = fma(polyVD1, z2, polyVD0);
+
+    polyVN0 = fma(VN6, z4, VN4);
+    polyVN1 = fma(VN5, z4, VN3);
+    polyVN0 = fma(polyVN0, z4, VN2);
+    polyVN1 = fma(polyVN1, z4, VN1);
+    polyVN0 = fma(polyVN0, z4, VN0);
+    polyVN0 = fma(polyVN1, z2, polyVN0);
+
+    return polyVN0 / polyVD0;
+}
+
+/*****************************************************************************
+ *   Double-precision floating point math functions mimicking SIMD versions  *
+ *****************************************************************************/
+
+
+/*! \brief Composes double value with the magnitude of x and the sign of y.
+ *
+ * \param x Value to set sign for
+ * \param y Value used to set sign
+ * \return  Magnitude of x, sign of y
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double copysign(double x, double y)
+{
+    return std::copysign(x, y);
+}
+
+// invsqrt(x) is already defined in math/functions.h
+
+/*! \brief Calculate 1/sqrt(x) for two doubles.
+ *
+ * \param x0  First argument, x0 must be positive - no argument checking.
+ * \param x1  Second argument, x1 must be positive - no argument checking.
+ * \param[out] out0  Result 1/sqrt(x0)
+ * \param[out] out1  Result 1/sqrt(x1)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void invsqrtPair(double x0, double x1, double* out0, double* out1)
+{
+    *out0 = invsqrt(x0);
+    *out1 = invsqrt(x1);
+}
+
+/*! \brief Calculate 1/x for double.
+ *
+ *  \param x Argument that must be nonzero. This routine does not check arguments.
+ *  \return 1/x. Result is undefined if your argument was invalid.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double inv(double x)
+{
+    return 1.0 / x;
+}
+
+/*! \brief Calculate 1/sqrt(x) for masked entry of double.
+ *
+ *  This routine only evaluates 1/sqrt(x) if mask is true.
+ *  Illegal values for a masked-out double will not lead to
+ *  floating-point exceptions.
+ *
+ *  \param x Argument that must be >0 if masked-in.
+ *  \param m Mask
+ *  \return 1/sqrt(x). Result is undefined if your argument was invalid or
+ *          entry was not masked, and 0.0 for masked-out entries.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double maskzInvsqrt(double x, bool m)
+{
+    return m ? invsqrt(x) : 0.0;
+}
+
+/*! \brief Calculate 1/x for masked entry of double.
+ *
+ *  This routine only evaluates 1/x if mask is true.
+ *  Illegal values for a masked-out double will not lead to
+ *  floating-point exceptions.
+ *
+ *  \param x Argument that must be nonzero if masked-in.
+ *  \param m Mask
+ *  \return 1/x. Result is undefined if your argument was invalid or
+ *          entry was not masked, and 0.0 for masked-out entries.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double maskzInv(double x, bool m)
+{
+    return m ? inv(x) : 0.0;
+}
+
+/*! \brief Double sqrt(x). This is the square root.
+ *
+ * \param x Argument, should be >= 0.
+ * \result The square root of x. Undefined if argument is invalid.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline double sqrt(double x)
+{
+    return std::sqrt(x);
+}
+
+/*! \brief Double cbrt(x). This is the cubic root.
+ *
+ * \param x Argument, should be >= 0.
+ * \result The cubic root of x. Undefined if argument is invalid.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline double cbrt(double x)
+{
+    return std::cbrt(x);
+}
+
+/*! \brief Double log(x). This is the natural logarithm.
+ *
+ * \param x Argument, should be >0.
+ * \result The natural logarithm of x. Undefined if argument is invalid.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double log(double x)
+{
+    return std::log(x);
+}
+
+/*! \brief Double 2^x.
+ *
+ * \param x Argument.
+ * \result 2^x. Undefined if input argument caused overflow.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline double exp2(double x)
+{
+    return std::exp2(x);
+}
+
+/*! \brief Double exp(x).
+ *
+ * \param x Argument.
+ * \result exp(x). Undefined if input argument caused overflow.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline double exp(double x)
+{
+    return std::exp(x);
+}
+
+/*! \brief Double erf(x).
+ *
+ * \param x Argument.
+ * \result erf(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double erf(double x)
+{
+    return std::erf(x);
+}
+
+/*! \brief Double erfc(x).
+ *
+ * \param x Argument.
+ * \result erfc(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double erfc(double x)
+{
+    return std::erfc(x);
+}
+
+
+/*! \brief Double sin \& cos.
+ *
+ * \param x The argument to evaluate sin/cos for
+ * \param[out] sinval Sin(x)
+ * \param[out] cosval Cos(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void sincos(double x, double* sinval, double* cosval)
+{
+    *sinval = std::sin(x);
+    *cosval = std::cos(x);
+}
+
+/*! \brief Double sin.
+ *
+ * \param x The argument to evaluate sin for
+ * \result Sin(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double sin(double x)
+{
+    return std::sin(x);
+}
+
+/*! \brief Double cos.
+ *
+ * \param x The argument to evaluate cos for
+ * \result Cos(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double cos(double x)
+{
+    return std::cos(x);
+}
+
+/*! \brief Double tan.
+ *
+ * \param x The argument to evaluate tan for
+ * \result Tan(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double tan(double x)
+{
+    return std::tan(x);
+}
+
+/*! \brief Double asin.
+ *
+ * \param x The argument to evaluate asin for
+ * \result Asin(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double asin(double x)
+{
+    return std::asin(x);
+}
+
+/*! \brief Double acos.
+ *
+ * \param x The argument to evaluate acos for
+ * \result Acos(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double acos(double x)
+{
+    return std::acos(x);
+}
+
+/*! \brief Double atan.
+ *
+ * \param x The argument to evaluate atan for
+ * \result Atan(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double atan(double x)
+{
+    return std::atan(x);
+}
+
+/*! \brief Double atan2(y,x).
+ *
+ * \param y Y component of vector, any quartile
+ * \param x X component of vector, any quartile
+ * \result Atan(y,x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double atan2(double y, double x)
+{
+    return std::atan2(y, x);
+}
+
+/*! \brief Calculate the force correction due to PME analytically in double.
+ *
+ * See the SIMD version of this function for details.
+ *
+ * \param z2 input parameter
+ * \returns Correction to use on force
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double pmeForceCorrection(double z2)
+{
+    const double FN10(-8.0072854618360083154e-14);
+    const double FN9(1.1859116242260148027e-11);
+    const double FN8(-8.1490406329798423616e-10);
+    const double FN7(3.4404793543907847655e-8);
+    const double FN6(-9.9471420832602741006e-7);
+    const double FN5(0.000020740315999115847456);
+    const double FN4(-0.00031991745139313364005);
+    const double FN3(0.0035074449373659008203);
+    const double FN2(-0.031750380176100813405);
+    const double FN1(0.13884101728898463426);
+    const double FN0(-0.75225277815249618847);
+
+    const double FD5(0.000016009278224355026701);
+    const double FD4(0.00051055686934806966046);
+    const double FD3(0.0081803507497974289008);
+    const double FD2(0.077181146026670287235);
+    const double FD1(0.41543303143712535988);
+    const double FD0(1.0);
+
+    double z4;
+    double polyFN0, polyFN1, polyFD0, polyFD1;
+
+    z4 = z2 * z2;
+
+    polyFD1 = fma(FD5, z4, FD3);
+    polyFD1 = fma(polyFD1, z4, FD1);
+    polyFD1 = polyFD1 * z2;
+    polyFD0 = fma(FD4, z4, FD2);
+    polyFD0 = fma(polyFD0, z4, FD0);
+    polyFD0 = polyFD0 + polyFD1;
+
+    polyFD0 = inv(polyFD0);
+
+    polyFN0 = fma(FN10, z4, FN8);
+    polyFN0 = fma(polyFN0, z4, FN6);
+    polyFN0 = fma(polyFN0, z4, FN4);
+    polyFN0 = fma(polyFN0, z4, FN2);
+    polyFN0 = fma(polyFN0, z4, FN0);
+    polyFN1 = fma(FN9, z4, FN7);
+    polyFN1 = fma(polyFN1, z4, FN5);
+    polyFN1 = fma(polyFN1, z4, FN3);
+    polyFN1 = fma(polyFN1, z4, FN1);
+    polyFN0 = fma(polyFN1, z2, polyFN0);
+
+    return polyFN0 * polyFD0;
+}
+
+/*! \brief Calculate the potential correction due to PME analytically in double.
+ *
+ * See the SIMD version of this function for details.
+ *
+ * \param z2 input parameter
+ * \returns Correction to use on potential.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double pmePotentialCorrection(double z2)
+{
+    const double VN9(-9.3723776169321855475e-13);
+    const double VN8(1.2280156762674215741e-10);
+    const double VN7(-7.3562157912251309487e-9);
+    const double VN6(2.6215886208032517509e-7);
+    const double VN5(-4.9532491651265819499e-6);
+    const double VN4(0.00025907400778966060389);
+    const double VN3(0.0010585044856156469792);
+    const double VN2(0.045247661136833092885);
+    const double VN1(0.11643931522926034421);
+    const double VN0(1.1283791671726767970);
+
+    const double VD5(0.000021784709867336150342);
+    const double VD4(0.00064293662010911388448);
+    const double VD3(0.0096311444822588683504);
+    const double VD2(0.085608012351550627051);
+    const double VD1(0.43652499166614811084);
+    const double VD0(1.0);
+
+    double z4;
+    double polyVN0, polyVN1, polyVD0, polyVD1;
+
+    z4 = z2 * z2;
+
+    polyVD1 = fma(VD5, z4, VD3);
+    polyVD0 = fma(VD4, z4, VD2);
+    polyVD1 = fma(polyVD1, z4, VD1);
+    polyVD0 = fma(polyVD0, z4, VD0);
+    polyVD0 = fma(polyVD1, z2, polyVD0);
+
+    polyVD0 = inv(polyVD0);
+
+    polyVN1 = fma(VN9, z4, VN7);
+    polyVN0 = fma(VN8, z4, VN6);
+    polyVN1 = fma(polyVN1, z4, VN5);
+    polyVN0 = fma(polyVN0, z4, VN4);
+    polyVN1 = fma(polyVN1, z4, VN3);
+    polyVN0 = fma(polyVN0, z4, VN2);
+    polyVN1 = fma(polyVN1, z4, VN1);
+    polyVN0 = fma(polyVN0, z4, VN0);
+    polyVN0 = fma(polyVN1, z2, polyVN0);
+
+    return polyVN0 * polyVD0;
+}
+
+
+/*****************************************************************************
+ *           Floating point math functions mimicking SIMD versions.          *
+ *              Double precision data, single precision accuracy.            *
+ *****************************************************************************/
+
+
+/*! \brief Calculate 1/sqrt(x) for double, but with single accuracy.
+ *
+ *  \param x Argument that must be >0. This routine does not check arguments.
+ *  \return 1/sqrt(x). Result is undefined if your argument was invalid.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double invsqrtSingleAccuracy(double x)
+{
+    return invsqrt(static_cast<float>(x));
+}
+
+/*! \brief Calculate 1/sqrt(x) for two doubles, but with single accuracy.
+ *
+ * \param x0  First argument, x0 must be positive - no argument checking.
+ * \param x1  Second argument, x1 must be positive - no argument checking.
+ * \param[out] out0  Result 1/sqrt(x0)
+ * \param[out] out1  Result 1/sqrt(x1)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void invsqrtPairSingleAccuracy(double x0, double x1, double* out0, double* out1)
+{
+    *out0 = invsqrt(static_cast<float>(x0));
+    *out1 = invsqrt(static_cast<float>(x1));
+}
+
+/*! \brief Calculate 1/x for double, but with single accuracy.
+ *
+ *  \param x Argument that must be nonzero. This routine does not check arguments.
+ *  \return 1/x. Result is undefined if your argument was invalid.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double invSingleAccuracy(double x)
+{
+    return 1.0F / x;
+}
+
+/*! \brief Calculate 1/sqrt(x) for masked entry of double, but with single accuracy.
+ *
+ *  This routine only evaluates 1/sqrt(x) if mask is true.
+ *  Illegal values for a masked-out double will not lead to
+ *  floating-point exceptions.
+ *
+ *  \param x Argument that must be >0 if masked-in.
+ *  \param m Mask
+ *  \return 1/sqrt(x). Result is undefined if your argument was invalid or
+ *          entry was not masked, and 0.0 for masked-out entries.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double maskzInvsqrtSingleAccuracy(double x, bool m)
+{
+    return m ? invsqrtSingleAccuracy(x) : 0.0;
+}
+
+/*! \brief Calculate 1/x for masked entry of double, but with single accuracy.
+ *
+ *  This routine only evaluates 1/x if mask is true.
+ *  Illegal values for a masked-out double will not lead to
+ *  floating-point exceptions.
+ *
+ *  \param x Argument that must be nonzero if masked-in.
+ *  \param m Mask
+ *  \return 1/x. Result is undefined if your argument was invalid or
+ *          entry was not masked, and 0.0 for masked-out entries.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double maskzInvSingleAccuracy(double x, bool m)
+{
+    return m ? invSingleAccuracy(x) : 0.0;
+}
+
+/*! \brief Calculate sqrt(x) for double, but with single accuracy.
+ *
+ *  \param x Argument that must be >=0.
+ *  \return sqrt(x).
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double sqrtSingleAccuracy(double x)
+{
+    return std::sqrt(static_cast<float>(x));
+}
+
+/*! \brief Double log(x), but with single accuracy. This is the natural logarithm.
+ *
+ * \param x Argument, should be >0.
+ * \result The natural logarithm of x. Undefined if argument is invalid.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double logSingleAccuracy(double x)
+{
+    return std::log(static_cast<float>(x));
+}
+
+/*! \brief Double 2^x, but with single accuracy.
+ *
+ * \param x Argument.
+ * \result 2^x. Undefined if input argument caused overflow.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double exp2SingleAccuracy(double x)
+{
+    return std::exp2(static_cast<float>(x));
+}
+
+/*! \brief Double exp(x), but with single accuracy.
+ *
+ * \param x Argument.
+ * \result exp(x). Undefined if input argument caused overflow.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double expSingleAccuracy(double x)
+{
+    return std::exp(static_cast<float>(x));
+}
+
+/*! \brief Double erf(x), but with single accuracy.
+ *
+ * \param x Argument.
+ * \result erf(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double erfSingleAccuracy(double x)
+{
+    return std::erf(static_cast<float>(x));
+}
+
+/*! \brief Double erfc(x), but with single accuracy.
+ *
+ * \param x Argument.
+ * \result erfc(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double erfcSingleAccuracy(double x)
+{
+    return std::erfc(static_cast<float>(x));
+}
+
+
+/*! \brief Double sin \& cos, but with single accuracy.
+ *
+ * \param x The argument to evaluate sin/cos for
+ * \param[out] sinval Sin(x)
+ * \param[out] cosval Cos(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void sincosSingleAccuracy(double x, double* sinval, double* cosval)
+{
+    // There is no single-precision sincos guaranteed in C++11, so use
+    // separate functions and hope the compiler optimizes it for us.
+    *sinval = std::sin(static_cast<float>(x));
+    *cosval = std::cos(static_cast<float>(x));
+}
+
+/*! \brief Double sin, but with single accuracy.
+ *
+ * \param x The argument to evaluate sin for
+ * \result Sin(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double sinSingleAccuracy(double x)
+{
+    return std::sin(static_cast<float>(x));
+}
+
+/*! \brief Double cos, but with single accuracy.
+ *
+ * \param x The argument to evaluate cos for
+ * \result Cos(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double cosSingleAccuracy(double x)
+{
+    return std::cos(static_cast<float>(x));
+}
+
+/*! \brief Double tan, but with single accuracy.
+ *
+ * \param x The argument to evaluate tan for
+ * \result Tan(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double tanSingleAccuracy(double x)
+{
+    return std::tan(static_cast<float>(x));
+}
+
+/*! \brief Double asin, but with single accuracy.
+ *
+ * \param x The argument to evaluate asin for
+ * \result Asin(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double asinSingleAccuracy(double x)
+{
+    return std::asin(static_cast<float>(x));
+}
+
+/*! \brief Double acos, but with single accuracy.
+ *
+ * \param x The argument to evaluate acos for
+ * \result Acos(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double acosSingleAccuracy(double x)
+{
+    return std::acos(static_cast<float>(x));
+}
+
+/*! \brief Double atan, but with single accuracy.
+ *
+ * \param x The argument to evaluate atan for
+ * \result Atan(x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double atanSingleAccuracy(double x)
+{
+    return std::atan(static_cast<float>(x));
+}
+
+/*! \brief Double atan2(y,x), but with single accuracy.
+ *
+ * \param y Y component of vector, any quartile
+ * \param x X component of vector, any quartile
+ * \result Atan(y,x)
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double atan2SingleAccuracy(double y, double x)
+{
+    return std::atan2(static_cast<float>(y), static_cast<float>(x));
+}
+
+/*! \brief Force correction due to PME in double, but with single accuracy.
+ *
+ * See the SIMD version of this function for details.
+ *
+ * \param z2 input parameter
+ * \returns Correction to use on force
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double pmeForceCorrectionSingleAccuracy(double z2)
+{
+    const float FN6(-1.7357322914161492954e-8F);
+    const float FN5(1.4703624142580877519e-6F);
+    const float FN4(-0.000053401640219807709149F);
+    const float FN3(0.0010054721316683106153F);
+    const float FN2(-0.019278317264888380590F);
+    const float FN1(0.069670166153766424023F);
+    const float FN0(-0.75225204789749321333F);
+
+    const float FD4(0.0011193462567257629232F);
+    const float FD3(0.014866955030185295499F);
+    const float FD2(0.11583842382862377919F);
+    const float FD1(0.50736591960530292870F);
+    const float FD0(1.0F);
+
+    float z4;
+    float polyFN0, polyFN1, polyFD0, polyFD1;
+
+    float z2f = z2;
+
+    z4 = z2f * z2f;
+
+    polyFD0 = fma(FD4, z4, FD2);
+    polyFD1 = fma(FD3, z4, FD1);
+    polyFD0 = fma(polyFD0, z4, FD0);
+    polyFD0 = fma(polyFD1, z2f, polyFD0);
+
+    polyFN0 = fma(FN6, z4, FN4);
+    polyFN1 = fma(FN5, z4, FN3);
+    polyFN0 = fma(polyFN0, z4, FN2);
+    polyFN1 = fma(polyFN1, z4, FN1);
+    polyFN0 = fma(polyFN0, z4, FN0);
+    polyFN0 = fma(polyFN1, z2f, polyFN0);
+
+    return polyFN0 / polyFD0;
+}
+
+/*! \brief Potential correction due to PME in double, but with single accuracy.
+ *
+ * See the SIMD version of this function for details.
+ *
+ * \param z2 input parameter
+ * \returns Correction to use on potential.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double pmePotentialCorrectionSingleAccuracy(double z2)
+{
+    const float VN6(1.9296833005951166339e-8F);
+    const float VN5(-1.4213390571557850962e-6F);
+    const float VN4(0.000041603292906656984871F);
+    const float VN3(-0.00013134036773265025626F);
+    const float VN2(0.038657983986041781264F);
+    const float VN1(0.11285044772717598220F);
+    const float VN0(1.1283802385263030286F);
+
+    const float VD3(0.0066752224023576045451F);
+    const float VD2(0.078647795836373922256F);
+    const float VD1(0.43336185284710920150F);
+    const float VD0(1.0F);
+
+    float z4;
+    float polyVN0, polyVN1, polyVD0, polyVD1;
+
+    float z2f = z2;
+
+    z4 = z2f * z2f;
+
+    polyVD1 = fma(VD3, z4, VD1);
+    polyVD0 = fma(VD2, z4, VD0);
+    polyVD0 = fma(polyVD1, z2f, polyVD0);
+
+    polyVN0 = fma(VN6, z4, VN4);
+    polyVN1 = fma(VN5, z4, VN3);
+    polyVN0 = fma(polyVN0, z4, VN2);
+    polyVN1 = fma(polyVN1, z4, VN1);
+    polyVN0 = fma(polyVN0, z4, VN0);
+    polyVN0 = fma(polyVN1, z2f, polyVN0);
+
+    return polyVN0 / polyVD0;
+}
+
+
+} // namespace gmx
+
+
+#endif // GMX_SIMD_SCALAR_MATH_H
diff --git a/src/include/gromacs/simd/scalar/scalar_util.h b/src/include/gromacs/simd/scalar/scalar_util.h
new file mode 100644 (file)
index 0000000..5002726
--- /dev/null
@@ -0,0 +1,558 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_SCALAR_UTIL_H
+#define GMX_SIMD_SCALAR_UTIL_H
+
+#include <cmath>
+
+/*! \libinternal \file
+ *
+ * \brief Scalar utility functions mimicking GROMACS SIMD utility functions
+ *
+ * These versions make it possible to write functions that are templated with
+ * either a SIMD or scalar type. While some of these functions might not appear
+ * SIMD-specific, we have placed them here because the only reason to use these
+ * instead of generic function is in templated combined SIMD/non-SIMD code.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_simd
+ */
+
+namespace gmx
+{
+
+/*****************************************************************************
+ *   Single-precision utility load/store functions mimicking SIMD versions   *
+ *****************************************************************************/
+
+/*! \brief Load 4 consecutive floats from base/offset into four variables
+ *
+ * \tparam     align  Alignment of the memory from which we read.
+ * \param      base   Pointer to the start of the memory area
+ * \param      offset Index to data.
+ * \param[out] v0     1st float, base[align*offset[0]].
+ * \param[out] v1     2nd float, base[align*offset[0] + 1].
+ * \param[out] v2     3rd float, base[align*offset[0] + 2].
+ * \param[out] v3     4th float, base[align*offset[0] + 3].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void
+gatherLoadTranspose(const float* base, const std::int32_t offset[], float* v0, float* v1, float* v2, float* v3)
+{
+    *v0 = base[align * offset[0]];
+    *v1 = base[align * offset[0] + 1];
+    *v2 = base[align * offset[0] + 2];
+    *v3 = base[align * offset[0] + 3];
+}
+
+/*! \brief Load 2 consecutive floats from base/offset into four variables
+ *
+ * \tparam     align  Alignment of the memory from which we read.
+ * \param      base   Pointer to the start of the memory area
+ * \param      offset Index to data.
+ * \param[out] v0     1st float, base[align*offset[0]].
+ * \param[out] v1     2nd float, base[align*offset[0] + 1].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void gatherLoadTranspose(const float* base, const std::int32_t offset[], float* v0, float* v1)
+{
+    *v0 = base[align * offset[0]];
+    *v1 = base[align * offset[0] + 1];
+}
+
+
+/*! \brief Load 3 consecutive floats from base/offsets, store into three vars.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param      base   Pointer to the start of the memory area
+ * \param      offset Offset to the start of data.
+ * \param[out] v0     1st value, base[align*offset[0]].
+ * \param[out] v1     2nd value, base[align*offset[0] + 1].
+ * \param[out] v2     3rd value, base[align*offset[0] + 2].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void
+gatherLoadUTranspose(const float* base, const std::int32_t offset[], float* v0, float* v1, float* v2)
+{
+    *v0 = base[align * offset[0]];
+    *v1 = base[align * offset[0] + 1];
+    *v2 = base[align * offset[0] + 2];
+}
+
+/*! \brief Store 3 floats to 3 to base/offset.
+ *
+ * \tparam     align  Alignment of the memory to which we write, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param[out] base   Pointer to the start of the memory area
+ * \param      offset Offset to the start of triplet.
+ * \param      v0     1st value, written to base[align*offset[0]].
+ * \param      v1     2nd value, written to base[align*offset[0] + 1].
+ * \param      v2     3rd value, written to base[align*offset[0] + 2].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void transposeScatterStoreU(float* base, const std::int32_t offset[], float v0, float v1, float v2)
+{
+    base[align * offset[0]]     = v0;
+    base[align * offset[0] + 1] = v1;
+    base[align * offset[0] + 2] = v2;
+}
+
+/*! \brief Add 3 floats to base/offset.
+ *
+ * \tparam     align  Alignment of the memory to which we write, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param[out] base   Pointer to the start of the memory area
+ * \param      offset Offset to the start of triplet.
+ * \param      v0     1st value, added to base[align*offset[0]].
+ * \param      v1     2nd value, added to base[align*offset[0] + 1].
+ * \param      v2     3rd value, added to base[align*offset[0] + 2].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void transposeScatterIncrU(float* base, const std::int32_t offset[], float v0, float v1, float v2)
+{
+    base[align * offset[0]] += v0;
+    base[align * offset[0] + 1] += v1;
+    base[align * offset[0] + 2] += v2;
+}
+
+/*! \brief Subtract 3 floats from base/offset.
+ *
+ * \tparam     align  Alignment of the memory to which we write, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param[out] base   Pointer to the start of the memory area
+ * \param      offset Offset to the start of triplet.
+ * \param      v0     1st value, subtracted from base[align*offset[0]].
+ * \param      v1     2nd value, subtracted from base[align*offset[0] + 1].
+ * \param      v2     3rd value, subtracted from base[align*offset[0] + 2].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void transposeScatterDecrU(float* base, const std::int32_t offset[], float v0, float v1, float v2)
+{
+    base[align * offset[0]] -= v0;
+    base[align * offset[0] + 1] -= v1;
+    base[align * offset[0] + 2] -= v2;
+}
+
+/*! \brief Copy single float to three variables.
+ *
+ * \param      scalar    Floating-point input.
+ * \param[out] triplets0 Copy 1.
+ * \param[out] triplets1 Copy 2.
+ * \param[out] triplets2 Copy 3.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void expandScalarsToTriplets(float scalar, float* triplets0, float* triplets1, float* triplets2)
+{
+    *triplets0 = scalar;
+    *triplets1 = scalar;
+    *triplets2 = scalar;
+}
+
+/*! \brief Load 4 floats from base/offsets and store into variables.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param      base   Aligned pointer to the start of the memory.
+ * \param      offset Integer type with offset to the start of each triplet.
+ * \param[out] v0     First float, base[align*offset[0]].
+ * \param[out] v1     Second float, base[align*offset[0] + 1].
+ * \param[out] v2     Third float, base[align*offset[0] + 2].
+ * \param[out] v3     Fourth float, base[align*offset[0] + 3].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void
+gatherLoadBySimdIntTranspose(const float* base, std::int32_t offset, float* v0, float* v1, float* v2, float* v3)
+{
+    *v0 = base[align * offset];
+    *v1 = base[align * offset + 1];
+    *v2 = base[align * offset + 2];
+    *v3 = base[align * offset + 3];
+}
+
+/*! \brief Load 2 floats from base/offsets and store into variables (unaligned).
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param      base   Aligned pointer to the start of the memory.
+ * \param      offset Integer type with offset to the start of each triplet.
+ * \param[out] v0     First float, base[align*offset[0]].
+ * \param[out] v1     Second float, base[align*offset[0] + 1].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void gatherLoadUBySimdIntTranspose(const float* base, std::int32_t offset, float* v0, float* v1)
+{
+    *v0 = base[align * offset];
+    *v1 = base[align * offset + 1];
+}
+
+/*! \brief Load 2 floats from base/offsets and store into variables (aligned).
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param      base   Aligned pointer to the start of the memory.
+ * \param      offset Integer type with offset to the start of each triplet.
+ * \param[out] v0     First float, base[align*offset[0]].
+ * \param[out] v1     Second float, base[align*offset[0] + 1].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void gatherLoadBySimdIntTranspose(const float* base, std::int32_t offset, float* v0, float* v1)
+{
+    *v0 = base[align * offset];
+    *v1 = base[align * offset + 1];
+}
+
+/*! \brief Add each float to four consecutive memory locations, return sum.
+ *
+ * \param m   Pointer to memory where four floats should be incremented
+ * \param v0  float to be added to m[0]
+ * \param v1  float to be added to m[1]
+ * \param v2  float to be added to m[2]
+ * \param v3  float to be added to m[3]
+ *
+ * \return v0+v1+v2+v3.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline float reduceIncr4ReturnSum(float* m, float v0, float v1, float v2, float v3)
+{
+    m[0] += v0;
+    m[1] += v1;
+    m[2] += v2;
+    m[3] += v3;
+
+    return v0 + v1 + v2 + v3;
+}
+
+
+/*****************************************************************************
+ *   Double-precision utility load/store functions mimicking SIMD versions   *
+ *****************************************************************************/
+
+/*! \brief Load 4 consecutive doubles from base/offset into four variables
+ *
+ * \tparam     align  Alignment of the memory from which we read.
+ * \param      base   Pointer to the start of the memory area
+ * \param      offset Index to data.
+ * \param[out] v0     1st double, base[align*offset[0]].
+ * \param[out] v1     2nd double, base[align*offset[0] + 1].
+ * \param[out] v2     3rd double, base[align*offset[0] + 2].
+ * \param[out] v3     4th double, base[align*offset[0] + 3].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void gatherLoadTranspose(const double*      base,
+                                       const std::int32_t offset[],
+                                       double*            v0,
+                                       double*            v1,
+                                       double*            v2,
+                                       double*            v3)
+{
+    *v0 = base[align * offset[0]];
+    *v1 = base[align * offset[0] + 1];
+    *v2 = base[align * offset[0] + 2];
+    *v3 = base[align * offset[0] + 3];
+}
+
+/*! \brief Load 2 consecutive doubles from base/offset into four variables
+ *
+ * \tparam     align  Alignment of the memory from which we read.
+ * \param      base   Pointer to the start of the memory area
+ * \param      offset Index to data.
+ * \param[out] v0     1st double, base[align*offset[0]].
+ * \param[out] v1     2nd double, base[align*offset[0] + 1].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void gatherLoadTranspose(const double* base, const std::int32_t offset[], double* v0, double* v1)
+{
+    *v0 = base[align * offset[0]];
+    *v1 = base[align * offset[0] + 1];
+}
+
+
+/*! \brief Load 3 consecutive doubles from base/offsets, store into three vars.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param      base   Pointer to the start of the memory area
+ * \param      offset Offset to the start of data.
+ * \param[out] v0     1st double, base[align*offset[0]].
+ * \param[out] v1     2nd double, base[align*offset[0] + 1].
+ * \param[out] v2     3rd double, base[align*offset[0] + 2].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void
+gatherLoadUTranspose(const double* base, const std::int32_t offset[], double* v0, double* v1, double* v2)
+{
+    *v0 = base[align * offset[0]];
+    *v1 = base[align * offset[0] + 1];
+    *v2 = base[align * offset[0] + 2];
+}
+
+/*! \brief Store 3 doubles to 3 to base/offset.
+ *
+ * \tparam     align  Alignment of the memory to which we write, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param[out] base   Pointer to the start of the memory area
+ * \param      offset Offset to the start of triplet.
+ * \param      v0     1st value, written to base[align*offset[0]].
+ * \param      v1     2nd value, written to base[align*offset[0] + 1].
+ * \param      v2     3rd value, written to base[align*offset[0] + 2].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void transposeScatterStoreU(double* base, const std::int32_t offset[], double v0, double v1, double v2)
+{
+    base[align * offset[0]]     = v0;
+    base[align * offset[0] + 1] = v1;
+    base[align * offset[0] + 2] = v2;
+}
+
+/*! \brief Add 3 doubles to base/offset.
+ *
+ * \tparam     align  Alignment of the memory to which we write, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param[out] base   Pointer to the start of the memory area
+ * \param      offset Offset to the start of triplet.
+ * \param      v0     1st value, added to base[align*offset[0]].
+ * \param      v1     2nd value, added to base[align*offset[0] + 1].
+ * \param      v2     3rd value, added to base[align*offset[0] + 2].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void transposeScatterIncrU(double* base, const std::int32_t offset[], double v0, double v1, double v2)
+{
+    base[align * offset[0]] += v0;
+    base[align * offset[0] + 1] += v1;
+    base[align * offset[0] + 2] += v2;
+}
+
+/*! \brief Subtract 3 doubles from base/offset.
+ *
+ * \tparam     align  Alignment of the memory to which we write, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param[out] base   Pointer to the start of the memory area
+ * \param      offset Offset to the start of triplet.
+ * \param      v0     1st value, subtracted from base[align*offset[0]].
+ * \param      v1     2nd value, subtracted from base[align*offset[0] + 1].
+ * \param      v2     3rd value, subtracted from base[align*offset[0] + 2].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void transposeScatterDecrU(double* base, const std::int32_t offset[], double v0, double v1, double v2)
+{
+    base[align * offset[0]] -= v0;
+    base[align * offset[0] + 1] -= v1;
+    base[align * offset[0] + 2] -= v2;
+}
+
+/*! \brief Copy single double to three variables.
+ *
+ * \param      scalar    Floating-point input.
+ * \param[out] triplets0 Copy 1.
+ * \param[out] triplets1 Copy 2.
+ * \param[out] triplets2 Copy 3.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline void expandScalarsToTriplets(double scalar, double* triplets0, double* triplets1, double* triplets2)
+{
+    *triplets0 = scalar;
+    *triplets1 = scalar;
+    *triplets2 = scalar;
+}
+
+/*! \brief Load 4 doubles from base/offsets and store into variables.
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param      base   Aligned pointer to the start of the memory.
+ * \param      offset Integer type with offset to the start of each triplet.
+ * \param[out] v0     First double, base[align*offset[0]].
+ * \param[out] v1     Second double, base[align*offset[0] + 1].
+ * \param[out] v2     Third double, base[align*offset[0] + 2].
+ * \param[out] v3     Fourth double, base[align*offset[0] + 3].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void gatherLoadBySimdIntTranspose(const double* base,
+                                                std::int32_t  offset,
+                                                double*       v0,
+                                                double*       v1,
+                                                double*       v2,
+                                                double*       v3)
+{
+    *v0 = base[align * offset];
+    *v1 = base[align * offset + 1];
+    *v2 = base[align * offset + 2];
+    *v3 = base[align * offset + 3];
+}
+
+/*! \brief Load 2 doubles from base/offsets and store into variables (unaligned).
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param      base   Aligned pointer to the start of the memory.
+ * \param      offset Integer type with offset to the start of each triplet.
+ * \param[out] v0     First double, base[align*offset[0]].
+ * \param[out] v1     Second double, base[align*offset[0] + 1].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void gatherLoadUBySimdIntTranspose(const double* base, std::int32_t offset, double* v0, double* v1)
+{
+    *v0 = base[align * offset];
+    *v1 = base[align * offset + 1];
+}
+
+/*! \brief Load 2 doubles from base/offsets and store into variables (aligned).
+ *
+ * \tparam     align  Alignment of the memory from which we read, i.e. distance
+ *                    (measured in elements, not bytes) between index points.
+ * \param      base   Aligned pointer to the start of the memory.
+ * \param      offset Integer type with offset to the start of each triplet.
+ * \param[out] v0     First double, base[align*offset[0]].
+ * \param[out] v1     Second double, base[align*offset[0] + 1].
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+template<int align>
+static inline void gatherLoadBySimdIntTranspose(const double* base, std::int32_t offset, double* v0, double* v1)
+{
+    *v0 = base[align * offset];
+    *v1 = base[align * offset + 1];
+}
+
+/*! \brief Add each double to four consecutive memory locations, return sum.
+ *
+ * \param m   Pointer to memory where four floats should be incremented
+ * \param v0  double to be added to m[0]
+ * \param v1  double to be added to m[1]
+ * \param v2  double to be added to m[2]
+ * \param v3  double to be added to m[3]
+ *
+ * \return v0+v1+v2+v3.
+ *
+ * \note This function might be superficially meaningless, but it helps us to
+ *       write templated SIMD/non-SIMD code. For clarity it should not be used
+ *       outside such code.
+ */
+static inline double reduceIncr4ReturnSum(double* m, double v0, double v1, double v2, double v3)
+{
+    m[0] += v0;
+    m[1] += v1;
+    m[2] += v2;
+    m[3] += v3;
+
+    return v0 + v1 + v2 + v3;
+}
+
+} // namespace gmx
+
+
+#endif // GMX_SIMD_SCALAR_UTIL_H
diff --git a/src/include/gromacs/simd/simd.h b/src/include/gromacs/simd/simd.h
new file mode 100644 (file)
index 0000000..5f98fcd
--- /dev/null
@@ -0,0 +1,819 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal
+ * \defgroup module_simd SIMD intrinsics interface (simd)
+ * \ingroup group_utilitymodules
+ *
+ * \brief Provides an architecture-independent way of doing SIMD coding.
+ *
+ * Overview of the SIMD implementation is provided in \ref page_simd.
+ * The details are documented in gromacs/simd/simd.h and the reference
+ * implementation impl_reference.h.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ */
+
+#ifndef GMX_SIMD_SIMD_H
+#define GMX_SIMD_SIMD_H
+
+/*! \libinternal \file
+ *
+ * \brief Definitions, capabilities, and wrappers for SIMD module.
+ *
+ * The macros in this file are intended to be used for writing
+ * architecture-independent SIMD intrinsics code.
+ * To support a new architecture, adding a new sub-include with macros here
+ * should be (nearly) all that is needed.
+ *
+ * The defines in this top-level file will set default Gromacs real precision
+ * operations to either single or double precision based on whether
+ * GMX_DOUBLE is 1. The actual implementation - including e.g.
+ * conversion operations specifically between single and double - is documented
+ * in impl_reference.h.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \inlibraryapi
+ * \ingroup module_simd
+ */
+
+#include "config.h"
+
+#include <cstddef>
+#include <cstdint>
+
+#include <array>
+#include <memory>
+#include <type_traits>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+//! \cond libapi
+
+
+/*! \addtogroup module_simd
+ * \{
+ */
+
+namespace gmx
+{
+/*! \libinternal \brief Tag type to select to load SimdFloat with simdLoad(U) */
+struct SimdFloatTag
+{
+};
+/*! \libinternal \brief Tag type to select to load SimdDouble with simdLoad(U) */
+struct SimdDoubleTag
+{
+};
+/*! \libinternal \brief Tag type to select to load SimdFInt32 with simdLoad(U) */
+struct SimdFInt32Tag
+{
+};
+/*! \libinternal \brief Tag type to select to load SimdDInt32 with simdLoad(U) */
+struct SimdDInt32Tag
+{
+};
+} // namespace gmx
+
+/*! \name SIMD predefined macros to describe high-level capabilities
+ *
+ *  These macros are used to describe the features available in default
+ *  Gromacs real precision. They are set from the lower-level implementation
+ *  files that have macros describing single and double precision individually,
+ *  as well as the implementation details.
+ *  \{
+ */
+
+#ifdef __clang__
+#    pragma clang diagnostic push
+/* reinterpret_cast is used for SIMD->scalar conversion
+ *
+ * In general using reinterpret_cast for bit_cast is UB but
+ * for intrinsics types it works for all known compilers
+ * and not all compilers produce as good code for memcpy.
+ */
+#    pragma clang diagnostic ignored "-Wundefined-reinterpret-cast"
+#endif
+
+#if GMX_SIMD_X86_SSE2
+#    include "impl_x86_sse2/impl_x86_sse2.h"
+#elif GMX_SIMD_X86_SSE4_1
+#    include "impl_x86_sse4_1/impl_x86_sse4_1.h"
+#elif GMX_SIMD_X86_AVX_128_FMA
+#    include "impl_x86_avx_128_fma/impl_x86_avx_128_fma.h"
+#elif GMX_SIMD_X86_AVX_256
+#    include "impl_x86_avx_256/impl_x86_avx_256.h"
+#elif GMX_SIMD_X86_AVX2_256
+#    include "impl_x86_avx2_256/impl_x86_avx2_256.h"
+#elif GMX_SIMD_X86_AVX2_128
+#    include "impl_x86_avx2_128/impl_x86_avx2_128.h"
+#elif GMX_SIMD_X86_AVX_512
+#    include "impl_x86_avx_512/impl_x86_avx_512.h"
+#elif GMX_SIMD_X86_AVX_512_KNL
+#    include "impl_x86_avx_512_knl/impl_x86_avx_512_knl.h"
+#elif GMX_SIMD_ARM_NEON_ASIMD
+#    include "impl_arm_neon_asimd/impl_arm_neon_asimd.h"
+#elif GMX_SIMD_ARM_SVE
+#    include "impl_arm_sve/impl_arm_sve.h"
+#elif GMX_SIMD_IBM_VSX
+#    include "impl_ibm_vsx/impl_ibm_vsx.h"
+#elif (GMX_SIMD_REFERENCE || defined DOXYGEN)
+#    include "impl_reference/impl_reference.h" // Includes doxygen documentation
+#else
+#    include "impl_none/impl_none.h"
+#endif
+
+#ifdef __clang__
+#    pragma clang diagnostic pop
+#endif
+
+// The scalar SIMD-mimicking functions are always included so we can use
+// templated functions even without SIMD support.
+#include "gromacs/simd/scalar/scalar.h"
+#include "gromacs/simd/scalar/scalar_math.h"
+#include "gromacs/simd/scalar/scalar_util.h"
+
+
+#if GMX_DOUBLE
+#    define GMX_SIMD_HAVE_REAL GMX_SIMD_HAVE_DOUBLE
+#    define GMX_SIMD_REAL_WIDTH GMX_SIMD_DOUBLE_WIDTH
+#    define GMX_SIMD_HAVE_INT32_EXTRACT GMX_SIMD_HAVE_DINT32_EXTRACT
+#    define GMX_SIMD_HAVE_INT32_LOGICAL GMX_SIMD_HAVE_DINT32_LOGICAL
+#    define GMX_SIMD_HAVE_INT32_ARITHMETICS GMX_SIMD_HAVE_DINT32_ARITHMETICS
+#    define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_REAL \
+        GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE
+#    define GMX_SIMD_HAVE_HSIMD_UTIL_REAL GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE
+#    define GMX_SIMD4_HAVE_REAL GMX_SIMD4_HAVE_DOUBLE
+#else // GMX_DOUBLE
+
+/*! \brief 1 if SimdReal is available, otherwise 0.
+ *
+ *  \ref GMX_SIMD_HAVE_DOUBLE if GMX_DOUBLE is 1, otherwise \ref GMX_SIMD_HAVE_FLOAT.
+ */
+#    define GMX_SIMD_HAVE_REAL GMX_SIMD_HAVE_FLOAT
+
+/*! \brief Width of SimdReal.
+ *
+ *  \ref GMX_SIMD_DOUBLE_WIDTH if GMX_DOUBLE is 1, otherwise \ref GMX_SIMD_FLOAT_WIDTH.
+ */
+#    define GMX_SIMD_REAL_WIDTH GMX_SIMD_FLOAT_WIDTH
+
+/*! \brief 1 if support is available for extracting elements from SimdInt32, otherwise 0
+ *
+ *  \ref GMX_SIMD_HAVE_DINT32_EXTRACT if GMX_DOUBLE is 1, otherwise
+ *  \ref GMX_SIMD_HAVE_FINT32_EXTRACT.
+ */
+#    define GMX_SIMD_HAVE_INT32_EXTRACT GMX_SIMD_HAVE_FINT32_EXTRACT
+
+/*! \brief 1 if logical ops are supported on SimdInt32, otherwise 0.
+ *
+ *  \ref GMX_SIMD_HAVE_DINT32_LOGICAL if GMX_DOUBLE is 1, otherwise
+ *  \ref GMX_SIMD_HAVE_FINT32_LOGICAL.
+ */
+#    define GMX_SIMD_HAVE_INT32_LOGICAL GMX_SIMD_HAVE_FINT32_LOGICAL
+
+/*! \brief 1 if arithmetic ops are supported on SimdInt32, otherwise 0.
+ *
+ *  \ref GMX_SIMD_HAVE_DINT32_ARITHMETICS if GMX_DOUBLE is 1, otherwise
+ *  \ref GMX_SIMD_HAVE_FINT32_ARITHMETICS.
+ */
+#    define GMX_SIMD_HAVE_INT32_ARITHMETICS GMX_SIMD_HAVE_FINT32_ARITHMETICS
+
+/*! \brief 1 if gmx::simdGatherLoadUBySimdIntTranspose is present, otherwise 0
+ *
+ *  \ref GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_DOUBLE if GMX_DOUBLE is 1, otherwise
+ *  \ref GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT.
+ */
+#    define GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_REAL \
+        GMX_SIMD_HAVE_GATHER_LOADU_BYSIMDINT_TRANSPOSE_FLOAT
+
+/*! \brief 1 if real half-register load/store/reduce utils present, otherwise 0
+ *
+ *  \ref GMX_SIMD_HAVE_HSIMD_UTIL_DOUBLE if GMX_DOUBLE is 1, otherwise
+ *  \ref GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT.
+ */
+#    define GMX_SIMD_HAVE_HSIMD_UTIL_REAL GMX_SIMD_HAVE_HSIMD_UTIL_FLOAT
+
+/*! \brief 1 if Simd4Real is available, otherwise 0.
+ *
+ *  \ref GMX_SIMD4_HAVE_DOUBLE if GMX_DOUBLE is 1, otherwise \ref GMX_SIMD4_HAVE_FLOAT.
+ */
+#    define GMX_SIMD4_HAVE_REAL GMX_SIMD4_HAVE_FLOAT
+
+#endif // GMX_DOUBLE
+
+//! \}  end of name-group describing high-level capabilities
+
+namespace gmx
+{
+
+template<class T, size_t N>
+struct AlignedArray;
+
+#if GMX_SIMD_HAVE_FLOAT
+/*! \libinternal \brief Identical to std::array with GMX_SIMD_FLOAT_WIDTH alignment.
+ *  Should not be deleted through base pointer (destructor is non-virtual).
+ */
+template<size_t N>
+struct alignas(GMX_SIMD_FLOAT_WIDTH * sizeof(float)) AlignedArray<float, N> :
+    public std::array<float, N>
+{
+};
+#endif
+
+#if GMX_SIMD_HAVE_DOUBLE
+/*! \libinternal \brief  Identical to std::array with GMX_SIMD_DOUBLE_WIDTH alignment.
+ *  Should not be deleted through base pointer (destructor is non-virtual).
+ */
+template<size_t N>
+struct alignas(GMX_SIMD_DOUBLE_WIDTH * sizeof(double)) AlignedArray<double, N> :
+    public std::array<double, N>
+{
+};
+#endif
+
+#if GMX_SIMD_HAVE_REAL
+
+/*! \name SIMD data types
+ *
+ *  The actual storage of these types is implementation dependent. The
+ *  documentation is generated from the reference implementation, but for
+ *  normal usage this will likely not be what you are using.
+ * \{
+ */
+
+/*! \brief Real precision floating-point SIMD datatype.
+ *
+ * This type is only available if \ref GMX_SIMD_HAVE_REAL is 1.
+ *
+ * \ref SimdDouble if GMX_DOUBLE is 1, otherwise \ref SimdFloat.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+#    if GMX_DOUBLE
+typedef SimdDouble SimdReal;
+#    else
+typedef SimdFloat  SimdReal;
+#    endif
+
+
+/*! \brief Boolean SIMD type for usage with \ref SimdReal.
+ *
+ * This type is only available if \ref GMX_SIMD_HAVE_REAL is 1.
+ *
+ * If GMX_DOUBLE is 1, this will be set to \ref SimdDBool
+ * internally, otherwise \ref SimdFBool. This is necessary since some
+ * SIMD implementations use bitpatterns for marking truth, so single-
+ * vs. double precision booleans are not necessarily exchangable.
+ * As long as you just use this type you will not have to worry about precision.
+ *
+ * See \ref SimdIBool for an explanation of real vs. integer booleans.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+#    if GMX_DOUBLE
+typedef SimdDBool SimdBool;
+#    else
+typedef SimdFBool  SimdBool;
+#    endif
+
+
+/*! \brief 32-bit integer SIMD type.
+ *
+ * If GMX_DOUBLE is 1, this will be set to \ref SimdDInt32
+ * internally, otherwise \ref SimdFInt32. This might seem a strange
+ * implementation detail, but it is because some SIMD implementations use
+ * different types/widths of integers registers when converting from
+ * double vs. single precision floating point. As long as you just use
+ * this type you will not have to worry about precision.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+#    if GMX_DOUBLE
+typedef SimdDInt32 SimdInt32;
+#    else
+typedef SimdFInt32 SimdInt32;
+#    endif
+
+#    if GMX_SIMD_HAVE_INT32_ARITHMETICS
+/*! \brief Boolean SIMD type for usage with \ref SimdInt32.
+ *
+ * This type is only available if \ref GMX_SIMD_HAVE_INT32_ARITHMETICS is 1.
+ *
+ * If GMX_DOUBLE is 1, this will be set to \ref SimdDIBool
+ * internally, otherwise \ref SimdFIBool. This is necessary since some
+ * SIMD implementations use bitpatterns for marking truth, so single-
+ * vs. double precision booleans are not necessarily exchangable, and while
+ * a double-precision boolean might be represented with a 64-bit mask, the
+ * corresponding integer might only use a 32-bit mask.
+ *
+ * We provide conversion routines for these cases, so the only thing you need to
+ * keep in mind is to use \ref SimdBool when working with
+ * \ref SimdReal while you pick \ref SimdIBool when working with
+ * \ref SimdInt32 .
+ *
+ * To convert between them, use \ref cvtB2IB and \ref cvtIB2B.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+#        if GMX_DOUBLE
+typedef SimdDIBool SimdIBool;
+#        else
+typedef SimdFIBool SimdIBool;
+#        endif
+#    endif // GMX_SIMD_HAVE_INT32_ARITHMETICS
+
+
+#    if GMX_DOUBLE
+const int c_simdBestPairAlignment = c_simdBestPairAlignmentDouble;
+#    else
+const int          c_simdBestPairAlignment = c_simdBestPairAlignmentFloat;
+#    endif
+
+#endif // GMX_SIMD_HAVE_REAL
+
+#if GMX_SIMD4_HAVE_REAL
+/*! \brief Real precision floating-point SIMD4 datatype.
+ *
+ * This type is only available if \ref GMX_SIMD4_HAVE_REAL is 1.
+ *
+ * \ref Simd4Double if GMX_DOUBLE is 1, otherwise \ref Simd4Float.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+#    if GMX_DOUBLE
+typedef Simd4Double Simd4Real;
+#    else
+typedef Simd4Float Simd4Real;
+#    endif
+
+
+/*! \brief Boolean SIMD4 type for usage with \ref SimdReal.
+ *
+ * This type is only available if \ref GMX_SIMD4_HAVE_REAL is 1.
+ *
+ * If GMX_DOUBLE is 1, this will be set to \ref Simd4DBool
+ * internally, otherwise \ref Simd4FBool. This is necessary since some
+ * SIMD implementations use bitpatterns for marking truth, so single-
+ * vs. double precision booleans are not necessarily exchangable.
+ * As long as you just use this type you will not have to worry about precision.
+ *
+ * \note This variable cannot be placed inside other structures or classes, since
+ *       some compilers (including at least clang-3.7) appear to lose the
+ *       alignment. This is likely particularly severe when allocating such
+ *       memory on the heap, but it occurs for stack structures too.
+ */
+#    if GMX_DOUBLE
+typedef Simd4DBool Simd4Bool;
+#    else
+typedef Simd4FBool Simd4Bool;
+#    endif
+#endif // GMX_SIMD4_HAVE_REAL
+
+//! \}  end of name-group describing SIMD data types
+
+/*! \name High-level SIMD proxy objects to disambiguate load/set operations
+ * \{
+ */
+
+namespace internal
+{
+/*! \libinternal \brief Simd traits
+ *
+ * These traits are used to query data about SIMD types. Currently provided
+ * data is useful for SIMD loads (load function and helper classes for
+ * ArrayRef<> in simd_memory.h). Provided data:
+ *  - type: scalar type corresponding to the SIMD type
+ *  - width: SIMD width
+ *  - tag: tag used for type dispatch of load function
+ */
+template<typename T>
+struct SimdTraits
+{
+};
+
+#if GMX_SIMD_HAVE_FLOAT
+template<>
+struct SimdTraits<SimdFloat>
+{
+    using type                 = float;
+    static constexpr int width = GMX_SIMD_FLOAT_WIDTH;
+    using tag                  = SimdFloatTag;
+};
+#endif
+#if GMX_SIMD_HAVE_DOUBLE
+template<>
+struct SimdTraits<SimdDouble>
+{
+    using type                 = double;
+    static constexpr int width = GMX_SIMD_DOUBLE_WIDTH;
+    using tag                  = SimdDoubleTag;
+};
+#endif
+#if GMX_SIMD_HAVE_FLOAT
+template<>
+struct SimdTraits<SimdFInt32>
+{
+    using type                 = int;
+    static constexpr int width = GMX_SIMD_FINT32_WIDTH;
+    using tag                  = SimdFInt32Tag;
+};
+#endif
+#if GMX_SIMD_HAVE_DOUBLE
+template<>
+struct SimdTraits<SimdDInt32>
+{
+    using type                 = int;
+    static constexpr int width = GMX_SIMD_DINT32_WIDTH;
+    using tag                  = SimdDInt32Tag;
+};
+#endif
+template<typename T>
+using SimdTraitsT = typename SimdTraits<T>::type;
+template<typename T>
+struct SimdTraits<const T>
+{
+    using type                 = const SimdTraitsT<T>;
+    static constexpr int width = SimdTraits<T>::width;
+    using tag                  = typename SimdTraits<T>::tag;
+};
+} // namespace internal
+
+/*! \brief Load function that returns SIMD or scalar
+ *
+ * Note that a load of T* where T is const returns a value, which is a
+ * copy, and the caller cannot be constrained to not change it, so the
+ * return type uses std::remove_const_t.
+ *
+ * \tparam T Type to load (type is always mandatory)
+ * \param  m Pointer to aligned memory
+ * \return   Loaded value
+ */
+template<typename T>
+static inline std::remove_const_t<T> load(const internal::SimdTraitsT<T>* m) // disabled by SFINAE for non-SIMD types
+{
+    return simdLoad(m, typename internal::SimdTraits<T>::tag());
+}
+
+template<typename T>
+static inline T
+/* the enable_if serves to prevent two different type of misuse:
+ * 1) load<SimdReal>(SimdReal*); should only be called on real* or int*
+ * 2) load(real*); template parameter is mandatory because otherwise ambiguity is
+ *    created. The dependent type disables type deduction.
+ */
+load(const std::enable_if_t<std::is_arithmetic_v<T>, T> *m)
+{
+    return *m;
+}
+
+template<typename T, size_t N>
+static inline T gmx_simdcall load(const AlignedArray<internal::SimdTraitsT<T>, N>& m)
+{
+    return simdLoad(m.data(), typename internal::SimdTraits<T>::tag());
+}
+
+/*! \brief Load function that returns SIMD or scalar based on template argument
+ *
+ * \tparam T Type to load (type is always mandatory)
+ * \param m Pointer to unaligned memory
+ * \return Loaded SimdFloat/Double/Int or basic scalar type
+ */
+template<typename T>
+static inline T loadU(const internal::SimdTraitsT<T>* m)
+{
+    return simdLoadU(m, typename internal::SimdTraits<T>::tag());
+}
+
+template<typename T>
+static inline T loadU(const std::enable_if_t<std::is_arithmetic_v<T>, T>* m)
+{
+    return *m;
+}
+
+template<typename T, size_t N>
+static inline T gmx_simdcall loadU(const AlignedArray<internal::SimdTraitsT<T>, N>& m)
+{
+    return simdLoadU(m.data(), typename internal::SimdTraits<T>::tag());
+}
+
+/*! \libinternal \brief Proxy object to enable setZero() for SIMD and real types.
+ *
+ * This object is returned by setZero(), and depending on what type you assign
+ * the result to the conversion method will call the right low-level function.
+ */
+class SimdSetZeroProxy
+{
+public:
+    //!\brief Conversion method that returns 0.0 as float
+    operator float() const { return 0.0F; }
+    //!\brief Conversion method that returns 0.0 as double
+    operator double() const { return 0.0; }
+    //!\brief Conversion method that returns 0.0 as int32
+    operator std::int32_t() const { return 0; }
+#if GMX_SIMD_HAVE_FLOAT
+    //!\brief Conversion method that will execute setZero() for SimdFloat
+    operator SimdFloat() const { return setZeroF(); }
+    //!\brief Conversion method that will execute setZero() for SimdFInt32
+    operator SimdFInt32() const { return setZeroFI(); }
+#endif
+#if GMX_SIMD4_HAVE_FLOAT
+    //!\brief Conversion method that will execute setZero() for Simd4Float
+    operator Simd4Float() const { return simd4SetZeroF(); }
+#endif
+#if GMX_SIMD_HAVE_DOUBLE
+    //!\brief Conversion method that will execute setZero() for SimdDouble
+    operator SimdDouble() const { return setZeroD(); }
+    //!\brief Conversion method that will execute setZero() for SimdDInt32
+    operator SimdDInt32() const { return setZeroDI(); }
+#endif
+#if GMX_SIMD4_HAVE_DOUBLE
+    //!\brief Conversion method that will execute setZero() for Simd4Double
+    operator Simd4Double() const { return simd4SetZeroD(); }
+#endif
+};
+
+/*! \brief Helper function to set any SIMD or scalar variable to zero
+ *
+ * \return Proxy object that will call the actual function to set a SIMD/scalar
+ *         variable to zero based on the conversion function called when you
+ *         assign the result.
+ */
+static inline SimdSetZeroProxy gmx_simdcall setZero()
+{
+    return {};
+}
+
+namespace internal
+{
+// TODO: Don't forward function but properly rename them and use proper traits
+template<typename T>
+struct Simd4Traits
+{
+};
+
+#if GMX_SIMD4_HAVE_FLOAT
+template<>
+struct Simd4Traits<Simd4Float>
+{
+    using type = float;
+};
+#endif
+
+#if GMX_SIMD4_HAVE_DOUBLE
+template<>
+struct Simd4Traits<Simd4Double>
+{
+    using type = double;
+};
+#endif
+template<typename T>
+using Simd4TraitsT = typename Simd4Traits<T>::type;
+} // namespace internal
+
+#if GMX_SIMD4_HAVE_REAL
+template<typename T>
+T load(const internal::Simd4TraitsT<T>* m)
+{
+    return load4(m);
+}
+template<typename T>
+T loadU(const internal::Simd4TraitsT<T>* m)
+{
+    return load4U(m);
+}
+#endif
+
+/* Implement most of 4xn functions by forwarding them to other functions when possible.
+ * The functions forwarded here don't need to be implemented by each implementation.
+ * For width=4 all functions are forwarded and for width=8 all but loadU4NOffset are forwarded.
+ */
+#if GMX_SIMD_HAVE_FLOAT
+#    if GMX_SIMD_FLOAT_WIDTH < 4
+#        define GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT (GMX_SIMD_HAVE_LOADU && GMX_SIMD4_HAVE_FLOAT)
+#    elif GMX_SIMD_FLOAT_WIDTH == 4
+#        define GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT GMX_SIMD_HAVE_LOADU
+// For GMX_SIMD_FLOAT_WIDTH>4 it is the reponsibility of the implementation to set
+// GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT
+#    endif
+
+#    if GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT
+#        if GMX_SIMD_FLOAT_WIDTH < 4
+using Simd4NFloat = Simd4Float;
+#            define GMX_SIMD4N_FLOAT_WIDTH 4
+#        else
+using Simd4NFloat = SimdFloat;
+#            define GMX_SIMD4N_FLOAT_WIDTH GMX_SIMD_FLOAT_WIDTH
+#        endif
+
+#        if GMX_SIMD_FLOAT_WIDTH <= 4
+static inline Simd4NFloat gmx_simdcall loadUNDuplicate4(const float* f)
+{
+    return Simd4NFloat(*f);
+}
+static inline Simd4NFloat gmx_simdcall load4DuplicateN(const float* f)
+{
+    return load<Simd4NFloat>(f);
+}
+static inline Simd4NFloat gmx_simdcall loadU4NOffset(const float* f, int)
+{
+    return loadU<Simd4NFloat>(f);
+}
+#        elif GMX_SIMD_FLOAT_WIDTH == 8
+static inline Simd4NFloat gmx_simdcall loadUNDuplicate4(const float* f)
+{
+    return loadU1DualHsimd(f);
+}
+static inline Simd4NFloat gmx_simdcall load4DuplicateN(const float* f)
+{
+    return loadDuplicateHsimd(f);
+}
+#        endif
+#    endif // GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT
+#else      // GMX_SIMD_HAVE_FLOAT
+#    define GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT 0
+#endif
+
+#if GMX_SIMD_HAVE_DOUBLE
+#    if GMX_SIMD_DOUBLE_WIDTH < 4
+#        define GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE (GMX_SIMD_HAVE_LOADU && GMX_SIMD4_HAVE_DOUBLE)
+#    elif GMX_SIMD_DOUBLE_WIDTH == 4
+#        define GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE GMX_SIMD_HAVE_LOADU
+// For GMX_SIMD_DOUBLE_WIDTH>4 it is the reponsibility of the implementation to set
+// GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE
+#    endif
+
+#    if GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE
+#        if GMX_SIMD_DOUBLE_WIDTH < 4
+using Simd4NDouble = Simd4Double;
+#            define GMX_SIMD4N_DOUBLE_WIDTH 4
+#        else
+using Simd4NDouble = SimdDouble;
+#            define GMX_SIMD4N_DOUBLE_WIDTH GMX_SIMD_DOUBLE_WIDTH
+#        endif
+
+#        if GMX_SIMD_DOUBLE_WIDTH <= 4
+static inline Simd4NDouble gmx_simdcall loadUNDuplicate4(const double* f)
+{
+    return Simd4NDouble(*f);
+}
+static inline Simd4NDouble gmx_simdcall load4DuplicateN(const double* f)
+{
+    return load<Simd4NDouble>(f);
+}
+static inline Simd4NDouble gmx_simdcall loadU4NOffset(const double* f, int /*unused*/)
+{
+    return loadU<Simd4NDouble>(f);
+}
+#        elif GMX_SIMD_DOUBLE_WIDTH == 8
+static inline Simd4NDouble gmx_simdcall loadUNDuplicate4(const double* f)
+{
+    return loadU1DualHsimd(f);
+}
+static inline Simd4NDouble gmx_simdcall load4DuplicateN(const double* f)
+{
+    return loadDuplicateHsimd(f);
+}
+#        endif
+#    endif // GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE
+#else      // GMX_SIMD_HAVE_DOUBLE
+#    define GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE 0
+#endif
+
+#if GMX_DOUBLE
+#    define GMX_SIMD_HAVE_4NSIMD_UTIL_REAL GMX_SIMD_HAVE_4NSIMD_UTIL_DOUBLE
+#else
+#    define GMX_SIMD_HAVE_4NSIMD_UTIL_REAL GMX_SIMD_HAVE_4NSIMD_UTIL_FLOAT
+#endif
+
+#if GMX_SIMD_HAVE_4NSIMD_UTIL_REAL
+#    if GMX_DOUBLE
+using Simd4NReal = Simd4NDouble;
+#        define GMX_SIMD4N_REAL_WIDTH GMX_SIMD4N_DOUBLE_WIDTH
+#    else
+using Simd4NReal = Simd4NFloat;
+#        define GMX_SIMD4N_REAL_WIDTH GMX_SIMD4N_FLOAT_WIDTH
+#    endif
+#endif
+
+//! \}  end of name-group proxy objects
+
+} // namespace gmx
+
+//! \}          end of module_simd
+
+//! \endcond   end of condition libapi
+
+
+#if GMX_SIMD_HAVE_FLOAT
+
+/*! \brief Returns whether a pointer to float is aligned to a SIMD boundary
+ *
+ * \param[in] ptr  A pointer to a float
+ */
+static inline bool isSimdAligned(const float* ptr)
+{
+    return reinterpret_cast<std::size_t>(ptr) % (GMX_SIMD_FLOAT_WIDTH * sizeof(float)) == 0;
+}
+
+#endif // GMX_SIMD_HAVE_FLOAT
+
+#if GMX_SIMD_HAVE_DOUBLE
+
+/*! \brief Returns whether a pointer to double is aligned to a SIMD boundary
+ *
+ * \param[in] ptr  A pointer to a double
+ */
+static inline bool isSimdAligned(const double* ptr)
+{
+    return reinterpret_cast<std::size_t>(ptr) % (GMX_SIMD_DOUBLE_WIDTH * sizeof(double)) == 0;
+}
+
+#endif // GMX_SIMD_HAVE_DOUBLE
+
+
+#if GMX_SIMD_HAVE_REAL
+#    if GMX_SIMD_REAL_WIDTH > GMX_REAL_MAX_SIMD_WIDTH
+#        error "GMX_SIMD_REAL_WIDTH > GMX_REAL_MAX_SIMD_WIDTH: increase GMX_REAL_MAX_SIMD_WIDTH in real.h"
+#    endif
+#endif
+
+
+#if 0
+/* This is a hack to cover the corner case of using an
+   explicit GMX_SIMD_HAVE_FLOAT or GMX_SIMD_HAVE_DOUBLE, rather than
+   GMX_SIMD_HAVE_REAL.
+
+   Such code is expected to include simd.h to get those symbols
+   defined, but the actual definitions are in the implemention headers
+   included by simd.h. check-source.py is not a full preprocessor, so
+   it does not see the definitions in the implementation headers as
+   belonging to simd.h, thus it cannot check that simd.h is being used
+   correctly in the above hypothetical corner case. However, the
+   checker also does not parse #if 0, so we can fool the checker into
+   thinking that definition occurs here, and that will work well
+   enough.
+
+   If there's ever other kinds of SIMD code that might have the same
+   problem, we might want to add other variables here.
+ */
+#    define GMX_SIMD_HAVE_FLOAT 1
+#    define GMX_SIMD_HAVE_DOUBLE 1
+
+#endif // end of hack
+
+// The ArrayRef<SimdReal> specialization is always included, because compiler
+// errors are confusing when template specialization aren't available.
+#include "gromacs/simd/simd_memory.h"
+
+#endif // GMX_SIMD_SIMD_H
diff --git a/src/include/gromacs/simd/simd_math.h b/src/include/gromacs/simd/simd_math.h
new file mode 100644 (file)
index 0000000..c919b36
--- /dev/null
@@ -0,0 +1,5240 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_SIMD_MATH_H
+#define GMX_SIMD_SIMD_MATH_H
+
+/*! \libinternal \file
+ *
+ * \brief Math functions for SIMD datatypes.
+ *
+ * \attention This file is generic for all SIMD architectures, so you cannot
+ * assume that any of the optional SIMD features (as defined in simd.h) are
+ * present. In particular, this means you cannot assume support for integers,
+ * logical operations (neither on floating-point nor integer values), shifts,
+ * and the architecture might only have SIMD for either float or double.
+ * Second, to keep this file clean and general, any additions to this file
+ * must work for all possible SIMD architectures in both single and double
+ * precision (if they support it), and you cannot make any assumptions about
+ * SIMD width.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \inlibraryapi
+ * \ingroup module_simd
+ */
+
+#include "config.h"
+
+#include <cmath>
+
+#include <limits>
+
+#include "gromacs/math/units.h"
+#include "gromacs/math/utilities.h"
+#include "gromacs/simd/simd.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+#if GMX_SIMD
+
+/*! \cond libapi */
+/*! \addtogroup module_simd */
+/*! \{ */
+
+/*! \name Implementation accuracy settings
+ *  \{
+ */
+
+/*! \} */
+
+#    if GMX_SIMD_HAVE_FLOAT
+
+/*! \name Single precision SIMD math functions
+ *
+ *  \note In most cases you should use the real-precision functions instead.
+ *  \{
+ */
+
+/****************************************
+ * SINGLE PRECISION SIMD MATH FUNCTIONS *
+ ****************************************/
+
+#        if !GMX_SIMD_HAVE_NATIVE_COPYSIGN_FLOAT
+/*! \brief Composes floating point value with the magnitude of x and the sign of y.
+ *
+ * \param x Values to set sign for
+ * \param y Values used to set sign
+ * \return  Magnitude of x, sign of y
+ */
+static inline SimdFloat gmx_simdcall copysign(SimdFloat x, SimdFloat y)
+{
+#            if GMX_SIMD_HAVE_LOGICAL
+    return abs(x) | (SimdFloat(GMX_FLOAT_NEGZERO) & y);
+#            else
+    return blend(abs(x), -abs(x), y < setZero());
+#            endif
+}
+#        endif
+
+#        if !GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_FLOAT
+/*! \brief Perform one Newton-Raphson iteration to improve 1/sqrt(x) for SIMD float.
+ *
+ * This is a low-level routine that should only be used by SIMD math routine
+ * that evaluates the inverse square root.
+ *
+ *  \param lu Approximation of 1/sqrt(x), typically obtained from lookup.
+ *  \param x  The reference (starting) value x for which we want 1/sqrt(x).
+ *  \return   An improved approximation with roughly twice as many bits of accuracy.
+ */
+static inline SimdFloat gmx_simdcall rsqrtIter(SimdFloat lu, SimdFloat x)
+{
+    SimdFloat tmp1 = x * lu;
+    SimdFloat tmp2 = SimdFloat(-0.5F) * lu;
+    tmp1           = fma(tmp1, lu, SimdFloat(-3.0F));
+    return tmp1 * tmp2;
+}
+#        endif
+
+/*! \brief Calculate 1/sqrt(x) for SIMD float.
+ *
+ *  \param x Argument that must be larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *
+ *  \return 1/sqrt(x). Result is undefined if your argument was invalid.
+ */
+static inline SimdFloat gmx_simdcall invsqrt(SimdFloat x)
+{
+    SimdFloat lu = rsqrt(x);
+#        if (GMX_SIMD_RSQRT_BITS < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 2 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 4 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+    return lu;
+}
+
+/*! \brief Calculate 1/sqrt(x) for two SIMD floats.
+ *
+ * \param x0  First set of arguments, x0 must be in single range (see below).
+ * \param x1  Second set of arguments, x1 must be in single range (see below).
+ * \param[out] out0  Result 1/sqrt(x0)
+ * \param[out] out1  Result 1/sqrt(x1)
+ *
+ *  In particular for double precision we can sometimes calculate square root
+ *  pairs slightly faster by using single precision until the very last step.
+ *
+ * \note Both arguments must be larger than GMX_FLOAT_MIN and smaller than
+ *       GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *       For the single precision implementation this is obviously always
+ *       true for positive values, but for double precision it adds an
+ *       extra restriction since the first lookup step might have to be
+ *       performed in single precision on some architectures. Note that the
+ *       responsibility for checking falls on you - this routine does not
+ *       check arguments.
+ */
+static inline void gmx_simdcall invsqrtPair(SimdFloat x0, SimdFloat x1, SimdFloat* out0, SimdFloat* out1)
+{
+    *out0 = invsqrt(x0);
+    *out1 = invsqrt(x1);
+}
+
+#        if !GMX_SIMD_HAVE_NATIVE_RCP_ITER_FLOAT
+/*! \brief Perform one Newton-Raphson iteration to improve 1/x for SIMD float.
+ *
+ * This is a low-level routine that should only be used by SIMD math routine
+ * that evaluates the reciprocal.
+ *
+ *  \param lu Approximation of 1/x, typically obtained from lookup.
+ *  \param x  The reference (starting) value x for which we want 1/x.
+ *  \return   An improved approximation with roughly twice as many bits of accuracy.
+ */
+static inline SimdFloat gmx_simdcall rcpIter(SimdFloat lu, SimdFloat x)
+{
+    return lu * fnma(lu, x, SimdFloat(2.0F));
+}
+#        endif
+
+/*! \brief Calculate 1/x for SIMD float.
+ *
+ *  \param x Argument with magnitude larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *
+ *  \return 1/x. Result is undefined if your argument was invalid.
+ */
+static inline SimdFloat gmx_simdcall inv(SimdFloat x)
+{
+    SimdFloat lu = rcp(x);
+#        if (GMX_SIMD_RCP_BITS < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 2 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 4 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rcpIter(lu, x);
+#        endif
+    return lu;
+}
+
+/*! \brief Division for SIMD floats
+ *
+ * \param nom    Nominator
+ * \param denom  Denominator, with magnitude in range (GMX_FLOAT_MIN,GMX_FLOAT_MAX).
+ *               For single precision this is equivalent to a nonzero argument,
+ *               but in double precision it adds an extra restriction since
+ *               the first lookup step might have to be performed in single
+ *               precision on some architectures. Note that the responsibility
+ *               for checking falls on you - this routine does not check arguments.
+ *
+ * \return nom/denom
+ *
+ * \note This function does not use any masking to avoid problems with
+ *       zero values in the denominator.
+ */
+static inline SimdFloat gmx_simdcall operator/(SimdFloat nom, SimdFloat denom)
+{
+    return nom * inv(denom);
+}
+
+/*! \brief Calculate 1/sqrt(x) for masked entries of SIMD float.
+ *
+ *  This routine only evaluates 1/sqrt(x) for elements for which mask is true.
+ *  Illegal values in the masked-out elements will not lead to
+ *  floating-point exceptions.
+ *
+ *  \param x Argument that must be larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX for masked-in entries.
+ *           See \ref invsqrt for the discussion about argument restrictions.
+ *  \param m Mask
+ *  \return 1/sqrt(x). Result is undefined if your argument was invalid or
+ *          entry was not masked, and 0.0 for masked-out entries.
+ */
+static inline SimdFloat maskzInvsqrt(SimdFloat x, SimdFBool m)
+{
+    SimdFloat lu = maskzRsqrt(x, m);
+#        if (GMX_SIMD_RSQRT_BITS < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 2 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 4 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+    return lu;
+}
+
+/*! \brief Calculate 1/x for SIMD float, masked version.
+ *
+ *  \param x Argument with magnitude larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX for masked-in entries.
+ *           See \ref invsqrt for the discussion about argument restrictions.
+ *  \param m Mask
+ *  \return 1/x for elements where m is true, or 0.0 for masked-out entries.
+ */
+static inline SimdFloat gmx_simdcall maskzInv(SimdFloat x, SimdFBool m)
+{
+    SimdFloat lu = maskzRcp(x, m);
+#        if (GMX_SIMD_RCP_BITS < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 2 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 4 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rcpIter(lu, x);
+#        endif
+    return lu;
+}
+
+/*! \brief Calculate sqrt(x) for SIMD floats
+ *
+ *  \tparam opt By default, this function checks if the input value is 0.0
+ *              and masks this to return the correct result. If you are certain
+ *              your argument will never be zero, and you know you need to save
+ *              every single cycle you can, you can alternatively call the
+ *              function as sqrt<MathOptimization::Unsafe>(x).
+ *
+ *  \param  x   Argument that must be in range 0 <=x <= GMX_FLOAT_MAX, since the
+ *              lookup step often has to be implemented in single precision.
+ *              Arguments smaller than GMX_FLOAT_MIN will always lead to a zero
+ *              result, even in double precision. If you are using the unsafe
+ *              math optimization parameter, the argument must be in the range
+ *              GMX_FLOAT_MIN <= x <= GMX_FLOAT_MAX.
+ *
+ *  \return sqrt(x). The result is undefined if the input value does not fall
+ *          in the allowed range specified for the argument.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall sqrt(SimdFloat x)
+{
+    if (opt == MathOptimization::Safe)
+    {
+        SimdFloat res = maskzInvsqrt(x, setZero() < x);
+        return res * x;
+    }
+    else
+    {
+        return x * invsqrt(x);
+    }
+}
+
+/*! \brief Cube root for SIMD floats
+ *
+ * \param x      Argument to calculate cube root of. Can be negative or zero,
+ *               but NaN or Inf values are not supported. Denormal values will
+ *               be treated as 0.0.
+ * \return       Cube root of x.
+ */
+static inline SimdFloat gmx_simdcall cbrt(SimdFloat x)
+{
+    const SimdFloat signBit(GMX_FLOAT_NEGZERO);
+    const SimdFloat minFloat(std::numeric_limits<float>::min());
+    // Bias is 128-1 = 127, which is not divisible by 3. Since the largest-magnitude
+    // negative exponent from frexp() is -126, we can subtract one more unit to get 126
+    // as offset, which is divisible by 3 (result 42). To avoid clang warnings about fragile integer
+    // division mixed with FP, we let the divided value (42) be the original constant.
+    const std::int32_t offsetDiv3(42);
+    const SimdFloat    c2(-0.191502161678719066F);
+    const SimdFloat    c1(0.697570460207922770F);
+    const SimdFloat    c0(0.492659620528969547F);
+    const SimdFloat    one(1.0F);
+    const SimdFloat    two(2.0F);
+    const SimdFloat    three(3.0F);
+    const SimdFloat    oneThird(1.0F / 3.0F);
+    const SimdFloat    cbrt2(1.2599210498948731648F);
+    const SimdFloat    sqrCbrt2(1.5874010519681994748F);
+
+    // To calculate cbrt(x) we first take the absolute value of x but save the sign,
+    // since cbrt(-x) = -cbrt(x). Then we only need to consider positive values for
+    // the main step.
+    // A number x is represented in IEEE754 as fraction*2^e. We rewrite this as
+    // x=fraction*2^(3*n)*2^m, where e=3*n+m, and m is a remainder.
+    // The cube root can the be evaluated by calculating the cube root of the fraction
+    // limited to the mantissa range, multiplied by 2^mod (which is either 1, +/-2^(1/3) or
+    // +/-2^(2/3), and then we load this into a new IEEE754 fp number with the exponent 2^n, where
+    // n is the integer part of the original exponent divided by 3.
+
+    SimdFloat xSignBit = x & signBit; // create bit mask where the sign bit is 1 for x elements < 0
+    SimdFloat xAbs     = andNot(signBit, x);   // select everthing but the sign bit => abs(x)
+    SimdFBool xIsNonZero = (minFloat <= xAbs); // treat denormals as 0
+
+    SimdFInt32 exponent;
+    SimdFloat  y = frexp(xAbs, &exponent);
+    // For the mantissa (y) we will use a limited-range approximation of cbrt(y),
+    // by first using a polynomial and then evaluating
+    // Transform y to z = c2*y^2 + c1*y + c0, then w = z^3, and finally
+    // evaluate the quotient q = z * (w + 2 * y) / (2 * w + y).
+    SimdFloat z        = fma(fma(y, c2, c1), y, c0);
+    SimdFloat w        = z * z * z;
+    SimdFloat nom      = z * fma(two, y, w);
+    SimdFloat invDenom = inv(fma(two, w, y));
+
+    // Handle the exponent. In principle there are beautiful ways to do this with custom 16-bit
+    // division converted to multiplication... but we can't do that since our SIMD layer cannot
+    // assume the presence of integer shift operations!
+    // However, when I first worked with the integer algorithm I still came up with a neat
+    // optimization, so I'll describe the full algorithm here in case we ever want to use it
+    // in the future:
+    //
+    // Our dividend is signed, which is a complication, but let's consider the unsigned case
+    // first: Division by 3 corresponds to multiplication by 1010101... Since we also know
+    // our dividend is less than 16 bits (exponent range) we can accomplish this by
+    // multiplying with 21845 (which is almost 2^16/3 - 21845.333 would be exact) and then
+    // right-shifting by 16 bits to divide out the 2^16 part.
+    // If we add 1 to the dividend to handle the extra 0.333, the integer result will be correct.
+    // To handle the signed exponent one alternative would be to take absolute values, saving
+    // signs, etc - but that gets a bit complicated with 2-complement integers.
+    // Instead, we remember that we don't really want the exact division per se - what we're
+    // really after is only rewriting e = 3*n+m. That will actually be *easier* to handle if
+    // we require that m must be positive (fewer cases to handle) instead of having n as the
+    // strict e/3.
+    // To handle this we start by adding 127 to the exponent. This value corresponds to the
+    // exponent bias, minus 1 because frexp() has a different standard for the value it returns,
+    // but then we add 1 back to handle the extra 0.333 in 21845. So, we have offsetExp = e+127
+    // and then multiply by 21845 to get a division result offsetExpDiv3.
+    // A (signed) value for n is then recovered by subtracting 42 (bias-1)/3 from k.
+    // To calculate a strict remainder we should evaluate offsetExp - 3*offsetExpDiv3 - 1, where
+    // the extra 1 corrects for the value we added to the exponent to get correct division.
+    // This remainder would have the value 0,1, or 2, but since we only use it to select
+    // other numbers we can skip the last step and just handle the cases as 1,2 or 3 instead.
+    //
+    // OK; end of long detour. Here's how we actually do it in our implementation by using
+    // floating-point for the exponent instead to avoid needing integer shifts:
+    //
+    // 1) Convert the exponent (obtained from frexp) to a float
+    // 2) Calculate offsetExp = exp + offset. Note that we should not add the extra 1 here since we
+    //    do floating-point division instead of our integer hack, so it's the exponent bias-1, or
+    //    the largest exponent minus 2.
+    // 3) Divide the float by 3 by multiplying with 1/3
+    // 4) Truncate it to an integer to get the division result. This is potentially dangerous in
+    //    combination with floating-point, because many integers cannot be represented exactly in
+    //    floating point, and if we are just epsilon below the result might be truncated to a lower
+    //    integer. I have not observed this on x86, but to have a safety margin we can add a small
+    //    fraction - since we already know the fraction part should be either 0, 0.333..., or 0.666...
+    //    We can even save this extra floating-point addition by adding a small fraction (0.1) when
+    //    we introduce the exponent offset - that will correspond to a safety margin of 0.1/3, which is plenty.
+    // 5) Get the remainder part by subtracting the truncated floating-point part.
+    //    Here too we will have a plain division, so the remainder is a strict modulus
+    //    and will have the values 0, 1 or 2.
+    //
+    // Before worrying about the few wasted cycles due to longer fp latency, this has the
+    // additional advantage that we don't use a single integer operation, so the algorithm
+    // will work just A-OK on all SIMD implementations, which avoids diverging code paths.
+
+    // The  0.1 here is the safety margin due to  truncation described in item 4 in the comments above.
+    SimdFloat offsetExp = cvtI2R(exponent) + SimdFloat(static_cast<float>(3 * offsetDiv3) + 0.1);
+
+    SimdFloat offsetExpDiv3 =
+            trunc(offsetExp * oneThird); // important to truncate here to mimic integer division
+
+    SimdFInt32 expDiv3 = cvtR2I(offsetExpDiv3 - SimdFloat(static_cast<float>(offsetDiv3)));
+
+    SimdFloat remainder = offsetExp - offsetExpDiv3 * three;
+
+    // If remainder is 0 we should just have the factor 1.0,
+    // so first pick 1.0 if it is below 0.5, and 2^(1/3) if it's above 0.5 (i.e., 1 or 2)
+    SimdFloat factor = blend(one, cbrt2, SimdFloat(0.5) < remainder);
+    // Second, we overwrite with 2^(2/3) if rem>1.5 (i.e., 2)
+    factor = blend(factor, sqrCbrt2, SimdFloat(1.5) < remainder);
+
+    // Assemble the non-signed fraction, and add the sign back by xor
+    SimdFloat fraction = (nom * invDenom * factor) ^ xSignBit;
+    // Load to IEEE754 number, and set result to 0.0 if x was 0.0 or denormal
+    SimdFloat result = selectByMask(ldexp(fraction, expDiv3), xIsNonZero);
+
+    return result;
+}
+
+/*! \brief Inverse cube root for SIMD floats
+ *
+ * \param x      Argument to calculate cube root of. Can be positive or
+ *               negative, but the magnitude cannot be lower than
+ *               the smallest normal number.
+ * \return       Cube root of x. Undefined for values that don't
+ *               fulfill the restriction of abs(x) > minFloat.
+ */
+static inline SimdFloat gmx_simdcall invcbrt(SimdFloat x)
+{
+    const SimdFloat signBit(GMX_FLOAT_NEGZERO);
+    const SimdFloat minFloat(std::numeric_limits<float>::min());
+    // Bias is 128-1 = 127, which is not divisible by 3. Since the largest-magnitude
+    // negative exponent from frexp() is -126, we can subtract one more unit to get 126
+    // as offset, which is divisible by 3 (result 42). To avoid clang warnings about fragile integer
+    // division mixed with FP, we let the divided value (42) be the original constant.
+    const std::int32_t offsetDiv3(42);
+    const SimdFloat    c2(-0.191502161678719066F);
+    const SimdFloat    c1(0.697570460207922770F);
+    const SimdFloat    c0(0.492659620528969547F);
+    const SimdFloat    one(1.0F);
+    const SimdFloat    two(2.0F);
+    const SimdFloat    three(3.0F);
+    const SimdFloat    oneThird(1.0F / 3.0F);
+    const SimdFloat    invCbrt2(1.0F / 1.2599210498948731648F);
+    const SimdFloat    invSqrCbrt2(1.0F / 1.5874010519681994748F);
+
+    // We use pretty much exactly the same implementation as for cbrt(x),
+    // but to compute the inverse we swap the nominator/denominator
+    // in the quotient, and also swap the sign of the exponent parts.
+
+    SimdFloat xSignBit = x & signBit; // create bit mask where the sign bit is 1 for x elements < 0
+    SimdFloat xAbs     = andNot(signBit, x); // select everthing but the sign bit => abs(x)
+
+    SimdFInt32 exponent;
+    SimdFloat  y = frexp(xAbs, &exponent);
+    // For the mantissa (y) we will use a limited-range approximation of cbrt(y),
+    // by first using a polynomial and then evaluating
+    // Transform y to z = c2*y^2 + c1*y + c0, then w = z^3, and finally
+    // evaluate the quotient q = z * (w + 2 * y) / (2 * w + y).
+    SimdFloat z        = fma(fma(y, c2, c1), y, c0);
+    SimdFloat w        = z * z * z;
+    SimdFloat nom      = fma(two, w, y);
+    SimdFloat invDenom = inv(z * fma(two, y, w));
+
+    // The  0.1 here is the safety margin due to  truncation described in item 4 in the comments above.
+    SimdFloat offsetExp = cvtI2R(exponent) + SimdFloat(static_cast<float>(3 * offsetDiv3) + 0.1);
+    SimdFloat offsetExpDiv3 =
+            trunc(offsetExp * oneThird); // important to truncate here to mimic integer division
+
+    // We should swap the sign here, so we change order of the terms in the subtraction
+    SimdFInt32 expDiv3 = cvtR2I(SimdFloat(static_cast<float>(offsetDiv3)) - offsetExpDiv3);
+
+    // Swap sign here too, so remainder is either 0, -1 or -2
+    SimdFloat remainder = offsetExpDiv3 * three - offsetExp;
+
+    // If remainder is 0 we should just have the factor 1.0,
+    // so first pick 1.0 if it is above -0.5, and 2^(-1/3) if it's below -0.5 (i.e., -1 or -2)
+    SimdFloat factor = blend(one, invCbrt2, remainder < SimdFloat(-0.5));
+    // Second, we overwrite with 2^(-2/3) if rem<-1.5 (i.e., -2)
+    factor = blend(factor, invSqrCbrt2, remainder < SimdFloat(-1.5));
+
+    // Assemble the non-signed fraction, and add the sign back by xor
+    SimdFloat fraction = (nom * invDenom * factor) ^ xSignBit;
+    // Load to IEEE754 number, and set result to 0.0 if x was 0.0 or denormal
+    SimdFloat result = ldexp(fraction, expDiv3);
+
+    return result;
+}
+
+/*! \brief SIMD float log2(x). This is the base-2 logarithm.
+ *
+ * \param x Argument, should be >0.
+ * \result The base-2 logarithm of x. Undefined if argument is invalid.
+ */
+static inline SimdFloat gmx_simdcall log2(SimdFloat x)
+{
+    // This implementation computes log2 by
+    // 1) Extracting the exponent and adding it to...
+    // 2) A 9th-order minimax approximation using only odd
+    //    terms of (x-1)/(x+1), where x is the mantissa.
+
+#        if GMX_SIMD_HAVE_NATIVE_LOG_FLOAT
+    // Just rescale if native log2() is not present, but log() is.
+    return log(x) * SimdFloat(std::log2(std::exp(1.0)));
+#        else
+    const SimdFloat one(1.0F);
+    const SimdFloat two(2.0F);
+    const SimdFloat invsqrt2(1.0F / std::sqrt(2.0F));
+    const SimdFloat CL9(0.342149508897807708152F);
+    const SimdFloat CL7(0.411570606888219447939F);
+    const SimdFloat CL5(0.577085979152320294183F);
+    const SimdFloat CL3(0.961796550607099898222F);
+    const SimdFloat CL1(2.885390081777926774009F);
+    SimdFloat       fExp, x2, p;
+    SimdFBool       m;
+    SimdFInt32      iExp;
+
+    // For the log2() function, the argument can never be 0, so use the faster version of frexp()
+    x    = frexp<MathOptimization::Unsafe>(x, &iExp);
+    fExp = cvtI2R(iExp);
+
+    m = x < invsqrt2;
+    // Adjust to non-IEEE format for x<1/sqrt(2): exponent -= 1, mantissa *= 2.0
+    fExp = fExp - selectByMask(one, m);
+    x    = x * blend(one, two, m);
+
+    x  = (x - one) * inv(x + one);
+    x2 = x * x;
+
+    p = fma(CL9, x2, CL7);
+    p = fma(p, x2, CL5);
+    p = fma(p, x2, CL3);
+    p = fma(p, x2, CL1);
+    p = fma(p, x, fExp);
+
+    return p;
+#        endif
+}
+
+#        if !GMX_SIMD_HAVE_NATIVE_LOG_FLOAT
+/*! \brief SIMD float log(x). This is the natural logarithm.
+ *
+ * \param x Argument, should be >0.
+ * \result The natural logarithm of x. Undefined if argument is invalid.
+ */
+static inline SimdFloat gmx_simdcall log(SimdFloat x)
+{
+    const SimdFloat one(1.0F);
+    const SimdFloat two(2.0F);
+    const SimdFloat invsqrt2(1.0F / std::sqrt(2.0F));
+    const SimdFloat corr(0.693147180559945286226764F);
+    const SimdFloat CL9(0.2371599674224853515625F);
+    const SimdFloat CL7(0.285279005765914916992188F);
+    const SimdFloat CL5(0.400005519390106201171875F);
+    const SimdFloat CL3(0.666666567325592041015625F);
+    const SimdFloat CL1(2.0F);
+    SimdFloat       fExp, x2, p;
+    SimdFBool       m;
+    SimdFInt32      iExp;
+
+    // For log(), the argument cannot be 0, so use the faster version of frexp()
+    x    = frexp<MathOptimization::Unsafe>(x, &iExp);
+    fExp = cvtI2R(iExp);
+
+    m = x < invsqrt2;
+    // Adjust to non-IEEE format for x<1/sqrt(2): exponent -= 1, mantissa *= 2.0
+    fExp = fExp - selectByMask(one, m);
+    x    = x * blend(one, two, m);
+
+    x  = (x - one) * inv(x + one);
+    x2 = x * x;
+
+    p = fma(CL9, x2, CL7);
+    p = fma(p, x2, CL5);
+    p = fma(p, x2, CL3);
+    p = fma(p, x2, CL1);
+    p = fma(p, x, corr * fExp);
+
+    return p;
+}
+#        endif
+
+#        if !GMX_SIMD_HAVE_NATIVE_EXP2_FLOAT
+/*! \brief SIMD float 2^x
+ *
+ * \tparam opt If this is changed from the default (safe) into the unsafe
+ *             option, input values that would otherwise lead to zero-clamped
+ *             results are not allowed and will lead to undefined results.
+ *
+ * \param x Argument. For the default (safe) function version this can be
+ *          arbitrarily small value, but the routine might clamp the result to
+ *          zero for arguments that would produce subnormal IEEE754-2008 results.
+ *          This corresponds to inputs below -126 in single or -1022 in double,
+ *          and it might overflow for arguments reaching 127 (single) or
+ *          1023 (double). If you enable the unsafe math optimization,
+ *          very small arguments will not necessarily be zero-clamped, but
+ *          can produce undefined results.
+ *
+ * \result 2^x. The result is undefined for very large arguments that cause
+ *          internal floating-point overflow. If unsafe optimizations are enabled,
+ *          this is also true for very small values.
+ *
+ * \note    The definition range of this function is just-so-slightly smaller
+ *          than the allowed IEEE exponents for many architectures. This is due
+ *          to the implementation, which will hopefully improve in the future.
+ *
+ * \warning You cannot rely on this implementation returning inf for arguments
+ *          that cause overflow. If you have some very large
+ *          values and need to rely on getting a valid numerical output,
+ *          take the minimum of your variable and the largest valid argument
+ *          before calling this routine.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall exp2(SimdFloat x)
+{
+    const SimdFloat CC6(0.0001534581200287996416911311F);
+    const SimdFloat CC5(0.001339993121934088894618990F);
+    const SimdFloat CC4(0.009618488957115180159497841F);
+    const SimdFloat CC3(0.05550328776964726865751735F);
+    const SimdFloat CC2(0.2402264689063408646490722F);
+    const SimdFloat CC1(0.6931472057372680777553816F);
+    const SimdFloat one(1.0F);
+
+    SimdFloat intpart;
+    SimdFloat fexppart;
+    SimdFloat p;
+
+    // Large negative values are valid arguments to exp2(), so there are two
+    // things we need to account for:
+    // 1. When the exponents reaches -127, the (biased) exponent field will be
+    //    zero and we can no longer multiply with it. There are special IEEE
+    //    formats to handle this range, but for now we have to accept that
+    //    we cannot handle those arguments. If input value becomes even more
+    //    negative, it will start to loop and we would end up with invalid
+    //    exponents. Thus, we need to limit or mask this.
+    // 2. For VERY large negative values, we will have problems that the
+    //    subtraction to get the fractional part loses accuracy, and then we
+    //    can end up with overflows in the polynomial.
+    //
+    // For now, we handle this by forwarding the math optimization setting to
+    // ldexp, where the routine will return zero for very small arguments.
+    //
+    // However, before doing that we need to make sure we do not call cvtR2I
+    // with an argument that is so negative it cannot be converted to an integer.
+    if (opt == MathOptimization::Safe)
+    {
+        x = max(x, SimdFloat(std::numeric_limits<std::int32_t>::lowest()));
+    }
+
+    fexppart = ldexp<opt>(one, cvtR2I(x));
+    intpart  = round(x);
+    x        = x - intpart;
+
+    p = fma(CC6, x, CC5);
+    p = fma(p, x, CC4);
+    p = fma(p, x, CC3);
+    p = fma(p, x, CC2);
+    p = fma(p, x, CC1);
+    p = fma(p, x, one);
+    x = p * fexppart;
+    return x;
+}
+#        endif
+
+#        if !GMX_SIMD_HAVE_NATIVE_EXP_FLOAT
+/*! \brief SIMD float exp(x).
+ *
+ * In addition to scaling the argument for 2^x this routine correctly does
+ * extended precision arithmetics to improve accuracy.
+ *
+ * \tparam opt If this is changed from the default (safe) into the unsafe
+ *             option, input values that would otherwise lead to zero-clamped
+ *             results are not allowed and will lead to undefined results.
+ *
+ * \param x Argument. For the default (safe) function version this can be
+ *          arbitrarily small value, but the routine might clamp the result to
+ *          zero for arguments that would produce subnormal IEEE754-2008 results.
+ *          This corresponds to input arguments reaching
+ *          -126*ln(2)=-87.3 in single, or -1022*ln(2)=-708.4 (double).
+ *          Similarly, it might overflow for arguments reaching
+ *          127*ln(2)=88.0 (single) or 1023*ln(2)=709.1 (double). If the
+ *          unsafe math optimizations are enabled, small input values that would
+ *          result in zero-clamped output are not allowed.
+ *
+ * \result exp(x). Overflowing arguments are likely to either return 0 or inf,
+ *         depending on the underlying implementation. If unsafe optimizations
+ *         are enabled, this is also true for very small values.
+ *
+ * \note    The definition range of this function is just-so-slightly smaller
+ *          than the allowed IEEE exponents for many architectures. This is due
+ *          to the implementation, which will hopefully improve in the future.
+ *
+ * \warning You cannot rely on this implementation returning inf for arguments
+ *          that cause overflow. If you have some very large
+ *          values and need to rely on getting a valid numerical output,
+ *          take the minimum of your variable and the largest valid argument
+ *          before calling this routine.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall exp(SimdFloat x)
+{
+    const SimdFloat argscale(1.44269504088896341F);
+    const SimdFloat invargscale0(-0.693145751953125F);
+    const SimdFloat invargscale1(-1.428606765330187045e-06F);
+    const SimdFloat CC4(0.00136324646882712841033936F);
+    const SimdFloat CC3(0.00836596917361021041870117F);
+    const SimdFloat CC2(0.0416710823774337768554688F);
+    const SimdFloat CC1(0.166665524244308471679688F);
+    const SimdFloat CC0(0.499999850988388061523438F);
+    const SimdFloat one(1.0F);
+    SimdFloat       fexppart;
+    SimdFloat       intpart;
+    SimdFloat       y, p;
+
+    // Large negative values are valid arguments to exp2(), so there are two
+    // things we need to account for:
+    // 1. When the exponents reaches -127, the (biased) exponent field will be
+    //    zero and we can no longer multiply with it. There are special IEEE
+    //    formats to handle this range, but for now we have to accept that
+    //    we cannot handle those arguments. If input value becomes even more
+    //    negative, it will start to loop and we would end up with invalid
+    //    exponents. Thus, we need to limit or mask this.
+    // 2. For VERY large negative values, we will have problems that the
+    //    subtraction to get the fractional part loses accuracy, and then we
+    //    can end up with overflows in the polynomial.
+    //
+    // For now, we handle this by forwarding the math optimization setting to
+    // ldexp, where the routine will return zero for very small arguments.
+    //
+    // However, before doing that we need to make sure we do not call cvtR2I
+    // with an argument that is so negative it cannot be converted to an integer
+    // after being multiplied by argscale.
+
+    if (opt == MathOptimization::Safe)
+    {
+        x = max(x, SimdFloat(std::numeric_limits<std::int32_t>::lowest()) / argscale);
+    }
+
+    y = x * argscale;
+
+
+    fexppart = ldexp<opt>(one, cvtR2I(y));
+    intpart  = round(y);
+
+    // Extended precision arithmetics
+    x = fma(invargscale0, intpart, x);
+    x = fma(invargscale1, intpart, x);
+
+    p = fma(CC4, x, CC3);
+    p = fma(p, x, CC2);
+    p = fma(p, x, CC1);
+    p = fma(p, x, CC0);
+    p = fma(x * x, p, x);
+#            if GMX_SIMD_HAVE_FMA
+    x = fma(p, fexppart, fexppart);
+#            else
+    x = (p + one) * fexppart;
+#            endif
+    return x;
+}
+#        endif
+
+/*! \brief SIMD float pow(x,y)
+ *
+ * This returns x^y for SIMD values.
+ *
+ * \tparam opt If this is changed from the default (safe) into the unsafe
+ *             option, there are no guarantees about correct results for x==0.
+ *
+ * \param x Base.
+ *
+ * \param y exponent.
+
+ * \result x^y. Overflowing arguments are likely to either return 0 or inf,
+ *         depending on the underlying implementation. If unsafe optimizations
+ *         are enabled, this is also true for x==0.
+ *
+ * \warning You cannot rely on this implementation returning inf for arguments
+ *          that cause overflow. If you have some very large
+ *          values and need to rely on getting a valid numerical output,
+ *          take the minimum of your variable and the largest valid argument
+ *          before calling this routine.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall pow(SimdFloat x, SimdFloat y)
+{
+    SimdFloat xcorr;
+
+    if (opt == MathOptimization::Safe)
+    {
+        xcorr = max(x, SimdFloat(std::numeric_limits<float>::min()));
+    }
+    else
+    {
+        xcorr = x;
+    }
+
+    SimdFloat result = exp2<opt>(y * log2(xcorr));
+
+    if (opt == MathOptimization::Safe)
+    {
+        // if x==0 and y>0 we explicitly set the result to 0.0
+        // For any x with y==0, the result will already be 1.0 since we multiply by y (0.0) and call exp().
+        result = blend(result, setZero(), x == setZero() && setZero() < y);
+    }
+
+    return result;
+}
+
+
+/*! \brief SIMD float erf(x).
+ *
+ * \param x The value to calculate erf(x) for.
+ * \result erf(x)
+ *
+ * This routine achieves very close to full precision, but we do not care about
+ * the last bit or the subnormal result range.
+ */
+static inline SimdFloat gmx_simdcall erf(SimdFloat x)
+{
+    // Coefficients for minimax approximation of erf(x)=x*P(x^2) in range [-1,1]
+    const SimdFloat CA6(7.853861353153693e-5F);
+    const SimdFloat CA5(-8.010193625184903e-4F);
+    const SimdFloat CA4(5.188327685732524e-3F);
+    const SimdFloat CA3(-2.685381193529856e-2F);
+    const SimdFloat CA2(1.128358514861418e-1F);
+    const SimdFloat CA1(-3.761262582423300e-1F);
+    const SimdFloat CA0(1.128379165726710F);
+    // Coefficients for minimax approximation of erfc(x)=Exp(-x^2)*P((1/(x-1))^2) in range [0.67,2]
+    const SimdFloat CB9(-0.0018629930017603923F);
+    const SimdFloat CB8(0.003909821287598495F);
+    const SimdFloat CB7(-0.0052094582210355615F);
+    const SimdFloat CB6(0.005685614362160572F);
+    const SimdFloat CB5(-0.0025367682853477272F);
+    const SimdFloat CB4(-0.010199799682318782F);
+    const SimdFloat CB3(0.04369575504816542F);
+    const SimdFloat CB2(-0.11884063474674492F);
+    const SimdFloat CB1(0.2732120154030589F);
+    const SimdFloat CB0(0.42758357702025784F);
+    // Coefficients for minimax approximation of erfc(x)=Exp(-x^2)*(1/x)*P((1/x)^2) in range [2,9.19]
+    const SimdFloat CC10(-0.0445555913112064F);
+    const SimdFloat CC9(0.21376355144663348F);
+    const SimdFloat CC8(-0.3473187200259257F);
+    const SimdFloat CC7(0.016690861551248114F);
+    const SimdFloat CC6(0.7560973182491192F);
+    const SimdFloat CC5(-1.2137903600145787F);
+    const SimdFloat CC4(0.8411872321232948F);
+    const SimdFloat CC3(-0.08670413896296343F);
+    const SimdFloat CC2(-0.27124782687240334F);
+    const SimdFloat CC1(-0.0007502488047806069F);
+    const SimdFloat CC0(0.5642114853803148F);
+    const SimdFloat one(1.0F);
+    const SimdFloat two(2.0F);
+
+    SimdFloat x2, x4, y;
+    SimdFloat t, t2, w, w2;
+    SimdFloat pA0, pA1, pB0, pB1, pC0, pC1;
+    SimdFloat expmx2;
+    SimdFloat res_erf, res_erfc, res;
+    SimdFBool m, maskErf;
+
+    // Calculate erf()
+    x2 = x * x;
+    x4 = x2 * x2;
+
+    pA0 = fma(CA6, x4, CA4);
+    pA1 = fma(CA5, x4, CA3);
+    pA0 = fma(pA0, x4, CA2);
+    pA1 = fma(pA1, x4, CA1);
+    pA0 = pA0 * x4;
+    pA0 = fma(pA1, x2, pA0);
+    // Constant term must come last for precision reasons
+    pA0 = pA0 + CA0;
+
+    res_erf = x * pA0;
+
+    // Calculate erfc
+    y       = abs(x);
+    maskErf = SimdFloat(0.75F) <= y;
+    t       = maskzInv(y, maskErf);
+    w       = t - one;
+    t2      = t * t;
+    w2      = w * w;
+
+    // No need for a floating-point sieve here (as in erfc), since erf()
+    // will never return values that are extremely small for large args.
+    expmx2 = exp(-y * y);
+
+    pB1 = fma(CB9, w2, CB7);
+    pB0 = fma(CB8, w2, CB6);
+    pB1 = fma(pB1, w2, CB5);
+    pB0 = fma(pB0, w2, CB4);
+    pB1 = fma(pB1, w2, CB3);
+    pB0 = fma(pB0, w2, CB2);
+    pB1 = fma(pB1, w2, CB1);
+    pB0 = fma(pB0, w2, CB0);
+    pB0 = fma(pB1, w, pB0);
+
+    pC0 = fma(CC10, t2, CC8);
+    pC1 = fma(CC9, t2, CC7);
+    pC0 = fma(pC0, t2, CC6);
+    pC1 = fma(pC1, t2, CC5);
+    pC0 = fma(pC0, t2, CC4);
+    pC1 = fma(pC1, t2, CC3);
+    pC0 = fma(pC0, t2, CC2);
+    pC1 = fma(pC1, t2, CC1);
+
+    pC0 = fma(pC0, t2, CC0);
+    pC0 = fma(pC1, t, pC0);
+    pC0 = pC0 * t;
+
+    // Select pB0 or pC0 for erfc()
+    m        = two < y;
+    res_erfc = blend(pB0, pC0, m);
+    res_erfc = res_erfc * expmx2;
+
+    // erfc(x<0) = 2-erfc(|x|)
+    m        = x < setZero();
+    res_erfc = blend(res_erfc, two - res_erfc, m);
+
+    // Select erf() or erfc()
+    res = blend(res_erf, one - res_erfc, maskErf);
+
+    return res;
+}
+
+/*! \brief SIMD float erfc(x).
+ *
+ * \param x The value to calculate erfc(x) for.
+ * \result erfc(x)
+ *
+ * This routine achieves full precision (bar the last bit) over most of the
+ * input range, but for large arguments where the result is getting close
+ * to the minimum representable numbers we accept slightly larger errors
+ * (think results that are in the ballpark of 10^-30 for single precision)
+ * since that is not relevant for MD.
+ */
+static inline SimdFloat gmx_simdcall erfc(SimdFloat x)
+{
+    // Coefficients for minimax approximation of erf(x)=x*P(x^2) in range [-1,1]
+    const SimdFloat CA6(7.853861353153693e-5F);
+    const SimdFloat CA5(-8.010193625184903e-4F);
+    const SimdFloat CA4(5.188327685732524e-3F);
+    const SimdFloat CA3(-2.685381193529856e-2F);
+    const SimdFloat CA2(1.128358514861418e-1F);
+    const SimdFloat CA1(-3.761262582423300e-1F);
+    const SimdFloat CA0(1.128379165726710F);
+    // Coefficients for minimax approximation of erfc(x)=Exp(-x^2)*P((1/(x-1))^2) in range [0.67,2]
+    const SimdFloat CB9(-0.0018629930017603923F);
+    const SimdFloat CB8(0.003909821287598495F);
+    const SimdFloat CB7(-0.0052094582210355615F);
+    const SimdFloat CB6(0.005685614362160572F);
+    const SimdFloat CB5(-0.0025367682853477272F);
+    const SimdFloat CB4(-0.010199799682318782F);
+    const SimdFloat CB3(0.04369575504816542F);
+    const SimdFloat CB2(-0.11884063474674492F);
+    const SimdFloat CB1(0.2732120154030589F);
+    const SimdFloat CB0(0.42758357702025784F);
+    // Coefficients for minimax approximation of erfc(x)=Exp(-x^2)*(1/x)*P((1/x)^2) in range [2,9.19]
+    const SimdFloat CC10(-0.0445555913112064F);
+    const SimdFloat CC9(0.21376355144663348F);
+    const SimdFloat CC8(-0.3473187200259257F);
+    const SimdFloat CC7(0.016690861551248114F);
+    const SimdFloat CC6(0.7560973182491192F);
+    const SimdFloat CC5(-1.2137903600145787F);
+    const SimdFloat CC4(0.8411872321232948F);
+    const SimdFloat CC3(-0.08670413896296343F);
+    const SimdFloat CC2(-0.27124782687240334F);
+    const SimdFloat CC1(-0.0007502488047806069F);
+    const SimdFloat CC0(0.5642114853803148F);
+    // Coefficients for expansion of exp(x) in [0,0.1]
+    // CD0 and CD1 are both 1.0, so no need to declare them separately
+    const SimdFloat CD2(0.5000066608081202F);
+    const SimdFloat CD3(0.1664795422874624F);
+    const SimdFloat CD4(0.04379839977652482F);
+    const SimdFloat one(1.0F);
+    const SimdFloat two(2.0F);
+
+    /* We need to use a small trick here, since we cannot assume all SIMD
+     * architectures support integers, and the flag we want (0xfffff000) would
+     * evaluate to NaN (i.e., it cannot be expressed as a floating-point num).
+     * Instead, we represent the flags 0xf0f0f000 and 0x0f0f0000 as valid
+     * fp numbers, and perform a logical or. Since the expression is constant,
+     * we can at least hope it is evaluated at compile-time.
+     */
+#        if GMX_SIMD_HAVE_LOGICAL
+    const SimdFloat sieve(SimdFloat(-5.965323564e+29F) | SimdFloat(7.05044434e-30F));
+#        else
+    const int                         isieve = 0xFFFFF000;
+    alignas(GMX_SIMD_ALIGNMENT) float mem[GMX_SIMD_FLOAT_WIDTH];
+
+    union
+    {
+        float f;
+        int   i;
+    } conv;
+    int i;
+#        endif
+
+    SimdFloat x2, x4, y;
+    SimdFloat q, z, t, t2, w, w2;
+    SimdFloat pA0, pA1, pB0, pB1, pC0, pC1;
+    SimdFloat expmx2, corr;
+    SimdFloat res_erf, res_erfc, res;
+    SimdFBool m, msk_erf;
+
+    // Calculate erf()
+    x2 = x * x;
+    x4 = x2 * x2;
+
+    pA0 = fma(CA6, x4, CA4);
+    pA1 = fma(CA5, x4, CA3);
+    pA0 = fma(pA0, x4, CA2);
+    pA1 = fma(pA1, x4, CA1);
+    pA1 = pA1 * x2;
+    pA0 = fma(pA0, x4, pA1);
+    // Constant term must come last for precision reasons
+    pA0 = pA0 + CA0;
+
+    res_erf = x * pA0;
+
+    // Calculate erfc
+    y       = abs(x);
+    msk_erf = SimdFloat(0.75F) <= y;
+    t       = maskzInv(y, msk_erf);
+    w       = t - one;
+    t2      = t * t;
+    w2      = w * w;
+    /*
+     * We cannot simply calculate exp(-y2) directly in single precision, since
+     * that will lose a couple of bits of precision due to the multiplication.
+     * Instead, we introduce y=z+w, where the last 12 bits of precision are in w.
+     * Then we get exp(-y2) = exp(-z2)*exp((z-y)*(z+y)).
+     *
+     * The only drawback with this is that it requires TWO separate exponential
+     * evaluations, which would be horrible performance-wise. However, the argument
+     * for the second exp() call is always small, so there we simply use a
+     * low-order minimax expansion on [0,0.1].
+     *
+     * However, this neat idea requires support for logical ops (and) on
+     * FP numbers, which some vendors decided isn't necessary in their SIMD
+     * instruction sets (Hi, IBM VSX!). In principle we could use some tricks
+     * in double, but we still need memory as a backup when that is not available,
+     * and this case is rare enough that we go directly there...
+     */
+#        if GMX_SIMD_HAVE_LOGICAL
+    z = y & sieve;
+#        else
+    store(mem, y);
+    for (i = 0; i < GMX_SIMD_FLOAT_WIDTH; i++)
+    {
+        conv.f = mem[i];
+        conv.i = conv.i & isieve;
+        mem[i] = conv.f;
+    }
+    z = load<SimdFloat>(mem);
+#        endif
+    q    = (z - y) * (z + y);
+    corr = fma(CD4, q, CD3);
+    corr = fma(corr, q, CD2);
+    corr = fma(corr, q, one);
+    corr = fma(corr, q, one);
+
+    expmx2 = exp(-z * z);
+    expmx2 = expmx2 * corr;
+
+    pB1 = fma(CB9, w2, CB7);
+    pB0 = fma(CB8, w2, CB6);
+    pB1 = fma(pB1, w2, CB5);
+    pB0 = fma(pB0, w2, CB4);
+    pB1 = fma(pB1, w2, CB3);
+    pB0 = fma(pB0, w2, CB2);
+    pB1 = fma(pB1, w2, CB1);
+    pB0 = fma(pB0, w2, CB0);
+    pB0 = fma(pB1, w, pB0);
+
+    pC0 = fma(CC10, t2, CC8);
+    pC1 = fma(CC9, t2, CC7);
+    pC0 = fma(pC0, t2, CC6);
+    pC1 = fma(pC1, t2, CC5);
+    pC0 = fma(pC0, t2, CC4);
+    pC1 = fma(pC1, t2, CC3);
+    pC0 = fma(pC0, t2, CC2);
+    pC1 = fma(pC1, t2, CC1);
+
+    pC0 = fma(pC0, t2, CC0);
+    pC0 = fma(pC1, t, pC0);
+    pC0 = pC0 * t;
+
+    // Select pB0 or pC0 for erfc()
+    m        = two < y;
+    res_erfc = blend(pB0, pC0, m);
+    res_erfc = res_erfc * expmx2;
+
+    // erfc(x<0) = 2-erfc(|x|)
+    m        = x < setZero();
+    res_erfc = blend(res_erfc, two - res_erfc, m);
+
+    // Select erf() or erfc()
+    res = blend(one - res_erf, res_erfc, msk_erf);
+
+    return res;
+}
+
+/*! \brief SIMD float sin \& cos.
+ *
+ * \param x The argument to evaluate sin/cos for
+ * \param[out] sinval Sin(x)
+ * \param[out] cosval Cos(x)
+ *
+ * This version achieves close to machine precision, but for very large
+ * magnitudes of the argument we inherently begin to lose accuracy due to the
+ * argument reduction, despite using extended precision arithmetics internally.
+ */
+static inline void gmx_simdcall sincos(SimdFloat x, SimdFloat* sinval, SimdFloat* cosval)
+{
+    // Constants to subtract Pi/4*x from y while minimizing precision loss
+    const SimdFloat argred0(-1.5703125);
+    const SimdFloat argred1(-4.83751296997070312500e-04F);
+    const SimdFloat argred2(-7.54953362047672271729e-08F);
+    const SimdFloat argred3(-2.56334406825708960298e-12F);
+    const SimdFloat two_over_pi(static_cast<float>(2.0F / M_PI));
+    const SimdFloat const_sin2(-1.9515295891e-4F);
+    const SimdFloat const_sin1(8.3321608736e-3F);
+    const SimdFloat const_sin0(-1.6666654611e-1F);
+    const SimdFloat const_cos2(2.443315711809948e-5F);
+    const SimdFloat const_cos1(-1.388731625493765e-3F);
+    const SimdFloat const_cos0(4.166664568298827e-2F);
+    const SimdFloat half(0.5F);
+    const SimdFloat one(1.0F);
+    SimdFloat       ssign, csign;
+    SimdFloat       x2, y, z, psin, pcos, sss, ccc;
+    SimdFBool       m;
+
+#        if GMX_SIMD_HAVE_FINT32_ARITHMETICS && GMX_SIMD_HAVE_LOGICAL
+    const SimdFInt32 ione(1);
+    const SimdFInt32 itwo(2);
+    SimdFInt32       iy;
+
+    z  = x * two_over_pi;
+    iy = cvtR2I(z);
+    y  = round(z);
+
+    m     = cvtIB2B((iy & ione) == SimdFInt32(0));
+    ssign = selectByMask(SimdFloat(GMX_FLOAT_NEGZERO), cvtIB2B((iy & itwo) == itwo));
+    csign = selectByMask(SimdFloat(GMX_FLOAT_NEGZERO), cvtIB2B(((iy + ione) & itwo) == itwo));
+#        else
+    const SimdFloat quarter(0.25f);
+    const SimdFloat minusquarter(-0.25f);
+    SimdFloat       q;
+    SimdFBool       m1, m2, m3;
+
+    /* The most obvious way to find the arguments quadrant in the unit circle
+     * to calculate the sign is to use integer arithmetic, but that is not
+     * present in all SIMD implementations. As an alternative, we have devised a
+     * pure floating-point algorithm that uses truncation for argument reduction
+     * so that we get a new value 0<=q<1 over the unit circle, and then
+     * do floating-point comparisons with fractions. This is likely to be
+     * slightly slower (~10%) due to the longer latencies of floating-point, so
+     * we only use it when integer SIMD arithmetic is not present.
+     */
+    ssign = x;
+    x     = abs(x);
+    // It is critical that half-way cases are rounded down
+    z = fma(x, two_over_pi, half);
+    y = trunc(z);
+    q = z * quarter;
+    q = q - trunc(q);
+    /* z now starts at 0.0 for x=-pi/4 (although neg. values cannot occur), and
+     * then increased by 1.0 as x increases by 2*Pi, when it resets to 0.0.
+     * This removes the 2*Pi periodicity without using any integer arithmetic.
+     * First check if y had the value 2 or 3, set csign if true.
+     */
+    q = q - half;
+    /* If we have logical operations we can work directly on the signbit, which
+     * saves instructions. Otherwise we need to represent signs as +1.0/-1.0.
+     * Thus, if you are altering defines to debug alternative code paths, the
+     * two GMX_SIMD_HAVE_LOGICAL sections in this routine must either both be
+     * active or inactive - you will get errors if only one is used.
+     */
+#            if GMX_SIMD_HAVE_LOGICAL
+    ssign = ssign & SimdFloat(GMX_FLOAT_NEGZERO);
+    csign = andNot(q, SimdFloat(GMX_FLOAT_NEGZERO));
+    ssign = ssign ^ csign;
+#            else
+    ssign = copysign(SimdFloat(1.0f), ssign);
+    csign = copysign(SimdFloat(1.0f), q);
+    csign = -csign;
+    ssign = ssign * csign; // swap ssign if csign was set.
+#            endif
+    // Check if y had value 1 or 3 (remember we subtracted 0.5 from q)
+    m1 = (q < minusquarter);
+    m2 = (setZero() <= q);
+    m3 = (q < quarter);
+    m2 = m2 && m3;
+    m  = m1 || m2;
+    // where mask is FALSE, swap sign.
+    csign   = csign * blend(SimdFloat(-1.0f), one, m);
+#        endif
+    x  = fma(y, argred0, x);
+    x  = fma(y, argred1, x);
+    x  = fma(y, argred2, x);
+    x  = fma(y, argred3, x);
+    x2 = x * x;
+
+    psin = fma(const_sin2, x2, const_sin1);
+    psin = fma(psin, x2, const_sin0);
+    psin = fma(psin, x * x2, x);
+    pcos = fma(const_cos2, x2, const_cos1);
+    pcos = fma(pcos, x2, const_cos0);
+    pcos = fms(pcos, x2, half);
+    pcos = fma(pcos, x2, one);
+
+    sss = blend(pcos, psin, m);
+    ccc = blend(psin, pcos, m);
+    // See comment for GMX_SIMD_HAVE_LOGICAL section above.
+#        if GMX_SIMD_HAVE_LOGICAL
+    *sinval = sss ^ ssign;
+    *cosval = ccc ^ csign;
+#        else
+    *sinval = sss * ssign;
+    *cosval = ccc * csign;
+#        endif
+}
+
+/*! \brief SIMD float sin(x).
+ *
+ * \param x The argument to evaluate sin for
+ * \result Sin(x)
+ *
+ * \attention Do NOT call both sin & cos if you need both results, since each of them
+ * will then call \ref sincos and waste a factor 2 in performance.
+ */
+static inline SimdFloat gmx_simdcall sin(SimdFloat x)
+{
+    SimdFloat s, c;
+    sincos(x, &s, &c);
+    return s;
+}
+
+/*! \brief SIMD float cos(x).
+ *
+ * \param x The argument to evaluate cos for
+ * \result Cos(x)
+ *
+ * \attention Do NOT call both sin & cos if you need both results, since each of them
+ * will then call \ref sincos and waste a factor 2 in performance.
+ */
+static inline SimdFloat gmx_simdcall cos(SimdFloat x)
+{
+    SimdFloat s, c;
+    sincos(x, &s, &c);
+    return c;
+}
+
+/*! \brief SIMD float tan(x).
+ *
+ * \param x The argument to evaluate tan for
+ * \result Tan(x)
+ */
+static inline SimdFloat gmx_simdcall tan(SimdFloat x)
+{
+    const SimdFloat argred0(-1.5703125);
+    const SimdFloat argred1(-4.83751296997070312500e-04F);
+    const SimdFloat argred2(-7.54953362047672271729e-08F);
+    const SimdFloat argred3(-2.56334406825708960298e-12F);
+    const SimdFloat two_over_pi(static_cast<float>(2.0F / M_PI));
+    const SimdFloat CT6(0.009498288995810566122993911);
+    const SimdFloat CT5(0.002895755790837379295226923);
+    const SimdFloat CT4(0.02460087336161924491836265);
+    const SimdFloat CT3(0.05334912882656359828045988);
+    const SimdFloat CT2(0.1333989091464957704418495);
+    const SimdFloat CT1(0.3333307599244198227797507);
+
+    SimdFloat x2, p, y, z;
+    SimdFBool m;
+
+#        if GMX_SIMD_HAVE_FINT32_ARITHMETICS && GMX_SIMD_HAVE_LOGICAL
+    SimdFInt32 iy;
+    SimdFInt32 ione(1);
+
+    z  = x * two_over_pi;
+    iy = cvtR2I(z);
+    y  = round(z);
+    m  = cvtIB2B((iy & ione) == ione);
+
+    x = fma(y, argred0, x);
+    x = fma(y, argred1, x);
+    x = fma(y, argred2, x);
+    x = fma(y, argred3, x);
+    x = selectByMask(SimdFloat(GMX_FLOAT_NEGZERO), m) ^ x;
+#        else
+    const SimdFloat quarter(0.25f);
+    const SimdFloat half(0.5f);
+    const SimdFloat threequarter(0.75f);
+    SimdFloat       w, q;
+    SimdFBool       m1, m2, m3;
+
+    w     = abs(x);
+    z     = fma(w, two_over_pi, half);
+    y     = trunc(z);
+    q     = z * quarter;
+    q     = q - trunc(q);
+    m1    = quarter <= q;
+    m2    = q < half;
+    m3    = threequarter <= q;
+    m1    = m1 && m2;
+    m     = m1 || m3;
+    w     = fma(y, argred0, w);
+    w     = fma(y, argred1, w);
+    w     = fma(y, argred2, w);
+    w     = fma(y, argred3, w);
+    w     = blend(w, -w, m);
+    x     = w * copysign(SimdFloat(1.0), x);
+#        endif
+    x2 = x * x;
+    p  = fma(CT6, x2, CT5);
+    p  = fma(p, x2, CT4);
+    p  = fma(p, x2, CT3);
+    p  = fma(p, x2, CT2);
+    p  = fma(p, x2, CT1);
+    p  = fma(x2 * p, x, x);
+
+    p = blend(p, maskzInv(p, m), m);
+    return p;
+}
+
+/*! \brief SIMD float asin(x).
+ *
+ * \param x The argument to evaluate asin for
+ * \result Asin(x)
+ */
+static inline SimdFloat gmx_simdcall asin(SimdFloat x)
+{
+    const SimdFloat limitlow(1e-4F);
+    const SimdFloat half(0.5F);
+    const SimdFloat one(1.0F);
+    const SimdFloat halfpi(static_cast<float>(M_PI / 2.0F));
+    const SimdFloat CC5(4.2163199048E-2F);
+    const SimdFloat CC4(2.4181311049E-2F);
+    const SimdFloat CC3(4.5470025998E-2F);
+    const SimdFloat CC2(7.4953002686E-2F);
+    const SimdFloat CC1(1.6666752422E-1F);
+    SimdFloat       xabs;
+    SimdFloat       z, z1, z2, q, q1, q2;
+    SimdFloat       pA, pB;
+    SimdFBool       m, m2;
+
+    xabs = abs(x);
+    m    = half < xabs;
+    z1   = half * (one - xabs);
+    m2   = xabs < one;
+    q1   = z1 * maskzInvsqrt(z1, m2);
+    q2   = xabs;
+    z2   = q2 * q2;
+    z    = blend(z2, z1, m);
+    q    = blend(q2, q1, m);
+
+    z2 = z * z;
+    pA = fma(CC5, z2, CC3);
+    pB = fma(CC4, z2, CC2);
+    pA = fma(pA, z2, CC1);
+    pA = pA * z;
+    z  = fma(pB, z2, pA);
+    z  = fma(z, q, q);
+    q2 = halfpi - z;
+    q2 = q2 - z;
+    z  = blend(z, q2, m);
+
+    m = limitlow < xabs;
+    z = blend(xabs, z, m);
+    z = copysign(z, x);
+
+    return z;
+}
+
+/*! \brief SIMD float acos(x).
+ *
+ * \param x The argument to evaluate acos for
+ * \result Acos(x)
+ */
+static inline SimdFloat gmx_simdcall acos(SimdFloat x)
+{
+    const SimdFloat one(1.0F);
+    const SimdFloat half(0.5F);
+    const SimdFloat pi(static_cast<float>(M_PI));
+    const SimdFloat halfpi(static_cast<float>(M_PI / 2.0F));
+    SimdFloat       xabs;
+    SimdFloat       z, z1, z2, z3;
+    SimdFBool       m1, m2, m3;
+
+    xabs = abs(x);
+    m1   = half < xabs;
+    m2   = setZero() < x;
+
+    z  = fnma(half, xabs, half);
+    m3 = xabs < one;
+    z  = z * maskzInvsqrt(z, m3);
+    z  = blend(x, z, m1);
+    z  = asin(z);
+
+    z2 = z + z;
+    z1 = pi - z2;
+    z3 = halfpi - z;
+    z  = blend(z1, z2, m2);
+    z  = blend(z3, z, m1);
+
+    return z;
+}
+
+/*! \brief SIMD float asin(x).
+ *
+ * \param x The argument to evaluate atan for
+ * \result Atan(x), same argument/value range as standard math library.
+ */
+static inline SimdFloat gmx_simdcall atan(SimdFloat x)
+{
+    const SimdFloat halfpi(static_cast<float>(M_PI / 2.0F));
+    const SimdFloat CA17(0.002823638962581753730774F);
+    const SimdFloat CA15(-0.01595690287649631500244F);
+    const SimdFloat CA13(0.04250498861074447631836F);
+    const SimdFloat CA11(-0.07489009201526641845703F);
+    const SimdFloat CA9(0.1063479334115982055664F);
+    const SimdFloat CA7(-0.1420273631811141967773F);
+    const SimdFloat CA5(0.1999269574880599975585F);
+    const SimdFloat CA3(-0.3333310186862945556640F);
+    const SimdFloat one(1.0F);
+    SimdFloat       x2, x3, x4, pA, pB;
+    SimdFBool       m, m2;
+
+    m  = x < setZero();
+    x  = abs(x);
+    m2 = one < x;
+    x  = blend(x, maskzInv(x, m2), m2);
+
+    x2 = x * x;
+    x3 = x2 * x;
+    x4 = x2 * x2;
+    pA = fma(CA17, x4, CA13);
+    pB = fma(CA15, x4, CA11);
+    pA = fma(pA, x4, CA9);
+    pB = fma(pB, x4, CA7);
+    pA = fma(pA, x4, CA5);
+    pB = fma(pB, x4, CA3);
+    pA = fma(pA, x2, pB);
+    pA = fma(pA, x3, x);
+
+    pA = blend(pA, halfpi - pA, m2);
+    pA = blend(pA, -pA, m);
+
+    return pA;
+}
+
+/*! \brief SIMD float atan2(y,x).
+ *
+ * \param y Y component of vector, any quartile
+ * \param x X component of vector, any quartile
+ * \result Atan(y,x), same argument/value range as standard math library.
+ *
+ * \note This routine should provide correct results for all finite
+ * non-zero or positive-zero arguments. However, negative zero arguments will
+ * be treated as positive zero, which means the return value will deviate from
+ * the standard math library atan2(y,x) for those cases. That should not be
+ * of any concern in Gromacs, and in particular it will not affect calculations
+ * of angles from vectors.
+ */
+static inline SimdFloat gmx_simdcall atan2(SimdFloat y, SimdFloat x)
+{
+    const SimdFloat pi(static_cast<float>(M_PI));
+    const SimdFloat halfpi(static_cast<float>(M_PI / 2.0));
+    SimdFloat       xinv, p, aoffset;
+    SimdFBool       mask_xnz, mask_ynz, mask_xlt0, mask_ylt0;
+
+    mask_xnz  = x != setZero();
+    mask_ynz  = y != setZero();
+    mask_xlt0 = x < setZero();
+    mask_ylt0 = y < setZero();
+
+    aoffset = selectByNotMask(halfpi, mask_xnz);
+    aoffset = selectByMask(aoffset, mask_ynz);
+
+    aoffset = blend(aoffset, pi, mask_xlt0);
+    aoffset = blend(aoffset, -aoffset, mask_ylt0);
+
+    xinv = maskzInv(x, mask_xnz);
+    p    = y * xinv;
+    p    = atan(p);
+    p    = p + aoffset;
+
+    return p;
+}
+
+/*! \brief Calculate the force correction due to PME analytically in SIMD float.
+ *
+ * \param z2 \f$(r \beta)^2\f$ - see below for details.
+ * \result Correction factor to coulomb force - see below for details.
+ *
+ * This routine is meant to enable analytical evaluation of the
+ * direct-space PME electrostatic force to avoid tables.
+ *
+ * The direct-space potential should be \f$ \mbox{erfc}(\beta r)/r\f$, but there
+ * are some problems evaluating that:
+ *
+ * First, the error function is difficult (read: expensive) to
+ * approxmiate accurately for intermediate to large arguments, and
+ * this happens already in ranges of \f$(\beta r)\f$ that occur in simulations.
+ * Second, we now try to avoid calculating potentials in Gromacs but
+ * use forces directly.
+ *
+ * We can simply things slight by noting that the PME part is really
+ * a correction to the normal Coulomb force since \f$\mbox{erfc}(z)=1-\mbox{erf}(z)\f$, i.e.
+ * \f[
+ * V = \frac{1}{r} - \frac{\mbox{erf}(\beta r)}{r}
+ * \f]
+ * The first term we already have from the inverse square root, so
+ * that we can leave out of this routine.
+ *
+ * For pme tolerances of 1e-3 to 1e-8 and cutoffs of 0.5nm to 1.8nm,
+ * the argument \f$beta r\f$ will be in the range 0.15 to ~4, which is
+ * the range used for the minimax fit. Use your favorite plotting program
+ * to realize how well-behaved \f$\frac{\mbox{erf}(z)}{z}\f$ is in this range!
+ *
+ * We approximate \f$f(z)=\mbox{erf}(z)/z\f$ with a rational minimax polynomial.
+ * However, it turns out it is more efficient to approximate \f$f(z)/z\f$ and
+ * then only use even powers. This is another minor optimization, since
+ * we actually \a want \f$f(z)/z\f$, because it is going to be multiplied by
+ * the vector between the two atoms to get the vectorial force. The
+ * fastest flops are the ones we can avoid calculating!
+ *
+ * So, here's how it should be used:
+ *
+ * 1. Calculate \f$r^2\f$.
+ * 2. Multiply by \f$\beta^2\f$, so you get \f$z^2=(\beta r)^2\f$.
+ * 3. Evaluate this routine with \f$z^2\f$ as the argument.
+ * 4. The return value is the expression:
+ *
+ * \f[
+ *    \frac{2 \exp{-z^2}}{\sqrt{\pi} z^2}-\frac{\mbox{erf}(z)}{z^3}
+ * \f]
+ *
+ * 5. Multiply the entire expression by \f$\beta^3\f$. This will get you
+ *
+ *  \f[
+ *    \frac{2 \beta^3 \exp(-z^2)}{\sqrt{\pi} z^2} - \frac{\beta^3 \mbox{erf}(z)}{z^3}
+ *  \f]
+ *
+ *    or, switching back to \f$r\f$ (since \f$z=r \beta\f$):
+ *
+ *  \f[
+ *    \frac{2 \beta \exp(-r^2 \beta^2)}{\sqrt{\pi} r^2} - \frac{\mbox{erf}(r \beta)}{r^3}
+ *  \f]
+ *
+ *    With a bit of math exercise you should be able to confirm that
+ *    this is exactly
+ *
+ *  \f[
+ *   \frac{\frac{d}{dr}\left( \frac{\mbox{erf}(\beta r)}{r} \right)}{r}
+ *  \f]
+ *
+ * 6. Add the result to \f$r^{-3}\f$, multiply by the product of the charges,
+ *    and you have your force (divided by \f$r\f$). A final multiplication
+ *    with the vector connecting the two particles and you have your
+ *    vectorial force to add to the particles.
+ *
+ * This approximation achieves an error slightly lower than 1e-6
+ * in single precision and 1e-11 in double precision
+ * for arguments smaller than 16 (\f$\beta r \leq 4 \f$);
+ * when added to \f$1/r\f$ the error will be insignificant.
+ * For \f$\beta r \geq 7206\f$ the return value can be inf or NaN.
+ *
+ */
+static inline SimdFloat gmx_simdcall pmeForceCorrection(SimdFloat z2)
+{
+    const SimdFloat FN6(-1.7357322914161492954e-8F);
+    const SimdFloat FN5(1.4703624142580877519e-6F);
+    const SimdFloat FN4(-0.000053401640219807709149F);
+    const SimdFloat FN3(0.0010054721316683106153F);
+    const SimdFloat FN2(-0.019278317264888380590F);
+    const SimdFloat FN1(0.069670166153766424023F);
+    const SimdFloat FN0(-0.75225204789749321333F);
+
+    const SimdFloat FD4(0.0011193462567257629232F);
+    const SimdFloat FD3(0.014866955030185295499F);
+    const SimdFloat FD2(0.11583842382862377919F);
+    const SimdFloat FD1(0.50736591960530292870F);
+    const SimdFloat FD0(1.0F);
+
+    SimdFloat z4;
+    SimdFloat polyFN0, polyFN1, polyFD0, polyFD1;
+
+    z4 = z2 * z2;
+
+    polyFD0 = fma(FD4, z4, FD2);
+    polyFD1 = fma(FD3, z4, FD1);
+    polyFD0 = fma(polyFD0, z4, FD0);
+    polyFD0 = fma(polyFD1, z2, polyFD0);
+
+    polyFD0 = inv(polyFD0);
+
+    polyFN0 = fma(FN6, z4, FN4);
+    polyFN1 = fma(FN5, z4, FN3);
+    polyFN0 = fma(polyFN0, z4, FN2);
+    polyFN1 = fma(polyFN1, z4, FN1);
+    polyFN0 = fma(polyFN0, z4, FN0);
+    polyFN0 = fma(polyFN1, z2, polyFN0);
+
+    return polyFN0 * polyFD0;
+}
+
+
+/*! \brief Calculate the potential correction due to PME analytically in SIMD float.
+ *
+ * \param z2 \f$(r \beta)^2\f$ - see below for details.
+ * \result Correction factor to coulomb potential - see below for details.
+ *
+ * See \ref pmeForceCorrection for details about the approximation.
+ *
+ * This routine calculates \f$\mbox{erf}(z)/z\f$, although you should provide \f$z^2\f$
+ * as the input argument.
+ *
+ * Here's how it should be used:
+ *
+ * 1. Calculate \f$r^2\f$.
+ * 2. Multiply by \f$\beta^2\f$, so you get \f$z^2=\beta^2*r^2\f$.
+ * 3. Evaluate this routine with z^2 as the argument.
+ * 4. The return value is the expression:
+ *
+ *  \f[
+ *   \frac{\mbox{erf}(z)}{z}
+ *  \f]
+ *
+ * 5. Multiply the entire expression by beta and switching back to \f$r\f$ (since \f$z=r \beta\f$):
+ *
+ *  \f[
+ *    \frac{\mbox{erf}(r \beta)}{r}
+ *  \f]
+ *
+ * 6. Subtract the result from \f$1/r\f$, multiply by the product of the charges,
+ *    and you have your potential.
+ *
+ * This approximation achieves an error slightly lower than 1e-6
+ * in single precision and 4e-11 in double precision
+ * for arguments smaller than 16 (\f$ 0.15 \leq \beta r \leq 4 \f$);
+ * for \f$ \beta r \leq 0.15\f$ the error can be twice as high;
+ * when added to \f$1/r\f$ the error will be insignificant.
+ * For \f$\beta r \geq 7142\f$ the return value can be inf or NaN.
+ */
+static inline SimdFloat gmx_simdcall pmePotentialCorrection(SimdFloat z2)
+{
+    const SimdFloat VN6(1.9296833005951166339e-8F);
+    const SimdFloat VN5(-1.4213390571557850962e-6F);
+    const SimdFloat VN4(0.000041603292906656984871F);
+    const SimdFloat VN3(-0.00013134036773265025626F);
+    const SimdFloat VN2(0.038657983986041781264F);
+    const SimdFloat VN1(0.11285044772717598220F);
+    const SimdFloat VN0(1.1283802385263030286F);
+
+    const SimdFloat VD3(0.0066752224023576045451F);
+    const SimdFloat VD2(0.078647795836373922256F);
+    const SimdFloat VD1(0.43336185284710920150F);
+    const SimdFloat VD0(1.0F);
+
+    SimdFloat z4;
+    SimdFloat polyVN0, polyVN1, polyVD0, polyVD1;
+
+    z4 = z2 * z2;
+
+    polyVD1 = fma(VD3, z4, VD1);
+    polyVD0 = fma(VD2, z4, VD0);
+    polyVD0 = fma(polyVD1, z2, polyVD0);
+
+    polyVD0 = inv(polyVD0);
+
+    polyVN0 = fma(VN6, z4, VN4);
+    polyVN1 = fma(VN5, z4, VN3);
+    polyVN0 = fma(polyVN0, z4, VN2);
+    polyVN1 = fma(polyVN1, z4, VN1);
+    polyVN0 = fma(polyVN0, z4, VN0);
+    polyVN0 = fma(polyVN1, z2, polyVN0);
+
+    return polyVN0 * polyVD0;
+}
+#    endif
+
+/*! \} */
+
+#    if GMX_SIMD_HAVE_DOUBLE
+
+
+/*! \name Double precision SIMD math functions
+ *
+ *  \note In most cases you should use the real-precision functions instead.
+ *  \{
+ */
+
+/****************************************
+ * DOUBLE PRECISION SIMD MATH FUNCTIONS *
+ ****************************************/
+
+#        if !GMX_SIMD_HAVE_NATIVE_COPYSIGN_DOUBLE
+/*! \brief Composes floating point value with the magnitude of x and the sign of y.
+ *
+ * \param x Values to set sign for
+ * \param y Values used to set sign
+ * \return  Magnitude of x, sign of y
+ */
+static inline SimdDouble gmx_simdcall copysign(SimdDouble x, SimdDouble y)
+{
+#            if GMX_SIMD_HAVE_LOGICAL
+    return abs(x) | (SimdDouble(GMX_DOUBLE_NEGZERO) & y);
+#            else
+    return blend(abs(x), -abs(x), (y < setZero()));
+#            endif
+}
+#        endif
+
+#        if !GMX_SIMD_HAVE_NATIVE_RSQRT_ITER_DOUBLE
+/*! \brief Perform one Newton-Raphson iteration to improve 1/sqrt(x) for SIMD double.
+ *
+ * This is a low-level routine that should only be used by SIMD math routine
+ * that evaluates the inverse square root.
+ *
+ *  \param lu Approximation of 1/sqrt(x), typically obtained from lookup.
+ *  \param x  The reference (starting) value x for which we want 1/sqrt(x).
+ *  \return   An improved approximation with roughly twice as many bits of accuracy.
+ */
+static inline SimdDouble gmx_simdcall rsqrtIter(SimdDouble lu, SimdDouble x)
+{
+    SimdDouble tmp1 = x * lu;
+    SimdDouble tmp2 = SimdDouble(-0.5) * lu;
+    tmp1            = fma(tmp1, lu, SimdDouble(-3.0));
+    return tmp1 * tmp2;
+}
+#        endif
+
+/*! \brief Calculate 1/sqrt(x) for SIMD double.
+ *
+ *  \param x Argument that must be larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *
+ *  \return 1/sqrt(x). Result is undefined if your argument was invalid.
+ */
+static inline SimdDouble gmx_simdcall invsqrt(SimdDouble x)
+{
+    SimdDouble lu = rsqrt(x);
+#        if (GMX_SIMD_RSQRT_BITS < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 2 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 4 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 8 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+    return lu;
+}
+
+/*! \brief Calculate 1/sqrt(x) for two SIMD doubles.
+ *
+ * \param x0  First set of arguments, x0 must be in single range (see below).
+ * \param x1  Second set of arguments, x1 must be in single range (see below).
+ * \param[out] out0  Result 1/sqrt(x0)
+ * \param[out] out1  Result 1/sqrt(x1)
+ *
+ *  In particular for double precision we can sometimes calculate square root
+ *  pairs slightly faster by using single precision until the very last step.
+ *
+ * \note Both arguments must be larger than GMX_FLOAT_MIN and smaller than
+ *       GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *       For the single precision implementation this is obviously always
+ *       true for positive values, but for double precision it adds an
+ *       extra restriction since the first lookup step might have to be
+ *       performed in single precision on some architectures. Note that the
+ *       responsibility for checking falls on you - this routine does not
+ *       check arguments.
+ */
+static inline void gmx_simdcall invsqrtPair(SimdDouble x0, SimdDouble x1, SimdDouble* out0, SimdDouble* out1)
+{
+#        if GMX_SIMD_HAVE_FLOAT && (GMX_SIMD_FLOAT_WIDTH == 2 * GMX_SIMD_DOUBLE_WIDTH) \
+                && (GMX_SIMD_RSQRT_BITS < 22)
+    SimdFloat  xf  = cvtDD2F(x0, x1);
+    SimdFloat  luf = rsqrt(xf);
+    SimdDouble lu0, lu1;
+    // Intermediate target is single - mantissa+1 bits
+#            if (GMX_SIMD_RSQRT_BITS < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    luf = rsqrtIter(luf, xf);
+#            endif
+#            if (GMX_SIMD_RSQRT_BITS * 2 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    luf = rsqrtIter(luf, xf);
+#            endif
+#            if (GMX_SIMD_RSQRT_BITS * 4 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    luf = rsqrtIter(luf, xf);
+#            endif
+    cvtF2DD(luf, &lu0, &lu1);
+    // Last iteration(s) performed in double - if we had 22 bits, this gets us to 44 (~1e-15)
+#            if (GMX_SIMD_ACCURACY_BITS_SINGLE < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu0 = rsqrtIter(lu0, x0);
+    lu1 = rsqrtIter(lu1, x1);
+#            endif
+#            if (GMX_SIMD_ACCURACY_BITS_SINGLE * 2 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu0 = rsqrtIter(lu0, x0);
+    lu1 = rsqrtIter(lu1, x1);
+#            endif
+    *out0 = lu0;
+    *out1 = lu1;
+#        else
+    *out0 = invsqrt(x0);
+    *out1 = invsqrt(x1);
+#        endif
+}
+
+#        if !GMX_SIMD_HAVE_NATIVE_RCP_ITER_DOUBLE
+/*! \brief Perform one Newton-Raphson iteration to improve 1/x for SIMD double.
+ *
+ * This is a low-level routine that should only be used by SIMD math routine
+ * that evaluates the reciprocal.
+ *
+ *  \param lu Approximation of 1/x, typically obtained from lookup.
+ *  \param x  The reference (starting) value x for which we want 1/x.
+ *  \return   An improved approximation with roughly twice as many bits of accuracy.
+ */
+static inline SimdDouble gmx_simdcall rcpIter(SimdDouble lu, SimdDouble x)
+{
+    return lu * fnma(lu, x, SimdDouble(2.0));
+}
+#        endif
+
+/*! \brief Calculate 1/x for SIMD double.
+ *
+ *  \param x Argument with magnitude larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *
+ *  \return 1/x. Result is undefined if your argument was invalid.
+ */
+static inline SimdDouble gmx_simdcall inv(SimdDouble x)
+{
+    SimdDouble lu = rcp(x);
+#        if (GMX_SIMD_RCP_BITS < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 2 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 4 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 8 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rcpIter(lu, x);
+#        endif
+    return lu;
+}
+
+/*! \brief Division for SIMD doubles
+ *
+ * \param nom    Nominator
+ * \param denom  Denominator, with magnitude in range (GMX_FLOAT_MIN,GMX_FLOAT_MAX).
+ *               For single precision this is equivalent to a nonzero argument,
+ *               but in double precision it adds an extra restriction since
+ *               the first lookup step might have to be performed in single
+ *               precision on some architectures. Note that the responsibility
+ *               for checking falls on you - this routine does not check arguments.
+ *
+ * \return nom/denom
+ *
+ * \note This function does not use any masking to avoid problems with
+ *       zero values in the denominator.
+ */
+static inline SimdDouble gmx_simdcall operator/(SimdDouble nom, SimdDouble denom)
+{
+    return nom * inv(denom);
+}
+
+
+/*! \brief Calculate 1/sqrt(x) for masked entries of SIMD double.
+ *
+ *  This routine only evaluates 1/sqrt(x) for elements for which mask is true.
+ *  Illegal values in the masked-out elements will not lead to
+ *  floating-point exceptions.
+ *
+ *  \param x Argument that must be larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX for masked-in entries.
+ *           See \ref invsqrt for the discussion about argument restrictions.
+ *  \param m Mask
+ *  \return 1/sqrt(x). Result is undefined if your argument was invalid or
+ *          entry was not masked, and 0.0 for masked-out entries.
+ */
+static inline SimdDouble maskzInvsqrt(SimdDouble x, SimdDBool m)
+{
+    SimdDouble lu = maskzRsqrt(x, m);
+#        if (GMX_SIMD_RSQRT_BITS < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 2 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 4 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 8 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+    return lu;
+}
+
+/*! \brief Calculate 1/x for SIMD double, masked version.
+ *
+ *  \param x Argument with magnitude larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX for masked-in entries.
+ *           See \ref invsqrt for the discussion about argument restrictions.
+ *  \param m Mask
+ *  \return 1/x for elements where m is true, or 0.0 for masked-out entries.
+ */
+static inline SimdDouble gmx_simdcall maskzInv(SimdDouble x, SimdDBool m)
+{
+    SimdDouble lu = maskzRcp(x, m);
+#        if (GMX_SIMD_RCP_BITS < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 2 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 4 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 8 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rcpIter(lu, x);
+#        endif
+    return lu;
+}
+
+
+/*! \brief Calculate sqrt(x) for SIMD doubles.
+ *
+ *  \copydetails sqrt(SimdFloat)
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall sqrt(SimdDouble x)
+{
+    if (opt == MathOptimization::Safe)
+    {
+        // As we might use a float version of rsqrt, we mask out small values
+        SimdDouble res = maskzInvsqrt(x, SimdDouble(GMX_FLOAT_MIN) < x);
+        return res * x;
+    }
+    else
+    {
+        return x * invsqrt(x);
+    }
+}
+
+/*! \brief Cube root for SIMD doubles
+ *
+ * \param x      Argument to calculate cube root of. Can be negative or zero,
+ *               but NaN or Inf values are not supported. Denormal values will
+ *               be treated as 0.0.
+ * \return       Cube root of x.
+ */
+static inline SimdDouble gmx_simdcall cbrt(SimdDouble x)
+{
+    const SimdDouble signBit(GMX_DOUBLE_NEGZERO);
+    const SimdDouble minDouble(std::numeric_limits<double>::min());
+    // Bias is 1024-1 = 1023, which is divisible by 3, so no need to change it more.
+    // To avoid clang warnings about fragile integer division mixed with FP, we let
+    // the divided value (1023/3=341) be the original constant.
+    const std::int32_t offsetDiv3(341);
+    const SimdDouble   c6(-0.145263899385486377);
+    const SimdDouble   c5(0.784932344976639262);
+    const SimdDouble   c4(-1.83469277483613086);
+    const SimdDouble   c3(2.44693122563534430);
+    const SimdDouble   c2(-2.11499494167371287);
+    const SimdDouble   c1(1.50819193781584896);
+    const SimdDouble   c0(0.354895765043919860);
+    const SimdDouble   one(1.0);
+    const SimdDouble   two(2.0);
+    const SimdDouble   three(3.0);
+    const SimdDouble   oneThird(1.0 / 3.0);
+    const SimdDouble   cbrt2(1.2599210498948731648);
+    const SimdDouble   sqrCbrt2(1.5874010519681994748);
+
+    // See the single precision routines for documentation of the algorithm
+
+    SimdDouble xSignBit = x & signBit; // create bit mask where the sign bit is 1 for x elements < 0
+    SimdDouble xAbs     = andNot(signBit, x);    // select everthing but the sign bit => abs(x)
+    SimdDBool  xIsNonZero = (minDouble <= xAbs); // treat denormals as 0
+
+    SimdDInt32 exponent;
+    SimdDouble y        = frexp(xAbs, &exponent);
+    SimdDouble z        = fma(y, c6, c5);
+    z                   = fma(z, y, c4);
+    z                   = fma(z, y, c3);
+    z                   = fma(z, y, c2);
+    z                   = fma(z, y, c1);
+    z                   = fma(z, y, c0);
+    SimdDouble w        = z * z * z;
+    SimdDouble nom      = z * fma(two, y, w);
+    SimdDouble invDenom = inv(fma(two, w, y));
+
+    SimdDouble offsetExp = cvtI2R(exponent) + SimdDouble(static_cast<double>(3 * offsetDiv3) + 0.1);
+    SimdDouble offsetExpDiv3 =
+            trunc(offsetExp * oneThird); // important to truncate here to mimic integer division
+    SimdDInt32 expDiv3   = cvtR2I(offsetExpDiv3 - SimdDouble(static_cast<double>(offsetDiv3)));
+    SimdDouble remainder = offsetExp - offsetExpDiv3 * three;
+    SimdDouble factor    = blend(one, cbrt2, SimdDouble(0.5) < remainder);
+    factor               = blend(factor, sqrCbrt2, SimdDouble(1.5) < remainder);
+    SimdDouble fraction  = (nom * invDenom * factor) ^ xSignBit;
+    SimdDouble result    = selectByMask(ldexp(fraction, expDiv3), xIsNonZero);
+    return result;
+}
+
+/*! \brief Inverse cube root for SIMD doubles.
+ *
+ * \param x      Argument to calculate cube root of. Can be positive or
+ *               negative, but the magnitude cannot be lower than
+ *               the smallest normal number.
+ * \return       Cube root of x. Undefined for values that don't
+ *               fulfill the restriction of abs(x) > minDouble.
+ */
+static inline SimdDouble gmx_simdcall invcbrt(SimdDouble x)
+{
+    const SimdDouble signBit(GMX_DOUBLE_NEGZERO);
+    // Bias is 1024-1 = 1023, which is divisible by 3, so no need to change it more.
+    // To avoid clang warnings about fragile integer division mixed with FP, we let
+    // the divided value (1023/3=341) be the original constant.
+    const std::int32_t offsetDiv3(341);
+    const SimdDouble   c6(-0.145263899385486377);
+    const SimdDouble   c5(0.784932344976639262);
+    const SimdDouble   c4(-1.83469277483613086);
+    const SimdDouble   c3(2.44693122563534430);
+    const SimdDouble   c2(-2.11499494167371287);
+    const SimdDouble   c1(1.50819193781584896);
+    const SimdDouble   c0(0.354895765043919860);
+    const SimdDouble   one(1.0);
+    const SimdDouble   two(2.0);
+    const SimdDouble   three(3.0);
+    const SimdDouble   oneThird(1.0 / 3.0);
+    const SimdDouble   invCbrt2(1.0 / 1.2599210498948731648);
+    const SimdDouble   invSqrCbrt2(1.0F / 1.5874010519681994748);
+
+    // See the single precision routines for documentation of the algorithm
+
+    SimdDouble xSignBit = x & signBit; // create bit mask where the sign bit is 1 for x elements < 0
+    SimdDouble xAbs     = andNot(signBit, x); // select everthing but the sign bit => abs(x)
+
+    SimdDInt32 exponent;
+    SimdDouble y         = frexp(xAbs, &exponent);
+    SimdDouble z         = fma(y, c6, c5);
+    z                    = fma(z, y, c4);
+    z                    = fma(z, y, c3);
+    z                    = fma(z, y, c2);
+    z                    = fma(z, y, c1);
+    z                    = fma(z, y, c0);
+    SimdDouble w         = z * z * z;
+    SimdDouble nom       = fma(two, w, y);
+    SimdDouble invDenom  = inv(z * fma(two, y, w));
+    SimdDouble offsetExp = cvtI2R(exponent) + SimdDouble(static_cast<double>(3 * offsetDiv3) + 0.1);
+    SimdDouble offsetExpDiv3 =
+            trunc(offsetExp * oneThird); // important to truncate here to mimic integer division
+    SimdDInt32 expDiv3   = cvtR2I(SimdDouble(static_cast<double>(offsetDiv3)) - offsetExpDiv3);
+    SimdDouble remainder = offsetExpDiv3 * three - offsetExp;
+    SimdDouble factor    = blend(one, invCbrt2, remainder < SimdDouble(-0.5));
+    factor               = blend(factor, invSqrCbrt2, remainder < SimdDouble(-1.5));
+    SimdDouble fraction  = (nom * invDenom * factor) ^ xSignBit;
+    SimdDouble result    = ldexp(fraction, expDiv3);
+    return result;
+}
+
+/*! \brief SIMD double log2(x). This is the base-2 logarithm.
+ *
+ * \param x Argument, should be >0.
+ * \result The base-2 logarithm of x. Undefined if argument is invalid.
+ */
+static inline SimdDouble gmx_simdcall log2(SimdDouble x)
+{
+#        if GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE
+    // Just rescale if native log2() is not present, but log is.
+    return log(x) * SimdDouble(std::log2(std::exp(1.0)));
+#        else
+    const SimdDouble one(1.0);
+    const SimdDouble two(2.0);
+    const SimdDouble invsqrt2(1.0 / std::sqrt(2.0));
+    const SimdDouble CL15(0.2138031565795550370534528);
+    const SimdDouble CL13(0.2208884091496370882801159);
+    const SimdDouble CL11(0.2623358279761824340958754);
+    const SimdDouble CL9(0.3205984930182496084327681);
+    const SimdDouble CL7(0.4121985864521960363227038);
+    const SimdDouble CL5(0.5770780163410746954610886);
+    const SimdDouble CL3(0.9617966939260027547931031);
+    const SimdDouble CL1(2.885390081777926774009302);
+    SimdDouble       fExp, x2, p;
+    SimdDBool        m;
+    SimdDInt32       iExp;
+
+    // For log2(), the argument cannot be 0, so use the faster version of frexp()
+    x    = frexp<MathOptimization::Unsafe>(x, &iExp);
+    fExp = cvtI2R(iExp);
+
+    m = x < invsqrt2;
+    // Adjust to non-IEEE format for x<1/sqrt(2): exponent -= 1, mantissa *= 2.0
+    fExp = fExp - selectByMask(one, m);
+    x    = x * blend(one, two, m);
+
+    x  = (x - one) * inv(x + one);
+    x2 = x * x;
+
+    p = fma(CL15, x2, CL13);
+    p = fma(p, x2, CL11);
+    p = fma(p, x2, CL9);
+    p = fma(p, x2, CL7);
+    p = fma(p, x2, CL5);
+    p = fma(p, x2, CL3);
+    p = fma(p, x2, CL1);
+    p = fma(p, x, fExp);
+
+    return p;
+#        endif
+}
+
+#        if !GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE
+/*! \brief SIMD double log(x). This is the natural logarithm.
+ *
+ * \param x Argument, should be >0.
+ * \result The natural logarithm of x. Undefined if argument is invalid.
+ */
+static inline SimdDouble gmx_simdcall log(SimdDouble x)
+{
+    const SimdDouble one(1.0);
+    const SimdDouble two(2.0);
+    const SimdDouble invsqrt2(1.0 / std::sqrt(2.0));
+    const SimdDouble corr(0.693147180559945286226764);
+    const SimdDouble CL15(0.148197055177935105296783);
+    const SimdDouble CL13(0.153108178020442575739679);
+    const SimdDouble CL11(0.181837339521549679055568);
+    const SimdDouble CL9(0.22222194152736701733275);
+    const SimdDouble CL7(0.285714288030134544449368);
+    const SimdDouble CL5(0.399999999989941956712869);
+    const SimdDouble CL3(0.666666666666685503450651);
+    const SimdDouble CL1(2.0);
+    SimdDouble       fExp, x2, p;
+    SimdDBool        m;
+    SimdDInt32       iExp;
+
+    // For log(), the argument cannot be 0, so use the faster version of frexp()
+    x    = frexp<MathOptimization::Unsafe>(x, &iExp);
+    fExp = cvtI2R(iExp);
+
+    m = x < invsqrt2;
+    // Adjust to non-IEEE format for x<1/sqrt(2): exponent -= 1, mantissa *= 2.0
+    fExp = fExp - selectByMask(one, m);
+    x    = x * blend(one, two, m);
+
+    x  = (x - one) * inv(x + one);
+    x2 = x * x;
+
+    p = fma(CL15, x2, CL13);
+    p = fma(p, x2, CL11);
+    p = fma(p, x2, CL9);
+    p = fma(p, x2, CL7);
+    p = fma(p, x2, CL5);
+    p = fma(p, x2, CL3);
+    p = fma(p, x2, CL1);
+    p = fma(p, x, corr * fExp);
+
+    return p;
+}
+#        endif
+
+#        if !GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE
+/*! \brief SIMD double 2^x.
+ *
+ * \copydetails exp2(SimdFloat)
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall exp2(SimdDouble x)
+{
+    const SimdDouble CE11(4.435280790452730022081181e-10);
+    const SimdDouble CE10(7.074105630863314448024247e-09);
+    const SimdDouble CE9(1.017819803432096698472621e-07);
+    const SimdDouble CE8(1.321543308956718799557863e-06);
+    const SimdDouble CE7(0.00001525273348995851746990884);
+    const SimdDouble CE6(0.0001540353046251466849082632);
+    const SimdDouble CE5(0.001333355814678995257307880);
+    const SimdDouble CE4(0.009618129107588335039176502);
+    const SimdDouble CE3(0.05550410866481992147457793);
+    const SimdDouble CE2(0.2402265069591015620470894);
+    const SimdDouble CE1(0.6931471805599453304615075);
+    const SimdDouble one(1.0);
+
+    SimdDouble intpart;
+    SimdDouble fexppart;
+    SimdDouble p;
+
+    // Large negative values are valid arguments to exp2(), so there are two
+    // things we need to account for:
+    // 1. When the exponents reaches -1023, the (biased) exponent field will be
+    //    zero and we can no longer multiply with it. There are special IEEE
+    //    formats to handle this range, but for now we have to accept that
+    //    we cannot handle those arguments. If input value becomes even more
+    //    negative, it will start to loop and we would end up with invalid
+    //    exponents. Thus, we need to limit or mask this.
+    // 2. For VERY large negative values, we will have problems that the
+    //    subtraction to get the fractional part loses accuracy, and then we
+    //    can end up with overflows in the polynomial.
+    //
+    // For now, we handle this by forwarding the math optimization setting to
+    // ldexp, where the routine will return zero for very small arguments.
+    //
+    // However, before doing that we need to make sure we do not call cvtR2I
+    // with an argument that is so negative it cannot be converted to an integer.
+    if (opt == MathOptimization::Safe)
+    {
+        x = max(x, SimdDouble(std::numeric_limits<std::int32_t>::lowest()));
+    }
+
+    fexppart = ldexp<opt>(one, cvtR2I(x));
+    intpart  = round(x);
+    x        = x - intpart;
+
+    p = fma(CE11, x, CE10);
+    p = fma(p, x, CE9);
+    p = fma(p, x, CE8);
+    p = fma(p, x, CE7);
+    p = fma(p, x, CE6);
+    p = fma(p, x, CE5);
+    p = fma(p, x, CE4);
+    p = fma(p, x, CE3);
+    p = fma(p, x, CE2);
+    p = fma(p, x, CE1);
+    p = fma(p, x, one);
+    x = p * fexppart;
+    return x;
+}
+#        endif
+
+#        if !GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE
+/*! \brief SIMD double exp(x).
+ *
+ * \copydetails exp(SimdFloat)
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall exp(SimdDouble x)
+{
+    const SimdDouble argscale(1.44269504088896340735992468100);
+    const SimdDouble invargscale0(-0.69314718055966295651160180568695068359375);
+    const SimdDouble invargscale1(-2.8235290563031577122588448175013436025525412068e-13);
+    const SimdDouble CE12(2.078375306791423699350304e-09);
+    const SimdDouble CE11(2.518173854179933105218635e-08);
+    const SimdDouble CE10(2.755842049600488770111608e-07);
+    const SimdDouble CE9(2.755691815216689746619849e-06);
+    const SimdDouble CE8(2.480158383706245033920920e-05);
+    const SimdDouble CE7(0.0001984127043518048611841321);
+    const SimdDouble CE6(0.001388888889360258341755930);
+    const SimdDouble CE5(0.008333333332907368102819109);
+    const SimdDouble CE4(0.04166666666663836745814631);
+    const SimdDouble CE3(0.1666666666666796929434570);
+    const SimdDouble CE2(0.5);
+    const SimdDouble one(1.0);
+    SimdDouble       fexppart;
+    SimdDouble       intpart;
+    SimdDouble       y, p;
+
+    // Large negative values are valid arguments to exp2(), so there are two
+    // things we need to account for:
+    // 1. When the exponents reaches -1023, the (biased) exponent field will be
+    //    zero and we can no longer multiply with it. There are special IEEE
+    //    formats to handle this range, but for now we have to accept that
+    //    we cannot handle those arguments. If input value becomes even more
+    //    negative, it will start to loop and we would end up with invalid
+    //    exponents. Thus, we need to limit or mask this.
+    // 2. For VERY large negative values, we will have problems that the
+    //    subtraction to get the fractional part loses accuracy, and then we
+    //    can end up with overflows in the polynomial.
+    //
+    // For now, we handle this by forwarding the math optimization setting to
+    // ldexp, where the routine will return zero for very small arguments.
+    //
+    // However, before doing that we need to make sure we do not call cvtR2I
+    // with an argument that is so negative it cannot be converted to an integer
+    // after being multiplied by argscale.
+
+    if (opt == MathOptimization::Safe)
+    {
+        x = max(x, SimdDouble(std::numeric_limits<std::int32_t>::lowest()) / argscale);
+    }
+
+    y = x * argscale;
+
+    fexppart = ldexp<opt>(one, cvtR2I(y));
+    intpart  = round(y);
+
+    // Extended precision arithmetics
+    x = fma(invargscale0, intpart, x);
+    x = fma(invargscale1, intpart, x);
+
+    p = fma(CE12, x, CE11);
+    p = fma(p, x, CE10);
+    p = fma(p, x, CE9);
+    p = fma(p, x, CE8);
+    p = fma(p, x, CE7);
+    p = fma(p, x, CE6);
+    p = fma(p, x, CE5);
+    p = fma(p, x, CE4);
+    p = fma(p, x, CE3);
+    p = fma(p, x, CE2);
+    p = fma(p, x * x, x);
+#            if GMX_SIMD_HAVE_FMA
+    x = fma(p, fexppart, fexppart);
+#            else
+    x = (p + one) * fexppart;
+#            endif
+
+    return x;
+}
+#        endif
+
+/*! \brief SIMD double pow(x,y)
+ *
+ * This returns x^y for SIMD values.
+ *
+ * \tparam opt If this is changed from the default (safe) into the unsafe
+ *             option, there are no guarantees about correct results for x==0.
+ *
+ * \param x Base.
+ *
+ * \param y exponent.
+ *
+ * \result x^y. Overflowing arguments are likely to either return 0 or inf,
+ *         depending on the underlying implementation. If unsafe optimizations
+ *         are enabled, this is also true for x==0.
+ *
+ * \warning You cannot rely on this implementation returning inf for arguments
+ *          that cause overflow. If you have some very large
+ *          values and need to rely on getting a valid numerical output,
+ *          take the minimum of your variable and the largest valid argument
+ *          before calling this routine.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall pow(SimdDouble x, SimdDouble y)
+{
+    SimdDouble xcorr;
+
+    if (opt == MathOptimization::Safe)
+    {
+        xcorr = max(x, SimdDouble(std::numeric_limits<double>::min()));
+    }
+    else
+    {
+        xcorr = x;
+    }
+
+    SimdDouble result = exp2<opt>(y * log2(xcorr));
+
+    if (opt == MathOptimization::Safe)
+    {
+        // if x==0 and y>0 we explicitly set the result to 0.0
+        // For any x with y==0, the result will already be 1.0 since we multiply by y (0.0) and call exp().
+        result = blend(result, setZero(), x == setZero() && setZero() < y);
+    }
+
+    return result;
+}
+
+
+/*! \brief SIMD double erf(x).
+ *
+ * \param x The value to calculate erf(x) for.
+ * \result erf(x)
+ *
+ * This routine achieves very close to full precision, but we do not care about
+ * the last bit or the subnormal result range.
+ */
+static inline SimdDouble gmx_simdcall erf(SimdDouble x)
+{
+    // Coefficients for minimax approximation of erf(x)=x*(CAoffset + P(x^2)/Q(x^2)) in range [-0.75,0.75]
+    const SimdDouble CAP4(-0.431780540597889301512e-4);
+    const SimdDouble CAP3(-0.00578562306260059236059);
+    const SimdDouble CAP2(-0.028593586920219752446);
+    const SimdDouble CAP1(-0.315924962948621698209);
+    const SimdDouble CAP0(0.14952975608477029151);
+
+    const SimdDouble CAQ5(-0.374089300177174709737e-5);
+    const SimdDouble CAQ4(0.00015126584532155383535);
+    const SimdDouble CAQ3(0.00536692680669480725423);
+    const SimdDouble CAQ2(0.0668686825594046122636);
+    const SimdDouble CAQ1(0.402604990869284362773);
+    // CAQ0 == 1.0
+    const SimdDouble CAoffset(0.9788494110107421875);
+
+    // Coefficients for minimax approximation of erfc(x)=exp(-x^2)*x*(P(x-1)/Q(x-1)) in range [1.0,4.5]
+    const SimdDouble CBP6(2.49650423685462752497647637088e-10);
+    const SimdDouble CBP5(0.00119770193298159629350136085658);
+    const SimdDouble CBP4(0.0164944422378370965881008942733);
+    const SimdDouble CBP3(0.0984581468691775932063932439252);
+    const SimdDouble CBP2(0.317364595806937763843589437418);
+    const SimdDouble CBP1(0.554167062641455850932670067075);
+    const SimdDouble CBP0(0.427583576155807163756925301060);
+    const SimdDouble CBQ7(0.00212288829699830145976198384930);
+    const SimdDouble CBQ6(0.0334810979522685300554606393425);
+    const SimdDouble CBQ5(0.2361713785181450957579508850717);
+    const SimdDouble CBQ4(0.955364736493055670530981883072);
+    const SimdDouble CBQ3(2.36815675631420037315349279199);
+    const SimdDouble CBQ2(3.55261649184083035537184223542);
+    const SimdDouble CBQ1(2.93501136050160872574376997993);
+    // CBQ0 == 1.0
+
+    // Coefficients for minimax approximation of erfc(x)=exp(-x^2)/x*(P(1/x)/Q(1/x)) in range [4.5,inf]
+    const SimdDouble CCP6(-2.8175401114513378771);
+    const SimdDouble CCP5(-3.22729451764143718517);
+    const SimdDouble CCP4(-2.5518551727311523996);
+    const SimdDouble CCP3(-0.687717681153649930619);
+    const SimdDouble CCP2(-0.212652252872804219852);
+    const SimdDouble CCP1(0.0175389834052493308818);
+    const SimdDouble CCP0(0.00628057170626964891937);
+
+    const SimdDouble CCQ6(5.48409182238641741584);
+    const SimdDouble CCQ5(13.5064170191802889145);
+    const SimdDouble CCQ4(22.9367376522880577224);
+    const SimdDouble CCQ3(15.930646027911794143);
+    const SimdDouble CCQ2(11.0567237927800161565);
+    const SimdDouble CCQ1(2.79257750980575282228);
+    // CCQ0 == 1.0
+    const SimdDouble CCoffset(0.5579090118408203125);
+
+    const SimdDouble one(1.0);
+    const SimdDouble two(2.0);
+    const SimdDouble minFloat(std::numeric_limits<float>::min());
+
+    SimdDouble xabs, x2, x4, t, t2, w, w2;
+    SimdDouble PolyAP0, PolyAP1, PolyAQ0, PolyAQ1;
+    SimdDouble PolyBP0, PolyBP1, PolyBQ0, PolyBQ1;
+    SimdDouble PolyCP0, PolyCP1, PolyCQ0, PolyCQ1;
+    SimdDouble res_erf, res_erfcB, res_erfcC, res_erfc, res;
+    SimdDouble expmx2;
+    SimdDBool  mask, mask_erf, notmask_erf;
+
+    // Calculate erf()
+    xabs        = abs(x);
+    mask_erf    = (xabs < one);
+    notmask_erf = (one <= xabs);
+    x2          = x * x;
+    x4          = x2 * x2;
+
+    PolyAP0 = fma(CAP4, x4, CAP2);
+    PolyAP1 = fma(CAP3, x4, CAP1);
+    PolyAP0 = fma(PolyAP0, x4, CAP0);
+    PolyAP0 = fma(PolyAP1, x2, PolyAP0);
+
+    PolyAQ1 = fma(CAQ5, x4, CAQ3);
+    PolyAQ0 = fma(CAQ4, x4, CAQ2);
+    PolyAQ1 = fma(PolyAQ1, x4, CAQ1);
+    PolyAQ0 = fma(PolyAQ0, x4, one);
+    PolyAQ0 = fma(PolyAQ1, x2, PolyAQ0);
+
+    res_erf = PolyAP0 * maskzInv(PolyAQ0, mask_erf && (minFloat <= abs(PolyAQ0)));
+    res_erf = CAoffset + res_erf;
+    res_erf = x * res_erf;
+
+    // Calculate erfc() in range [1,4.5]
+    t  = xabs - one;
+    t2 = t * t;
+
+    PolyBP0 = fma(CBP6, t2, CBP4);
+    PolyBP1 = fma(CBP5, t2, CBP3);
+    PolyBP0 = fma(PolyBP0, t2, CBP2);
+    PolyBP1 = fma(PolyBP1, t2, CBP1);
+    PolyBP0 = fma(PolyBP0, t2, CBP0);
+    PolyBP0 = fma(PolyBP1, t, PolyBP0);
+
+    PolyBQ1 = fma(CBQ7, t2, CBQ5);
+    PolyBQ0 = fma(CBQ6, t2, CBQ4);
+    PolyBQ1 = fma(PolyBQ1, t2, CBQ3);
+    PolyBQ0 = fma(PolyBQ0, t2, CBQ2);
+    PolyBQ1 = fma(PolyBQ1, t2, CBQ1);
+    PolyBQ0 = fma(PolyBQ0, t2, one);
+    PolyBQ0 = fma(PolyBQ1, t, PolyBQ0);
+
+    // The denominator polynomial can be zero outside the range
+    res_erfcB = PolyBP0 * maskzInv(PolyBQ0, notmask_erf && (minFloat <= abs(PolyBQ0)));
+
+    res_erfcB = res_erfcB * xabs;
+
+    // Calculate erfc() in range [4.5,inf]
+    w  = maskzInv(xabs, notmask_erf && (minFloat <= xabs));
+    w2 = w * w;
+
+    PolyCP0 = fma(CCP6, w2, CCP4);
+    PolyCP1 = fma(CCP5, w2, CCP3);
+    PolyCP0 = fma(PolyCP0, w2, CCP2);
+    PolyCP1 = fma(PolyCP1, w2, CCP1);
+    PolyCP0 = fma(PolyCP0, w2, CCP0);
+    PolyCP0 = fma(PolyCP1, w, PolyCP0);
+
+    PolyCQ0 = fma(CCQ6, w2, CCQ4);
+    PolyCQ1 = fma(CCQ5, w2, CCQ3);
+    PolyCQ0 = fma(PolyCQ0, w2, CCQ2);
+    PolyCQ1 = fma(PolyCQ1, w2, CCQ1);
+    PolyCQ0 = fma(PolyCQ0, w2, one);
+    PolyCQ0 = fma(PolyCQ1, w, PolyCQ0);
+
+    expmx2 = exp(-x2);
+
+    // The denominator polynomial can be zero outside the range
+    res_erfcC = PolyCP0 * maskzInv(PolyCQ0, notmask_erf && (minFloat <= abs(PolyCQ0)));
+    res_erfcC = res_erfcC + CCoffset;
+    res_erfcC = res_erfcC * w;
+
+    mask     = (SimdDouble(4.5) < xabs);
+    res_erfc = blend(res_erfcB, res_erfcC, mask);
+
+    res_erfc = res_erfc * expmx2;
+
+    // erfc(x<0) = 2-erfc(|x|)
+    mask     = (x < setZero());
+    res_erfc = blend(res_erfc, two - res_erfc, mask);
+
+    // Select erf() or erfc()
+    res = blend(one - res_erfc, res_erf, mask_erf);
+
+    return res;
+}
+
+/*! \brief SIMD double erfc(x).
+ *
+ * \param x The value to calculate erfc(x) for.
+ * \result erfc(x)
+ *
+ * This routine achieves full precision (bar the last bit) over most of the
+ * input range, but for large arguments where the result is getting close
+ * to the minimum representable numbers we accept slightly larger errors
+ * (think results that are in the ballpark of 10^-200 for double)
+ * since that is not relevant for MD.
+ */
+static inline SimdDouble gmx_simdcall erfc(SimdDouble x)
+{
+    // Coefficients for minimax approximation of erf(x)=x*(CAoffset + P(x^2)/Q(x^2)) in range [-0.75,0.75]
+    const SimdDouble CAP4(-0.431780540597889301512e-4);
+    const SimdDouble CAP3(-0.00578562306260059236059);
+    const SimdDouble CAP2(-0.028593586920219752446);
+    const SimdDouble CAP1(-0.315924962948621698209);
+    const SimdDouble CAP0(0.14952975608477029151);
+
+    const SimdDouble CAQ5(-0.374089300177174709737e-5);
+    const SimdDouble CAQ4(0.00015126584532155383535);
+    const SimdDouble CAQ3(0.00536692680669480725423);
+    const SimdDouble CAQ2(0.0668686825594046122636);
+    const SimdDouble CAQ1(0.402604990869284362773);
+    // CAQ0 == 1.0
+    const SimdDouble CAoffset(0.9788494110107421875);
+
+    // Coefficients for minimax approximation of erfc(x)=exp(-x^2)*x*(P(x-1)/Q(x-1)) in range [1.0,4.5]
+    const SimdDouble CBP6(2.49650423685462752497647637088e-10);
+    const SimdDouble CBP5(0.00119770193298159629350136085658);
+    const SimdDouble CBP4(0.0164944422378370965881008942733);
+    const SimdDouble CBP3(0.0984581468691775932063932439252);
+    const SimdDouble CBP2(0.317364595806937763843589437418);
+    const SimdDouble CBP1(0.554167062641455850932670067075);
+    const SimdDouble CBP0(0.427583576155807163756925301060);
+    const SimdDouble CBQ7(0.00212288829699830145976198384930);
+    const SimdDouble CBQ6(0.0334810979522685300554606393425);
+    const SimdDouble CBQ5(0.2361713785181450957579508850717);
+    const SimdDouble CBQ4(0.955364736493055670530981883072);
+    const SimdDouble CBQ3(2.36815675631420037315349279199);
+    const SimdDouble CBQ2(3.55261649184083035537184223542);
+    const SimdDouble CBQ1(2.93501136050160872574376997993);
+    // CBQ0 == 1.0
+
+    // Coefficients for minimax approximation of erfc(x)=exp(-x^2)/x*(P(1/x)/Q(1/x)) in range [4.5,inf]
+    const SimdDouble CCP6(-2.8175401114513378771);
+    const SimdDouble CCP5(-3.22729451764143718517);
+    const SimdDouble CCP4(-2.5518551727311523996);
+    const SimdDouble CCP3(-0.687717681153649930619);
+    const SimdDouble CCP2(-0.212652252872804219852);
+    const SimdDouble CCP1(0.0175389834052493308818);
+    const SimdDouble CCP0(0.00628057170626964891937);
+
+    const SimdDouble CCQ6(5.48409182238641741584);
+    const SimdDouble CCQ5(13.5064170191802889145);
+    const SimdDouble CCQ4(22.9367376522880577224);
+    const SimdDouble CCQ3(15.930646027911794143);
+    const SimdDouble CCQ2(11.0567237927800161565);
+    const SimdDouble CCQ1(2.79257750980575282228);
+    // CCQ0 == 1.0
+    const SimdDouble CCoffset(0.5579090118408203125);
+
+    const SimdDouble one(1.0);
+    const SimdDouble two(2.0);
+    const SimdDouble minFloat(std::numeric_limits<float>::min());
+
+    SimdDouble xabs, x2, x4, t, t2, w, w2;
+    SimdDouble PolyAP0, PolyAP1, PolyAQ0, PolyAQ1;
+    SimdDouble PolyBP0, PolyBP1, PolyBQ0, PolyBQ1;
+    SimdDouble PolyCP0, PolyCP1, PolyCQ0, PolyCQ1;
+    SimdDouble res_erf, res_erfcB, res_erfcC, res_erfc, res;
+    SimdDouble expmx2;
+    SimdDBool  mask, mask_erf, notmask_erf;
+
+    // Calculate erf()
+    xabs        = abs(x);
+    mask_erf    = (xabs < one);
+    notmask_erf = (one <= xabs);
+    x2          = x * x;
+    x4          = x2 * x2;
+
+    PolyAP0 = fma(CAP4, x4, CAP2);
+    PolyAP1 = fma(CAP3, x4, CAP1);
+    PolyAP0 = fma(PolyAP0, x4, CAP0);
+    PolyAP0 = fma(PolyAP1, x2, PolyAP0);
+    PolyAQ1 = fma(CAQ5, x4, CAQ3);
+    PolyAQ0 = fma(CAQ4, x4, CAQ2);
+    PolyAQ1 = fma(PolyAQ1, x4, CAQ1);
+    PolyAQ0 = fma(PolyAQ0, x4, one);
+    PolyAQ0 = fma(PolyAQ1, x2, PolyAQ0);
+
+    res_erf = PolyAP0 * maskzInv(PolyAQ0, mask_erf && (minFloat <= abs(PolyAQ0)));
+    res_erf = CAoffset + res_erf;
+    res_erf = x * res_erf;
+
+    // Calculate erfc() in range [1,4.5]
+    t  = xabs - one;
+    t2 = t * t;
+
+    PolyBP0 = fma(CBP6, t2, CBP4);
+    PolyBP1 = fma(CBP5, t2, CBP3);
+    PolyBP0 = fma(PolyBP0, t2, CBP2);
+    PolyBP1 = fma(PolyBP1, t2, CBP1);
+    PolyBP0 = fma(PolyBP0, t2, CBP0);
+    PolyBP0 = fma(PolyBP1, t, PolyBP0);
+
+    PolyBQ1 = fma(CBQ7, t2, CBQ5);
+    PolyBQ0 = fma(CBQ6, t2, CBQ4);
+    PolyBQ1 = fma(PolyBQ1, t2, CBQ3);
+    PolyBQ0 = fma(PolyBQ0, t2, CBQ2);
+    PolyBQ1 = fma(PolyBQ1, t2, CBQ1);
+    PolyBQ0 = fma(PolyBQ0, t2, one);
+    PolyBQ0 = fma(PolyBQ1, t, PolyBQ0);
+
+    // The denominator polynomial can be zero outside the range
+    res_erfcB = PolyBP0 * maskzInv(PolyBQ0, notmask_erf && (minFloat <= abs(PolyBQ0)));
+
+    res_erfcB = res_erfcB * xabs;
+
+    // Calculate erfc() in range [4.5,inf]
+    // Note that 1/x can only handle single precision!
+    w  = maskzInv(xabs, minFloat <= xabs);
+    w2 = w * w;
+
+    PolyCP0 = fma(CCP6, w2, CCP4);
+    PolyCP1 = fma(CCP5, w2, CCP3);
+    PolyCP0 = fma(PolyCP0, w2, CCP2);
+    PolyCP1 = fma(PolyCP1, w2, CCP1);
+    PolyCP0 = fma(PolyCP0, w2, CCP0);
+    PolyCP0 = fma(PolyCP1, w, PolyCP0);
+
+    PolyCQ0 = fma(CCQ6, w2, CCQ4);
+    PolyCQ1 = fma(CCQ5, w2, CCQ3);
+    PolyCQ0 = fma(PolyCQ0, w2, CCQ2);
+    PolyCQ1 = fma(PolyCQ1, w2, CCQ1);
+    PolyCQ0 = fma(PolyCQ0, w2, one);
+    PolyCQ0 = fma(PolyCQ1, w, PolyCQ0);
+
+    expmx2 = exp(-x2);
+
+    // The denominator polynomial can be zero outside the range
+    res_erfcC = PolyCP0 * maskzInv(PolyCQ0, notmask_erf && (minFloat <= abs(PolyCQ0)));
+    res_erfcC = res_erfcC + CCoffset;
+    res_erfcC = res_erfcC * w;
+
+    mask     = (SimdDouble(4.5) < xabs);
+    res_erfc = blend(res_erfcB, res_erfcC, mask);
+
+    res_erfc = res_erfc * expmx2;
+
+    // erfc(x<0) = 2-erfc(|x|)
+    mask     = (x < setZero());
+    res_erfc = blend(res_erfc, two - res_erfc, mask);
+
+    // Select erf() or erfc()
+    res = blend(res_erfc, one - res_erf, mask_erf);
+
+    return res;
+}
+
+/*! \brief SIMD double sin \& cos.
+ *
+ * \param x The argument to evaluate sin/cos for
+ * \param[out] sinval Sin(x)
+ * \param[out] cosval Cos(x)
+ *
+ * This version achieves close to machine precision, but for very large
+ * magnitudes of the argument we inherently begin to lose accuracy due to the
+ * argument reduction, despite using extended precision arithmetics internally.
+ */
+static inline void gmx_simdcall sincos(SimdDouble x, SimdDouble* sinval, SimdDouble* cosval)
+{
+    // Constants to subtract Pi/4*x from y while minimizing precision loss
+    const SimdDouble argred0(-2 * 0.78539816290140151978);
+    const SimdDouble argred1(-2 * 4.9604678871439933374e-10);
+    const SimdDouble argred2(-2 * 1.1258708853173288931e-18);
+    const SimdDouble argred3(-2 * 1.7607799325916000908e-27);
+    const SimdDouble two_over_pi(2.0 / M_PI);
+    const SimdDouble const_sin5(1.58938307283228937328511e-10);
+    const SimdDouble const_sin4(-2.50506943502539773349318e-08);
+    const SimdDouble const_sin3(2.75573131776846360512547e-06);
+    const SimdDouble const_sin2(-0.000198412698278911770864914);
+    const SimdDouble const_sin1(0.0083333333333191845961746);
+    const SimdDouble const_sin0(-0.166666666666666130709393);
+
+    const SimdDouble const_cos7(-1.13615350239097429531523e-11);
+    const SimdDouble const_cos6(2.08757471207040055479366e-09);
+    const SimdDouble const_cos5(-2.75573144028847567498567e-07);
+    const SimdDouble const_cos4(2.48015872890001867311915e-05);
+    const SimdDouble const_cos3(-0.00138888888888714019282329);
+    const SimdDouble const_cos2(0.0416666666666665519592062);
+    const SimdDouble half(0.5);
+    const SimdDouble one(1.0);
+    SimdDouble       ssign, csign;
+    SimdDouble       x2, y, z, psin, pcos, sss, ccc;
+    SimdDBool        mask;
+#        if GMX_SIMD_HAVE_DINT32_ARITHMETICS && GMX_SIMD_HAVE_LOGICAL
+    const SimdDInt32 ione(1);
+    const SimdDInt32 itwo(2);
+    SimdDInt32       iy;
+
+    z  = x * two_over_pi;
+    iy = cvtR2I(z);
+    y  = round(z);
+
+    mask  = cvtIB2B((iy & ione) == setZero());
+    ssign = selectByMask(SimdDouble(GMX_DOUBLE_NEGZERO), cvtIB2B((iy & itwo) == itwo));
+    csign = selectByMask(SimdDouble(GMX_DOUBLE_NEGZERO), cvtIB2B(((iy + ione) & itwo) == itwo));
+#        else
+    const SimdDouble quarter(0.25);
+    const SimdDouble minusquarter(-0.25);
+    SimdDouble       q;
+    SimdDBool        m1, m2, m3;
+
+    /* The most obvious way to find the arguments quadrant in the unit circle
+     * to calculate the sign is to use integer arithmetic, but that is not
+     * present in all SIMD implementations. As an alternative, we have devised a
+     * pure floating-point algorithm that uses truncation for argument reduction
+     * so that we get a new value 0<=q<1 over the unit circle, and then
+     * do floating-point comparisons with fractions. This is likely to be
+     * slightly slower (~10%) due to the longer latencies of floating-point, so
+     * we only use it when integer SIMD arithmetic is not present.
+     */
+    ssign = x;
+    x     = abs(x);
+    // It is critical that half-way cases are rounded down
+    z = fma(x, two_over_pi, half);
+    y = trunc(z);
+    q = z * quarter;
+    q = q - trunc(q);
+    /* z now starts at 0.0 for x=-pi/4 (although neg. values cannot occur), and
+     * then increased by 1.0 as x increases by 2*Pi, when it resets to 0.0.
+     * This removes the 2*Pi periodicity without using any integer arithmetic.
+     * First check if y had the value 2 or 3, set csign if true.
+     */
+    q = q - half;
+    /* If we have logical operations we can work directly on the signbit, which
+     * saves instructions. Otherwise we need to represent signs as +1.0/-1.0.
+     * Thus, if you are altering defines to debug alternative code paths, the
+     * two GMX_SIMD_HAVE_LOGICAL sections in this routine must either both be
+     * active or inactive - you will get errors if only one is used.
+     */
+#            if GMX_SIMD_HAVE_LOGICAL
+    ssign = ssign & SimdDouble(GMX_DOUBLE_NEGZERO);
+    csign = andNot(q, SimdDouble(GMX_DOUBLE_NEGZERO));
+    ssign = ssign ^ csign;
+#            else
+    ssign = copysign(SimdDouble(1.0), ssign);
+    csign = copysign(SimdDouble(1.0), q);
+    csign = -csign;
+    ssign = ssign * csign; // swap ssign if csign was set.
+#            endif
+    // Check if y had value 1 or 3 (remember we subtracted 0.5 from q)
+    m1   = (q < minusquarter);
+    m2   = (setZero() <= q);
+    m3   = (q < quarter);
+    m2   = m2 && m3;
+    mask = m1 || m2;
+    // where mask is FALSE, swap sign.
+    csign   = csign * blend(SimdDouble(-1.0), one, mask);
+#        endif
+    x  = fma(y, argred0, x);
+    x  = fma(y, argred1, x);
+    x  = fma(y, argred2, x);
+    x  = fma(y, argred3, x);
+    x2 = x * x;
+
+    psin = fma(const_sin5, x2, const_sin4);
+    psin = fma(psin, x2, const_sin3);
+    psin = fma(psin, x2, const_sin2);
+    psin = fma(psin, x2, const_sin1);
+    psin = fma(psin, x2, const_sin0);
+    psin = fma(psin, x2 * x, x);
+
+    pcos = fma(const_cos7, x2, const_cos6);
+    pcos = fma(pcos, x2, const_cos5);
+    pcos = fma(pcos, x2, const_cos4);
+    pcos = fma(pcos, x2, const_cos3);
+    pcos = fma(pcos, x2, const_cos2);
+    pcos = fms(pcos, x2, half);
+    pcos = fma(pcos, x2, one);
+
+    sss = blend(pcos, psin, mask);
+    ccc = blend(psin, pcos, mask);
+    // See comment for GMX_SIMD_HAVE_LOGICAL section above.
+#        if GMX_SIMD_HAVE_LOGICAL
+    *sinval = sss ^ ssign;
+    *cosval = ccc ^ csign;
+#        else
+    *sinval = sss * ssign;
+    *cosval = ccc * csign;
+#        endif
+}
+
+/*! \brief SIMD double sin(x).
+ *
+ * \param x The argument to evaluate sin for
+ * \result Sin(x)
+ *
+ * \attention Do NOT call both sin & cos if you need both results, since each of them
+ * will then call \ref sincos and waste a factor 2 in performance.
+ */
+static inline SimdDouble gmx_simdcall sin(SimdDouble x)
+{
+    SimdDouble s, c;
+    sincos(x, &s, &c);
+    return s;
+}
+
+/*! \brief SIMD double cos(x).
+ *
+ * \param x The argument to evaluate cos for
+ * \result Cos(x)
+ *
+ * \attention Do NOT call both sin & cos if you need both results, since each of them
+ * will then call \ref sincos and waste a factor 2 in performance.
+ */
+static inline SimdDouble gmx_simdcall cos(SimdDouble x)
+{
+    SimdDouble s, c;
+    sincos(x, &s, &c);
+    return c;
+}
+
+/*! \brief SIMD double tan(x).
+ *
+ * \param x The argument to evaluate tan for
+ * \result Tan(x)
+ */
+static inline SimdDouble gmx_simdcall tan(SimdDouble x)
+{
+    const SimdDouble argred0(-2 * 0.78539816290140151978);
+    const SimdDouble argred1(-2 * 4.9604678871439933374e-10);
+    const SimdDouble argred2(-2 * 1.1258708853173288931e-18);
+    const SimdDouble argred3(-2 * 1.7607799325916000908e-27);
+    const SimdDouble two_over_pi(2.0 / M_PI);
+    const SimdDouble CT15(1.01419718511083373224408e-05);
+    const SimdDouble CT14(-2.59519791585924697698614e-05);
+    const SimdDouble CT13(5.23388081915899855325186e-05);
+    const SimdDouble CT12(-3.05033014433946488225616e-05);
+    const SimdDouble CT11(7.14707504084242744267497e-05);
+    const SimdDouble CT10(8.09674518280159187045078e-05);
+    const SimdDouble CT9(0.000244884931879331847054404);
+    const SimdDouble CT8(0.000588505168743587154904506);
+    const SimdDouble CT7(0.00145612788922812427978848);
+    const SimdDouble CT6(0.00359208743836906619142924);
+    const SimdDouble CT5(0.00886323944362401618113356);
+    const SimdDouble CT4(0.0218694882853846389592078);
+    const SimdDouble CT3(0.0539682539781298417636002);
+    const SimdDouble CT2(0.133333333333125941821962);
+    const SimdDouble CT1(0.333333333333334980164153);
+    const SimdDouble minFloat(std::numeric_limits<float>::min());
+
+    SimdDouble x2, p, y, z;
+    SimdDBool  m;
+
+#        if GMX_SIMD_HAVE_DINT32_ARITHMETICS && GMX_SIMD_HAVE_LOGICAL
+    SimdDInt32 iy;
+    SimdDInt32 ione(1);
+
+    z  = x * two_over_pi;
+    iy = cvtR2I(z);
+    y  = round(z);
+    m  = cvtIB2B((iy & ione) == ione);
+
+    x = fma(y, argred0, x);
+    x = fma(y, argred1, x);
+    x = fma(y, argred2, x);
+    x = fma(y, argred3, x);
+    x = selectByMask(SimdDouble(GMX_DOUBLE_NEGZERO), m) ^ x;
+#        else
+    const SimdDouble quarter(0.25);
+    const SimdDouble half(0.5);
+    const SimdDouble threequarter(0.75);
+    const SimdDouble minFloat(std::numeric_limits<float>::min());
+    SimdDouble       w, q;
+    SimdDBool        m1, m2, m3;
+
+    w  = abs(x);
+    z  = fma(w, two_over_pi, half);
+    y  = trunc(z);
+    q  = z * quarter;
+    q  = q - trunc(q);
+    m1 = (quarter <= q);
+    m2 = (q < half);
+    m3 = (threequarter <= q);
+    m1 = m1 && m2;
+    m  = m1 || m3;
+    w  = fma(y, argred0, w);
+    w  = fma(y, argred1, w);
+    w  = fma(y, argred2, w);
+    w  = fma(y, argred3, w);
+
+    w     = blend(w, -w, m);
+    x     = w * copysign(SimdDouble(1.0), x);
+#        endif
+    x2 = x * x;
+    p  = fma(CT15, x2, CT14);
+    p  = fma(p, x2, CT13);
+    p  = fma(p, x2, CT12);
+    p  = fma(p, x2, CT11);
+    p  = fma(p, x2, CT10);
+    p  = fma(p, x2, CT9);
+    p  = fma(p, x2, CT8);
+    p  = fma(p, x2, CT7);
+    p  = fma(p, x2, CT6);
+    p  = fma(p, x2, CT5);
+    p  = fma(p, x2, CT4);
+    p  = fma(p, x2, CT3);
+    p  = fma(p, x2, CT2);
+    p  = fma(p, x2, CT1);
+    p  = fma(x2, p * x, x);
+
+    p = blend(p, maskzInv(p, m && (minFloat < abs(p))), m);
+    return p;
+}
+
+/*! \brief SIMD double asin(x).
+ *
+ * \param x The argument to evaluate asin for
+ * \result Asin(x)
+ */
+static inline SimdDouble gmx_simdcall asin(SimdDouble x)
+{
+    // Same algorithm as cephes library
+    const SimdDouble limit1(0.625);
+    const SimdDouble limit2(1e-8);
+    const SimdDouble one(1.0);
+    const SimdDouble quarterpi(M_PI / 4.0);
+    const SimdDouble morebits(6.123233995736765886130e-17);
+
+    const SimdDouble P5(4.253011369004428248960e-3);
+    const SimdDouble P4(-6.019598008014123785661e-1);
+    const SimdDouble P3(5.444622390564711410273e0);
+    const SimdDouble P2(-1.626247967210700244449e1);
+    const SimdDouble P1(1.956261983317594739197e1);
+    const SimdDouble P0(-8.198089802484824371615e0);
+
+    const SimdDouble Q4(-1.474091372988853791896e1);
+    const SimdDouble Q3(7.049610280856842141659e1);
+    const SimdDouble Q2(-1.471791292232726029859e2);
+    const SimdDouble Q1(1.395105614657485689735e2);
+    const SimdDouble Q0(-4.918853881490881290097e1);
+
+    const SimdDouble R4(2.967721961301243206100e-3);
+    const SimdDouble R3(-5.634242780008963776856e-1);
+    const SimdDouble R2(6.968710824104713396794e0);
+    const SimdDouble R1(-2.556901049652824852289e1);
+    const SimdDouble R0(2.853665548261061424989e1);
+
+    const SimdDouble S3(-2.194779531642920639778e1);
+    const SimdDouble S2(1.470656354026814941758e2);
+    const SimdDouble S1(-3.838770957603691357202e2);
+    const SimdDouble S0(3.424398657913078477438e2);
+
+    SimdDouble xabs;
+    SimdDouble zz, ww, z, q, w, zz2, ww2;
+    SimdDouble PA, PB;
+    SimdDouble QA, QB;
+    SimdDouble RA, RB;
+    SimdDouble SA, SB;
+    SimdDouble nom, denom;
+    SimdDBool  mask, mask2;
+
+    xabs = abs(x);
+
+    mask = (limit1 < xabs);
+
+    zz  = one - xabs;
+    ww  = xabs * xabs;
+    zz2 = zz * zz;
+    ww2 = ww * ww;
+
+    // R
+    RA = fma(R4, zz2, R2);
+    RB = fma(R3, zz2, R1);
+    RA = fma(RA, zz2, R0);
+    RA = fma(RB, zz, RA);
+
+    // S, SA = zz2
+    SB = fma(S3, zz2, S1);
+    SA = zz2 + S2;
+    SA = fma(SA, zz2, S0);
+    SA = fma(SB, zz, SA);
+
+    // P
+    PA = fma(P5, ww2, P3);
+    PB = fma(P4, ww2, P2);
+    PA = fma(PA, ww2, P1);
+    PB = fma(PB, ww2, P0);
+    PA = fma(PA, ww, PB);
+
+    // Q, QA = ww2
+    QB = fma(Q4, ww2, Q2);
+    QA = ww2 + Q3;
+    QA = fma(QA, ww2, Q1);
+    QB = fma(QB, ww2, Q0);
+    QA = fma(QA, ww, QB);
+
+    RA = RA * zz;
+    PA = PA * ww;
+
+    nom   = blend(PA, RA, mask);
+    denom = blend(QA, SA, mask);
+
+    mask2 = (limit2 < xabs);
+    q     = nom * maskzInv(denom, mask2);
+
+    zz = zz + zz;
+    zz = sqrt(zz);
+    z  = quarterpi - zz;
+    zz = fms(zz, q, morebits);
+    z  = z - zz;
+    z  = z + quarterpi;
+
+    w = xabs * q;
+    w = w + xabs;
+
+    z = blend(w, z, mask);
+
+    z = blend(xabs, z, mask2);
+
+    z = copysign(z, x);
+
+    return z;
+}
+
+/*! \brief SIMD double acos(x).
+ *
+ * \param x The argument to evaluate acos for
+ * \result Acos(x)
+ */
+static inline SimdDouble gmx_simdcall acos(SimdDouble x)
+{
+    const SimdDouble one(1.0);
+    const SimdDouble half(0.5);
+    const SimdDouble quarterpi0(7.85398163397448309616e-1);
+    const SimdDouble quarterpi1(6.123233995736765886130e-17);
+
+    SimdDBool  mask1;
+    SimdDouble z, z1, z2;
+
+    mask1 = (half < x);
+    z1    = half * (one - x);
+    z1    = sqrt(z1);
+    z     = blend(x, z1, mask1);
+
+    z = asin(z);
+
+    z1 = z + z;
+
+    z2 = quarterpi0 - z;
+    z2 = z2 + quarterpi1;
+    z2 = z2 + quarterpi0;
+
+    z = blend(z2, z1, mask1);
+
+    return z;
+}
+
+/*! \brief SIMD double asin(x).
+ *
+ * \param x The argument to evaluate atan for
+ * \result Atan(x), same argument/value range as standard math library.
+ */
+static inline SimdDouble gmx_simdcall atan(SimdDouble x)
+{
+    // Same algorithm as cephes library
+    const SimdDouble limit1(0.66);
+    const SimdDouble limit2(2.41421356237309504880);
+    const SimdDouble quarterpi(M_PI / 4.0);
+    const SimdDouble halfpi(M_PI / 2.0);
+    const SimdDouble mone(-1.0);
+    const SimdDouble morebits1(0.5 * 6.123233995736765886130E-17);
+    const SimdDouble morebits2(6.123233995736765886130E-17);
+
+    const SimdDouble P4(-8.750608600031904122785E-1);
+    const SimdDouble P3(-1.615753718733365076637E1);
+    const SimdDouble P2(-7.500855792314704667340E1);
+    const SimdDouble P1(-1.228866684490136173410E2);
+    const SimdDouble P0(-6.485021904942025371773E1);
+
+    const SimdDouble Q4(2.485846490142306297962E1);
+    const SimdDouble Q3(1.650270098316988542046E2);
+    const SimdDouble Q2(4.328810604912902668951E2);
+    const SimdDouble Q1(4.853903996359136964868E2);
+    const SimdDouble Q0(1.945506571482613964425E2);
+
+    SimdDouble y, xabs, t1, t2;
+    SimdDouble z, z2;
+    SimdDouble P_A, P_B, Q_A, Q_B;
+    SimdDBool  mask1, mask2;
+
+    xabs = abs(x);
+
+    mask1 = (limit1 < xabs);
+    mask2 = (limit2 < xabs);
+
+    t1 = (xabs + mone) * maskzInv(xabs - mone, mask1);
+    t2 = mone * maskzInv(xabs, mask2);
+
+    y    = selectByMask(quarterpi, mask1);
+    y    = blend(y, halfpi, mask2);
+    xabs = blend(xabs, t1, mask1);
+    xabs = blend(xabs, t2, mask2);
+
+    z  = xabs * xabs;
+    z2 = z * z;
+
+    P_A = fma(P4, z2, P2);
+    P_B = fma(P3, z2, P1);
+    P_A = fma(P_A, z2, P0);
+    P_A = fma(P_B, z, P_A);
+
+    // Q_A = z2
+    Q_B = fma(Q4, z2, Q2);
+    Q_A = z2 + Q3;
+    Q_A = fma(Q_A, z2, Q1);
+    Q_B = fma(Q_B, z2, Q0);
+    Q_A = fma(Q_A, z, Q_B);
+
+    z = z * P_A;
+    z = z * inv(Q_A);
+    z = fma(z, xabs, xabs);
+
+    t1 = selectByMask(morebits1, mask1);
+    t1 = blend(t1, morebits2, mask2);
+
+    z = z + t1;
+    y = y + z;
+
+    y = copysign(y, x);
+
+    return y;
+}
+
+/*! \brief SIMD double atan2(y,x).
+ *
+ * \param y Y component of vector, any quartile
+ * \param x X component of vector, any quartile
+ * \result Atan(y,x), same argument/value range as standard math library.
+ *
+ * \note This routine should provide correct results for all finite
+ * non-zero or positive-zero arguments. However, negative zero arguments will
+ * be treated as positive zero, which means the return value will deviate from
+ * the standard math library atan2(y,x) for those cases. That should not be
+ * of any concern in Gromacs, and in particular it will not affect calculations
+ * of angles from vectors.
+ */
+static inline SimdDouble gmx_simdcall atan2(SimdDouble y, SimdDouble x)
+{
+    const SimdDouble pi(M_PI);
+    const SimdDouble halfpi(M_PI / 2.0);
+    const SimdDouble minFloat(std::numeric_limits<float>::min());
+    SimdDouble       xinv, p, aoffset;
+    SimdDBool        mask_xnz, mask_ynz, mask_xlt0, mask_ylt0;
+
+    mask_xnz  = x != setZero();
+    mask_ynz  = y != setZero();
+    mask_xlt0 = (x < setZero());
+    mask_ylt0 = (y < setZero());
+
+    aoffset = selectByNotMask(halfpi, mask_xnz);
+    aoffset = selectByMask(aoffset, mask_ynz);
+
+    aoffset = blend(aoffset, pi, mask_xlt0);
+    aoffset = blend(aoffset, -aoffset, mask_ylt0);
+
+    xinv = maskzInv(x, mask_xnz && (minFloat <= abs(x)));
+    p    = y * xinv;
+    p    = atan(p);
+    p    = p + aoffset;
+
+    return p;
+}
+
+
+/*! \brief Calculate the force correction due to PME analytically in SIMD double.
+ *
+ * \param z2 This should be the value \f$(r \beta)^2\f$, where r is your
+ *           interaction distance and beta the ewald splitting parameters.
+ * \result Correction factor to coulomb force.
+ *
+ * This routine is meant to enable analytical evaluation of the
+ * direct-space PME electrostatic force to avoid tables. For details, see the
+ * single precision function.
+ */
+static inline SimdDouble gmx_simdcall pmeForceCorrection(SimdDouble z2)
+{
+    const SimdDouble FN10(-8.0072854618360083154e-14);
+    const SimdDouble FN9(1.1859116242260148027e-11);
+    const SimdDouble FN8(-8.1490406329798423616e-10);
+    const SimdDouble FN7(3.4404793543907847655e-8);
+    const SimdDouble FN6(-9.9471420832602741006e-7);
+    const SimdDouble FN5(0.000020740315999115847456);
+    const SimdDouble FN4(-0.00031991745139313364005);
+    const SimdDouble FN3(0.0035074449373659008203);
+    const SimdDouble FN2(-0.031750380176100813405);
+    const SimdDouble FN1(0.13884101728898463426);
+    const SimdDouble FN0(-0.75225277815249618847);
+
+    const SimdDouble FD5(0.000016009278224355026701);
+    const SimdDouble FD4(0.00051055686934806966046);
+    const SimdDouble FD3(0.0081803507497974289008);
+    const SimdDouble FD2(0.077181146026670287235);
+    const SimdDouble FD1(0.41543303143712535988);
+    const SimdDouble FD0(1.0);
+
+    SimdDouble z4;
+    SimdDouble polyFN0, polyFN1, polyFD0, polyFD1;
+
+    z4 = z2 * z2;
+
+    polyFD1 = fma(FD5, z4, FD3);
+    polyFD1 = fma(polyFD1, z4, FD1);
+    polyFD1 = polyFD1 * z2;
+    polyFD0 = fma(FD4, z4, FD2);
+    polyFD0 = fma(polyFD0, z4, FD0);
+    polyFD0 = polyFD0 + polyFD1;
+
+    polyFD0 = inv(polyFD0);
+
+    polyFN0 = fma(FN10, z4, FN8);
+    polyFN0 = fma(polyFN0, z4, FN6);
+    polyFN0 = fma(polyFN0, z4, FN4);
+    polyFN0 = fma(polyFN0, z4, FN2);
+    polyFN0 = fma(polyFN0, z4, FN0);
+    polyFN1 = fma(FN9, z4, FN7);
+    polyFN1 = fma(polyFN1, z4, FN5);
+    polyFN1 = fma(polyFN1, z4, FN3);
+    polyFN1 = fma(polyFN1, z4, FN1);
+    polyFN0 = fma(polyFN1, z2, polyFN0);
+
+
+    return polyFN0 * polyFD0;
+}
+
+
+/*! \brief Calculate the potential correction due to PME analytically in SIMD double.
+ *
+ * \param z2 This should be the value \f$(r \beta)^2\f$, where r is your
+ *           interaction distance and beta the ewald splitting parameters.
+ * \result Correction factor to coulomb force.
+ *
+ * This routine is meant to enable analytical evaluation of the
+ * direct-space PME electrostatic potential to avoid tables. For details, see the
+ * single precision function.
+ */
+static inline SimdDouble gmx_simdcall pmePotentialCorrection(SimdDouble z2)
+{
+    const SimdDouble VN9(-9.3723776169321855475e-13);
+    const SimdDouble VN8(1.2280156762674215741e-10);
+    const SimdDouble VN7(-7.3562157912251309487e-9);
+    const SimdDouble VN6(2.6215886208032517509e-7);
+    const SimdDouble VN5(-4.9532491651265819499e-6);
+    const SimdDouble VN4(0.00025907400778966060389);
+    const SimdDouble VN3(0.0010585044856156469792);
+    const SimdDouble VN2(0.045247661136833092885);
+    const SimdDouble VN1(0.11643931522926034421);
+    const SimdDouble VN0(1.1283791671726767970);
+
+    const SimdDouble VD5(0.000021784709867336150342);
+    const SimdDouble VD4(0.00064293662010911388448);
+    const SimdDouble VD3(0.0096311444822588683504);
+    const SimdDouble VD2(0.085608012351550627051);
+    const SimdDouble VD1(0.43652499166614811084);
+    const SimdDouble VD0(1.0);
+
+    SimdDouble z4;
+    SimdDouble polyVN0, polyVN1, polyVD0, polyVD1;
+
+    z4 = z2 * z2;
+
+    polyVD1 = fma(VD5, z4, VD3);
+    polyVD0 = fma(VD4, z4, VD2);
+    polyVD1 = fma(polyVD1, z4, VD1);
+    polyVD0 = fma(polyVD0, z4, VD0);
+    polyVD0 = fma(polyVD1, z2, polyVD0);
+
+    polyVD0 = inv(polyVD0);
+
+    polyVN1 = fma(VN9, z4, VN7);
+    polyVN0 = fma(VN8, z4, VN6);
+    polyVN1 = fma(polyVN1, z4, VN5);
+    polyVN0 = fma(polyVN0, z4, VN4);
+    polyVN1 = fma(polyVN1, z4, VN3);
+    polyVN0 = fma(polyVN0, z4, VN2);
+    polyVN1 = fma(polyVN1, z4, VN1);
+    polyVN0 = fma(polyVN0, z4, VN0);
+    polyVN0 = fma(polyVN1, z2, polyVN0);
+
+    return polyVN0 * polyVD0;
+}
+
+/*! \} */
+
+
+/*! \name SIMD math functions for double prec. data, single prec. accuracy
+ *
+ *  \note In some cases we do not need full double accuracy of individual
+ *        SIMD math functions, although the data is stored in double precision
+ *        SIMD registers. This might be the case for special algorithms, or
+ *        if the architecture does not support single precision.
+ *        Since the full double precision evaluation of math functions
+ *        typically require much more expensive polynomial approximations
+ *        these functions implement the algorithms used in the single precision
+ *        SIMD math functions, but they operate on double precision
+ *        SIMD variables.
+ *
+ *  \{
+ */
+
+/*********************************************************************
+ * SIMD MATH FUNCTIONS WITH DOUBLE PREC. DATA, SINGLE PREC. ACCURACY *
+ *********************************************************************/
+
+/*! \brief Calculate 1/sqrt(x) for SIMD double, but in single accuracy.
+ *
+ *  \param x Argument that must be larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *
+ *  \return 1/sqrt(x). Result is undefined if your argument was invalid.
+ */
+static inline SimdDouble gmx_simdcall invsqrtSingleAccuracy(SimdDouble x)
+{
+    SimdDouble lu = rsqrt(x);
+#        if (GMX_SIMD_RSQRT_BITS < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 2 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 4 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+    return lu;
+}
+
+/*! \brief 1/sqrt(x) for masked-in entries of SIMD double, but in single accuracy.
+ *
+ *  This routine only evaluates 1/sqrt(x) for elements for which mask is true.
+ *  Illegal values in the masked-out elements will not lead to
+ *  floating-point exceptions.
+ *
+ *  \param x Argument that must be larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *
+ *  \param m Mask
+ *  \return 1/sqrt(x). Result is undefined if your argument was invalid or
+ *          entry was not masked, and 0.0 for masked-out entries.
+ */
+static inline SimdDouble maskzInvsqrtSingleAccuracy(SimdDouble x, SimdDBool m)
+{
+    SimdDouble lu = maskzRsqrt(x, m);
+#        if (GMX_SIMD_RSQRT_BITS < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 2 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 4 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+    return lu;
+}
+
+/*! \brief Calculate 1/sqrt(x) for two SIMD doubles, but single accuracy.
+ *
+ * \param x0  First set of arguments, x0 must be in single range (see below).
+ * \param x1  Second set of arguments, x1 must be in single range (see below).
+ * \param[out] out0  Result 1/sqrt(x0)
+ * \param[out] out1  Result 1/sqrt(x1)
+ *
+ *  In particular for double precision we can sometimes calculate square root
+ *  pairs slightly faster by using single precision until the very last step.
+ *
+ * \note Both arguments must be larger than GMX_FLOAT_MIN and smaller than
+ *       GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *       For the single precision implementation this is obviously always
+ *       true for positive values, but for double precision it adds an
+ *       extra restriction since the first lookup step might have to be
+ *       performed in single precision on some architectures. Note that the
+ *       responsibility for checking falls on you - this routine does not
+ *       check arguments.
+ */
+static inline void gmx_simdcall invsqrtPairSingleAccuracy(SimdDouble  x0,
+                                                          SimdDouble  x1,
+                                                          SimdDouble* out0,
+                                                          SimdDouble* out1)
+{
+#        if GMX_SIMD_HAVE_FLOAT && (GMX_SIMD_FLOAT_WIDTH == 2 * GMX_SIMD_DOUBLE_WIDTH) \
+                && (GMX_SIMD_RSQRT_BITS < 22)
+    SimdFloat  xf  = cvtDD2F(x0, x1);
+    SimdFloat  luf = rsqrt(xf);
+    SimdDouble lu0, lu1;
+    // Intermediate target is single - mantissa+1 bits
+#            if (GMX_SIMD_RSQRT_BITS < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    luf = rsqrtIter(luf, xf);
+#            endif
+#            if (GMX_SIMD_RSQRT_BITS * 2 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    luf = rsqrtIter(luf, xf);
+#            endif
+#            if (GMX_SIMD_RSQRT_BITS * 4 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    luf = rsqrtIter(luf, xf);
+#            endif
+    cvtF2DD(luf, &lu0, &lu1);
+    // We now have single-precision accuracy values in lu0/lu1
+    *out0 = lu0;
+    *out1 = lu1;
+#        else
+    *out0 = invsqrtSingleAccuracy(x0);
+    *out1 = invsqrtSingleAccuracy(x1);
+#        endif
+}
+
+/*! \brief Calculate 1/x for SIMD double, but in single accuracy.
+ *
+ *  \param x Argument with magnitude larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *
+ *  \return 1/x. Result is undefined if your argument was invalid.
+ */
+static inline SimdDouble gmx_simdcall invSingleAccuracy(SimdDouble x)
+{
+    SimdDouble lu = rcp(x);
+#        if (GMX_SIMD_RCP_BITS < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 2 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 4 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rcpIter(lu, x);
+#        endif
+    return lu;
+}
+
+/*! \brief 1/x for masked entries of SIMD double, single accuracy.
+ *
+ *  \param x Argument with magnitude larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *
+ *  \param m Mask
+ *  \return 1/x for elements where m is true, or 0.0 for masked-out entries.
+ */
+static inline SimdDouble gmx_simdcall maskzInvSingleAccuracy(SimdDouble x, SimdDBool m)
+{
+    SimdDouble lu = maskzRcp(x, m);
+#        if (GMX_SIMD_RCP_BITS < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 2 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rcpIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RCP_BITS * 4 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rcpIter(lu, x);
+#        endif
+    return lu;
+}
+
+
+/*! \brief Calculate sqrt(x) (correct for 0.0) for SIMD double, with single accuracy.
+ *
+ *  \copydetails sqrt(SimdFloat)
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall sqrtSingleAccuracy(SimdDouble x)
+{
+    if (opt == MathOptimization::Safe)
+    {
+        SimdDouble res = maskzInvsqrt(x, SimdDouble(GMX_FLOAT_MIN) < x);
+        return res * x;
+    }
+    else
+    {
+        return x * invsqrtSingleAccuracy(x);
+    }
+}
+
+/*! \brief Cube root for SIMD doubles, single accuracy.
+ *
+ * \param x      Argument to calculate cube root of. Can be negative or zero,
+ *               but NaN or Inf values are not supported. Denormal values will
+ *               be treated as 0.0.
+ * \return       Cube root of x.
+ */
+static inline SimdDouble gmx_simdcall cbrtSingleAccuracy(SimdDouble x)
+{
+    const SimdDouble signBit(GMX_DOUBLE_NEGZERO);
+    const SimdDouble minDouble(std::numeric_limits<double>::min());
+    // Bias is 1024-1 = 1023, which is divisible by 3, so no need to change it more.
+    // Use the divided value as original constant to avoid division warnings.
+    const std::int32_t offsetDiv3(341);
+    const SimdDouble   c2(-0.191502161678719066);
+    const SimdDouble   c1(0.697570460207922770);
+    const SimdDouble   c0(0.492659620528969547);
+    const SimdDouble   one(1.0);
+    const SimdDouble   two(2.0);
+    const SimdDouble   three(3.0);
+    const SimdDouble   oneThird(1.0 / 3.0);
+    const SimdDouble   cbrt2(1.2599210498948731648);
+    const SimdDouble   sqrCbrt2(1.5874010519681994748);
+
+    // See the single precision routines for documentation of the algorithm
+
+    SimdDouble xSignBit = x & signBit; // create bit mask where the sign bit is 1 for x elements < 0
+    SimdDouble xAbs     = andNot(signBit, x);    // select everthing but the sign bit => abs(x)
+    SimdDBool  xIsNonZero = (minDouble <= xAbs); // treat denormals as 0
+
+    SimdDInt32 exponent;
+    SimdDouble y        = frexp(xAbs, &exponent);
+    SimdDouble z        = fma(fma(y, c2, c1), y, c0);
+    SimdDouble w        = z * z * z;
+    SimdDouble nom      = z * fma(two, y, w);
+    SimdDouble invDenom = inv(fma(two, w, y));
+
+    SimdDouble offsetExp = cvtI2R(exponent) + SimdDouble(static_cast<double>(3 * offsetDiv3) + 0.1);
+    SimdDouble offsetExpDiv3 =
+            trunc(offsetExp * oneThird); // important to truncate here to mimic integer division
+    SimdDInt32 expDiv3   = cvtR2I(offsetExpDiv3 - SimdDouble(static_cast<double>(offsetDiv3)));
+    SimdDouble remainder = offsetExp - offsetExpDiv3 * three;
+    SimdDouble factor    = blend(one, cbrt2, SimdDouble(0.5) < remainder);
+    factor               = blend(factor, sqrCbrt2, SimdDouble(1.5) < remainder);
+    SimdDouble fraction  = (nom * invDenom * factor) ^ xSignBit;
+    SimdDouble result    = selectByMask(ldexp(fraction, expDiv3), xIsNonZero);
+    return result;
+}
+
+/*! \brief Inverse cube root for SIMD doubles, single accuracy.
+ *
+ * \param x      Argument to calculate cube root of. Can be positive or
+ *               negative, but the magnitude cannot be lower than
+ *               the smallest normal number.
+ * \return       Cube root of x. Undefined for values that don't
+ *               fulfill the restriction of abs(x) > minDouble.
+ */
+static inline SimdDouble gmx_simdcall invcbrtSingleAccuracy(SimdDouble x)
+{
+    const SimdDouble signBit(GMX_DOUBLE_NEGZERO);
+    // Bias is 1024-1 = 1023, which is divisible by 3, so no need to change it more.
+    // Use the divided value as original constant to avoid division warnings.
+    const std::int32_t offsetDiv3(341);
+    const SimdDouble   c2(-0.191502161678719066);
+    const SimdDouble   c1(0.697570460207922770);
+    const SimdDouble   c0(0.492659620528969547);
+    const SimdDouble   one(1.0);
+    const SimdDouble   two(2.0);
+    const SimdDouble   three(3.0);
+    const SimdDouble   oneThird(1.0 / 3.0);
+    const SimdDouble   invCbrt2(1.0 / 1.2599210498948731648);
+    const SimdDouble   invSqrCbrt2(1.0F / 1.5874010519681994748);
+
+    // See the single precision routines for documentation of the algorithm
+
+    SimdDouble xSignBit = x & signBit; // create bit mask where the sign bit is 1 for x elements < 0
+    SimdDouble xAbs     = andNot(signBit, x); // select everthing but the sign bit => abs(x)
+
+    SimdDInt32 exponent;
+    SimdDouble y         = frexp(xAbs, &exponent);
+    SimdDouble z         = fma(fma(y, c2, c1), y, c0);
+    SimdDouble w         = z * z * z;
+    SimdDouble nom       = fma(two, w, y);
+    SimdDouble invDenom  = inv(z * fma(two, y, w));
+    SimdDouble offsetExp = cvtI2R(exponent) + SimdDouble(static_cast<double>(3 * offsetDiv3) + 0.1);
+    SimdDouble offsetExpDiv3 =
+            trunc(offsetExp * oneThird); // important to truncate here to mimic integer division
+    SimdDInt32 expDiv3   = cvtR2I(SimdDouble(static_cast<double>(offsetDiv3)) - offsetExpDiv3);
+    SimdDouble remainder = offsetExpDiv3 * three - offsetExp;
+    SimdDouble factor    = blend(one, invCbrt2, remainder < SimdDouble(-0.5));
+    factor               = blend(factor, invSqrCbrt2, remainder < SimdDouble(-1.5));
+    SimdDouble fraction  = (nom * invDenom * factor) ^ xSignBit;
+    SimdDouble result    = ldexp(fraction, expDiv3);
+    return result;
+}
+
+/*! \brief SIMD log2(x). Double precision SIMD data, single accuracy.
+ *
+ * \param x Argument, should be >0.
+ * \result The base 2 logarithm of x. Undefined if argument is invalid.
+ */
+static inline SimdDouble gmx_simdcall log2SingleAccuracy(SimdDouble x)
+{
+#        if GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE
+    return log(x) * SimdDouble(std::log2(std::exp(1.0)));
+#        else
+    const SimdDouble one(1.0);
+    const SimdDouble two(2.0);
+    const SimdDouble sqrt2(std::sqrt(2.0));
+    const SimdDouble CL9(0.342149508897807708152F);
+    const SimdDouble CL7(0.411570606888219447939F);
+    const SimdDouble CL5(0.577085979152320294183F);
+    const SimdDouble CL3(0.961796550607099898222F);
+    const SimdDouble CL1(2.885390081777926774009F);
+    SimdDouble       fexp, x2, p;
+    SimdDInt32       iexp;
+    SimdDBool        mask;
+
+    // For log2(), the argument cannot be 0, so use the faster version of frexp
+    x    = frexp<MathOptimization::Unsafe>(x, &iexp);
+    fexp = cvtI2R(iexp);
+
+    mask = (x < sqrt2);
+    // Adjust to non-IEEE format for x<sqrt(2): exponent -= 1, mantissa *= 2.0
+    fexp = fexp - selectByMask(one, mask);
+    x    = x * blend(one, two, mask);
+
+    x  = (x - one) * invSingleAccuracy(x + one);
+    x2 = x * x;
+
+    p = fma(CL9, x2, CL7);
+    p = fma(p, x2, CL5);
+    p = fma(p, x2, CL3);
+    p = fma(p, x2, CL1);
+    p = fma(p, x, fexp);
+
+    return p;
+#        endif
+}
+
+/*! \brief SIMD log(x). Double precision SIMD data, single accuracy.
+ *
+ * \param x Argument, should be >0.
+ * \result The natural logarithm of x. Undefined if argument is invalid.
+ */
+static inline SimdDouble gmx_simdcall logSingleAccuracy(SimdDouble x)
+{
+#        if GMX_SIMD_HAVE_NATIVE_LOG_DOUBLE
+    return log(x);
+#        else
+    const SimdDouble one(1.0);
+    const SimdDouble two(2.0);
+    const SimdDouble invsqrt2(1.0 / std::sqrt(2.0));
+    const SimdDouble corr(0.693147180559945286226764);
+    const SimdDouble CL9(0.2371599674224853515625);
+    const SimdDouble CL7(0.285279005765914916992188);
+    const SimdDouble CL5(0.400005519390106201171875);
+    const SimdDouble CL3(0.666666567325592041015625);
+    const SimdDouble CL1(2.0);
+    SimdDouble       fexp, x2, p;
+    SimdDInt32       iexp;
+    SimdDBool        mask;
+
+    // For log(), the argument cannot be 0, so use the faster version of frexp
+    x    = frexp<MathOptimization::Unsafe>(x, &iexp);
+    fexp = cvtI2R(iexp);
+
+    mask = x < invsqrt2;
+    // Adjust to non-IEEE format for x<1/sqrt(2): exponent -= 1, mantissa *= 2.0
+    fexp = fexp - selectByMask(one, mask);
+    x    = x * blend(one, two, mask);
+
+    x  = (x - one) * invSingleAccuracy(x + one);
+    x2 = x * x;
+
+    p = fma(CL9, x2, CL7);
+    p = fma(p, x2, CL5);
+    p = fma(p, x2, CL3);
+    p = fma(p, x2, CL1);
+    p = fma(p, x, corr * fexp);
+
+    return p;
+#        endif
+}
+
+/*! \brief SIMD 2^x. Double precision SIMD, single accuracy.
+ *
+ * \copydetails exp2(SimdFloat)
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall exp2SingleAccuracy(SimdDouble x)
+{
+#        if GMX_SIMD_HAVE_NATIVE_EXP2_DOUBLE
+    return exp2(x);
+#        else
+    const SimdDouble CC6(0.0001534581200287996416911311);
+    const SimdDouble CC5(0.001339993121934088894618990);
+    const SimdDouble CC4(0.009618488957115180159497841);
+    const SimdDouble CC3(0.05550328776964726865751735);
+    const SimdDouble CC2(0.2402264689063408646490722);
+    const SimdDouble CC1(0.6931472057372680777553816);
+    const SimdDouble one(1.0);
+
+    SimdDouble intpart;
+    SimdDouble p;
+    SimdDInt32 ix;
+
+    // Large negative values are valid arguments to exp2(), so there are two
+    // things we need to account for:
+    // 1. When the exponents reaches -1023, the (biased) exponent field will be
+    //    zero and we can no longer multiply with it. There are special IEEE
+    //    formats to handle this range, but for now we have to accept that
+    //    we cannot handle those arguments. If input value becomes even more
+    //    negative, it will start to loop and we would end up with invalid
+    //    exponents. Thus, we need to limit or mask this.
+    // 2. For VERY large negative values, we will have problems that the
+    //    subtraction to get the fractional part loses accuracy, and then we
+    //    can end up with overflows in the polynomial.
+    //
+    // For now, we handle this by forwarding the math optimization setting to
+    // ldexp, where the routine will return zero for very small arguments.
+    //
+    // However, before doing that we need to make sure we do not call cvtR2I
+    // with an argument that is so negative it cannot be converted to an integer.
+    if (opt == MathOptimization::Safe)
+    {
+        x = max(x, SimdDouble(std::numeric_limits<std::int32_t>::lowest()));
+    }
+
+    ix      = cvtR2I(x);
+    intpart = round(x);
+    x       = x - intpart;
+
+    p = fma(CC6, x, CC5);
+    p = fma(p, x, CC4);
+    p = fma(p, x, CC3);
+    p = fma(p, x, CC2);
+    p = fma(p, x, CC1);
+    p = fma(p, x, one);
+    x = ldexp<opt>(p, ix);
+
+    return x;
+#        endif
+}
+
+
+/*! \brief SIMD exp(x). Double precision SIMD, single accuracy.
+ *
+ * \copydetails exp(SimdFloat)
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall expSingleAccuracy(SimdDouble x)
+{
+#        if GMX_SIMD_HAVE_NATIVE_EXP_DOUBLE
+    return exp(x);
+#        else
+    const SimdDouble argscale(1.44269504088896341);
+    // Lower bound: Clamp args that would lead to an IEEE fp exponent below -1023.
+    const SimdDouble smallArgLimit(-709.0895657128);
+    const SimdDouble invargscale(-0.69314718055994528623);
+    const SimdDouble CC4(0.00136324646882712841033936);
+    const SimdDouble CC3(0.00836596917361021041870117);
+    const SimdDouble CC2(0.0416710823774337768554688);
+    const SimdDouble CC1(0.166665524244308471679688);
+    const SimdDouble CC0(0.499999850988388061523438);
+    const SimdDouble one(1.0);
+    SimdDouble       intpart;
+    SimdDouble       y, p;
+    SimdDInt32       iy;
+
+    // Large negative values are valid arguments to exp2(), so there are two
+    // things we need to account for:
+    // 1. When the exponents reaches -1023, the (biased) exponent field will be
+    //    zero and we can no longer multiply with it. There are special IEEE
+    //    formats to handle this range, but for now we have to accept that
+    //    we cannot handle those arguments. If input value becomes even more
+    //    negative, it will start to loop and we would end up with invalid
+    //    exponents. Thus, we need to limit or mask this.
+    // 2. For VERY large negative values, we will have problems that the
+    //    subtraction to get the fractional part loses accuracy, and then we
+    //    can end up with overflows in the polynomial.
+    //
+    // For now, we handle this by forwarding the math optimization setting to
+    // ldexp, where the routine will return zero for very small arguments.
+    //
+    // However, before doing that we need to make sure we do not call cvtR2I
+    // with an argument that is so negative it cannot be converted to an integer
+    // after being multiplied by argscale.
+
+    if (opt == MathOptimization::Safe)
+    {
+        x = max(x, SimdDouble(std::numeric_limits<std::int32_t>::lowest()) / argscale);
+    }
+
+    y = x * argscale;
+
+    iy      = cvtR2I(y);
+    intpart = round(y); // use same rounding algorithm here
+
+    // Extended precision arithmetics not needed since
+    // we have double precision and only need single accuracy.
+    x = fma(invargscale, intpart, x);
+
+    p = fma(CC4, x, CC3);
+    p = fma(p, x, CC2);
+    p = fma(p, x, CC1);
+    p = fma(p, x, CC0);
+    p = fma(x * x, p, x);
+    p = p + one;
+    x = ldexp<opt>(p, iy);
+    return x;
+#        endif
+}
+
+/*! \brief SIMD pow(x,y). Double precision SIMD data, single accuracy.
+ *
+ * This returns x^y for SIMD values.
+ *
+ * \tparam opt If this is changed from the default (safe) into the unsafe
+ *             option, there are no guarantees about correct results for x==0.
+ *
+ * \param x Base.
+ *
+ * \param y exponent.
+
+ * \result x^y. Overflowing arguments are likely to either return 0 or inf,
+ *         depending on the underlying implementation. If unsafe optimizations
+ *         are enabled, this is also true for x==0.
+ *
+ * \warning You cannot rely on this implementation returning inf for arguments
+ *          that cause overflow. If you have some very large
+ *          values and need to rely on getting a valid numerical output,
+ *          take the minimum of your variable and the largest valid argument
+ *          before calling this routine.
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdDouble gmx_simdcall powSingleAccuracy(SimdDouble x, SimdDouble y)
+{
+    SimdDouble xcorr;
+
+    if (opt == MathOptimization::Safe)
+    {
+        xcorr = max(x, SimdDouble(std::numeric_limits<double>::min()));
+    }
+    else
+    {
+        xcorr = x;
+    }
+
+    SimdDouble result = exp2SingleAccuracy<opt>(y * log2SingleAccuracy(xcorr));
+
+    if (opt == MathOptimization::Safe)
+    {
+        // if x==0 and y>0 we explicitly set the result to 0.0
+        // For any x with y==0, the result will already be 1.0 since we multiply by y (0.0) and call exp().
+        result = blend(result, setZero(), x == setZero() && setZero() < y);
+    }
+
+    return result;
+}
+
+/*! \brief SIMD erf(x). Double precision SIMD data, single accuracy.
+ *
+ * \param x The value to calculate erf(x) for.
+ * \result erf(x)
+ *
+ * This routine achieves very close to single precision, but we do not care about
+ * the last bit or the subnormal result range.
+ */
+static inline SimdDouble gmx_simdcall erfSingleAccuracy(SimdDouble x)
+{
+    // Coefficients for minimax approximation of erf(x)=x*P(x^2) in range [-1,1]
+    const SimdDouble CA6(7.853861353153693e-5);
+    const SimdDouble CA5(-8.010193625184903e-4);
+    const SimdDouble CA4(5.188327685732524e-3);
+    const SimdDouble CA3(-2.685381193529856e-2);
+    const SimdDouble CA2(1.128358514861418e-1);
+    const SimdDouble CA1(-3.761262582423300e-1);
+    const SimdDouble CA0(1.128379165726710);
+    // Coefficients for minimax approximation of erfc(x)=Exp(-x^2)*P((1/(x-1))^2) in range [0.67,2]
+    const SimdDouble CB9(-0.0018629930017603923);
+    const SimdDouble CB8(0.003909821287598495);
+    const SimdDouble CB7(-0.0052094582210355615);
+    const SimdDouble CB6(0.005685614362160572);
+    const SimdDouble CB5(-0.0025367682853477272);
+    const SimdDouble CB4(-0.010199799682318782);
+    const SimdDouble CB3(0.04369575504816542);
+    const SimdDouble CB2(-0.11884063474674492);
+    const SimdDouble CB1(0.2732120154030589);
+    const SimdDouble CB0(0.42758357702025784);
+    // Coefficients for minimax approximation of erfc(x)=Exp(-x^2)*(1/x)*P((1/x)^2) in range [2,9.19]
+    const SimdDouble CC10(-0.0445555913112064);
+    const SimdDouble CC9(0.21376355144663348);
+    const SimdDouble CC8(-0.3473187200259257);
+    const SimdDouble CC7(0.016690861551248114);
+    const SimdDouble CC6(0.7560973182491192);
+    const SimdDouble CC5(-1.2137903600145787);
+    const SimdDouble CC4(0.8411872321232948);
+    const SimdDouble CC3(-0.08670413896296343);
+    const SimdDouble CC2(-0.27124782687240334);
+    const SimdDouble CC1(-0.0007502488047806069);
+    const SimdDouble CC0(0.5642114853803148);
+    const SimdDouble one(1.0);
+    const SimdDouble two(2.0);
+
+    SimdDouble x2, x4, y;
+    SimdDouble t, t2, w, w2;
+    SimdDouble pA0, pA1, pB0, pB1, pC0, pC1;
+    SimdDouble expmx2;
+    SimdDouble res_erf, res_erfc, res;
+    SimdDBool  mask, msk_erf;
+
+    // Calculate erf()
+    x2 = x * x;
+    x4 = x2 * x2;
+
+    pA0 = fma(CA6, x4, CA4);
+    pA1 = fma(CA5, x4, CA3);
+    pA0 = fma(pA0, x4, CA2);
+    pA1 = fma(pA1, x4, CA1);
+    pA0 = pA0 * x4;
+    pA0 = fma(pA1, x2, pA0);
+    // Constant term must come last for precision reasons
+    pA0 = pA0 + CA0;
+
+    res_erf = x * pA0;
+
+    // Calculate erfc
+    y       = abs(x);
+    msk_erf = (SimdDouble(0.75) <= y);
+    t       = maskzInvSingleAccuracy(y, msk_erf);
+    w       = t - one;
+    t2      = t * t;
+    w2      = w * w;
+
+    expmx2 = expSingleAccuracy(-y * y);
+
+    pB1 = fma(CB9, w2, CB7);
+    pB0 = fma(CB8, w2, CB6);
+    pB1 = fma(pB1, w2, CB5);
+    pB0 = fma(pB0, w2, CB4);
+    pB1 = fma(pB1, w2, CB3);
+    pB0 = fma(pB0, w2, CB2);
+    pB1 = fma(pB1, w2, CB1);
+    pB0 = fma(pB0, w2, CB0);
+    pB0 = fma(pB1, w, pB0);
+
+    pC0 = fma(CC10, t2, CC8);
+    pC1 = fma(CC9, t2, CC7);
+    pC0 = fma(pC0, t2, CC6);
+    pC1 = fma(pC1, t2, CC5);
+    pC0 = fma(pC0, t2, CC4);
+    pC1 = fma(pC1, t2, CC3);
+    pC0 = fma(pC0, t2, CC2);
+    pC1 = fma(pC1, t2, CC1);
+
+    pC0 = fma(pC0, t2, CC0);
+    pC0 = fma(pC1, t, pC0);
+    pC0 = pC0 * t;
+
+    // Select pB0 or pC0 for erfc()
+    mask     = (two < y);
+    res_erfc = blend(pB0, pC0, mask);
+    res_erfc = res_erfc * expmx2;
+
+    // erfc(x<0) = 2-erfc(|x|)
+    mask     = (x < setZero());
+    res_erfc = blend(res_erfc, two - res_erfc, mask);
+
+    // Select erf() or erfc()
+    mask = (y < SimdDouble(0.75));
+    res  = blend(one - res_erfc, res_erf, mask);
+
+    return res;
+}
+
+/*! \brief SIMD erfc(x). Double precision SIMD data, single accuracy.
+ *
+ * \param x The value to calculate erfc(x) for.
+ * \result erfc(x)
+ *
+ * This routine achieves singleprecision (bar the last bit) over most of the
+ * input range, but for large arguments where the result is getting close
+ * to the minimum representable numbers we accept slightly larger errors
+ * (think results that are in the ballpark of 10^-30) since that is not
+ * relevant for MD.
+ */
+static inline SimdDouble gmx_simdcall erfcSingleAccuracy(SimdDouble x)
+{
+    // Coefficients for minimax approximation of erf(x)=x*P(x^2) in range [-1,1]
+    const SimdDouble CA6(7.853861353153693e-5);
+    const SimdDouble CA5(-8.010193625184903e-4);
+    const SimdDouble CA4(5.188327685732524e-3);
+    const SimdDouble CA3(-2.685381193529856e-2);
+    const SimdDouble CA2(1.128358514861418e-1);
+    const SimdDouble CA1(-3.761262582423300e-1);
+    const SimdDouble CA0(1.128379165726710);
+    // Coefficients for minimax approximation of erfc(x)=Exp(-x^2)*P((1/(x-1))^2) in range [0.67,2]
+    const SimdDouble CB9(-0.0018629930017603923);
+    const SimdDouble CB8(0.003909821287598495);
+    const SimdDouble CB7(-0.0052094582210355615);
+    const SimdDouble CB6(0.005685614362160572);
+    const SimdDouble CB5(-0.0025367682853477272);
+    const SimdDouble CB4(-0.010199799682318782);
+    const SimdDouble CB3(0.04369575504816542);
+    const SimdDouble CB2(-0.11884063474674492);
+    const SimdDouble CB1(0.2732120154030589);
+    const SimdDouble CB0(0.42758357702025784);
+    // Coefficients for minimax approximation of erfc(x)=Exp(-x^2)*(1/x)*P((1/x)^2) in range [2,9.19]
+    const SimdDouble CC10(-0.0445555913112064);
+    const SimdDouble CC9(0.21376355144663348);
+    const SimdDouble CC8(-0.3473187200259257);
+    const SimdDouble CC7(0.016690861551248114);
+    const SimdDouble CC6(0.7560973182491192);
+    const SimdDouble CC5(-1.2137903600145787);
+    const SimdDouble CC4(0.8411872321232948);
+    const SimdDouble CC3(-0.08670413896296343);
+    const SimdDouble CC2(-0.27124782687240334);
+    const SimdDouble CC1(-0.0007502488047806069);
+    const SimdDouble CC0(0.5642114853803148);
+    const SimdDouble one(1.0);
+    const SimdDouble two(2.0);
+
+    SimdDouble x2, x4, y;
+    SimdDouble t, t2, w, w2;
+    SimdDouble pA0, pA1, pB0, pB1, pC0, pC1;
+    SimdDouble expmx2;
+    SimdDouble res_erf, res_erfc, res;
+    SimdDBool  mask, msk_erf;
+
+    // Calculate erf()
+    x2 = x * x;
+    x4 = x2 * x2;
+
+    pA0 = fma(CA6, x4, CA4);
+    pA1 = fma(CA5, x4, CA3);
+    pA0 = fma(pA0, x4, CA2);
+    pA1 = fma(pA1, x4, CA1);
+    pA1 = pA1 * x2;
+    pA0 = fma(pA0, x4, pA1);
+    // Constant term must come last for precision reasons
+    pA0 = pA0 + CA0;
+
+    res_erf = x * pA0;
+
+    // Calculate erfc
+    y       = abs(x);
+    msk_erf = (SimdDouble(0.75) <= y);
+    t       = maskzInvSingleAccuracy(y, msk_erf);
+    w       = t - one;
+    t2      = t * t;
+    w2      = w * w;
+
+    expmx2 = expSingleAccuracy(-y * y);
+
+    pB1 = fma(CB9, w2, CB7);
+    pB0 = fma(CB8, w2, CB6);
+    pB1 = fma(pB1, w2, CB5);
+    pB0 = fma(pB0, w2, CB4);
+    pB1 = fma(pB1, w2, CB3);
+    pB0 = fma(pB0, w2, CB2);
+    pB1 = fma(pB1, w2, CB1);
+    pB0 = fma(pB0, w2, CB0);
+    pB0 = fma(pB1, w, pB0);
+
+    pC0 = fma(CC10, t2, CC8);
+    pC1 = fma(CC9, t2, CC7);
+    pC0 = fma(pC0, t2, CC6);
+    pC1 = fma(pC1, t2, CC5);
+    pC0 = fma(pC0, t2, CC4);
+    pC1 = fma(pC1, t2, CC3);
+    pC0 = fma(pC0, t2, CC2);
+    pC1 = fma(pC1, t2, CC1);
+
+    pC0 = fma(pC0, t2, CC0);
+    pC0 = fma(pC1, t, pC0);
+    pC0 = pC0 * t;
+
+    // Select pB0 or pC0 for erfc()
+    mask     = (two < y);
+    res_erfc = blend(pB0, pC0, mask);
+    res_erfc = res_erfc * expmx2;
+
+    // erfc(x<0) = 2-erfc(|x|)
+    mask     = (x < setZero());
+    res_erfc = blend(res_erfc, two - res_erfc, mask);
+
+    // Select erf() or erfc()
+    mask = (y < SimdDouble(0.75));
+    res  = blend(res_erfc, one - res_erf, mask);
+
+    return res;
+}
+
+/*! \brief SIMD sin \& cos. Double precision SIMD data, single accuracy.
+ *
+ * \param x The argument to evaluate sin/cos for
+ * \param[out] sinval Sin(x)
+ * \param[out] cosval Cos(x)
+ */
+static inline void gmx_simdcall sinCosSingleAccuracy(SimdDouble x, SimdDouble* sinval, SimdDouble* cosval)
+{
+    // Constants to subtract Pi/4*x from y while minimizing precision loss
+    const SimdDouble argred0(2 * 0.78539816290140151978);
+    const SimdDouble argred1(2 * 4.9604678871439933374e-10);
+    const SimdDouble argred2(2 * 1.1258708853173288931e-18);
+    const SimdDouble two_over_pi(2.0 / M_PI);
+    const SimdDouble const_sin2(-1.9515295891e-4);
+    const SimdDouble const_sin1(8.3321608736e-3);
+    const SimdDouble const_sin0(-1.6666654611e-1);
+    const SimdDouble const_cos2(2.443315711809948e-5);
+    const SimdDouble const_cos1(-1.388731625493765e-3);
+    const SimdDouble const_cos0(4.166664568298827e-2);
+
+    const SimdDouble half(0.5);
+    const SimdDouble one(1.0);
+    SimdDouble       ssign, csign;
+    SimdDouble       x2, y, z, psin, pcos, sss, ccc;
+    SimdDBool        mask;
+
+#        if GMX_SIMD_HAVE_DINT32_ARITHMETICS && GMX_SIMD_HAVE_LOGICAL
+    const SimdDInt32 ione(1);
+    const SimdDInt32 itwo(2);
+    SimdDInt32       iy;
+
+    z  = x * two_over_pi;
+    iy = cvtR2I(z);
+    y  = round(z);
+
+    mask  = cvtIB2B((iy & ione) == setZero());
+    ssign = selectByMask(SimdDouble(GMX_DOUBLE_NEGZERO), cvtIB2B((iy & itwo) == itwo));
+    csign = selectByMask(SimdDouble(GMX_DOUBLE_NEGZERO), cvtIB2B(((iy + ione) & itwo) == itwo));
+#        else
+    const SimdDouble quarter(0.25);
+    const SimdDouble minusquarter(-0.25);
+    SimdDouble       q;
+    SimdDBool        m1, m2, m3;
+
+    /* The most obvious way to find the arguments quadrant in the unit circle
+     * to calculate the sign is to use integer arithmetic, but that is not
+     * present in all SIMD implementations. As an alternative, we have devised a
+     * pure floating-point algorithm that uses truncation for argument reduction
+     * so that we get a new value 0<=q<1 over the unit circle, and then
+     * do floating-point comparisons with fractions. This is likely to be
+     * slightly slower (~10%) due to the longer latencies of floating-point, so
+     * we only use it when integer SIMD arithmetic is not present.
+     */
+    ssign = x;
+    x     = abs(x);
+    // It is critical that half-way cases are rounded down
+    z = fma(x, two_over_pi, half);
+    y = trunc(z);
+    q = z * quarter;
+    q = q - trunc(q);
+    /* z now starts at 0.0 for x=-pi/4 (although neg. values cannot occur), and
+     * then increased by 1.0 as x increases by 2*Pi, when it resets to 0.0.
+     * This removes the 2*Pi periodicity without using any integer arithmetic.
+     * First check if y had the value 2 or 3, set csign if true.
+     */
+    q = q - half;
+    /* If we have logical operations we can work directly on the signbit, which
+     * saves instructions. Otherwise we need to represent signs as +1.0/-1.0.
+     * Thus, if you are altering defines to debug alternative code paths, the
+     * two GMX_SIMD_HAVE_LOGICAL sections in this routine must either both be
+     * active or inactive - you will get errors if only one is used.
+     */
+#            if GMX_SIMD_HAVE_LOGICAL
+    ssign = ssign & SimdDouble(GMX_DOUBLE_NEGZERO);
+    csign = andNot(q, SimdDouble(GMX_DOUBLE_NEGZERO));
+    ssign = ssign ^ csign;
+#            else
+    ssign = copysign(SimdDouble(1.0), ssign);
+    csign = copysign(SimdDouble(1.0), q);
+    csign = -csign;
+    ssign = ssign * csign; // swap ssign if csign was set.
+#            endif
+    // Check if y had value 1 or 3 (remember we subtracted 0.5 from q)
+    m1   = (q < minusquarter);
+    m2   = (setZero() <= q);
+    m3   = (q < quarter);
+    m2   = m2 && m3;
+    mask = m1 || m2;
+    // where mask is FALSE, swap sign.
+    csign   = csign * blend(SimdDouble(-1.0), one, mask);
+#        endif
+    x  = fnma(y, argred0, x);
+    x  = fnma(y, argred1, x);
+    x  = fnma(y, argred2, x);
+    x2 = x * x;
+
+    psin = fma(const_sin2, x2, const_sin1);
+    psin = fma(psin, x2, const_sin0);
+    psin = fma(psin, x * x2, x);
+    pcos = fma(const_cos2, x2, const_cos1);
+    pcos = fma(pcos, x2, const_cos0);
+    pcos = fms(pcos, x2, half);
+    pcos = fma(pcos, x2, one);
+
+    sss = blend(pcos, psin, mask);
+    ccc = blend(psin, pcos, mask);
+    // See comment for GMX_SIMD_HAVE_LOGICAL section above.
+#        if GMX_SIMD_HAVE_LOGICAL
+    *sinval = sss ^ ssign;
+    *cosval = ccc ^ csign;
+#        else
+    *sinval = sss * ssign;
+    *cosval = ccc * csign;
+#        endif
+}
+
+/*! \brief SIMD sin(x). Double precision SIMD data, single accuracy.
+ *
+ * \param x The argument to evaluate sin for
+ * \result Sin(x)
+ *
+ * \attention Do NOT call both sin & cos if you need both results, since each of them
+ * will then call \ref sincos and waste a factor 2 in performance.
+ */
+static inline SimdDouble gmx_simdcall sinSingleAccuracy(SimdDouble x)
+{
+    SimdDouble s, c;
+    sinCosSingleAccuracy(x, &s, &c);
+    return s;
+}
+
+/*! \brief SIMD cos(x). Double precision SIMD data, single accuracy.
+ *
+ * \param x The argument to evaluate cos for
+ * \result Cos(x)
+ *
+ * \attention Do NOT call both sin & cos if you need both results, since each of them
+ * will then call \ref sincos and waste a factor 2 in performance.
+ */
+static inline SimdDouble gmx_simdcall cosSingleAccuracy(SimdDouble x)
+{
+    SimdDouble s, c;
+    sinCosSingleAccuracy(x, &s, &c);
+    return c;
+}
+
+/*! \brief SIMD tan(x). Double precision SIMD data, single accuracy.
+ *
+ * \param x The argument to evaluate tan for
+ * \result Tan(x)
+ */
+static inline SimdDouble gmx_simdcall tanSingleAccuracy(SimdDouble x)
+{
+    const SimdDouble argred0(2 * 0.78539816290140151978);
+    const SimdDouble argred1(2 * 4.9604678871439933374e-10);
+    const SimdDouble argred2(2 * 1.1258708853173288931e-18);
+    const SimdDouble two_over_pi(2.0 / M_PI);
+    const SimdDouble CT6(0.009498288995810566122993911);
+    const SimdDouble CT5(0.002895755790837379295226923);
+    const SimdDouble CT4(0.02460087336161924491836265);
+    const SimdDouble CT3(0.05334912882656359828045988);
+    const SimdDouble CT2(0.1333989091464957704418495);
+    const SimdDouble CT1(0.3333307599244198227797507);
+
+    SimdDouble x2, p, y, z;
+    SimdDBool  mask;
+
+#        if GMX_SIMD_HAVE_DINT32_ARITHMETICS && GMX_SIMD_HAVE_LOGICAL
+    SimdDInt32 iy;
+    SimdDInt32 ione(1);
+
+    z    = x * two_over_pi;
+    iy   = cvtR2I(z);
+    y    = round(z);
+    mask = cvtIB2B((iy & ione) == ione);
+
+    x = fnma(y, argred0, x);
+    x = fnma(y, argred1, x);
+    x = fnma(y, argred2, x);
+    x = selectByMask(SimdDouble(GMX_DOUBLE_NEGZERO), mask) ^ x;
+#        else
+    const SimdDouble quarter(0.25);
+    const SimdDouble half(0.5);
+    const SimdDouble threequarter(0.75);
+    SimdDouble       w, q;
+    SimdDBool        m1, m2, m3;
+
+    w    = abs(x);
+    z    = fma(w, two_over_pi, half);
+    y    = trunc(z);
+    q    = z * quarter;
+    q    = q - trunc(q);
+    m1   = (quarter <= q);
+    m2   = (q < half);
+    m3   = (threequarter <= q);
+    m1   = m1 && m2;
+    mask = m1 || m3;
+    w    = fnma(y, argred0, w);
+    w    = fnma(y, argred1, w);
+    w    = fnma(y, argred2, w);
+
+    w = blend(w, -w, mask);
+    x = w * copysign(SimdDouble(1.0), x);
+#        endif
+    x2 = x * x;
+    p  = fma(CT6, x2, CT5);
+    p  = fma(p, x2, CT4);
+    p  = fma(p, x2, CT3);
+    p  = fma(p, x2, CT2);
+    p  = fma(p, x2, CT1);
+    p  = fma(x2, p * x, x);
+
+    p = blend(p, maskzInvSingleAccuracy(p, mask), mask);
+    return p;
+}
+
+/*! \brief SIMD asin(x). Double precision SIMD data, single accuracy.
+ *
+ * \param x The argument to evaluate asin for
+ * \result Asin(x)
+ */
+static inline SimdDouble gmx_simdcall asinSingleAccuracy(SimdDouble x)
+{
+    const SimdDouble limitlow(1e-4);
+    const SimdDouble half(0.5);
+    const SimdDouble one(1.0);
+    const SimdDouble halfpi(M_PI / 2.0);
+    const SimdDouble CC5(4.2163199048E-2);
+    const SimdDouble CC4(2.4181311049E-2);
+    const SimdDouble CC3(4.5470025998E-2);
+    const SimdDouble CC2(7.4953002686E-2);
+    const SimdDouble CC1(1.6666752422E-1);
+    SimdDouble       xabs;
+    SimdDouble       z, z1, z2, q, q1, q2;
+    SimdDouble       pA, pB;
+    SimdDBool        mask, mask2;
+
+    xabs  = abs(x);
+    mask  = (half < xabs);
+    z1    = half * (one - xabs);
+    mask2 = (xabs < one);
+    q1    = z1 * maskzInvsqrtSingleAccuracy(z1, mask2);
+    q2    = xabs;
+    z2    = q2 * q2;
+    z     = blend(z2, z1, mask);
+    q     = blend(q2, q1, mask);
+
+    z2 = z * z;
+    pA = fma(CC5, z2, CC3);
+    pB = fma(CC4, z2, CC2);
+    pA = fma(pA, z2, CC1);
+    pA = pA * z;
+    z  = fma(pB, z2, pA);
+    z  = fma(z, q, q);
+    q2 = halfpi - z;
+    q2 = q2 - z;
+    z  = blend(z, q2, mask);
+
+    mask = (limitlow < xabs);
+    z    = blend(xabs, z, mask);
+    z    = copysign(z, x);
+
+    return z;
+}
+
+/*! \brief SIMD acos(x). Double precision SIMD data, single accuracy.
+ *
+ * \param x The argument to evaluate acos for
+ * \result Acos(x)
+ */
+static inline SimdDouble gmx_simdcall acosSingleAccuracy(SimdDouble x)
+{
+    const SimdDouble one(1.0);
+    const SimdDouble half(0.5);
+    const SimdDouble pi(M_PI);
+    const SimdDouble halfpi(M_PI / 2.0);
+    SimdDouble       xabs;
+    SimdDouble       z, z1, z2, z3;
+    SimdDBool        mask1, mask2, mask3;
+
+    xabs  = abs(x);
+    mask1 = (half < xabs);
+    mask2 = (setZero() < x);
+
+    z     = half * (one - xabs);
+    mask3 = (xabs < one);
+    z     = z * maskzInvsqrtSingleAccuracy(z, mask3);
+    z     = blend(x, z, mask1);
+    z     = asinSingleAccuracy(z);
+
+    z2 = z + z;
+    z1 = pi - z2;
+    z3 = halfpi - z;
+    z  = blend(z1, z2, mask2);
+    z  = blend(z3, z, mask1);
+
+    return z;
+}
+
+/*! \brief SIMD asin(x). Double precision SIMD data, single accuracy.
+ *
+ * \param x The argument to evaluate atan for
+ * \result Atan(x), same argument/value range as standard math library.
+ */
+static inline SimdDouble gmx_simdcall atanSingleAccuracy(SimdDouble x)
+{
+    const SimdDouble halfpi(M_PI / 2);
+    const SimdDouble CA17(0.002823638962581753730774);
+    const SimdDouble CA15(-0.01595690287649631500244);
+    const SimdDouble CA13(0.04250498861074447631836);
+    const SimdDouble CA11(-0.07489009201526641845703);
+    const SimdDouble CA9(0.1063479334115982055664);
+    const SimdDouble CA7(-0.1420273631811141967773);
+    const SimdDouble CA5(0.1999269574880599975585);
+    const SimdDouble CA3(-0.3333310186862945556640);
+    SimdDouble       x2, x3, x4, pA, pB;
+    SimdDBool        mask, mask2;
+
+    mask  = (x < setZero());
+    x     = abs(x);
+    mask2 = (SimdDouble(1.0) < x);
+    x     = blend(x, maskzInvSingleAccuracy(x, mask2), mask2);
+
+    x2 = x * x;
+    x3 = x2 * x;
+    x4 = x2 * x2;
+    pA = fma(CA17, x4, CA13);
+    pB = fma(CA15, x4, CA11);
+    pA = fma(pA, x4, CA9);
+    pB = fma(pB, x4, CA7);
+    pA = fma(pA, x4, CA5);
+    pB = fma(pB, x4, CA3);
+    pA = fma(pA, x2, pB);
+    pA = fma(pA, x3, x);
+
+    pA = blend(pA, halfpi - pA, mask2);
+    pA = blend(pA, -pA, mask);
+
+    return pA;
+}
+
+/*! \brief SIMD atan2(y,x). Double precision SIMD data, single accuracy.
+ *
+ * \param y Y component of vector, any quartile
+ * \param x X component of vector, any quartile
+ * \result Atan(y,x), same argument/value range as standard math library.
+ *
+ * \note This routine should provide correct results for all finite
+ * non-zero or positive-zero arguments. However, negative zero arguments will
+ * be treated as positive zero, which means the return value will deviate from
+ * the standard math library atan2(y,x) for those cases. That should not be
+ * of any concern in Gromacs, and in particular it will not affect calculations
+ * of angles from vectors.
+ */
+static inline SimdDouble gmx_simdcall atan2SingleAccuracy(SimdDouble y, SimdDouble x)
+{
+    const SimdDouble pi(M_PI);
+    const SimdDouble halfpi(M_PI / 2.0);
+    SimdDouble       xinv, p, aoffset;
+    SimdDBool        mask_xnz, mask_ynz, mask_xlt0, mask_ylt0;
+
+    mask_xnz  = x != setZero();
+    mask_ynz  = y != setZero();
+    mask_xlt0 = (x < setZero());
+    mask_ylt0 = (y < setZero());
+
+    aoffset = selectByNotMask(halfpi, mask_xnz);
+    aoffset = selectByMask(aoffset, mask_ynz);
+
+    aoffset = blend(aoffset, pi, mask_xlt0);
+    aoffset = blend(aoffset, -aoffset, mask_ylt0);
+
+    xinv = maskzInvSingleAccuracy(x, mask_xnz);
+    p    = y * xinv;
+    p    = atanSingleAccuracy(p);
+    p    = p + aoffset;
+
+    return p;
+}
+
+/*! \brief Analytical PME force correction, double SIMD data, single accuracy.
+ *
+ * \param z2 \f$(r \beta)^2\f$ - see below for details.
+ * \result Correction factor to coulomb force - see below for details.
+ *
+ * This routine is meant to enable analytical evaluation of the
+ * direct-space PME electrostatic force to avoid tables.
+ *
+ * The direct-space potential should be \f$ \mbox{erfc}(\beta r)/r\f$, but there
+ * are some problems evaluating that:
+ *
+ * First, the error function is difficult (read: expensive) to
+ * approxmiate accurately for intermediate to large arguments, and
+ * this happens already in ranges of \f$(\beta r)\f$ that occur in simulations.
+ * Second, we now try to avoid calculating potentials in Gromacs but
+ * use forces directly.
+ *
+ * We can simply things slight by noting that the PME part is really
+ * a correction to the normal Coulomb force since \f$\mbox{erfc}(z)=1-\mbox{erf}(z)\f$, i.e.
+ * \f[
+ * V = \frac{1}{r} - \frac{\mbox{erf}(\beta r)}{r}
+ * \f]
+ * The first term we already have from the inverse square root, so
+ * that we can leave out of this routine.
+ *
+ * For pme tolerances of 1e-3 to 1e-8 and cutoffs of 0.5nm to 1.8nm,
+ * the argument \f$beta r\f$ will be in the range 0.15 to ~4. Use your
+ * favorite plotting program to realize how well-behaved \f$\frac{\mbox{erf}(z)}{z}\f$ is
+ * in this range!
+ *
+ * We approximate \f$f(z)=\mbox{erf}(z)/z\f$ with a rational minimax polynomial.
+ * However, it turns out it is more efficient to approximate \f$f(z)/z\f$ and
+ * then only use even powers. This is another minor optimization, since
+ * we actually \a want \f$f(z)/z\f$, because it is going to be multiplied by
+ * the vector between the two atoms to get the vectorial force. The
+ * fastest flops are the ones we can avoid calculating!
+ *
+ * So, here's how it should be used:
+ *
+ * 1. Calculate \f$r^2\f$.
+ * 2. Multiply by \f$\beta^2\f$, so you get \f$z^2=(\beta r)^2\f$.
+ * 3. Evaluate this routine with \f$z^2\f$ as the argument.
+ * 4. The return value is the expression:
+ *
+ * \f[
+ *    \frac{2 \exp{-z^2}}{\sqrt{\pi} z^2}-\frac{\mbox{erf}(z)}{z^3}
+ * \f]
+ *
+ * 5. Multiply the entire expression by \f$\beta^3\f$. This will get you
+ *
+ *  \f[
+ *    \frac{2 \beta^3 \exp(-z^2)}{\sqrt{\pi} z^2} - \frac{\beta^3 \mbox{erf}(z)}{z^3}
+ *  \f]
+ *
+ *    or, switching back to \f$r\f$ (since \f$z=r \beta\f$):
+ *
+ *  \f[
+ *    \frac{2 \beta \exp(-r^2 \beta^2)}{\sqrt{\pi} r^2} - \frac{\mbox{erf}(r \beta)}{r^3}
+ *  \f]
+ *
+ *    With a bit of math exercise you should be able to confirm that
+ *    this is exactly
+ *
+ *  \f[
+ *   \frac{\frac{d}{dr}\left( \frac{\mbox{erf}(\beta r)}{r} \right)}{r}
+ *  \f]
+ *
+ * 6. Add the result to \f$r^{-3}\f$, multiply by the product of the charges,
+ *    and you have your force (divided by \f$r\f$). A final multiplication
+ *    with the vector connecting the two particles and you have your
+ *    vectorial force to add to the particles.
+ *
+ * This approximation achieves an accuracy slightly lower than 1e-6; when
+ * added to \f$1/r\f$ the error will be insignificant.
+ *
+ */
+static inline SimdDouble gmx_simdcall pmeForceCorrectionSingleAccuracy(SimdDouble z2)
+{
+    const SimdDouble FN6(-1.7357322914161492954e-8);
+    const SimdDouble FN5(1.4703624142580877519e-6);
+    const SimdDouble FN4(-0.000053401640219807709149);
+    const SimdDouble FN3(0.0010054721316683106153);
+    const SimdDouble FN2(-0.019278317264888380590);
+    const SimdDouble FN1(0.069670166153766424023);
+    const SimdDouble FN0(-0.75225204789749321333);
+
+    const SimdDouble FD4(0.0011193462567257629232);
+    const SimdDouble FD3(0.014866955030185295499);
+    const SimdDouble FD2(0.11583842382862377919);
+    const SimdDouble FD1(0.50736591960530292870);
+    const SimdDouble FD0(1.0);
+
+    SimdDouble z4;
+    SimdDouble polyFN0, polyFN1, polyFD0, polyFD1;
+
+    z4 = z2 * z2;
+
+    polyFD0 = fma(FD4, z4, FD2);
+    polyFD1 = fma(FD3, z4, FD1);
+    polyFD0 = fma(polyFD0, z4, FD0);
+    polyFD0 = fma(polyFD1, z2, polyFD0);
+
+    polyFD0 = invSingleAccuracy(polyFD0);
+
+    polyFN0 = fma(FN6, z4, FN4);
+    polyFN1 = fma(FN5, z4, FN3);
+    polyFN0 = fma(polyFN0, z4, FN2);
+    polyFN1 = fma(polyFN1, z4, FN1);
+    polyFN0 = fma(polyFN0, z4, FN0);
+    polyFN0 = fma(polyFN1, z2, polyFN0);
+
+    return polyFN0 * polyFD0;
+}
+
+
+/*! \brief Analytical PME potential correction, double SIMD data, single accuracy.
+ *
+ * \param z2 \f$(r \beta)^2\f$ - see below for details.
+ * \result Correction factor to coulomb potential - see below for details.
+ *
+ * This routine calculates \f$\mbox{erf}(z)/z\f$, although you should provide \f$z^2\f$
+ * as the input argument.
+ *
+ * Here's how it should be used:
+ *
+ * 1. Calculate \f$r^2\f$.
+ * 2. Multiply by \f$\beta^2\f$, so you get \f$z^2=\beta^2*r^2\f$.
+ * 3. Evaluate this routine with z^2 as the argument.
+ * 4. The return value is the expression:
+ *
+ *  \f[
+ *   \frac{\mbox{erf}(z)}{z}
+ *  \f]
+ *
+ * 5. Multiply the entire expression by beta and switching back to \f$r\f$ (since \f$z=r \beta\f$):
+ *
+ *  \f[
+ *    \frac{\mbox{erf}(r \beta)}{r}
+ *  \f]
+ *
+ * 6. Subtract the result from \f$1/r\f$, multiply by the product of the charges,
+ *    and you have your potential.
+ *
+ * This approximation achieves an accuracy slightly lower than 1e-6; when
+ * added to \f$1/r\f$ the error will be insignificant.
+ */
+static inline SimdDouble gmx_simdcall pmePotentialCorrectionSingleAccuracy(SimdDouble z2)
+{
+    const SimdDouble VN6(1.9296833005951166339e-8);
+    const SimdDouble VN5(-1.4213390571557850962e-6);
+    const SimdDouble VN4(0.000041603292906656984871);
+    const SimdDouble VN3(-0.00013134036773265025626);
+    const SimdDouble VN2(0.038657983986041781264);
+    const SimdDouble VN1(0.11285044772717598220);
+    const SimdDouble VN0(1.1283802385263030286);
+
+    const SimdDouble VD3(0.0066752224023576045451);
+    const SimdDouble VD2(0.078647795836373922256);
+    const SimdDouble VD1(0.43336185284710920150);
+    const SimdDouble VD0(1.0);
+
+    SimdDouble z4;
+    SimdDouble polyVN0, polyVN1, polyVD0, polyVD1;
+
+    z4 = z2 * z2;
+
+    polyVD1 = fma(VD3, z4, VD1);
+    polyVD0 = fma(VD2, z4, VD0);
+    polyVD0 = fma(polyVD1, z2, polyVD0);
+
+    polyVD0 = invSingleAccuracy(polyVD0);
+
+    polyVN0 = fma(VN6, z4, VN4);
+    polyVN1 = fma(VN5, z4, VN3);
+    polyVN0 = fma(polyVN0, z4, VN2);
+    polyVN1 = fma(polyVN1, z4, VN1);
+    polyVN0 = fma(polyVN0, z4, VN0);
+    polyVN0 = fma(polyVN1, z2, polyVN0);
+
+    return polyVN0 * polyVD0;
+}
+
+#    endif
+
+
+/*! \name SIMD4 math functions
+ *
+ * \note Only a subset of the math functions are implemented for SIMD4.
+ *  \{
+ */
+
+
+#    if GMX_SIMD4_HAVE_FLOAT
+
+/*************************************************************************
+ * SINGLE PRECISION SIMD4 MATH FUNCTIONS - JUST A SMALL SUBSET SUPPORTED *
+ *************************************************************************/
+
+/*! \brief Perform one Newton-Raphson iteration to improve 1/sqrt(x) for SIMD4 float.
+ *
+ * This is a low-level routine that should only be used by SIMD math routine
+ * that evaluates the inverse square root.
+ *
+ *  \param lu Approximation of 1/sqrt(x), typically obtained from lookup.
+ *  \param x  The reference (starting) value x for which we want 1/sqrt(x).
+ *  \return   An improved approximation with roughly twice as many bits of accuracy.
+ */
+static inline Simd4Float gmx_simdcall rsqrtIter(Simd4Float lu, Simd4Float x)
+{
+    Simd4Float tmp1 = x * lu;
+    Simd4Float tmp2 = Simd4Float(-0.5F) * lu;
+    tmp1            = fma(tmp1, lu, Simd4Float(-3.0F));
+    return tmp1 * tmp2;
+}
+
+/*! \brief Calculate 1/sqrt(x) for SIMD4 float.
+ *
+ *  \param x Argument that must be larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *  \return  1/sqrt(x). Result is undefined if your argument was invalid.
+ */
+static inline Simd4Float gmx_simdcall invsqrt(Simd4Float x)
+{
+    Simd4Float lu = rsqrt(x);
+#        if (GMX_SIMD_RSQRT_BITS < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 2 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 4 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+    return lu;
+}
+
+
+#    endif // GMX_SIMD4_HAVE_FLOAT
+
+
+#    if GMX_SIMD4_HAVE_DOUBLE
+/*************************************************************************
+ * DOUBLE PRECISION SIMD4 MATH FUNCTIONS - JUST A SMALL SUBSET SUPPORTED *
+ *************************************************************************/
+
+/*! \brief Perform one Newton-Raphson iteration to improve 1/sqrt(x) for SIMD4 double.
+ *
+ * This is a low-level routine that should only be used by SIMD math routine
+ * that evaluates the inverse square root.
+ *
+ *  \param lu Approximation of 1/sqrt(x), typically obtained from lookup.
+ *  \param x  The reference (starting) value x for which we want 1/sqrt(x).
+ *  \return   An improved approximation with roughly twice as many bits of accuracy.
+ */
+static inline Simd4Double gmx_simdcall rsqrtIter(Simd4Double lu, Simd4Double x)
+{
+    Simd4Double tmp1 = x * lu;
+    Simd4Double tmp2 = Simd4Double(-0.5F) * lu;
+    tmp1             = fma(tmp1, lu, Simd4Double(-3.0F));
+    return tmp1 * tmp2;
+}
+
+/*! \brief Calculate 1/sqrt(x) for SIMD4 double.
+ *
+ *  \param x Argument that must be larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *  \return  1/sqrt(x). Result is undefined if your argument was invalid.
+ */
+static inline Simd4Double gmx_simdcall invsqrt(Simd4Double x)
+{
+    Simd4Double lu = rsqrt(x);
+#        if (GMX_SIMD_RSQRT_BITS < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 2 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 4 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 8 < GMX_SIMD_ACCURACY_BITS_DOUBLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+    return lu;
+}
+
+
+/**********************************************************************
+ * SIMD4 MATH FUNCTIONS WITH DOUBLE PREC. DATA, SINGLE PREC. ACCURACY *
+ **********************************************************************/
+
+/*! \brief Calculate 1/sqrt(x) for SIMD4 double, but in single accuracy.
+ *
+ *  \param x Argument that must be larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *  \return  1/sqrt(x). Result is undefined if your argument was invalid.
+ */
+static inline Simd4Double gmx_simdcall invsqrtSingleAccuracy(Simd4Double x)
+{
+    Simd4Double lu = rsqrt(x);
+#        if (GMX_SIMD_RSQRT_BITS < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 2 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+#        if (GMX_SIMD_RSQRT_BITS * 4 < GMX_SIMD_ACCURACY_BITS_SINGLE)
+    lu = rsqrtIter(lu, x);
+#        endif
+    return lu;
+}
+
+
+#    endif // GMX_SIMD4_HAVE_DOUBLE
+
+/*! \} */
+
+#    if GMX_SIMD_HAVE_FLOAT
+/*! \brief Calculate 1/sqrt(x) for SIMD float, only targeting single accuracy.
+ *
+ *  \param x Argument that must be larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *  \return  1/sqrt(x). Result is undefined if your argument was invalid.
+ */
+static inline SimdFloat gmx_simdcall invsqrtSingleAccuracy(SimdFloat x)
+{
+    return invsqrt(x);
+}
+
+/*! \brief Calculate 1/sqrt(x) for masked SIMD floats, only targeting single accuracy.
+ *
+ *  This routine only evaluates 1/sqrt(x) for elements for which mask is true.
+ *  Illegal values in the masked-out elements will not lead to
+ *  floating-point exceptions.
+ *
+ *  \param x Argument that must be larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *  \param m Mask
+ *  \return  1/sqrt(x). Result is undefined if your argument was invalid or
+ *           entry was not masked, and 0.0 for masked-out entries.
+ */
+static inline SimdFloat maskzInvsqrtSingleAccuracy(SimdFloat x, SimdFBool m)
+{
+    return maskzInvsqrt(x, m);
+}
+
+/*! \brief Calculate 1/sqrt(x) for two SIMD floats, only targeting single accuracy.
+ *
+ * \param x0  First set of arguments, x0 must be in single range (see below).
+ * \param x1  Second set of arguments, x1 must be in single range (see below).
+ * \param[out] out0  Result 1/sqrt(x0)
+ * \param[out] out1  Result 1/sqrt(x1)
+ *
+ *  In particular for double precision we can sometimes calculate square root
+ *  pairs slightly faster by using single precision until the very last step.
+ *
+ * \note Both arguments must be larger than GMX_FLOAT_MIN and smaller than
+ *       GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *       For the single precision implementation this is obviously always
+ *       true for positive values, but for double precision it adds an
+ *       extra restriction since the first lookup step might have to be
+ *       performed in single precision on some architectures. Note that the
+ *       responsibility for checking falls on you - this routine does not
+ *       check arguments.
+ */
+static inline void gmx_simdcall invsqrtPairSingleAccuracy(SimdFloat x0, SimdFloat x1, SimdFloat* out0, SimdFloat* out1)
+{
+    return invsqrtPair(x0, x1, out0, out1);
+}
+
+/*! \brief Calculate 1/x for SIMD float, only targeting single accuracy.
+ *
+ *  \param x Argument with magnitude larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *  \return  1/x. Result is undefined if your argument was invalid.
+ */
+static inline SimdFloat gmx_simdcall invSingleAccuracy(SimdFloat x)
+{
+    return inv(x);
+}
+
+
+/*! \brief Calculate 1/x for masked SIMD floats, only targeting single accuracy.
+ *
+ *  \param x Argument with magnitude larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *  \param m Mask
+ *  \return  1/x for elements where m is true, or 0.0 for masked-out entries.
+ */
+static inline SimdFloat maskzInvSingleAccuracy(SimdFloat x, SimdFBool m)
+{
+    return maskzInv(x, m);
+}
+
+/*! \brief Calculate sqrt(x) for SIMD float, always targeting single accuracy.
+ *
+ * \copydetails sqrt(SimdFloat)
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall sqrtSingleAccuracy(SimdFloat x)
+{
+    return sqrt<opt>(x);
+}
+
+/*! \brief Calculate cbrt(x) for SIMD float, always targeting single accuracy.
+ *
+ * \copydetails cbrt(SimdFloat)
+ */
+static inline SimdFloat gmx_simdcall cbrtSingleAccuracy(SimdFloat x)
+{
+    return cbrt(x);
+}
+
+/*! \brief Calculate 1/cbrt(x) for SIMD float, always targeting single accuracy.
+ *
+ * \copydetails cbrt(SimdFloat)
+ */
+static inline SimdFloat gmx_simdcall invcbrtSingleAccuracy(SimdFloat x)
+{
+    return invcbrt(x);
+}
+
+/*! \brief SIMD float log2(x), only targeting single accuracy. This is the base-2 logarithm.
+ *
+ * \param x Argument, should be >0.
+ * \result The base-2 logarithm of x. Undefined if argument is invalid.
+ */
+static inline SimdFloat gmx_simdcall log2SingleAccuracy(SimdFloat x)
+{
+    return log2(x);
+}
+
+/*! \brief SIMD float log(x), only targeting single accuracy. This is the natural logarithm.
+ *
+ * \param x Argument, should be >0.
+ * \result The natural logarithm of x. Undefined if argument is invalid.
+ */
+static inline SimdFloat gmx_simdcall logSingleAccuracy(SimdFloat x)
+{
+    return log(x);
+}
+
+/*! \brief SIMD float 2^x, only targeting single accuracy.
+ *
+ * \copydetails exp2(SimdFloat)
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall exp2SingleAccuracy(SimdFloat x)
+{
+    return exp2<opt>(x);
+}
+
+/*! \brief SIMD float e^x, only targeting single accuracy.
+ *
+ * \copydetails exp(SimdFloat)
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall expSingleAccuracy(SimdFloat x)
+{
+    return exp<opt>(x);
+}
+
+/*! \brief SIMD pow(x,y), only targeting single accuracy.
+ *
+ * \copydetails pow(SimdFloat,SimdFloat)
+ */
+template<MathOptimization opt = MathOptimization::Safe>
+static inline SimdFloat gmx_simdcall powSingleAccuracy(SimdFloat x, SimdFloat y)
+{
+    return pow<opt>(x, y);
+}
+
+/*! \brief SIMD float erf(x), only targeting single accuracy.
+ *
+ * \param x The value to calculate erf(x) for.
+ * \result erf(x)
+ *
+ * This routine achieves very close to single precision, but we do not care about
+ * the last bit or the subnormal result range.
+ */
+static inline SimdFloat gmx_simdcall erfSingleAccuracy(SimdFloat x)
+{
+    return erf(x);
+}
+
+/*! \brief SIMD float erfc(x), only targeting single accuracy.
+ *
+ * \param x The value to calculate erfc(x) for.
+ * \result erfc(x)
+ *
+ * This routine achieves singleprecision (bar the last bit) over most of the
+ * input range, but for large arguments where the result is getting close
+ * to the minimum representable numbers we accept slightly larger errors
+ * (think results that are in the ballpark of 10^-30) since that is not
+ * relevant for MD.
+ */
+static inline SimdFloat gmx_simdcall erfcSingleAccuracy(SimdFloat x)
+{
+    return erfc(x);
+}
+
+/*! \brief SIMD float sin \& cos, only targeting single accuracy.
+ *
+ * \param x The argument to evaluate sin/cos for
+ * \param[out] sinval Sin(x)
+ * \param[out] cosval Cos(x)
+ */
+static inline void gmx_simdcall sinCosSingleAccuracy(SimdFloat x, SimdFloat* sinval, SimdFloat* cosval)
+{
+    sincos(x, sinval, cosval);
+}
+
+/*! \brief SIMD float sin(x), only targeting single accuracy.
+ *
+ * \param x The argument to evaluate sin for
+ * \result Sin(x)
+ *
+ * \attention Do NOT call both sin & cos if you need both results, since each of them
+ * will then call \ref sincos and waste a factor 2 in performance.
+ */
+static inline SimdFloat gmx_simdcall sinSingleAccuracy(SimdFloat x)
+{
+    return sin(x);
+}
+
+/*! \brief SIMD float cos(x), only targeting single accuracy.
+ *
+ * \param x The argument to evaluate cos for
+ * \result Cos(x)
+ *
+ * \attention Do NOT call both sin & cos if you need both results, since each of them
+ * will then call \ref sincos and waste a factor 2 in performance.
+ */
+static inline SimdFloat gmx_simdcall cosSingleAccuracy(SimdFloat x)
+{
+    return cos(x);
+}
+
+/*! \brief SIMD float tan(x), only targeting single accuracy.
+ *
+ * \param x The argument to evaluate tan for
+ * \result Tan(x)
+ */
+static inline SimdFloat gmx_simdcall tanSingleAccuracy(SimdFloat x)
+{
+    return tan(x);
+}
+
+/*! \brief SIMD float asin(x), only targeting single accuracy.
+ *
+ * \param x The argument to evaluate asin for
+ * \result Asin(x)
+ */
+static inline SimdFloat gmx_simdcall asinSingleAccuracy(SimdFloat x)
+{
+    return asin(x);
+}
+
+/*! \brief SIMD float acos(x), only targeting single accuracy.
+ *
+ * \param x The argument to evaluate acos for
+ * \result Acos(x)
+ */
+static inline SimdFloat gmx_simdcall acosSingleAccuracy(SimdFloat x)
+{
+    return acos(x);
+}
+
+/*! \brief SIMD float atan(x), only targeting single accuracy.
+ *
+ * \param x The argument to evaluate atan for
+ * \result Atan(x), same argument/value range as standard math library.
+ */
+static inline SimdFloat gmx_simdcall atanSingleAccuracy(SimdFloat x)
+{
+    return atan(x);
+}
+
+/*! \brief SIMD float atan2(y,x), only targeting single accuracy.
+ *
+ * \param y Y component of vector, any quartile
+ * \param x X component of vector, any quartile
+ * \result Atan(y,x), same argument/value range as standard math library.
+ *
+ * \note This routine should provide correct results for all finite
+ * non-zero or positive-zero arguments. However, negative zero arguments will
+ * be treated as positive zero, which means the return value will deviate from
+ * the standard math library atan2(y,x) for those cases. That should not be
+ * of any concern in Gromacs, and in particular it will not affect calculations
+ * of angles from vectors.
+ */
+static inline SimdFloat gmx_simdcall atan2SingleAccuracy(SimdFloat y, SimdFloat x)
+{
+    return atan2(y, x);
+}
+
+/*! \brief SIMD Analytic PME force correction, only targeting single accuracy.
+ *
+ * \param z2 \f$(r \beta)^2\f$ - see default single precision version for details.
+ * \result Correction factor to coulomb force.
+ */
+static inline SimdFloat gmx_simdcall pmeForceCorrectionSingleAccuracy(SimdFloat z2)
+{
+    return pmeForceCorrection(z2);
+}
+
+/*! \brief SIMD Analytic PME potential correction, only targeting single accuracy.
+ *
+ * \param z2 \f$(r \beta)^2\f$ - see default single precision version for details.
+ * \result Correction factor to coulomb force.
+ */
+static inline SimdFloat gmx_simdcall pmePotentialCorrectionSingleAccuracy(SimdFloat z2)
+{
+    return pmePotentialCorrection(z2);
+}
+#    endif // GMX_SIMD_HAVE_FLOAT
+
+#    if GMX_SIMD4_HAVE_FLOAT
+/*! \brief Calculate 1/sqrt(x) for SIMD4 float, only targeting single accuracy.
+ *
+ *  \param x Argument that must be larger than GMX_FLOAT_MIN and smaller than
+ *           GMX_FLOAT_MAX, i.e. within the range of single precision.
+ *           For the single precision implementation this is obviously always
+ *           true for positive values, but for double precision it adds an
+ *           extra restriction since the first lookup step might have to be
+ *           performed in single precision on some architectures. Note that the
+ *           responsibility for checking falls on you - this routine does not
+ *           check arguments.
+ *  \return 1/sqrt(x). Result is undefined if your argument was invalid.
+ */
+static inline Simd4Float gmx_simdcall invsqrtSingleAccuracy(Simd4Float x)
+{
+    return invsqrt(x);
+}
+#    endif // GMX_SIMD4_HAVE_FLOAT
+
+/*! \}   end of addtogroup module_simd */
+/*! \endcond  end of condition libabl */
+
+#endif // GMX_SIMD
+
+} // namespace gmx
+
+#endif // GMX_SIMD_SIMD_MATH_H
diff --git a/src/include/gromacs/simd/simd_memory.h b/src/include/gromacs/simd/simd_memory.h
new file mode 100644 (file)
index 0000000..d6c3a69
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares SimdArrayRef
+ *
+ * \author Roland Schulz <roland.schulz@intel.com>
+ * \inlibraryapi
+ * \ingroup module_simd
+ */
+#ifndef GMX_SIMD_SIMD_MEMORY_H
+#define GMX_SIMD_SIMD_MEMORY_H
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+namespace internal
+{
+
+template<typename T>
+class SimdReference
+{
+private:
+    using non_const_T = std::remove_const_t<T>;
+    using pointer     = SimdTraitsT<T>*;
+
+public:
+    //! \brief Constructor
+    explicit SimdReference(pointer m) : m_(m) {}
+    //! \brief Conversion method that will execute load
+    operator non_const_T() const { return load<non_const_T>(m_); }
+    //! \brief Assignment operator that will execute store
+    SimdReference operator=(T o) // NOLINT(misc-unconventional-assign-operator,cppcoreguidelines-c-copy-assignment-signature)
+    {
+        store(m_, o);
+        return *this;
+    }
+    //! \brief Addition assignment operator that will execute load+store
+    SimdReference operator+=(T o)
+    {
+        store(m_, load<non_const_T>(m_) + o);
+        return *this;
+    }
+    //! \brief Subtraction assignment operator that will execute load+store
+    SimdReference operator-=(T o)
+    {
+        store(m_, load<non_const_T>(m_) - o);
+        return *this;
+    }
+    //! \brief Multiplication assignment operator that will execute load+store
+    SimdReference operator*=(T o)
+    {
+        store(m_, load<non_const_T>(m_) * o);
+        return *this;
+    }
+
+private:
+    pointer m_; //!< The pointer used to load memory
+};
+
+template<typename T>
+class SimdIterator :
+    public boost::stl_interfaces::iterator_interface<SimdIterator<T>, std::random_access_iterator_tag, T, SimdReference<T>>
+{
+    using Base =
+            boost::stl_interfaces::iterator_interface<SimdIterator<T>, std::random_access_iterator_tag, T, SimdReference<T>>;
+    // pointer is T*
+    using DataPointer = SimdTraitsT<T>*;
+
+public:
+    explicit SimdIterator(DataPointer p = 0) : p_(p)
+    {
+        GMX_ASSERT((reinterpret_cast<size_t>(p) / sizeof(*p)) % simdWidth == 0,
+                   "Trying to create aligned iterator for non aligned address.");
+    }
+    SimdIterator& operator+=(typename Base::difference_type d)
+    {
+        p_ += simdWidth * d;
+        return *this;
+    }
+    typename Base::difference_type operator-(SimdIterator o) { return (p_ - o.p_) / simdWidth; }
+    typename Base::reference       operator*() const { return typename Base::reference(p_); }
+
+private:
+    DataPointer          p_;
+    static constexpr int simdWidth = SimdTraits<T>::width;
+};
+
+/*! \internal
+ * \brief STL-like container for aligned SIMD type. Used as ArrayRef<SimdReal>.
+ *
+ * Should provide the same interface as ArrayRef. Any missing functions should be
+ * added as needed. The pointer type (used e.g. for initialization) is a real
+ * pointer. The reference type (used e.g. for operator[] and iterator dereference)
+ * is SimdReference which executes the aligned load/store as is appropriate. For
+ * both iterator and element access, the access happens in blocks of SIMD width.
+ * Meaning that a[1] refers to the 2nd SIMD vector and thus reals 8-15 for 8-wide
+ * SIMD. The starting address has to be aligned and the length has to be multiple
+ * of the SIMD width.
+ *
+ * \tparam T SIMD type (e.g. SimdReal)
+ */
+template<typename T>
+class SimdArrayRef
+{
+public:
+    //! Type for representing size of the container.
+    using size_type = size_t;
+    //! Type for representing difference between two container indices.
+    using difference_type = std::ptrdiff_t;
+    //! Type of values stored in the container.
+    using value_type = T;
+    //! Pointer to a container element.
+    using pointer = SimdTraitsT<T>*;
+    //! Reference to a container element.
+    using reference = internal::SimdReference<T>;
+    //! Iterator type for the container.
+    using iterator = SimdIterator<T>;
+    //! Standard reverse iterator.
+    using reverse_iterator = std::reverse_iterator<iterator>;
+
+    //! \copydoc ArrayRef::ArrayRef(pointer, pointer)
+    SimdArrayRef(pointer begin, pointer end) : begin_(begin), end_(end)
+    {
+        GMX_ASSERT(end >= begin, "Invalid range");
+        GMX_ASSERT((reinterpret_cast<size_type>(begin) / sizeof(*begin)) % simdWidth == 0,
+                   "Aligned ArrayRef requires aligned starting address");
+        GMX_ASSERT((reinterpret_cast<size_type>(end) / sizeof(*end)) % simdWidth == 0,
+                   "Size of ArrayRef needs to be divisible by type size");
+    }
+    //! \copydoc ArrayRef::ArrayRef(U)
+    template<typename U, typename = std::enable_if_t<std::is_convertible_v<typename std::remove_reference_t<U>::pointer, pointer>>>
+    SimdArrayRef(U&& o) :
+        begin_(reinterpret_cast<pointer>(o.data())),
+        end_(reinterpret_cast<pointer>(o.data() + o.size()))
+    {
+    }
+    // reinterpret_cast is only needed for const conversion of SimdArrayRef itself.
+    // All other containers have type(o.data())==U::pointer (the cast does nothing).
+
+    //! Returns the size of the container.
+    size_type size() const { return (end_ - begin_) / simdWidth; }
+    //! Whether the container is empty.
+    bool empty() const { return begin_ == end_; }
+    //! Returns an iterator to the beginning of the container.
+    iterator begin() const { return iterator(begin_); }
+    //! Returns an iterator to the end of the container.
+    iterator end() const { return iterator(end_); }
+
+    //! Access container element.
+    reference operator[](size_type n) { return reference(begin_ + n * simdWidth); }
+
+    //! Returns the first element in the container.
+    reference front() const { return reference(begin_); }
+    //! Returns the first element in the container.
+    reference back() const { return reference(end_ - simdWidth); }
+
+private:
+    static constexpr int simdWidth = SimdTraits<T>::width;
+    using pack_type                = SimdTraitsT<T>[simdWidth];
+    // Private because dereferencing return value is undefined behavior (strict aliasing rule)
+    // Only use is conversion constructor above which immediately casts it back.
+    // Return type is not "pointer" because then data()+size() would be ill defined.
+    // Has to be pack_type and not value_type in case
+    // sizeof(value_type)/sizeof(pointer)!=simdWidth (e.g. int32 for double SSE2).
+    pack_type* data() const { return reinterpret_cast<pack_type*>(begin_); }
+
+    template<typename U>
+    friend class SimdArrayRef;
+
+    pointer begin_;
+    pointer end_;
+};
+
+} // namespace internal
+
+/* Specialize ArraryRef<SimdReal>
+ * So far only an aligned version is implemented. The constructor verifies that
+ * a ArrayRef<SimdReal> is constructed only for properly aligned data.
+ */
+#if GMX_SIMD_HAVE_FLOAT
+template<>
+class ArrayRef<SimdFloat> : public internal::SimdArrayRef<SimdFloat>
+{
+    using Base = internal::SimdArrayRef<SimdFloat>;
+    using Base::Base;
+};
+template<>
+class ArrayRef<const SimdFloat> : public internal::SimdArrayRef<const SimdFloat>
+{
+    using Base = internal::SimdArrayRef<const SimdFloat>;
+    using Base::Base;
+};
+#endif
+#if GMX_SIMD_HAVE_DOUBLE
+template<>
+class ArrayRef<SimdDouble> : public internal::SimdArrayRef<SimdDouble>
+{
+    using Base = internal::SimdArrayRef<SimdDouble>;
+    using Base::Base;
+};
+template<>
+class ArrayRef<const SimdDouble> : public internal::SimdArrayRef<const SimdDouble>
+{
+    using Base = internal::SimdArrayRef<const SimdDouble>;
+    using Base::Base;
+};
+#endif
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/simd/support.h b/src/include/gromacs/simd/support.h
new file mode 100644 (file)
index 0000000..339e172
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team.
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_SUPPORT_H
+#define GMX_SIMD_SUPPORT_H
+
+
+/*! \libinternal \file
+ *
+ * \brief Functions to query compiled and supported SIMD architectures
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \inlibraryapi
+ * \ingroup module_simd
+ */
+
+#include "gromacs/hardware/cpuinfo.h"
+
+namespace gmx
+{
+
+/*! \cond libapi */
+
+/*! \brief Enumerated options for SIMD architectures */
+enum class SimdType
+{
+    None,          //!< Disable all SIMD support
+    Reference,     //!< Gromacs reference software SIMD
+    Generic,       //!< Placeholder for future support for gcc generic SIMD
+    X86_Sse2,      //!< SSE2
+    X86_Sse4_1,    //!< SSE4.1
+    X86_Avx128Fma, //!< 128-bit Avx with FMA (Amd)
+    X86_Avx,       //!< 256-bit Avx
+    X86_Avx2,      //!< AVX2
+    X86_Avx2_128,  //!< 128-bit AVX2, better than 256-bit for AMD Ryzen
+    X86_Avx512,    //!< AVX_512
+    X86_Avx512Knl, //!< AVX_512_KNL
+    Arm_NeonAsimd, //!< 64-bit ARM AArch64 Advanced SIMD
+    Arm_Sve,       //!< ARM Scalable Vector Extensions
+    Ibm_Vsx        //!< IBM VSX SIMD (Power7 and later)
+};
+
+/*! \libinternal \brief Return a string with the name of a SIMD type
+ *
+ *  \param s  SIMD type to turn into string
+ */
+const std::string& simdString(SimdType s);
+
+/*! \libinternal \brief Return the SIMD type that would fit this hardware best */
+SimdType simdSuggested(const CpuInfo& c);
+
+/*! \libinternal \brief Return the SIMD type the library was compiled with */
+SimdType simdCompiled();
+
+/*! \libinternal \brief Check if binary was compiled with the provided SIMD type
+ *
+ *  \param s              SIMD type to query. If this matches the suggested type
+ *                        for this cpu, the routine returns quietly.
+ *  \param log            If not nullptr, statistics will be printed to the file.
+ *                        If we do not have a match there will also be a warning.
+ *  \param warnToStdErr   If true, warnings will also be printed to stderr.
+ */
+bool simdCheck(SimdType s, FILE* log, bool warnToStdErr);
+
+/*! \endcond */
+
+} // namespace gmx
+
+
+#endif // GMX_SIMD_SUPPORT_H
diff --git a/src/include/gromacs/simd/tests/base.h b/src/include/gromacs/simd/tests/base.h
new file mode 100644 (file)
index 0000000..a3fb617
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_SIMD_TESTS_BASE_H
+#define GMX_SIMD_TESTS_BASE_H
+
+/*! \internal \file
+ * \brief
+ * Declares common base class for testing SIMD and SIMD4.
+ *
+ * The base class contains the settings for absolute and ulp tolerances,
+ * as well as testing ranges used for both SIMD and SIMD4 tests, mainly
+ * to keep everything symmetric and clean. The class also defines a couple
+ * of generic tests that compare vectors of elements with arbitrary length for
+ * either exact or approximate matching (in terms of ulp). These are used in
+ * derived classes that convert either SIMD or SIMD4 values to
+ * std::vector<real> and then performs the comparison.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ * \ingroup module_simd
+ */
+#include "config.h"
+
+#include <cstdint>
+
+#include <limits>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+namespace test
+{
+
+//! \internal \brief Test-time utility macro for current precision accuracy
+#define GMX_SIMD_ACCURACY_BITS_REAL \
+    (GMX_DOUBLE ? GMX_SIMD_ACCURACY_BITS_DOUBLE : GMX_SIMD_ACCURACY_BITS_SINGLE)
+
+/*! \internal
+ * \brief
+ * Base class for SIMD test fixtures.
+ *
+ * This class contains settings that are common for SIMD and SIMD4 tests,
+ * and it is thus not used directly for any tests, but derived separately
+ * in simd.h and simd4.h.
+ *
+ * \ingroup module_simd
+ */
+class SimdBaseTest : public ::testing::Test
+{
+public:
+    /*! \brief Return the default ulp tolerance for current precision
+     */
+    static constexpr std::int64_t defaultRealUlpTol()
+    {
+        return (1LL << (2 + std::numeric_limits<real>::digits - GMX_SIMD_ACCURACY_BITS_REAL));
+    }
+
+    /*! \brief Initialize new SIMD test fixture with default tolerances.
+     *
+     * The default absolute tolerance is set to 0, which means the we always
+     * check the ulp tolerance by default (passing the absolute tolerance
+     * test would otherwise mean we approve the test instantly).
+     *
+     * The default ulp tolerance is set based on the target number of
+     * bits requested for single or double precision, depending on what
+     * the default Gromacs precision is. We add two bits to avoid
+     * tests failing due to corner cases where compiler optimization might
+     * cause a slight precision loss e.g. for very small numbers.
+     *
+     * Most SIMD math functions actually achieve 2-3 ulp accuracy in single,
+     * but by being a bit liberal we only catch real errors rather than
+     * doing compiler-standard-compliance debugging.
+     *
+     * The range is used by derived classes to test math functions. The
+     * default test range will be [1,10], which is intentionally
+     * conservative so it works with (inverse) square root, division,
+     * exponentials, logarithms, and error functions.
+     */
+    SimdBaseTest() : ulpTol_(defaultRealUlpTol()), absTol_(0) {}
+
+    /*! \brief Adjust ulp tolerance from the default 10 (float) or 255 (double). */
+    void setUlpTol(std::int64_t newTol) { ulpTol_ = newTol; }
+
+    /*! \brief Adjust ulp tolerance for single accuracy functions. */
+    void setUlpTolSingleAccuracy(std::int64_t newTol)
+    {
+        const int realBits   = std::numeric_limits<real>::digits;
+        const int singleBits = std::numeric_limits<float>::digits;
+        // In single precision the expression (1LL << 0) evaluates to 1.
+        setUlpTol(newTol * (1LL << (realBits - singleBits)));
+    }
+
+    /*! \brief Adjust the absolute tolerance from the default 0.
+     *
+     * If values are closer than the absolute tolerance, the test will pass
+     * no matter what their ulp difference is.
+     */
+    void setAbsTol(real newTol) { absTol_ = newTol; }
+
+    /*! \brief Number of test points to use, settable on command line.
+     *
+     * \note While this has to be a static non-const variable for the
+     *       command-line option to work, you should never change it
+     *       manually in any of the tests, because the static storage
+     *       class will make the value apply to all subsequent tests
+     *       unless you remember to reset it.
+     */
+    static int s_nPoints;
+
+    /*! \brief Compare two std::vector<real> for approximate equality.
+     *
+     * This is an internal implementation routine that will be used by
+     * routines in derived child classes that first convert SIMD or SIMD4
+     * variables to std::vector<real>. Do not call it directly.
+     *
+     * This routine is designed according to the Google test specs, so the char
+     * strings will describe the arguments to the macro.
+     *
+     * The comparison is applied to each element, and it returns true if each element
+     * in the vector test variable is within the class tolerances of the corresponding
+     * reference elements.
+     */
+    ::testing::AssertionResult compareVectorRealUlp(const char*              refExpr,
+                                                    const char*              tstExpr,
+                                                    const std::vector<real>& ref,
+                                                    const std::vector<real>& tst) const;
+
+    /*! \brief Compare std::vectors for exact equality.
+     *
+     * The template in this class makes it usable for testing both
+     * SIMD floating-point and integers variables, after conversion to
+     * vectors.
+     * This is an internal implementation routine that will be used by
+     * routines in derived child classes that first convert SIMD or SIMD4
+     * variables to std::vector<real>. Do not call it directly.
+     *
+     * This routine is designed according to the Google test specs, so the char
+     * strings will describe the arguments to the macro.
+     *
+     * The comparison is applied to each element, and it returns true if each element
+     * in the vector test variable is within the class tolerances of the corresponding
+     * reference elements.
+     */
+    template<typename T>
+    ::testing::AssertionResult compareVectorEq(const char*           refExpr,
+                                               const char*           tstExpr,
+                                               const std::vector<T>& ref,
+                                               const std::vector<T>& tst)
+    {
+        if (ref == tst)
+        {
+            return ::testing::AssertionSuccess();
+        }
+        else
+        {
+            return ::testing::AssertionFailure()
+                   << "Failing SIMD comparison between " << refExpr << " and " << tstExpr << std::endl
+                   << "Ref. values: " << ::testing::PrintToString(ref) << std::endl
+                   << "Test values: " << ::testing::PrintToString(tst) << std::endl;
+        }
+    }
+
+protected:
+    std::int64_t ulpTol_; //!< Current tolerance in units-in-last-position.
+    real         absTol_; //!< Current absolute tolerance.
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_SIMD_TESTS_BASE_H
diff --git a/src/include/gromacs/simd/tests/data.h b/src/include/gromacs/simd/tests/data.h
new file mode 100644 (file)
index 0000000..9793550
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_TESTS_DATA_H
+#define GMX_SIMD_TESTS_DATA_H
+
+/*! \internal \file
+ * \brief Common test data constants for SIMD, SIMD4 and scalar tests
+ *
+ * To avoid silent bugs due to double/float truncation if we ever use the
+ * wrong return type of routines, we want to use test data that fills all
+ * available bits in either single or double precision. The values themselves
+ * are meaningless.
+ * Note that the data is used to initialize the SIMD constants, which for
+ * technical (alignment) reasons in some compilers cannot be placed inside
+ * the text fixture classes. For that reason this data cannot go in the
+ * fixtures either.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ * \ingroup module_simd
+ */
+
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+namespace test
+{
+
+/*! \cond internal */
+/*! \addtogroup module_simd */
+/*! \{ */
+constexpr real czero = 0.0;                //!< zero
+constexpr real c0    = 0.3333333333333333; //!< test constant 0.0 + 1.0/3.0
+constexpr real c1    = 1.7142857142857144; //!< test constant 1.0 + 5.0/7.0
+constexpr real c2    = 2.6923076923076925; //!< test constant 2.0 + 9.0/13.0
+constexpr real c3    = 3.8947368421052633; //!< test constant 3.0 + 17.0/19.0
+constexpr real c4    = 4.793103448275862;  //!< test constant 4.0 + 23.0/29.0
+constexpr real c5    = 5.837837837837838;  //!< test constant 5.0 + 31.0/37.0
+constexpr real c6    = 6.953488372093023;  //!< test constant 6.0 + 41.0/43.0
+constexpr real c7    = 7.886792452830189;  //!< test constant 7.0 + 47.0/53.0
+constexpr real c8    = 8.967213114754099;  //!< test constant 8.0 + 59.0/61.0
+
+/*! \} */
+/*! \endcond */
+
+} // namespace test
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/simd/tests/simd.h b/src/include/gromacs/simd/tests/simd.h
new file mode 100644 (file)
index 0000000..6e1d32d
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_TESTS_SIMD_H
+#define GMX_SIMD_TESTS_SIMD_H
+
+/*! \internal \file
+ * \brief
+ * Declares fixture for testing of normal SIMD (not SIMD4) functionality.
+ *
+ * The SIMD tests are both simple and complicated. The actual testing logic
+ * is \a very straightforward since we just need to test single values against
+ * the math library, and for some math functions we need to do it in a loop.
+ * This could have been achieved in minutes with the default Google Test tools,
+ * if it wasn't for the problem that we cannot access or compare SIMD contents
+ * directly without using lots of other SIMD functionality. For this reason
+ * we have separate the basic testing of load/store operations into a separate
+ * bootstrapping test. Once this works, we use a set of utility routines to
+ * convert SIMD contents to/from std:vector<> and perform the rest of the tests,
+ * which then can farmed out to the base class SimdBaseTest that is common
+ * to SIMD and SIMD4.
+ *
+ * Another complication is that the width of the SIMD implementation will
+ * depend on the hardware and precision. For some simple operations it is
+ * sufficient to set all SIMD elements to the same value, and check that the
+ * result is present in all elements. However, for a few more complex
+ * instructions that might rely on shuffling under-the-hood it is important
+ * that we can test operations with different elements. We achieve this by
+ * having test code that can initialize a SIMD variable from an std::vector
+ * of arbitrary length; the vector is simply repeated to fill all elements in
+ * the SIMD variable. We also have similar routines to compare a SIMD result
+ * with values in a vector, which returns true iff all elements match.
+ *
+ * This way we can write simple tests that use different values for all SIMD
+ * elements. Personally I like using vectors of length 3, since this means
+ * there are no simple repeated patterns in low/high halves of SIMD variables
+ * that are 2,4,8,or 16 elements wide, and we still don't have to care about
+ * the exact SIMD width of the underlying implementation.
+ *
+ * Note that this utility uses a few SIMD load/store instructions internally -
+ * those have been tested separately in the bootstrap_loadstore.cpp file.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ * \ingroup module_simd
+ */
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/simd/simd.h"
+
+#include "base.h"
+#include "data.h"
+
+#if GMX_SIMD
+
+namespace gmx
+{
+namespace test
+{
+
+
+/*! \cond internal */
+/*! \addtogroup module_simd */
+/*! \{ */
+
+/* Unfortunately we cannot keep static SIMD constants in the test fixture class.
+ * The problem is that SIMD memory need to be aligned, and in particular
+ * this applies to automatic storage of variables in classes. For SSE registers
+ * this means 16-byte alignment (which seems to work), but AVX requires 32-bit
+ * alignment. At least both gcc-4.7.3 and Apple clang-5.0 (OS X 10.9) fail to
+ * align these variables when they are stored as data in a class.
+ *
+ * In theory we could set some of these on-the-fly e.g. with setSimdFrom3R()
+ * instead (although that would mean repeating code between tests), but many of
+ * the constants depend on the current precision not to mention they
+ * occasionally have many digits that need to be exactly right, and keeping
+ * them in a single place makes sure they are consistent.
+ */
+#    if GMX_SIMD_HAVE_REAL
+extern const SimdReal rSimd_c0c1c2; //!< c0,c1,c2 repeated
+extern const SimdReal rSimd_c3c4c5; //!< c3,c4,c5 repeated
+extern const SimdReal rSimd_c6c7c8; //!< c6,c7,c8 repeated
+extern const SimdReal rSimd_c3c0c4; //!< c3,c0,c4 repeated
+extern const SimdReal rSimd_c4c6c8; //!< c4,c6,c8 repeated
+extern const SimdReal rSimd_c7c2c3; //!< c7,c2,c3 repeated
+extern const SimdReal rSimd_m0m1m2; //!< -c0,-c1,-c2 repeated
+extern const SimdReal rSimd_m3m0m4; //!< -c3,-c0,-c4 repeated
+
+extern const SimdReal rSimd_2p25;  //!< Value that rounds down.
+extern const SimdReal rSimd_3p25;  //!< Value that rounds down.
+extern const SimdReal rSimd_3p75;  //!< Value that rounds up.
+extern const SimdReal rSimd_m2p25; //!< Negative value that rounds up.
+extern const SimdReal rSimd_m3p25; //!< Negative value that rounds up.
+extern const SimdReal rSimd_m3p75; //!< Negative value that rounds down.
+//! Three large floating-point values whose exponents are >32.
+extern const SimdReal rSimd_Exp;
+
+#        if GMX_SIMD_HAVE_LOGICAL
+extern const SimdReal rSimd_logicalA;         //!< Bit pattern to test logical ops
+extern const SimdReal rSimd_logicalB;         //!< Bit pattern to test logical ops
+extern const SimdReal rSimd_logicalResultOr;  //!< Result or bitwise 'or' of A and B
+extern const SimdReal rSimd_logicalResultAnd; //!< Result or bitwise 'and' of A and B
+#        endif                                // GMX_SIMD_HAVE_LOGICAL
+
+#        if GMX_SIMD_HAVE_DOUBLE && GMX_DOUBLE
+// Make sure we also test exponents outside single precision when we use double
+extern const SimdReal rSimd_ExpDouble1;
+extern const SimdReal rSimd_ExpDouble2;
+#        endif
+// Magic FP numbers corresponding to specific bit patterns
+extern const SimdReal rSimd_Bits1; //!< Pattern F0 repeated to fill single/double.
+extern const SimdReal rSimd_Bits2; //!< Pattern CC repeated to fill single/double.
+extern const SimdReal rSimd_Bits3; //!< Pattern C0 repeated to fill single/double.
+extern const SimdReal rSimd_Bits4; //!< Pattern 0C repeated to fill single/double.
+extern const SimdReal rSimd_Bits5; //!< Pattern FC repeated to fill single/double.
+extern const SimdReal rSimd_Bits6; //!< Pattern 3C repeated to fill single/double.
+#    endif                         // GMX_SIMD_HAVE_REAL
+#    if GMX_SIMD_HAVE_INT32_ARITHMETICS
+extern const SimdInt32 iSimd_1_2_3;    //!< Three generic ints.
+extern const SimdInt32 iSimd_4_5_6;    //!< Three generic ints.
+extern const SimdInt32 iSimd_7_8_9;    //!< Three generic ints.
+extern const SimdInt32 iSimd_5_7_9;    //!< iSimd_1_2_3 + iSimd_4_5_6.
+extern const SimdInt32 iSimd_1M_2M_3M; //!< Term1 for 32bit add/sub.
+extern const SimdInt32 iSimd_4M_5M_6M; //!< Term2 for 32bit add/sub.
+extern const SimdInt32 iSimd_5M_7M_9M; //!< iSimd_1M_2M_3M + iSimd_4M_5M_6M.
+#    endif
+#    if GMX_SIMD_HAVE_INT32_LOGICAL
+extern const SimdInt32 iSimd_0xF0F0F0F0; //!< Bitpattern to test integer logical operations.
+extern const SimdInt32 iSimd_0xCCCCCCCC; //!< Bitpattern to test integer logical operations.
+#    endif
+
+
+/*! \internal
+ * \brief
+ * Test fixture for SIMD tests.
+ *
+ * This is a very simple test fixture that basically just takes the common
+ * SIMD/SIMD4 functionality from SimdBaseTest and creates wrapper routines
+ * specific for normal SIMD functionality.
+ */
+class SimdTest : public SimdBaseTest
+{
+public:
+#    if GMX_SIMD_HAVE_REAL
+    /*! \brief Compare two real SIMD variables for approximate equality.
+     *
+     * This is an internal implementation routine. YOu should always use
+     * GMX_EXPECT_SIMD_REAL_NEAR() instead.
+     *
+     * This routine is designed according to the Google test specs, so the char
+     * strings will describe the arguments to the macro.
+     *
+     * The comparison is applied to each element, and it returns true if each element
+     * in the SIMD test variable is within the class tolerances of the corresponding
+     * reference element.
+     */
+
+
+    ::testing::AssertionResult
+    compareSimdRealUlp(const char* refExpr, const char* tstExpr, SimdReal ref, SimdReal tst);
+
+    /*! \brief Compare two real SIMD variables for exact equality.
+     *
+     * This is an internal implementation routine. YOu should always use
+     * GMX_EXPECT_SIMD_REAL_NEAR() instead.
+     *
+     * This routine is designed according to the Google test specs, so the char
+     * strings will describe the arguments to the macro.
+     *
+     * The comparison is applied to each element, and it returns true if each element
+     * in the SIMD test variable is within the class tolerances of the corresponding
+     * reference element.
+     */
+    ::testing::AssertionResult compareSimdEq(const char* refExpr, const char* tstExpr, SimdReal ref, SimdReal tst);
+
+    /*! \brief Compare two 32-bit integer SIMD variables.
+     *
+     * This is an internal implementation routine. YOu should always use
+     * GMX_EXPECT_SIMD_INT_EQ() instead.
+     *
+     * This routine is designed according to the Google test specs, so the char
+     * strings will describe the arguments to the macro, while the SIMD and
+     * tolerance arguments are used to decide if the values are approximately equal.
+     *
+     * The comparison is applied to each element, and it returns true if each element
+     * in the SIMD variable tst is identical to the corresponding reference element.
+     */
+    ::testing::AssertionResult compareSimdEq(const char* refExpr, const char* tstExpr, SimdInt32 ref, SimdInt32 tst);
+#    endif
+};
+
+#    if GMX_SIMD_HAVE_REAL
+/*! \brief Convert SIMD real to std::vector<real>.
+ *
+ * The returned vector will have the same length as the SIMD width.
+ */
+std::vector<real> simdReal2Vector(SimdReal simd);
+
+/*! \brief Return floating-point SIMD value from std::vector<real>.
+ *
+ * If the vector is longer than SIMD width, only the first elements will be used.
+ * If it is shorter, the contents will be repeated to fill the SIMD register.
+ */
+SimdReal vector2SimdReal(const std::vector<real>& v);
+
+/*! \brief Set SIMD register contents from three real values.
+ *
+ * Our reason for using three values is that 3 is not a factor in any known
+ * SIMD width, so this way there will not be any simple repeated patterns e.g.
+ * between the low/high 64/128/256 bits in the SIMD register, which could hide bugs.
+ */
+SimdReal setSimdRealFrom3R(real r0, real r1, real r2);
+
+/*! \brief Set SIMD register contents from single real value.
+ *
+ * All elements is set from the given value. This is effectively the same
+ * operation as simdSet1(), but is implemented using only load/store
+ * operations that have been tested separately in the bootstrapping tests.
+ */
+SimdReal setSimdRealFrom1R(real value);
+
+/*! \brief Test if a SIMD real is bitwise identical to reference SIMD value. */
+#        define GMX_EXPECT_SIMD_REAL_EQ(ref, tst) EXPECT_PRED_FORMAT2(compareSimdEq, ref, tst)
+
+/*! \brief Test if a SIMD is bitwise identical to reference SIMD value. */
+#        define GMX_EXPECT_SIMD_EQ(ref, tst) EXPECT_PRED_FORMAT2(compareSimdEq, ref, tst)
+
+/*! \brief Test if a SIMD real is within tolerance of reference SIMD value. */
+#        define GMX_EXPECT_SIMD_REAL_NEAR(ref, tst) \
+            EXPECT_PRED_FORMAT2(compareSimdRealUlp, ref, tst)
+
+/*! \brief Convert SIMD integer to std::vector<int>.
+ *
+ * The returned vector will have the same length as the SIMD width.
+ */
+std::vector<std::int32_t> simdInt2Vector(SimdInt32 simd);
+
+/*! \brief Return 32-bit integer SIMD value from std::vector<int>.
+ *
+ * If the vector is longer than SIMD width, only the first elements will be used.
+ * If it is shorter, the contents will be repeated to fill the SIMD register.
+ */
+SimdInt32 vector2SimdInt(const std::vector<std::int32_t>& v);
+
+/*! \brief Set SIMD register contents from three int values.
+ *
+ * Our reason for using three values is that 3 is not a factor in any known
+ * SIMD width, so this way there will not be any simple repeated patterns e.g.
+ * between the low/high 64/128/256 bits in the SIMD register, which could hide bugs.
+ */
+SimdInt32 setSimdIntFrom3I(int i0, int i1, int i2);
+
+/*! \brief Set SIMD register contents from single integer value.
+ *
+ * All elements is set from the given value. This is effectively the same
+ * operation as simdSet1I(), but is implemented using only load/store
+ * operations that have been tested separately in the bootstrapping tests.
+ */
+SimdInt32 setSimdIntFrom1I(int value);
+
+/*! \brief Macro that checks SIMD integer expression against SIMD or reference int.
+ *
+ * If the reference argument is a scalar integer it will be expanded into
+ * the width of the SIMD register and tested against all elements.
+ */
+#        define GMX_EXPECT_SIMD_INT_EQ(ref, tst) EXPECT_PRED_FORMAT2(compareSimdEq, ref, tst)
+
+#    endif // GMX_SIMD_HAVE_REAL
+
+/*! \} */
+/*! \endcond */
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_SIMD
+
+#endif // GMX_SIMD_TESTS_SIMD_H
diff --git a/src/include/gromacs/simd/tests/simd4.h b/src/include/gromacs/simd/tests/simd4.h
new file mode 100644 (file)
index 0000000..6378bf9
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_SIMD_TESTS_SIMD4_H
+#define GMX_SIMD_TESTS_SIMD4_H
+
+/*! \internal \file
+ * \brief
+ * Declares fixture for testing of SIMD4 functionality.
+ *
+ * This files specializes the common base test utilities to be used
+ * for SIMD4 variables. For detailed documentation, check out the normal
+ * SIMD test classes and files.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ * \ingroup module_simd
+ */
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/simd/simd.h"
+
+#include "base.h"
+#include "data.h"
+
+#if GMX_SIMD
+
+namespace gmx
+{
+namespace test
+{
+
+/*! \cond internal */
+/*! \addtogroup module_simd */
+/*! \{ */
+
+#    if GMX_SIMD4_HAVE_REAL
+extern const Simd4Real rSimd4_c0c1c2; //!< c0,c1,c2 repeated
+extern const Simd4Real rSimd4_c3c4c5; //!< c3,c4,c5 repeated
+extern const Simd4Real rSimd4_c6c7c8; //!< c6,c7,c8 repeated
+extern const Simd4Real rSimd4_c3c0c4; //!< c3,c0,c4 repeated
+extern const Simd4Real rSimd4_c4c6c8; //!< c4,c6,c8 repeated
+extern const Simd4Real rSimd4_c7c2c3; //!< c7,c2,c3 repeated
+extern const Simd4Real rSimd4_m0m1m2; //!< -c0,-c1,-c2 repeated
+extern const Simd4Real rSimd4_m3m0m4; //!< -c3,-c0,-c4 repeated
+extern const Simd4Real rSimd4_2p25;   //!< Value that rounds down.
+extern const Simd4Real rSimd4_3p75;   //!< Value that rounds up.
+extern const Simd4Real rSimd4_m2p25;  //!< Negative value that rounds up.
+extern const Simd4Real rSimd4_m3p75;  //!< Negative value that rounds down.
+//! Three large floating-point values whose exponents are >32.
+extern const Simd4Real rSimd4_Exp;
+
+#        if GMX_SIMD_HAVE_LOGICAL
+extern const Simd4Real rSimd4_logicalA;         //!< Bit pattern to test logical ops
+extern const Simd4Real rSimd4_logicalB;         //!< Bit pattern to test logical ops
+extern const Simd4Real rSimd4_logicalResultOr;  //!< Result or bitwise 'or' of A and B
+extern const Simd4Real rSimd4_logicalResultAnd; //!< Result or bitwise 'and' of A and B
+#        endif                                  // GMX_SIMD_HAVE_LOGICAL
+
+extern const Simd4Real rSimd4_Bits1; //!< Pattern F0 repeated to fill single/double.
+extern const Simd4Real rSimd4_Bits2; //!< Pattern CC repeated to fill single/double.
+extern const Simd4Real rSimd4_Bits3; //!< Pattern C0 repeated to fill single/double.
+extern const Simd4Real rSimd4_Bits4; //!< Pattern 0C repeated to fill single/double.
+extern const Simd4Real rSimd4_Bits5; //!< Pattern FC repeated to fill single/double.
+extern const Simd4Real rSimd4_Bits6; //!< Pattern 3C repeated to fill single/double.
+
+/*! \internal
+ * \brief
+ * Test fixture for SIMD4 tests - contains test settings.
+ *
+ * This is a very simple test fixture that basically just takes the common
+ * SIMD/SIMD4 functionality from SimdBaseTest and creates wrapper routines
+ * specific for SIMD4 functionality.
+ */
+class Simd4Test : public SimdBaseTest
+{
+public:
+    /*! \brief Compare two real SIMD4 variables for approximate equality.
+     *
+     * This is an internal implementation routine. YOu should always use
+     * GMX_EXPECT_SIMD4_REAL_NEAR() instead.
+     *
+     * This routine is designed according to the Google test specs, so the char
+     * strings will describe the arguments to the macro.
+     *
+     * The comparison is applied to each element, and it returns true if each element
+     * in the SIMD4 test variable is within the class tolerances of the corresponding
+     * reference element.
+     */
+    ::testing::AssertionResult
+    compareSimd4RealUlp(const char* refExpr, const char* tstExpr, Simd4Real ref, Simd4Real tst);
+
+    /*! \brief Compare two real SIMD4 variables for exact equality.
+     *
+     * This is an internal implementation routine. YOu should always use
+     * GMX_EXPECT_SIMD4_REAL_NEAR() instead.
+     *
+     * This routine is designed according to the Google test specs, so the char
+     * strings will describe the arguments to the macro.
+     *
+     * The comparison is applied to each element, and it returns true if each element
+     * in the SIMD4 test variable is within the class tolerances of the corresponding
+     * reference element.
+     */
+    ::testing::AssertionResult
+    compareSimd4RealEq(const char* refExpr, const char* tstExpr, Simd4Real ref, Simd4Real tst);
+};
+
+/*! \brief Convert SIMD4 real to std::vector<real>.
+ *
+ * The returned vector will have the same length as the SIMD4 width.
+ */
+std::vector<real> simd4Real2Vector(Simd4Real simd4);
+
+/*! \brief Return floating-point SIMD4 value from std::vector<real>.
+ *
+ * If the vector is longer than SIMD4 width, only the first elements will be used.
+ * If it is shorter, the contents will be repeated to fill the SIMD4 register.
+ */
+Simd4Real vector2Simd4Real(const std::vector<real>& v);
+
+/*! \brief Set SIMD4 register contents from three real values.
+ *
+ * It might seem stupid to use three values when we know that the SIMD4 width
+ * is 4, but it simplifies the test organization when the SIMD and SIMD4 tests
+ * are completely symmetric.
+ */
+Simd4Real setSimd4RealFrom3R(real r0, real r1, real r2);
+
+/*! \brief Set SIMD4 register contents from single real value.
+ *
+ * All elements is set from the given value. This is effectively the same
+ * operation as simd4Set1(), but is implemented using only load/store
+ * operations that have been tested separately in the bootstrapping tests.
+ */
+Simd4Real setSimd4RealFrom1R(real value);
+
+/*! \brief Test if a SIMD4 real is bitwise identical to reference SIMD4 value. */
+#        define GMX_EXPECT_SIMD4_REAL_EQ(ref, tst) EXPECT_PRED_FORMAT2(compareSimd4RealEq, ref, tst)
+
+/*! \brief Test if a SIMD4 real is within tolerance of reference SIMD4 value. */
+#        define GMX_EXPECT_SIMD4_REAL_NEAR(ref, tst) \
+            EXPECT_PRED_FORMAT2(compareSimd4RealUlp, ref, tst)
+
+#    endif // GMX_SIMD4_HAVE_REAL
+
+/*! \} */
+/*! \endcond */
+
+} // namespace test
+} // namespace gmx
+
+#endif // GMX_SIMD
+
+#endif // GMX_SIMD_TESTS_SIMD4_H
diff --git a/src/include/gromacs/simd/vector_operations.h b/src/include/gromacs/simd/vector_operations.h
new file mode 100644 (file)
index 0000000..a3fcc65
--- /dev/null
@@ -0,0 +1,303 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2017,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ *
+ * \brief SIMD operations corresponding to Gromacs rvec datatypes.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ *
+ * \inlibraryapi
+ * \ingroup module_simd
+ */
+
+#ifndef GMX_SIMD_VECTOR_OPERATIONS_H
+#define GMX_SIMD_VECTOR_OPERATIONS_H
+
+#include "config.h"
+
+#include "gromacs/simd/simd.h"
+
+namespace gmx
+{
+
+#if GMX_SIMD
+
+/*! \cond libapi */
+/*! \addtogroup module_simd */
+/*! \{ */
+
+/* This check is not actually required, but it must be true if the
+ * code below actualy declares anything, and it makes it easy for
+ * check-source to know that this file depends on simd.h (though
+ * symbols like GMX_SIMD_HAVE_FLOAT are actually defined in its
+ * implementation headers). */
+#    if GMX_SIMD_HAVE_REAL || defined DOXYGEN
+
+#        if GMX_SIMD_HAVE_FLOAT || defined DOXYGEN
+/*! \brief SIMD float inner product of multiple float vectors.
+ *
+ * \param ax X components of first vectors
+ * \param ay Y components of first vectors
+ * \param az Z components of first vectors
+ * \param bx X components of second vectors
+ * \param by Y components of second vectors
+ * \param bz Z components of second vectors
+ *
+ * \return Element i will be res[i] = ax[i]*bx[i]+ay[i]*by[i]+az[i]*bz[i].
+ *
+ * \note The SIMD part is that we calculate many scalar products in one call.
+ */
+static inline SimdFloat gmx_simdcall
+iprod(SimdFloat ax, SimdFloat ay, SimdFloat az, SimdFloat bx, SimdFloat by, SimdFloat bz)
+{
+    SimdFloat ret;
+
+    ret = ax * bx;
+    ret = ay * by + ret;
+    ret = az * bz + ret;
+
+    return ret;
+}
+
+/*! \brief SIMD float norm squared of multiple vectors.
+ *
+ * \param ax X components of vectors
+ * \param ay Y components of vectors
+ * \param az Z components of vectors
+ *
+ * \return Element i will be res[i] = ax[i]*ax[i]+ay[i]*ay[i]+az[i]*az[i].
+ *
+ * \note This corresponds to the scalar product of the vector with itself, but
+ * the compiler might be able to optimize it better with identical vectors.
+ */
+static inline SimdFloat gmx_simdcall norm2(SimdFloat ax, SimdFloat ay, SimdFloat az)
+{
+    SimdFloat ret;
+
+    ret = ax * ax;
+    ret = ay * ay + ret;
+    ret = az * az + ret;
+
+    return ret;
+}
+
+/*! \brief SIMD float cross-product of multiple vectors.
+ *
+ * \param ax X components of first vectors
+ * \param ay Y components of first vectors
+ * \param az Z components of first vectors
+ * \param bx X components of second vectors
+ * \param by Y components of second vectors
+ * \param bz Z components of second vectors
+ * \param[out] cx X components of cross product vectors
+ * \param[out] cy Y components of cross product vectors
+ * \param[out] cz Z components of cross product vectors
+ *
+ * \returns void
+ *
+ * This calculates C = A x B, where the cross denotes the cross product.
+ * The arguments x/y/z denotes the different components, and each element
+ * corresponds to a separate vector.
+ */
+static inline void gmx_simdcall cprod(SimdFloat  ax,
+                                      SimdFloat  ay,
+                                      SimdFloat  az,
+                                      SimdFloat  bx,
+                                      SimdFloat  by,
+                                      SimdFloat  bz,
+                                      SimdFloat* cx,
+                                      SimdFloat* cy,
+                                      SimdFloat* cz)
+{
+    *cx = ay * bz;
+    *cx = fnma(az, by, *cx);
+
+    *cy = az * bx;
+    *cy = fnma(ax, bz, *cy);
+
+    *cz = ax * by;
+    *cz = fnma(ay, bx, *cz);
+}
+#        endif // GMX_SIMD_HAVE_FLOAT
+
+#        if GMX_SIMD_HAVE_DOUBLE || defined DOXYGEN
+/*! \brief SIMD double inner product of multiple double vectors.
+ *
+ * \param ax X components of first vectors
+ * \param ay Y components of first vectors
+ * \param az Z components of first vectors
+ * \param bx X components of second vectors
+ * \param by Y components of second vectors
+ * \param bz Z components of second vectors
+ *
+ * \return Element i will be res[i] = ax[i]*bx[i]+ay[i]*by[i]+az[i]*bz[i].
+ *
+ * \note The SIMD part is that we calculate many scalar products in one call.
+ */
+static inline SimdDouble gmx_simdcall
+iprod(SimdDouble ax, SimdDouble ay, SimdDouble az, SimdDouble bx, SimdDouble by, SimdDouble bz)
+{
+    SimdDouble ret;
+
+    ret = ax * bx;
+    ret = ay * by + ret;
+    ret = az * bz + ret;
+
+    return ret;
+}
+
+/*! \brief SIMD double norm squared of multiple vectors.
+ *
+ * \param ax X components of vectors
+ * \param ay Y components of vectors
+ * \param az Z components of vectors
+ *
+ * \return Element i will be res[i] = ax[i]*ax[i]+ay[i]*ay[i]+az[i]*az[i].
+ *
+ * \note This corresponds to the scalar product of the vector with itself, but
+ * the compiler might be able to optimize it better with identical vectors.
+ */
+static inline SimdDouble gmx_simdcall norm2(SimdDouble ax, SimdDouble ay, SimdDouble az)
+{
+    SimdDouble ret;
+
+    ret = ax * ax;
+    ret = ay * ay + ret;
+    ret = az * az + ret;
+
+    return ret;
+}
+
+/*! \brief SIMD double cross-product of multiple vectors.
+ *
+ * \param ax X components of first vectors
+ * \param ay Y components of first vectors
+ * \param az Z components of first vectors
+ * \param bx X components of second vectors
+ * \param by Y components of second vectors
+ * \param bz Z components of second vectors
+ * \param[out] cx X components of cross product vectors
+ * \param[out] cy Y components of cross product vectors
+ * \param[out] cz Z components of cross product vectors
+ *
+ * \returns void
+ *
+ * This calculates C = A x B, where the cross denotes the cross product.
+ * The arguments x/y/z denotes the different components, and each element
+ * corresponds to a separate vector.
+ */
+static inline void gmx_simdcall cprod(SimdDouble  ax,
+                                      SimdDouble  ay,
+                                      SimdDouble  az,
+                                      SimdDouble  bx,
+                                      SimdDouble  by,
+                                      SimdDouble  bz,
+                                      SimdDouble* cx,
+                                      SimdDouble* cy,
+                                      SimdDouble* cz)
+{
+    *cx = ay * bz;
+    *cx = *cx - az * by;
+
+    *cy = az * bx;
+    *cy = *cy - ax * bz;
+
+    *cz = ax * by;
+    *cz = *cz - ay * bx;
+}
+#        endif // GMX_SIMD_HAVE_DOUBLE
+
+
+#        if GMX_SIMD4_HAVE_FLOAT || defined DOXYGEN
+/*! \brief SIMD4 float norm squared of multiple vectors.
+ *
+ * \param ax X components of vectors
+ * \param ay Y components of vectors
+ * \param az Z components of vectors
+ *
+ * \return Element i will be res[i] = ax[i]*ax[i]+ay[i]*ay[i]+az[i]*az[i].
+ *
+ * \note This corresponds to the scalar product of the vector with itself, but
+ * the compiler might be able to optimize it better with identical vectors.
+ */
+static inline Simd4Float gmx_simdcall norm2(Simd4Float ax, Simd4Float ay, Simd4Float az)
+{
+    Simd4Float ret;
+
+    ret = ax * ax;
+    ret = ay * ay + ret;
+    ret = az * az + ret;
+
+    return ret;
+}
+
+#        endif // GMX_SIMD4_HAVE_FLOAT
+
+#        if GMX_SIMD4_HAVE_DOUBLE || defined DOXYGEN
+/*! \brief SIMD4 double norm squared of multiple vectors.
+ *
+ * \param ax X components of vectors
+ * \param ay Y components of vectors
+ * \param az Z components of vectors
+ *
+ * \return Element i will be res[i] = ax[i]*ax[i]+ay[i]*ay[i]+az[i]*az[i].
+ *
+ * \note This corresponds to the scalar product of the vector with itself, but
+ * the compiler might be able to optimize it better with identical vectors.
+ */
+static inline Simd4Double gmx_simdcall norm2(Simd4Double ax, Simd4Double ay, Simd4Double az)
+{
+    Simd4Double ret;
+
+    ret = ax * ax;
+    ret = ay * ay + ret;
+    ret = az * az + ret;
+
+    return ret;
+}
+
+#        endif // GMX_SIMD4_HAVE_DOUBLE
+
+#    endif // GMX_SIMD_HAVE REAL || defined DOXYGEN
+
+/*! \} */
+/*! \endcond */
+
+#endif // GMX_SIMD
+
+} // namespace gmx
+
+#endif // GMX_SIMD_VECTOR_OPERATIONS_H
diff --git a/src/include/gromacs/statistics/statistics.h b/src/include/gromacs/statistics/statistics.h
new file mode 100644 (file)
index 0000000..ca0909c
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares simple statistics toolbox
+ *
+ * \authors David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \inlibraryapi
+ */
+#ifndef GMX_STATISTICS_H
+#define GMX_STATISTICS_H
+
+#include <cstdio>
+
+#include <tuple>
+
+#include "gromacs/utility/real.h"
+
+//! Abstract container type
+typedef struct gmx_stats* gmx_stats_t;
+
+//! Enum for statistical weights
+enum
+{
+    elsqWEIGHT_NONE,
+    elsqWEIGHT_X,
+    elsqWEIGHT_Y,
+    elsqWEIGHT_XY,
+    elsqWEIGHT_NR
+};
+
+/*! \brief
+ * Initiate a data structure
+ * \return the data structure
+ */
+gmx_stats_t gmx_stats_init();
+
+/*! \brief
+ * Destroy a data structure
+ * \param stats The data structure
+ */
+void gmx_stats_free(gmx_stats_t stats);
+
+/*! \brief
+ * Add a point to the data set
+ * \param[in] stats The data structure
+ * \param[in] x   The x value
+ * \param[in] y   The y value
+ * \param[in] dx  The error in the x value
+ * \param[in] dy  The error in the y value
+ */
+void gmx_stats_add_point(gmx_stats_t stats, double x, double y, double dx, double dy);
+
+/*! \brief
+ * Fit the data to y = ax + b, possibly weighted, if uncertainties
+ * have been input. da and db may be NULL.
+ * \param[in] stats The data structure
+ * \param[in] weight type of weighting
+ * \param[out] a slope
+ * \param[out] b intercept
+ * \param[out] da sigma in a
+ * \param[out] db sigma in b
+ * \param[out] chi2 normalized quality of fit
+ * \param[out] Rfit correlation coefficient
+ */
+void gmx_stats_get_ab(gmx_stats_t stats, int weight, real* a, real* b, real* da, real* db, real* chi2, real* Rfit);
+
+/*! \brief
+ * Computes and returns the average value.
+ * \param[in]  stats The data structure
+ * \return Average value
+ * \throws  InconsistentInputError if given no points to average
+ */
+real gmx_stats_get_average(gmx_stats_t stats);
+
+/*! \brief
+ * Pointers may be null, in which case no assignment will be done.
+ * \param[in]  stats The data structure
+ * \return Tuple of (average value, its standard deviation, its standard error)
+ * \throws  InconsistentInputError if given no points to analyze
+ */
+std::tuple<real, real, real> gmx_stats_get_ase(gmx_stats_t stats);
+
+/****************************************************
+ * Some statistics utilities for convenience: useful when a complete data
+ * set is available already from another source, e.g. an xvg file.
+ ****************************************************/
+
+/*! \brief
+ * Fit a straight line y=ax+b thru the n data points x, y.
+ * \param[in] n number of points
+ * \param[in] x data points x
+ * \param[in] y data point y
+ * \param[out] a slope
+ * \param[out] b intercept
+ * \param[out] r correlation coefficient
+ * \param[out] chi2 quality of fit
+ *
+ * \throws  InconsistentInputError if given no points to fit
+ */
+void lsq_y_ax_b(int n, real x[], real y[], real* a, real* b, real* r, real* chi2);
+
+/*! \copydoc lsq_y_ax_b
+ * Suits cases where x is already always computed in double precision
+ * even in a mixed-precision build configuration.
+ */
+void lsq_y_ax_b_xdouble(int n, double x[], real y[], real* a, real* b, real* r, real* chi2);
+
+/*! \brief
+ * Fit a straight line y=ax+b thru the n data points x, y.
+ * \param[in] n number of points
+ * \param[in] x data points x
+ * \param[in] y data point y
+ * \param[in] dy uncertainty in data point y
+ * \param[out] a slope
+ * \param[out] b intercept
+ * \param[out] da error in slope
+ * \param[out] db error in intercept
+ * \param[out] r correlation coefficient
+ * \param[out] chi2 quality of fit
+ *
+ * \throws  InconsistentInputError if given no points to fit
+ */
+void lsq_y_ax_b_error(int n, real x[], real y[], real dy[], real* a, real* b, real* da, real* db, real* r, real* chi2);
+
+#endif
diff --git a/src/include/gromacs/swap/swapcoords.h b/src/include/gromacs/swap/swapcoords.h
new file mode 100644 (file)
index 0000000..ec8fdd5
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal
+ * \defgroup module_swap "Computational Electrophysiology" position swapping (swap)
+ * \ingroup group_mdrun
+ * \brief
+ * Implements the "Computational Electrophysiology" protocol.
+ *
+ * \author Carsten Kutzner <ckutzne@gwdg.de>
+ */
+/*! \libinternal \file
+ * \brief
+ * The "Computational Electrophysiology" protocol for ion/water position swapping.
+ *
+ * \author Carsten Kutzner <ckutzne@gwdg.de>
+ * \inlibraryapi
+ * \ingroup module_swap
+ */
+#ifndef GMX_SWAP_SWAPCOORDS_H
+#define GMX_SWAP_SWAPCOORDS_H
+
+#include <cstdio>
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_domdec_t;
+struct gmx_mtop_t;
+struct gmx_output_env_t;
+struct gmx_wallcycle;
+struct swaphistory_t;
+struct t_commrec;
+struct t_inputrec;
+class t_state;
+struct t_swap;
+struct t_swapcoords;
+struct ObservablesHistory;
+
+namespace gmx
+{
+enum class StartingBehavior;
+class IMDModule;
+class LocalAtomSetManager;
+struct MdrunOptions;
+
+/*! \brief
+ * Creates a module for Computational Electrophysiology swapping.
+ */
+std::unique_ptr<IMDModule> createSwapCoordinatesModule();
+
+} // namespace gmx
+
+/*! \brief Initialize ion / water position swapping ("Computational Electrophysiology").
+ *
+ * This routine does the memory allocation for various helper arrays, opens
+ * the output file, sets up swap data checkpoint writing, etc. and returns it.
+ *
+ * \param[in] fplog         General output file, normally md.log.
+ * \param[in] ir            Structure containing MD input parameters, among those
+ *                          also the structure needed for position swapping.
+ * \param[in] fn            Output file name for swap data.
+ * \param[in] mtop          Molecular topology.
+ * \param[in] globalState   The global state, only used on the master rank.
+ * \param[in] oh            Contains struct with swap data that is read from or written to checkpoint.
+ * \param[in] cr            Pointer to MPI communication data.
+ * \param[in] atomSets      Manager tending to swap atom indices.
+ * \param[in] oenv          Needed to open the swap output XVGR file.
+ * \param[in] mdrunOptions  Options for mdrun.
+ * \param[in] startingBehavior  Describes whether this is a restart appending to output files
+ */
+t_swap* init_swapcoords(FILE*                     fplog,
+                        const t_inputrec*         ir,
+                        const char*               fn,
+                        const gmx_mtop_t&         mtop,
+                        const t_state*            globalState,
+                        ObservablesHistory*       oh,
+                        t_commrec*                cr,
+                        gmx::LocalAtomSetManager* atomSets,
+                        const gmx_output_env_t*   oenv,
+                        const gmx::MdrunOptions&  mdrunOptions,
+                        gmx::StartingBehavior     startingBehavior);
+
+
+/*! \brief Finalizes ion / water position swapping, if it was active.
+ *
+ * \param[in] s             Pointer to swap data.
+ */
+void finish_swapcoords(t_swap* s);
+
+
+/*! \brief "Computational Electrophysiology" main routine within MD loop.
+ *
+ * \param[in] cr       Pointer to MPI communication data.
+ * \param[in] step     The number of the MD time step.
+ * \param[in] t        The time.
+ * \param[in] ir       Structure containing MD input parameters
+ * \param[in,out] s    The structure needed for position swapping.
+ * \param[in] wcycle   Count wallcycles of swap routines for diagnostic output.
+ * \param[in] x        Positions of home particles this node owns.
+ * \param[in] box      The simulation box.
+ * \param[in] bVerbose Should we be quiet or verbose?
+ * \param[in] bRerun   Are we doing a rerun?
+ *
+ * \returns Whether at least one pair of molecules was swapped.
+ */
+gmx_bool do_swapcoords(t_commrec*        cr,
+                       int64_t           step,
+                       double            t,
+                       const t_inputrec* ir,
+                       t_swap*           s,
+                       gmx_wallcycle*    wcycle,
+                       rvec              x[],
+                       matrix            box,
+                       gmx_bool          bVerbose,
+                       gmx_bool          bRerun);
+
+#endif
diff --git a/src/include/gromacs/tables/cubicsplinetable.h b/src/include/gromacs/tables/cubicsplinetable.h
new file mode 100644 (file)
index 0000000..01bca6c
--- /dev/null
@@ -0,0 +1,640 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief
+ * Declares classes for cubic spline table
+ *
+ * \inlibraryapi
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \ingroup module_tables
+ */
+#ifndef GMX_TABLES_CUBICSPLINETABLE_H
+#define GMX_TABLES_CUBICSPLINETABLE_H
+
+#include <initializer_list>
+#include <memory>
+#include <vector>
+
+#include "gromacs/simd/simd.h"
+#include "gromacs/tables/tableinput.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief Cubic spline interpolation table.
+ *
+ * This class interpolates a function specified either as an analytical
+ * expression or from user-provided table data.
+ *
+ * At initialization, you provide the reference function of vectors
+ * as a list of tuples that contain a brief name, the function, and
+ * derivative for each function to tabulate. To create a table with
+ * two functions this initializer list can for instance look like
+ *
+ *     { {"LJ6", lj6Func, lj6Der}, {"LJ12", lj12Func, lj12Der} }
+ *
+ * The names are only used so exceptions during initialization can
+ * be traced to a specific table.
+ *
+ * When interpolating, there are methods to interpolate either 1, 2, or 3
+ * functions in one go. By default these interpolation routines will
+ * operate on tables with the same number of functions as specified in
+ * the interpolation method (debug builds check that this is consistent with
+ * the table). However, it is also possible to use optional template
+ * parameters that specify the total number of functions in a table, and
+ * what function index to interpolate. For instance, to interpolate the
+ * derivative of the second function (i.e., index 1) in a
+ * multi-function-table with three functions in total, you can write
+ *
+ *     table.evaluateDerivative<3,1>(x,&der);
+ *
+ * Here too, debug builds will check that the template parameters are
+ * consistent with the table.
+ *
+ * This class interpolates a function specified either as an analytical
+ * expression or from user-provided table data. The coefficients for each
+ * table point are precalculated such that we simply evaluate
+ *
+ * \f{eqnarray*}{
+ * V(x)  = Y + F \epsilon + G \epsilon^2 + H \epsilon^3
+ * V'(x) = (F + 2 G \epsilon + 3 H \epsilon^2)/h
+ * \f}
+ *
+ * Where h is the spacing and epsilon the fractional offset from table point.
+ *
+ * While it is possible to create tables only from function values
+ * (i.e., no derivatives), it is recommended to provide derivatives for higher
+ * accuracy and to avoid issues with numerical differentiation. Note that the
+ * table input should be smooth, i.e. it should not contain noise e.g. from an
+ * (iterative) Boltzmann inversion procedure - you have been warned.
+ *
+ * \note This class is responsible for fundamental interpolation of any function,
+ *       which might or might not correspond to a potential. For this reason
+ *       both input and output derivatives are proper function derivatives, and
+ *       we do not swap any signs to get forces directly from the table.
+ *
+ * \note There will be a small additional accuracy loss from the internal
+ *       operation where we calculate the epsilon offset from the nearest table
+ *       point, since the integer part we subtract can get large in those cases.
+ *       While this is technically possible to solve with extended precision
+ *       arithmetics, that would introduce extra instructions in some highly
+ *       performance-sensitive code parts. For typical GROMACS interaction
+ *       functions the derivatives will decay faster than the potential, which
+ *       means it will never play any role. For other functions it will only
+ *       cause a small increase in the relative error for arguments where the
+ *       magnitude of the function or derivative is very small.
+ *       Since we typically sum several results in GROMACS, this should never
+ *       show up in any real cases, and for this reason we choose not to do
+ *       the extended precision arithmetics.
+ *
+ * \note These routines are not suitable for table ranges starting far away
+ *       from zero, since we allocate memory and calculate indices starting from
+ *       range zero for efficiency reasons.
+ */
+class CubicSplineTable
+{
+private:
+    /*! \brief Change that function value falls inside range when debugging
+     *
+     *  \tparam T   Lookup argument floating-point type, typically SimdReal or real.
+     *  \param  r   Lookup argument to test
+     *
+     *  \throws Debug builds will throw gmx::RangeError for values that are
+     *          larger than the upper limit of the range, or smaller than 0.
+     *          We allow the table to be called with arguments between 0 and
+     *          the lower limit of the range, since this might in theory occur
+     *          once-in-a-blue-moon with some algorithms.
+     */
+    template<typename T>
+    void rangeCheck(T gmx_unused r) const
+    {
+#ifndef NDEBUG
+        // Check that all values fall in range when debugging
+        if (anyTrue(r < T(0.0) || T(range_.second) <= r))
+        {
+            GMX_THROW(RangeError("Interpolation input value falls outside table definition range"));
+        }
+#endif
+    }
+
+public:
+    /*! \brief Default tolerance for cubic spline tables
+     *
+     * This is 10*GMX_FLOAT_EPS in single precision, and
+     * 1e-10 for double precision. It might not be worth setting
+     * this tolerance lower than 1e-10 in double precision, both because
+     * you will end up with very large tables, and because
+     * functions like r^-12 become so large for small values of r the
+     * table generation code will lead to some precision loss even
+     * in double precision.
+     */
+    static const real defaultTolerance;
+
+    /*! \brief Initialize table data from function
+     *
+     * \param analyticalInputList Initializer list with one or more functions to tabulate,
+     *                            specified as elements with a string description and
+     *                            the function as well as derivative. The function will also
+     *                            be called for values smaller than the lower limit of the
+     *                            range, but we avoid calling it for 0.0 if that value
+     *                            is not included in the range.
+     *                            Constructor will throw gmx::APIError for negative values.
+     *                            Due to the way the numerical derivative evaluation depends
+     *                            on machine precision internally, this range must be
+     *                            at least 0.001, or the constructor throws gmx::APIError.
+     * \param range                Range over which the function will be tabulated.
+     *                             Constructor will throw gmx::APIError for negative values,
+     *                             or if the value/derivative vector does not cover the
+     *                             range.
+     * \param tolerance           Requested accuracy of the table. This will be used to
+     *                            calculate the required internal spacing. If this cannot
+     *                            be achieved (for instance because the table would require
+     *                            too much memory) the constructor will throw gmx::ToleranceError.
+     *
+     * \note The functions are always defined in double precision to avoid
+     *       losing accuracy when constructing tables.
+     *
+     * \note Since we fill the table for values below range.first, you can achieve
+     *       a smaller table by using a smaller range where the tolerance has to be
+     *       met, and accept that a few function calls below range.first do not
+     *       quite reach the tolerance.
+     *
+     * \warning For efficiency reasons (since this code is used in some inner
+     *       (kernels), we always allocate memory and calculate table indices
+     *       for the complete interval [0,range.second], although the data will
+     *       not be valid outside the definition range to avoid calling the
+     *       function there. This means you should \a not use this class
+     *       to tabulate functions for small ranges very far away from zero,
+     *       since you would both waste a huge amount of memory and incur
+     *       truncation errors when calculating the index.
+     *
+     * \throws gmx::ToleranceError if the requested tolerance cannot be achieved,
+     *         and gmx::APIError for other incorrect input.
+     */
+    CubicSplineTable(std::initializer_list<AnalyticalSplineTableInput> analyticalInputList,
+                     const std::pair<real, real>&                      range,
+                     real tolerance = defaultTolerance);
+
+    /*! \brief Initialize table data from tabulated values and derivatives
+     *
+     * \param numericalInputList  Initializer list with one or more functions to tabulate,
+     *                            specified as a string description, vectors with function and
+     *                            derivative values, and the input spacing. Data points are
+     *                            separated by the spacing parameter, starting from 0.
+     *                            Values below the lower limit of the range will be used to
+     *                            attempt defining the table, but we avoid using index 0
+     *                            unless 0.0 is included in the range. Some extra points beyond
+     *                            range.second are required to re-interpolate values, so add
+     *                            some margin. The constructor will throw gmx::APIError if the
+     *                            input vectors are too short to cover the requested range
+     *                            (and they must always be at least five points).
+     * \param range               Range over which the function will be tabulated.
+     *                            Constructor will throw gmx::APIError for negative values,
+     *                            or if the value/derivative vector does not cover the
+     *                            range.
+     * \param tolerance           Requested accuracy of the table. This will be used to
+     *                            calculate the required internal spacing and possibly
+     *                            re-interpolate. The constructor will throw
+     *                            gmx::ToleranceError if the input spacing is too coarse
+     *                            to achieve this accuracy.
+     *
+     * \note The input data vectors are always double precision to avoid
+     *       losing accuracy when constructing tables.
+     *
+     * \note Since we fill the table for values below range.first, you can achieve
+     *       a smaller table by using a smaller range where the tolerance has to be
+     *       met, and accept that a few function calls below range.first do not
+     *       quite reach the tolerance.
+     *
+     * \warning For efficiency reasons (since this code is used in some inner
+     *       (kernels), we always allocate memory and calculate table indices
+     *       for the complete interval [0,range.second], although the data will
+     *       not be valid outside the definition range to avoid calling the
+     *       function there. This means you should \a not use this class
+     *       to tabulate functions for small ranges very far away from zero,
+     *       since you would both waste a huge amount of memory and incur
+     *       truncation errors when calculating the index.
+     */
+    CubicSplineTable(std::initializer_list<NumericalSplineTableInput> numericalInputList,
+                     const std::pair<real, real>&                     range,
+                     real                                             tolerance = defaultTolerance);
+
+
+    /************************************************************
+     *           Evaluation methods for single functions        *
+     ************************************************************/
+
+    /*! \brief Evaluate both function and derivative, single table function
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable  Number of separate functions in table, default is 1
+     *  \tparam     funcIndex       Index of function to evaluate in table, default is 0
+     *  \tparam     T               Type (SimdReal or real) of lookup and result
+     *  \param      r               Points for which to evaluate function and derivative
+     *  \param[out] functionValue   Function value
+     *  \param[out] derivativeValue Function derivative
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 1, int funcIndex = 0, typename T>
+    void evaluateFunctionAndDerivative(T r, T* functionValue, T* derivativeValue) const
+    {
+        rangeCheck(r);
+        GMX_ASSERT(numFuncInTable == numFuncInTable_,
+                   "Evaluation method not matching number of functions in table");
+        GMX_ASSERT(funcIndex < numFuncInTable,
+                   "Function index not in range of the number of tables");
+
+        T    rTable   = r * T(tableScale_);
+        auto tabIndex = cvttR2I(rTable); // type is either std::int32_t or SimdInt32
+        T    eps      = rTable - trunc(rTable);
+        T    Y, F, G, H;
+
+        // Load Derivative, Delta, Function, and Zero values for each table point.
+        // The 4 refers to these four values - not any SIMD width.
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                yfghMultiTableData_.data() + 4 * funcIndex, tabIndex, &Y, &F, &G, &H);
+        *functionValue   = fma(fma(fma(H, eps, G), eps, F), eps, Y);
+        *derivativeValue = tableScale_ * fma(fma(T(3.0) * H, eps, T(2.0) * G), eps, F);
+    }
+
+    /*! \brief Evaluate function value only, single table function
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable  Number of separate functions in table, default is 1
+     *  \tparam     funcIndex       Index of function to evaluate in table, default is 0
+     *  \tparam     T               Type (SimdReal or real) of lookup and result
+     *  \param      r               Points for which to evaluate function value
+     *  \param[out] functionValue   Function value
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 1, int funcIndex = 0, typename T>
+    void evaluateFunction(T r, T* functionValue) const
+    {
+        T der gmx_unused;
+
+        evaluateFunctionAndDerivative<numFuncInTable, funcIndex>(r, functionValue, &der);
+    }
+
+    /*! \brief Evaluate function derivative only, single table function
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable  Number of separate functions in table, default is 1
+     *  \tparam     funcIndex       Index of function to evaluate in table, default is 0
+     *  \tparam     T               Type (SimdReal or real) of lookup and result
+     *  \param      r               Points for which to evaluate function derivative
+     *  \param[out] derivativeValue Function derivative
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 1, int funcIndex = 0, typename T>
+    void evaluateDerivative(T r, T* derivativeValue) const
+    {
+        rangeCheck(r);
+        GMX_ASSERT(numFuncInTable == numFuncInTable_,
+                   "Evaluation method not matching number of functions in table");
+        GMX_ASSERT(funcIndex < numFuncInTable,
+                   "Function index not in range of the number of tables");
+
+        T    rTable   = r * T(tableScale_);
+        auto tabIndex = cvttR2I(rTable); // type is either std::int32_t or SimdInt32
+        T    eps      = rTable - trunc(rTable);
+        T    Y, F, G, H;
+
+        // Load Derivative, Delta, Function, and Zero values for each table point.
+        // The 4 refers to these four values - not any SIMD width.
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                yfghMultiTableData_.data() + 4 * funcIndex, tabIndex, &Y, &F, &G, &H);
+        *derivativeValue = tableScale_ * fma(fma(T(3.0) * H, eps, T(2.0) * G), eps, F);
+    }
+
+    /************************************************************
+     *             Evaluation methods for two functions         *
+     ************************************************************/
+
+    /*! \brief Evaluate both function and derivative, two table functions
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable  Number of separate functions in table, default is 2
+     *  \tparam     funcIndex0       Index of 1st function to evaluate in table, default is 0
+     *  \tparam     funcIndex1       Index of 2nd function to evaluate in table, default is 1
+     *  \tparam     T                Type (SimdReal or real) of lookup and result
+     *  \param      r                Points for which to evaluate function and derivative
+     *  \param[out] functionValue0   Interpolated value for first function
+     *  \param[out] derivativeValue0 Interpolated derivative for first function
+     *  \param[out] functionValue1   Interpolated value for second function
+     *  \param[out] derivativeValue1 Interpolated derivative for second function
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 2, int funcIndex0 = 0, int funcIndex1 = 1, typename T>
+    void evaluateFunctionAndDerivative(T r, T* functionValue0, T* derivativeValue0, T* functionValue1, T* derivativeValue1) const
+    {
+        rangeCheck(r);
+        GMX_ASSERT(numFuncInTable == numFuncInTable_,
+                   "Evaluation method not matching number of functions in table");
+        GMX_ASSERT(funcIndex0 < numFuncInTable && funcIndex1 < numFuncInTable,
+                   "Function index not in range of the number of tables");
+
+        T    rTable   = r * T(tableScale_);
+        auto tabIndex = cvttR2I(rTable); // type is either std::int32_t or SimdInt32
+        T    eps      = rTable - trunc(rTable);
+        T    Y, F, G, H;
+
+        // Load Derivative, Delta, Function, and Zero values for each table point.
+        // The 4 refers to these four values - not any SIMD width.
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                yfghMultiTableData_.data() + 4 * funcIndex0, tabIndex, &Y, &F, &G, &H);
+        *functionValue0   = fma(fma(fma(H, eps, G), eps, F), eps, Y);
+        *derivativeValue0 = tableScale_ * fma(fma(T(3.0) * H, eps, T(2.0) * G), eps, F);
+
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                yfghMultiTableData_.data() + 4 * funcIndex1, tabIndex, &Y, &F, &G, &H);
+        *functionValue1   = fma(fma(fma(H, eps, G), eps, F), eps, Y);
+        *derivativeValue1 = tableScale_ * fma(fma(T(3.0) * H, eps, T(2.0) * G), eps, F);
+    }
+
+    /*! \brief Evaluate function value only, two table functions
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable   Number of separate functions in table, default is 2
+     *  \tparam     funcIndex0       Index of 1st function to evaluate in table, default is 0
+     *  \tparam     funcIndex1       Index of 2nd function to evaluate in table, default is 1
+     *  \tparam     T                Type (SimdReal or real) of lookup and result
+     *  \param      r                Points for which to evaluate function value
+     *  \param[out] functionValue0   Interpolated value for first function
+     *  \param[out] functionValue1   Interpolated value for second function
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 2, int funcIndex0 = 0, int funcIndex1 = 1, typename T>
+    void evaluateFunction(T r, T* functionValue0, T* functionValue1) const
+    {
+        T der0 gmx_unused;
+        T der1 gmx_unused;
+
+        evaluateFunctionAndDerivative<numFuncInTable, funcIndex0, funcIndex1>(
+                r, functionValue0, &der0, functionValue1, &der1);
+    }
+
+    /*! \brief Evaluate function derivative only, two table functions
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable   Number of separate functions in table, default is 2
+     *  \tparam     funcIndex0       Index of 1st function to evaluate in table, default is 0
+     *  \tparam     funcIndex1       Index of 2nd function to evaluate in table, default is 1
+     *  \tparam     T                Type (SimdReal or real) of lookup and result
+     *  \param      r                Points for which to evaluate function derivative
+     *  \param[out] derivativeValue0 Interpolated derivative for first function
+     *  \param[out] derivativeValue1 Interpolated derivative for second function
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 2, int funcIndex0 = 0, int funcIndex1 = 1, typename T>
+    void evaluateDerivative(T r, T* derivativeValue0, T* derivativeValue1) const
+    {
+        rangeCheck(r);
+        GMX_ASSERT(numFuncInTable == numFuncInTable_,
+                   "Evaluation method not matching number of functions in table");
+        GMX_ASSERT(funcIndex0 < numFuncInTable && funcIndex1 < numFuncInTable,
+                   "Function index not in range of the number of tables");
+
+        T    rTable   = r * T(tableScale_);
+        auto tabIndex = cvttR2I(rTable); // type is either std::int32_t or SimdInt32
+        T    eps      = rTable - trunc(rTable);
+        T    Y, F, G, H;
+
+        // Load Derivative, Delta, Function, and Zero values for each table point.
+        // The 4 refers to these four values - not any SIMD width.
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                yfghMultiTableData_.data() + 4 * funcIndex0, tabIndex, &Y, &F, &G, &H);
+        *derivativeValue0 = tableScale_ * fma(fma(T(3.0) * H, eps, T(2.0) * G), eps, F);
+
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                yfghMultiTableData_.data() + 4 * funcIndex1, tabIndex, &Y, &F, &G, &H);
+        *derivativeValue1 = tableScale_ * fma(fma(T(3.0) * H, eps, T(2.0) * G), eps, F);
+    }
+
+    /************************************************************
+     *            Evaluation methods for three functions        *
+     ************************************************************/
+
+
+    /*! \brief Evaluate both function and derivative, three table functions
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable   Number of separate functions in table, default is 3
+     *  \tparam     funcIndex0       Index of 1st function to evaluate in table, default is 0
+     *  \tparam     funcIndex1       Index of 2nd function to evaluate in table, default is 1
+     *  \tparam     funcIndex2       Index of 3rd function to evaluate in table, default is 2
+     *  \tparam     T                Type (SimdReal or real) of lookup and result
+     *  \param      r                Points for which to evaluate function and derivative
+     *  \param[out] functionValue0   Interpolated value for first function
+     *  \param[out] derivativeValue0 Interpolated derivative for first function
+     *  \param[out] functionValue1   Interpolated value for second function
+     *  \param[out] derivativeValue1 Interpolated derivative for second function
+     *  \param[out] functionValue2   Interpolated value for third function
+     *  \param[out] derivativeValue2 Interpolated derivative for third function
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 3, int funcIndex0 = 0, int funcIndex1 = 1, int funcIndex2 = 2, typename T>
+    void evaluateFunctionAndDerivative(T  r,
+                                       T* functionValue0,
+                                       T* derivativeValue0,
+                                       T* functionValue1,
+                                       T* derivativeValue1,
+                                       T* functionValue2,
+                                       T* derivativeValue2) const
+    {
+        rangeCheck(r);
+        GMX_ASSERT(numFuncInTable == numFuncInTable_,
+                   "Evaluation method not matching number of functions in table");
+        GMX_ASSERT(funcIndex0 < numFuncInTable && funcIndex1 < numFuncInTable && funcIndex2 < numFuncInTable,
+                   "Function index not in range of the number of tables");
+
+        T    rTable   = r * T(tableScale_);
+        auto tabIndex = cvttR2I(rTable); // type is either std::int32_t or SimdInt32
+        T    eps      = rTable - trunc(rTable);
+        T    Y, F, G, H;
+
+        // Load Derivative, Delta, Function, and Zero values for each table point.
+        // The 4 refers to these four values - not any SIMD width.
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                yfghMultiTableData_.data() + 4 * funcIndex0, tabIndex, &Y, &F, &G, &H);
+        *functionValue0   = fma(fma(fma(H, eps, G), eps, F), eps, Y);
+        *derivativeValue0 = tableScale_ * fma(fma(T(3.0) * H, eps, T(2.0) * G), eps, F);
+
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                yfghMultiTableData_.data() + 4 * funcIndex1, tabIndex, &Y, &F, &G, &H);
+        *functionValue1   = fma(fma(fma(H, eps, G), eps, F), eps, Y);
+        *derivativeValue1 = tableScale_ * fma(fma(T(3.0) * H, eps, T(2.0) * G), eps, F);
+
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                yfghMultiTableData_.data() + 4 * funcIndex2, tabIndex, &Y, &F, &G, &H);
+        *functionValue2   = fma(fma(fma(H, eps, G), eps, F), eps, Y);
+        *derivativeValue2 = tableScale_ * fma(fma(T(3.0) * H, eps, T(2.0) * G), eps, F);
+    }
+
+    /*! \brief Evaluate function value only, three table functions
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable   Number of separate functions in table, default is 3
+     *  \tparam     funcIndex0       Index of 1st function to evaluate in table, default is 0
+     *  \tparam     funcIndex1       Index of 2nd function to evaluate in table, default is 1
+     *  \tparam     funcIndex2       Index of 3rd function to evaluate in table, default is 2
+     *  \tparam     T                Type (SimdReal or real) of lookup and result
+     *  \param      r                Points for which to evaluate function value
+     *  \param[out] functionValue0   Interpolated value for first function
+     *  \param[out] functionValue1   Interpolated value for second function
+     *  \param[out] functionValue2   Interpolated value for third function
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 3, int funcIndex0 = 0, int funcIndex1 = 1, int funcIndex2 = 2, typename T>
+    void evaluateFunction(T r, T* functionValue0, T* functionValue1, T* functionValue2) const
+    {
+        T der0 gmx_unused;
+        T der1 gmx_unused;
+        T der2 gmx_unused;
+
+        evaluateFunctionAndDerivative<numFuncInTable, funcIndex0, funcIndex1, funcIndex2>(
+                r, functionValue0, &der0, functionValue1, &der1, functionValue2, &der2);
+    }
+
+    /*! \brief Evaluate function derivative only, three table functions
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable   Number of separate functions in table, default is 3
+     *  \tparam     funcIndex0       Index of 1st function to evaluate in table, default is 0
+     *  \tparam     funcIndex1       Index of 2nd function to evaluate in table, default is 1
+     *  \tparam     funcIndex2       Index of 3rd function to evaluate in table, default is 2
+     *  \tparam     T                Type (SimdReal or real) of lookup and result
+     *  \param      r                Points for which to evaluate function derivative
+     *  \param[out] derivativeValue0 Interpolated derivative for first function
+     *  \param[out] derivativeValue1 Interpolated derivative for second function
+     *  \param[out] derivativeValue2 Interpolated derivative for third function
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 3, int funcIndex0 = 0, int funcIndex1 = 1, int funcIndex2 = 2, typename T>
+    void evaluateDerivative(T r, T* derivativeValue0, T* derivativeValue1, T* derivativeValue2) const
+    {
+        rangeCheck(r);
+        GMX_ASSERT(numFuncInTable == numFuncInTable_,
+                   "Evaluation method not matching number of functions in table");
+        GMX_ASSERT(funcIndex0 < numFuncInTable && funcIndex1 < numFuncInTable && funcIndex2 < numFuncInTable,
+                   "Function index not in range of the number of tables");
+
+        T    rTable   = r * T(tableScale_);
+        auto tabIndex = cvttR2I(rTable); // type is either std::int32_t or SimdInt32
+        T    eps      = rTable - trunc(rTable);
+        T    Y, F, G, H;
+
+        // Load Derivative, Delta, Function, and Zero values for each table point.
+        // The 4 refers to these four values - not any SIMD width.
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                yfghMultiTableData_.data() + 4 * funcIndex0, tabIndex, &Y, &F, &G, &H);
+        *derivativeValue0 = tableScale_ * fma(fma(T(3.0) * H, eps, T(2.0) * G), eps, F);
+
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                yfghMultiTableData_.data() + 4 * funcIndex1, tabIndex, &Y, &F, &G, &H);
+        *derivativeValue1 = tableScale_ * fma(fma(T(3.0) * H, eps, T(2.0) * G), eps, F);
+
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                yfghMultiTableData_.data() + 4 * funcIndex2, tabIndex, &Y, &F, &G, &H);
+        *derivativeValue2 = tableScale_ * fma(fma(T(3.0) * H, eps, T(2.0) * G), eps, F);
+    }
+
+    /*! \brief Return the table spacing (distance between points)
+     *
+     *  You should never have to use this for normal code, but due to the
+     *  way tables are constructed internally we need this in the unit tests
+     *  to check relative tolerances over each interval.
+     *
+     *  \return table spacing.
+     */
+    real tableSpacing() const { return 1.0 / tableScale_; }
+
+private:
+    std::size_t           numFuncInTable_; //!< Number of separate tabluated functions
+    std::pair<real, real> range_;          //!< Range for which table evaluation is allowed
+    real                  tableScale_;     //!< Table scale (inverse of spacing between points)
+
+    /*! \brief Vector with combined table data to save calculations after lookup.
+     *
+     *  For table point i, this vector contains the four coefficients
+     *  Y,F,G,H that we use to express the function value as
+     *  V(x)  = Y + F e + G e^2 + H e^3, where e is the epsilon offset from
+     *  the nearest table point.
+     *
+     *  To allow aligned SIMD loads we need to use an aligned allocator for
+     *  this container.
+     */
+    std::vector<real, AlignedAllocator<real>> yfghMultiTableData_;
+
+    // There should never be any reason to copy the table since it is read-only
+    GMX_DISALLOW_COPY_AND_ASSIGN(CubicSplineTable);
+};
+
+
+} // namespace gmx
+
+#endif // GMX_TABLES_CUBICSPLINETABLE_H
diff --git a/src/include/gromacs/tables/forcetable.h b/src/include/gromacs/tables/forcetable.h
new file mode 100644 (file)
index 0000000..d2cde89
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TABLES_FORCETABLE_H
+#define GMX_TABLES_FORCETABLE_H
+
+/*! \libinternal \file
+ * \brief
+ * Old routines for table generation (will eventually be replaced)
+ *
+ * \inlibraryapi
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \ingroup module_tables
+ */
+
+#include <cstdio>
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/real.h"
+
+struct EwaldCorrectionTables;
+struct bondedtable_t;
+struct interaction_const_t;
+
+/*! \brief Flag to select user tables for make_tables */
+#define GMX_MAKETABLES_FORCEUSER (1 << 0)
+/*! \brief Flag to only make 1,4 pair tables for make_tables */
+#define GMX_MAKETABLES_14ONLY (1 << 1)
+
+//! \brief The types of interactions contained in the table
+enum class TableInteraction : int
+{
+    VdwRepulsionVdwDispersion,
+    ElectrostaticVdwRepulsionVdwDispersion,
+    Count
+};
+
+/* Different formats for table data. Cubic spline tables are typically stored
+ * with the four Y,F,G,H intermediate values (check tables.c for format), which
+ * makes it easy to load with a single 4-way SIMD instruction too.
+ * Linear tables only need one value per table point, or two if both V and F
+ * are calculated. However, with SIMD instructions this makes the loads unaligned,
+ * and in that case we store the data as F, D=F(i+1)-F(i), V, and then a blank value,
+ * which again makes it possible to load as a single instruction.
+ */
+enum class TableFormat : int
+{
+    CubicsplineYfgh,
+    Count
+};
+
+//! \internal \brief Structure describing the data in a single table
+struct t_forcetable
+{
+    t_forcetable(TableInteraction interaction, TableFormat format);
+
+    ~t_forcetable();
+
+    //! Types of interactions stored in this table
+    TableInteraction interaction;
+    //! Interpolation type and data format
+    TableFormat format;
+    //! range of the table
+    real interactionRange;
+    //! n+1 is the number of table points
+    int numTablePoints;
+    //! distance (nm) between two table points
+    real scale;
+    //! The actual table data
+    std::vector<real, gmx::AlignedAllocator<real>> data;
+
+    /* Some information about the table layout. This can also be derived from the interpolation
+     * type and the table interactions, but it is convenient to have here for sanity checks, and it
+     * makes it much easier to access the tables in the nonbonded kernels when we can set the data
+     * from variables. It is always true that stride = formatsize*ninteractions
+     */
+
+    //! Number of fp variables for each table point (1 for F, 2 for VF, 4 for YFGH, etc.), only YFGH is implemented
+    static constexpr int formatsize = 4;
+    //! Number of interactions in table, 1 for coul-only, 3 for coul+rep+disp.
+    int numInteractions;
+    //! Distance to next table point (number of fp variables per table point in total)
+    int stride;
+};
+
+/*! \brief Enumerated type to describe the interaction types in a table */
+enum
+{
+    etiCOUL, //!< Coulomb
+    etiLJ6,  //!< Dispersion
+    etiLJ12, //!< Repulsion
+    etiNR    //!< Total number of interaction types
+};
+
+/*! \brief Function pointer to calculate the grid contribution for coulomb/LJ
+ *
+ * Used to tell table_spline3_fill_ewald_lr whether it
+ * should calculate the grid contribution for electrostatics or LJ.
+ */
+typedef double (*real_space_grid_contribution_computer)(double, double);
+
+
+/*! \brief Construct tables with the Ewald long-range force interaction
+ *
+ * Creates and fills tables of numPoints points with the spacing
+ * set to 1/tableScaling with the Ewald long-range (mesh) force.
+ * There are three separate tables with format F, V, FDV0.
+ * This function interpolates the Ewald mesh potential contribution
+ * with coefficient beta using a quadratic spline.
+ * The force is then be interpolated linearly.
+ *
+ * \param numPoints     Number of points in the tables
+ * \param tableScaling  1/spacing, units 1/nm
+ * \param beta          Ewald splitting parameter, units 1/nm
+ * \param v_lr          Pointer to function calculating real-space grid contribution
+ * \returns a set of Ewald correction tables
+ */
+EwaldCorrectionTables generateEwaldCorrectionTables(int    numPoints,
+                                                    double tableScaling,
+                                                    real   beta,
+                                                    real_space_grid_contribution_computer v_lr);
+
+/*! \brief Compute scaling for the Ewald quadratic spline tables.
+ *
+ * \param ic                     Pointer to interaction constant structure
+ * \param generateCoulombTables  Take the spacing for Coulomb Ewald corrections into account
+ * \param generateVdwTables      Take the spacing for Van der Waals Ewald corrections into account
+ * \return The scaling factor in units 1/nm
+ */
+real ewald_spline3_table_scale(const interaction_const_t& ic, bool generateCoulombTables, bool generateVdwTables);
+
+/*! \brief Return the real space grid contribution for Ewald
+ *
+ *  \param beta  Ewald splitting parameter
+ *  \param r     Distance for which to calculate the real-space contrib
+ *  \return      Real space grid contribution for Ewald electrostatics
+ */
+double v_q_ewald_lr(double beta, double r);
+
+/*! \brief Return the real space grid contribution for LJ-Ewald
+ *
+ *  \param beta  Ewald splitting parameter
+ *  \param r     Distance for which to calculate the real-space contrib
+ *  \return      Real space grid contribution for Ewald Lennard-Jones interaction
+ */
+double v_lj_ewald_lr(double beta, double r);
+
+/*! \brief Return tables for inner loops.
+ *
+ * \param fp     Log file pointer
+ * \param ic     Non-bonded interaction constants
+ * \param fn     File name from which to read user tables
+ * \param rtab   Largest interaction distance to tabulate
+ * \param flags  Flags to select table settings
+ *
+ * \return Pointer to inner loop table structure
+ */
+std::unique_ptr<t_forcetable>
+make_tables(FILE* fp, const interaction_const_t* ic, const char* fn, real rtab, int flags);
+
+/*! \brief Return a table for bonded interactions,
+ *
+ * \param  fplog   Pointer to log file
+ * \param  fn      File name
+ * \param  angle   Type of angle: bonds 0, angles 1, dihedrals 2
+ * \return New bonded table datatype
+ */
+bondedtable_t make_bonded_table(FILE* fplog, const char* fn, int angle);
+
+/*! \brief Construct and return tabulated dispersion and repulsion interactions
+ *
+ * This table can be used to compute long-range dispersion corrections.
+ * Returns pointer owning nothing when tabfn=nullptr.
+ */
+std::unique_ptr<t_forcetable>
+makeDispersionCorrectionTable(FILE* fp, const interaction_const_t* ic, real rtab, const char* tabfn);
+
+#endif /* GMX_TABLES_FORCETABLE_H */
diff --git a/src/include/gromacs/tables/quadraticsplinetable.h b/src/include/gromacs/tables/quadraticsplinetable.h
new file mode 100644 (file)
index 0000000..776ee42
--- /dev/null
@@ -0,0 +1,766 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal
+ * \defgroup module_tables  Classes for table interpolation
+ * \ingroup group_utilitymodules
+ *
+ * \brief Table interpolation from analytical or numerical input
+ *
+ * This module provides quadratic spline interpolation tables used
+ * both for the nonbonded kernels and listed interactions.
+ *
+ * \author Erik Lindahl <erik.lindahl@scilifelab.se>
+ */
+
+
+/*! \libinternal \file
+ * \brief
+ * Declares classes for quadratic spline table
+ *
+ * \inlibraryapi
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \ingroup module_tables
+ */
+#ifndef GMX_TABLES_QUADRATICSPLINETABLE_H
+#define GMX_TABLES_QUADRATICSPLINETABLE_H
+
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <vector>
+
+#include "gromacs/simd/simd.h"
+#include "gromacs/tables/tableinput.h"
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief Quadratic spline interpolation table.
+ *
+ * This class interpolates a function specified either as an analytical
+ * expression or from user-provided table data.
+ *
+ * At initialization, you provide the reference function of vectors
+ * as a list of tuples that contain a brief name, the function, and
+ * derivative for each function to tabulate. To create a table with
+ * two functions this initializer list can for instance look like
+ *
+ *     { {"LJ6", lj6Func, lj6Der}, {"LJ12", lj12Func, lj12Der} }
+ *
+ * The names are only used so exceptions during initialization can
+ * be traced to a specific table.
+ *
+ * When interpolating, there are methods to interpolate either 1, 2, or 3
+ * functions in one go. By default these interpolation routines will
+ * operate on tables with the same number of functions as specified in
+ * the interpolation method (debug builds check that this is consistent with
+ * the table). However, it is also possible to use optional template
+ * parameters that specify the total number of functions in a table, and
+ * what function index to interpolate. For instance, to interpolate the
+ * derivative of the second function (i.e., index 1) in a
+ * multi-function-table with three functions in total, you can write
+ *
+ *     table.evaluateDerivative<3,1>(x,&der);
+ *
+ * Here too, debug builds will check that the template parameters are
+ * consistent with the table.
+ *
+ * The table data is internally adjusted to guarantee that the interpolated
+ * derivative is the true derivative of the interpolated potential, which is
+ * important to avoid systematic errors for the common case when the derivative
+ * is concave/convex in the entire interval.
+ * We do this by expressing the difference in the function value
+ * at a small offset h relative to a reference value in position 0 with a forward
+ * Taylor series expanded around 0, and then doing the opposite of expressing
+ * difference in the function at position 0 relative to a reference value in
+ * position h when using a backward Taylor expansion:
+ *
+ * \f{eqnarray*}{
+ *  \Delta V & = & hV'(0) + \frac{1}{2} h^2 V''(0) + \frac{1}{6} h^3 V'''(0) + O(h^4) \\
+ *  \Delta V & = & hV'(h) - \frac{1}{2} h^2 V''(h) + \frac{1}{6} h^3 V'''(h) + O(h^4)
+ * \f}
+ *
+ * Summing the equations leads to
+ *
+ * \f[
+ *  2 \Delta V = h(V'(0) + V'(h)) + \frac{1}{2} h^2 (V''(0)-V''(h)) +
+ * \frac{1}{6}h^3(V'''(0)+V'''(h)) + O(h^4) \f]
+ *
+ * To make the second term symmetric too, we can replace it with the average of
+ * the Taylor expansion at 0 and h (i.e., using the third derivative). This gives
+ *
+ * \f[
+ *  2 \Delta V = h(V'(0) + V'(h)) - \frac{1}{12} h^3 (V'''(0)+V'''(h)) + O(h^4)
+ * \f]
+ *
+ * Thus, if we replace the derivative in the internal quadratic table data with
+ *
+ * \f[
+ *  V' - \frac{1}{12}h^2 V'''
+ * \f]
+ *
+ * we will cancel the h^3 term in the error. This will make the integral of the
+ * forces match the potential much better (The h^4 term actually disappears, so
+ * when summing over 1/h points the remaining error will be O(h^4).
+ *
+ * While it is possible to create tables only from function values
+ * (i.e., no derivatives), it is recommended to provide derivatives for higher
+ * accuracy and to avoid issues with numerical differentiation. Note that the
+ * table input should be smooth, i.e. it should not contain noise e.g. from an
+ * (iterative) Boltzmann inversion procedure - you have been warned.
+ *
+ * \note This class is responsible for fundamental interpolation of any function,
+ *       which might or might not correspond to a potential. For this reason
+ *       both input and output derivatives are proper function derivatives, and
+ *       we do not swap any signs to get forces directly from the table.
+ *
+ * \note There will be a small additional accuracy loss from the internal
+ *       operation where we calculate the epsilon offset from the nearest table
+ *       point, since the integer part we subtract can get large in those cases.
+ *       The absolute such error both in the function and derivative value will
+ *       be roughly f''*x*GMX_REAL_EPS, where x is the argument and f'' the
+ *       second derivative.
+ *       While this is technically possible to solve with extended precision
+ *       arithmetics, that would introduce extra instructions in some highly
+ *       performance-sensitive code parts. For typical GROMACS interaction
+ *       functions the derivatives will decay faster than the potential, which
+ *       means it will never play any role. For other functions it will only
+ *       cause a small increase in the relative error for arguments where the
+ *       magnitude of the function or derivative is very small.
+ *       Since we typically sum several results in GROMACS, this should never
+ *       show up in any real cases, and for this reason we choose not to do
+ *       the extended precision arithmetics.
+ *
+ * \note These routines are not suitable for table ranges starting far away
+ *       from zero, since we allocate memory and calculate indices starting from
+ *       range zero for efficiency reasons.
+ */
+class QuadraticSplineTable
+{
+private:
+    /*! \brief Change that function value falls inside range when debugging
+     *
+     *  \tparam T   Lookup argument floating-point type, typically SimdReal or real.
+     *  \param  r   Lookup argument to test
+     *
+     *  \throws Debug builds will throw gmx::RangeError for values that are
+     *          larger than the upper limit of the range, or smaller than 0.
+     *          We allow the table to be called with arguments between 0 and
+     *          the lower limit of the range, since this might in theory occur
+     *          once-in-a-blue-moon with some algorithms.
+     */
+    template<typename T>
+    void rangeCheck(T gmx_unused r) const
+    {
+#ifndef NDEBUG
+        // Check that all values fall in range when debugging
+        if (anyTrue(r < T(0.0) || T(range_.second) <= r))
+        {
+            GMX_THROW(RangeError("Interpolation input value falls outside table definition range"));
+        }
+#endif
+    }
+
+public:
+    /*! \brief Default tolerance for tables is 10*GMX_FLOAT_EPS
+     *
+     *  \note Even for double precision builds we set the tolerance to
+     *        one order of magnitude above the single precision epsilon.
+     */
+    static const real defaultTolerance;
+
+    /*! \brief Initialize table data from function
+     *
+     * \param analyticalInputList Initializer list with one or more functions to tabulate,
+     *                            specified as pairs containing analytical
+     *                            functions and their derivatives. The function will also
+     *                            be called for values smaller than the lower limit of the
+     *                            range, but we avoid calling it for 0.0 if that value
+     *                            is not included in the range.
+     * \param range               Range over which the function will be tabulated.
+     *                            Constructor will throw gmx::APIError for negative values.
+     *                            Due to the way the numerical derivative evaluation depends
+     *                            on machine precision internally, this range must be
+     *                            at least 0.001, or the constructor throws gmx::APIError.
+     * \param tolerance           Requested accuracy of the table. This will be used to
+     *                            calculate the required internal spacing. If this cannot
+     *                            be achieved (for instance because the table would require
+     *                            too much memory) the constructor will throw gmx::ToleranceError.
+     *
+     * \note The functions are always defined in double precision to avoid
+     *       losing accuracy when constructing tables.
+     *
+     * \note Since we fill the table for values below range.first, you can achieve
+     *       a smaller table by using a smaller range where the tolerance has to be
+     *       met, and accept that a few function calls below range.first do not
+     *       quite reach the tolerance.
+     *
+     * \warning For efficiency reasons (since this code is used in some inner
+     *       (kernels), we always allocate memory and calculate table indices
+     *       for the complete interval [0,range.second], although the data will
+     *       not be valid outside the definition range to avoid calling the
+     *       function there. This means you should \a not use this class
+     *       to tabulate functions for small ranges very far away from zero,
+     *       since you would both waste a huge amount of memory and incur
+     *       truncation errors when calculating the index.
+     *
+     * \throws gmx::ToleranceError if the requested tolerance cannot be achieved,
+     *         and gmx::APIError for other incorrect input.
+     */
+    QuadraticSplineTable(std::initializer_list<AnalyticalSplineTableInput> analyticalInputList,
+                         const std::pair<real, real>&                      range,
+                         real tolerance = defaultTolerance);
+
+    /*! \brief Initialize table data from tabulated values and derivatives
+     *
+     * \param numericalInputList  Initializer list with one or more functions to tabulate,
+     *                            specified as pairs containing containing vectors for the
+     *                            function values and their derivatives. Data points are
+     *                            separated by the spacing parameter, starting from 0.
+     *                            Values below the lower limit of the range will be used to
+     *                            attempt defining the table, but we avoid using index 0
+     *                            unless 0.0 is included in the range. Some extra points beyond
+     *                            range.second are required to re-interpolate values, so add
+     *                            some margin. The constructor will throw gmx::APIError if the
+     *                            input vectors are too short to cover the requested range
+     *                            (and they must always be at least five points).
+     * \param range               Range over which the function will be tabulated.
+     *                            Constructor will throw gmx::APIError for negative values,
+     *                            or if the value/derivative vector does not cover the
+     *                            range.
+     * \param tolerance           Requested accuracy of the table in the range. This will be
+     *                            used to calculate the required internal spacing and possibly
+     *                            re-interpolate. The constructor will throw
+     *                            gmx::ToleranceError if the input spacing is too coarse
+     *                            to achieve this accuracy.
+     *
+     * \note The input data vectors are always double precision to avoid
+     *       losing accuracy when constructing tables.
+     *
+     * \note Since we fill the table for values below range.first, you can achieve
+     *       a smaller table by using a smaller range where the tolerance has to be
+     *       met, and accept that a few function calls below range.first do not
+     *       quite reach the tolerance.
+     *
+     * \warning For efficiency reasons (since this code is used in some inner
+     *       (kernels), we always allocate memory and calculate table indices
+     *       for the complete interval [0,range.second], although the data will
+     *       not be valid outside the definition range to avoid calling the
+     *       function there. This means you should \a not use this class
+     *       to tabulate functions for small ranges very far away from zero,
+     *       since you would both waste a huge amount of memory and incur
+     *       truncation errors when calculating the index.
+     */
+    QuadraticSplineTable(std::initializer_list<NumericalSplineTableInput> numericalInputList,
+                         const std::pair<real, real>&                     range,
+                         real tolerance = defaultTolerance);
+
+
+    /************************************************************
+     *           Evaluation methods for single functions        *
+     ************************************************************/
+
+    /*! \brief Evaluate both function and derivative, single table function
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable  Number of separate functions in table, default is 1
+     *  \tparam     funcIndex       Index of function to evaluate in table, default is 0
+     *  \tparam     T               Type (SimdReal or real) of lookup and result
+     *  \param      r               Points for which to evaluate function and derivative
+     *  \param[out] functionValue   Function value
+     *  \param[out] derivativeValue Function derivative
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 1, int funcIndex = 0, typename T>
+    void evaluateFunctionAndDerivative(T r, T* functionValue, T* derivativeValue) const
+    {
+        rangeCheck(r);
+        GMX_ASSERT(numFuncInTable == numFuncInTable_,
+                   "Evaluation method not matching number of functions in table");
+        GMX_ASSERT(funcIndex < numFuncInTable,
+                   "Function index not in range of the number of tables");
+
+        T    rTable   = r * T(tableScale_);
+        auto tabIndex = cvttR2I(rTable); // type is either std::int32_t or SimdInt32
+        T    eps      = rTable - trunc(rTable);
+        T    t0;
+        T    t1;
+        T    t2;
+        T t3 gmx_unused;
+
+        // Load Derivative, Delta, Function, and Zero values for each table point.
+        // The 4 refers to these four values - not any SIMD width.
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                ddfzMultiTableData_.data() + 4 * funcIndex, tabIndex, &t0, &t1, &t2, &t3);
+
+        t1               = t0 + eps * t1;
+        *functionValue   = fma(eps * T(halfSpacing_), t0 + t1, t2);
+        *derivativeValue = t1;
+    }
+
+    /*! \brief Evaluate function value only, single table function
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable  Number of separate functions in table, default is 1
+     *  \tparam     funcIndex       Index of function to evaluate in table, default is 0
+     *  \tparam     T               Type (SimdReal or real) of lookup and result
+     *  \param      r               Points for which to evaluate function value
+     *  \param[out] functionValue   Function value
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 1, int funcIndex = 0, typename T>
+    void evaluateFunction(T r, T* functionValue) const
+    {
+        T der gmx_unused;
+
+        evaluateFunctionAndDerivative<numFuncInTable, funcIndex>(r, functionValue, &der);
+    }
+
+    /*! \brief Evaluate function derivative only, single table function
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable  Number of separate functions in table, default is 1
+     *  \tparam     funcIndex       Index of function to evaluate in table, default is 0
+     *  \tparam     T               Type (SimdReal or real) of lookup and result
+     *  \param      r               Points for which to evaluate function derivative
+     *  \param[out] derivativeValue Function derivative
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 1, int funcIndex = 0, typename T>
+    void evaluateDerivative(T r, T* derivativeValue) const
+    {
+        rangeCheck(r);
+        GMX_ASSERT(numFuncInTable == numFuncInTable_,
+                   "Evaluation method not matching number of functions in table");
+        GMX_ASSERT(funcIndex < numFuncInTable,
+                   "Function index not in range of the number of tables");
+
+        T    rTable   = r * T(tableScale_);
+        auto tabIndex = cvttR2I(rTable); // type is either std::int32_t or SimdInt32
+        T    eps      = rTable - trunc(rTable);
+        T    t0;
+        T    t1;
+        T t2 gmx_unused;
+
+        if (numFuncInTable == 1)
+        {
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(
+                    derivativeMultiTableData_.data() + funcIndex, tabIndex, &t0, &t1); // works for scalar T too
+        }
+        else
+        {
+            // This is not ideal, but we need a version of gatherLoadUBySimdIntTranspose that
+            // only loads a single value from memory to implement it better (will be written)
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(
+                    derivativeMultiTableData_.data() + funcIndex, tabIndex, &t0, &t2); // works for scalar T too
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(derivativeMultiTableData_.data() + funcIndex,
+                                                          tabIndex + T(1),
+                                                          &t1,
+                                                          &t2); // works for scalar T too
+        }
+
+        // (1-eps)*t0 + eps*t1
+        *derivativeValue = fma(t1 - t0, eps, t0);
+    }
+
+    /************************************************************
+     *             Evaluation methods for two functions         *
+     ************************************************************/
+
+    /*! \brief Evaluate both function and derivative, two table functions
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable  Number of separate functions in table, default is 2
+     *  \tparam     funcIndex0       Index of 1st function to evaluate in table, default is 0
+     *  \tparam     funcIndex1       Index of 2nd function to evaluate in table, default is 1
+     *  \tparam     T                Type (SimdReal or real) of lookup and result
+     *  \param      r                Points for which to evaluate function and derivative
+     *  \param[out] functionValue1   Interpolated value for first function
+     *  \param[out] derivativeValue1 Interpolated derivative for first function
+     *  \param[out] functionValue2   Interpolated value for second function
+     *  \param[out] derivativeValue2 Interpolated derivative for second function
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 2, int funcIndex0 = 0, int funcIndex1 = 1, typename T>
+    void evaluateFunctionAndDerivative(T r, T* functionValue1, T* derivativeValue1, T* functionValue2, T* derivativeValue2) const
+    {
+        rangeCheck(r);
+        GMX_ASSERT(numFuncInTable == numFuncInTable_,
+                   "Evaluation method not matching number of functions in table");
+        GMX_ASSERT(funcIndex0 < numFuncInTable && funcIndex1 < numFuncInTable,
+                   "Function index not in range of the number of tables");
+
+        T    rTable   = r * T(tableScale_);
+        auto tabIndex = cvttR2I(rTable); // type is either std::int32_t or SimdInt32
+        T    eps      = rTable - trunc(rTable);
+        T    t0;
+        T    t1;
+        T    t2;
+        T t3 gmx_unused;
+
+        // Load Derivative, Delta, Function, and Zero values for each table point.
+        // The 4 refers to these four values - not any SIMD width.
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                ddfzMultiTableData_.data() + 4 * funcIndex0, tabIndex, &t0, &t1, &t2, &t3);
+        t1                = t0 + eps * t1;
+        *functionValue1   = fma(eps * T(halfSpacing_), t0 + t1, t2);
+        *derivativeValue1 = t1;
+
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                ddfzMultiTableData_.data() + 4 * funcIndex1, tabIndex, &t0, &t1, &t2, &t3);
+        t1                = t0 + eps * t1;
+        *functionValue2   = fma(eps * T(halfSpacing_), t0 + t1, t2);
+        *derivativeValue2 = t1;
+    }
+
+    /*! \brief Evaluate function value only, two table functions
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable   Number of separate functions in table, default is 2
+     *  \tparam     funcIndex0       Index of 1st function to evaluate in table, default is 0
+     *  \tparam     funcIndex1       Index of 2nd function to evaluate in table, default is 1
+     *  \tparam     T                Type (SimdReal or real) of lookup and result
+     *  \param      r                Points for which to evaluate function value
+     *  \param[out] functionValue1   Interpolated value for first function
+     *  \param[out] functionValue2   Interpolated value for second function
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 2, int funcIndex0 = 0, int funcIndex1 = 1, typename T>
+    void evaluateFunction(T r, T* functionValue1, T* functionValue2) const
+    {
+        T der1 gmx_unused;
+        T der2 gmx_unused;
+
+        evaluateFunctionAndDerivative<numFuncInTable, funcIndex0, funcIndex1>(
+                r, functionValue1, &der1, functionValue2, &der2);
+    }
+
+    /*! \brief Evaluate function derivative only, two table functions
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable   Number of separate functions in table, default is 2
+     *  \tparam     funcIndex0       Index of 1st function to evaluate in table, default is 0
+     *  \tparam     funcIndex1       Index of 2nd function to evaluate in table, default is 1
+     *  \tparam     T                Type (SimdReal or real) of lookup and result
+     *  \param      r                Points for which to evaluate function derivative
+     *  \param[out] derivativeValue1 Interpolated derivative for first function
+     *  \param[out] derivativeValue2 Interpolated derivative for second function
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 2, int funcIndex0 = 0, int funcIndex1 = 1, typename T>
+    void evaluateDerivative(T r, T* derivativeValue1, T* derivativeValue2) const
+    {
+        rangeCheck(r);
+        GMX_ASSERT(numFuncInTable == numFuncInTable_,
+                   "Evaluation method not matching number of functions in table");
+        GMX_ASSERT(funcIndex0 < numFuncInTable && funcIndex1 < numFuncInTable,
+                   "Function index not in range of the number of tables");
+
+        T    rTable   = r * T(tableScale_);
+        auto tabIndex = cvttR2I(rTable); // type is either std::int32_t or SimdInt32
+        T    eps      = rTable - trunc(rTable);
+
+        if (numFuncInTable == 2 && funcIndex0 == 0 && funcIndex1 == 1)
+        {
+            T t0A, t0B, t1A, t1B;
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(
+                    derivativeMultiTableData_.data(), tabIndex, &t0A, &t0B); // works for scalar T too
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(
+                    derivativeMultiTableData_.data() + 2, tabIndex, &t1A, &t1B); // works for scalar T too
+            *derivativeValue1 = fma(t1A - t0A, eps, t0A);
+            *derivativeValue2 = fma(t1B - t0B, eps, t0B);
+        }
+        else
+        {
+            T t0, t1, t2;
+            // This is not ideal, but we need a version of gatherLoadUBySimdIntTranspose that
+            // only loads a single value from memory to implement it better (will be written)
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(
+                    derivativeMultiTableData_.data() + funcIndex0, tabIndex, &t0, &t2); // works for scalar T too
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(derivativeMultiTableData_.data() + funcIndex0,
+                                                          tabIndex + T(1),
+                                                          &t1,
+                                                          &t2); // works for scalar T too
+            *derivativeValue1 = fma(t1 - t0, eps, t0);
+
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(
+                    derivativeMultiTableData_.data() + funcIndex1, tabIndex, &t0, &t2); // works for scalar T too
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(derivativeMultiTableData_.data() + funcIndex1,
+                                                          tabIndex + T(1),
+                                                          &t1,
+                                                          &t2); // works for scalar T too
+            *derivativeValue2 = fma(t1 - t0, eps, t0);
+        }
+    }
+
+    /************************************************************
+     *            Evaluation methods for three functions        *
+     ************************************************************/
+
+
+    /*! \brief Evaluate both function and derivative, three table functions
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable   Number of separate functions in table, default is 3
+     *  \tparam     funcIndex0       Index of 1st function to evaluate in table, default is 0
+     *  \tparam     funcIndex1       Index of 2nd function to evaluate in table, default is 1
+     *  \tparam     funcIndex2       Index of 3rd function to evaluate in table, default is 2
+     *  \tparam     T                Type (SimdReal or real) of lookup and result
+     *  \param      r                Points for which to evaluate function and derivative
+     *  \param[out] functionValue1   Interpolated value for first function
+     *  \param[out] derivativeValue1 Interpolated derivative for first function
+     *  \param[out] functionValue2   Interpolated value for second function
+     *  \param[out] derivativeValue2 Interpolated derivative for second function
+     *  \param[out] functionValue3   Interpolated value for third function
+     *  \param[out] derivativeValue3 Interpolated derivative for third function
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 3, int funcIndex0 = 0, int funcIndex1 = 1, int funcIndex2 = 2, typename T>
+    void evaluateFunctionAndDerivative(T  r,
+                                       T* functionValue1,
+                                       T* derivativeValue1,
+                                       T* functionValue2,
+                                       T* derivativeValue2,
+                                       T* functionValue3,
+                                       T* derivativeValue3) const
+    {
+        rangeCheck(r);
+        GMX_ASSERT(numFuncInTable == numFuncInTable,
+                   "Evaluation method not matching number of functions in table");
+        GMX_ASSERT(funcIndex0 < numFuncInTable && funcIndex1 < numFuncInTable && funcIndex2 < numFuncInTable,
+                   "Function index not in range of the number of tables");
+
+        T    rTable   = r * T(tableScale_);
+        auto tabIndex = cvttR2I(rTable); // type is either std::int32_t or SimdInt32
+        T    eps      = rTable - trunc(rTable);
+        T    t0;
+        T    t1;
+        T    t2;
+        T t3 gmx_unused;
+
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                ddfzMultiTableData_.data() + 4 * funcIndex0, tabIndex, &t0, &t1, &t2, &t3);
+        t1                = t0 + eps * t1;
+        *functionValue1   = fma(eps * T(halfSpacing_), t0 + t1, t2);
+        *derivativeValue1 = t1;
+
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                ddfzMultiTableData_.data() + 4 * funcIndex1, tabIndex, &t0, &t1, &t2, &t3);
+        t1                = t0 + eps * t1;
+        *functionValue2   = fma(eps * T(halfSpacing_), t0 + t1, t2);
+        *derivativeValue2 = t1;
+
+        gatherLoadBySimdIntTranspose<4 * numFuncInTable>(
+                ddfzMultiTableData_.data() + 4 * funcIndex2, tabIndex, &t0, &t1, &t2, &t3);
+        t1                = t0 + eps * t1;
+        *functionValue3   = fma(eps * T(halfSpacing_), t0 + t1, t2);
+        *derivativeValue3 = t1;
+    }
+
+    /*! \brief Evaluate function value only, three table functions
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable   Number of separate functions in table, default is 3
+     *  \tparam     funcIndex0       Index of 1st function to evaluate in table, default is 0
+     *  \tparam     funcIndex1       Index of 2nd function to evaluate in table, default is 1
+     *  \tparam     funcIndex2       Index of 3rd function to evaluate in table, default is 2
+     *  \tparam     T                Type (SimdReal or real) of lookup and result
+     *  \param      r                Points for which to evaluate function value
+     *  \param[out] functionValue1   Interpolated value for first function
+     *  \param[out] functionValue2   Interpolated value for second function
+     *  \param[out] functionValue3   Interpolated value for third function
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 3, int funcIndex0 = 0, int funcIndex1 = 1, int funcIndex2 = 2, typename T>
+    void evaluateFunction(T r, T* functionValue1, T* functionValue2, T* functionValue3) const
+    {
+        T der1 gmx_unused;
+        T der2 gmx_unused;
+        T der3 gmx_unused;
+
+        evaluateFunctionAndDerivative<numFuncInTable, funcIndex0, funcIndex1, funcIndex2>(
+                r, functionValue1, &der1, functionValue2, &der2, functionValue3, &der3);
+    }
+
+    /*! \brief Evaluate function derivative only, three table functions
+     *
+     *  This is a templated method where the template can be either real or SimdReal.
+     *
+     *  \tparam     numFuncInTable   Number of separate functions in table, default is 3
+     *  \tparam     funcIndex0       Index of 1st function to evaluate in table, default is 0
+     *  \tparam     funcIndex1       Index of 2nd function to evaluate in table, default is 1
+     *  \tparam     funcIndex2       Index of 3rd function to evaluate in table, default is 2
+     *  \tparam     T                Type (SimdReal or real) of lookup and result
+     *  \param      r                Points for which to evaluate function derivative
+     *  \param[out] derivativeValue1 Interpolated derivative for first function
+     *  \param[out] derivativeValue2 Interpolated derivative for second function
+     *  \param[out] derivativeValue3 Interpolated derivative for third function
+     *
+     *  For debug builds we assert that the input values fall in the range
+     *  specified when constructing the table.
+     */
+    template<int numFuncInTable = 3, int funcIndex0 = 0, int funcIndex1 = 1, int funcIndex2 = 2, typename T>
+    void evaluateDerivative(T r, T* derivativeValue1, T* derivativeValue2, T* derivativeValue3) const
+    {
+        rangeCheck(r);
+        GMX_ASSERT(numFuncInTable == numFuncInTable_,
+                   "Evaluation method not matching number of functions in table");
+        GMX_ASSERT(funcIndex0 < numFuncInTable && funcIndex1 < numFuncInTable && funcIndex2 < numFuncInTable,
+                   "Function index not in range of the number of tables");
+
+        T    rTable   = r * T(tableScale_);
+        auto tabIndex = cvttR2I(rTable); // type is either std::int32_t or SimdInt32
+        T    eps      = rTable - trunc(rTable);
+
+        if (numFuncInTable == 3 && funcIndex0 == 0 && funcIndex1 == 1 && funcIndex2 == 2)
+        {
+            T t0A, t0B, t0C, t1A, t1B, t1C;
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(
+                    derivativeMultiTableData_.data(), tabIndex, &t0A, &t0B);
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(
+                    derivativeMultiTableData_.data() + 2, tabIndex, &t0C, &t1A);
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(
+                    derivativeMultiTableData_.data() + 4, tabIndex, &t1B, &t1C);
+            *derivativeValue1 = fma(t1A - t0A, eps, t0A);
+            *derivativeValue2 = fma(t1B - t0B, eps, t0B);
+            *derivativeValue3 = fma(t1C - t0C, eps, t0C);
+        }
+        else
+        {
+            T t0, t1, t2;
+            // This is not ideal, but we need a version of gatherLoadUBySimdIntTranspose that
+            // only loads a single value from memory to implement it better (will be written)
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(
+                    derivativeMultiTableData_.data() + funcIndex0, tabIndex, &t0, &t2); // works for scalar T too
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(derivativeMultiTableData_.data() + funcIndex0,
+                                                          tabIndex + T(1),
+                                                          &t1,
+                                                          &t2); // works for scalar T too
+            *derivativeValue1 = fma(t1 - t0, eps, t0);
+
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(
+                    derivativeMultiTableData_.data() + funcIndex1, tabIndex, &t0, &t2); // works for scalar T too
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(derivativeMultiTableData_.data() + funcIndex1,
+                                                          tabIndex + T(1),
+                                                          &t1,
+                                                          &t2); // works for scalar T too
+            *derivativeValue2 = fma(t1 - t0, eps, t0);
+
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(
+                    derivativeMultiTableData_.data() + funcIndex2, tabIndex, &t0, &t2); // works for scalar T too
+            gatherLoadUBySimdIntTranspose<numFuncInTable>(derivativeMultiTableData_.data() + funcIndex2,
+                                                          tabIndex + T(1),
+                                                          &t1,
+                                                          &t2); // works for scalar T too
+            *derivativeValue3 = fma(t1 - t0, eps, t0);
+        }
+    }
+
+    /*! \brief Return the table spacing (distance between points)
+     *
+     *  You should never have to use this for normal code, but due to the
+     *  way tables are constructed internally we need this in the unit tests
+     *  to check relative tolerances over each interval.
+     *
+     *  \return table spacing.
+     */
+    real tableSpacing() const { return 1.0 / tableScale_; }
+
+private:
+    std::size_t           numFuncInTable_; //!< Number of separate tabluated functions
+    std::pair<real, real> range_;          //!< Range for which table evaluation is allowed
+    real                  tableScale_;     //!< Table scale (inverse of spacing between points)
+    real                  halfSpacing_;    //!< 0.5*spacing (used for DDFZ table data)
+
+    //!< Derivative values only, with the third-derivative subtraction described in the class documentation.
+    std::vector<real> derivativeMultiTableData_;
+
+    /*! \brief Combined derivative, difference to next derivative, value, and zero.
+     *
+     *  For table point i, this vector contains the four values:
+     *  - derivative[i]
+     *  - (derivative[i+1]-derivative[i])
+     *  - value[i]
+     *  - 0.0
+     *
+     *  For the derivative terms we have subtracted the third-derivative term described
+     *  in the main class documentation.
+     *
+     *  This is typically more efficient than the individual tables, in particular
+     *  when using SIMD. The result should be identical outside the class, so this
+     *  is merely an internal implementation optimization. However, to allow
+     *  aligned SIMD loads we need to use an aligned allocator for this container.
+     *  We occasionally abbreviate this data as DDFZ.
+     */
+    std::vector<real, AlignedAllocator<real>> ddfzMultiTableData_;
+
+    // There should never be any reason to copy the table since it is read-only
+    GMX_DISALLOW_COPY_AND_ASSIGN(QuadraticSplineTable);
+};
+
+
+} // namespace gmx
+
+#endif // GMX_TABLES_QUADRATICSPLINETABLE_H
diff --git a/src/include/gromacs/tables/splineutil.h b/src/include/gromacs/tables/splineutil.h
new file mode 100644 (file)
index 0000000..1ce52d3
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \internal \file
+ * \brief
+ * Declares internal utility functions for spline tables
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \ingroup module_tables
+ */
+#ifndef GMX_TABLES_SPLINEUTIL_H
+#define GMX_TABLES_SPLINEUTIL_H
+
+#include <functional>
+#include <utility>
+#include <vector>
+
+#include "gromacs/utility/alignedallocator.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+namespace internal
+{
+
+/*! \brief Ensure analytical derivative is the derivative of analytical function.
+ *
+ *  This routine evaluates the numerical derivative of the function for
+ *  a few (1000) points in the interval and checks that the relative difference
+ *  between numerical and analytical derivative is within the expected error
+ *  for the numerical derivative approximation we use.
+ *
+ *  The main point of this routine is to make sure the user has not made a
+ *  mistake or sign error when defining the functions.
+ *
+ *  \param function   Analytical function to differentiate
+ *  \param derivative Analytical derivative to compare with
+ *  \param range      Range to test
+ *
+ *  \throws If the provided derivative does not seem to match the function.
+ *
+ *  \note The function/derivative are always double-valued to avoid accuracy loss.
+ */
+void throwUnlessDerivativeIsConsistentWithFunction(const std::function<double(double)>& function,
+                                                   const std::function<double(double)>& derivative,
+                                                   const std::pair<real, real>&         range);
+
+/*! \brief Ensure vector of derivative values is the derivative of function vector.
+ *
+ *  This routine differentiates a vector of numerical values and checks
+ *  that the relative difference to a provided vector of numerical derivatives
+ *  is smaller than the expected error from the numerical differentiation.
+ *
+ *  The main point of this routine is to make sure the user has not made a
+ *  mistake or sign error when defining the functions.
+ *
+ *  To avoid problems if the vectors change from zero to finite values at the
+ *  start/end of the interval, we only check inside the range requested.
+ *
+ *  \param function     Numerical function value vector to differentiate
+ *  \param derivative   Numerical derivative vector to compare with
+ *  \param inputSpacing Distance between input points
+ *  \param range        Range to test
+ *
+ *  \throws If the provided derivative does not seem to match the function.
+ *
+ *  \note The function/derivative vectors and spacing are always double-valued
+ *        to avoid accuracy loss.
+ */
+void throwUnlessDerivativeIsConsistentWithFunction(ArrayRef<const double>       function,
+                                                   ArrayRef<const double>       derivative,
+                                                   double                       inputSpacing,
+                                                   const std::pair<real, real>& range);
+
+
+/*! \brief Find smallest quotient between analytical function and its 2nd derivative
+ *
+ *  Used to calculate spacing for quadratic spline tables. This function divides the
+ *  function value by the second derivative (or a very small number when that is zero),
+ *  and returns the smallest such quotient found in the range.
+ *
+ *  Our quadratic tables corresponds to linear interpolation of the derivative,
+ *  which means the derivative will typically have larger error than the value
+ *  when interpolating. The spacing required to reach a particular relative
+ *  tolerance in the derivative depends on the quotient between the first
+ *  derivative and the third derivative of the function itself.
+ *
+ *  You should call this routine with the analytical derivative as the "function"
+ *  parameter, and the quotient between "function and second derivative" will
+ *  then correspond to the quotient bewteen the derivative and the third derivative
+ *  of the actual function we want to tabulate.
+ *
+ *  Since all functions that can be tabulated efficiently are reasonably smooth,
+ *  we simply check 1,000 points in the interval rather than bother about
+ *  implementing any complicated global optimization scheme.
+ *
+ *  \param f          Analytical function
+ *  \param range      Interval
+ *
+ *  \return Smallest quotient found in range.
+ *
+ *  \note The function is always double-valued to avoid accuracy loss.
+ */
+real findSmallestQuotientOfFunctionAndSecondDerivative(const std::function<double(double)>& f,
+                                                       const std::pair<real, real>&         range);
+
+
+/*! \brief Find smallest quotient between vector of values and its 2nd derivative
+ *
+ *  Used to calculate spacing for quadratic spline tables. This function divides the
+ *  function value by the second derivative (or a very small number when that is zero),
+ *  and returns the smallest such quotient found in the range.
+ *
+ *  Our quadratic tables corresponds to linear interpolation of the derivative,
+ *  which means the derivative will typically have larger error than the value
+ *  when interpolating. The spacing required to reach a particular relative
+ *  tolerance in the derivative depends on the quotient between the first
+ *  derivative and the third derivative of the function itself.
+ *
+ *  You should call this routine with the analytical derivative as the "function"
+ *  parameter, and the quotient between "function and second derivative" will
+ *  then correspond to the quotient bewteen the derivative and the third derivative
+ *  of the actual function we want to tabulate.
+ *
+ *  \param function     Vector with function values
+ *  \param inputSpacing Spacing between function values
+ *  \param range        Interval to check
+ *
+ *  \return Smallest quotient found in range.
+ *
+ *  \note The function vector and input spacing are always double-valued to
+ *        avoid accuracy loss.
+ */
+real findSmallestQuotientOfFunctionAndSecondDerivative(ArrayRef<const double>       function,
+                                                       double                       inputSpacing,
+                                                       const std::pair<real, real>& range);
+
+
+/*! \brief Find smallest quotient between analytical function and its 3rd derivative
+ *
+ *  Used to calculate table spacing. This function divides the function value
+ *  by the second derivative (or a very small number when that is zero), and
+ *  returns the smallest such quotient found in the range.
+ *
+ *  Our quadratic tables corresponds to linear interpolation of the derivative,
+ *  which means the derivative will typically have larger error than the value
+ *  when interpolating. The spacing required to reach a particular relative
+ *  tolerance in the derivative depends on the quotient between the first
+ *  derivative and the third derivative of the function itself.
+ *
+ *  You should call this routine with the analytical derivative as the "function"
+ *  parameter, and the quotient between "function and second derivative" will
+ *  then correspond to the quotient bewteen the derivative and the third derivative
+ *  of the actual function we want to tabulate.
+ *
+ *  Since all functions that can be tabulated efficiently are reasonably smooth,
+ *  we simply check 1,000 points in the interval rather than bother about
+ *  implementing any complicated global optimization scheme.
+ *
+ *  \param f          Analytical function
+ *  \param range      Interval
+ *
+ *  \return Smallest quotient found in range.
+ *
+ *  \note The function is always double-valued to avoid accuracy loss.
+ */
+real findSmallestQuotientOfFunctionAndThirdDerivative(const std::function<double(double)>& f,
+                                                      const std::pair<real, real>&         range);
+
+
+/*! \brief Find smallest quotient between function and 2nd derivative (vectors)
+ *
+ *  Used to calculate table spacing. This function divides the function value
+ *  by the second derivative (or a very small number when that is zero), and
+ *  returns the smallest such quotient found in the range.
+ *
+ *  Our quadratic tables corresponds to linear interpolation of the derivative,
+ *  which means the derivative will typically have larger error than the value
+ *  when interpolating. The spacing required to reach a particular relative
+ *  tolerance in the derivative depends on the quotient between the first
+ *  derivative and the third derivative of the function itself.
+ *
+ *  You should call this routine with the analytical derivative as the "function"
+ *  parameter, and the quotient between "function and second derivative" will
+ *  then correspond to the quotient bewteen the derivative and the third derivative
+ *  of the actual function we want to tabulate.
+ *
+ *  \param function     Vector with function values
+ *  \param inputSpacing Spacing between function values
+ *  \param range        Interval to check
+ *
+ *  \return Smallest quotient found in range.
+ *
+ *  \note The function vector and input spacing are always double-valued to
+ *        avoid accuracy loss.
+ */
+real findSmallestQuotientOfFunctionAndThirdDerivative(ArrayRef<const double>       function,
+                                                      double                       inputSpacing,
+                                                      const std::pair<real, real>& range);
+
+
+/*! \brief Calculate second derivative of vector and return vector of same length
+ *
+ *  5-point approximations are used, with endpoints using non-center interpolation.
+ *
+ *  \param f       Vector (function) for which to calculate second derivative
+ *  \param spacing Spacing of input data.
+ *
+ *  \throws If the input vector has fewer than five data points.
+ *
+ * \note This function always uses double precision arguments since it is meant
+ *       to be used on raw user input data for tables, where we want to avoid
+ *       accuracy loss (since differentiation can be numerically fragile).
+ */
+std::vector<double> vectorSecondDerivative(ArrayRef<const double> f, double spacing);
+
+
+/*! \brief Copy (temporary) table data into aligned multiplexed vector
+ *
+ *  This routine takes the temporary data generated for a single table
+ *  and writes multiplexed output into a multiple-table-data vector.
+ *  If the output vector is empty we will resize it to fit the data, and
+ *  otherwise we assert the size is correct to add out input data.
+ *
+ *  \tparam T     Type of container for input data
+ *  \tparam U     Type of container for output data
+ *
+ *  \param[in]    inputData               Input data for single table
+ *  \param[inout] multiplexedOutputData   Multiplexed output vector, many tables.
+ *  \param[in]    valuesPerTablePoint     Number of real values for each table point,
+ *                                        for instance 4 in DDFZ tables.
+ *  \param[in]    numTables               Number of tables mixed into multiplexed output
+ *  \param[in]    thisTableIndex          Index of this table in multiplexed output
+ *
+ *  \note The output container type can be different from the input since the latter
+ *        sometimes uses an aligned allocator so the data can be loaded efficiently
+ *        in the GROMACS nonbonded kernels.
+ */
+template<class T, class U>
+void fillMultiplexedTableData(const T     inputData,
+                              U*          multiplexedOutputData,
+                              std::size_t valuesPerTablePoint,
+                              std::size_t numTables,
+                              std::size_t thisTableIndex)
+{
+    if (multiplexedOutputData->empty())
+    {
+        multiplexedOutputData->resize(inputData.size() * numTables);
+    }
+    else
+    {
+        GMX_ASSERT(inputData.size() * numTables == multiplexedOutputData->size(),
+                   "Size mismatch when multiplexing table data");
+    }
+
+    GMX_ASSERT(inputData.size() % valuesPerTablePoint == 0,
+               "Input table size must be a multiple of valuesPerTablePoint");
+
+    std::size_t points = inputData.size() / valuesPerTablePoint;
+
+    for (std::size_t i = 0; i < points; i++)
+    {
+        std::size_t inputOffset  = valuesPerTablePoint * i;
+        std::size_t outputOffset = valuesPerTablePoint * (numTables * i + thisTableIndex);
+
+        for (std::size_t j = 0; j < valuesPerTablePoint; j++)
+        {
+            (*multiplexedOutputData)[outputOffset + j] = inputData[inputOffset + j];
+        }
+    }
+}
+
+
+} // namespace internal
+
+} // namespace gmx
+
+#endif // GMX_TABLES_SPLINEUTIL_H
diff --git a/src/include/gromacs/tables/tableinput.h b/src/include/gromacs/tables/tableinput.h
new file mode 100644 (file)
index 0000000..6c30acd
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/*! \libinternal \file
+ * \brief
+ * Declares structures for analytical or numerical input data to construct tables
+ *
+ * \inlibraryapi
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \ingroup module_tables
+ */
+
+#ifndef GMX_TABLES_TABLEINPUT_H
+#define GMX_TABLES_TABLEINPUT_H
+
+#include <functional>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief Specification for analytical table function (name, function, derivative)
+ */
+struct AnalyticalSplineTableInput
+{
+    //NOLINTNEXTLINE(google-runtime-member-string-references)
+    const std::string&            desc;       //!< \libinternal Brief description of function
+    std::function<double(double)> function;   //!< \libinternal Analytical form of function
+    std::function<double(double)> derivative; //!< \libinternal Analytical derivative
+};
+
+/*! \libinternal \brief Specification for vector table function (name, function, derivative, spacing)
+ */
+struct NumericalSplineTableInput
+{
+    //NOLINTNEXTLINE(google-runtime-member-string-references)
+    const std::string&     desc;       //!< \libinternal Brief description of function
+    ArrayRef<const double> function;   //!< \libinternal Vector with function values
+    ArrayRef<const double> derivative; //!< \libinternal Vector with derivative values
+    double                 spacing;    //!< \libinternal Distance between data points
+};
+
+
+} // namespace gmx
+
+
+#endif // GMX_TABLES_TABLEINPUT_H
diff --git a/src/include/gromacs/taskassignment/decidegpuusage.h b/src/include/gromacs/taskassignment/decidegpuusage.h
new file mode 100644 (file)
index 0000000..3404beb
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares functionality for deciding whether tasks will run on GPUs.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_taskassignment
+ * \inlibraryapi
+ */
+
+#ifndef GMX_TASKASSIGNMENT_DECIDEGPUUSAGE_H
+#define GMX_TASKASSIGNMENT_DECIDEGPUUSAGE_H
+
+#include <vector>
+
+struct gmx_hw_info_t;
+struct gmx_mtop_t;
+struct t_inputrec;
+enum class PmeRunMode;
+
+namespace gmx
+{
+
+class MDLogger;
+
+//! Record where a compute task is targetted.
+enum class TaskTarget : int
+{
+    Auto,
+    Cpu,
+    Gpu
+};
+
+//! Help pass GPU-emulation parameters with type safety.
+enum class EmulateGpuNonbonded : bool
+{
+    //! Do not emulate GPUs.
+    No,
+    //! Do emulate GPUs.
+    Yes
+};
+
+/*! \libinternal
+ *  \brief Structure that holds boolean flags corresponding to the development
+ *        features present enabled through environment variables.
+ *
+ */
+struct DevelopmentFeatureFlags
+{
+    //! True if the Buffer ops development feature is enabled
+    // TODO: when the trigger of the buffer ops offload is fully automated this should go away
+    bool enableGpuBufferOps = false;
+    //! If true, forces 'mdrun -update auto' default to 'gpu'
+    bool forceGpuUpdateDefault = false;
+    //! True if the GPU halo exchange development feature is enabled
+    bool enableGpuHaloExchange = false;
+    //! True if the PME PP direct communication GPU development feature is enabled
+    bool enableGpuPmePPComm = false;
+    //! True if the CUDA-aware MPI is being used for GPU direct communication feature
+    bool usingCudaAwareMpi = false;
+};
+
+
+class MDAtoms;
+
+/*! \brief Decide whether this thread-MPI simulation will run
+ * nonbonded tasks on GPUs.
+ *
+ * The number of GPU tasks and devices influences both the choice of
+ * the number of ranks, and checks upon any such choice made by the
+ * user. So we need to consider this before any automated choice of
+ * the number of thread-MPI ranks.
+ *
+ * \param[in] nonbondedTarget              The user's choice for mdrun -nb for where to assign
+ *                                         short-ranged nonbonded interaction tasks.
+ * \param[in] haveAvailableDevices         Whether there are available devices.
+ * \param[in] userGpuTaskAssignment        The user-specified assignment of GPU tasks to device IDs.
+ * \param[in] emulateGpuNonbonded          Whether we will emulate GPU calculation of nonbonded
+ *                                         interactions.
+ * \param[in] buildSupportsNonbondedOnGpu  Whether GROMACS was built with GPU support.
+ * \param[in] nonbondedOnGpuIsUseful       Whether computing nonbonded interactions on a GPU is
+ *                                         useful for this calculation.
+ * \param[in] numRanksPerSimulation        The number of ranks in each simulation.
+ *
+ * \returns    Whether the simulation will run nonbonded tasks on GPUs.
+ *
+ * \throws     std::bad_alloc          If out of memory
+ *             InconsistentInputError  If the user requirements are inconsistent. */
+bool decideWhetherToUseGpusForNonbondedWithThreadMpi(TaskTarget              nonbondedTarget,
+                                                     bool                    haveAvailableDevices,
+                                                     const std::vector<int>& userGpuTaskAssignment,
+                                                     EmulateGpuNonbonded     emulateGpuNonbonded,
+                                                     bool buildSupportsNonbondedOnGpu,
+                                                     bool nonbondedOnGpuIsUseful,
+                                                     int  numRanksPerSimulation);
+
+/*! \brief Decide whether this thread-MPI simulation will run
+ * PME tasks on GPUs.
+ *
+ * The number of GPU tasks and devices influences both the choice of
+ * the number of ranks, and checks upon any such choice made by the
+ * user. So we need to consider this before any automated choice of
+ * the number of thread-MPI ranks.
+ *
+ * \param[in]  useGpuForNonbonded        Whether GPUs will be used for nonbonded interactions.
+ * \param[in]  pmeTarget                 The user's choice for mdrun -pme for where to assign
+ *                                       long-ranged PME nonbonded interaction tasks.
+ * \param[in]  numDevicesToUse           The number of compatible GPUs that the user permitted us to use.
+ * \param[in]  userGpuTaskAssignment     The user-specified assignment of GPU tasks to device IDs.
+ * \param[in]  hardwareInfo              Hardware information
+ * \param[in]  inputrec                  The user input
+ * \param[in]  numRanksPerSimulation     The number of ranks in each simulation.
+ * \param[in]  numPmeRanksPerSimulation  The number of PME ranks in each simulation.
+ *
+ * \returns    Whether the simulation will run PME tasks on GPUs.
+ *
+ * \throws     std::bad_alloc          If out of memory
+ *             InconsistentInputError  If the user requirements are inconsistent. */
+bool decideWhetherToUseGpusForPmeWithThreadMpi(bool                    useGpuForNonbonded,
+                                               TaskTarget              pmeTarget,
+                                               int                     numDevicesToUse,
+                                               const std::vector<int>& userGpuTaskAssignment,
+                                               const gmx_hw_info_t&    hardwareInfo,
+                                               const t_inputrec&       inputrec,
+                                               int                     numRanksPerSimulation,
+                                               int                     numPmeRanksPerSimulation);
+
+/*! \brief Decide whether the simulation will try to run nonbonded
+ * tasks on GPUs.
+ *
+ * The final decision cannot be made until after the duty of the rank
+ * is known. But we need to know if nonbonded will run on GPUs for
+ * setting up DD (particularly rlist) and determining duty. If the
+ * user requires GPUs for the tasks of that duty, then it will be an
+ * error when none are found.
+ *
+ * With thread-MPI, calls have been made to
+ * decideWhetherToUseGpusForNonbondedWithThreadMpi() and
+ * decideWhetherToUseGpusForPmeWithThreadMpi() to help determine
+ * the number of ranks and run some checks, but the final
+ * decision is made in this routine, along with many more
+ * consistency checks.
+ *
+ * \param[in]  nonbondedTarget             The user's choice for mdrun -nb for where to assign short-ranged nonbonded interaction tasks.
+ * \param[in]  userGpuTaskAssignment       The user-specified assignment of GPU tasks to device IDs.
+ * \param[in]  emulateGpuNonbonded         Whether we will emulate GPU calculation of nonbonded interactions.
+ * \param[in]  buildSupportsNonbondedOnGpu Whether GROMACS was build with GPU support.
+ * \param[in]  nonbondedOnGpuIsUseful      Whether computing nonbonded interactions on a GPU is useful for this calculation.
+ * \param[in]  gpusWereDetected            Whether compatible GPUs were detected on any node.
+ *
+ * \returns    Whether the simulation will run nonbonded and PME tasks, respectively, on GPUs.
+ *
+ * \throws     std::bad_alloc          If out of memory
+ *             InconsistentInputError  If the user requirements are inconsistent. */
+bool decideWhetherToUseGpusForNonbonded(TaskTarget              nonbondedTarget,
+                                        const std::vector<int>& userGpuTaskAssignment,
+                                        EmulateGpuNonbonded     emulateGpuNonbonded,
+                                        bool                    buildSupportsNonbondedOnGpu,
+                                        bool                    nonbondedOnGpuIsUseful,
+                                        bool                    gpusWereDetected);
+
+/*! \brief Decide whether the simulation will try to run tasks of
+ * different types on GPUs.
+ *
+ * The final decision cannot be made until after the duty of the rank
+ * is known. But we need to know if nonbonded will run on GPUs for
+ * setting up DD (particularly rlist) and determining duty. If the
+ * user requires GPUs for the tasks of that duty, then it will be an
+ * error when none are found.
+ *
+ * With thread-MPI, calls have been made to
+ * decideWhetherToUseGpusForNonbondedWithThreadMpi() and
+ * decideWhetherToUseGpusForPmeWithThreadMpi() to help determine
+ * the number of ranks and run some checks, but the final
+ * decision is made in this routine, along with many more
+ * consistency checks.
+ *
+ * \param[in]  useGpuForNonbonded        Whether GPUs will be used for nonbonded interactions.
+ * \param[in]  pmeTarget                 The user's choice for mdrun -pme for where to assign long-ranged PME nonbonded interaction tasks.
+ * \param[in]  userGpuTaskAssignment     The user-specified assignment of GPU tasks to device IDs.
+ * \param[in]  hardwareInfo              Hardware information
+ * \param[in]  inputrec                  The user input
+ * \param[in]  numRanksPerSimulation     The number of ranks in each simulation.
+ * \param[in]  numPmeRanksPerSimulation  The number of PME ranks in each simulation.
+ * \param[in]  gpusWereDetected          Whether compatible GPUs were detected on any node.
+ *
+ * \returns    Whether the simulation will run nonbonded and PME tasks, respectively, on GPUs.
+ *
+ * \throws     std::bad_alloc          If out of memory
+ *             InconsistentInputError  If the user requirements are inconsistent. */
+bool decideWhetherToUseGpusForPme(bool                    useGpuForNonbonded,
+                                  TaskTarget              pmeTarget,
+                                  const std::vector<int>& userGpuTaskAssignment,
+                                  const gmx_hw_info_t&    hardwareInfo,
+                                  const t_inputrec&       inputrec,
+                                  int                     numRanksPerSimulation,
+                                  int                     numPmeRanksPerSimulation,
+                                  bool                    gpusWereDetected);
+
+/*! \brief Determine PME run mode.
+ *
+ * Given the PME task assignment in \p useGpuForPme and the user-provided
+ * FFT task target in \p pmeFftTarget, returns a PME run mode for the
+ * current run. It also checks the compatibility of the two.
+ *
+ * \note Aborts the run upon incompatible values of \p useGpuForPme and \p pmeFftTarget.
+ *
+ * \param[in]  useGpuForPme              PME task assignment, true if PME task is mapped to the GPU.
+ * \param[in]  pmeFftTarget              The user's choice for -pmefft for where to assign the FFT
+ * work of the PME task. \param[in]  inputrec                  The user input record
+ * */
+PmeRunMode determinePmeRunMode(bool useGpuForPme, const TaskTarget& pmeFftTarget, const t_inputrec& inputrec);
+
+/*! \brief Decide whether the simulation will try to run bonded tasks on GPUs.
+ *
+ * \param[in]  useGpuForNonbonded        Whether GPUs will be used for nonbonded interactions.
+ * \param[in]  useGpuForPme              Whether GPUs will be used for PME interactions.
+ * \param[in]  bondedTarget              The user's choice for mdrun -bonded for where to assign tasks.
+ * \param[in]  inputrec                  The user input.
+ * \param[in]  mtop                      The global topology.
+ * \param[in]  numPmeRanksPerSimulation  The number of PME ranks in each simulation, can be -1 for auto.
+ * \param[in]  gpusWereDetected          Whether compatible GPUs were detected on any node.
+ *
+ * \returns    Whether the simulation will run bondeded tasks on GPUs.
+ *
+ * \throws     std::bad_alloc          If out of memory
+ *             InconsistentInputError  If the user requirements are inconsistent. */
+bool decideWhetherToUseGpusForBonded(bool              useGpuForNonbonded,
+                                     bool              useGpuForPme,
+                                     TaskTarget        bondedTarget,
+                                     const t_inputrec& inputrec,
+                                     const gmx_mtop_t& mtop,
+                                     int               numPmeRanksPerSimulation,
+                                     bool              gpusWereDetected);
+
+/*! \brief Decide whether to use GPU for update.
+ *
+ * \param[in]  isDomainDecomposition        Whether there more than one domain.
+ * \param[in]  useUpdateGroups              If the constraints can be split across domains.
+ * \param[in]  pmeRunMode                   PME running mode: CPU, GPU or mixed.
+ * \param[in]  havePmeOnlyRank              If there is a PME-only rank in the simulation.
+ * \param[in]  useGpuForNonbonded           Whether GPUs will be used for nonbonded interactions.
+ * \param[in]  updateTarget                 User choice for running simulation on GPU.
+ * \param[in]  gpusWereDetected             Whether compatible GPUs were detected on any node.
+ * \param[in]  inputrec                     The user input.
+ * \param[in]  mtop                         The global topology.
+ * \param[in]  useEssentialDynamics         If essential dynamics is active.
+ * \param[in]  doOrientationRestraints      If orientation restraints are enabled.
+ * \param[in]  haveFrozenAtoms              If this simulation has frozen atoms (see Issue #3920).
+ * \param[in]  doRerun                      It this is a rerun.
+ * \param[in]  devFlags                     GPU development / experimental feature flags.
+ * \param[in]  mdlog                        MD logger.
+ *
+ * \returns    Whether complete simulation can be run on GPU.
+ * \throws     std::bad_alloc            If out of memory
+ *             InconsistentInputError    If the user requirements are inconsistent.
+ */
+bool decideWhetherToUseGpuForUpdate(bool                           isDomainDecomposition,
+                                    bool                           useUpdateGroups,
+                                    PmeRunMode                     pmeRunMode,
+                                    bool                           havePmeOnlyRank,
+                                    bool                           useGpuForNonbonded,
+                                    TaskTarget                     updateTarget,
+                                    bool                           gpusWereDetected,
+                                    const t_inputrec&              inputrec,
+                                    const gmx_mtop_t&              mtop,
+                                    bool                           useEssentialDynamics,
+                                    bool                           doOrientationRestraints,
+                                    bool                           haveFrozenAtoms,
+                                    bool                           doRerun,
+                                    const DevelopmentFeatureFlags& devFlags,
+                                    const gmx::MDLogger&           mdlog);
+
+
+/*! \brief Decide whether to use GPU for halo exchange.
+ *
+ * \param[in]  devFlags                     GPU development / experimental feature flags.
+ * \param[in]  havePPDomainDecomposition    Whether PP domain decomposition is in use.
+ * \param[in]  useGpuForNonbonded           Whether GPUs will be used for nonbonded interactions.
+ * \param[in]  useModularSimulator          Whether modularsimulator is in use.
+ * \param[in]  doRerun                      Whether this is a rerun.
+ * \param[in]  haveEnergyMinimization       Whether energy minimization is in use.
+ *
+ * \returns    Whether halo exchange can be run on GPU.
+ */
+bool decideWhetherToUseGpuForHalo(const DevelopmentFeatureFlags& devFlags,
+                                  bool                           havePPDomainDecomposition,
+                                  bool                           useGpuForNonbonded,
+                                  bool                           useModularSimulator,
+                                  bool                           doRerun,
+                                  bool                           haveEnergyMinimization);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/taskassignment/decidesimulationworkload.h b/src/include/gromacs/taskassignment/decidesimulationworkload.h
new file mode 100644 (file)
index 0000000..3ea543e
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal
+ * \file
+ * \brief Declares routine for deciding simulation workload based on GPU tasks.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \ingroup module_taskassignment
+ */
+#ifndef GMX_TASKASSIGNMENT_DECIDESIMULATIONWORKLOAD_H
+#define GMX_TASKASSIGNMENT_DECIDESIMULATIONWORKLOAD_H
+
+#include <vector>
+
+#include "gromacs/mdtypes/inputrec.h"
+#include "gromacs/mdtypes/simulation_workload.h"
+
+enum class PmeRunMode;
+
+namespace gmx
+{
+
+struct DevelopmentFeatureFlags;
+
+/*! \brief
+ * Build datastructure that contains decisions whether to run different workload
+ * task on GPUs.
+ *
+ * \param[in] inputrec           The input record
+ * \param[in] disableNonbondedCalculation  Disable calculation of nonbonded forces
+ * \param[in] devFlags           The development feature flags
+ * \param[in] havePpDomainDecomposition Whether PP domain decomposition is used in this run.
+ * \param[in] haveSeparatePmeRank Whether separate PME rank(s) are used in this run.
+ * \param[in] useGpuForNonbonded Whether we have short-range nonbonded interactions
+ *                               calculations on GPU(s).
+ * \param[in] pmeRunMode         Run mode indicating what resource is PME execured on.
+ * \param[in] useGpuForBonded    Whether bonded interactions are calculated on GPU(s).
+ * \param[in] useGpuForUpdate    Whether coordinate update and constraint solving is performed on
+ *                               GPU(s).
+ * \param[in] useGpuDirectHalo   Whether halo exchange is performed directly between GPUs.
+ * \returns Simulation lifetime constant workload description.
+ */
+SimulationWorkload createSimulationWorkload(const t_inputrec& inputrec,
+                                            bool              disableNonbondedCalculation,
+                                            const DevelopmentFeatureFlags& devFlags,
+                                            bool       havePpDomainDecomposition,
+                                            bool       haveSeparatePmeRank,
+                                            bool       useGpuForNonbonded,
+                                            PmeRunMode pmeRunMode,
+                                            bool       useGpuForBonded,
+                                            bool       useGpuForUpdate,
+                                            bool       useGpuDirectHalo);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/taskassignment/findallgputasks.h b/src/include/gromacs/taskassignment/findallgputasks.h
new file mode 100644 (file)
index 0000000..c507c05
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal
+ * \file
+ * \brief Declares routine for collecting all GPU tasks found on ranks of a node.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_taskassignment
+ */
+#ifndef GMX_TASKASSIGNMENT_FINDALLGPUTASKS_H
+#define GMX_TASKASSIGNMENT_FINDALLGPUTASKS_H
+
+#include <vector>
+
+namespace gmx
+{
+
+enum class GpuTask;
+enum class TaskTarget;
+class PhysicalNodeCommunicator;
+template<typename T>
+class ArrayRef;
+//! Container of compute tasks suitable to run on a GPU e.g. on each rank of a node.
+using GpuTasksOnRanks = std::vector<std::vector<GpuTask>>;
+
+/*! \brief Returns container of all tasks on this rank
+ * that are eligible for GPU execution.
+ *
+ * \param[in]  haveGpusOnThisPhysicalNode Whether there are any GPUs on this physical node.
+ * \param[in]  nonbondedTarget            The user's choice for mdrun -nb for where to assign
+ *                                        short-ranged nonbonded interaction tasks.
+ * \param[in]  pmeTarget                  The user's choice for mdrun -pme for where to assign
+ *                                        long-ranged PME nonbonded interaction tasks.
+ * \param[in]  bondedTarget               The user's choice for mdrun -bonded for where to assign tasks.
+ * \param[in]  updateTarget               The user's choice for mdrun -update for where to assign tasks.
+ * \param[in]  useGpuForNonbonded         Whether GPUs will be used for nonbonded interactions.
+ * \param[in]  useGpuForPme               Whether GPUs will be used for PME interactions.
+ * \param[in]  rankHasPpTask              Whether this rank has a PP task
+ * \param[in]  rankHasPmeTask             Whether this rank has a PME task
+ */
+std::vector<GpuTask> findGpuTasksOnThisRank(bool       haveGpusOnThisPhysicalNode,
+                                            TaskTarget nonbondedTarget,
+                                            TaskTarget pmeTarget,
+                                            TaskTarget bondedTarget,
+                                            TaskTarget updateTarget,
+                                            bool       useGpuForNonbonded,
+                                            bool       useGpuForPme,
+                                            bool       rankHasPpTask,
+                                            bool       rankHasPmeTask);
+
+/*! \brief Returns container of all tasks on all ranks of this node
+ * that are eligible for GPU execution.
+ *
+ * Perform all necessary communication for preparing for task
+ * assignment. Separating this aspect makes it possible to unit test
+ * the logic of task assignment. */
+GpuTasksOnRanks findAllGpuTasksOnThisNode(ArrayRef<const GpuTask>         gpuTasksOnThisRank,
+                                          const PhysicalNodeCommunicator& physicalNodeComm);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/taskassignment/reportgpuusage.h b/src/include/gromacs/taskassignment/reportgpuusage.h
new file mode 100644 (file)
index 0000000..dd07a14
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \defgroup module_taskassignment Assigning simulation tasks to hardware (taskassignment)
+ * \ingroup group_mdrun
+ * \brief Provides code that manages assignment of simulation tasks to hardware.
+ */
+/*! \internal
+ * \file
+ * \brief Declares routine for reporting GPU usage.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_taskassignment
+ */
+#ifndef GMX_TASKASSIGNMENT_REPORTGPUUSAGE_H
+#define GMX_TASKASSIGNMENT_REPORTGPUUSAGE_H
+
+#include <cstdlib>
+
+#include <vector>
+
+enum class PmeRunMode;
+
+namespace gmx
+{
+
+class MDLogger;
+struct GpuTaskMapping;
+template<typename T>
+class ArrayRef;
+using GpuTaskAssignment = std::vector<GpuTaskMapping>;
+
+/*! \brief Log a report on how GPUs are being used on
+ * the ranks of the physical node of rank 0 of the simulation.
+ *
+ * \todo It could be useful to report also whether any nodes differed,
+ * and in what way.
+ *
+ * \param[in]  mdlog                               Logging object.
+ * \param[in]  gpuTaskAssignmentOnRanksOfThisNode  The selected GPU IDs.
+ * \param[in]  numGpuTasksOnThisNode               The number of GPU tasks on this node.
+ * \param[in]  numPpRanks                          Number of PP ranks on this node
+ * \param[in]  printHostName                       Print the hostname in the usage information
+ * \param[in]  useGpuForBonded                     Whether GPU PP tasks will do bonded work on GPU
+ * \param[in]  pmeRunMode                          Describes the execution of PME tasks
+ * \param[in]  useGpuForUpdate                     Whether update will run on the GPU.
+ *
+ * \throws     std::bad_alloc if out of memory */
+void reportGpuUsage(const MDLogger&                   mdlog,
+                    ArrayRef<const GpuTaskAssignment> gpuTaskAssignmentOnRanksOfThisNode,
+                    size_t                            numGpuTasksOnThisNode,
+                    size_t                            numPpRanks,
+                    bool                              printHostName,
+                    bool                              useGpuForBonded,
+                    PmeRunMode                        pmeRunMode,
+                    bool                              useGpuForUpdate);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/taskassignment/resourcedivision.h b/src/include/gromacs/taskassignment/resourcedivision.h
new file mode 100644 (file)
index 0000000..f9e7151
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares utility functionality for dividing resources and
+ * checking for consistency and usefulness.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_taskassignment
+ * \inlibraryapi
+ */
+
+#ifndef GMX_TASKASSIGNMENT_RESOURCEDIVISION_H
+#define GMX_TASKASSIGNMENT_RESOURCEDIVISION_H
+
+#include <cstdio>
+
+#include <vector>
+
+#include "gromacs/ewald/pme.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct gmx_hw_info_t;
+struct gmx_hw_opt_t;
+struct gmx_mtop_t;
+struct gmx_multisim_t;
+struct t_commrec;
+struct t_inputrec;
+
+namespace gmx
+{
+class HardwareTopology;
+class MDLogger;
+class PhysicalNodeCommunicator;
+} // namespace gmx
+
+/*! \brief Return the number of threads to use for thread-MPI based on how many
+ * were requested, which algorithms we're using,
+ * and how many particles there are.
+ * At the point we have already called check_and_update_hw_opt.
+ * Thus all options should be internally consistent and consistent
+ * with the hardware, except that ntmpi could be larger than number of GPUs.
+ * If necessary, this function will modify hw_opt->nthreads_omp.
+ */
+int get_nthreads_mpi(const gmx_hw_info_t* hwinfo,
+                     gmx_hw_opt_t*        hw_opt,
+                     int                  numDevicesToUse,
+                     bool                 nonbondedOnGpu,
+                     bool                 pmeOnGpu,
+                     const t_inputrec*    inputrec,
+                     const gmx_mtop_t&    mtop,
+                     const gmx::MDLogger& mdlog,
+                     bool                 doMembed);
+
+/*! \brief Check if the number of OpenMP threads is within reasonable range
+ * considering the hardware used. This is a crude check, but mainly
+ * intended to catch cases where the user starts 1 MPI rank per hardware
+ * thread or 1 rank per physical node.
+ * With a sub-optimal setup a note is printed to fplog and stderr when
+ * bNtOmpSet==TRUE; with bNtOptOptionSet==FALSE a fatal error is issued.
+ * This function should be called after thread-MPI and OpenMP are set up.
+ */
+void check_resource_division_efficiency(const gmx_hw_info_t* hwinfo,
+                                        bool                 willUsePhysicalGpu,
+                                        gmx_bool             bNtOmpOptionSet,
+                                        t_commrec*           cr,
+                                        const gmx::MDLogger& mdlog);
+
+/*! \brief Checks what our hardware options are based on how Gromacs was compiled
+ *  and user-set options
+ *
+ *  \param[in]      mdlog                     Logger
+ *  \param[in, out] hw_opt                    Hardware-related and threading options
+ *  \param[in]      isSimulationMasterRank
+ *  \param[in]      nPmeRanks                 Number of PME ranks
+ *  \param[in]      inputrec                  The input record, should point to a valid object when \p isSimulationMasterRank = true
+ *  */
+void checkAndUpdateHardwareOptions(const gmx::MDLogger& mdlog,
+                                   gmx_hw_opt_t*        hw_opt,
+                                   bool                 isSimulationMasterRank,
+                                   int                  nPmeRanks,
+                                   const t_inputrec*    inputrec);
+
+/*! \brief Check, and if necessary update, the number of OpenMP threads requested
+ *
+ * Should be called when we know the MPI rank count and PME run mode.
+ */
+void checkAndUpdateRequestedNumOpenmpThreads(gmx_hw_opt_t*         hw_opt,
+                                             const gmx_hw_info_t&  hwinfo,
+                                             const t_commrec*      cr,
+                                             const gmx_multisim_t* ms,
+                                             int                   numRanksOnThisNode,
+                                             PmeRunMode            pmeRunMode,
+                                             const gmx_mtop_t&     mtop,
+                                             const t_inputrec&     inputrec);
+
+namespace gmx
+{
+
+/*! \brief Warns for oversubscribing the hardware threads, when that is the case
+ */
+void checkHardwareOversubscription(int                             numThreadsOnThisRank,
+                                   int                             rank,
+                                   const HardwareTopology&         hwTop,
+                                   const PhysicalNodeCommunicator& comm,
+                                   const MDLogger&                 mdlog);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/taskassignment/taskassignment.h b/src/include/gromacs/taskassignment/taskassignment.h
new file mode 100644 (file)
index 0000000..1267de1
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \defgroup module_taskassignment Assigning simulation tasks to hardware (taskassignment)
+ * \ingroup group_mdrun
+ * \brief Provides code that manages assignment of simulation tasks to hardware.
+ */
+/*! \libinternal
+ * \file
+ * \brief Declares high-level functionality for managing assigning
+ * tasks on ranks of a node to hardware on that node, and the factory
+ * function to build the correct flavours of gmx::INodeTaskAssigner
+ * required to implement the user's requirements.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_taskassignment
+ * \inlibraryapi
+ */
+#ifndef GMX_TASKASSIGNMENT_TASKASSIGNMENT_H
+#define GMX_TASKASSIGNMENT_TASKASSIGNMENT_H
+
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxmpi.h"
+
+struct DeviceInformation;
+struct gmx_hw_info_t;
+struct t_commrec;
+
+enum class PmeRunMode;
+
+namespace gmx
+{
+
+enum class TaskTarget;
+class MDLogger;
+class PhysicalNodeCommunicator;
+
+/*! \brief Types of compute tasks that can be run on a GPU.
+ *
+ * These names refer to existing practice in GROMACS, which is not
+ * strictly accurate. */
+enum class GpuTask : int
+{
+    //! Short-ranged interactions.
+    Nonbonded,
+    //! Long-ranged interactions.
+    Pme,
+    //! Number of possible tasks.
+    Count
+};
+
+/*! \libinternal
+ * \brief Specifies the GPU deviceID_ available for task_ to use. */
+struct GpuTaskMapping
+{
+    //! The type of this GPU task.
+    GpuTask task_;
+    //! Device ID on this node to which this GPU task is mapped.
+    int deviceId_;
+};
+
+//! Container of GPU tasks on a rank, specifying the task type and GPU device ID, e.g. potentially ready for consumption by the modules on that rank.
+using GpuTaskAssignment = std::vector<GpuTaskMapping>;
+
+class GpuTaskAssignments;
+
+/*! \libinternal
+ * \brief Builder for the GpuTaskAssignments for all ranks on this
+ * node.
+ *
+ * This will coordinate the final stages of task assignment and
+ * reporting, and build the GpuTaskAssignments object used to
+ * configure the modules that might run tasks on GPUs.
+ *
+ * Communicates between ranks on a node to coordinate task assignment
+ * between them onto available hardware, e.g. accelerators.
+ *
+ * \todo Later, this might become a loop over all registered modules
+ * relevant to the mdp inputs, to find those that have such tasks.
+ *
+ * \todo Later we might need the concept of computeTasksOnThisRank,
+ * from which we construct gpuTasksOnThisRank.
+ *
+ * Currently the DD code assigns duty to ranks that can
+ * include PP work that currently can be executed on a single
+ * GPU, if present and compatible.  This has to be coordinated
+ * across PP ranks on a node, with possible multiple devices
+ * or sharing devices on a node, either from the user
+ * selection, or automatically. */
+class GpuTaskAssignmentsBuilder
+{
+public:
+    //! Constructor
+    GpuTaskAssignmentsBuilder();
+
+    /*! \brief Builds a GpuTaskAssignments
+     *
+     * This method reconciles
+     *
+     *   - user mdrun command-line options,
+     *   - the results of hardware detection
+     *   - the duty assigned by the DD setup,
+     *   - the requested simulation modules, and
+     *   - the possible existence of multi-simulations
+     *
+     * to assign the GPUs on each physical node to the tasks on
+     * the ranks of that node. It throws InconsistentInputError
+     * when a/the useful GPU task assignment is not possible.
+     *
+     * \param[in]  availableDevices       The compatible devices that the user permitted us to use.
+     * \param[in]  userGpuTaskAssignment  The user-specified assignment of GPU tasks to device IDs.
+     * \param[in]  hardwareInfo           The detected hardware
+     * \param[in]  gromacsWorldComm       MPI communicator for all ranks in the current GROMACS run
+     * \param[in]  physicalNodeComm       Communication object for this physical node.
+     * \param[in]  nonbondedTarget        The user's choice for mdrun -nb for where to assign
+     *                                    short-ranged nonbonded interaction tasks.
+     * \param[in]  pmeTarget              The user's choice for mdrun -pme for where to assign
+     *                                    long-ranged PME nonbonded interaction tasks.
+     * \param[in]  bondedTarget           The user's choice for mdrun -bonded for where to assign tasks.
+     * \param[in]  updateTarget           The user's choice for mdrun -update for where to assign tasks.
+     * \param[in]  useGpuForNonbonded     Whether GPUs will be used for nonbonded interactions.
+     * \param[in]  useGpuForPme           Whether GPUs will be used for PME interactions.
+     * \param[in]  rankHasPpTask          Whether this rank has a PP task
+     * \param[in]  rankHasPmeTask         Whether this rank has a PME task
+     *
+     * \throws   std::bad_alloc          If out of memory.
+     *           InconsistentInputError  If user and/or detected inputs are inconsistent.
+     */
+    static GpuTaskAssignments build(ArrayRef<const int>             availableDevices,
+                                    ArrayRef<const int>             userGpuTaskAssignment,
+                                    const gmx_hw_info_t&            hardwareInfo,
+                                    MPI_Comm                        gromacsWorldComm,
+                                    const PhysicalNodeCommunicator& physicalNodeComm,
+                                    TaskTarget                      nonbondedTarget,
+                                    TaskTarget                      pmeTarget,
+                                    TaskTarget                      bondedTarget,
+                                    TaskTarget                      updateTarget,
+                                    bool                            useGpuForNonbonded,
+                                    bool                            useGpuForPme,
+                                    bool                            rankHasPpTask,
+                                    bool                            rankHasPmeTask);
+};
+
+/*! \libinternal
+ * \brief Contains the GPU task assignment for all ranks on this
+ * physical node.
+ *
+ * This can be used to configure the modules that might run tasks on
+ * GPUs.
+ *
+ * This assignment is made by a GpuTaskAssignmentsBuilder object. */
+class GpuTaskAssignments
+{
+public:
+    //! Public move constructor to use with the builder
+    GpuTaskAssignments(GpuTaskAssignments&& source) noexcept = default;
+
+private:
+    // Let the builder handle construction
+    friend class GpuTaskAssignmentsBuilder;
+    //! Private constructor so only the builder can construct
+    GpuTaskAssignments(const gmx_hw_info_t& hardwareInfo);
+    /*! \brief Information about hardware on this physical node
+     *
+     * The lifetime of the object referred to must exceed that
+     * of this object. */
+    const gmx_hw_info_t& hardwareInfo_;
+    //! The GPU task assignment for all ranks on this node
+    std::vector<GpuTaskAssignment> assignmentForAllRanksOnThisNode_;
+    /*! \brief The index of this rank within those on this node.
+     *
+     * This is useful for indexing into \c
+     * assignmentForAllRanksOnThisNode_. */
+    index indexOfThisRank_ = -1;
+    //! Number of GPU tasks on this node.
+    size_t numGpuTasksOnThisNode_ = 0;
+    //! Number of ranks on this physical node.
+    size_t numRanksOnThisNode_ = 0;
+
+    //! Vector of device IDs assigned to this node
+    std::vector<int> deviceIdsAssigned_;
+
+public:
+    /*! \brief Log a report on how GPUs are being used on
+     * the ranks of the physical node of rank 0 of the simulation.
+     *
+     * \todo It could be useful to report also whether any nodes differed,
+     * and in what way.
+     *
+     * \param[in]  mdlog           Logging object.
+     * \param[in]  printHostName   Print the hostname in the usage information.
+     * \param[in]  useGpuForBonded Whether GPU PP tasks will do bonded work on the GPU.
+     * \param[in]  pmeRunMode      Describes the execution of PME tasks.
+     * \param[in]  useGpuForUpdate Whether the update is offloaded on the GPU.
+     *
+     * \throws     std::bad_alloc if out of memory
+     */
+    void reportGpuUsage(const MDLogger& mdlog,
+                        bool            printHostName,
+                        bool            useGpuForBonded,
+                        PmeRunMode      pmeRunMode,
+                        bool            useGpuForUpdate);
+
+    /*! \brief Logs to \c mdlog information that may help a user
+     * learn how to let mdrun make a task assignment that runs
+     * faster.
+     *
+     * \param[in]  mdlog                         Logging object.
+     * \param[in]  numAvailableDevicesOnThisNode The number of compatible devices on this node
+     *                                           that the user permitted us to use.
+     * */
+    void logPerformanceHints(const MDLogger& mdlog, size_t numAvailableDevicesOnThisNode);
+    /*! \brief Return handle to the initialized GPU to use in the this rank.
+     *
+     * \param[out] deviceId Index of the assigned device.
+     *
+     * \returns Device information on the selected devicce. Returns nullptr if no GPU task
+     *          is assigned to this rank.
+     */
+    DeviceInformation* initDevice(int* deviceId) const;
+    //! Return whether this rank has a PME task running on a GPU
+    bool thisRankHasPmeGpuTask() const;
+    //! Return whether this rank has any task running on a GPU
+    bool thisRankHasAnyGpuTask() const;
+    //! Get the list of unique devices that have been assigned tasks on this physical node
+    std::vector<int> deviceIdsAssigned() { return deviceIdsAssigned_; }
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/taskassignment/usergpuids.h b/src/include/gromacs/taskassignment/usergpuids.h
new file mode 100644 (file)
index 0000000..c6c89aa
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \defgroup module_taskassignment Assigning simulation tasks to hardware (taskassignment)
+ * \ingroup group_mdrun
+ * \brief Provides code that manages assignment of simulation tasks to hardware.
+ */
+/*! \libinternal
+ * \file
+ * \brief Declares routines for handling user-specified GPU IDs.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \ingroup module_taskassignment
+ * \inlibraryapi
+ */
+#ifndef GMX_TASKASSIGNMENT_USERGPUIDS_H
+#define GMX_TASKASSIGNMENT_USERGPUIDS_H
+
+#include <cstddef>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+
+struct DeviceInformation;
+
+namespace gmx
+{
+
+/*! \brief Parse a GPU ID specifier string into a container describing device IDs exposed to the run.
+ *
+ * \param[in]   gpuIdString  String like "013" or "0,1,3" typically
+ *                           supplied by the user to mdrun -gpu_id.
+ *                           Must contain only unique decimal digits, or only decimal
+ *                           digits separated by comma delimiters. A terminal
+ *                           comma is accceptable (and required to specify a
+ *                           single ID that is larger than 9).
+ *
+ * \returns  A vector of unique GPU IDs.
+ *
+ * \throws   std::bad_alloc     If out of memory.
+ *           InvalidInputError  If an invalid character is found (ie not a digit or ',') or if
+ *                              identifiers are duplicated in the specifier list.
+ */
+std::vector<int> parseUserGpuIdString(const std::string& gpuIdString);
+
+/*! \brief Implement GPU ID selection by returning the available GPU
+ * IDs on this physical node that are compatible.
+ *
+ * If the string supplied by the user is empty, then return the IDs of
+ * all compatible GPUs on this physical node. Otherwise, check the
+ * user specified compatible GPUs and return their IDs.
+ *
+ * \param[in]  deviceInfoList               Information on the GPUs on this physical node.
+ * \param[in]  devicesSelectedByUserString  String like "013" or "0,1,3" typically
+ *                                          supplied by the user to mdrun -gpu_id.
+ *                                          Must contain only unique decimal digits, or only decimal
+ *                                          digits separated by comma delimiters. A terminal
+ *                                          comma is accceptable (and required to specify a
+ *                                          single ID that is larger than 9).
+ *
+ * \returns  A vector of unique compatible GPU IDs on this physical node.
+ *
+ * \throws   std::bad_alloc     If out of memory.
+ *           InvalidInputError  If an invalid character is found (ie not a digit or ',') or if
+ *                              identifiers are duplicated in the specifier list.
+ *           InvalidInputError  If devicesSelectedByUserString specifies IDs of the devices that are
+ *                              not compatible.
+ */
+std::vector<int> makeListOfAvailableDevices(gmx::ArrayRef<const std::unique_ptr<DeviceInformation>> deviceInfoList,
+                                            const std::string& devicesSelectedByUserString);
+
+/*! \brief Parse a GPU ID specifier string into a container describing device ID to task mapping.
+ *
+ * \param[in]   gpuIdString  String like "0011" or "0,0,1,1" typically
+ *                           supplied by the user to mdrun -gputasks.
+ *                           Must contain only decimal digits, or only decimal
+ *                           digits separated by comma delimiters. A terminal
+ *                           comma is accceptable (and required to specify a
+ *                           single ID that is larger than 9).
+ *
+ * \returns  A vector of GPU IDs.
+ *
+ * \throws   std::bad_alloc     If out of memory.
+ *           InvalidInputError  If an invalid character is found (ie not a digit or ',').
+ */
+std::vector<int> parseUserTaskAssignmentString(const std::string& gpuIdString);
+
+
+/*! \brief Make a vector containing \c numGpuTasks IDs of the IDs found in \c compatibleGpus.
+ *
+ * \throws  std::bad_alloc          If out of memory
+ *
+ * \returns A sorted vector of IDs of compatible vectors, whose
+ * length matches that of the number of GPU tasks required.
+ */
+std::vector<int> makeGpuIds(ArrayRef<const int> compatibleGpus, size_t numGpuTasks);
+
+/*! \brief Convert a container of GPU deviced IDs to a string that
+ * can be used by gmx tune_pme as input to mdrun -gputasks.
+ *
+ * Produce a valid input for mdrun -gputasks that refers to the device
+ * IDs in \c gpuIds but produces a mapping for \c
+ * totalNumberOfTasks tasks. Note that gmx tune_pme does not
+ * currently support filling mdrun -gputasks.
+ *
+ * \param[in]   gpuIds              Container of device IDs
+ * \param[in]   totalNumberOfTasks  Total number of tasks for the output mapping produced by the returned string.
+ *
+ * \returns  A string that is suitable to pass to mdrun -gputasks.
+ *
+ * \throws   std::bad_alloc     If out of memory.
+ */
+std::string makeGpuIdString(const std::vector<int>& gpuIds, int totalNumberOfTasks);
+
+/*! \brief Check that all user-selected GPUs are compatible.
+ *
+ * Given the \c gpuIds and \c hardwareInfo, throw if
+ * any selected GPUs is not compatible.
+ *
+ * The error is given with a suitable descriptive message, which will
+ * have context if this check is done after the hardware detection
+ * results have been reported to the user. However, note that only the
+ * GPUs detected on the master rank are reported, because of the
+ * existing limitations of that reporting.
+ *
+ * \todo Note that the selected GPUs can be different on each rank,
+ * and the IDs of compatible GPUs can be different on each node, so
+ * this routine ought to do communication to determine whether all
+ * ranks are able to proceed. Currently this relies on the MPI runtime
+ * to kill the other processes because GROMACS lacks the appropriate
+ * infrastructure to do a good job of coordinating error messages and
+ * behaviour across MPMD ranks and multiple simulations.
+ *
+ * \param[in]   deviceInfoList  Information on the GPUs on this physical node.
+ * \param[in]   compatibleGpus  Vector of GPUs that are compatible
+ * \param[in]   gpuIds          The GPU IDs selected by the user.
+ *
+ * \throws  std::bad_alloc          If out of memory
+ *          InconsistentInputError  If the assigned GPUs are not valid
+ */
+void checkUserGpuIds(ArrayRef<const std::unique_ptr<DeviceInformation>> deviceInfoList,
+                     ArrayRef<const int>                                compatibleGpus,
+                     ArrayRef<const int>                                gpuIds);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/timing/cyclecounter.h b/src/include/gromacs/timing/cyclecounter.h
new file mode 100644 (file)
index 0000000..956386a
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2006 David van der Spoel, Erik Lindahl, Berk Hess, University of Groningen.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * High-resolution timestamp or CPU clock cycle counters.
+ *
+ * After reading the current value with gmx_cycles_read() you can add or
+ * subtract these numbers as normal integers of type gmx_cycles_t.
+ *
+ * \inlibraryapi
+ */
+#ifndef GMX_TIMING_CYCLECOUNTER_H
+#define GMX_TIMING_CYCLECOUNTER_H
+
+/*
+ * Define GMX_USE_RDTSCP=1 to use the serializing rdtscp instruction instead of rdtsc.
+ * This is supported on essentially all Intel/AMD hardware still in use, and provides better accuracy.
+ */
+#include "config.h"
+
+#ifdef _MSC_VER
+#    include <intrin.h>
+#endif
+
+#if ((defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__PATHSCALE__) || defined(__PGIC__)) \
+     && (defined(__i386__) || defined(__x86_64__)))
+/* x86 or x86-64 with GCC inline assembly */
+typedef unsigned long long gmx_cycles_t;
+
+#elif ((defined __aarch64__) \
+       && (defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__PATHSCALE__) || defined(__PGIC__)))
+/* 64-bit ARM cycle counters with GCC inline assembly */
+typedef unsigned long long     gmx_cycles_t;
+
+#elif defined(__ARM_ARCH_7A__) && defined(__GNUC__)
+/* Armv7A can provide 64-bit cycles by returning two registers */
+typedef unsigned long long     gmx_cycles_t;
+
+#elif defined(_MSC_VER)
+#    include <windows.h>
+typedef __int64              gmx_cycles_t;
+
+#elif (defined(__hpux) || defined(__HP_cc)) && defined(__ia64)
+/* HP compiler on ia64 */
+#    include <machine/sys/inline.h>
+typedef unsigned long      gmx_cycles_t;
+
+#elif (defined(__INTEL_COMPILER) || defined(__ECC)) && defined(__ia64__)
+/* Intel compiler on ia64 */
+#    include <ia64intrin.h>
+typedef unsigned long          gmx_cycles_t;
+
+#elif defined(__GNUC__) && defined(__ia64__)
+/* ia64 with GCC inline assembly */
+typedef unsigned long          gmx_cycles_t;
+
+#elif ((defined(__hppa__) || defined(__hppa)) && defined(__GNUC__))
+/* HP PA-RISC, inline asm with gcc */
+typedef unsigned long          gmx_cycles_t;
+
+#elif ((defined(__hppa__) || defined(__hppa)) && defined(__hpux))
+/* HP PA-RISC, instruction when using HP compiler */
+#    include <machine/inline.h>
+typedef unsigned long      gmx_cycles_t;
+
+#elif defined(__GNUC__) && defined(__s390__)
+/* S390, taken from FFTW who got it from James Treacy */
+typedef unsigned long long     gmx_cycles_t;
+
+#elif defined(__GNUC__) && defined(__alpha__)
+/* gcc inline assembly on alpha CPUs */
+typedef unsigned long          gmx_cycles_t;
+
+#elif defined(__GNUC__) && defined(__sparc_v9__)
+/* gcc inline assembly on sparc v9 */
+typedef unsigned long          gmx_cycles_t;
+
+#elif defined(__DECC) && defined(__alpha)
+/* Digital GEM C compiler on alpha */
+#    include <c_asm.h>
+typedef unsigned long        gmx_cycles_t;
+
+#elif (defined(__sgi) && defined(CLOCK_SGI_CYCLE))
+/* Irix compilers on SGI hardware. Get nanoseconds from struct timespec */
+typedef unsigned long long   gmx_cycles_t;
+
+#elif (defined(__SVR4) && defined(__SUNPRO_CC))
+/* Solaris high-resolution timers */
+typedef hrtime_t           gmx_cycles_t;
+
+#elif defined(__xlC__) && defined(_AIX)
+/* AIX compilers */
+#    include <sys/systemcfg.h>
+#    include <sys/time.h>
+typedef unsigned long long gmx_cycles_t;
+
+#elif ((defined(__GNUC__) || defined(__IBM_GCC_ASM) || defined(__IBM_STDCPP_ASM)) \
+       && (defined(__powerpc__) || defined(__ppc__)))
+/* PowerPC using gcc inline assembly (also works on xlc>=7.0 with -qasm=gcc) */
+typedef unsigned long long     gmx_cycles_t;
+
+#elif (defined(__MWERKS__) && (defined(MAC) || defined(macintosh)))
+/* Metrowerks on macintosh */
+typedef unsigned long long     gmx_cycles_t;
+
+#elif defined(__sun) && defined(__sparcv9)
+
+typedef unsigned long gmx_cycles_t;
+
+#else
+/*! \brief Integer-like datatype for cycle counter values
+ *
+ *  Depending on your system this will usually be something like long long,
+ *  or a special cycle datatype from the system header files. It is NOT
+ *  necessarily real processor cycles - many systems count in nanoseconds
+ *  or a special external time register at fixed frequency (not the CPU freq.)
+ *
+ *  You can subtract or add gmx_cycle_t types just as normal integers, and if
+ *  you run the calibration routine you can also multiply it with a factor to
+ *  translate the cycle data to seconds.
+ */
+typedef long gmx_cycles_t;
+
+#endif
+
+/*! \brief Read CPU cycle counter
+ *
+ *  This routine returns an abstract datatype containing a
+ *  cycle counter timestamp.
+ *
+ *  \return Opaque data corresponding to a cycle reading.
+ *
+ *  Please note that on most systems it takes several cycles
+ *  to read and return the cycle counters. If you are measuring
+ *  small intervals, you can compensate for this time by calling
+ *  the routine twice and calculating what the difference is.
+ *  Subtract this from your other measurements to get an accurate result.
+ *
+ *  Use gmx_cycles_difference() to get a real number corresponding to
+ *  the difference between two gmx_cycles_t values returned from this
+ *  routine.
+ */
+#if ((defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__PATHSCALE__) || defined(__PGIC__)) \
+     && (defined(__i386__) || defined(__x86_64__)) && !defined(_CRAYC))
+static __inline__ gmx_cycles_t gmx_cycles_read()
+{
+    /* x86 with GCC inline assembly - pentium TSC register */
+    unsigned low, high;
+
+#    if GMX_USE_RDTSCP
+    __asm__ __volatile__("rdtscp" : "=a"(low), "=d"(high)::"ecx");
+#    else
+    __asm__ __volatile__("rdtsc" : "=a"(low), "=d"(high));
+#    endif
+    const gmx_cycles_t c_low  = low;
+    const gmx_cycles_t c_high = high;
+    return c_low | c_high << 32;
+}
+#elif ((defined __aarch64__) \
+       && (defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__PATHSCALE__) || defined(__PGIC__)))
+static __inline__ gmx_cycles_t gmx_cycles_read(void)
+{
+    /* 64-bit ARM cycle counters with GCC inline assembly */
+    gmx_cycles_t cycle;
+    __asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(cycle));
+
+    return cycle;
+}
+#elif defined(__ARM_ARCH_7A__) && defined(__GNUC__)
+static __inline__ gmx_cycles_t gmx_cycles_read(void)
+{
+    unsigned int cycles_lo, cycles_hi;
+    asm volatile("mrrc p15, 1, %0, %1, c14" : "=r"(cycles_lo), "=r"(cycles_hi));
+    return ((gmx_cycles_t)cycles_lo) | (((gmx_cycles_t)cycles_hi) << 32);
+}
+#elif defined(_MSC_VER)
+static __inline gmx_cycles_t gmx_cycles_read(void)
+{
+#    ifdef _M_ARM
+    /* Windows on 64-bit ARM */
+    return __rdpmccntr64();
+#    else
+    /* x86 */
+#        if GMX_USE_RDTSCP
+    unsigned int ui;
+    return __rdtscp(&ui);
+#        else
+    return __rdtsc();
+#        endif
+#    endif
+}
+#elif (defined(__hpux) || defined(__HP_cc)) && defined(__ia64)
+static inline gmx_cycles_t gmx_cycles_read(void)
+{
+    /* HP compiler on ia64 */
+    gmx_cycles_t ret;
+    ret = _Asm_mov_from_ar(_AREG_ITC);
+    return ret;
+}
+#elif (defined(__INTEL_COMPILER) && defined(__ia64__))
+static __inline__ gmx_cycles_t gmx_cycles_read(void)
+{
+    /* Intel compiler on ia64 */
+    return __getReg(_IA64_REG_AR_ITC);
+}
+#elif defined(__GNUC__) && defined(__ia64__)
+static __inline__ gmx_cycles_t gmx_cycles_read(void)
+{
+    /* ia64 with GCC inline assembly */
+    gmx_cycles_t ret;
+    __asm__ __volatile__("mov %0=ar.itc" : "=r"(ret));
+    return ret;
+}
+#elif ((defined(__hppa__) || defined(__hppa)) && defined(__GNUC__))
+static __inline__ gmx_cycles_t gmx_cycles_read(void)
+{
+    /* HP PA-RISC, inline asm with gcc */
+    gmx_cycles_t ret;
+    __asm__ __volatile__("mfctl 16, %0" : "=r"(ret));
+    /* no input, nothing else clobbered */
+    return ret;
+}
+#elif ((defined(__hppa__) || defined(__hppa)) && defined(__hpux))
+static inline gmx_cycles_t gmx_cycles_read(void)
+{
+    /* HP PA-RISC, instruction when using HP compiler */
+    gmx_cycles_t ret;
+    _MFCTL(16, ret);
+    return ret;
+}
+#elif defined(__GNUC__) && defined(__s390__)
+static __inline__ gmx_cycles_t gmx_cycles_read(void)
+{
+    /* S390, taken from FFTW who got it from James Treacy */
+    gmx_cycles_t cycle;
+    __asm__("stck 0(%0)" : : "a"(&(cycle)) : "memory", "cc");
+    return cycle;
+}
+#elif defined(__GNUC__) && defined(__alpha__)
+static __inline__ gmx_cycles_t gmx_cycles_read(void)
+{
+    /* gcc inline assembly on alpha CPUs */
+    unsigned long cycle;
+    __asm__ __volatile__("rpcc %0" : "=r"(cycle));
+    return (cycle & 0xFFFFFFFF);
+}
+#elif defined(__GNUC__) && defined(__sparc_v9__)
+static __inline__ gmx_cycles_t gmx_cycles_read(void)
+{
+    /* gcc inline assembly on sparc v9 */
+    unsigned long ret;
+    __asm__("rd %%tick, %0" : "=r"(ret));
+    return ret;
+}
+#elif defined(__DECC) && defined(__alpha)
+static __inline gmx_cycles_t gmx_cycles_read(void)
+{
+    /* Digital GEM C compiler on alpha */
+    unsigned long cycle;
+    cycle = asm("rpcc %v0");
+    return (cycle & 0xFFFFFFFF);
+}
+#elif (defined(__sgi) && defined(CLOCK_SGI_CYCLE))
+static __inline gmx_cycles_t gmx_cycles_read(void)
+{
+    /* Irix compilers on SGI hardware */
+    struct timespec t;
+    clock_gettime(CLOCK_SGI_CYCLE, &t);
+    /* Return the number of nanoseconds, so we can subtract/add */
+    return ((unsigned long long)t.tv_sec) * 1000000000 + (unsigned long long)t.tv_nsec;
+}
+#elif (defined(__SVR4) && defined(__SUNPRO_CC))
+static inline gmx_cycles_t gmx_cycles_read(void)
+{
+    /* Solaris high-resolution timers */
+    return gethrtime();
+}
+#elif defined(__xlC__) && defined(_AIX)
+static inline gmx_cycles_t gmx_cycles_read(void)
+{
+    /* AIX compilers. Inline the calculation instead of using library functions */
+    timebasestruct_t t1;
+    read_real_time(&t1, TIMEBASE_SZ);
+    /* POWER returns real time (seconds + nanoseconds),
+     * POWER_PC returns high/low 32 bits of a counter.
+     */
+    if (t1.flag == RTC_POWER_PC)
+    {
+        return ((gmx_cycles_t)t1.tb_high) << 32 | (gmx_cycles_t)t1.tb_low;
+    }
+    else
+    {
+        return ((gmx_cycles_t)t1.tb_high) * 1000000000 + (gmx_cycles_t)t1.tb_low;
+    }
+}
+#elif ((defined(__GNUC__) || defined(__IBM_GCC_ASM) || defined(__IBM_STDCPP_ASM)) \
+       && (defined(__powerpc__) || defined(__ppc__)))
+static __inline__ gmx_cycles_t gmx_cycles_read(void)
+{
+    /* PowerPC using gcc inline assembly (and xlC>=7.0 with -qasm=gcc, and clang) */
+    unsigned long low, high1, high2;
+    do
+    {
+        // clang 3.7 incorrectly warns that mftb* are
+        // deprecated. That's not correct - see
+        // https://llvm.org/bugs/show_bug.cgi?id=23680.
+        __asm__ __volatile__("mftbu %0" : "=r"(high1) :);
+        __asm__ __volatile__("mftb %0" : "=r"(low) :);
+        __asm__ __volatile__("mftbu %0" : "=r"(high2) :);
+    } while (high1 != high2);
+
+    return (((gmx_cycles_t)high2) << 32) | (gmx_cycles_t)low;
+}
+#elif (defined(__MWERKS__) && (defined(MAC) || defined(macintosh)))
+static __inline__ gmx_cycles_t gmx_cycles_read(void)
+{
+    /* Metrowerks on macintosh */
+    unsigned int long low, high1, high2;
+    do
+    {
+        __asm__ __volatile__("mftbu %0" : "=r"(high1) :);
+        __asm__ __volatile__("mftb %0" : "=r"(low) :);
+        __asm__ __volatile__("mftbu %0" : "=r"(high2) :);
+    } while (high1 != high2);
+
+    return (((gmx_cycles_t)high2) << 32) | (gmx_cycles_t)low;
+}
+#elif defined(__sun) && defined(__sparcv9)
+
+static __inline__ gmx_cycles_t gmx_cycles_read(void)
+{
+    gmx_cycles_t ret;
+    __asm__ __volatile__("rd %%tick, %0" : "=r"(ret));
+    return ret;
+}
+
+#elif defined(_CRAYC)
+#    include <intrinsics.h>
+
+static __inline gmx_cycles_t gmx_cycles_read(void)
+{
+    return _rtc();
+}
+#else
+static gmx_cycles_t gmx_cycles_read(void)
+{
+    return 0;
+}
+#endif
+
+
+/*! \brief Check if high-resolution cycle counters are available
+ *
+ *  Not all architectures provide any way to read timestep counters
+ *  in the CPU, and on some it is broken. Although we refer to it
+ *  as cycle counters, it is not necessarily given in units of
+ *  cycles.
+ *
+ *  If you notice that system is missing, implement support for it,
+ *  find out how to detect the system during preprocessing, and send us a
+ *  patch.
+ *
+ *  \return 1 if cycle counters are available, 0 if not.
+ *
+ * \note This functions not need to be in the header for performance
+ *       reasons, but it is very important that we get exactly the
+ *       same detection as for gmx_cycles_read() routines. If you
+ *       compile the library with one compiler, and then use a different
+ *       one when later linking to the library it might happen that the
+ *       library supports cyclecounters but not the headers, or vice versa.
+ */
+#if ((defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__PATHSCALE__) \
+      || defined(__PGIC__) || defined(_CRAYC))                                 \
+     && (defined(__i386__) || defined(__x86_64__)))
+static __inline__ bool gmx_cycles_have_counter()
+{
+    /* x86 or x86-64 with GCC inline assembly - pentium TSC register */
+    return true;
+}
+#elif ((defined __aarch64__) \
+       && (defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__PATHSCALE__) || defined(__PGIC__)))
+static __inline bool gmx_cycles_have_counter(void)
+{
+    /* 64-bit ARM cycle counters with GCC inline assembly */
+    return 1;
+}
+#elif defined(__ARM_ARCH_7A__) && defined(__GNUC__)
+static __inline bool gmx_cycles_have_counter(void)
+{
+    /* Armv7A can provide 64-bit cycles by returning two registers. However, it will not work unless
+     * the performance registers have been made available from user space by a kernel module -
+     * otherwise it returns 0.
+     */
+    gmx_cycles_t c0, c1;
+
+    c0 = gmx_cycles_read();
+    c1 = gmx_cycles_read();
+
+    /* if both counters return 0, support is not present */
+    return (c0 != 0 || c1 != 0);
+}
+#elif (defined(_MSC_VER))
+static __inline bool gmx_cycles_have_counter(void)
+{
+    return 1;
+}
+#elif (defined(__hpux) || defined(__HP_cc)) && defined(__ia64)
+static inline bool gmx_cycles_have_counter(void)
+{
+    /* HP compiler on ia64, use special instruction to read ITC */
+    return 1;
+}
+#elif (defined(__INTEL_COMPILER) || defined(__ECC)) && defined(__ia64__)
+static __inline__ bool gmx_cycles_have_counter(void)
+{
+    /* Intel compiler on ia64, use special instruction to read ITC */
+    return 1;
+}
+#elif defined(__GNUC__) && defined(__ia64__)
+static __inline__ bool gmx_cycles_have_counter(void)
+{
+    /* AMD64 with GCC inline assembly - TSC register */
+    return 1;
+}
+#elif ((defined(__hppa__) || defined(__hppa)) && defined(__GNUC__))
+static __inline__ bool gmx_cycles_have_counter(void)
+{
+    /* HP PA-RISC, inline asm with gcc */
+    return 1;
+}
+#elif ((defined(__hppa__) || defined(__hppa)) && defined(__hpux))
+static inline bool gmx_cycles_have_counter(void)
+{
+    /* HP PA-RISC, instruction when using HP compiler */
+    return 1;
+}
+#elif defined(__GNUC__) && defined(__s390__)
+static __inline__ bool gmx_cycles_have_counter(void)
+{
+    /* S390, taken from FFTW who got it from James Treacy */
+    return 1;
+}
+#elif defined(__GNUC__) && defined(__alpha__)
+static __inline__ bool gmx_cycles_have_counter(void)
+{
+    /* gcc inline assembly on alpha CPUs */
+    return 1;
+}
+#elif defined(__GNUC__) && defined(__sparc_v9__)
+static __inline__ bool gmx_cycles_have_counter(void)
+{
+    /* gcc inline assembly on sparc v9 */
+    return 1;
+}
+#elif defined(__DECC) && defined(__alpha)
+static __inline bool gmx_cycles_have_counter(void)
+{
+    /* Digital GEM C compiler on alpha */
+    return 1;
+}
+#elif (defined(__sgi) && defined(CLOCK_SGI_CYCLE))
+static __inline bool gmx_cycles_have_counter(void)
+{
+    /* Irix compilers on SGI hardware */
+    return 1;
+}
+#elif (defined(__SVR4) && defined(__SUNPRO_CC))
+static inline bool gmx_cycles_have_counter(void)
+{
+    /* Solaris high-resolution timers */
+    return 1;
+}
+#elif defined(__xlC__) && defined(_AIX)
+static inline bool gmx_cycles_have_counter(void)
+{
+    /* AIX compilers */
+    return 1;
+}
+#elif ((defined(__GNUC__) || defined(__IBM_GCC_ASM) || defined(__IBM_STDCPP_ASM)) \
+       && (defined(__powerpc__) || defined(__ppc__)))
+static __inline__ bool gmx_cycles_have_counter(void)
+{
+    /* PowerPC using gcc inline assembly (and xlc>=7.0 with -qasm=gcc) */
+    return 1;
+}
+#elif (defined(__MWERKS__) && (defined(MAC) || defined(macintosh)))
+static __inline__ bool gmx_cycles_have_counter(void)
+{
+    /* Metrowerks on macintosh */
+    return 1;
+}
+#elif defined(__sun) && defined(__sparcv9)
+
+static __inline__ bool gmx_cycles_have_counter(void)
+{
+    /* Solaris on SPARC*/
+    return 1;
+}
+#else
+static bool gmx_cycles_have_counter(void)
+{
+    /* No cycle counter that we know of on this system */
+    return 0;
+}
+#endif
+
+
+/*! \brief Calculate number of seconds per cycle tick on host
+ *
+ *  This routine runs a timer loop to calibrate the number of
+ *  seconds per the units returned fro gmx_cycles_read().
+ *
+ *  \param  sampletime Minimum real sample time. It takes some trial-and-error
+ *          to find the correct delay loop size, so the total runtime of
+ *          this routine is about twice this time.
+ *  \return Number of seconds per cycle unit. If it is not possible to
+ *          calculate on this system (for whatever reason) the return value
+ *          will be -1, so check that it is positive before using it.
+ */
+double gmx_cycles_calibrate(double sampletime);
+
+#endif
diff --git a/src/include/gromacs/timing/gpu_timing.h b/src/include/gromacs/timing/gpu_timing.h
new file mode 100644 (file)
index 0000000..24768bc
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *  \brief Declares data types for GPU timing
+ *
+ *  \author Szilard Pall <pall.szilard@gmail.com>
+ *  \author Mark Abraham <mark.j.abraham@gmail.com>
+ *  \inlibraryapi
+ */
+
+#ifndef GMX_TIMING_GPU_TIMING_H
+#define GMX_TIMING_GPU_TIMING_H
+
+#include "gromacs/utility/enumerationhelpers.h"
+
+/*! \internal \brief GPU kernel time and call count. */
+struct gmx_kernel_timing_data_t
+{
+    double t; /**< Accumulated lapsed time */
+    int    c; /**< Number of calls corresponding to the elapsed time */
+};
+
+/*! \internal \brief
+ * PME GPU stages timing events indices, corresponding to the string in PMEStageNames in wallcycle.cpp.
+ */
+enum class PmeStage : int
+{
+    Spline = 0,
+    Spread,
+    SplineAndSpread,
+    FftTransformR2C,
+    Solve,
+    FftTransformC2R,
+    Gather,
+    Count /* not a stage ID but a static array size */
+};
+
+/*! \internal \brief GPU timings for PME. */
+struct gmx_wallclock_gpu_pme_t
+{
+    /* A separate PME structure to avoid refactoring the NB code for gmx_wallclock_gpu_t later
+     * TODO: devise a better GPU timing data structuring.
+     */
+    /*! \brief Array of PME GPU timing data. */
+    gmx::EnumerationArray<PmeStage, gmx_kernel_timing_data_t> timing;
+};
+
+/*! \internal \brief GPU NB timings for kernels and H2d/D2H transfers. */
+struct gmx_wallclock_gpu_nbnxn_t
+{
+    gmx_kernel_timing_data_t ktime[2][2]; /**< table containing the timings of the four
+                                                   versions of the nonbonded kernels: force-only,
+                                                   force+energy, force+pruning, and force+energy+pruning */
+    gmx_kernel_timing_data_t pruneTime; /**< table containing the timings of the 1st pass prune-only kernels */
+    gmx_kernel_timing_data_t dynamicPruneTime; /**< table containing the timings of dynamic prune-only kernels */
+    double                   nb_h2d_t; /**< host to device transfer time in nb calculation  */
+    double                   nb_d2h_t; /**< device to host transfer time in nb calculation */
+    int                      nb_c;     /**< total call count of the nonbonded gpu operations */
+    double                   pl_h2d_t; /**< pair search step host to device transfer time */
+    int                      pl_h2d_c; /**< pair search step  host to device transfer call count */
+};
+
+#endif
diff --git a/src/include/gromacs/timing/wallcycle.h b/src/include/gromacs/timing/wallcycle.h
new file mode 100644 (file)
index 0000000..ca25660
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TIMING_WALLCYCLE_H
+#define GMX_TIMING_WALLCYCLE_H
+
+/* NOTE: None of the routines here are safe to call within an OpenMP
+ * region */
+
+#include <stdio.h>
+
+#include <array>
+#include <memory>
+#include <vector>
+
+#include "gromacs/timing/cyclecounter.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+#ifndef DEBUG_WCYCLE
+/*! \brief Enables consistency checking for the counters.
+ *
+ * If the macro is set to 1, code checks if you stop a counter different from the last
+ * one that was opened and if you do nest too deep.
+ */
+#    define DEBUG_WCYCLE 0
+#endif
+
+struct t_commrec;
+
+#ifndef DEBUG_WCYCLE
+/*! \brief Enables consistency checking for the counters.
+ *
+ * If the macro is set to 1, code checks if you stop a counter different from the last
+ * one that was opened and if you do nest too deep.
+ */
+#    define DEBUG_WCYCLE 0
+#endif
+
+enum class WallCycleCounter : int
+{
+    Run,
+    Step,
+    PpDuringPme,
+    Domdec,
+    DDCommLoad,
+    DDCommBound,
+    VsiteConstr,
+    PpPmeSendX,
+    NS,
+    LaunchGpu,
+    MoveX,
+    Force,
+    MoveF,
+    PmeMesh,
+    PmeRedistXF,
+    PmeSpread,
+    PmeGather,
+    PmeFft,
+    PmeFftComm,
+    LJPme,
+    PmeSolve,
+    PmeWaitComm,
+    PpPmeWaitRecvF,
+    WaitGpuPmeSpread,
+    PmeFftMixedMode,
+    PmeSolveMixedMode,
+    WaitGpuPmeGather,
+    WaitGpuBonded,
+    PmeGpuFReduction,
+    WaitGpuNbNL,
+    WaitGpuNbL,
+    WaitGpuStatePropagatorData,
+    NbXFBufOps,
+    VsiteSpread,
+    PullPot,
+    Awh,
+    Traj,
+    Update,
+    Constr,
+    MoveE,
+    Rot,
+    RotAdd,
+    Swap,
+    Imd,
+    Test,
+    Count
+};
+
+enum class WallCycleSubCounter : int
+{
+    DDRedist,
+    DDGrid,
+    DDSetupComm,
+    DDMakeTop,
+    DDMakeConstr,
+    DDTopOther,
+    DDGpu,
+    NBSGridLocal,
+    NBSGridNonLocal,
+    NBSSearchLocal,
+    NBSSearchNonLocal,
+    Listed,
+    ListedFep,
+    Restraints,
+    ListedBufOps,
+    NonbondedPruning,
+    NonbondedKernel,
+    NonbondedClear,
+    NonbondedFep,
+    NonbondedFepReduction,
+    LaunchGpuNonBonded,
+    LaunchGpuBonded,
+    LaunchGpuPme,
+    LaunchStatePropagatorData,
+    EwaldCorrection,
+    NBXBufOps,
+    NBFBufOps,
+    ClearForceBuffer,
+    LaunchGpuNBXBufOps,
+    LaunchGpuNBFBufOps,
+    LaunchGpuMoveX,
+    LaunchGpuMoveF,
+    LaunchGpuUpdateConstrain,
+    Test,
+    Count
+};
+
+static constexpr int sc_numWallCycleCounters        = static_cast<int>(WallCycleCounter::Count);
+static constexpr int sc_numWallCycleSubCounters     = static_cast<int>(WallCycleSubCounter::Count);
+static constexpr int sc_numWallCycleCountersSquared = sc_numWallCycleCounters * sc_numWallCycleCounters;
+static constexpr bool sc_useCycleSubcounters        = GMX_CYCLE_SUBCOUNTERS;
+
+struct wallcc_t
+{
+    int          n;
+    gmx_cycles_t c;
+    gmx_cycles_t start;
+};
+
+#if DEBUG_WCYCLE
+static constexpr int c_MaxWallCycleDepth = 6;
+#endif
+
+
+struct gmx_wallcycle
+{
+    gmx::EnumerationArray<WallCycleCounter, wallcc_t> wcc;
+    /* did we detect one or more invalid cycle counts */
+    bool haveInvalidCount;
+    /* variables for testing/debugging */
+    bool                  wc_barrier;
+    std::vector<wallcc_t> wcc_all;
+    int                   wc_depth;
+#if DEBUG_WCYCLE
+    std::array<WallCycleCounter, c_MaxWallCycleDepth> counterlist;
+    int                                               count_depth;
+    bool                                              isMasterRank;
+#endif
+    WallCycleCounter                                     ewc_prev;
+    gmx_cycles_t                                         cycle_prev;
+    int64_t                                              reset_counters;
+    const t_commrec*                                     cr;
+    gmx::EnumerationArray<WallCycleSubCounter, wallcc_t> wcsc;
+};
+
+//! Returns if cycle counting is supported
+bool wallcycle_have_counter();
+
+//! Returns the wall cycle structure.
+std::unique_ptr<gmx_wallcycle> wallcycle_init(FILE* fplog, int resetstep, const t_commrec* cr);
+
+//! Adds custom barrier for wallcycle counting.
+void wallcycleBarrier(gmx_wallcycle* wc);
+
+void wallcycle_sub_get(gmx_wallcycle* wc, WallCycleSubCounter ewcs, int* n, double* c);
+/* Returns the cumulative count and sub cycle count for ewcs */
+
+inline void wallcycle_all_start(gmx_wallcycle* wc, WallCycleCounter ewc, gmx_cycles_t cycle)
+{
+    wc->ewc_prev   = ewc;
+    wc->cycle_prev = cycle;
+}
+
+inline void wallcycle_all_stop(gmx_wallcycle* wc, WallCycleCounter ewc, gmx_cycles_t cycle)
+{
+    const int prev    = static_cast<int>(wc->ewc_prev);
+    const int current = static_cast<int>(ewc);
+    wc->wcc_all[prev * sc_numWallCycleCounters + current].n += 1;
+    wc->wcc_all[prev * sc_numWallCycleCounters + current].c += cycle - wc->cycle_prev;
+}
+
+//! Starts the cycle counter (and increases the call count)
+inline void wallcycle_start(gmx_wallcycle* wc, WallCycleCounter ewc)
+{
+    if (wc == nullptr)
+    {
+        return;
+    }
+
+    wallcycleBarrier(wc);
+
+#if DEBUG_WCYCLE
+    debug_start_check(wc, ewc);
+#endif
+    gmx_cycles_t cycle = gmx_cycles_read();
+    wc->wcc[ewc].start = cycle;
+    if (!wc->wcc_all.empty())
+    {
+        wc->wc_depth++;
+        if (ewc == WallCycleCounter::Run)
+        {
+            wallcycle_all_start(wc, ewc, cycle);
+        }
+        else if (wc->wc_depth == 3)
+        {
+            wallcycle_all_stop(wc, ewc, cycle);
+        }
+    }
+}
+
+//! Starts the cycle counter without increasing the call count
+inline void wallcycle_start_nocount(gmx_wallcycle* wc, WallCycleCounter ewc)
+{
+    if (wc == nullptr)
+    {
+        return;
+    }
+    wallcycle_start(wc, ewc);
+    wc->wcc[ewc].n--;
+}
+
+//! Stop the cycle count for ewc , returns the last cycle count
+inline double wallcycle_stop(gmx_wallcycle* wc, WallCycleCounter ewc)
+{
+    gmx_cycles_t cycle, last;
+
+    if (wc == nullptr)
+    {
+        return 0;
+    }
+
+    wallcycleBarrier(wc);
+
+#if DEBUG_WCYCLE
+    debug_stop_check(wc, ewc);
+#endif
+
+    /* When processes or threads migrate between cores, the cycle counting
+     * can get messed up if the cycle counter on different cores are not
+     * synchronized. When this happens we expect both large negative and
+     * positive cycle differences. We can detect negative cycle differences.
+     * Detecting too large positive counts if difficult, since count can be
+     * large, especially for ewcRUN. If we detect a negative count,
+     * we will not print the cycle accounting table.
+     */
+    cycle = gmx_cycles_read();
+    if (cycle >= wc->wcc[ewc].start)
+    {
+        last = cycle - wc->wcc[ewc].start;
+    }
+    else
+    {
+        last                 = 0;
+        wc->haveInvalidCount = true;
+    }
+    wc->wcc[ewc].c += last;
+    wc->wcc[ewc].n++;
+    if (!wc->wcc_all.empty())
+    {
+        wc->wc_depth--;
+        if (ewc == WallCycleCounter::Run)
+        {
+            wallcycle_all_stop(wc, ewc, cycle);
+        }
+        else if (wc->wc_depth == 2)
+        {
+            wallcycle_all_start(wc, ewc, cycle);
+        }
+    }
+
+    return last;
+}
+
+//! Only increment call count for ewc by one
+inline void wallcycle_increment_event_count(gmx_wallcycle* wc, WallCycleCounter ewc)
+{
+    if (wc == nullptr)
+    {
+        return;
+    }
+    wc->wcc[ewc].n++;
+}
+
+//! Returns the cumulative count and cycle count for ewc
+void wallcycle_get(gmx_wallcycle* wc, WallCycleCounter ewc, int* n, double* c);
+
+//! Resets all cycle counters to zero
+void wallcycle_reset_all(gmx_wallcycle* wc);
+
+//! Scale the cycle counts to reflect how many threads run for that number of cycles
+void wallcycle_scale_by_num_threads(gmx_wallcycle* wc, bool isPmeRank, int nthreads_pp, int nthreads_pme);
+
+//! Return reset_counters from wc struct
+int64_t wcycle_get_reset_counters(gmx_wallcycle* wc);
+
+//! Set reset_counters
+void wcycle_set_reset_counters(gmx_wallcycle* wc, int64_t reset_counters);
+
+//! Set the start sub cycle count for ewcs
+inline void wallcycle_sub_start(gmx_wallcycle* wc, WallCycleSubCounter ewcs)
+{
+    if (sc_useCycleSubcounters && wc != nullptr)
+    {
+        wc->wcsc[ewcs].start = gmx_cycles_read();
+    }
+}
+
+//! Set the start sub cycle count for ewcs without increasing the call count
+inline void wallcycle_sub_start_nocount(gmx_wallcycle* wc, WallCycleSubCounter ewcs)
+{
+    if (sc_useCycleSubcounters && wc != nullptr)
+    {
+        wallcycle_sub_start(wc, ewcs);
+        wc->wcsc[ewcs].n--;
+    }
+}
+
+//! Stop the sub cycle count for ewcs
+inline void wallcycle_sub_stop(gmx_wallcycle* wc, WallCycleSubCounter ewcs)
+{
+    if (sc_useCycleSubcounters && wc != nullptr)
+    {
+        wc->wcsc[ewcs].c += gmx_cycles_read() - wc->wcsc[ewcs].start;
+        wc->wcsc[ewcs].n++;
+    }
+}
+
+#endif
diff --git a/src/include/gromacs/timing/wallcyclereporting.h b/src/include/gromacs/timing/wallcyclereporting.h
new file mode 100644 (file)
index 0000000..950b5b8
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2008, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TIMING_WALLCYCLEREPORTING_H
+#define GMX_TIMING_WALLCYCLEREPORTING_H
+
+/* NOTE: None of the routines here are safe to call within an OpenMP
+ * region */
+
+#include <stdio.h>
+
+#include <array>
+
+#include "gromacs/timing/wallcycle.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct t_commrec;
+
+namespace gmx
+{
+class MDLogger;
+}
+
+struct gmx_wallclock_gpu_nbnxn_t;
+struct gmx_wallclock_gpu_pme_t;
+
+using WallcycleCounts = std::array<double, sc_numWallCycleCounters + sc_numWallCycleSubCounters>;
+/* Convenience typedef */
+
+WallcycleCounts wallcycle_sum(const t_commrec* cr, gmx_wallcycle* wc);
+/* Return a vector of the sum of cycle counts over the nodes in
+   cr->mpi_comm_mysim. */
+
+void wallcycle_print(FILE*                            fplog,
+                     const gmx::MDLogger&             mdlog,
+                     int                              nnodes,
+                     int                              npme,
+                     int                              nth_pp,
+                     int                              nth_pme,
+                     double                           realtime,
+                     gmx_wallcycle*                   wc,
+                     const WallcycleCounts&           cyc_sum,
+                     const gmx_wallclock_gpu_nbnxn_t* gpu_nbnxn_t,
+                     const gmx_wallclock_gpu_pme_t*   gpu_pme_t);
+/* Print the cycle and time accounting */
+
+#endif
diff --git a/src/include/gromacs/timing/walltime_accounting.h b/src/include/gromacs/timing/walltime_accounting.h
new file mode 100644 (file)
index 0000000..f782acb
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_TIMING_WALLTIME_ACCOUNTING_H
+#define GMX_TIMING_WALLTIME_ACCOUNTING_H
+
+#include "gromacs/utility/basedefinitions.h"
+
+/*! \brief
+ * Contains per-process and per-thread data about elapsed wall-clock
+ * times and integration steps performed. */
+typedef struct gmx_walltime_accounting* gmx_walltime_accounting_t;
+
+//! Constructor
+gmx_walltime_accounting_t walltime_accounting_init(int numOpenMPThreads);
+
+//! Destructor
+void walltime_accounting_destroy(gmx_walltime_accounting_t walltime_accounting);
+
+/*! \brief
+ * Record initial time stamps, e.g. at run start
+ */
+void walltime_accounting_start_time(gmx_walltime_accounting_t walltime_accounting);
+
+/*! \brief
+ * Reset time stamps, e.g. at counter re-initalization time
+ */
+void walltime_accounting_reset_time(gmx_walltime_accounting_t walltime_accounting, int64_t step);
+
+/*! \brief
+ * Measure and cache the elapsed wall-clock time since
+ * walltime_accounting_reset_time() */
+void walltime_accounting_end_time(gmx_walltime_accounting_t walltime_accounting);
+
+/*! \brief
+ * Measure and return the elapsed wall-clock time since
+ * walltime_accounting_reset_time() */
+double walltime_accounting_get_time_since_reset(gmx_walltime_accounting_t walltime_accounting);
+
+//! Get the wall-clock time since the actual start of the run (regardless of any resets).
+double walltime_accounting_get_time_since_start(gmx_walltime_accounting_t walltime_accounting);
+
+//! Get the cached wall-clock time, multiplied by the number of OpenMP threads
+double walltime_accounting_get_time_since_reset_over_all_threads(gmx_walltime_accounting_t walltime_accounting);
+
+//! Get the cached initial time stamp for this node
+double walltime_accounting_get_start_time_stamp(gmx_walltime_accounting_t walltime_accounting);
+
+//! Get the number of integration steps done
+int64_t walltime_accounting_get_nsteps_done_since_reset(gmx_walltime_accounting_t walltime_accounting);
+
+/*! \brief Set the number of integration steps done
+ *
+ * TODO consider whether this should get done in walltime_accounting_end */
+void walltime_accounting_set_nsteps_done(gmx_walltime_accounting_t walltime_accounting, int64_t nsteps_done);
+
+//! Record that the simulation finished in a way valid for reporting walltime.
+void walltime_accounting_set_valid_finish(gmx_walltime_accounting_t walltime_accounting);
+
+//! Return whether the simulation finished in a way valid for reporting walltime.
+bool walltime_accounting_get_valid_finish(const gmx_walltime_accounting* walltime_accounting);
+
+/*! \brief
+ * Calls system timing routines (e.g. clock_gettime) to get the (fractional)
+ * number of seconds elapsed since the epoch.
+ *
+ * Resolution is implementation-dependent, but typically nanoseconds
+ * or microseconds. */
+double gmx_gettime();
+
+#endif /* GMX_TIMING_WALLTIME_ACCOUNTING_H */
diff --git a/src/include/gromacs/tools/check.h b/src/include/gromacs/tools/check.h
new file mode 100644 (file)
index 0000000..6870b6f
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOOLS_CHECK_H
+#define GMX_TOOLS_CHECK_H
+
+/*! \brief Implements gmx check
+ *
+ * \param[in] argc  argc value passed to main().
+ * \param[in] argv  argv array passed to main().
+ */
+int gmx_check(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/tools/convert_tpr.h b/src/include/gromacs/tools/convert_tpr.h
new file mode 100644 (file)
index 0000000..7af6d4e
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOOLS_CONVERT_TPR_H
+#define GMX_TOOLS_CONVERT_TPR_H
+
+#include "gromacs/commandline/cmdlineoptionsmodule.h"
+
+namespace gmx
+{
+
+//! Declares gmx convert-tpr
+class ConvertTprInfo
+{
+public:
+    //! Name of the module.
+    static const char name[];
+    //! Short description what the module does.
+    static const char shortDescription[];
+    //! Instantiatiates the module.
+    static ICommandLineOptionsModulePointer create();
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/tools/dump.h b/src/include/gromacs/tools/dump.h
new file mode 100644 (file)
index 0000000..efa27ad
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOOLS_DUMP_H
+#define GMX_TOOLS_DUMP_H
+
+#include "gromacs/commandline/cmdlineoptionsmodule.h"
+
+namespace gmx
+{
+
+class DumpInfo
+{
+
+public:
+    static const char                       name[];
+    static const char                       shortDescription[];
+    static ICommandLineOptionsModulePointer create();
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/tools/eneconv.h b/src/include/gromacs/tools/eneconv.h
new file mode 100644 (file)
index 0000000..5c9045d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOOLS_ENECONV_H
+#define GMX_TOOLS_ENECONV_H
+
+/*! \brief Implements gmx eneconv
+ *
+ * \param[in] argc  argc value passed to main().
+ * \param[in] argv  argv array passed to main().
+ */
+int gmx_eneconv(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/tools/make_ndx.h b/src/include/gromacs/tools/make_ndx.h
new file mode 100644 (file)
index 0000000..99cd2c8
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOOLS_MAKE_NDX_H
+#define GMX_TOOLS_MAKE_NDX_H
+
+/*! \brief Implements gmx make_ndx
+ *
+ * \param[in] argc  argc value passed to main().
+ * \param[in] argv  argv array passed to main().
+ */
+int gmx_make_ndx(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/tools/mk_angndx.h b/src/include/gromacs/tools/mk_angndx.h
new file mode 100644 (file)
index 0000000..e462fb7
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOOLS_MK_ANGNDX_H
+#define GMX_TOOLS_MK_ANGNDX_H
+
+/*! \brief Implements gmx mk_angndx
+ *
+ * \param[in] argc  argc value passed to main().
+ * \param[in] argv  argv array passed to main().
+ */
+int gmx_mk_angndx(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/tools/pme_error.h b/src/include/gromacs/tools/pme_error.h
new file mode 100644 (file)
index 0000000..4add450
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOOLS_PME_ERROR_H
+#define GMX_TOOLS_PME_ERROR_H
+
+/*! \brief Implements gmx pme_error
+ *
+ * \param[in] argc  argc value passed to main().
+ * \param[in] argv  argv array passed to main().
+ */
+int gmx_pme_error(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/tools/report_methods.h b/src/include/gromacs/tools/report_methods.h
new file mode 100644 (file)
index 0000000..1e75dd0
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOOLS_REPORT_METHODS_H
+#define GMX_TOOLS_REPORT_METHODS_H
+
+#include <string>
+
+#include "gromacs/commandline/cmdlineoptionsmodule.h"
+#include "gromacs/utility/filestream.h"
+#include "gromacs/utility/textwriter.h"
+
+struct gmx_mtop_t;
+struct t_inputrec;
+
+namespace gmx
+{
+
+class ReportMethodsInfo
+{
+public:
+    static const char                       name[];
+    static const char                       shortDescription[];
+    static ICommandLineOptionsModulePointer create();
+};
+
+// Helper functions of the class
+
+/*! \brief
+ * Write appropiate Header to output stream.
+ *
+ * \param[in] writer TextWriter object for writing information.
+ * \param[in] text String with the header before writing.
+ * \param[in] section String with section text for header.
+ * \param[in] writeFormattedText If we need to format the text for LaTeX output or not
+ */
+void writeHeader(TextWriter* writer, const std::string& text, const std::string& section, bool writeFormattedText);
+
+/*! \brief
+ * Write information about the molecules in the system.
+ *
+ * This method should write all possible information about
+ * the molecular composition of the system.
+ *
+ * \param[in] writer TextWriter object for writing information.
+ * \param[in] top Local topology used to derive the information to write out.
+ * \param[in] writeFormattedText Decide if we want formatted text output or not.
+ *
+ */
+void writeSystemInformation(TextWriter* writer, const gmx_mtop_t& top, bool writeFormattedText);
+
+/*! \brief
+ * Write information about system parameters.
+ *
+ * This method writes the basic information for the system parameters
+ * and simulation settings as reported in the \p ir.
+ *
+ * \param[in] writer TextWriter object for writing information.
+ * \param[in] ir Reference to inputrec of the run input.
+ * \param[in] writeFormattedText Decide if we want formatted text output or not.
+ */
+void writeParameterInformation(TextWriter* writer, const t_inputrec& ir, bool writeFormattedText);
+
+/*! \brief
+ * Wrapper for writing out information.
+ *
+ * This function is actual called from within the run method
+ * to write the information to the terminal or to file.
+ * New write out methods should be added to it instead of adding them in run.
+ *
+ * \param[in] outputStream The filestream used to write the information to.
+ * \param[in] ir Reference to inputrec of the run input.
+ * \param[in] top Local topology used to derive the information to write out.
+ * \param[in] writeFormattedText Decide if we want formatted text output or not.
+ * \param[in] notStdout Bool to see if we can close the file after writing or not in case of stdout.
+ */
+void writeInformation(TextOutputFile*   outputStream,
+                      const t_inputrec& ir,
+                      const gmx_mtop_t& top,
+                      bool              writeFormattedText,
+                      bool              notStdout);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/tools/trjcat.h b/src/include/gromacs/tools/trjcat.h
new file mode 100644 (file)
index 0000000..cbbf005
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOOLS_TRJCAT_H
+#define GMX_TOOLS_TRJCAT_H
+
+/*! \brief Implements gmx trjcat
+ *
+ * \param[in] argc  argc value passed to main().
+ * \param[in] argv  argv array passed to main().
+ */
+int gmx_trjcat(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/tools/trjconv.h b/src/include/gromacs/tools/trjconv.h
new file mode 100644 (file)
index 0000000..bb5849d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOOLS_TRJCONV_H
+#define GMX_TOOLS_TRJCONV_H
+
+/*! \brief Implements gmx trjconv
+ *
+ * \param[in] argc  argc value passed to main().
+ * \param[in] argv  argv array passed to main().
+ */
+int gmx_trjconv(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/tools/tune_pme.h b/src/include/gromacs/tools/tune_pme.h
new file mode 100644 (file)
index 0000000..cedd5f1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOOLS_TUNE_PME_H
+#define GMX_TOOLS_TUNE_PME_H
+
+/*! \brief Implements gmx tune_pme
+ *
+ * \param[in] argc  argc value passed to main().
+ * \param[in] argv  argv array passed to main().
+ */
+int gmx_tune_pme(int argc, char* argv[]);
+
+#endif
diff --git a/src/include/gromacs/topology/atomprop.h b/src/include/gromacs/topology/atomprop.h
new file mode 100644 (file)
index 0000000..721f0e9
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOPOLOGY_ATOMPROP_H
+#define GMX_TOPOLOGY_ATOMPROP_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+enum
+{
+    epropMass,
+    epropVDW,
+    epropDGsol,
+    epropElectroneg,
+    epropElement,
+    epropNR
+};
+
+struct AtomProperty;
+/*! \brief
+ * Holds all the atom property information loaded.
+ */
+class AtomProperties
+{
+public:
+    //! Default constructor.
+    AtomProperties();
+    //! Default destructor
+    ~AtomProperties();
+
+    /*! \brief
+     * Get element string from atom number.
+     *
+     * \param[in] atomNumber Atomnumber to check.
+     * \returns Name of the element.
+     *
+     * \todo This should be made const once the lazy
+     * implementation is done properly for the class.
+     */
+    std::string elementFromAtomNumber(int atomNumber);
+    /*! \brief
+     * Get atom number from element string.
+     *
+     * \param[in] element Name of element.
+     * \returns AtomNumber that was being looked for.
+     *
+     * \todo This should be made const once the lazy
+     * implementation is done properly for the class.
+     */
+    int atomNumberFromElement(const char* element);
+    /*! \brief
+     * Set atom property based on atomname.
+     *
+     * Extract a \p value from the database. Returns true
+     * if this is successful, or false if not. Sets default value
+     * in the later case. The first time this function is called
+     * for this property the database will be initialized.
+     *
+     * \param[in] eprop Property to set.
+     * \param[in] residueName Residue name for entry.
+     * \param[in] atomName Atom name for entry.
+     * \param[out] value New value to set or default.
+     * \returns If the operation has been succesful.
+     */
+    bool setAtomProperty(int eprop, const std::string& residueName, const std::string& atomName, real* value);
+    /*! \brief
+     * Get handle to property.
+     *
+     * \param[in] eprop Which property we need a handle to.
+     * \returns Pointer to property entry.
+     */
+    AtomProperty* prop(int eprop);
+
+private:
+    //! Implementation pointer.
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+#endif
diff --git a/src/include/gromacs/topology/atoms.h b/src/include/gromacs/topology/atoms.h
new file mode 100644 (file)
index 0000000..a8fba1d
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOPOLOGY_ATOMS_H
+#define GMX_TOPOLOGY_ATOMS_H
+
+#include <stdio.h>
+
+#include <vector>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+#include "gromacs/utility/unique_cptr.h"
+
+struct t_symtab;
+
+/* The particle type */
+enum class ParticleType : int
+{
+    Atom,
+    Nucleus,
+    Shell,
+    Bond,
+    VSite,
+    Count
+};
+
+/* The particle type names */
+const char* enumValueToString(ParticleType enumValue);
+
+/* Enumerated type for pdb records. The other entries are ignored
+ * when reading a pdb file
+ */
+enum class PdbRecordType : int
+{
+    Atom,
+    Hetatm,
+    Anisou,
+    Cryst1,
+    Compound,
+    Model,
+    EndModel,
+    Ter,
+    Header,
+    Title,
+    Remark,
+    Conect,
+    Count
+};
+
+const char* enumValueToString(PdbRecordType enumValue);
+
+typedef struct t_atom
+{
+    real           m, q;       /* Mass and charge                      */
+    real           mB, qB;     /* Mass and charge for Free Energy calc */
+    unsigned short type;       /* Atom type                            */
+    unsigned short typeB;      /* Atom type for Free Energy calc       */
+    ParticleType   ptype;      /* Particle type                        */
+    int            resind;     /* Index into resinfo (in t_atoms)      */
+    int            atomnumber; /* Atomic Number or 0                   */
+    char           elem[4];    /* Element name                         */
+} t_atom;
+
+typedef struct t_resinfo
+{
+    char**        name;     /* Pointer to the residue name          */
+    int           nr;       /* Residue number                       */
+    unsigned char ic;       /* Code for insertion of residues       */
+    int           chainnum; /* Iincremented at TER or new chain id  */
+    char          chainid;  /* Chain identifier written/read to pdb */
+    char**        rtp;      /* rtp building block name (optional)   */
+} t_resinfo;
+
+typedef struct t_pdbinfo
+{
+    PdbRecordType type;         /* PDB record name                      */
+    int           atomnr;       /* PDB atom number                      */
+    char          altloc;       /* Alternate location indicator         */
+    char          atomnm[6];    /* True atom name including leading spaces */
+    real          occup;        /* Occupancy                            */
+    real          bfac;         /* B-factor                             */
+    gmx_bool      bAnisotropic; /* (an)isotropic switch                 */
+    int           uij[6];       /* Anisotropic B-factor                 */
+} t_pdbinfo;
+
+//! Contains indices into group names for different groups.
+using AtomGroupIndices = std::vector<int>;
+
+typedef struct t_atoms
+{
+    int     nr;         /* Nr of atoms                          */
+    t_atom* atom;       /* Array of atoms (dim: nr)             */
+                        /* The following entries will not       */
+                        /* always be used (nres==0)             */
+    char*** atomname;   /* Array of pointers to atom name       */
+                        /* use: (*(atomname[i]))                */
+    char*** atomtype;   /* Array of pointers to atom types      */
+                        /* use: (*(atomtype[i]))                */
+    char*** atomtypeB;  /* Array of pointers to B atom types    */
+                        /* use: (*(atomtypeB[i]))               */
+    int        nres;    /* The number of resinfo entries        */
+    t_resinfo* resinfo; /* Array of residue names and numbers   */
+    t_pdbinfo* pdbinfo; /* PDB Information, such as aniso. Bfac */
+
+    /* Flags that tell if properties are set for all nr atoms.
+     * For B-state parameters, both haveBState and the mass/charge/type
+     * flag should be TRUE.
+     */
+    gmx_bool haveMass;    /* Mass available                       */
+    gmx_bool haveCharge;  /* Charge available                     */
+    gmx_bool haveType;    /* Atom type available                  */
+    gmx_bool haveBState;  /* B-state parameters available         */
+    gmx_bool havePdbInfo; /* pdbinfo available                    */
+} t_atoms;
+
+typedef struct t_atomtypes
+{
+    int  nr;         /* number of atomtypes                          */
+    int* atomnumber; /* Atomic number, used for QM/MM                */
+} t_atomtypes;
+
+#define PERTURBED(a) (((a).mB != (a).m) || ((a).qB != (a).q) || ((a).typeB != (a).type))
+
+void init_atom(t_atoms* at);
+void init_atomtypes(t_atomtypes* at);
+void done_atom(t_atoms* at);
+void done_and_delete_atoms(t_atoms* atoms);
+void done_atomtypes(t_atomtypes* at);
+
+void init_t_atoms(t_atoms* atoms, int natoms, gmx_bool bPdbinfo);
+/* allocate memory for the arrays, set nr to natoms and nres to 0
+ * set pdbinfo to NULL or allocate memory for it */
+
+void gmx_pdbinfo_init_default(t_pdbinfo* pdbinfo);
+
+t_atoms* copy_t_atoms(const t_atoms* src);
+/* copy an atoms struct from src to a new one */
+
+void add_t_atoms(t_atoms* atoms, int natom_extra, int nres_extra);
+/* allocate extra space for more atoms and or residues */
+
+void t_atoms_set_resinfo(t_atoms*         atoms,
+                         int              atom_ind,
+                         struct t_symtab* symtab,
+                         const char*      resname,
+                         int              resnr,
+                         unsigned char    ic,
+                         int              chainnum,
+                         char             chainid);
+/* Set the residue name, number, insertion code and chain identifier
+ * of atom index atom_ind.
+ */
+
+void pr_atoms(FILE* fp, int indent, const char* title, const t_atoms* atoms, gmx_bool bShownumbers);
+void pr_atomtypes(FILE* fp, int indent, const char* title, const t_atomtypes* atomtypes, gmx_bool bShowNumbers);
+
+/*! \brief Compare information in the t_atoms data structure.
+ *
+ * \param[in] fp Pointer to file to write to.
+ * \param[in] a1 Pointer to first data structure to compare.
+ * \param[in] a2 Pointer to second data structure or nullptr.
+ * \param[in] relativeTolerance Relative floating point comparison tolerance.
+ * \param[in] absoluteTolerance Absolute floating point comparison tolerance.
+ */
+void compareAtoms(FILE* fp, const t_atoms* a1, const t_atoms* a2, real relativeTolerance, real absoluteTolerance);
+
+/*! \brief Set mass for each atom using the atom and residue names using a database
+ *
+ * If atoms->haveMass = TRUE does nothing.
+ * If printMissingMasss = TRUE, prints details for first 10 missing masses
+ * to stderr.
+ */
+void atomsSetMassesBasedOnNames(t_atoms* atoms, gmx_bool printMissingMasses);
+
+//! Deleter for t_atoms, needed until it has a proper destructor.
+using AtomsDataPtr = gmx::unique_cptr<t_atoms, done_and_delete_atoms>;
+
+
+#endif
diff --git a/src/include/gromacs/topology/atomsbuilder.h b/src/include/gromacs/topology/atomsbuilder.h
new file mode 100644 (file)
index 0000000..2212203
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2016,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Utility classes for manipulating \c t_atoms structures.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ */
+#ifndef GMX_TOPOLOGY_ATOMSBUILDER_H
+#define GMX_TOPOLOGY_ATOMSBUILDER_H
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+
+#include "gromacs/utility/classhelpers.h"
+#include "gromacs/utility/real.h"
+
+struct t_atoms;
+struct t_resinfo;
+struct t_symtab;
+
+namespace gmx
+{
+
+class AtomsBuilder
+{
+public:
+    AtomsBuilder(t_atoms* atoms, t_symtab* symtab);
+    ~AtomsBuilder();
+
+    void reserve(int atomCount, int residueCount);
+    void clearAtoms();
+
+    int currentAtomCount() const;
+
+    void setNextResidueNumber(int number);
+    void addAtom(const t_atoms& atoms, int i);
+    void startResidue(const t_resinfo& resinfo);
+    void finishResidue(const t_resinfo& resinfo);
+    void discardCurrentResidue();
+
+    void mergeAtoms(const t_atoms& atoms);
+
+private:
+    char** symtabString(char** source);
+
+    t_atoms*  atoms_;
+    t_symtab* symtab_;
+    int       nrAlloc_;
+    int       nresAlloc_;
+    int       currentResidueIndex_;
+    int       nextResidueNumber_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(AtomsBuilder);
+};
+
+class AtomsRemover
+{
+public:
+    explicit AtomsRemover(const t_atoms& atoms);
+    ~AtomsRemover();
+
+    void refreshAtomCount(const t_atoms& atoms);
+
+    void markAll();
+    void markResidue(const t_atoms& atoms, int atomIndex, bool bStatus);
+    bool isMarked(int atomIndex) const { return removed_[atomIndex] != 0; }
+
+    void removeMarkedElements(std::vector<RVec>* container) const;
+    void removeMarkedElements(std::vector<real>* container) const;
+    void removeMarkedAtoms(t_atoms* atoms) const;
+
+private:
+    std::vector<char> removed_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(AtomsRemover);
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/topology/block.h b/src/include/gromacs/topology/block.h
new file mode 100644 (file)
index 0000000..0acf7f4
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOPOLOGY_BLOCK_H
+#define GMX_TOPOLOGY_BLOCK_H
+
+#include <stdio.h>
+
+#include <vector>
+
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/range.h"
+
+namespace gmx
+{
+
+template<typename>
+class ListOfLists;
+
+/*! \brief Division of a range of indices into consecutive blocks
+ *
+ * A range of consecutive indices 0 to full.range.end() is divided
+ * into numBlocks() consecutive blocks of consecutive indices.
+ * Block b contains indices i for which block(b).begin() <= i < block(b).end().
+ */
+class RangePartitioning
+{
+public:
+    /*! \brief A block defined by a range of atom indices */
+    using Block = Range<int>;
+
+    /*! \brief Returns the number of blocks */
+    int numBlocks() const { return static_cast<int>(index_.size()) - 1; }
+
+    /*! \brief Returns the size of the block with index \p blockIndex */
+    Block block(int blockIndex) const
+    {
+        return Block(index_[blockIndex], index_[blockIndex + 1LL]);
+    }
+
+    /*! \brief Returns the full range */
+    Block fullRange() const { return Block(index_.front(), index_.back()); }
+
+    /*! \brief Returns a range starting at \p blockIndexBegin and ending at \p blockIndexEnd */
+    Block subRange(int blockIndexBegin, int blockIndexEnd) const
+    {
+        return Block(index_[blockIndexBegin], index_[blockIndexEnd]);
+    }
+
+    /*! \brief Returns true when all blocks have size 0 or numBlocks()=0 */
+    bool allBlocksHaveSizeOne() const { return (index_.back() == numBlocks()); }
+
+    /*! \brief Appends a block of size \p blockSize at the end of the range
+     *
+     * \note blocksize has to be >= 1
+     */
+    void appendBlock(int blockSize)
+    {
+        GMX_ASSERT(blockSize > 0, "block sizes should be >= 1");
+        index_.push_back(index_.back() + blockSize);
+    }
+
+    /*! \brief Removes all blocks */
+    void clear() { index_.resize(1); }
+
+    /*! \brief Reduces the number of blocks to \p newNumBlocks
+     *
+     * \note \p newNumBlocks should be <= numBlocks().
+     */
+    void reduceNumBlocks(int newNumBlocks)
+    {
+        GMX_ASSERT(newNumBlocks <= numBlocks(), "Can only shrink to fewer blocks");
+        index_.resize(newNumBlocks + 1LL);
+    }
+
+    /*! \brief Sets the partitioning to \p numBlocks blocks each of size 1 */
+    void setAllBlocksSizeOne(int numBlocks);
+
+    /*! \brief Returns the raw block index array, avoid using this */
+    std::vector<int>& rawIndex() { return index_; }
+
+private:
+    std::vector<int> index_ = { 0 }; /**< The list of block begin/end indices */
+};
+
+} // namespace gmx
+
+/* Deprecated, C-style version of RangePartitioning */
+typedef struct t_block
+{
+    int blockSize(int blockIndex) const
+    {
+        GMX_ASSERT(blockIndex < nr, "blockIndex should be in range");
+        return index[blockIndex + 1] - index[blockIndex];
+    }
+
+    int  nr;           /* The number of blocks          */
+    int* index;        /* Array of indices (dim: nr+1)  */
+    int  nalloc_index; /* The allocation size for index */
+} t_block;
+
+struct t_blocka
+{
+    int  nr;    /* The number of blocks              */
+    int* index; /* Array of indices in a (dim: nr+1) */
+    int  nra;   /* The number of atoms               */
+    int* a;     /* Array of atom numbers in each group  */
+    /* (dim: nra)                           */
+    /* Block i (0<=i<nr) runs from          */
+    /* index[i] to index[i+1]-1. There will */
+    /* allways be an extra entry in index   */
+    /* to terminate the table               */
+    int nalloc_index; /* The allocation size for index        */
+    int nalloc_a;     /* The allocation size for a            */
+};
+
+/*! \brief
+ * Fully initialize t_block datastructure.
+ *
+ * Initializes a \p block and sets up the first index to zero.
+ *
+ * \param[in,out] block datastructure to initialize.
+ */
+void init_block(t_block* block);
+
+/*! \brief
+ * Fully initialize t_blocka datastructure.
+ *
+ * Initializes a \p block and sets up the first index to zero.
+ * The atom number array is initialized to nullptr.
+ *
+ * \param[in,out] block datastructure to initialize.
+ */
+void init_blocka(t_blocka* block);
+
+/* TODO
+ * In general all t_block datastructures should be avoided
+ * in favour of RangePartitioning. This here is a simple cludge
+ * to use more modern initialization while we move to the use
+ * of RangePartitioning.
+ */
+
+/*! \brief
+ * Minimal initialization of t_block datastructure.
+ *
+ * Performs the equivalent to a snew on a t_block, setting all
+ * values to zero or nullptr. Needed for some cases where the topology
+ * handling expects a block to be valid initialized (e.g. during domain
+ * decomposition) but without the first block set to zero.
+ *
+ * \param[in,out] block datastructure to initialize.
+ */
+void init_block_null(t_block* block);
+
+/*! \brief
+ * Minimal initialization of t_blocka datastructure.
+ *
+ * Performs the equivalent to a snew on a t_blocka, setting all
+ * values to zero or nullptr. Needed for some cases where the topology
+ * handling expects a block to be valid initialized (e.g. during domain
+ * decomposition) but without the first block set to zero.
+ *
+ * \param[in,out] block datastructure to initialize.
+ */
+void init_blocka_null(t_blocka* block);
+
+t_blocka* new_blocka();
+/* allocate new block */
+
+void done_block(t_block* block);
+void done_blocka(t_blocka* block);
+
+void copy_blocka(const t_blocka* src, t_blocka* dest);
+
+void copy_block(const t_block* src, t_block* dst);
+
+void stupid_fill_block(t_block* grp, int natom, gmx_bool bOneIndexGroup);
+/* Fill a block structure with numbers identical to the index
+ * (0, 1, 2, .. natom-1)
+ * If bOneIndexGroup, then all atoms are  lumped in one index group,
+ * otherwise there is one atom per index entry
+ */
+
+void stupid_fill_blocka(t_blocka* grp, int natom);
+/* Fill a block structure with numbers identical to the index
+ * (0, 1, 2, .. natom-1)
+ * There is one atom per index entry
+ */
+
+void pr_block(FILE* fp, int indent, const char* title, const t_block* block, gmx_bool bShowNumbers);
+void pr_blocka(FILE* fp, int indent, const char* title, const t_blocka* block, gmx_bool bShowNumbers);
+void pr_listoflists(FILE* fp, int indent, const char* title, const gmx::ListOfLists<int>* block, gmx_bool bShowNumbers);
+
+#endif
diff --git a/src/include/gromacs/topology/exclusionblocks.h b/src/include/gromacs/topology/exclusionblocks.h
new file mode 100644 (file)
index 0000000..69c9c54
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+#ifndef GMX_TOPOLOGY_EXCLUSIONBLOCKS_H
+#define GMX_TOPOLOGY_EXCLUSIONBLOCKS_H
+
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+
+struct t_blocka;
+
+namespace gmx
+{
+template<typename>
+class ListOfLists;
+
+/*! \libinternal \brief
+ * Describes exclusions for a single atom.
+ */
+struct ExclusionBlock
+{
+    //! Atom numbers for exclusion.
+    std::vector<int> atomNumber;
+    //! Number of atoms in the exclusion.
+    int nra() const { return atomNumber.size(); }
+};
+
+/*! \brief Merge the contents of \c b2 into \c excl.
+ *
+ * Requires that \c b2 and \c excl describe the same number of
+ * particles, if \c b2 describes a non-zero number.
+ */
+void mergeExclusions(ListOfLists<int>* excl, gmx::ArrayRef<ExclusionBlock> b2);
+
+/*! \brief
+ * Convert the exclusions.
+ *
+ * Convert t_blocka exclusions in \p b into ExclusionBlock form and
+ * include them in \p b2.
+ *
+ * \param[in] b Exclusions in t_blocka form.
+ * \param[inout] b2 ExclusionBlocks to populate with t_blocka exclusions.
+ */
+void blockaToExclusionBlocks(const t_blocka* b, gmx::ArrayRef<ExclusionBlock> b2);
+
+//! Convert the exclusions expressed in \c b into t_blocka form
+void exclusionBlocksToBlocka(gmx::ArrayRef<const ExclusionBlock> b2, t_blocka* b);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/topology/forcefieldparameters.h b/src/include/gromacs/topology/forcefieldparameters.h
new file mode 100644 (file)
index 0000000..0534261
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOPOLOGY_FORCEFIELDPARAMETERS_H
+#define GMX_TOPOLOGY_FORCEFIELDPARAMETERS_H
+
+#include <cstdio>
+
+#include <vector>
+
+#include "gromacs/topology/idef.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/real.h"
+
+/*! \brief Struct that holds all force field parameters for the simulated system */
+struct gmx_ffparams_t
+{
+    /*! \brief Returns the number of function types, which matches the number of elements in iparams */
+    int numTypes() const
+    {
+        GMX_ASSERT(iparams.size() == functype.size(), "Parameters and function types go together");
+
+        return static_cast<int>(functype.size());
+    }
+
+    /* TODO: Consider merging functype and iparams, either by storing
+     *       the functype in t_iparams or by putting both in a single class.
+     */
+    int                     atnr = 0;    /**< The number of non-bonded atom types */
+    std::vector<t_functype> functype;    /**< The function type per type */
+    std::vector<t_iparams>  iparams;     /**< Force field parameters per type */
+    double                  reppow  = 0; /**< The repulsion power for VdW: C12*r^-reppow   */
+    real                    fudgeQQ = 0; /**< The scaling factor for Coulomb 1-4: f*q1*q2  */
+    gmx_cmap_t              cmap_grid;   /**< The dihedral correction maps                 */
+};
+
+void pr_ffparams(FILE* fp, int indent, const char* title, const gmx_ffparams_t* ffparams, gmx_bool bShowNumbers);
+
+#endif
diff --git a/src/include/gromacs/topology/idef.h b/src/include/gromacs/topology/idef.h
new file mode 100644 (file)
index 0000000..7c68e2c
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOPOLOGY_IDEF_H
+#define GMX_TOPOLOGY_IDEF_H
+
+#include <cstdio>
+
+#include <array>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/ifunc.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_ffparams_t;
+
+typedef union t_iparams
+{
+    /* Some parameters have A and B values for free energy calculations.
+     * The B values are not used for regular simulations of course.
+     * Free Energy for nonbondeds can be computed by changing the atom type.
+     * The harmonic type is used for all harmonic potentials:
+     * bonds, angles and improper dihedrals
+     */
+    struct
+    {
+        real a, b, c;
+    } bham;
+    struct
+    {
+        real rA, krA, rB, krB;
+    } harmonic;
+    struct
+    {
+        real klinA, aA, klinB, aB;
+    } linangle;
+    struct
+    {
+        real lowA, up1A, up2A, kA, lowB, up1B, up2B, kB;
+    } restraint;
+    /* No free energy supported for cubic bonds, FENE, WPOL or cross terms */
+    struct
+    {
+        real b0, kb, kcub;
+    } cubic;
+    struct
+    {
+        real bm, kb;
+    } fene;
+    struct
+    {
+        real r1e, r2e, krr;
+    } cross_bb;
+    struct
+    {
+        real r1e, r2e, r3e, krt;
+    } cross_ba;
+    struct
+    {
+        real thetaA, kthetaA, r13A, kUBA, thetaB, kthetaB, r13B, kUBB;
+    } u_b;
+    struct
+    {
+        real theta, c[5];
+    } qangle;
+    struct
+    {
+        real alpha;
+    } polarize;
+    struct
+    {
+        real alpha, drcut, khyp;
+    } anharm_polarize;
+    struct
+    {
+        real al_x, al_y, al_z, rOH, rHH, rOD;
+    } wpol;
+    struct
+    {
+        real a, alpha1, alpha2, rfac;
+    } thole;
+    struct
+    {
+        real c6, c12;
+    } lj;
+    struct
+    {
+        real c6A, c12A, c6B, c12B;
+    } lj14;
+    struct
+    {
+        real fqq, qi, qj, c6, c12;
+    } ljc14;
+    struct
+    {
+        real qi, qj, c6, c12;
+    } ljcnb;
+    /* Proper dihedrals can not have different multiplicity when
+     * doing free energy calculations, because the potential would not
+     * be periodic anymore.
+     */
+    struct
+    {
+        real phiA, cpA;
+        int  mult;
+        real phiB, cpB;
+    } pdihs;
+    struct
+    {
+        real dA, dB;
+    } constr;
+    /* Settle can not be used for Free energy calculations of water bond geometry.
+     * Use shake (or lincs) instead if you have to change the water bonds.
+     */
+    struct
+    {
+        real doh, dhh;
+    } settle;
+    struct
+    {
+        real b0A, cbA, betaA, b0B, cbB, betaB;
+    } morse;
+    struct
+    {
+        real pos0A[DIM], fcA[DIM], pos0B[DIM], fcB[DIM];
+    } posres;
+    struct
+    {
+        real pos0[DIM], r, k;
+        int  geom;
+    } fbposres;
+    struct
+    {
+        real rbcA[NR_RBDIHS], rbcB[NR_RBDIHS];
+    } rbdihs;
+    struct
+    {
+        real cbtcA[NR_CBTDIHS], cbtcB[NR_CBTDIHS];
+    } cbtdihs;
+    struct
+    {
+        real a, b, c, d, e, f;
+    } vsite;
+    struct
+    {
+        int  n;
+        real a;
+    } vsiten;
+    /* NOTE: npair is only set after reading the tpx file */
+    struct
+    {
+        real low, up1, up2, kfac;
+        int  type, label, npair;
+    } disres;
+    struct
+    {
+        real phiA, dphiA, kfacA, phiB, dphiB, kfacB;
+    } dihres;
+    struct
+    {
+        int  ex, power, label;
+        real c, obs, kfac;
+    } orires;
+    struct
+    {
+        int  table;
+        real kA;
+        real kB;
+    } tab;
+    struct
+    {
+        int cmapA, cmapB;
+    } cmap;
+    struct
+    {
+        real buf[MAXFORCEPARAM];
+    } generic; /* Conversion */
+} t_iparams;
+
+typedef int t_functype;
+
+/* List of listed interactions, see description further down.
+ *
+ * TODO: Consider storing the function type as well.
+ * TODO: Consider providing per interaction access.
+ */
+struct InteractionList
+{
+    /* Returns the total number of elements in iatoms */
+    int size() const { return static_cast<int>(iatoms.size()); }
+
+    /* Returns whether the list is empty */
+    bool empty() const { return iatoms.empty(); }
+
+    /* Adds one interaction to the list */
+    template<std::size_t numAtoms>
+    void push_back(const int parameterType, const std::array<int, numAtoms>& atoms)
+    {
+        const std::size_t oldSize = iatoms.size();
+        iatoms.resize(iatoms.size() + 1 + numAtoms);
+        iatoms[oldSize] = parameterType;
+        for (std::size_t i = 0; i < numAtoms; i++)
+        {
+            iatoms[oldSize + 1 + i] = atoms[i];
+        }
+    }
+
+    /* Adds one interaction to the list */
+    void push_back(const int parameterType, const int numAtoms, const int* atoms)
+    {
+        const std::size_t oldSize = iatoms.size();
+        iatoms.resize(iatoms.size() + 1 + numAtoms);
+        iatoms[oldSize] = parameterType;
+        for (int i = 0; i < numAtoms; i++)
+        {
+            iatoms[oldSize + 1 + i] = atoms[i];
+        }
+    }
+
+    /* Appends \p ilist at the back of the list */
+    void append(const InteractionList& ilist)
+    {
+        iatoms.insert(iatoms.end(), ilist.iatoms.begin(), ilist.iatoms.end());
+    }
+
+    /* Clears the list */
+    void clear() { iatoms.clear(); }
+
+    /* List of interactions, see explanation further down */
+    std::vector<int> iatoms;
+};
+
+/* List of interaction lists, one list for each interaction type
+ *
+ * TODO: Consider only including entries in use instead of all F_NRE
+ */
+using InteractionLists = std::array<InteractionList, F_NRE>;
+
+/* Deprecated list of listed interactions */
+struct t_ilist
+{
+    /* Returns the total number of elements in iatoms */
+    int size() const { return nr; }
+
+    /* Returns whether the list is empty */
+    bool empty() const { return nr == 0; }
+
+    int      nr;
+    t_iatom* iatoms;
+    int      nalloc;
+};
+
+/* TODO: Remove t_ilist and remove templating on list type in mshift.cpp */
+
+/*
+ * The structs InteractionList and t_ilist defines a list of atoms with their interactions.
+ * General field description:
+ *   int nr
+ *      the size (nr elements) of the interactions array (iatoms[]).
+ *   t_iatom *iatoms
+ *  specifies which atoms are involved in an interaction of a certain
+ *       type. The layout of this array is as follows:
+ *
+ *        +-----+---+---+---+-----+---+---+-----+---+---+---+-----+---+---+...
+ *        |type1|at1|at2|at3|type2|at1|at2|type1|at1|at2|at3|type3|at1|at2|
+ *        +-----+---+---+---+-----+---+---+-----+---+---+---+-----+---+---+...
+ *
+ *  So for interaction type type1 3 atoms are needed, and for type2 and
+ *      type3 only 2. The type identifier is used to select the function to
+ *      calculate the interaction and its actual parameters. This type
+ *      identifier is an index in a params[] and functype[] array.
+ */
+
+/*! \brief Type for returning a list of InteractionList references
+ *
+ * TODO: Remove when the function type is made part of InteractionList
+ */
+struct InteractionListHandle
+{
+    const int               functionType; //!< The function type
+    const std::vector<int>& iatoms;       //!< Reference to interaction list
+};
+
+/*! \brief Returns a list of all non-empty InteractionList entries with any of the interaction flags in \p flags set
+ *
+ * \param[in] ilists  Set of interaction lists
+ * \param[in] flags   Bit mask with one or more IF_... bits set
+ */
+static inline std::vector<InteractionListHandle> extractILists(const InteractionLists& ilists, int flags)
+{
+    std::vector<InteractionListHandle> handles;
+    for (size_t ftype = 0; ftype < ilists.size(); ftype++)
+    {
+        if ((interaction_function[ftype].flags & flags) && !ilists[ftype].empty())
+        {
+            handles.push_back({ static_cast<int>(ftype), ilists[ftype].iatoms });
+        }
+    }
+    return handles;
+}
+
+/*! \brief Returns the stride for the iatoms array in \p ilistHandle
+ *
+ * \param[in] ilistHandle  The ilist to return the stride for
+ */
+static inline int ilistStride(const InteractionListHandle& ilistHandle)
+{
+    return 1 + NRAL(ilistHandle.functionType);
+}
+
+struct gmx_cmapdata_t
+{
+    std::vector<real> cmap; /* Has length 4*grid_spacing*grid_spacing, */
+    /* there are 4 entries for each cmap type (V,dVdx,dVdy,d2dVdxdy) */
+};
+
+struct gmx_cmap_t
+{
+    int                         grid_spacing = 0; /* Grid spacing */
+    std::vector<gmx_cmapdata_t> cmapdata; /* Lists of grids with actual, pre-interpolated data */
+};
+
+
+enum
+{
+    ilsortUNKNOWN,
+    ilsortNO_FE,
+    ilsortFE_SORTED
+};
+
+/* Struct with list of interaction parameters and lists of interactions
+ *
+ * TODO: Convert to a proper class with private data members so we can
+ * ensure that the free-energy sorting and sorting setting is consistent.
+ */
+class InteractionDefinitions
+{
+public:
+    /* Constructor
+     *
+     * \param[in] ffparams  The interaction parameters, the lifetime of the created object should not exceed the lifetime of the passed parameters
+     */
+    InteractionDefinitions(const gmx_ffparams_t& ffparams);
+
+    // Clears data not read in from ffparams
+    void clear();
+
+    // The interaction parameters
+    const std::vector<t_iparams>& iparams;
+    // The function type per type
+    const std::vector<int>& functype;
+    // Position restraint interaction parameters
+    std::vector<t_iparams> iparams_posres;
+    // Flat-bottomed position restraint parameters
+    std::vector<t_iparams> iparams_fbposres;
+    // The list of interactions for each type. Note that some, such as LJ and COUL will have 0 entries.
+    std::array<InteractionList, F_NRE> il;
+    /* The number of non-perturbed interactions at the start of each entry in il */
+    std::array<int, F_NRE> numNonperturbedInteractions;
+    // The sorting state of interaction in il
+    int ilsort = ilsortUNKNOWN;
+    // The dihedral correction maps
+    gmx_cmap_t cmap_grid;
+};
+
+/* Deprecated interation definitions, used in t_topology */
+struct t_idef
+{
+    int         ntypes;
+    int         atnr;
+    t_functype* functype;
+    t_iparams*  iparams;
+    real        fudgeQQ;
+    t_iparams * iparams_posres, *iparams_fbposres;
+
+    t_ilist il[F_NRE];
+    int     ilsort;
+};
+
+/*
+ * The struct t_idef defines all the interactions for the complete
+ * simulation. The structure is setup in such a way that the multinode
+ * version of the program  can use it as easy as the single node version.
+ * General field description:
+ *   int ntypes
+ *      defines the number of elements in functype[] and param[].
+ *   int nodeid
+ *      the node id (if parallel machines)
+ *   int atnr
+ *      the number of atomtypes
+ *   t_functype *functype
+ *      array of length ntypes, defines for every force type what type of
+ *      function to use. Every "bond" with the same function but different
+ *      force parameters is a different force type. The type identifier in the
+ *      forceatoms[] array is an index in this array.
+ *   t_iparams *iparams
+ *      array of length ntypes, defines the parameters for every interaction
+ *      type. The type identifier in the actual interaction list
+ *      (ilist[ftype].iatoms[]) is an index in this array.
+ *   gmx_cmap_t cmap_grid
+ *      the grid for the dihedral pair correction maps.
+ *   t_iparams *iparams_posres, *iparams_fbposres
+ *      defines the parameters for position restraints only.
+ *      Position restraints are the only interactions that have different
+ *      parameters (reference positions) for different molecules
+ *      of the same type. ilist[F_POSRES].iatoms[] is an index in this array.
+ *   t_ilist il[F_NRE]
+ *      The list of interactions for each type. Note that some,
+ *      such as LJ and COUL will have 0 entries.
+ *   int ilsort
+ *      The state of the sorting of il, values are provided above.
+ */
+
+namespace gmx
+{
+class TextWriter;
+} // namespace gmx
+
+void printInteractionParameters(gmx::TextWriter* writer, t_functype ftype, const t_iparams& iparams);
+void pr_iparams(FILE* fp, t_functype ftype, const t_iparams& iparams);
+void pr_ilist(FILE*                  fp,
+              int                    indent,
+              const char*            title,
+              const t_functype*      functype,
+              const InteractionList& ilist,
+              bool                   bShowNumbers,
+              bool                   bShowParameters,
+              const t_iparams*       iparams);
+void pr_idef(FILE* fp, int indent, const char* title, const t_idef* idef, bool bShowNumbers, bool bShowParameters);
+
+/*! \brief
+ * Properly initialize idef struct.
+ *
+ * \param[in] idef Pointer to idef struct to initialize.
+ */
+void init_idef(t_idef* idef);
+
+/*! \brief
+ * Properly clean up idef struct.
+ *
+ * \param[in] idef Pointer to idef struct to clean up.
+ */
+void done_idef(t_idef* idef);
+
+#endif
diff --git a/src/include/gromacs/topology/ifunc.h b/src/include/gromacs/topology/ifunc.h
new file mode 100644 (file)
index 0000000..0ac9c0c
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2015,2016,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOPOLOGY_IFUNC_H
+#define GMX_TOPOLOGY_IFUNC_H
+
+#include "gromacs/math/vectypes.h"
+
+struct t_fcdata;
+struct t_graph;
+union t_iparams;
+struct t_mdatoms;
+struct t_pbc;
+
+/* TODO: Remove this typedef when t_ilist is removed */
+typedef int t_iatom;
+
+/* Real vector type with an additional, unused 4th element.
+ * This type is used to allow aligned 4-wide SIMD loads and stores.
+ */
+typedef real rvec4[4];
+
+/*
+ * The function type t_ifunc() calculates one interaction, using iatoms[]
+ * and iparams. Within the function the number of atoms to be used is
+ * known. Within the function only the atomid part of the iatoms[] array
+ * is supplied, not the type field (see also t_ilist). The function
+ * returns the potential energy. If pbc==NULL the coordinates in x are
+ * assumed to be such that no calculation of PBC is necessary,
+ * If pbc!=NULL a full PBC calculation is performed.
+ * If g!=NULL it is used for determining the shift forces.
+ * With domain decomposition ddgatindex can be used for getting global
+ * atom numbers for warnings and error messages.
+ * ddgatindex is NULL when domain decomposition is not used.
+ */
+
+constexpr unsigned int IF_NULL       = 0;
+constexpr unsigned int IF_BOND       = 1 << 0;
+constexpr unsigned int IF_VSITE      = 1 << 1;
+constexpr unsigned int IF_CONSTRAINT = 1 << 2;
+constexpr unsigned int IF_CHEMBOND   = 1 << 3;
+constexpr unsigned int IF_BTYPE      = 1 << 4;
+constexpr unsigned int IF_ATYPE      = 1 << 5;
+constexpr unsigned int IF_DIHEDRAL   = 1 << 6;
+constexpr unsigned int IF_PAIR       = 1 << 7;
+constexpr unsigned int IF_TABULATED  = 1 << 8;
+constexpr unsigned int IF_LIMZERO    = 1 << 9;
+/* These flags tell to some of the routines what can be done with this
+ * item in the list.
+ * With IF_BOND a bonded interaction will be calculated.
+ * With IF_BTYPE grompp can convert the bond to a Morse potential.
+ * With IF_BTYPE or IF_ATYPE the bond/angle can be converted to
+ * a constraint or used for vsite parameter determination by grompp.
+ * IF_LIMZERO indicates that for a bonded interaction the potential
+ * does goes to zero for large distances, thus if such an interaction
+ * it not assigned to any node by the domain decompostion, the simulation
+ * still continue, if mdrun has been told so.
+ */
+
+struct t_interaction_function // NOLINT (clang-analyzer-optin.performance.Padding)
+{
+    const char* name;         /* the name of this function                     */
+    const char* longname;     /* The name for printing etc.                   */
+    int         nratoms;      /* nr of atoms needed for this function          */
+    int         nrfpA, nrfpB; /* number of parameters for this function.      */
+                              /* this corresponds to the number of params in  */
+                              /* iparams struct! (see idef.h)                 */
+    /* A and B are for normal and free energy components respectively.    */
+    unsigned int flags; /* Flags (see above)                            */
+};
+
+#define NRFPA(ftype) (interaction_function[(ftype)].nrfpA)
+#define NRFPB(ftype) (interaction_function[(ftype)].nrfpB)
+#define NRFP(ftype) (NRFPA(ftype) + NRFPB(ftype))
+#define NRAL(ftype) (interaction_function[(ftype)].nratoms)
+
+#define IS_CHEMBOND(ftype) \
+    (interaction_function[(ftype)].nratoms == 2 && (interaction_function[(ftype)].flags & IF_CHEMBOND))
+/* IS_CHEMBOND tells if function type ftype represents a chemical bond */
+
+/* IS_ANGLE tells if a function type ftype represents an angle
+ * Per Larsson, 2007-11-06
+ */
+#define IS_ANGLE(ftype) \
+    (interaction_function[(ftype)].nratoms == 3 && (interaction_function[(ftype)].flags & IF_ATYPE))
+#define IS_VSITE(ftype) (interaction_function[(ftype)].flags & IF_VSITE)
+
+#define IS_TABULATED(ftype) (interaction_function[(ftype)].flags & IF_TABULATED)
+
+/* this MUST correspond to the
+   t_interaction_function[F_NRE] in gmxlib/ifunc.cpp */
+enum
+{
+    F_BONDS,
+    F_G96BONDS,
+    F_MORSE,
+    F_CUBICBONDS,
+    F_CONNBONDS,
+    F_HARMONIC,
+    F_FENEBONDS,
+    F_TABBONDS,
+    F_TABBONDSNC,
+    F_RESTRBONDS,
+    F_ANGLES,
+    F_G96ANGLES,
+    F_RESTRANGLES,
+    F_LINEAR_ANGLES,
+    F_CROSS_BOND_BONDS,
+    F_CROSS_BOND_ANGLES,
+    F_UREY_BRADLEY,
+    F_QUARTIC_ANGLES,
+    F_TABANGLES,
+    F_PDIHS,
+    F_RBDIHS,
+    F_RESTRDIHS,
+    F_CBTDIHS,
+    F_FOURDIHS,
+    F_IDIHS,
+    F_PIDIHS,
+    F_TABDIHS,
+    F_CMAP,
+    F_GB12_NOLONGERUSED,
+    F_GB13_NOLONGERUSED,
+    F_GB14_NOLONGERUSED,
+    F_GBPOL_NOLONGERUSED,
+    F_NPSOLVATION_NOLONGERUSED,
+    F_LJ14,
+    F_COUL14,
+    F_LJC14_Q,
+    F_LJC_PAIRS_NB,
+    F_LJ,
+    F_BHAM,
+    F_LJ_LR_NOLONGERUSED,
+    F_BHAM_LR_NOLONGERUSED,
+    F_DISPCORR,
+    F_COUL_SR,
+    F_COUL_LR_NOLONGERUSED,
+    F_RF_EXCL,
+    F_COUL_RECIP,
+    F_LJ_RECIP,
+    F_DPD,
+    F_POLARIZATION,
+    F_WATER_POL,
+    F_THOLE_POL,
+    F_ANHARM_POL,
+    F_POSRES,
+    F_FBPOSRES,
+    F_DISRES,
+    F_DISRESVIOL,
+    F_ORIRES,
+    F_ORIRESDEV,
+    F_ANGRES,
+    F_ANGRESZ,
+    F_DIHRES,
+    F_DIHRESVIOL,
+    F_CONSTR,
+    F_CONSTRNC,
+    F_SETTLE,
+    F_VSITE1,
+    F_VSITE2,
+    F_VSITE2FD,
+    F_VSITE3,
+    F_VSITE3FD,
+    F_VSITE3FAD,
+    F_VSITE3OUT,
+    F_VSITE4FD,
+    F_VSITE4FDN,
+    F_VSITEN,
+    F_COM_PULL,
+    F_DENSITYFITTING,
+    F_EQM,
+    F_EPOT,
+    F_EKIN,
+    F_ETOT,
+    F_ECONSERVED,
+    F_TEMP,
+    F_VTEMP_NOLONGERUSED,
+    F_PDISPCORR,
+    F_PRES,
+    F_DVDL_CONSTR,
+    F_DVDL,
+    F_DKDL,
+    F_DVDL_COUL,
+    F_DVDL_VDW,
+    F_DVDL_BONDED,
+    F_DVDL_RESTRAINT,
+    F_DVDL_TEMPERATURE, /* not calculated for now, but should just be the energy (NVT) or enthalpy (NPT), or 0 (NVE) */
+    F_NRE /* This number is for the total number of energies      */
+};
+
+static inline bool IS_RESTRAINT_TYPE(int ifunc)
+{
+    return ifunc == F_POSRES || ifunc == F_FBPOSRES || ifunc == F_DISRES || ifunc == F_RESTRBONDS
+           || ifunc == F_DISRESVIOL || ifunc == F_ORIRES || ifunc == F_ORIRESDEV
+           || ifunc == F_ANGRES || ifunc == F_ANGRESZ || ifunc == F_DIHRES;
+}
+
+/* Maximum allowed number of atoms, parameters and terms in interaction_function.
+ * Check kernel/toppush.c when you change these numbers.
+ */
+constexpr int MAXATOMLIST   = 6;
+constexpr int MAXFORCEPARAM = 12;
+constexpr int NR_RBDIHS     = 6;
+constexpr int NR_CBTDIHS    = 6;
+constexpr int NR_FOURDIHS   = 4;
+
+extern const t_interaction_function interaction_function[F_NRE];
+/* initialised interaction functions descriptor                                */
+
+#endif
diff --git a/src/include/gromacs/topology/index.h b/src/include/gromacs/topology/index.h
new file mode 100644 (file)
index 0000000..638c0b6
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOPOLOGY_INDEX_H
+#define GMX_TOPOLOGY_INDEX_H
+
+#include <stdio.h>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+
+struct t_atoms;
+struct t_blocka;
+
+void check_index(const char* gname, int n, int index[], const char* traj, int natoms);
+/* Checks if any index is smaller than zero or larger than natoms,
+ * if so a fatal_error is given with the gname (if gname=NULL, "Index" is used)
+ * and traj (if traj=NULL, "the trajectory" is used).
+ */
+
+struct t_blocka* init_index(const char* gfile, char*** grpname);
+/* Lower level routine than the next */
+
+void rd_index(const char* statfile, int ngrps, int isize[], int* index[], char* grpnames[]);
+/* Assume the group file is generated, so the
+ * format need not be user-friendly. The format is:
+ * nr of groups, total nr of atoms
+ * for each group: name nr of element, elements.
+ *
+ * The function opens a file, reads ngrps groups, asks the
+ * user for group numbers, and puts the resulting sizes in
+ * isize, the int s in index and the names of
+ * the groups in grpnames.
+ *
+ * It is also assumed, that when ngrps groups are requested
+ * memory has been allocated for ngrps index arrays, and that
+ * the dimension of the isize and grpnames arrays are ngrps.
+ */
+
+void get_index(const t_atoms* atoms, const char* fnm, int ngrps, int isize[], int* index[], char* grpnames[]);
+/* Does the same as rd_index, but if the fnm pointer is NULL it
+ * will not read from fnm, but it will make default index groups
+ * for the atoms in *atoms.
+ */
+
+typedef struct
+{
+    int              maxframe;
+    char**           grpname;
+    struct t_blocka* clust;
+    int*             inv_clust;
+} t_cluster_ndx;
+
+t_cluster_ndx* cluster_index(FILE* fplog, const char* ndx);
+
+
+void write_index(const char* outf, struct t_blocka* b, char** gnames, gmx_bool bDuplicate, int natoms);
+/* Writes index blocks to outf (writes an indexfile) */
+
+/*! \brief
+ * Add a new group with \p name to \p b.
+ *
+ * \param[in] b Block struct to add group to.
+ * \param[in] gnames Names of groups.
+ * \param[in] a Group to add to Block.
+ * \param[in] name Group name.
+ */
+void add_grp(struct t_blocka* b, char*** gnames, gmx::ArrayRef<const int> a, const std::string& name);
+/* Ads group a with name name to block b and namelist gnames */
+
+void analyse(const t_atoms* atoms, struct t_blocka* gb, char*** gn, gmx_bool bASK, gmx_bool bVerb);
+/* Makes index groups gb with names gn for atoms in atoms.
+ * bASK=FALSE gives default groups.
+ */
+
+/*! \brief Look up a group in a list.
+ *
+ * \param[inout] s    The string to look up
+ * \param[in] ngrps   The number of groups
+ * \param[in] grpname The names of the groups
+ * \return the group number or -1 if not found.
+ */
+int find_group(const char* s, int ngrps, char** grpname);
+
+
+#endif
diff --git a/src/include/gromacs/topology/invblock.h b/src/include/gromacs/topology/invblock.h
new file mode 100644 (file)
index 0000000..33494e5
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOPOLOGY_INVBLOCK_H
+#define GMX_TOPOLOGY_INVBLOCK_H
+
+
+struct t_block;
+struct t_blocka;
+
+int* make_invblock(const struct t_block* block, int nr);
+/* Inverse the block structure. nr is the maximum entry in the inversed
+ * array, and therefore the dimension of the returned array
+ */
+
+int* make_invblocka(const struct t_blocka* block, int nr);
+/* Inverse the block structure. nr is the maximum entry in the inversed
+ * array, and therefore the dimension of the returned array
+ */
+
+#endif
diff --git a/src/include/gromacs/topology/mtop_lookup.h b/src/include/gromacs/topology/mtop_lookup.h
new file mode 100644 (file)
index 0000000..8b9c4be
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ *
+ * \brief This file contains inline functions to look up atom information
+ * using the global atom index.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_mtop
+ */
+
+#ifndef GMX_TOPOLOGY_MTOP_LOOKUP_H
+#define GMX_TOPOLOGY_MTOP_LOOKUP_H
+
+#include "gromacs/topology/topology.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+
+struct t_atom;
+
+// TODO All of the functions taking a const gmx_mtop * are deprecated
+// and should be replaced by versions taking const gmx_mtop & when
+// their callers are refactored similarly.
+
+/*! \brief Look up the molecule block and other indices of a global atom index
+ *
+ * The atom index has to be in range: 0 <= \p globalAtomIndex < \p mtop->natoms.
+ * The input value of moleculeBlock should be in range. Use 0 as starting value.
+ * For subsequent calls to this function, e.g. in a loop, pass in the previously
+ * returned value for best performance. Atoms in a group tend to be in the same
+ * molecule(block), so this minimizes the search time.
+ *
+ * \param[in]     mtop                 The molecule topology
+ * \param[in]     globalAtomIndex      The global atom index to look up
+ * \param[in,out] moleculeBlock        The molecule block index in \p mtop
+ * \param[out]    moleculeIndex        The index of the molecule in the block, can be NULL
+ * \param[out]    atomIndexInMolecule  The atom index in the molecule, can be NULL
+ */
+static inline void mtopGetMolblockIndex(const gmx_mtop_t& mtop,
+                                        int               globalAtomIndex,
+                                        int*              moleculeBlock,
+                                        int*              moleculeIndex,
+                                        int*              atomIndexInMolecule)
+{
+    GMX_ASSERT(globalAtomIndex >= 0, "The atom index to look up should not be negative");
+    GMX_ASSERT(globalAtomIndex < mtop.natoms, "The atom index to look up should be within range");
+    GMX_ASSERT(moleculeBlock != nullptr, "molBlock can not be NULL");
+    GMX_ASSERT(!mtop.moleculeBlockIndices.empty(), "The moleculeBlockIndices should not be empty");
+    GMX_ASSERT(*moleculeBlock >= 0,
+               "The starting molecule block index for the search should not be negative");
+    GMX_ASSERT(*moleculeBlock < gmx::ssize(mtop.moleculeBlockIndices),
+               "The starting molecule block index for the search should be within range");
+
+    /* Search the molecule block index using bisection */
+    int molBlock0 = -1;
+    int molBlock1 = mtop.molblock.size();
+
+    int globalAtomStart = 0;
+    while (TRUE)
+    {
+        globalAtomStart = mtop.moleculeBlockIndices[*moleculeBlock].globalAtomStart;
+        if (globalAtomIndex < globalAtomStart)
+        {
+            molBlock1 = *moleculeBlock;
+        }
+        else if (globalAtomIndex >= mtop.moleculeBlockIndices[*moleculeBlock].globalAtomEnd)
+        {
+            molBlock0 = *moleculeBlock;
+        }
+        else
+        {
+            break;
+        }
+        *moleculeBlock = ((molBlock0 + molBlock1 + 1) >> 1);
+    }
+
+    int molIndex = (globalAtomIndex - globalAtomStart)
+                   / mtop.moleculeBlockIndices[*moleculeBlock].numAtomsPerMolecule;
+    if (moleculeIndex != nullptr)
+    {
+        *moleculeIndex = molIndex;
+    }
+    if (atomIndexInMolecule != nullptr)
+    {
+        *atomIndexInMolecule = globalAtomIndex - globalAtomStart
+                               - molIndex * mtop.moleculeBlockIndices[*moleculeBlock].numAtomsPerMolecule;
+    }
+}
+
+/*! \brief Returns the global molecule index of a global atom index
+ *
+ * The atom index has to be in range: 0 <= \p globalAtomIndex < \p mtop->natoms.
+ * The input value of moleculeBlock should be in range. Use 0 as starting value.
+ * For subsequent calls to this function, e.g. in a loop, pass in the previously
+ * returned value for best performance. Atoms in a group tend to be in the same
+ * molecule(block), so this minimizes the search time.
+ *
+ * \param[in]     mtop                 The molecule topology
+ * \param[in]     globalAtomIndex      The global atom index to look up
+ * \param[in,out] moleculeBlock        The molecule block index in \p mtop
+ */
+static inline int mtopGetMoleculeIndex(const gmx_mtop_t& mtop, int globalAtomIndex, int* moleculeBlock)
+{
+    int localMoleculeIndex = 0;
+    mtopGetMolblockIndex(mtop, globalAtomIndex, moleculeBlock, &localMoleculeIndex, nullptr);
+
+    return mtop.moleculeBlockIndices[*moleculeBlock].moleculeIndexStart + localMoleculeIndex;
+}
+
+/*! \brief Returns the atom data for an atom based on global atom index
+ *
+ * The atom index has to be in range: 0 <= \p globalAtomIndex < \p mtop->natoms.
+ * The input value of moleculeBlock should be in range. Use 0 as starting value.
+ * For subsequent calls to this function, e.g. in a loop, pass in the previously
+ * returned value for best performance. Atoms in a group tend to be in the same
+ * molecule(block), so this minimizes the search time.
+ *
+ * \param[in]     mtop                 The molecule topology
+ * \param[in]     globalAtomIndex      The global atom index to look up
+ * \param[in,out] moleculeBlock        The molecule block index in \p mtop
+ */
+static inline const t_atom& mtopGetAtomParameters(const gmx_mtop_t& mtop, int globalAtomIndex, int* moleculeBlock)
+{
+    int atomIndexInMolecule = 0;
+    mtopGetMolblockIndex(mtop, globalAtomIndex, moleculeBlock, nullptr, &atomIndexInMolecule);
+    const gmx_moltype_t& moltype = mtop.moltype[mtop.molblock[*moleculeBlock].type];
+    return moltype.atoms.atom[atomIndexInMolecule];
+}
+
+/*! \brief Returns the mass of an atom based on global atom index
+ *
+ * Returns that A-state mass of the atom with global index \p globalAtomIndex.
+ * The atom index has to be in range: 0 <= \p globalAtomIndex < \p mtop->natoms.
+ * The input value of moleculeBlock should be in range. Use 0 as starting value.
+ * For subsequent calls to this function, e.g. in a loop, pass in the previously
+ * returned value for best performance. Atoms in a group tend to be in the same
+ * molecule(block), so this minimizes the search time.
+ *
+ * \param[in]     mtop                 The molecule topology
+ * \param[in]     globalAtomIndex      The global atom index to look up
+ * \param[in,out] moleculeBlock        The molecule block index in \p mtop
+ */
+static inline real mtopGetAtomMass(const gmx_mtop_t& mtop, int globalAtomIndex, int* moleculeBlock)
+{
+    const t_atom& atom = mtopGetAtomParameters(mtop, globalAtomIndex, moleculeBlock);
+    return atom.m;
+}
+
+/*! \brief Look up the atom and residue name and residue number and index of a global atom index
+ *
+ * The atom index has to be in range: 0 <= \p globalAtomIndex < \p mtop->natoms.
+ * The input value of moleculeBlock should be in range. Use 0 as starting value.
+ * For subsequent calls to this function, e.g. in a loop, pass in the previously
+ * returned value for best performance. Atoms in a group tend to be in the same
+ * molecule(block), so this minimizes the search time.
+ * Note that this function does a (somewhat expensive) lookup. If you want
+ * to look up data sequentially for all atoms in a molecule or the system,
+ * use one of the mtop loop functionalities.
+ *
+ * \param[in]     mtop                The molecule topology
+ * \param[in]     globalAtomIndex     The global atom index to look up
+ * \param[in,out] moleculeBlock       The molecule block index in \p mtop
+ * \param[out]    atomName            The atom name, input can be NULL
+ * \param[out]    residueNumber       The residue number, input can be NULL
+ * \param[out]    residueName         The residue name, input can be NULL
+ * \param[out]    globalResidueIndex  The gobal residue index, input can be NULL
+ */
+static inline void mtopGetAtomAndResidueName(const gmx_mtop_t& mtop,
+                                             int               globalAtomIndex,
+                                             int*              moleculeBlock,
+                                             const char**      atomName,
+                                             int*              residueNumber,
+                                             const char**      residueName,
+                                             int*              globalResidueIndex)
+{
+    int moleculeIndex       = 0;
+    int atomIndexInMolecule = 0;
+    mtopGetMolblockIndex(mtop, globalAtomIndex, moleculeBlock, &moleculeIndex, &atomIndexInMolecule);
+
+    const gmx_molblock_t&       molb    = mtop.molblock[*moleculeBlock];
+    const t_atoms&              atoms   = mtop.moltype[molb.type].atoms;
+    const MoleculeBlockIndices& indices = mtop.moleculeBlockIndices[*moleculeBlock];
+    if (atomName != nullptr)
+    {
+        *atomName = *(atoms.atomname[atomIndexInMolecule]);
+    }
+    if (residueNumber != nullptr)
+    {
+        if (atoms.nres > mtop.maxResiduesPerMoleculeToTriggerRenumber())
+        {
+            *residueNumber = atoms.resinfo[atoms.atom[atomIndexInMolecule].resind].nr;
+        }
+        else
+        {
+            /* Single residue molecule, keep counting */
+            *residueNumber = indices.residueNumberStart + moleculeIndex * atoms.nres
+                             + atoms.atom[atomIndexInMolecule].resind;
+        }
+    }
+    if (residueName != nullptr)
+    {
+        *residueName = *(atoms.resinfo[atoms.atom[atomIndexInMolecule].resind].name);
+    }
+    if (globalResidueIndex != nullptr)
+    {
+        *globalResidueIndex = indices.globalResidueStart + moleculeIndex * atoms.nres
+                              + atoms.atom[atomIndexInMolecule].resind;
+    }
+}
+
+/*! \brief Returns residue information for an atom based on global atom index
+ *
+ * The atom index has to be in range: 0 <= \p globalAtomIndex < \p mtop->natoms.
+ * The input value of moleculeBlock should be in range. Use 0 as starting value.
+ * For subsequent calls to this function, e.g. in a loop, pass in the previously
+ * returned value for best performance. Atoms in a group tend to be in the same
+ * molecule(block), so this minimizes the search time.
+ *
+ * \param[in]     mtop                 The molecule topology
+ * \param[in]     globalAtomIndex      The global atom index to look up
+ * \param[in,out] moleculeBlock        The molecule block index in \p mtop
+ */
+static inline const t_resinfo& mtopGetResidueInfo(const gmx_mtop_t& mtop, int globalAtomIndex, int* moleculeBlock)
+{
+    int atomIndexInMolecule = 0;
+    mtopGetMolblockIndex(mtop, globalAtomIndex, moleculeBlock, nullptr, &atomIndexInMolecule);
+    const gmx_moltype_t& moltype = mtop.moltype[mtop.molblock[*moleculeBlock].type];
+    const int            resind  = moltype.atoms.atom[atomIndexInMolecule].resind;
+    return moltype.atoms.resinfo[resind];
+}
+
+/*! \brief Returns PDB information for an atom based on global atom index
+ *
+ * The atom index has to be in range: 0 <= \p globalAtomIndex < \p mtop->natoms.
+ * The input value of moleculeBlock should be in range. Use 0 as starting value.
+ * For subsequent calls to this function, e.g. in a loop, pass in the previously
+ * returned value for best performance. Atoms in a group tend to be in the same
+ * molecule(block), so this minimizes the search time.
+ *
+ * \param[in]     mtop                 The molecule topology
+ * \param[in]     globalAtomIndex      The global atom index to look up
+ * \param[in,out] moleculeBlock        The molecule block index in \p mtop
+ */
+static inline const t_pdbinfo& mtopGetAtomPdbInfo(const gmx_mtop_t& mtop, int globalAtomIndex, int* moleculeBlock)
+{
+    int atomIndexInMolecule = 0;
+    mtopGetMolblockIndex(mtop, globalAtomIndex, moleculeBlock, nullptr, &atomIndexInMolecule);
+    const gmx_moltype_t& moltype = mtop.moltype[mtop.molblock[*moleculeBlock].type];
+    GMX_ASSERT(moltype.atoms.havePdbInfo, "PDB information not present when requested");
+    return moltype.atoms.pdbinfo[atomIndexInMolecule];
+}
+
+#endif
diff --git a/src/include/gromacs/topology/mtop_util.h b/src/include/gromacs/topology/mtop_util.h
new file mode 100644 (file)
index 0000000..22c90e0
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOPOLOGY_MTOP_UTIL_H
+#define GMX_TOPOLOGY_MTOP_UTIL_H
+
+#include <cstddef>
+
+#include <array>
+#include <vector>
+
+#include <boost/stl_interfaces/iterator_interface.hpp>
+
+#include "gromacs/topology/topology.h"
+#include "gromacs/utility/enumerationhelpers.h"
+
+struct gmx_localtop_t;
+struct t_atom;
+struct t_atoms;
+struct t_block;
+struct t_symtab;
+
+// TODO All of the functions taking a const gmx_mtop * are deprecated
+// and should be replaced by versions taking const gmx_mtop & when
+// their callers are refactored similarly.
+
+/* Counts the number of atoms of each type. State should be 0 for
+ * state A and 1 for state B types.  typecount should have at
+ * least mtop->ffparams.atnr elements.
+ */
+void gmx_mtop_count_atomtypes(const gmx_mtop_t& mtop, int state, int typecount[]);
+
+/*!\brief Returns the total number of molecules in mtop
+ *
+ * \param[in] mtop  The global topology
+ */
+int gmx_mtop_num_molecules(const gmx_mtop_t& mtop);
+
+/* Returns the total number of residues in mtop. */
+int gmx_mtop_nres(const gmx_mtop_t& mtop);
+
+class AtomIterator;
+
+//! Proxy object returned from AtomIterator
+class AtomProxy
+{
+public:
+    //! Default constructor.
+    AtomProxy(const AtomIterator* it) : it_(it) {}
+    //! Access current global atom number.
+    int globalAtomNumber() const;
+    //! Access current t_atom struct.
+    const t_atom& atom() const;
+    //! Access current name of the atom.
+    const char* atomName() const;
+    //! Access current name of the residue the atom is in.
+    const char* residueName() const;
+    //! Access current residue number.
+    int residueNumber() const;
+    //! Access current molecule type.
+    const gmx_moltype_t& moleculeType() const;
+    //! Access the position of the current atom in the molecule.
+    int atomNumberInMol() const;
+
+private:
+    const AtomIterator* it_;
+};
+
+/*! \brief
+ * Object that allows looping over all atoms in an mtop.
+ */
+class AtomIterator :
+    public boost::stl_interfaces::proxy_iterator_interface<AtomIterator, std::forward_iterator_tag, t_atom, AtomProxy>
+{
+    using Base =
+            boost::stl_interfaces::proxy_iterator_interface<AtomIterator, std::forward_iterator_tag, t_atom, AtomProxy>;
+
+public:
+    //! Construct from topology and optionalally a global atom number.
+    explicit AtomIterator(const gmx_mtop_t& mtop, int globalAtomNumber = 0);
+
+    //! Prefix increment.
+    AtomIterator& operator++();
+    using Base::  operator++;
+
+    //! Equality comparison.
+    bool operator==(const AtomIterator& o) const;
+
+    //! Dereference operator. Returns proxy.
+    AtomProxy operator*() const { return { this }; }
+
+private:
+    //! Global topology.
+    const gmx_mtop_t* mtop_;
+    //! Current molecule block.
+    size_t mblock_;
+    //! The atoms of the current molecule.
+    const t_atoms* atoms_;
+    //! The current molecule.
+    int currentMolecule_;
+    //! Current highest number for residues.
+    int highestResidueNumber_;
+    //! Current local atom number.
+    int localAtomNumber_;
+    //! Global current atom number.
+    int globalAtomNumber_;
+
+    friend class AtomProxy;
+};
+
+//! Range over all atoms of topology.
+class AtomRange
+{
+public:
+    //! Default constructor.
+    explicit AtomRange(const gmx_mtop_t& mtop) : begin_(mtop), end_(mtop, mtop.natoms) {}
+    //! Iterator to begin of range.
+    AtomIterator& begin() { return begin_; }
+    //! Iterator to end of range.
+    AtomIterator& end() { return end_; }
+
+private:
+    AtomIterator begin_, end_;
+};
+
+class IListIterator;
+
+//! Proxy object returned from IListIterator
+class IListProxy
+{
+public:
+    //! Default constructor.
+    IListProxy(const IListIterator* it) : it_(it) {}
+    //! Access current global atom number.
+    const InteractionLists& list() const;
+    //! Access current molecule.
+    int nmol() const;
+
+private:
+    const IListIterator* it_;
+};
+
+/*! \brief
+ * Object that allows looping over all atoms in an mtop.
+ */
+class IListIterator :
+    public boost::stl_interfaces::proxy_iterator_interface<IListIterator, std::forward_iterator_tag, InteractionLists, IListProxy>
+{
+    using Base =
+            boost::stl_interfaces::proxy_iterator_interface<IListIterator, std::forward_iterator_tag, InteractionLists, IListProxy>;
+
+public:
+    //! Construct from topology.
+    explicit IListIterator(const gmx_mtop_t& mtop, size_t mblock = 0);
+
+    //! Prefix increment.
+    IListIterator& operator++();
+    using Base::   operator++;
+
+    //! Equality comparison.
+    bool operator==(const IListIterator& o) const;
+
+    //! Dereference operator. Returns proxy.
+    IListProxy operator*() const { return { this }; }
+
+private:
+    //! Global topology.
+    const gmx_mtop_t* mtop_;
+    //! Index of molecule block corresponding to the current location.
+    size_t mblock_;
+
+    friend class IListProxy;
+};
+
+
+/*! \brief
+ * Range over all interaction lists of topology.
+ *
+ * Includes the intermolecular interactions as the final element in the
+ * range if present.
+ */
+class IListRange
+{
+public:
+    //! Default constructor.
+    explicit IListRange(const gmx_mtop_t& mtop);
+    //! Iterator to begin of range.
+    IListIterator& begin() { return begin_; }
+    //! Iterator to end of range.
+    IListIterator& end() { return end_; }
+
+private:
+    IListIterator begin_, end_;
+};
+
+/* Abstract type for atom loop over atoms in all molecule blocks */
+typedef struct gmx_mtop_atomloop_block* gmx_mtop_atomloop_block_t;
+
+/* Initialize an atom loop over atoms in all molecule blocks the system.
+ */
+gmx_mtop_atomloop_block_t gmx_mtop_atomloop_block_init(const gmx_mtop_t& mtop);
+
+/* Loop to the next atom.
+ * When not at the end:
+ *   returns TRUE
+ *   sets the pointer atom to the t_atom struct of that atom
+ *   and return the number of molecules corresponding to this atom.
+ * When at the end, destroys aloop and returns FALSE.
+ * Use as:
+ * gmx_mtop_atomloop_block_t aloop;
+ * aloop = gmx_mtop_atomloop_block_init(mtop)
+ * while (gmx_mtop_atomloop_block_next(aloop,&atom,&nmol)) {
+ *     ...
+ * }
+ */
+gmx_bool gmx_mtop_atomloop_block_next(gmx_mtop_atomloop_block_t aloop, const t_atom** atom, int* nmol);
+
+
+/* Returns the total number of interactions in the system of type ftype */
+int gmx_mtop_ftype_count(const gmx_mtop_t& mtop, int ftype);
+
+/* Returns the total number of interactions in the system with all interaction flags that are set in \p if_flags set */
+int gmx_mtop_interaction_count(const gmx_mtop_t& mtop, int unsigned if_flags);
+
+/* Returns the count of atoms for each particle type */
+gmx::EnumerationArray<ParticleType, int> gmx_mtop_particletype_count(const gmx_mtop_t& mtop);
+
+/* Returns a single t_atoms struct for the whole system */
+t_atoms gmx_mtop_global_atoms(const gmx_mtop_t& mtop);
+
+/*! \brief Return whether the atom with the given index is within a
+ * 1-4 interaction within the given molecule type and its charge is
+ * perturbed.
+ *
+ * Some 1-4 interactions like F_COUL14 have the charges stored in the
+ * iparams list, but others do not. The commonly used F_LJ14 gets its
+ * charges from the topology, so we need more detailed checks for it,
+ * when FEP is active.
+ *
+ * \param[in] atomIndex  Index within range [0, molt.nr)
+ * \param[in] molt       Moleculetype to consider
+ * \return               Whether this atom is in a 1-4 interaction and charge is perturbed
+ * */
+bool atomHasPerturbedChargeIn14Interaction(int atomIndex, const gmx_moltype_t& molt);
+
+/*! \brief
+ * Populate a 'local' topology for the whole system.
+ *
+ * When freeEnergyInteractionsAtEnd == true, the free energy interactions will
+ * be sorted to the end.
+ *
+ * \param[in]     mtop                        The global topology used to populate the local one.
+ * \param[in,out] top                         New local topology populated from global \p mtop.
+ * \param[in]     freeEnergyInteractionsAtEnd If free energy interactions will be sorted.
+ */
+void gmx_mtop_generate_local_top(const gmx_mtop_t& mtop, gmx_localtop_t* top, bool freeEnergyInteractionsAtEnd);
+
+
+/*!\brief Creates and returns a struct with begin/end atom indices of all molecules
+ *
+ * \param[in] mtop  The global topology
+ * \returns A RangePartitioning object with numBlocks() equal to the number
+ * of molecules and atom indices such that molecule m contains atoms a with:
+ * index[m] <= a < index[m+1].
+ */
+gmx::RangePartitioning gmx_mtop_molecules(const gmx_mtop_t& mtop);
+
+/*! \brief
+ * Returns the index range from residue begin to end for each residue in a molecule block.
+ *
+ * Note that residues will always have consecutive atoms numbers internally.
+ *
+ * \param[in] moltype  Molecule Type to parse for start and end.
+ * \returns Vector of ranges for all residues.
+ */
+std::vector<gmx::Range<int>> atomRangeOfEachResidue(const gmx_moltype_t& moltype);
+
+/* Converts a gmx_mtop_t struct to t_topology.
+ *
+ * If the lifetime of the returned topology should be longer than that
+ * of mtop, your need to pass freeMtop==true.
+ * If freeMTop == true, memory related to mtop will be freed so that done_top()
+ * on the result value will free all memory.
+ * If freeMTop == false, mtop and the return value will share some of their
+ * memory, and there is currently no way to consistently free all the memory.
+ */
+t_topology gmx_mtop_t_to_t_topology(gmx_mtop_t* mtop, bool freeMTop);
+
+/*! \brief Get vector of atoms indices from topology
+ *
+ * This function returns the indices of all particles with type
+ * eptAtom, that is shells, vsites etc. are left out.
+ * \param[in]  mtop Molecular topology
+ * \returns Vector that will be filled with the atom indices
+ */
+std::vector<int> get_atom_index(const gmx_mtop_t& mtop);
+
+/*! \brief Converts a t_atoms struct to an mtop struct
+ *
+ * All pointers contained in \p atoms will be copied into \p mtop.
+ * Note that this will produce one moleculetype encompassing the whole system.
+ *
+ * \param[in]  symtab  The symbol table
+ * \param[in]  name    Pointer to the name for the topology
+ * \param[in]  atoms   The atoms to convert
+ * \param[out] mtop    The molecular topology output containing atoms.
+ */
+void convertAtomsToMtop(t_symtab* symtab, char** name, t_atoms* atoms, gmx_mtop_t* mtop);
+
+//! Checks and returns whether non-bonded interactions are perturbed for free-energy calculations
+bool haveFepPerturbedNBInteractions(const gmx_mtop_t& mtop);
+
+//! Checks whether masses are perturbed for free-energy calculations
+bool haveFepPerturbedMasses(const gmx_mtop_t& mtop);
+
+//! Checks whether masses are perturbed for free-energy calculations in SETTLE interactions
+bool haveFepPerturbedMassesInSettles(const gmx_mtop_t& mtop);
+
+//! Checks whether constraints are perturbed for free-energy calculations
+bool havePerturbedConstraints(const gmx_mtop_t& mtop);
+
+#endif
diff --git a/src/include/gromacs/topology/residuetypes.h b/src/include/gromacs/topology/residuetypes.h
new file mode 100644 (file)
index 0000000..d18e099
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2014,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOPOLOGY_RESIDUETYPES_H
+#define GMX_TOPOLOGY_RESIDUETYPES_H
+
+#include <string>
+#include <unordered_map>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/stringutil.h"
+
+/*! \brief Convenience type aliases
+ *
+ * These are not as useful as strong types, but they will
+ * help clarify usage to humans in some cases. */
+//! \{
+using ResidueName = std::string;
+using ResidueType = std::string;
+//! \}
+
+/*! \brief Maps residue names to residue types
+ *
+ * The contents are typically loaded from share/top/residuetypes.dat
+ * or similar file provided in the users's working directory.
+ */
+using ResidueTypeMap =
+        std::unordered_map<ResidueName, ResidueType, std::hash<ResidueName>, gmx::EqualCaseInsensitive>;
+
+/*! \brief
+ * Add entry to ResidueTypeMap if unique.
+ *
+ * \param[in] residueTypeMap Map to which to add new name+type entry
+ * \param[in] residueName    Name of new residue.
+ * \param[in] residueType    Type of new residue.
+ */
+void addResidue(ResidueTypeMap* residueTypeMap, const ResidueName& residueName, const ResidueType& residueType);
+
+/*! \brief Returns a ResidueTypeMap filled from a file
+ *
+ * The value of the parameter is typically "residuetypes.dat" which
+ * treats that as a GROMACS library file, ie. loads it from the working
+ * directory or from "share/top" corresponding to the sourced GMXRC.
+ *
+ * \param[in] residueTypesDatFilename Library file to read and from which to fill the returned map
+ */
+ResidueTypeMap residueTypeMapFromLibraryFile(const std::string& residueTypesDatFilename);
+
+/*! \brief
+ * Checks if the indicated \p residueName is of \p residueType.
+ *
+ * \param[in] residueTypeMap Map to search
+ * \param[in] residueName    Residue that should be checked.
+ * \param[in] residueType    Which ResidueType the residue should have.
+ * \returns If the check was successful.
+ */
+bool namedResidueHasType(const ResidueTypeMap& residueTypeMap,
+                         const ResidueName&    residueName,
+                         const ResidueType&    residueType);
+
+/*! \brief
+ * Return the residue type if a residue with that name exists, or "Other"
+ *
+ * \param[in] residueTypeMap Map to search
+ * \param[in] residueName    Name of the residue to search for.
+ * \returns The residue type of any matching residue, or "Other"
+ */
+ResidueType typeOfNamedDatabaseResidue(const ResidueTypeMap& residueTypeMap, const ResidueName& residueName);
+
+#endif
diff --git a/src/include/gromacs/topology/symtab.h b/src/include/gromacs/topology/symtab.h
new file mode 100644 (file)
index 0000000..6ffbcdf
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares modern and legacy symbol table used to store strings of characters.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ *
+ * \ingroup module_topology
+ * \inlibraryapi
+ */
+#ifndef GMX_TOPOLOGY_SYMTAB_H
+#define GMX_TOPOLOGY_SYMTAB_H
+
+#include <stdio.h>
+
+#include <functional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+
+struct t_commrec;
+struct t_fileio;
+
+namespace gmx
+{
+class ISerializer;
+namespace test
+{
+class StringTableTest;
+} // namespace test
+} // namespace gmx
+
+//! Convenience typedef for pair stored in map.
+using StringTablePair = std::pair<std::string, int>;
+//! Convenience typedef for string reference wrapper.
+using StringReference = std::reference_wrapper<const std::string>;
+
+class StringTableBuilder;
+class StringTableEntry;
+/*! \brief
+ * A class to store strings for lookup.
+ *
+ * We store the strings in a dedicated object to avoid
+ * wrong usage of the flat string vector, and forcing people
+ * to use an object that can only be constructed from the transitional
+ * StringTableBuilder or filled during file IO.
+ *
+ * Note that strings are stripped of trailing and leading whitespace.
+ */
+class StringTable
+{
+public:
+    //! Constructor used to generate table object from file reading.
+    StringTable(gmx::ISerializer* serializer);
+    //! Can move construct.
+    StringTable(StringTable&&) = default;
+    //! Can move assign.
+    StringTable& operator=(StringTable&&) = default;
+    //! No copy constructor.
+    StringTable(const StringTable&) = delete;
+    //! No copy assign.
+    StringTable& operator=(const StringTable&) = delete;
+    /*! \brief
+     *  Access string at \p index.
+     *
+     *  \returns Entry type that constains both the string and the index,
+     *           with the index needed during (de-)serialization.
+     *  \throws  On index being out of range.
+     */
+    StringTableEntry at(gmx::index index) const;
+    //! Bracket operator.
+    StringTableEntry operator[](gmx::index index) const;
+    //! Handle file IO.
+    void serializeStringTable(gmx::ISerializer* serializer);
+
+    //! Print human readable format of storage.
+    void printStringTableStorageToFile(FILE* fp, int indent, const char* title) const;
+
+    /*! \brief
+     * Copy data in new datastructure to legacy version.
+     *
+     * The legacy datastructures need to be already initialized.
+     *
+     * \param[in] symtab Legacy symbol table to add entries to.
+     */
+    void copyToLegacySymtab(struct t_symtab* symtab) const;
+
+    friend class StringTableBuilder;
+
+private:
+    /*! \brief
+     * Private constructor so that only builder can create the final table.
+     *
+     * \param[in] table A vector of strings to be stored in the table.
+     */
+    StringTable(const std::vector<std::string>& table) : table_(table) {}
+
+    //! The table is stored as a vector of strings.
+    std::vector<std::string> table_;
+};
+
+/*! \brief
+ * Helper class to access members in StringTable.
+ *
+ * This class is a wrapper around a string reference to access
+ * the actual entry in the table, as well as an index used for
+ * serializing the datastructure.
+ *
+ * This also provides efficient comparison calls between different entries.
+ */
+class StringTableEntry
+{
+public:
+    //! Copy construct.
+    StringTableEntry(const StringTableEntry&) = default;
+    //! Move construct.
+    StringTableEntry(StringTableEntry&&) noexcept = default;
+    //! Copy assign.
+    StringTableEntry& operator=(const StringTableEntry&) = default;
+    //! Move assign.
+    StringTableEntry& operator=(StringTableEntry&&) = default;
+
+    //! Compare entries by indices. Same string should always have same index.
+    bool operator==(const StringTableEntry& o) const { return tableIndex_ == o.tableIndex_; }
+    //! Unequal comparisson.
+    bool operator!=(const StringTableEntry& o) const { return !(*this == o); }
+    //! Access to underlying view.
+    const std::string& operator*() const { return entry_; }
+    //! Access to underlying view.
+    const std::string* operator->() const { return &entry_.get(); }
+    //! Serialize index.
+    void serialize(gmx::ISerializer* serializer) const;
+
+    // We only allow construction from the places that are known to create
+    // valid objects for us.
+    friend StringTableEntry readStringTableEntry(gmx::ISerializer* serializer, const StringTable& table);
+    friend class StringTableBuilder;
+    friend class StringTable;
+
+private:
+    //! Only allow construct with all information present.
+    StringTableEntry(StringReference entry, int tableIndex) : entry_(entry), tableIndex_(tableIndex)
+    {
+    }
+    //! The actual string reference that is stored.
+    StringReference entry_;
+    //! The index into the table.
+    int tableIndex_ = -1;
+};
+
+/*! \brief
+ * De-serialize StringTableEntry using the index into the \p table.
+ *
+ * \param[in] serializer  The object containing the serialized index.
+ * \param[in] table       The storage object holding all strings.
+ * \returns The entry into the Table as StringTableEntry.
+ */
+StringTableEntry readStringTableEntry(gmx::ISerializer* serializer, const StringTable& table);
+
+/*! \libinternal \brief
+ * Builds a memory efficient storage for strings of characters.
+ *
+ * Allows storing strings of characters with unique entries.
+ */
+class StringTableBuilder
+{
+public:
+    /*! \brief
+     * Place new unique string in storage object.
+     *
+     * Enters new string into the underlying storage or recovers existing entry.
+     * \param[in] theString New string to enter.
+     * \returns New entry object with reference to string and index into storage.
+     *          The reference is only valid while the builder is in use, and becomes
+     *          invalidated when generating the StringTable.
+     */
+    StringTableEntry addString(const std::string& theString);
+    //! Find matching entry in storage by name as string.
+    int findEntryByName(const std::string& name) const;
+    /*! \brief
+     * Build the StringTable from the internal map of strings.
+     *
+     * The unique indices returned from addString() can be used
+     * to index into the returned StringTable. Clears the
+     * temporary storage so that the StringTableBuilder can be re-used to
+     * build a distinct StringTable.
+     */
+    StringTable build();
+
+private:
+    //! Storage object for entries.
+    std::unordered_map<std::string, int> map_;
+};
+
+// Below this is the legacy code for the old symbol table, only used in
+// deprecated datastructures.
+/*! \libinternal \brief
+ * Legacy symbol table entry as linked list.
+ */
+struct t_symbuf
+{
+    //! Number of entries in this item
+    int bufsize;
+    //! Storage for strings in this item.
+    char** buf;
+    //! Next item in linked list.
+    struct t_symbuf* next;
+};
+
+/* \libinternal \brief
+ * Legacy symbol table.
+ */
+struct t_symtab
+{
+    //! Total number of entries stored.
+    int nr;
+    //! First item in linked list of storage elements.
+    t_symbuf* symbuf;
+};
+
+/*
+ * This module handles symbol table manipulation. All text strings
+ * needed by an application are allocated only once. All references
+ * to these text strings use handles returned from the put_symtab()
+ * routine. These handles can easily be converted to address independent
+ * values by invoking lookup_symtab(). So when writing structures to
+ * a file which contains text strings, this value can be written in stead
+ * of the text string or its address. This value can easily be converted
+ * back to a text string handle by get_symtab_handle().
+ */
+
+//! Initialises the symbol table symtab.
+void open_symtab(t_symtab* symtab);
+
+/*! \brief
+ * Undoes the effect of open_symtab()
+ *
+ * After invoking this function, no value can be added to the
+ * symbol table, only values can be retrieved using get_symtab_handle().
+ *
+ * Note that this does no work.
+ * \param[inout] symtab Symbol table to close.
+ */
+void close_symtab(t_symtab* symtab);
+
+/*! \brief Returns a deep copy of \c symtab. */
+t_symtab* duplicateSymtab(const t_symtab* symtab);
+
+//! Frees the space allocated by the symbol table itself.
+void free_symtab(t_symtab* symtab);
+
+//! Frees the space allocated by the symbol table, including all entries in it.
+void done_symtab(t_symtab* symtab);
+
+/*! \brief
+ * Enters a string into the symbol table.
+ *
+ * If the string \p name was not present before, a reference to a copy is returned,
+ * else a reference to the earlier entered value is returned. Strings are trimmed of spaces.
+ *
+ * \param[inout] symtab Symbol table to add string to.
+ * \param[in] name String to add.
+ * \returns Pointer to entry of string in symtab.
+ */
+char** put_symtab(t_symtab* symtab, const char* name);
+
+/*! \brief
+ * Returns unique handle for \p name.
+ *
+ * Looks up the string pointer \p name in the symbol table and returns the
+ * index in it to the matching entry. Gives fatal error if \p name is
+ * not found. \p name has to be entered first using put_symtab().
+ *
+ * \param[in] symtab Symbol table to search.
+ * \param[in] name String pointer into \p symtab.
+ * \returns Unique index to position in symbol table.
+ */
+int lookup_symtab(t_symtab* symtab, char** name);
+
+/*! \brief
+ * Returns text string corresponding to \p index.
+ *
+ * \p index needs to be value obtained from call to lookup_symtab().
+ * get_symtab_handle() and lookup_symtab() are inverse functions.
+ *
+ * \param[in] symtab Symbol table to search.
+ * \param[in] index  Entry to find in table.
+ * \returns String pointer into \p symtab corresponding to the entry.
+ */
+char** get_symtab_handle(t_symtab* symtab, int index);
+
+/*! \brief
+ * Prints human readable form of \p symtab.
+ *
+ * \param[in] fp File to print to.
+ * \param[in] indent Number of spaces to use for indentation.
+ * \param[in] title Name for header text.
+ * \param[in] symtab Symbol table to print out.
+ */
+void pr_symtab(FILE* fp, int indent, const char* title, t_symtab* symtab);
+
+#endif
diff --git a/src/include/gromacs/topology/topology.h b/src/include/gromacs/topology/topology.h
new file mode 100644 (file)
index 0000000..ed809ab
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2011,2014,2015,2016,2018, The GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOPOLOGY_TOPOLOGY_H
+#define GMX_TOPOLOGY_TOPOLOGY_H
+
+#include <cstdio>
+
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/atoms.h"
+#include "gromacs/topology/block.h"
+#include "gromacs/topology/forcefieldparameters.h"
+#include "gromacs/topology/idef.h"
+#include "gromacs/topology/symtab.h"
+#include "gromacs/utility/enumerationhelpers.h"
+#include "gromacs/utility/listoflists.h"
+#include "gromacs/utility/unique_cptr.h"
+
+enum class SimulationAtomGroupType : int
+{
+    TemperatureCoupling,
+    EnergyOutput,
+    AccelerationUnused,
+    Freeze,
+    User1,
+    User2,
+    MassCenterVelocityRemoval,
+    CompressedPositionOutput,
+    OrientationRestraintsFit,
+    QuantumMechanics,
+    Count
+};
+
+//! Short strings used for describing atom groups in log and energy files
+const char* shortName(SimulationAtomGroupType type);
+
+// const char *shortName(int type); // if necessary
+
+/*! \brief Molecules type data: atoms, interactions and exclusions */
+struct gmx_moltype_t
+{
+    gmx_moltype_t();
+
+    ~gmx_moltype_t();
+
+    /*! \brief Deleted copy assignment operator to avoid (not) freeing pointers */
+    gmx_moltype_t& operator=(const gmx_moltype_t&) = delete;
+
+    /*! \brief Default copy constructor */
+    gmx_moltype_t(const gmx_moltype_t&) = default;
+
+    char**                name;  /**< Name of the molecule type            */
+    t_atoms               atoms; /**< The atoms in this molecule           */
+    InteractionLists      ilist; /**< Interaction list with local indices  */
+    gmx::ListOfLists<int> excls; /**< The exclusions                       */
+};
+
+/*! \brief Block of molecules of the same type, used in gmx_mtop_t */
+struct gmx_molblock_t
+{
+    int                    type = -1; /**< The molecule type index in mtop.moltype  */
+    int                    nmol = 0;  /**< The number of molecules in this block    */
+    std::vector<gmx::RVec> posres_xA; /**< Position restraint coordinates for top A */
+    std::vector<gmx::RVec> posres_xB; /**< Position restraint coordinates for top B */
+};
+
+/*! \brief Indices for a gmx_molblock_t, derived from other gmx_mtop_t contents */
+struct MoleculeBlockIndices
+{
+    int numAtomsPerMolecule; /**< Number of atoms in a molecule in the block */
+    int globalAtomStart;     /**< Global atom index of the first atom in the block */
+    int globalAtomEnd;       /**< Global atom index + 1 of the last atom in the block */
+    int globalResidueStart;  /**< Global residue index of the first residue in the block */
+    int residueNumberStart; /**< Residue numbers start from this value if the number of residues per molecule is <= maxres_renum */
+    int moleculeIndexStart; /**< Global molecule indexing starts from this value */
+};
+
+/*! \brief Contains the simulation atom groups.
+ *
+ * Organized as containers for the different
+ * SimulationAtomGroupTypes
+ */
+struct SimulationGroups
+{
+    // TODO: collect groups and groupNumbers in a struct for each group type
+    //! Group numbers for each of the different SimulationAtomGroupType groups.
+    gmx::EnumerationArray<SimulationAtomGroupType, AtomGroupIndices> groups;
+    //! Names of groups, stored as pointer to the entries in the symbol table.
+    std::vector<char**> groupNames;
+    //! Indices into groups for each atom for each of the different SimulationAtomGroupType groups.
+    gmx::EnumerationArray<SimulationAtomGroupType, std::vector<unsigned char>> groupNumbers;
+
+    /*! \brief
+     * Number of atoms for which group numbers are stored for a single SimulationGroup.
+     *
+     * \param[in] group  The group type.
+     */
+    int numberOfGroupNumbers(SimulationAtomGroupType group) const
+    {
+        return static_cast<int>(groupNumbers[group].size());
+    }
+};
+
+/*! \brief
+ * Returns group number of an input group for a given atom.
+ *
+ * Returns the group \p type for \p atom in \p group, or 0 if the
+ * entries for all atoms in the group are 0 and the pointer is thus null.
+ *
+ * \param[in] group Group to check.
+ * \param[in] type  Type of group to check.
+ * \param[in] atom  Atom to check if it has an entry.
+ */
+int getGroupType(const SimulationGroups& group, SimulationAtomGroupType type, int atom);
+
+/* The global, complete system topology struct, based on molecule types.
+ * This structure should contain no data that is O(natoms) in memory.
+ *
+ * TODO: Find a solution for ensuring that the derived data is in sync
+ *       with the primary data, possibly by converting to a class.
+ */
+struct gmx_mtop_t //NOLINT(clang-analyzer-optin.performance.Padding)
+{
+    gmx_mtop_t();
+
+    ~gmx_mtop_t();
+
+    //! Name of the topology.
+    char** name = nullptr;
+    //! Force field parameters used.
+    gmx_ffparams_t ffparams;
+    //! Vector of different molecule types.
+    std::vector<gmx_moltype_t> moltype;
+    //! Vector of different molecule blocks.
+    std::vector<gmx_molblock_t> molblock;
+    //! Are there intermolecular interactions?
+    bool bIntermolecularInteractions = false;
+    /* \brief
+     * List of intermolecular interactions using system wide
+     * atom indices, either NULL or size F_NRE
+     */
+    std::unique_ptr<InteractionLists> intermolecular_ilist = nullptr;
+    //! Number of global atoms.
+    int natoms = 0;
+    //! Atomtype properties
+    t_atomtypes atomtypes;
+    //! Groups of atoms for different purposes
+    SimulationGroups groups;
+    //! The legacy symbol table
+    t_symtab symtab;
+    //! Tells whether we have valid molecule indices
+    bool haveMoleculeIndices = false;
+    /*! \brief List of global atom indices of atoms between which
+     * non-bonded interactions must be excluded.
+     */
+    std::vector<int> intermolecularExclusionGroup;
+
+    //! Maximum number of residues in molecule to trigger renumbering of residues
+    int maxResiduesPerMoleculeToTriggerRenumber() const
+    {
+        return maxResiduesPerMoleculeToTriggerRenumber_;
+    }
+    //! Maximum residue number that is not renumbered.
+    int maxResNumberNotRenumbered() const { return maxResNumberNotRenumbered_; }
+    /*! \brief Finalize this data structure.
+     *
+     * Should be called after generating or reading mtop, to set some compute
+     * intesive variables to avoid N^2 operations later on.
+     *
+     * \todo Move into a builder class, once available.
+     */
+    void finalize();
+
+    /* Derived data below */
+    //! Indices for each molblock entry for fast lookup of atom properties
+    std::vector<MoleculeBlockIndices> moleculeBlockIndices;
+
+private:
+    //! Build the molblock indices
+    void buildMolblockIndices();
+    //! Maximum number of residues in molecule to trigger renumbering of residues
+    int maxResiduesPerMoleculeToTriggerRenumber_ = 0;
+    //! The maximum residue number in moltype that is not renumbered
+    int maxResNumberNotRenumbered_ = -1;
+};
+
+/*! \brief
+ * The fully written out topology for a domain over its lifetime
+ *
+ * Also used in some analysis code.
+ */
+struct gmx_localtop_t
+{
+    //! Constructor used for normal operation, manages own resources.
+    gmx_localtop_t(const gmx_ffparams_t& ffparams);
+
+    //! The interaction function definition
+    InteractionDefinitions idef;
+    //! The exclusions
+    gmx::ListOfLists<int> excls;
+};
+
+/* The old topology struct, completely written out, used in analysis tools */
+typedef struct t_topology
+{
+    char**      name;                        /* Name of the topology                 */
+    t_idef      idef;                        /* The interaction function definition  */
+    t_atoms     atoms;                       /* The atoms                            */
+    t_atomtypes atomtypes;                   /* Atomtype properties                  */
+    t_block     mols;                        /* The molecules                        */
+    gmx_bool    bIntermolecularInteractions; /* Inter.mol. int. ?   */
+    /* Note that the exclusions are not stored in t_topology */
+    t_symtab symtab; /* The symbol table                     */
+} t_topology;
+
+void init_top(t_topology* top);
+void done_top(t_topology* top);
+// Frees both t_topology and gmx_mtop_t when the former has been created from
+// the latter.
+void done_top_mtop(t_topology* top, gmx_mtop_t* mtop);
+
+bool gmx_mtop_has_masses(const gmx_mtop_t* mtop);
+bool gmx_mtop_has_charges(const gmx_mtop_t* mtop);
+bool gmx_mtop_has_perturbed_charges(const gmx_mtop_t& mtop);
+bool gmx_mtop_has_atomtypes(const gmx_mtop_t* mtop);
+bool gmx_mtop_has_pdbinfo(const gmx_mtop_t* mtop);
+
+void pr_mtop(FILE* fp, int indent, const char* title, const gmx_mtop_t* mtop, gmx_bool bShowNumbers, gmx_bool bShowParameters);
+void pr_top(FILE* fp, int indent, const char* title, const t_topology* top, gmx_bool bShowNumbers, gmx_bool bShowParameters);
+
+/*! \brief Compare two mtop topologies.
+ *
+ * \param[in] fp File pointer to write to.
+ * \param[in] mtop1 First topology to compare.
+ * \param[in] mtop2 Second topology to compare.
+ * \param[in] relativeTolerance Relative tolerance for comparison.
+ * \param[in] absoluteTolerance Absolute tolerance for comparison.
+ */
+void compareMtop(FILE* fp, const gmx_mtop_t& mtop1, const gmx_mtop_t& mtop2, real relativeTolerance, real absoluteTolerance);
+
+/*! \brief Check perturbation parameters in topology.
+ *
+ * \param[in] fp File pointer to write to.
+ * \param[in] mtop1 Topology to check perturbation parameters in.
+ * \param[in] relativeTolerance Relative tolerance for comparison.
+ * \param[in] absoluteTolerance Absolute tolerance for comparison.
+ */
+void compareMtopAB(FILE* fp, const gmx_mtop_t& mtop1, real relativeTolerance, real absoluteTolerance);
+
+/*! \brief Compare groups.
+ *
+ * \param[in] fp File pointer to write to.
+ * \param[in] g0 First group for comparison.
+ * \param[in] g1 Second group for comparison.
+ * \param[in] natoms0 Number of atoms for first group.
+ * \param[in] natoms1 Number of atoms for second group.
+ */
+void compareAtomGroups(FILE* fp, const SimulationGroups& g0, const SimulationGroups& g1, int natoms0, int natoms1);
+
+//! Typedef for gmx_localtop in analysis tools.
+using ExpandedTopologyPtr = std::unique_ptr<gmx_localtop_t>;
+
+void copy_moltype(const gmx_moltype_t* src, gmx_moltype_t* dst);
+
+#endif
diff --git a/src/include/gromacs/topology/topsort.h b/src/include/gromacs/topology/topsort.h
new file mode 100644 (file)
index 0000000..fd471a9
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2008,2009,2010,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TOPOLOGY_TOPSORT_H
+#define GMX_TOPOLOGY_TOPSORT_H
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct gmx_mtop_t;
+class InteractionDefinitions;
+
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+} // namespace gmx
+
+/* Returns if there are perturbed bonded interactions */
+gmx_bool gmx_mtop_bondeds_free_energy(const struct gmx_mtop_t* mtop);
+
+/* Sort all the bonded ilists in idef to have the perturbed ones at the end
+ * and set nr_nr_nonperturbed in ilist.
+ */
+void gmx_sort_ilist_fe(InteractionDefinitions* idef, gmx::ArrayRef<const int64_t> atomInfo);
+
+#endif
diff --git a/src/include/gromacs/trajectory/energyframe.h b/src/include/gromacs/trajectory/energyframe.h
new file mode 100644 (file)
index 0000000..2f9609b
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TRAJECTORY_ENERGYFRAME_H
+#define GMX_TRAJECTORY_ENERGYFRAME_H
+
+#include <map>
+#include <string>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct t_enxblock;
+
+struct t_energy
+{
+    //! The current energy.
+    real e;
+    //! The running average of the energy
+    double eav;
+    //! The sum of energies until now.
+    double esum;
+};
+
+/* The frames that are read/written */
+struct t_enxframe
+{
+    double      t;            /* Timestamp of this frame                            */
+    int64_t     step;         /* MD step                                    */
+    int64_t     nsteps;       /* The number of steps between frames            */
+    double      dt;           /* The MD time step                              */
+    int         nsum;         /* The number of terms for the sums in energyGroupPairTerms      */
+    int         nre;          /* Number of energies                         */
+    int         e_size;       /* Size (in bytes) of energies                */
+    int         e_alloc;      /* Allocated size (in elements) of energyGroupPairTerms          */
+    t_energy*   ener;         /* The energies                                  */
+    int         nblock;       /* Number of following energy blocks             */
+    t_enxblock* block;        /* The blocks                                    */
+    int         nblock_alloc; /* The number of blocks allocated                */
+};
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief Contains the content of an .edr frame read by an EnergyFrameReader
+ *
+ * The interface of this class is intended to resemble a subset of std::map. */
+class EnergyFrame
+{
+public:
+    //! Convenience type
+    using MapType = std::map<std::string, real>;
+    //! Convenience type
+    using MapConstIterator = MapType::const_iterator;
+    //! Constructor
+    EnergyFrame(const t_enxframe& enxframe, const std::map<std::string, int>& indicesOfEnergyFields);
+    /*! \brief Return string that helps users identify this frame, containing time and step number.
+     *
+     * \throws std::bad_alloc  when out of memory */
+    std::string frameName() const;
+    /*! \brief Return the value read for energy \c name.
+     *
+     * \throws APIError  if \c name was not registered with EnergyFileReader. */
+    const real& at(const std::string& name) const;
+    //! Return const interator to first element of values.
+    MapConstIterator begin() const;
+    //! Return const interator to past the end element of values.
+    MapConstIterator end() const;
+    //! Return a const interator to the element with \c key, or end() if not found.
+    MapConstIterator find(const std::string& key) const;
+
+private:
+    //! Container for energy values, indexed by name
+    MapType values_;
+    //! Step number read from the .edr file frame
+    std::int64_t step_;
+    //! Time read from the .edr file frame
+    double time_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectory/trajectoryframe.h b/src/include/gromacs/trajectory/trajectoryframe.h
new file mode 100644 (file)
index 0000000..0adc4db
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+
+/* The gmx_bools indicate whether a field was read from the trajectory.
+ * Do not try to use a pointer when its gmx_bool is FALSE, as memory might
+ * not be allocated.
+ */
+#ifndef GMX_TRAJECTORY_TRX_H
+#define GMX_TRAJECTORY_TRX_H
+
+#include <cstdio>
+
+#include <array>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+struct t_atoms;
+enum class PbcType : int;
+
+typedef struct t_trxframe // NOLINT (clang-analyzer-optin.performance.Padding)
+{
+    int      not_ok;  /* integrity flags                  */
+    gmx_bool bDouble; /* Double precision?                */
+    int      natoms;  /* number of atoms (atoms, x, v, f, index) */
+    gmx_bool bStep;
+    int64_t  step; /* MD step number                   */
+    gmx_bool bTime;
+    real     time; /* time of the frame                */
+    gmx_bool bLambda;
+    gmx_bool bFepState; /* does it contain fep_state?       */
+    real     lambda;    /* free energy perturbation lambda  */
+    int      fep_state; /* which fep state are we in? */
+    gmx_bool bAtoms;
+    t_atoms* atoms; /* atoms struct (natoms)            */
+    gmx_bool bPrec;
+    real     prec; /* precision of x, fraction of 1 nm */
+    gmx_bool bX;
+    rvec*    x; /* coordinates (natoms)             */
+    gmx_bool bV;
+    rvec*    v; /* velocities (natoms)              */
+    gmx_bool bF;
+    rvec*    f; /* forces (natoms)                  */
+    gmx_bool bBox;
+    matrix   box; /* the 3 box vectors                */
+    gmx_bool bPBC;
+    PbcType  pbcType; /* the type of pbc                  */
+    gmx_bool bIndex;
+    int*     index; /* atom indices of contained coordinates */
+} t_trxframe;
+
+void comp_frame(FILE* fp, t_trxframe* fr1, t_trxframe* fr2, gmx_bool bRMSD, real ftol, real abstol);
+
+void done_frame(t_trxframe* frame);
+
+namespace gmx
+{
+
+/*!\brief A 3x3 matrix data type useful for simulation boxes
+ *
+ * \todo Implement a full replacement for C-style real[DIM][DIM] */
+using BoxMatrix = std::array<std::array<real, DIM>, DIM>;
+
+/*! \internal
+ * \brief Contains a valid trajectory frame.
+ *
+ * Valid frames have a step and time, but need not have any particular
+ * other fields.
+ *
+ * \todo Eventually t_trxframe should be replaced by a class such as
+ * this. Currently we need to introduce BoxMatrix so that we can have
+ * a normal C++ getter that returns the contents of a box matrix,
+ * since you cannot use a real[DIM][DIM] as a function return type.
+ *
+ * \todo Consider a std::optional work-alike type for expressing that
+ * a field may or may not have content. */
+class TrajectoryFrame
+{
+public:
+    /*! \brief Constructor
+     *
+     * \throws APIError If \c frame lacks either step or time.
+     */
+    explicit TrajectoryFrame(const t_trxframe& frame);
+    /*! \brief Return a string that helps users identify this frame, containing time and step number.
+     *
+     * \throws std::bad_alloc  when out of memory */
+    std::string frameName() const;
+    //! Step number read from the trajectory file frame.
+    std::int64_t step() const;
+    //! Time read from the trajectory file frame.
+    double time() const;
+    //! The PBC characteristics of the box.
+    PbcType pbc() const;
+    //! Get a view of position coordinates of the frame (which could be empty).
+    ArrayRef<const RVec> x() const;
+    //! Get a view of velocity coordinates of the frame (which could be empty).
+    ArrayRef<const RVec> v() const;
+    //! Get a view of force coordinates of the frame (which could be empty).
+    ArrayRef<const RVec> f() const;
+    //! Return whether the frame has a box.
+    bool hasBox() const;
+    //! Return a handle to the frame's box, which is all zero if the frame has no box.
+    const BoxMatrix& box() const;
+
+private:
+    //! Handle to trajectory data
+    const t_trxframe& frame_;
+    //! Box matrix data from the frame_.
+    BoxMatrix box_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/analysismodule.h b/src/include/gromacs/trajectoryanalysis/analysismodule.h
new file mode 100644 (file)
index 0000000..e78232f
--- /dev/null
@@ -0,0 +1,498 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::TrajectoryAnalysisModule and
+ * gmx::TrajectoryAnalysisModuleData.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_ANALYSISMODULE_H
+#define GMX_TRAJECTORYANALYSIS_ANALYSISMODULE_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/selection/selection.h" // For gmx::SelectionList
+
+struct t_pbc;
+struct t_trxframe;
+
+namespace gmx
+{
+
+class AbstractAnalysisData;
+class AnalysisData;
+class AnalysisDataHandle;
+class AnalysisDataParallelOptions;
+class IOptionsContainer;
+class Options;
+class SelectionCollection;
+class TopologyInformation;
+class TrajectoryAnalysisModule;
+class TrajectoryAnalysisSettings;
+
+/*! \brief
+ * Base class for thread-local data storage during trajectory analysis.
+ *
+ * Thread-local storage of data handles and selections is implemented in this
+ * class; TrajectoryAnalysisModule instances can access the thread-local values
+ * in their TrajectoryAnalysisModule::analyzeFrame() method using dataHandle()
+ * and parallelSelection().
+ *
+ * \see TrajectoryAnalysisModule::startFrames()
+ * \see TrajectoryAnalysisModule::analyzeFrame()
+ * \see TrajectoryAnalysisModule::finishFrames()
+ *
+ * \inpublicapi
+ * \ingroup module_trajectoryanalysis
+ */
+class TrajectoryAnalysisModuleData
+{
+public:
+    virtual ~TrajectoryAnalysisModuleData();
+
+    /*! \brief
+     * Performs any finishing actions after all frames have been processed.
+     *
+     * \throws  unspecified Implementation may throw exceptions to indicate
+     *      errors.
+     *
+     * This function is called immediately before the destructor, after
+     * TrajectoryAnalysisModule::finishFrames().
+     * Derived classes should implement any final operations that need to
+     * be done after successful analysis.
+     * All implementations should call finishDataHandles().
+     */
+    virtual void finish() = 0;
+
+    /*! \brief
+     * Returns a data handle for a given dataset.
+     *
+     * \param[in] data  Analysis data object.
+     * \returns   Data handle for \p data stored in this thread-local data.
+     *
+     * \p data should have previously been registered with
+     * TrajectoryAnalysisModule::registerAnalysisDataset().
+     * If \p data has zero columns in all data sets, the returned data
+     * handle is invalid.
+     *
+     * Does not throw.
+     */
+    AnalysisDataHandle dataHandle(const AnalysisData& data);
+    /*! \brief
+     * Returns a selection that corresponds to the given selection.
+     *
+     * \param[in] selection Global selection object.
+     * \returns   Selection object corresponding to this thread-local data.
+     *
+     * \p selection is the selection object that was obtained from
+     * SelectionOption.  The return value is the corresponding selection
+     * in the selection collection with which this data object was
+     * constructed with.
+     *
+     * Does not throw.
+     */
+    Selection parallelSelection(const Selection& selection);
+    /*! \brief
+     * Returns a set of selection that corresponds to the given selections.
+     *
+     * \throws std::bad_alloc if out of memory.
+     *
+     * Works as parallelSelection(), but for a list of selections at once.
+     *
+     * \see parallelSelection()
+     */
+    SelectionList parallelSelections(const SelectionList& selections);
+
+protected:
+    /*! \brief
+     * Initializes thread-local storage for data handles and selections.
+     *
+     * \param[in] module     Analysis module to use for data objects.
+     * \param[in] opt        Data parallelization options.
+     * \param[in] selections Thread-local selection collection.
+     * \throws  std::bad_alloc if out of memory.
+     * \throws  unspecified Can throw any exception thrown by
+     *      AnalysisData::startData().
+     *
+     * Calls AnalysisData::startData() on all data objects registered with
+     * TrajectoryAnalysisModule::registerAnalysisDataset() in \p module.
+     * The handles are accessible through dataHandle().
+     */
+    TrajectoryAnalysisModuleData(TrajectoryAnalysisModule*          module,
+                                 const AnalysisDataParallelOptions& opt,
+                                 const SelectionCollection&         selections);
+
+    /*! \brief
+     * Calls finishData() on all data handles.
+     *
+     * \throws  unspecified Can throw any exception thrown by
+     *      AnalysisDataHandle::finishData().
+     *
+     * This function should be called from the implementation of finish()
+     * in all subclasses.
+     */
+    void finishDataHandles();
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+//! Smart pointer to manage a TrajectoryAnalysisModuleData object.
+typedef std::unique_ptr<TrajectoryAnalysisModuleData> TrajectoryAnalysisModuleDataPointer;
+
+/*! \brief
+ * Base class for trajectory analysis modules.
+ *
+ * Trajectory analysis methods should derive from this class and override the
+ * necessary virtual methods to implement initialization (initOptions(),
+ * optionsFinished(), initAnalysis(), initAfterFirstFrame()), per-frame analysis
+ * (analyzeFrame()), and final processing (finishFrames(), finishAnalysis(),
+ * writeOutput()).
+ *
+ * For parallel analysis using threads, only a single object is constructed,
+ * but the methods startFrames(), analyzeFrame() and finishFrames() are called
+ * in each thread.  Frame-local data should be initialized in startFrames() and
+ * stored in a class derived from TrajectoryAnalysisModuleData that is passed
+ * to the other methods.  The default implementation of startFrames() can be
+ * used if only data handles and selections need to be thread-local.
+ *
+ * To get the full benefit from this class,
+ * \ref module_analysisdata "analysis data objects" and
+ * \ref module_selection "selections" should be used in the implementation.
+ * See the corresponding modules' documentation for details of how they work.
+ *
+ * Typical way of using AnalysisData in derived classes is to have the
+ * AnalysisData object as a member variable and register it using
+ * registerAnalysisDataset().  Analysis modules are initialized in
+ * initAnalysis() and the processing chain is initialized.  If any of the
+ * modules is required, e.g., for post-processing in finishAnalysis(), it can
+ * be stored in a member variable.  To add data to the data object in
+ * analyzeFrame(), a data handle is obtained using
+ * TrajectoryAnalysisModuleData::dataHandle().
+ *
+ * Typical way of using selections in derived classes is to have the required
+ * \ref Selection objects (or ::SelectionList objects) as member variables, and
+ * add the required selection options in initOptions().  These member variables
+ * can be accessed in initAnalysis() to get general information about the
+ * selections.  In analyzeFrame(), these selection objects should not be used
+ * directly, but instead TrajectoryAnalysisModuleData::parallelSelection()
+ * should be used to obtain a selection object that works correctly also for
+ * parallel analysis.
+ *
+ * Derived classes should use exceptions to indicate errors in the virtual
+ * methods.
+ *
+ * \inpublicapi
+ * \ingroup module_trajectoryanalysis
+ */
+class TrajectoryAnalysisModule
+{
+public:
+    virtual ~TrajectoryAnalysisModule();
+
+    /*! \brief
+     * Initializes options understood by the module.
+     *
+     * \param[in,out] options  Options object to add the options to.
+     * \param[in,out] settings Settings to pass to and from the module.
+     *
+     * This method is called first after the constructor, and it should
+     * add options understood by the module to \p options.  Output values
+     * from options (including selections) should be stored in member
+     * variables.
+     *
+     * In addition to initializing the options, this method can also
+     * provide information about the module's requirements using the
+     * \p settings object; see TrajectoryAnalysisSettings for more details.
+     *
+     * If settings depend on the option values provided by the user, see
+     * optionsFinished().
+     */
+    virtual void initOptions(IOptionsContainer* options, TrajectoryAnalysisSettings* settings) = 0;
+    /*! \brief
+     * Called after all option values have been set.
+     *
+     * \param[in,out] settings Settings to pass to and from the module.
+     *
+     * This method is called after option values have been assigned (but
+     * interactive selection input has not yet been performed).
+     *
+     * If the module needs to change settings that affect topology loading
+     * (can be done using the \p settings object) or selection
+     * initialization (can be done using SelectionOptionInfo) based on
+     * option values, this method has to be overridden.
+     *
+     * The default implementation does nothing.
+     */
+    virtual void optionsFinished(TrajectoryAnalysisSettings* settings);
+    /*! \brief
+     * Initializes the analysis.
+     *
+     * \param[in]    settings Settings to pass to and from the module.
+     * \param[in]    top      Topology information.
+     *
+     * When this function is called, selections have been initialized based
+     * on user input, and a topology has been loaded if provided by the
+     * user.  For dynamic selections, the selections have been evaluated to
+     * the largest possible selection, i.e., the selections passed to
+     * analyzeFrame() are always a subset of the selections provided here.
+     */
+    virtual void initAnalysis(const TrajectoryAnalysisSettings& settings,
+                              const TopologyInformation&        top) = 0;
+    /*! \brief
+     * Performs additional initialization after reading the first frame.
+     *
+     * When this function is called, selections are the same as in
+     * initAnalysis(), i.e., they have not been evaluated for the first
+     * frame.
+     *
+     * It is necessary to override this method only if the module needs to
+     * do initialization for which it requires data from the first frame.
+     *
+     * The default implementation does nothing.
+     */
+    virtual void initAfterFirstFrame(const TrajectoryAnalysisSettings& settings, const t_trxframe& fr);
+
+    /*! \brief
+     * Starts the analysis of frames.
+     *
+     * \param[in]  opt         Parallel options
+     * \param[in]  selections  Frame-local selection collection object.
+     * \returns    Data structure for thread-local data.
+     *
+     * This function is necessary only for threaded parallelization.
+     * It is called once for each thread and should initialize a class that
+     * contains any required frame-local data in the returned value.
+     * The default implementation creates a basic data structure that holds
+     * thread-local data handles for all data objects registered with
+     * registerAnalysisDataset(), as well as the thread-local selection
+     * collection.  These can be accessed in analyzeFrame() using the
+     * methods in TrajectoryAnalysisModuleData.
+     * If other thread-local data is needed, this function should be
+     * overridden and it should create an instance of a class derived from
+     * TrajectoryAnalysisModuleData.
+     *
+     * \see TrajectoryAnalysisModuleData
+     */
+    virtual TrajectoryAnalysisModuleDataPointer startFrames(const AnalysisDataParallelOptions& opt,
+                                                            const SelectionCollection& selections);
+    /*! \brief
+     * Analyzes a single frame.
+     *
+     * \param[in]     frnr   Frame number, a zero-based index that
+     *      uniquely identifies the frame.
+     * \param[in]     fr     Current frame.
+     * \param[in]     pbc    Periodic boundary conditions for \p fr.
+     * \param[in,out] pdata  Data structure for frame-local data.
+     *
+     * This method is called once for each frame to be analyzed, and should
+     * analyze the positions provided in the selections.  Data handles and
+     * selections should be obtained from the \p pdata structure.
+     *
+     * For threaded analysis, this method is called asynchronously in
+     * different threads to analyze different frames.  The \p pdata
+     * structure is one of the structures created with startFrames(),
+     * but no assumptions should be made about which of these data
+     * structures is used.  It is guaranteed that two instances of
+     * analyzeFrame() are not running concurrently with the same \p pdata
+     * data structure.
+     * Any access to data structures not stored in \p pdata should be
+     * designed to be thread-safe.
+     */
+    virtual void analyzeFrame(int frnr, const t_trxframe& fr, t_pbc* pbc, TrajectoryAnalysisModuleData* pdata) = 0;
+    /*! \brief
+     * Finishes the analysis of frames.
+     *
+     * \param[in]  pdata    Data structure for thread-local data.
+     *
+     * This method is called once for each call of startFrames(), with the
+     * data structure returned by the corresponding startFrames().
+     * The \p pdata object should be destroyed by the caller after this
+     * function has been called.
+     *
+     * You only need to override this method if you need custom
+     * operations to combine data from the frame-local data structures
+     * to get the final result.  In such cases, the data should be
+     * aggregated in this function and stored in a member attribute.
+     *
+     * The default implementation does nothing.
+     *
+     * \see startFrames()
+     */
+    virtual void finishFrames(TrajectoryAnalysisModuleData* pdata);
+
+    /*! \brief
+     * Postprocesses data after frames have been read.
+     *
+     * \param[in]  nframes  Total number of frames processed.
+     *
+     * This function is called after all finishFrames() calls have been
+     * called.
+     * \p nframes will equal the number of calls to analyzeFrame() that
+     * have occurred.
+     */
+    virtual void finishAnalysis(int nframes) = 0;
+    /*! \brief
+     * Writes output into files and/or standard output/error.
+     *
+     * All output from the module, excluding data written out for each
+     * frame during analyzeFrame(), should be confined into this function.
+     * This function is guaranteed to be called only after
+     * finishAnalysis().
+     */
+    virtual void writeOutput() = 0;
+
+    /*! \brief
+     * Returns the number of datasets provided by the module.
+     *
+     * Does not throw.
+     */
+    int datasetCount() const;
+    /*! \brief
+     * Returns a vector with the names of datasets provided by the module.
+     *
+     * Does not throw.
+     */
+    const std::vector<std::string>& datasetNames() const;
+    /*! \brief
+     * Returns a pointer to the data set \p index.
+     *
+     * \param[in] index  Data set to query for.
+     * \returns   Reference to the requested data set.
+     * \throws    APIError if \p index is not valid.
+     *
+     * \p index should be >= 0 and < datasetCount().
+     *
+     * The return value is not const to allow callers to add modules to the
+     * data sets. However, the AbstractAnalysisData interface does not
+     * provide any means to alter the data, so the module does not need to
+     * care about external modifications.
+     */
+    AbstractAnalysisData& datasetFromIndex(int index) const;
+    /*! \brief
+     * Returns a pointer to the data set with name \p name
+     *
+     * \param[in] name  Data set to query for.
+     * \returns   Reference to the requested data set.
+     * \throws    APIError if \p name is not valid.
+     *
+     * \p name should be one of the names returned by datasetNames().
+     *
+     * The return value is not const to allow callers to add modules to the
+     * data sets. However, the AbstractAnalysisData interface does not
+     * provide any means to alter the data, so the module does not need to
+     * care about external modifications.
+     */
+    AbstractAnalysisData& datasetFromName(const char* name) const;
+    /*! \brief
+     * Processes data in AnalysisData objects in serial for each frame.
+     *
+     * \param[in] frameIndex  Index of the frame that has been finished.
+     *
+     * This method is called by the framework in order for each frame,
+     * after the analysis for that frame has been finished.  These calls
+     * always execute in serial and in sequential frame order, even during
+     * parallel analysis where multiple analyzeFrame() calls may be
+     * executing concurrently.
+     *
+     * \see AnalysisData::finishFrameSerial()
+     */
+    void finishFrameSerial(int frameIndex);
+
+protected:
+    /*! \brief
+     * Initializes the dataset registration mechanism.
+     *
+     * \throws    std::bad_alloc if out of memory.
+     */
+    TrajectoryAnalysisModule();
+
+    /*! \brief
+     * Registers a dataset that exports data.
+     *
+     * \param     data  Data object to register.
+     * \param[in] name  Name to register the dataset with.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * Registers \p data as a dataset that provides output from the
+     * analysis module.  Callers for the module can access the dataset
+     * with datasetFromName() using \p name as an AbstractAnalysisData
+     * object.  This allows them to add their own data modules to do extra
+     * processing.
+     *
+     * \p name must be unique across all calls within the same
+     * TrajectoryAnalysisModule instance.
+     */
+    void registerBasicDataset(AbstractAnalysisData* data, const char* name);
+    /*! \brief
+     * Registers a parallelized dataset that exports data.
+     *
+     * \param     data  AnalysisData object to register.
+     * \param[in] name  Name to register the dataset with.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * This method works as registerBasicDataset(), but additionally allows
+     * data handles for \p data to be accessed using
+     * TrajectoryAnalysisData.
+     *
+     * \see registerBasicDataset()
+     */
+    void registerAnalysisDataset(AnalysisData* data, const char* name);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+
+    /*! \brief
+     * Needed to access the registered analysis data sets.
+     */
+    friend class TrajectoryAnalysisModuleData;
+};
+
+//! Smart pointer to manage a TrajectoryAnalysisModule.
+typedef std::unique_ptr<TrajectoryAnalysisModule> TrajectoryAnalysisModulePointer;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/analysissettings.h b/src/include/gromacs/trajectoryanalysis/analysissettings.h
new file mode 100644 (file)
index 0000000..7e7d47e
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010-2018, The GROMACS development team.
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::TrajectoryAnalysisSettings.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_ANALYSISSETTINGS_H
+#define GMX_TRAJECTORYANALYSIS_ANALYSISSETTINGS_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/options/timeunitmanager.h"
+
+namespace gmx
+{
+
+template<typename T>
+class ArrayRef;
+
+class AnalysisDataPlotSettings;
+class ICommandLineOptionsModuleSettings;
+class Options;
+class TrajectoryAnalysisRunnerCommon;
+
+/*! \brief
+ * Trajectory analysis module configuration object.
+ *
+ * This class is used by trajectory analysis modules to inform the caller
+ * about the requirements they have on the input (e.g., whether a topology is
+ * required, or whether PBC removal makes sense).  It is also used to pass
+ * similar information back to the analysis module after parsing user input.
+ *
+ * Having this functionality as a separate class makes the
+ * TrajectoryAnalysisModule interface much cleaner, and also reduces the need to
+ * change existing code when new options are added.
+ *
+ * Methods in this class do not throw, except for the constructor, which may
+ * throw an std::bad_alloc.
+ *
+ * \todo
+ * Remove plain flags from the public interface.
+ *
+ * \inpublicapi
+ * \ingroup module_trajectoryanalysis
+ */
+class TrajectoryAnalysisSettings
+{
+public:
+    //! Recognized flags.
+    enum
+    {
+        /*! \brief
+         * Forces loading of a topology file.
+         *
+         * If this flag is not specified, the topology file is loaded only
+         * if it is provided on the command line explicitly.
+         */
+        efRequireTop = 1 << 0,
+        /*! \brief
+         * Requests topology coordinates.
+         *
+         * If this flag is specified, the position coordinates loaded from the
+         * topology can be accessed, otherwise they are not loaded.
+         *
+         * \see TopologyInformation
+         */
+        efUseTopX = 1 << 1,
+        /*! \brief
+         * Requests topology coordinates.
+         *
+         * If this flag is specified, the velocity coordinates loaded from the
+         * topology can be accessed, otherwise they are not loaded.
+         *
+         * \see TopologyInformation
+         */
+        efUseTopV = 1 << 2,
+        /*! \brief
+         * Disallows the user from changing PBC handling.
+         *
+         * If this option is not specified, the analysis module (see
+         * TrajectoryAnalysisModule::analyzeFrame()) may be passed a NULL
+         * PBC structure, and it should be able to handle such a situation.
+         *
+         * \see setPBC()
+         */
+        efNoUserPBC = 1 << 4,
+        /*! \brief
+         * Disallows the user from changing PBC removal.
+         *
+         * \see setRmPBC()
+         */
+        efNoUserRmPBC = 1 << 5,
+    };
+
+    //! Initializes default settings.
+    TrajectoryAnalysisSettings();
+    ~TrajectoryAnalysisSettings();
+
+    //! Injects command line options module settings for some methods to use.
+    void setOptionsModuleSettings(ICommandLineOptionsModuleSettings* settings);
+
+    //! Returns the time unit the user has requested.
+    TimeUnit timeUnit() const;
+    //! Returns common settings for analysis data plot modules.
+    const AnalysisDataPlotSettings& plotSettings() const;
+
+    //! Returns the currently set flags.
+    unsigned long flags() const;
+    //! Tests whether a flag has been set.
+    bool hasFlag(unsigned long flag) const;
+    /*! \brief
+     * Returns whether PBC should be used.
+     *
+     * Returns the value set with setPBC() and/or overridden by the user.
+     * The user-provided value can be accessed in
+     * TrajectoryAnalysisModule::optionsFinished(), and can be overridden
+     * with a call to setPBC().
+     */
+    bool hasPBC() const;
+    /*! \brief
+     * Returns whether molecules should be made whole.
+     *
+     * See hasPBC() for information on accessing or overriding the
+     * user-provided value.
+     */
+    bool hasRmPBC() const;
+    //! Returns the currently set frame flags.
+    int frflags() const;
+
+    /*! \brief
+     * Sets flags.
+     *
+     * Overrides any earlier set flags.
+     * By default, no flags are set.
+     */
+    void setFlags(unsigned long flags);
+    //! Sets or clears an individual flag.
+    void setFlag(unsigned long flag, bool bSet = true);
+    /*! \brief
+     * Sets whether PBC are used.
+     *
+     * \param[in]  bPBC   true if PBC should be used.
+     *
+     * If called in TrajectoryAnalysisModule::initOptions(), this function
+     * sets the default for whether PBC are used in the analysis.
+     * If \ref efNoUserPBC is not set, a command-line option is provided
+     * for the user to override the default value.
+     * If called later, it overrides the setting provided by the user or an
+     * earlier call.
+     *
+     * If this function is not called, the default is to use PBC.
+     *
+     * If PBC are not used, the \p pbc pointer passed to
+     * TrajectoryAnalysisModule::analyzeFrame() is NULL.
+     * The value of the flag can also be accessed with hasPBC().
+     *
+     * \see efNoUserPBC
+     */
+    void setPBC(bool bPBC);
+    /*! \brief
+     * Sets whether molecules are made whole.
+     *
+     * \param[in]     bRmPBC true if molecules should be made whole.
+     *
+     * If called in TrajectoryAnalysisModule::initOptions(), this function
+     * sets the default for whether molecules are made whole.
+     * If \ref efNoUserRmPBC is not set, a command-line option is provided
+     * for the user to override the default value.
+     * If called later, it overrides the setting provided by the user or an
+     * earlier call.
+     *
+     * If this function is not called, the default is to make molecules
+     * whole.
+     *
+     * The main use of this function is to call it with \c false if your
+     * analysis program does not require whole molecules as this can
+     * increase the performance.
+     * In such a case, you can also specify \ref efNoUserRmPBC to not to
+     * confuse the user with an option that would only slow the program
+     * down.
+     *
+     * \see efNoUserRmPBC
+     */
+    void setRmPBC(bool bRmPBC);
+    /*! \brief
+     * Sets flags that determine what to read from the trajectory.
+     *
+     * \param[in]     frflags Flags for what to read from the trajectory file.
+     *
+     * If this function is not called, the flags default to TRX_NEED_X.
+     * If the analysis module needs some other information (velocities,
+     * forces), it can call this function to load additional information
+     * from the trajectory.
+     */
+    void setFrameFlags(int frflags);
+
+    //! \copydoc ICommandLineOptionsModuleSettings::setHelpText()
+    void setHelpText(const ArrayRef<const char* const>& help);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+
+    friend class TrajectoryAnalysisRunnerCommon;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/analysissettings_impl.h b/src/include/gromacs/trajectoryanalysis/analysissettings_impl.h
new file mode 100644 (file)
index 0000000..4fa009e
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares private implementation class for gmx::TrajectoryAnalysisSettings.
+ *
+ * \ingroup module_trajectoryanalysis
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_ANALYSISSETTINGS_IMPL_H
+#define GMX_TRAJECTORYANALYSIS_ANALYSISSETTINGS_IMPL_H
+
+#include <string>
+
+#include "gromacs/analysisdata/modules/plot.h"
+#include "gromacs/options/timeunitmanager.h"
+#include "gromacs/trajectoryanalysis/analysissettings.h"
+
+namespace gmx
+{
+
+class ICommandLineOptionsModuleSettings;
+
+/*! \internal \brief
+ * Private implementation class for TrajectoryAnalysisSettings.
+ *
+ * \ingroup module_trajectoryanalysis
+ */
+class TrajectoryAnalysisSettings::Impl
+{
+public:
+    //! Initializes the default values for the settings object.
+    Impl() :
+        timeUnit(TimeUnit::Default), flags(0), frflags(0), bRmPBC(true), bPBC(true), optionsModuleSettings_(nullptr)
+    {
+    }
+
+    //! Global time unit setting for the analysis module.
+    TimeUnit timeUnit;
+    //! Global plotting settings for the analysis module.
+    AnalysisDataPlotSettings plotSettings;
+    //! Flags for the analysis module.
+    unsigned long flags;
+    //! Frame reading flags for the analysis module.
+    int frflags;
+
+    //! Whether to make molecules whole for each frame.
+    bool bRmPBC;
+    //! Whether to pass PBC information to the analysis module.
+    bool bPBC;
+
+    //! Lower-level settings object wrapped by these settings.
+    ICommandLineOptionsModuleSettings* optionsModuleSettings_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/cmdlinerunner.h b/src/include/gromacs/trajectoryanalysis/cmdlinerunner.h
new file mode 100644 (file)
index 0000000..8dcb402
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::TrajectoryAnalysisCommandLineRunner.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_CMDLINERUNNER_H
+#define GMX_TRAJECTORYANALYSIS_CMDLINERUNNER_H
+
+#include <functional>
+#include <memory>
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+namespace gmx
+{
+
+class CommandLineModuleManager;
+class ICommandLineOptionsModule;
+
+/*! \brief
+ * Runner for command-line trajectory analysis tools.
+ *
+ * This class provides static methods to implement a command-line analysis
+ * program, given a TrajectoryAnalysisModule object (or a factory of such).
+ * It takes care of common command-line parameters, initializing and evaluating
+ * selections, and looping over trajectory frames.
+ *
+ * \inpublicapi
+ * \ingroup module_trajectoryanalysis
+ */
+class TrajectoryAnalysisCommandLineRunner
+{
+public:
+    /*! \brief
+     * Factory method type for creating a trajectory analysis module.
+     *
+     * This method allows the module creation to be postponed to the point
+     * where the module is needed, reducing initialization costs in, e.g.,
+     * the `gmx` binary, and simplifying exception handling.
+     */
+    typedef std::function<TrajectoryAnalysisModulePointer()> ModuleFactoryMethod;
+
+    /*! \brief
+     * Implements a main() method that runs a given module.
+     *
+     * \tparam ModuleType  Trajectory analysis module.
+     * \param  argc        \c argc passed to main().
+     * \param  argv        \c argv passed to main().
+     *
+     * This method abstracts away all the logic required to implement a
+     * main() method in user tools, allowing that to be changed without
+     * requiring changes to the tools themselves.
+     *
+     * \p ModuleType should be default-constructible and derive from
+     * TrajectoryAnalysisModule.
+     *
+     * Does not throw.  All exceptions are caught and handled internally.
+     */
+    template<class ModuleType>
+    static int runAsMain(int argc, char* argv[])
+    {
+        return runAsMain(argc, argv, &createModule<ModuleType>);
+    }
+    /*! \brief
+     * Implements a main() method that runs a given module.
+     *
+     * \param  argc        \c argc passed to main().
+     * \param  argv        \c argv passed to main().
+     * \param  factory     Function that creates the module on demand.
+     *
+     * Implements the template runAsMain(), but can also be used
+     * independently.
+     *
+     * Does not throw.  All exceptions are caught and handled internally.
+     */
+    static int runAsMain(int argc, char* argv[], const ModuleFactoryMethod& factory);
+    /*! \brief
+     * Registers a command-line module that runs a given module.
+     *
+     * \param  manager     Manager to register the module to.
+     * \param  name        Name of the module to register.
+     * \param  description One-line description for the module to register.
+     * \param  factory     Function that creates the module on demand.
+     *
+     * \p name and \p descriptions must be string constants or otherwise
+     * stay valid for the duration of the program execution.
+     */
+    static void registerModule(CommandLineModuleManager*  manager,
+                               const char*                name,
+                               const char*                description,
+                               const ModuleFactoryMethod& factory);
+    /*! \brief
+     * Create a command-line module that runs the provided analysis module.
+     *
+     * \param[in]  module     Module to run.
+     * \returns    Command-line module that runs the provided analysis
+     *      module.
+     * \throws std::bad_alloc if out of memory.
+     *
+     * This is mainly provided for testing purposes that want to bypass
+     * CommandLineModuleManager.
+     */
+    static std::unique_ptr<ICommandLineOptionsModule> createModule(TrajectoryAnalysisModulePointer module);
+
+private:
+    // Prevent instantiation.
+    TrajectoryAnalysisCommandLineRunner() {}
+
+    /*! \brief
+     * Creates a trajectory analysis module of a given type.
+     *
+     * \tparam ModuleType  Module to create.
+     */
+    template<class ModuleType>
+    static TrajectoryAnalysisModulePointer createModule()
+    {
+        return TrajectoryAnalysisModulePointer(new ModuleType());
+    }
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules.h b/src/include/gromacs/trajectoryanalysis/modules.h
new file mode 100644 (file)
index 0000000..75acf87
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2014,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Generic interface for accessing trajectory analysis modules.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_H
+
+namespace gmx
+{
+
+class CommandLineModuleManager;
+
+//! \cond libapi
+/*! \brief
+ * Registers all trajectory analysis command-line modules.
+ *
+ * \param[in] manager  Command-line module manager to receive the modules.
+ * \throws    std::bad_alloc if out of memory.
+ *
+ * Registers all trajectory analysis modules declared in the library such that
+ * they can be run through \p manager.
+ *
+ * \ingroup module_trajectoryanalysis
+ */
+void registerTrajectoryAnalysisModules(CommandLineModuleManager* manager);
+//! \endcond
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules/angle.h b/src/include/gromacs/trajectoryanalysis/modules/angle.h
new file mode 100644 (file)
index 0000000..5fe7028
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2011,2012,2013,2014,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares trajectory analysis module for angle calculations.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_ANGLE_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_ANGLE_H
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+class AngleInfo
+{
+public:
+    static const char                      name[];
+    static const char                      shortDescription[];
+    static TrajectoryAnalysisModulePointer create();
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules/convert_trj.h b/src/include/gromacs/trajectoryanalysis/modules/convert_trj.h
new file mode 100644 (file)
index 0000000..ffce0dc
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares trajectory analysis module for trajectory conversion.
+ *
+ * \author Paul Bauer <paul.bauer.q@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_CONVERT_TRJ_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_CONVERT_TRJ_H
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+class ConvertTrjInfo
+{
+public:
+    static const char                      name[];
+    static const char                      shortDescription[];
+    static TrajectoryAnalysisModulePointer create();
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules/distance.h b/src/include/gromacs/trajectoryanalysis/modules/distance.h
new file mode 100644 (file)
index 0000000..ef2709b
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares trajectory analysis module for distance calculations.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_DISTANCE_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_DISTANCE_H
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+class DistanceInfo
+{
+public:
+    static const char                      name[];
+    static const char                      shortDescription[];
+    static TrajectoryAnalysisModulePointer create();
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules/extract_cluster.h b/src/include/gromacs/trajectoryanalysis/modules/extract_cluster.h
new file mode 100644 (file)
index 0000000..d9cf9dc
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares trajectory analysis module for extracting frames corresponding
+ * to clusters.
+ *
+ * \author Paul Bauer apual.bauer.q@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_EXTRACTCLUSTER_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_EXTRACTCLUSTER_H
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+class ExtractClusterInfo
+{
+public:
+    static const char                      name[];
+    static const char                      shortDescription[];
+    static TrajectoryAnalysisModulePointer create();
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules/freevolume.h b/src/include/gromacs/trajectoryanalysis/modules/freevolume.h
new file mode 100644 (file)
index 0000000..a9f64fb
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares trajectory analysis module for free volume calculations.
+ *
+ * \author David van der Spoel <david.vanderspoel@icm.uu.se>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_FREEVOLUME_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_FREEVOLUME_H
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+class FreeVolumeInfo
+{
+public:
+    static const char                      name[];
+    static const char                      shortDescription[];
+    static TrajectoryAnalysisModulePointer create();
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules/msd.h b/src/include/gromacs/trajectoryanalysis/modules/msd.h
new file mode 100644 (file)
index 0000000..53f890c
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares trajectory analysis module for Mean Squared Displacement calculations.
+ *
+ * \author Kevin Boyd <kevin44boyd@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_MSD_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_MSD_H
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+namespace gmx::analysismodules
+{
+
+class MsdInfo
+{
+public:
+    static const char                      name[];
+    static const char                      shortDescription[];
+    static TrajectoryAnalysisModulePointer create();
+};
+
+} // namespace gmx::analysismodules
+
+#endif // GMX_TRAJECTORYANALYSIS_MODULES_MSD_H
diff --git a/src/include/gromacs/trajectoryanalysis/modules/pairdist.h b/src/include/gromacs/trajectoryanalysis/modules/pairdist.h
new file mode 100644 (file)
index 0000000..ad151b4
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares trajectory analysis module for pairwise distance calculations.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_PAIRDIST_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_PAIRDIST_H
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+class PairDistanceInfo
+{
+public:
+    static const char                      name[];
+    static const char                      shortDescription[];
+    static TrajectoryAnalysisModulePointer create();
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules/rdf.h b/src/include/gromacs/trajectoryanalysis/modules/rdf.h
new file mode 100644 (file)
index 0000000..fe521dc
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares trajectory analysis module for RDF calculations.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_RDF_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_RDF_H
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+class RdfInfo
+{
+public:
+    static const char                      name[];
+    static const char                      shortDescription[];
+    static TrajectoryAnalysisModulePointer create();
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules/sasa.h b/src/include/gromacs/trajectoryanalysis/modules/sasa.h
new file mode 100644 (file)
index 0000000..d1a1f07
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares trajectory analysis module for surface area calculations.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_SASA_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_SASA_H
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+class SasaInfo
+{
+public:
+    static const char                      name[];
+    static const char                      shortDescription[];
+    static TrajectoryAnalysisModulePointer create();
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules/select.h b/src/include/gromacs/trajectoryanalysis/modules/select.h
new file mode 100644 (file)
index 0000000..ca12253
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2011,2012,2013,2014,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares trajectory analysis module for basic selection information.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_SELECT_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_SELECT_H
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+class SelectInfo
+{
+public:
+    static const char                      name[];
+    static const char                      shortDescription[];
+    static TrajectoryAnalysisModulePointer create();
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules/surfacearea.h b/src/include/gromacs/trajectoryanalysis/modules/surfacearea.h
new file mode 100644 (file)
index 0000000..8129d3c
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2017,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_SURFACEAREA_H
+#define GMX_TRAJECTORYANALYSIS_SURFACEAREA_H
+
+#include <memory>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/real.h"
+
+struct t_pbc;
+
+#define FLAG_DOTS 01
+#define FLAG_VOLUME 02
+#define FLAG_ATOM_AREA 04
+
+namespace gmx
+{
+
+/*! \internal
+ * \brief
+ * Computes surface areas for a group of atoms/spheres.
+ *
+ * This class provides a surface area/volume calculator.
+ *
+ * The algorithm is based on representing each atom/sphere surface as a set of
+ * dots, and determining which dots are on the surface (not covered by any
+ * other atom/sphere).  The dots are distributed evenly using an icosahedron- or
+ * a dodecahedron-based method (see the original reference cited in the code).
+ * The area is then estimated from the area represented by each dot.
+ * The volume is calculated by selecting a fixed point and integrating over the
+ * surface dots, summing up the cones whose tip is at the fixed point and base
+ * at the surface points.
+ *
+ * The default dot density per sphere is 32, which gives quite inaccurate
+ * areas and volumes, but a reasonable number of surface points.  According to
+ * original documentation of the method, a density of 600-700 dots gives an
+ * accuracy of 1.5 A^2 per atom.
+ *
+ * \ingroup module_trajectoryanalysis
+ */
+class SurfaceAreaCalculator
+{
+public:
+    /*! \brief
+     * Initializes a surface area calculator.
+     *
+     * \throws std::bad_alloc if out of memory.
+     */
+    SurfaceAreaCalculator();
+    ~SurfaceAreaCalculator();
+
+    /*! \brief
+     * Sets the number of surface dots per sphere to use.
+     *
+     * This function must be called before calculate() to set the desired
+     * accuracy/computational cost.
+     */
+    void setDotCount(int dotCount);
+    /*! \brief
+     * Sets the radii of spheres to use in the calculation.
+     *
+     * \param[in]  radius  Radius for each atom/sphere.
+     *
+     * This function must be called before calculate() to set the radii for
+     * the spheres.  All calculations must use the same set of radii to
+     * share the same grid search.
+     * These radii are used as-is, without adding any probe radius.
+     * The passed array must remain valid for the lifetime of this object.
+     *
+     * Does not throw.
+     */
+    void setRadii(const ArrayRef<const real>& radius);
+
+    /*! \brief
+     * Requests calculation of volume.
+     *
+     * If not called, and FLAG_VOLUME is not passed to calculate(), the
+     * volume output is not produced.
+     *
+     * Does not throw.
+     */
+    void setCalculateVolume(bool bVolume);
+    /*! \brief
+     * Requests output of per-atom areas.
+     *
+     * If not called, and FLAG_ATOM_AREA is not passed to calculate(), the
+     * atom area output is not produced.
+     *
+     * Does not throw.
+     */
+    void setCalculateAtomArea(bool bAtomArea);
+    /*! \brief
+     * Requests output of all surface dots.
+     *
+     * If not called, and FLAG_DOTS is not passed to calculate(), the
+     * surface dot output is not produced.
+     *
+     * Does not throw.
+     */
+    void setCalculateSurfaceDots(bool bDots);
+
+    /*! \brief
+     * Calculates the surface area for a set of positions.
+     *
+     * \param[in]  x       Atom positions (sphere centers).
+     * \param[in]  pbc     PBC information (if `NULL`, calculation is done
+     *     without PBC).
+     * \param[in]  nat     Number of atoms to calculate.
+     * \param[in]  index   Atom indices to include in the calculation.
+     * \param[in]  flags   Additional flags for the calculation.
+     * \param[out] area    Total surface area (must be non-`NULL`).
+     * \param[out] volume  Total volume (can be `NULL`).
+     * \param[out] at_area Surface area for each atom in \p index
+     *     (\p nat values) (can be `NULL`).
+     * \param[out] lidots  Surface dots as x,y,z triplets (`3*lidots` values)
+     *     (can be `NULL`).
+     * \param[out] n_dots Number of surface dots in \p lidots
+     *     (can be `NULL`).
+     *
+     * Calculates the surface area of spheres centered at `x[index[0]]`,
+     * ..., `x[index[nat-1]]`, with radii `radii[index[0]]`, ..., where
+     * `radii` is the array passed to setRadii().
+     *
+     * If \p flags is 0, the calculation is done for the items specified
+     * with setCalculateVolume(), setCalculateAtomArea(), and
+     * setCalculateSurfaceDots().  Flags can specify FLAG_VOLUME,
+     * FLAG_ATOM_AREA, and/or FLAG_DOTS to request additional output for
+     * this particular calculation.  If any output is `NULL`, that output
+     * is not calculated, irrespective of the calculation mode set.
+     *
+     * \todo
+     * Make the output options more C++-like, in particular for the array
+     * outputs.
+     */
+    void calculate(const rvec*  x,
+                   const t_pbc* pbc,
+                   int          nat,
+                   int          index[],
+                   int          flags,
+                   real*        area,
+                   real*        volume,
+                   real**       at_area,
+                   real**       lidots,
+                   int*         n_dots) const;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules/trajectory.h b/src/include/gromacs/trajectoryanalysis/modules/trajectory.h
new file mode 100644 (file)
index 0000000..260fe7b
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares trajectory analysis module for basic trajectory information.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_MODULES_TRAJECTORY_H
+#define GMX_TRAJECTORYANALYSIS_MODULES_TRAJECTORY_H
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+namespace gmx
+{
+
+namespace analysismodules
+{
+
+class TrajectoryInfo
+{
+public:
+    static const char                      name[];
+    static const char                      shortDescription[];
+    static TrajectoryAnalysisModulePointer create();
+};
+
+} // namespace analysismodules
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/modules/unionfind.h b/src/include/gromacs/trajectoryanalysis/modules/unionfind.h
new file mode 100644 (file)
index 0000000..0b12357
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Implements gmx::UnionFinder and gmx::MappedUnionFinder.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_UNIONFIND_H
+#define GMX_TRAJECTORYANALYSIS_UNIONFIND_H
+
+#include <algorithm>
+#include <numeric>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Union-find data structure for keeping track of disjoint sets.
+ *
+ * Union-find keeps track of a number of items, represented here by continuous
+ * integer indices starting at zero, and supports the following operations:
+ *  - Initialization puts each item into a set of its own.
+ *  - Given two items, merge the sets that contain these items.
+ *  - Given an item, find a representative item that is in the same set, such
+ *    that queries for items in the same set yield the same value.
+ * Merging and querying is supported in amortized constant time.
+ *
+ * Note that in order to achieve the amortized behavior, querying the structure
+ * modifies the internal state, but does not alter the externally visible
+ * behavior.
+ *
+ * \ingroup module_trajectoryanalysis
+ */
+class UnionFinder
+{
+public:
+    /*! \brief
+     * Initializes `count` items, putting each in its own set.
+     */
+    void init(int count)
+    {
+        parent_.clear();
+        rank_.clear();
+        parent_.reserve(count);
+        rank_.reserve(count);
+        parent_.resize(count);
+        rank_.resize(count, 0);
+        std::iota(parent_.begin(), parent_.end(), 0);
+    }
+    /*! \brief
+     * Merges sets that contain two given items.
+     *
+     * If the items are already in the same set, nothing happens.
+     */
+    void merge(int item1, int item2)
+    {
+        GMX_ASSERT(item1 >= 0 && item1 < count(), "Input index out of range");
+        GMX_ASSERT(item2 >= 0 && item2 < count(), "Input index out of range");
+        const int root1 = findRootAndCompressPath(item1);
+        const int root2 = findRootAndCompressPath(item2);
+        if (root1 != root2)
+        {
+            mergeRoots(root1, root2);
+        }
+    }
+    /*! \brief
+     * Returns a representative item from the set containing `item`.
+     */
+    int representativeItem(int item)
+    {
+        GMX_ASSERT(item >= 0 && item < count(), "Input index out of range");
+        return findRootAndCompressPath(item);
+    }
+    /*! \brief
+     * Returns the sizes of all sets (in arbitrary order).
+     */
+    std::vector<int> allSizes()
+    {
+        const int        count = parent_.size();
+        std::vector<int> result(count, 0);
+        for (int i = 0; i < count; ++i)
+        {
+            ++result[findRootAndCompressPath(i)];
+        }
+        result.erase(std::remove(result.begin(), result.end(), 0), result.end());
+        return result;
+    }
+
+private:
+    //! Number of items.
+    int count() const { return parent_.size(); }
+    int findRootAndCompressPath(int i)
+    {
+        while (parent_[i] != i)
+        {
+            const int prev = i;
+            i              = parent_[i];
+            parent_[prev]  = parent_[i];
+        }
+        return i;
+    }
+    void mergeRoots(int root1, int root2)
+    {
+        if (rank_[root1] > rank_[root2])
+        {
+            parent_[root2] = root1;
+        }
+        else if (rank_[root2] > rank_[root1])
+        {
+            parent_[root1] = root2;
+        }
+        else
+        {
+            parent_[root1] = root2;
+            ++rank_[root1];
+        }
+    }
+
+    /*! \brief
+     * Parent item for each item in the tree representing the set.
+     *
+     * Root items are parents of themselves, and are the reprensentative
+     * items of their sets.
+     */
+    std::vector<int> parent_;
+    //! Worst-case height for each root (as if no compression was done).
+    std::vector<int> rank_;
+};
+
+/*! \libinternal \brief
+ * Extension of UnionFind that supports non-consecutive integer indices as
+ * items.
+ *
+ * Sometimes, it is more convenient to operate on a set of integers that do not
+ * start at zero and are not consecutive as UnionFind expects.  This class
+ * implements a mapping on top of UnionFind such that this is possible.
+ *
+ * The current implementation assumes that the indices are bounded between zero
+ * and some reasonably small integer, i.e., the memory usage depends on the
+ * largest index number, not just on the number of items.
+ *
+ * \ingroup module_trajectoryanalysis
+ */
+class MappedUnionFinder
+{
+public:
+    /*! \brief
+     * Initializes the finder with indices.
+     *
+     * The size of `indices` sets the number of input items, and each
+     * unique value in `indices` maps to a single internal item.
+     * If multiple indices are the same, then these items are considered
+     * equivalent.
+     */
+    void initWithGroupIndices(ArrayRef<const int> indices)
+    {
+        mapping_.clear();
+        int groupCount = 0;
+        if (!indices.empty())
+        {
+            const int maxIndex = *std::max_element(indices.begin(), indices.end());
+            mapping_.resize(maxIndex + 1, -1);
+            for (int item : indices)
+            {
+                GMX_ASSERT(item >= 0, "Negative group numbers not supported");
+                if (mapping_[item] == -1)
+                {
+                    mapping_[item] = groupCount;
+                    ++groupCount;
+                }
+            }
+        }
+        finder_.init(groupCount);
+    }
+    /*! \brief
+     * Returns a reprensetative value for an item that is unique for each
+     * set.
+     *
+     * `group` should be one of the values that were passed in as an index
+     * to initWithGroupIndices().
+     * The return value is an internal index that has no simple relation to
+     * the input indices.
+     */
+    int representativeValue(int group)
+    {
+        GMX_ASSERT(group >= 0 && group < maxGroupNumber(), "Input value out of range");
+        GMX_ASSERT(mapping_[group] != -1, "Input value not in initialization set");
+        return finder_.representativeItem(mapping_[group]);
+    }
+    /*! \brief
+     * Merges sets that contain two given items.
+     *
+     * If the items are already in the same set, nothing happens.
+     * Each input value should be one of the values that were passed in as
+     * an index to initWithGroupIndices().
+     */
+    void mergeGroups(int group1, int group2)
+    {
+        GMX_ASSERT(group1 >= 0 && group1 < maxGroupNumber(), "Input value out of range");
+        GMX_ASSERT(group2 >= 0 && group2 < maxGroupNumber(), "Input value out of range");
+        GMX_ASSERT(mapping_[group1] != -1, "Input value not in initialization set");
+        GMX_ASSERT(mapping_[group2] != -1, "Input value not in initialization set");
+        finder_.merge(mapping_[group1], mapping_[group2]);
+    }
+    /*! \brief
+     * Returns the sizes of all sets (in arbitrary order).
+     *
+     * If there were multiple identical indices passed to
+     * initWithGroupIndices(), these are only counted as one when
+     * computing the sizes.
+     */
+    std::vector<int> allSizes() { return finder_.allSizes(); }
+
+private:
+    int maxGroupNumber() const { return mapping_.size(); }
+
+    UnionFinder finder_;
+    //! Mapping from input indices to zero-based indices used by finder_.
+    std::vector<int> mapping_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/runnercommon.h b/src/include/gromacs/trajectoryanalysis/runnercommon.h
new file mode 100644 (file)
index 0000000..91d936d
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2014 by the GROMACS development team.
+ * Copyright (c) 2015,2016,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares gmx::TrajectoryAnalysisRunnerCommon.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_RUNNERCOMMON_H
+#define GMX_TRAJECTORYANALYSIS_RUNNERCOMMON_H
+
+#include <memory>
+
+struct t_trxframe;
+
+namespace gmx
+{
+
+class IOptionsContainer;
+class ITopologyProvider;
+class SelectionCollection;
+class TimeUnitBehavior;
+class TopologyInformation;
+class TrajectoryAnalysisSettings;
+
+/*! \internal
+ * \brief
+ * Implements common trajectory analysis runner functionality.
+ *
+ * As there is currently only one runner (TrajectoryAnalysisCommandLineRunner),
+ * the division of responsibilities is not yet very clear.
+ *
+ * \ingroup module_trajectoryanalysis
+ */
+class TrajectoryAnalysisRunnerCommon
+{
+public:
+    /*! \brief
+     * Initializes a new runner helper.
+     *
+     * \param    settings  Settings object to use.
+     */
+    explicit TrajectoryAnalysisRunnerCommon(TrajectoryAnalysisSettings* settings);
+    ~TrajectoryAnalysisRunnerCommon();
+
+    //! Returns a topology provider for SelectionOptionBehavior.
+    ITopologyProvider* topologyProvider();
+
+    /*! \brief
+     * Initializes common options for trajectory analysis.
+     *
+     * \param[in,out] options  Options object to add the options to.
+     * \param[in,out] timeUnitBehavior  Time unit behavior to use for adding
+     *    and handling the `-tu` option.
+     */
+    void initOptions(IOptionsContainer* options, TimeUnitBehavior* timeUnitBehavior);
+    //! Processes common option values after they have been parsed.
+    void optionsFinished();
+    //! Load topology information if provided and/or required.
+    void initTopology();
+    /*! \brief
+     * Reads the first frame from the trajectory.
+     *
+     * After this call, frame() returns the first frame.
+     */
+    void initFirstFrame();
+    /*! \brief
+     * Initializes the index in frame() that specifies the atoms contained.
+     *
+     * Can be called after selections have been compiled.
+     */
+    void initFrameIndexGroup();
+    /*! \brief
+     * Reads the next frame from the trajectory.
+     *
+     * \returns false if there were no more frames.
+     *
+     * After this call, frame() returns the newly loaded frame.
+     */
+    bool readNextFrame();
+    /*! \brief
+     * Performs common initialization for the currently loaded frame.
+     *
+     * Currently, makes molecules whole if requested.
+     */
+    void initFrame();
+
+    //! Returns true if input data comes from a trajectory.
+    bool hasTrajectory() const;
+    //! Returns the topology information object.
+    const TopologyInformation& topologyInformation() const;
+    //! Returns the currently loaded frame.
+    t_trxframe& frame() const;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/tests/lysozyme.top b/src/include/gromacs/trajectoryanalysis/tests/lysozyme.top
new file mode 100644 (file)
index 0000000..a80dfc9
--- /dev/null
@@ -0,0 +1,1465 @@
+; Include forcefield parameters
+#include "oplsaa.ff/forcefield.itp"
+
+[ moleculetype ]
+; Name            nrexcl
+Protein_chain_B     3
+
+[ atoms ]
+;   nr       type  resnr residue  atom   cgnr     charge       mass  typeB    chargeB      massB
+; residue   1 LYS rtp LYSH q +2.0
+     1   opls_287      1    LYS      N      1       -0.3    14.0027   ; qtot -0.3
+     2   opls_290      1    LYS     H1      1       0.33      1.008   ; qtot 0.03
+     3   opls_290      1    LYS     H2      1       0.33      1.008   ; qtot 0.36
+     4   opls_290      1    LYS     H3      1       0.33      1.008   ; qtot 0.69
+     5  opls_293B      1    LYS     CA      1       0.25     12.011   ; qtot 0.94
+     6   opls_140      1    LYS     HA      1       0.06      1.008   ; qtot 1
+     7   opls_136      1    LYS     CB      2      -0.12     12.011   ; qtot 0.88
+     8   opls_140      1    LYS    HB1      2       0.06      1.008   ; qtot 0.94
+     9   opls_140      1    LYS    HB2      2       0.06      1.008   ; qtot 1
+    10   opls_136      1    LYS     CG      3      -0.12     12.011   ; qtot 0.88
+    11   opls_140      1    LYS    HG1      3       0.06      1.008   ; qtot 0.94
+    12   opls_140      1    LYS    HG2      3       0.06      1.008   ; qtot 1
+    13   opls_136      1    LYS     CD      4      -0.12     12.011   ; qtot 0.88
+    14   opls_140      1    LYS    HD1      4       0.06      1.008   ; qtot 0.94
+    15   opls_140      1    LYS    HD2      4       0.06      1.008   ; qtot 1
+    16   opls_292      1    LYS     CE      5       0.19     12.011   ; qtot 1.19
+    17   opls_140      1    LYS    HE1      5       0.06      1.008   ; qtot 1.25
+    18   opls_140      1    LYS    HE2      5       0.06      1.008   ; qtot 1.31
+    19   opls_287      1    LYS     NZ      6       -0.3    14.0067   ; qtot 1.01
+    20   opls_290      1    LYS    HZ1      6       0.33      1.008   ; qtot 1.34
+    21   opls_290      1    LYS    HZ2      6       0.33      1.008   ; qtot 1.67
+    22   opls_290      1    LYS    HZ3      6       0.33      1.008   ; qtot 2
+    23   opls_235      1    LYS      C      7        0.5     12.011   ; qtot 2.5
+    24   opls_236      1    LYS      O      7       -0.5    15.9994   ; qtot 2
+; residue   2 VAL rtp VAL  q  0.0
+    25   opls_238      2    VAL      N      8       -0.5    14.0067   ; qtot 1.5
+    26   opls_241      2    VAL      H      8        0.3      1.008   ; qtot 1.8
+    27  opls_224B      2    VAL     CA      8       0.14     12.011   ; qtot 1.94
+    28   opls_140      2    VAL     HA      8       0.06      1.008   ; qtot 2
+    29   opls_137      2    VAL     CB      9      -0.06     12.011   ; qtot 1.94
+    30   opls_140      2    VAL     HB      9       0.06      1.008   ; qtot 2
+    31   opls_135      2    VAL    CG1     10      -0.18     12.011   ; qtot 1.82
+    32   opls_140      2    VAL   HG11     10       0.06      1.008   ; qtot 1.88
+    33   opls_140      2    VAL   HG12     10       0.06      1.008   ; qtot 1.94
+    34   opls_140      2    VAL   HG13     10       0.06      1.008   ; qtot 2
+    35   opls_135      2    VAL    CG2     11      -0.18     12.011   ; qtot 1.82
+    36   opls_140      2    VAL   HG21     11       0.06      1.008   ; qtot 1.88
+    37   opls_140      2    VAL   HG22     11       0.06      1.008   ; qtot 1.94
+    38   opls_140      2    VAL   HG23     11       0.06      1.008   ; qtot 2
+    39   opls_235      2    VAL      C     12        0.5     12.011   ; qtot 2.5
+    40   opls_236      2    VAL      O     12       -0.5    15.9994   ; qtot 2
+; residue   3 PHE rtp PHE  q  0.0
+    41   opls_238      3    PHE      N     13       -0.5    14.0067   ; qtot 1.5
+    42   opls_241      3    PHE      H     13        0.3      1.008   ; qtot 1.8
+    43  opls_224B      3    PHE     CA     13       0.14     12.011   ; qtot 1.94
+    44   opls_140      3    PHE     HA     13       0.06      1.008   ; qtot 2
+    45   opls_149      3    PHE     CB     14     -0.005     12.011   ; qtot 1.995
+    46   opls_140      3    PHE    HB1     14       0.06      1.008   ; qtot 2.055
+    47   opls_140      3    PHE    HB2     14       0.06      1.008   ; qtot 2.115
+    48   opls_145      3    PHE     CG     14     -0.115     12.011   ; qtot 2
+    49   opls_145      3    PHE    CD1     15     -0.115     12.011   ; qtot 1.885
+    50   opls_146      3    PHE    HD1     15      0.115      1.008   ; qtot 2
+    51   opls_145      3    PHE    CD2     16     -0.115     12.011   ; qtot 1.885
+    52   opls_146      3    PHE    HD2     16      0.115      1.008   ; qtot 2
+    53   opls_145      3    PHE    CE1     17     -0.115     12.011   ; qtot 1.885
+    54   opls_146      3    PHE    HE1     17      0.115      1.008   ; qtot 2
+    55   opls_145      3    PHE    CE2     18     -0.115     12.011   ; qtot 1.885
+    56   opls_146      3    PHE    HE2     18      0.115      1.008   ; qtot 2
+    57   opls_145      3    PHE     CZ     19     -0.115     12.011   ; qtot 1.885
+    58   opls_146      3    PHE     HZ     19      0.115      1.008   ; qtot 2
+    59   opls_235      3    PHE      C     20        0.5     12.011   ; qtot 2.5
+    60   opls_236      3    PHE      O     20       -0.5    15.9994   ; qtot 2
+; residue   4 GLY rtp GLY  q  0.0
+    61   opls_238      4    GLY      N     21       -0.5    14.0067   ; qtot 1.5
+    62   opls_241      4    GLY      H     21        0.3      1.008   ; qtot 1.8
+    63  opls_223B      4    GLY     CA     21       0.08     12.011   ; qtot 1.88
+    64   opls_140      4    GLY    HA1     21       0.06      1.008   ; qtot 1.94
+    65   opls_140      4    GLY    HA2     21       0.06      1.008   ; qtot 2
+    66   opls_235      4    GLY      C     22        0.5     12.011   ; qtot 2.5
+    67   opls_236      4    GLY      O     22       -0.5    15.9994   ; qtot 2
+; residue   5 ARG rtp ARG  q +1.0
+    68   opls_238      5    ARG      N     23       -0.5    14.0067   ; qtot 1.5
+    69   opls_241      5    ARG      H     23        0.3      1.008   ; qtot 1.8
+    70  opls_224B      5    ARG     CA     23       0.14     12.011   ; qtot 1.94
+    71   opls_140      5    ARG     HA     23       0.06      1.008   ; qtot 2
+    72   opls_136      5    ARG     CB     24      -0.12     12.011   ; qtot 1.88
+    73   opls_140      5    ARG    HB1     24       0.06      1.008   ; qtot 1.94
+    74   opls_140      5    ARG    HB2     24       0.06      1.008   ; qtot 2
+    75   opls_308      5    ARG     CG     25      -0.05     12.011   ; qtot 1.95
+    76   opls_140      5    ARG    HG1     25       0.06      1.008   ; qtot 2.01
+    77   opls_140      5    ARG    HG2     25       0.06      1.008   ; qtot 2.07
+    78   opls_307      5    ARG     CD     26       0.19     12.011   ; qtot 2.26
+    79   opls_140      5    ARG    HD1     26       0.06      1.008   ; qtot 2.32
+    80   opls_140      5    ARG    HD2     26       0.06      1.008   ; qtot 2.38
+    81   opls_303      5    ARG     NE     27       -0.7    14.0067   ; qtot 1.68
+    82   opls_304      5    ARG     HE     27       0.44      1.008   ; qtot 2.12
+    83   opls_302      5    ARG     CZ     27       0.64     12.011   ; qtot 2.76
+    84   opls_300      5    ARG    NH1     28       -0.8    14.0067   ; qtot 1.96
+    85   opls_301      5    ARG   HH11     28       0.46      1.008   ; qtot 2.42
+    86   opls_301      5    ARG   HH12     28       0.46      1.008   ; qtot 2.88
+    87   opls_300      5    ARG    NH2     29       -0.8    14.0067   ; qtot 2.08
+    88   opls_301      5    ARG   HH21     29       0.46      1.008   ; qtot 2.54
+    89   opls_301      5    ARG   HH22     29       0.46      1.008   ; qtot 3
+    90   opls_235      5    ARG      C     30        0.5     12.011   ; qtot 3.5
+    91   opls_236      5    ARG      O     30       -0.5    15.9994   ; qtot 3
+; residue   6 CYS rtp CYSH q  0.0
+    92   opls_238      6    CYS      N     31       -0.5    14.0067   ; qtot 2.5
+    93   opls_241      6    CYS      H     31        0.3      1.008   ; qtot 2.8
+    94  opls_224B      6    CYS     CA     31       0.14     12.011   ; qtot 2.94
+    95   opls_140      6    CYS     HA     31       0.06      1.008   ; qtot 3
+    96   opls_206      6    CYS     CB     32       0.06     12.011   ; qtot 3.06
+    97   opls_140      6    CYS    HB1     32       0.06      1.008   ; qtot 3.12
+    98   opls_140      6    CYS    HB2     32       0.06      1.008   ; qtot 3.18
+    99   opls_200      6    CYS     SG     33     -0.335      32.06   ; qtot 2.845
+   100   opls_204      6    CYS     HG     33      0.155      1.008   ; qtot 3
+   101   opls_235      6    CYS      C     34        0.5     12.011   ; qtot 3.5
+   102   opls_236      6    CYS      O     34       -0.5    15.9994   ; qtot 3
+; residue   7 GLU rtp GLU  q -1.0
+   103   opls_238      7    GLU      N     35       -0.5    14.0067   ; qtot 2.5
+   104   opls_241      7    GLU      H     35        0.3      1.008   ; qtot 2.8
+   105  opls_224B      7    GLU     CA     35       0.14     12.011   ; qtot 2.94
+   106   opls_140      7    GLU     HA     35       0.06      1.008   ; qtot 3
+   107   opls_136      7    GLU     CB     36      -0.12     12.011   ; qtot 2.88
+   108   opls_140      7    GLU    HB1     36       0.06      1.008   ; qtot 2.94
+   109   opls_140      7    GLU    HB2     36       0.06      1.008   ; qtot 3
+   110   opls_274      7    GLU     CG     37      -0.22     12.011   ; qtot 2.78
+   111   opls_140      7    GLU    HG1     37       0.06      1.008   ; qtot 2.84
+   112   opls_140      7    GLU    HG2     37       0.06      1.008   ; qtot 2.9
+   113   opls_271      7    GLU     CD     38        0.7     12.011   ; qtot 3.6
+   114   opls_272      7    GLU    OE1     38       -0.8    15.9994   ; qtot 2.8
+   115   opls_272      7    GLU    OE2     38       -0.8    15.9994   ; qtot 2
+   116   opls_235      7    GLU      C     39        0.5     12.011   ; qtot 2.5
+   117   opls_236      7    GLU      O     39       -0.5    15.9994   ; qtot 2
+; residue   8 LEU rtp LEU  q  0.0
+   118   opls_238      8    LEU      N     40       -0.5    14.0067   ; qtot 1.5
+   119   opls_241      8    LEU      H     40        0.3      1.008   ; qtot 1.8
+   120  opls_224B      8    LEU     CA     40       0.14     12.011   ; qtot 1.94
+   121   opls_140      8    LEU     HA     40       0.06      1.008   ; qtot 2
+   122   opls_136      8    LEU     CB     41      -0.12     12.011   ; qtot 1.88
+   123   opls_140      8    LEU    HB1     41       0.06      1.008   ; qtot 1.94
+   124   opls_140      8    LEU    HB2     41       0.06      1.008   ; qtot 2
+   125   opls_137      8    LEU     CG     42      -0.06     12.011   ; qtot 1.94
+   126   opls_140      8    LEU     HG     42       0.06      1.008   ; qtot 2
+   127   opls_135      8    LEU    CD1     43      -0.18     12.011   ; qtot 1.82
+   128   opls_140      8    LEU   HD11     43       0.06      1.008   ; qtot 1.88
+   129   opls_140      8    LEU   HD12     43       0.06      1.008   ; qtot 1.94
+   130   opls_140      8    LEU   HD13     43       0.06      1.008   ; qtot 2
+   131   opls_135      8    LEU    CD2     44      -0.18     12.011   ; qtot 1.82
+   132   opls_140      8    LEU   HD21     44       0.06      1.008   ; qtot 1.88
+   133   opls_140      8    LEU   HD22     44       0.06      1.008   ; qtot 1.94
+   134   opls_140      8    LEU   HD23     44       0.06      1.008   ; qtot 2
+   135   opls_235      8    LEU      C     45        0.5     12.011   ; qtot 2.5
+   136   opls_236      8    LEU      O     45       -0.5    15.9994   ; qtot 2
+; residue   9 ALA rtp ALA  q  0.0
+   137   opls_238      9    ALA      N     46       -0.5    14.0067   ; qtot 1.5
+   138   opls_241      9    ALA      H     46        0.3      1.008   ; qtot 1.8
+   139  opls_224B      9    ALA     CA     46       0.14     12.011   ; qtot 1.94
+   140   opls_140      9    ALA     HA     46       0.06      1.008   ; qtot 2
+   141   opls_135      9    ALA     CB     47      -0.18     12.011   ; qtot 1.82
+   142   opls_140      9    ALA    HB1     47       0.06      1.008   ; qtot 1.88
+   143   opls_140      9    ALA    HB2     47       0.06      1.008   ; qtot 1.94
+   144   opls_140      9    ALA    HB3     47       0.06      1.008   ; qtot 2
+   145   opls_235      9    ALA      C     48        0.5     12.011   ; qtot 2.5
+   146   opls_236      9    ALA      O     48       -0.5    15.9994   ; qtot 2
+; residue  10 ALA rtp ALA  q  0.0
+   147   opls_238     10    ALA      N     49       -0.5    14.0067   ; qtot 1.5
+   148   opls_241     10    ALA      H     49        0.3      1.008   ; qtot 1.8
+   149  opls_224B     10    ALA     CA     49       0.14     12.011   ; qtot 1.94
+   150   opls_140     10    ALA     HA     49       0.06      1.008   ; qtot 2
+   151   opls_135     10    ALA     CB     50      -0.18     12.011   ; qtot 1.82
+   152   opls_140     10    ALA    HB1     50       0.06      1.008   ; qtot 1.88
+   153   opls_140     10    ALA    HB2     50       0.06      1.008   ; qtot 1.94
+   154   opls_140     10    ALA    HB3     50       0.06      1.008   ; qtot 2
+   155   opls_235     10    ALA      C     51        0.5     12.011   ; qtot 2.5
+   156   opls_236     10    ALA      O     51       -0.5    15.9994   ; qtot 2
+
+[ bonds ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     2     1
+    1     3     1
+    1     4     1
+    1     5     1
+    5     6     1
+    5     7     1
+    5    23     1
+    7     8     1
+    7     9     1
+    7    10     1
+   10    11     1
+   10    12     1
+   10    13     1
+   13    14     1
+   13    15     1
+   13    16     1
+   16    17     1
+   16    18     1
+   16    19     1
+   19    20     1
+   19    21     1
+   19    22     1
+   23    24     1
+   23    25     1
+   25    26     1
+   25    27     1
+   27    28     1
+   27    29     1
+   27    39     1
+   29    30     1
+   29    31     1
+   29    35     1
+   31    32     1
+   31    33     1
+   31    34     1
+   35    36     1
+   35    37     1
+   35    38     1
+   39    40     1
+   39    41     1
+   41    42     1
+   41    43     1
+   43    44     1
+   43    45     1
+   43    59     1
+   45    46     1
+   45    47     1
+   45    48     1
+   48    49     1
+   48    51     1
+   49    50     1
+   49    53     1
+   51    52     1
+   51    55     1
+   53    54     1
+   53    57     1
+   55    56     1
+   55    57     1
+   57    58     1
+   59    60     1
+   59    61     1
+   61    62     1
+   61    63     1
+   63    64     1
+   63    65     1
+   63    66     1
+   66    67     1
+   66    68     1
+   68    69     1
+   68    70     1
+   70    71     1
+   70    72     1
+   70    90     1
+   72    73     1
+   72    74     1
+   72    75     1
+   75    76     1
+   75    77     1
+   75    78     1
+   78    79     1
+   78    80     1
+   78    81     1
+   81    82     1
+   81    83     1
+   83    84     1
+   83    87     1
+   84    85     1
+   84    86     1
+   87    88     1
+   87    89     1
+   90    91     1
+   90    92     1
+   92    93     1
+   92    94     1
+   94    95     1
+   94    96     1
+   94   101     1
+   96    97     1
+   96    98     1
+   96    99     1
+   99   100     1
+  101   102     1
+  101   103     1
+  103   104     1
+  103   105     1
+  105   106     1
+  105   107     1
+  105   116     1
+  107   108     1
+  107   109     1
+  107   110     1
+  110   111     1
+  110   112     1
+  110   113     1
+  113   114     1
+  113   115     1
+  116   117     1
+  116   118     1
+  118   119     1
+  118   120     1
+  120   121     1
+  120   122     1
+  120   135     1
+  122   123     1
+  122   124     1
+  122   125     1
+  125   126     1
+  125   127     1
+  125   131     1
+  127   128     1
+  127   129     1
+  127   130     1
+  131   132     1
+  131   133     1
+  131   134     1
+  135   136     1
+  135   137     1
+  137   138     1
+  137   139     1
+  139   140     1
+  139   141     1
+  139   145     1
+  141   142     1
+  141   143     1
+  141   144     1
+  145   146     1
+  145   147     1
+  147   148     1
+  147   149     1
+  149   150     1
+  149   151     1
+  149   155     1
+  151   152     1
+  151   153     1
+  151   154     1
+  155   156     1
+
+[ pairs ]
+;  ai    aj funct            c0            c1            c2            c3
+    1     8     1
+    1     9     1
+    1    10     1
+    1    24     1
+    1    25     1
+    2     6     1
+    2     7     1
+    2    23     1
+    3     6     1
+    3     7     1
+    3    23     1
+    4     6     1
+    4     7     1
+    4    23     1
+    5    11     1
+    5    12     1
+    5    13     1
+    5    26     1
+    5    27     1
+    6     8     1
+    6     9     1
+    6    10     1
+    6    24     1
+    6    25     1
+    7    14     1
+    7    15     1
+    7    16     1
+    7    24     1
+    7    25     1
+    8    11     1
+    8    12     1
+    8    13     1
+    8    23     1
+    9    11     1
+    9    12     1
+    9    13     1
+    9    23     1
+   10    17     1
+   10    18     1
+   10    19     1
+   10    23     1
+   11    14     1
+   11    15     1
+   11    16     1
+   12    14     1
+   12    15     1
+   12    16     1
+   13    20     1
+   13    21     1
+   13    22     1
+   14    17     1
+   14    18     1
+   14    19     1
+   15    17     1
+   15    18     1
+   15    19     1
+   17    20     1
+   17    21     1
+   17    22     1
+   18    20     1
+   18    21     1
+   18    22     1
+   23    28     1
+   23    29     1
+   23    39     1
+   24    26     1
+   24    27     1
+   25    30     1
+   25    31     1
+   25    35     1
+   25    40     1
+   25    41     1
+   26    28     1
+   26    29     1
+   26    39     1
+   27    32     1
+   27    33     1
+   27    34     1
+   27    36     1
+   27    37     1
+   27    38     1
+   27    42     1
+   27    43     1
+   28    30     1
+   28    31     1
+   28    35     1
+   28    40     1
+   28    41     1
+   29    40     1
+   29    41     1
+   30    32     1
+   30    33     1
+   30    34     1
+   30    36     1
+   30    37     1
+   30    38     1
+   30    39     1
+   31    36     1
+   31    37     1
+   31    38     1
+   31    39     1
+   32    35     1
+   33    35     1
+   34    35     1
+   35    39     1
+   39    44     1
+   39    45     1
+   39    59     1
+   40    42     1
+   40    43     1
+   41    46     1
+   41    47     1
+   41    48     1
+   41    60     1
+   41    61     1
+   42    44     1
+   42    45     1
+   42    59     1
+   43    49     1
+   43    51     1
+   43    62     1
+   43    63     1
+   44    46     1
+   44    47     1
+   44    48     1
+   44    60     1
+   44    61     1
+   45    50     1
+   45    52     1
+   45    53     1
+   45    55     1
+   45    60     1
+   45    61     1
+   46    49     1
+   46    51     1
+   46    59     1
+   47    49     1
+   47    51     1
+   47    59     1
+   48    54     1
+   48    56     1
+   48    57     1
+   48    59     1
+   49    52     1
+   49    55     1
+   49    58     1
+   50    51     1
+   50    54     1
+   50    57     1
+   51    53     1
+   51    58     1
+   52    56     1
+   52    57     1
+   53    56     1
+   54    55     1
+   54    58     1
+   56    58     1
+   59    64     1
+   59    65     1
+   59    66     1
+   60    62     1
+   60    63     1
+   61    67     1
+   61    68     1
+   62    64     1
+   62    65     1
+   62    66     1
+   63    69     1
+   63    70     1
+   64    67     1
+   64    68     1
+   65    67     1
+   65    68     1
+   66    71     1
+   66    72     1
+   66    90     1
+   67    69     1
+   67    70     1
+   68    73     1
+   68    74     1
+   68    75     1
+   68    91     1
+   68    92     1
+   69    71     1
+   69    72     1
+   69    90     1
+   70    76     1
+   70    77     1
+   70    78     1
+   70    93     1
+   70    94     1
+   71    73     1
+   71    74     1
+   71    75     1
+   71    91     1
+   71    92     1
+   72    79     1
+   72    80     1
+   72    81     1
+   72    91     1
+   72    92     1
+   73    76     1
+   73    77     1
+   73    78     1
+   73    90     1
+   74    76     1
+   74    77     1
+   74    78     1
+   74    90     1
+   75    82     1
+   75    83     1
+   75    90     1
+   76    79     1
+   76    80     1
+   76    81     1
+   77    79     1
+   77    80     1
+   77    81     1
+   78    84     1
+   78    87     1
+   79    82     1
+   79    83     1
+   80    82     1
+   80    83     1
+   81    85     1
+   81    86     1
+   81    88     1
+   81    89     1
+   82    84     1
+   82    87     1
+   84    88     1
+   84    89     1
+   85    87     1
+   86    87     1
+   90    95     1
+   90    96     1
+   90   101     1
+   91    93     1
+   91    94     1
+   92    97     1
+   92    98     1
+   92    99     1
+   92   102     1
+   92   103     1
+   93    95     1
+   93    96     1
+   93   101     1
+   94   100     1
+   94   104     1
+   94   105     1
+   95    97     1
+   95    98     1
+   95    99     1
+   95   102     1
+   95   103     1
+   96   102     1
+   96   103     1
+   97   100     1
+   97   101     1
+   98   100     1
+   98   101     1
+   99   101     1
+  101   106     1
+  101   107     1
+  101   116     1
+  102   104     1
+  102   105     1
+  103   108     1
+  103   109     1
+  103   110     1
+  103   117     1
+  103   118     1
+  104   106     1
+  104   107     1
+  104   116     1
+  105   111     1
+  105   112     1
+  105   113     1
+  105   119     1
+  105   120     1
+  106   108     1
+  106   109     1
+  106   110     1
+  106   117     1
+  106   118     1
+  107   114     1
+  107   115     1
+  107   117     1
+  107   118     1
+  108   111     1
+  108   112     1
+  108   113     1
+  108   116     1
+  109   111     1
+  109   112     1
+  109   113     1
+  109   116     1
+  110   116     1
+  111   114     1
+  111   115     1
+  112   114     1
+  112   115     1
+  116   121     1
+  116   122     1
+  116   135     1
+  117   119     1
+  117   120     1
+  118   123     1
+  118   124     1
+  118   125     1
+  118   136     1
+  118   137     1
+  119   121     1
+  119   122     1
+  119   135     1
+  120   126     1
+  120   127     1
+  120   131     1
+  120   138     1
+  120   139     1
+  121   123     1
+  121   124     1
+  121   125     1
+  121   136     1
+  121   137     1
+  122   128     1
+  122   129     1
+  122   130     1
+  122   132     1
+  122   133     1
+  122   134     1
+  122   136     1
+  122   137     1
+  123   126     1
+  123   127     1
+  123   131     1
+  123   135     1
+  124   126     1
+  124   127     1
+  124   131     1
+  124   135     1
+  125   135     1
+  126   128     1
+  126   129     1
+  126   130     1
+  126   132     1
+  126   133     1
+  126   134     1
+  127   132     1
+  127   133     1
+  127   134     1
+  128   131     1
+  129   131     1
+  130   131     1
+  135   140     1
+  135   141     1
+  135   145     1
+  136   138     1
+  136   139     1
+  137   142     1
+  137   143     1
+  137   144     1
+  137   146     1
+  137   147     1
+  138   140     1
+  138   141     1
+  138   145     1
+  139   148     1
+  139   149     1
+  140   142     1
+  140   143     1
+  140   144     1
+  140   146     1
+  140   147     1
+  141   146     1
+  141   147     1
+  142   145     1
+  143   145     1
+  144   145     1
+  145   150     1
+  145   151     1
+  145   155     1
+  146   148     1
+  146   149     1
+  147   152     1
+  147   153     1
+  147   154     1
+  147   156     1
+  148   150     1
+  148   151     1
+  148   155     1
+  150   152     1
+  150   153     1
+  150   154     1
+  150   156     1
+  151   156     1
+  152   155     1
+  153   155     1
+  154   155     1
+
+[ angles ]
+;  ai    aj    ak funct            c0            c1            c2            c3
+    2     1     3     1
+    2     1     4     1
+    2     1     5     1
+    3     1     4     1
+    3     1     5     1
+    4     1     5     1
+    1     5     6     1
+    1     5     7     1
+    1     5    23     1
+    6     5     7     1
+    6     5    23     1
+    7     5    23     1
+    5     7     8     1
+    5     7     9     1
+    5     7    10     1
+    8     7     9     1
+    8     7    10     1
+    9     7    10     1
+    7    10    11     1
+    7    10    12     1
+    7    10    13     1
+   11    10    12     1
+   11    10    13     1
+   12    10    13     1
+   10    13    14     1
+   10    13    15     1
+   10    13    16     1
+   14    13    15     1
+   14    13    16     1
+   15    13    16     1
+   13    16    17     1
+   13    16    18     1
+   13    16    19     1
+   17    16    18     1
+   17    16    19     1
+   18    16    19     1
+   16    19    20     1
+   16    19    21     1
+   16    19    22     1
+   20    19    21     1
+   20    19    22     1
+   21    19    22     1
+    5    23    24     1
+    5    23    25     1
+   24    23    25     1
+   23    25    26     1
+   23    25    27     1
+   26    25    27     1
+   25    27    28     1
+   25    27    29     1
+   25    27    39     1
+   28    27    29     1
+   28    27    39     1
+   29    27    39     1
+   27    29    30     1
+   27    29    31     1
+   27    29    35     1
+   30    29    31     1
+   30    29    35     1
+   31    29    35     1
+   29    31    32     1
+   29    31    33     1
+   29    31    34     1
+   32    31    33     1
+   32    31    34     1
+   33    31    34     1
+   29    35    36     1
+   29    35    37     1
+   29    35    38     1
+   36    35    37     1
+   36    35    38     1
+   37    35    38     1
+   27    39    40     1
+   27    39    41     1
+   40    39    41     1
+   39    41    42     1
+   39    41    43     1
+   42    41    43     1
+   41    43    44     1
+   41    43    45     1
+   41    43    59     1
+   44    43    45     1
+   44    43    59     1
+   45    43    59     1
+   43    45    46     1
+   43    45    47     1
+   43    45    48     1
+   46    45    47     1
+   46    45    48     1
+   47    45    48     1
+   45    48    49     1
+   45    48    51     1
+   49    48    51     1
+   48    49    50     1
+   48    49    53     1
+   50    49    53     1
+   48    51    52     1
+   48    51    55     1
+   52    51    55     1
+   49    53    54     1
+   49    53    57     1
+   54    53    57     1
+   51    55    56     1
+   51    55    57     1
+   56    55    57     1
+   53    57    55     1
+   53    57    58     1
+   55    57    58     1
+   43    59    60     1
+   43    59    61     1
+   60    59    61     1
+   59    61    62     1
+   59    61    63     1
+   62    61    63     1
+   61    63    64     1
+   61    63    65     1
+   61    63    66     1
+   64    63    65     1
+   64    63    66     1
+   65    63    66     1
+   63    66    67     1
+   63    66    68     1
+   67    66    68     1
+   66    68    69     1
+   66    68    70     1
+   69    68    70     1
+   68    70    71     1
+   68    70    72     1
+   68    70    90     1
+   71    70    72     1
+   71    70    90     1
+   72    70    90     1
+   70    72    73     1
+   70    72    74     1
+   70    72    75     1
+   73    72    74     1
+   73    72    75     1
+   74    72    75     1
+   72    75    76     1
+   72    75    77     1
+   72    75    78     1
+   76    75    77     1
+   76    75    78     1
+   77    75    78     1
+   75    78    79     1
+   75    78    80     1
+   75    78    81     1
+   79    78    80     1
+   79    78    81     1
+   80    78    81     1
+   78    81    82     1
+   78    81    83     1
+   82    81    83     1
+   81    83    84     1
+   81    83    87     1
+   84    83    87     1
+   83    84    85     1
+   83    84    86     1
+   85    84    86     1
+   83    87    88     1
+   83    87    89     1
+   88    87    89     1
+   70    90    91     1
+   70    90    92     1
+   91    90    92     1
+   90    92    93     1
+   90    92    94     1
+   93    92    94     1
+   92    94    95     1
+   92    94    96     1
+   92    94   101     1
+   95    94    96     1
+   95    94   101     1
+   96    94   101     1
+   94    96    97     1
+   94    96    98     1
+   94    96    99     1
+   97    96    98     1
+   97    96    99     1
+   98    96    99     1
+   96    99   100     1
+   94   101   102     1
+   94   101   103     1
+  102   101   103     1
+  101   103   104     1
+  101   103   105     1
+  104   103   105     1
+  103   105   106     1
+  103   105   107     1
+  103   105   116     1
+  106   105   107     1
+  106   105   116     1
+  107   105   116     1
+  105   107   108     1
+  105   107   109     1
+  105   107   110     1
+  108   107   109     1
+  108   107   110     1
+  109   107   110     1
+  107   110   111     1
+  107   110   112     1
+  107   110   113     1
+  111   110   112     1
+  111   110   113     1
+  112   110   113     1
+  110   113   114     1
+  110   113   115     1
+  114   113   115     1
+  105   116   117     1
+  105   116   118     1
+  117   116   118     1
+  116   118   119     1
+  116   118   120     1
+  119   118   120     1
+  118   120   121     1
+  118   120   122     1
+  118   120   135     1
+  121   120   122     1
+  121   120   135     1
+  122   120   135     1
+  120   122   123     1
+  120   122   124     1
+  120   122   125     1
+  123   122   124     1
+  123   122   125     1
+  124   122   125     1
+  122   125   126     1
+  122   125   127     1
+  122   125   131     1
+  126   125   127     1
+  126   125   131     1
+  127   125   131     1
+  125   127   128     1
+  125   127   129     1
+  125   127   130     1
+  128   127   129     1
+  128   127   130     1
+  129   127   130     1
+  125   131   132     1
+  125   131   133     1
+  125   131   134     1
+  132   131   133     1
+  132   131   134     1
+  133   131   134     1
+  120   135   136     1
+  120   135   137     1
+  136   135   137     1
+  135   137   138     1
+  135   137   139     1
+  138   137   139     1
+  137   139   140     1
+  137   139   141     1
+  137   139   145     1
+  140   139   141     1
+  140   139   145     1
+  141   139   145     1
+  139   141   142     1
+  139   141   143     1
+  139   141   144     1
+  142   141   143     1
+  142   141   144     1
+  143   141   144     1
+  139   145   146     1
+  139   145   147     1
+  146   145   147     1
+  145   147   148     1
+  145   147   149     1
+  148   147   149     1
+  147   149   150     1
+  147   149   151     1
+  147   149   155     1
+  150   149   151     1
+  150   149   155     1
+  151   149   155     1
+  149   151   152     1
+  149   151   153     1
+  149   151   154     1
+  152   151   153     1
+  152   151   154     1
+  153   151   154     1
+  149   155   156     1
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3            c4            c5
+    2     1     5     6     3
+    2     1     5     7     3
+    2     1     5    23     3
+    3     1     5     6     3
+    3     1     5     7     3
+    3     1     5    23     3
+    4     1     5     6     3
+    4     1     5     7     3
+    4     1     5    23     3
+    1     5     7    10     3    dih_LYS_chi1_N_C_C_C
+   23     5     7    10     3    dih_LYS_chi1_C_C_C_CO
+    1     5     7     8     3
+    1     5     7     9     3
+    6     5     7     8     3
+    6     5     7     9     3
+    6     5     7    10     3
+   23     5     7     8     3
+   23     5     7     9     3
+    1     5    23    24     3
+    1     5    23    25     3
+    6     5    23    24     3
+    6     5    23    25     3
+    7     5    23    24     3
+    7     5    23    25     3
+    5     7    10    11     3
+    5     7    10    12     3
+    5     7    10    13     3
+    8     7    10    11     3
+    8     7    10    12     3
+    8     7    10    13     3
+    9     7    10    11     3
+    9     7    10    12     3
+    9     7    10    13     3
+    7    10    13    14     3
+    7    10    13    15     3
+    7    10    13    16     3
+   11    10    13    14     3
+   11    10    13    15     3
+   11    10    13    16     3
+   12    10    13    14     3
+   12    10    13    15     3
+   12    10    13    16     3
+   10    13    16    17     3
+   10    13    16    18     3
+   10    13    16    19     3
+   14    13    16    17     3
+   14    13    16    18     3
+   14    13    16    19     3
+   15    13    16    17     3
+   15    13    16    18     3
+   15    13    16    19     3
+   13    16    19    20     3    dih_LYS_chi5_C_C_N_H
+   13    16    19    21     3    dih_LYS_chi5_C_C_N_H
+   13    16    19    22     3    dih_LYS_chi5_C_C_N_H
+   17    16    19    20     3
+   17    16    19    21     3
+   17    16    19    22     3
+   18    16    19    20     3
+   18    16    19    21     3
+   18    16    19    22     3
+    5    23    25    26     3
+    5    23    25    27     3
+   24    23    25    26     3
+   24    23    25    27     3
+   23    25    27    28     3
+   23    25    27    29     3
+   23    25    27    39     3
+   26    25    27    28     3
+   26    25    27    29     3
+   26    25    27    39     3
+   25    27    29    31     3    dih_VAL_chi1_N_C_C_C
+   25    27    29    35     3    dih_VAL_chi1_N_C_C_C
+   39    27    29    31     3    dih_VAL_chi1_C_C_C_CO
+   39    27    29    35     3    dih_VAL_chi1_C_C_C_CO
+   25    27    29    30     3
+   28    27    29    30     3
+   28    27    29    31     3
+   28    27    29    35     3
+   39    27    29    30     3
+   25    27    39    40     3
+   25    27    39    41     3
+   28    27    39    40     3
+   28    27    39    41     3
+   29    27    39    40     3
+   29    27    39    41     3
+   27    29    31    32     3
+   27    29    31    33     3
+   27    29    31    34     3
+   30    29    31    32     3
+   30    29    31    33     3
+   30    29    31    34     3
+   35    29    31    32     3
+   35    29    31    33     3
+   35    29    31    34     3
+   27    29    35    36     3
+   27    29    35    37     3
+   27    29    35    38     3
+   30    29    35    36     3
+   30    29    35    37     3
+   30    29    35    38     3
+   31    29    35    36     3
+   31    29    35    37     3
+   31    29    35    38     3
+   27    39    41    42     3
+   27    39    41    43     3
+   40    39    41    42     3
+   40    39    41    43     3
+   39    41    43    44     3
+   39    41    43    45     3
+   39    41    43    59     3
+   42    41    43    44     3
+   42    41    43    45     3
+   42    41    43    59     3
+   41    43    45    46     3
+   41    43    45    47     3
+   41    43    45    48     3
+   44    43    45    46     3
+   44    43    45    47     3
+   44    43    45    48     3
+   59    43    45    46     3
+   59    43    45    47     3
+   59    43    45    48     3
+   41    43    59    60     3
+   41    43    59    61     3
+   44    43    59    60     3
+   44    43    59    61     3
+   45    43    59    60     3
+   45    43    59    61     3
+   43    45    48    49     3
+   43    45    48    51     3
+   46    45    48    49     3
+   46    45    48    51     3
+   47    45    48    49     3
+   47    45    48    51     3
+   45    48    49    50     3
+   45    48    49    53     3
+   51    48    49    50     3
+   51    48    49    53     3
+   45    48    51    52     3
+   45    48    51    55     3
+   49    48    51    52     3
+   49    48    51    55     3
+   48    49    53    54     3
+   48    49    53    57     3
+   50    49    53    54     3
+   50    49    53    57     3
+   48    51    55    56     3
+   48    51    55    57     3
+   52    51    55    56     3
+   52    51    55    57     3
+   49    53    57    55     3
+   49    53    57    58     3
+   54    53    57    55     3
+   54    53    57    58     3
+   51    55    57    53     3
+   51    55    57    58     3
+   56    55    57    53     3
+   56    55    57    58     3
+   43    59    61    62     3
+   43    59    61    63     3
+   60    59    61    62     3
+   60    59    61    63     3
+   59    61    63    64     3
+   59    61    63    65     3
+   59    61    63    66     3
+   62    61    63    64     3
+   62    61    63    65     3
+   62    61    63    66     3
+   61    63    66    67     3
+   61    63    66    68     3
+   64    63    66    67     3
+   64    63    66    68     3
+   65    63    66    67     3
+   65    63    66    68     3
+   63    66    68    69     3
+   63    66    68    70     3
+   67    66    68    69     3
+   67    66    68    70     3
+   66    68    70    71     3
+   66    68    70    72     3
+   66    68    70    90     3
+   69    68    70    71     3
+   69    68    70    72     3
+   69    68    70    90     3
+   68    70    72    75     3    dih_ARG_chi1_N_C_C_C
+   90    70    72    75     3    dih_ARG_chi1_C_C_C_CO
+   68    70    72    73     3
+   68    70    72    74     3
+   71    70    72    73     3
+   71    70    72    74     3
+   71    70    72    75     3
+   90    70    72    73     3
+   90    70    72    74     3
+   68    70    90    91     3
+   68    70    90    92     3
+   71    70    90    91     3
+   71    70    90    92     3
+   72    70    90    91     3
+   72    70    90    92     3
+   70    72    75    76     3
+   70    72    75    77     3
+   70    72    75    78     3
+   73    72    75    76     3
+   73    72    75    77     3
+   73    72    75    78     3
+   74    72    75    76     3
+   74    72    75    77     3
+   74    72    75    78     3
+   72    75    78    79     3
+   72    75    78    80     3
+   72    75    78    81     3
+   76    75    78    79     3
+   76    75    78    80     3
+   76    75    78    81     3
+   77    75    78    79     3
+   77    75    78    80     3
+   77    75    78    81     3
+   75    78    81    82     3
+   75    78    81    83     3
+   79    78    81    82     3
+   79    78    81    83     3
+   80    78    81    82     3
+   80    78    81    83     3
+   78    81    83    84     3
+   78    81    83    87     3
+   82    81    83    84     3
+   82    81    83    87     3
+   81    83    84    85     3
+   81    83    84    86     3
+   87    83    84    85     3
+   87    83    84    86     3
+   81    83    87    88     3
+   81    83    87    89     3
+   84    83    87    88     3
+   84    83    87    89     3
+   70    90    92    93     3
+   70    90    92    94     3
+   91    90    92    93     3
+   91    90    92    94     3
+   90    92    94    95     3
+   90    92    94    96     3
+   90    92    94   101     3
+   93    92    94    95     3
+   93    92    94    96     3
+   93    92    94   101     3
+   92    94    96    99     3    dih_CYS_chi1_N_C_C_S
+  101    94    96    99     3    dih_CYS_chi1_CO_C_C_S
+   92    94    96    97     3
+   92    94    96    98     3
+   95    94    96    97     3
+   95    94    96    98     3
+   95    94    96    99     3
+  101    94    96    97     3
+  101    94    96    98     3
+   92    94   101   102     3
+   92    94   101   103     3
+   95    94   101   102     3
+   95    94   101   103     3
+   96    94   101   102     3
+   96    94   101   103     3
+   94    96    99   100     3
+   97    96    99   100     3
+   98    96    99   100     3
+   94   101   103   104     3
+   94   101   103   105     3
+  102   101   103   104     3
+  102   101   103   105     3
+  101   103   105   106     3
+  101   103   105   107     3
+  101   103   105   116     3
+  104   103   105   106     3
+  104   103   105   107     3
+  104   103   105   116     3
+  103   105   107   110     3    dih_GLU_chi1_N_C_C_C
+  116   105   107   110     3    dih_GLU_chi1_C_C_C_CO
+  103   105   107   108     3
+  103   105   107   109     3
+  106   105   107   108     3
+  106   105   107   109     3
+  106   105   107   110     3
+  116   105   107   108     3
+  116   105   107   109     3
+  103   105   116   117     3
+  103   105   116   118     3
+  106   105   116   117     3
+  106   105   116   118     3
+  107   105   116   117     3
+  107   105   116   118     3
+  105   107   110   111     3
+  105   107   110   112     3
+  105   107   110   113     3
+  108   107   110   111     3
+  108   107   110   112     3
+  108   107   110   113     3
+  109   107   110   111     3
+  109   107   110   112     3
+  109   107   110   113     3
+  107   110   113   114     3
+  107   110   113   115     3
+  111   110   113   114     3
+  111   110   113   115     3
+  112   110   113   114     3
+  112   110   113   115     3
+  105   116   118   119     3
+  105   116   118   120     3
+  117   116   118   119     3
+  117   116   118   120     3
+  116   118   120   121     3
+  116   118   120   122     3
+  116   118   120   135     3
+  119   118   120   121     3
+  119   118   120   122     3
+  119   118   120   135     3
+  118   120   122   125     3    dih_LEU_chi1_N_C_C_C
+  135   120   122   125     3    dih_LEU_chi1_C_C_C_CO
+  118   120   122   123     3
+  118   120   122   124     3
+  121   120   122   123     3
+  121   120   122   124     3
+  121   120   122   125     3
+  135   120   122   123     3
+  135   120   122   124     3
+  118   120   135   136     3
+  118   120   135   137     3
+  121   120   135   136     3
+  121   120   135   137     3
+  122   120   135   136     3
+  122   120   135   137     3
+  120   122   125   126     3
+  120   122   125   127     3
+  120   122   125   131     3
+  123   122   125   126     3
+  123   122   125   127     3
+  123   122   125   131     3
+  124   122   125   126     3
+  124   122   125   127     3
+  124   122   125   131     3
+  122   125   127   128     3
+  122   125   127   129     3
+  122   125   127   130     3
+  126   125   127   128     3
+  126   125   127   129     3
+  126   125   127   130     3
+  131   125   127   128     3
+  131   125   127   129     3
+  131   125   127   130     3
+  122   125   131   132     3
+  122   125   131   133     3
+  122   125   131   134     3
+  126   125   131   132     3
+  126   125   131   133     3
+  126   125   131   134     3
+  127   125   131   132     3
+  127   125   131   133     3
+  127   125   131   134     3
+  120   135   137   138     3
+  120   135   137   139     3
+  136   135   137   138     3
+  136   135   137   139     3
+  135   137   139   140     3
+  135   137   139   141     3
+  135   137   139   145     3
+  138   137   139   140     3
+  138   137   139   141     3
+  138   137   139   145     3
+  137   139   141   142     3
+  137   139   141   143     3
+  137   139   141   144     3
+  140   139   141   142     3
+  140   139   141   143     3
+  140   139   141   144     3
+  145   139   141   142     3
+  145   139   141   143     3
+  145   139   141   144     3
+  137   139   145   146     3
+  137   139   145   147     3
+  140   139   145   146     3
+  140   139   145   147     3
+  141   139   145   146     3
+  141   139   145   147     3
+  139   145   147   148     3
+  139   145   147   149     3
+  146   145   147   148     3
+  146   145   147   149     3
+  145   147   149   150     3
+  145   147   149   151     3
+  145   147   149   155     3
+  148   147   149   150     3
+  148   147   149   151     3
+  148   147   149   155     3
+  147   149   151   152     3
+  147   149   151   153     3
+  147   149   151   154     3
+  150   149   151   152     3
+  150   149   151   153     3
+  150   149   151   154     3
+  155   149   151   152     3
+  155   149   151   153     3
+  155   149   151   154     3
+  147   149   155   156     3
+  150   149   155   156     3
+  151   149   155   156     3
+
+[ dihedrals ]
+;  ai    aj    ak    al funct            c0            c1            c2            c3
+    5    25    23    24     1    improper_O_C_X_Y
+   23    27    25    26     1    improper_Z_N_X_Y
+   27    41    39    40     1    improper_O_C_X_Y
+   39    43    41    42     1    improper_Z_N_X_Y
+   43    61    59    60     1    improper_O_C_X_Y
+   45    48    51    49     1    improper_Z_CA_X_Y
+   48    53    49    50     1    improper_Z_CA_X_Y
+   48    55    51    52     1    improper_Z_CA_X_Y
+   49    57    53    54     1    improper_Z_CA_X_Y
+   51    57    55    56     1    improper_Z_CA_X_Y
+   53    55    57    58     1    improper_Z_CA_X_Y
+   59    63    61    62     1    improper_Z_N_X_Y
+   63    68    66    67     1    improper_O_C_X_Y
+   66    70    68    69     1    improper_Z_N_X_Y
+   70    92    90    91     1    improper_O_C_X_Y
+   78    83    81    82     1    improper_Z_N_X_Y
+   81    84    83    87     1    improper_O_C_X_Y
+   83    85    84    86     1    improper_Z_N_X_Y
+   83    88    87    89     1    improper_Z_N_X_Y
+   90    94    92    93     1    improper_Z_N_X_Y
+   94   103   101   102     1    improper_O_C_X_Y
+  101   105   103   104     1    improper_Z_N_X_Y
+  105   118   116   117     1    improper_O_C_X_Y
+  110   114   113   115     1    improper_O_C_X_Y
+  116   120   118   119     1    improper_Z_N_X_Y
+  120   137   135   136     1    improper_O_C_X_Y
+  135   139   137   138     1    improper_Z_N_X_Y
+  139   147   145   146     1    improper_O_C_X_Y
+  145   149   147   148     1    improper_Z_N_X_Y
+
+[ system ]
+; Name
+First 10 residues from 1AKI
+
+[ molecules ]
+; Compound        #mols
+Protein_chain_B     1
diff --git a/src/include/gromacs/trajectoryanalysis/tests/moduletest.h b/src/include/gromacs/trajectoryanalysis/tests/moduletest.h
new file mode 100644 (file)
index 0000000..dc3a0a6
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares test fixture for TrajectoryAnalysisModule subclasses.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_TESTS_MODULETEST_H
+#define GMX_TRAJECTORYANALYSIS_TESTS_MODULETEST_H
+
+#include <memory>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/trajectoryanalysis/analysismodule.h"
+
+#include "testutils/cmdlinetest.h"
+
+namespace gmx
+{
+
+class TrajectoryAnalysisModule;
+
+namespace test
+{
+
+class FloatingPointTolerance;
+
+/*! \internal
+ * \brief
+ * Test fixture for trajectory analysis modules.
+ *
+ * This class implements common logic for all tests for trajectory analysis
+ * modules.  The tests simply need to specify the type of the module to test,
+ * input files and any additional options, names of datasets to test (if not
+ * all), and possible output files to explicitly test by calling the different
+ * methods in this class.  runTest() then runs the specified module with the
+ * given options and performs all the requested tests against reference data.
+ *
+ * Tests should prefer to test the underlying data sets instead of string
+ * comparison on the output files using setOutputFile().
+ *
+ * The actual module to be tested is constructed in the pure virtual
+ * createModule() method, which should be implemented in a subclass.
+ * Typically, the TrajectoryAnalysisModuleTestFixture template can be used.
+ *
+ * Any method in this class may throw std::bad_alloc if out of memory.
+ *
+ * \todo
+ * Adding facilities to AnalysisData to check whether there are any
+ * output modules attached to the data object (directly or indirectly),
+ * marking the mocks as output modules, and using these checks in the
+ * tools instead of or in addition to the output file presence would be
+ * a superior.
+ * Also, the full file names should be deducible from the options.
+ *
+ * \ingroup module_trajectoryanalysis
+ */
+class AbstractTrajectoryAnalysisModuleTestFixture : public CommandLineTestBase
+{
+public:
+    AbstractTrajectoryAnalysisModuleTestFixture();
+    ~AbstractTrajectoryAnalysisModuleTestFixture() override;
+
+    /*! \brief
+     * Sets the topology file to use for the test.
+     *
+     * \param[in] filename  Name of input topology file.
+     *
+     * \p filename is interpreted relative to the test input data
+     * directory, see getTestDataPath().
+     *
+     * Must be called at most once.  Either this method or setTrajectory()
+     * must be called before runTest().
+     */
+    void setTopology(const char* filename);
+    /*! \brief
+     * Sets the trajectory file to use for the test.
+     *
+     * \param[in] filename  Name of input trajectory file.
+     *
+     * \p filename is interpreted relative to the test input data
+     * directory, see getTestDataPath().
+     *
+     * Must be called at most once.  Either this method or setTopology()
+     * must be called before runTest().
+     */
+    void setTrajectory(const char* filename);
+    /*! \brief
+     * Includes only specified dataset for the test.
+     *
+     * \param[in] name  Name of dataset to include.
+     *
+     * If this method is not called, all datasets are tested by default.
+     * If called once, only the specified dataset is tested.
+     * If called more than once, also the additional datasets are tested.
+     *
+     * \p name should be one of the names registered for the tested module
+     * using TrajectoryAnalysisModule::registerBasicDataset() or
+     * TrajectoryAnalysisModule::registerAnalysisDataset().
+     */
+    void includeDataset(const char* name);
+    /*! \brief
+     * Excludes specified dataset from the test.
+     *
+     * \param[in] name  Name of dataset to exclude.
+     *
+     * \p name should be one of the names registered for the tested module
+     * using TrajectoryAnalysisModule::registerBasicDataset() or
+     * TrajectoryAnalysisModule::registerAnalysisDataset().
+     */
+    void excludeDataset(const char* name);
+    /*! \brief
+     * Sets a custom tolerance for checking a dataset.
+     *
+     * \param[in] name       Name of dataset to set the tolerance for.
+     * \param[in] tolerance  Tolerance used when verifying the data.
+     *
+     * \p name should be one of the names registered for the tested module
+     * using TrajectoryAnalysisModule::registerBasicDataset() or
+     * TrajectoryAnalysisModule::registerAnalysisDataset().
+     */
+    void setDatasetTolerance(const char* name, const FloatingPointTolerance& tolerance);
+
+    /*! \brief
+     * Runs the analysis module with the given additional options.
+     *
+     * \param[in] args  Options to provide to the module.
+     *
+     * \p args should be formatted as command-line options, and contain the
+     * name of the module as the first argument (the latter requirement is
+     * for clarity only).  They are passed to the module in addition to
+     * those specified using other methods in this class.
+     *
+     * All other methods should be called before calling this method.
+     *
+     * Exceptions thrown by the module are caught by this method.
+     */
+    void runTest(const CommandLine& args);
+
+protected:
+    /*! \brief
+     * Constructs the analysis module to be tested.
+     */
+    virtual TrajectoryAnalysisModulePointer createModule() = 0;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \internal \brief
+ * Test fixture for a trajectory analysis module.
+ *
+ * \tparam ModuleInfo  Info object for the analysis module to test.
+ *
+ * \p ModuleInfo should provide a static
+ * `TrajectoryAnalysisModulePointer create()` method.
+ *
+ * \ingroup module_trajectoryanalysis
+ */
+template<class ModuleInfo>
+class TrajectoryAnalysisModuleTestFixture : public AbstractTrajectoryAnalysisModuleTestFixture
+{
+protected:
+    TrajectoryAnalysisModulePointer createModule() override { return ModuleInfo::create(); }
+};
+
+} // namespace test
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/trajectoryanalysis/topologyinformation.h b/src/include/gromacs/trajectoryanalysis/topologyinformation.h
new file mode 100644 (file)
index 0000000..2f353d0
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::TopologyInformation.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_trajectoryanalysis
+ */
+#ifndef GMX_TRAJECTORYANALYSIS_TOPOLOGYINFORMATION_H
+#define GMX_TRAJECTORYANALYSIS_TOPOLOGYINFORMATION_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/topology/atoms.h"
+#include "gromacs/topology/topology.h"
+#include "gromacs/utility/classhelpers.h"
+
+//! Forward declaration
+typedef struct gmx_rmpbc* gmx_rmpbc_t;
+enum class PbcType : int;
+
+namespace gmx
+{
+
+template<typename T>
+class ArrayRef;
+
+class TopologyInformation;
+class TrajectoryAnalysisRunnerCommon;
+
+/*! \libinternal
+ * \brief Topology information available to a trajectory analysis module.
+ *
+ * This class is used to provide topology information to trajectory
+ * analysis modules and to manage memory for them. Having a single
+ * wrapper object instead of passing each item separately makes
+ * TrajectoryAnalysisModule interface simpler, and also reduces the
+ * need to change existing code if additional information is added.
+ *
+ * It is intended that eventually most clients of this class will be
+ * analysis tools ported to the new analysis framework, but we will
+ * use this infrastructure also from the legacy analysis tools during
+ * the transition period. That will make it easier to put those tools
+ * under tests, and eventually port them.
+ *
+ * Methods in this class do not throw if not explicitly stated.
+ *
+ * The main data content is constant once loaded, but some content is
+ * constructed only when required (e.g. atoms_ and
+ * expandedTopology_). Their data members are mutable, so that the
+ * lazy construction idiom works properly. Some clients wish to modify
+ * the t_atoms, so there is an efficient mechanism for them to get a
+ * copy they can modify without disturbing this class. (The
+ * implementation releases the cached lazily constructed atoms_, but
+ * from the point of view of the caller, that is a copy.) The getters
+ * and copy functions are const for callers, which correctly expresses
+ * that the topology information is not being changed, merely copied
+ * and presented in a different form.
+ *
+ * \ingroup module_trajectoryanalysis
+ */
+class TopologyInformation
+{
+public:
+    //! Returns true if a topology file was loaded.
+    bool hasTopology() const { return hasLoadedMtop_; }
+    //! Returns true if a full topology file was loaded.
+    bool hasFullTopology() const { return bTop_; }
+    /*! \brief Builder function to fill the contents of
+     * TopologyInformation in \c topInfo from \c filename.
+     *
+     * Different tools require, might need, would benefit from, or
+     * do not need topology information. This functions implements
+     * the two-phase construction that is currently needed to
+     * support that.
+     *
+     * Any coordinate or run input file format will work, but the
+     * kind of data available from the getter methods afterwards
+     * will vary. For example, the mtop() available after reading
+     * a plain structure file will have a single molecule block and
+     * molecule type, regardless of contents.
+     *
+     * After reading, this object can return many kinds of primary
+     * and derived data structures to its caller.
+     *
+     * \todo This should throw upon error but currently does
+     * not. */
+    void fillFromInputFile(const std::string& filename);
+    /*! \brief Returns the loaded topology, or nullptr if not loaded. */
+    gmx_mtop_t* mtop() const { return mtop_.get(); }
+    //! Returns the loaded topology fully expanded, or nullptr if no topology is available.
+    const gmx_localtop_t* expandedTopology() const;
+    /*! \brief Returns a read-only handle to the fully expanded
+     * atom data arrays, which might be valid but empty if no
+     * topology is available. */
+    const t_atoms* atoms() const;
+    /*! \brief Copies the fully expanded atom data arrays, which
+     * might be valid but empty if no topology is available. */
+    AtomsDataPtr copyAtoms() const;
+    //! Returns the pbcType field from the topology.
+    PbcType pbcType() const { return pbcType_; }
+    /*! \brief
+     * Gets the configuration positions from the topology file.
+     *
+     * If TrajectoryAnalysisSettings::efUseTopX has not been specified,
+     * this method should not be called.
+     *
+     * \throws  APIError if topology position coordinates are not available
+     */
+    ArrayRef<const RVec> x() const;
+    /*! \brief
+     * Gets the configuration velocities from the topology file.
+     *
+     * If TrajectoryAnalysisSettings::efUseTopV has not been specified,
+     * this method should not be called.
+     *
+     * \throws  APIError if topology velocity coordinates are not available
+     */
+    ArrayRef<const RVec> v() const;
+    /*! \brief
+     * Gets the configuration box from the topology file.
+     *
+     * \param[out] box   Box size from the topology file, must not be nullptr.
+     */
+    void getBox(matrix box) const;
+    /*! \brief Returns a name for the topology.
+     *
+     * If a full topology was read from a a file, returns the name
+     * it contained, otherwise the empty string. */
+    const char* name() const;
+
+    TopologyInformation();
+    ~TopologyInformation();
+
+private:
+    //! The topology structure, or nullptr if no topology loaded.
+    std::unique_ptr<gmx_mtop_t> mtop_;
+    //! Whether a topology has been loaded.
+    bool hasLoadedMtop_;
+    //! The fully expanded topology structure, nullptr if not yet constructed.
+    mutable ExpandedTopologyPtr expandedTopology_;
+    //! The fully expanded atoms data structure, nullptr if not yet constructed.
+    mutable AtomsDataPtr atoms_;
+    //! true if full tpx file was loaded, false otherwise.
+    bool bTop_;
+    //! Position coordinates from the topology (can be nullptr).
+    std::vector<RVec> xtop_;
+    //! Velocity coordinates from the topology (can be nullptr).
+    std::vector<RVec> vtop_;
+    //! The box loaded from the topology file.
+    matrix boxtop_{};
+    //! The pbcType field loaded from the topology file.
+    PbcType pbcType_;
+
+    // TODO This type is probably movable if we need that.
+    GMX_DISALLOW_COPY_AND_ASSIGN(TopologyInformation);
+
+    /*! \brief
+     * Needed to initialize the data.
+     */
+    friend class TrajectoryAnalysisRunnerCommon;
+};
+
+//! Convenience overload useful for implementing legacy tools.
+gmx_rmpbc_t gmx_rmpbc_init(const gmx::TopologyInformation& topInfo);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/alignedallocator.h b/src/include/gromacs/utility/alignedallocator.h
new file mode 100644 (file)
index 0000000..f6e66cd
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief Declares allocation policy classes and allocators that are
+ * used to make library containers compatible with alignment
+ * requirements of particular hardware, e.g. memory operations for
+ * SIMD or accelerators.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_ALIGNEDALLOCATOR_H
+#define GMX_UTILITY_ALIGNEDALLOCATOR_H
+
+#include <cstddef>
+
+#include "gromacs/utility/allocator.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief Policy class for configuring gmx::Allocator, to manage
+ * allocations of aligned memory for SIMD code.
+ */
+class AlignedAllocationPolicy
+{
+public:
+    /*! \brief Return the alignment size. */
+    static std::size_t alignment();
+    /*! \brief Allocate memory aligned to alignment() bytes.
+     *
+     *  \param bytes Amount of memory (bytes) to allocate. It is valid to ask for
+     *               0 bytes, which will return a non-null pointer that is properly
+     *               aligned and padded (but that you should not use).
+     *
+     * \return Valid pointer if the allocation worked, otherwise nullptr.
+     *
+     * The memory will always be aligned to 128 bytes, which is our
+     * estimate of the longest cache lines on architectures currently in use.
+     * It will also be padded by the same amount at the end of the
+     * area, to help avoid false cache sharing.
+     *
+     *  \note Memory allocated with this routine must be released with
+     *        gmx::AlignedAllocationPolicy::free(), and absolutely not the system free().
+     */
+    static void* malloc(std::size_t bytes);
+    /*! \brief Free aligned memory
+     *
+     *  \param p  Memory pointer previously returned from malloc()
+     *
+     *  \note This routine should only be called with pointers obtained from
+     *        gmx::AlignedAllocationPolicy::malloc(), and absolutely not any
+     *        pointers obtained the system malloc().
+     */
+    static void free(void* p);
+};
+
+/*! \brief Aligned memory allocator.
+ *
+ *  \tparam T          Type of objects to allocate
+ *
+ * This convenience partial specialization can be used for the
+ * optional allocator template parameter in standard library
+ * containers, which is necessary e.g. to use SIMD aligned load and
+ * store operations on data in those containers. The memory will
+ * always be aligned according to the behavior of
+ * AlignedAllocationPolicy.
+ */
+template<class T>
+using AlignedAllocator = Allocator<T, AlignedAllocationPolicy>;
+
+
+/*! \brief Return the memory page size on this system
+ *
+ * Implements the "construct on first use" idiom to avoid the static
+ * initialization order fiasco where a possible static page-aligned
+ * container would be initialized before the alignment variable was.
+ *
+ * Note that thread-safety is guaranteed by the C++11 language
+ * standard. */
+std::size_t pageSize();
+
+/*! \libinternal \brief Policy class for configuring gmx::Allocator,
+ * to manage allocations of page-aligned memory that can be locked for
+ * asynchronous transfer to GPU devices.
+ */
+class PageAlignedAllocationPolicy
+{
+public:
+    /*! \brief Return the alignment size of memory pages on this system.
+     *
+     * Queries sysconf/WinAPI, otherwise guesses 4096. */
+    static std::size_t alignment();
+    /*! \brief Allocate memory aligned to alignment() bytes.
+     *
+     *  \param bytes Amount of memory (bytes) to allocate. It is valid to ask for
+     *               0 bytes, which will return a non-null pointer that is properly
+     *               aligned and padded (but that you should not use).
+     *
+     * \return Valid pointer if the allocation worked, otherwise nullptr.
+     *
+     *  \note Memory allocated with this routine must be released with
+     *        gmx::PageAlignedAllocationPolicy::free(), and absolutely not the system free().
+     */
+    static void* malloc(std::size_t bytes);
+    /*! \brief Free aligned memory
+     *
+     *  \param p  Memory pointer previously returned from malloc()
+     *
+     *  \note This routine should only be called with pointers obtained from
+     *        gmx::PageAlignedAllocationPolicy::malloc(), and absolutely not any
+     *        pointers obtained the system malloc().
+     */
+    static void free(void* p);
+};
+
+/*! \brief PageAligned memory allocator.
+ *
+ *  \tparam T          Type of objects to allocate
+ *
+ * This convenience partial specialization can be used for the
+ * optional allocator template parameter in standard library
+ * containers, which is necessary for locking memory pages for
+ * asynchronous transfer between a GPU device and the host.  The
+ * memory will always be aligned according to the behavior of
+ * PageAlignedAllocationPolicy.
+ */
+template<class T>
+using PageAlignedAllocator = Allocator<T, PageAlignedAllocationPolicy>;
+
+} // namespace gmx
+
+#endif // GMX_UTILITY_ALIGNEDALLOCATOR_H
diff --git a/src/include/gromacs/utility/allocator.h b/src/include/gromacs/utility/allocator.h
new file mode 100644 (file)
index 0000000..74ec200
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::Allocator template whose allocation functionality is
+ * configured both by type of object allocated and a policy class that
+ * configures the necessary matching malloc and free operation.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_ALLOCATOR_H
+#define GMX_UTILITY_ALLOCATOR_H
+
+#include <cstddef>
+
+#include <memory>
+#include <new>
+
+#include "gromacs/utility/basedefinitions.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief Policy-based memory allocator.
+ *
+ *  \tparam T                 Type of objects to allocate
+ *  \tparam AllocationPolicy  Policy of (matching) allocation and deallocation functions.
+ *
+ * This class can be used for the optional allocator template
+ * parameter in standard library containers. It must be configured
+ * with both the type of object to allocate, and an AllocationPolicy
+ * which effectively wraps a matching pair of malloc and free
+ * functions. This permits implementing a family of related allocators
+ * e.g. with SIMD alignment, GPU host-side page locking, or perhaps
+ * both, in a way that preserves a common programming interface and
+ * duplicates minimal code.
+ *
+ * AllocationPolicy is used as a base class, so that if
+ * AllocationPolicy is stateless, then the empty base optimization
+ * will ensure that Allocation is also stateless, and objects made
+ * with the Allocator will incur no size penalty. (Embedding an
+ * AllocationPolicy object incurs a size penalty always, even if the
+ * object is empty.) Normally a stateless allocator will be used.
+ *
+ * However, an AllocationPolicy with state might be desirable for
+ * simplifying writing code that needs to allocate suitably for a
+ * transfer to a GPU. That code needs to specify an Allocator that can
+ * do the right job, which can be stateless. However, if we have code
+ * that will not know until run time whether a GPU transfer will
+ * occur, then the allocator needs to be aware of the state.  That
+ * will increase the size of a container that uses the stateful
+ * allocator.
+ *
+ * \throws std::bad_alloc Instead of a GROMACS exception object, we
+ * throw the standard one on allocation failures to make it as
+ * compatible as possible with the errors expected by code using the
+ * standard library containers.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+template<class T, typename AllocationPolicy>
+class Allocator : public AllocationPolicy
+{
+public:
+    // The standard library specification for a custom allocator
+    // requires this typedef, with this capitalization/underscoring.
+    typedef T value_type; //!< Type of allocated elements
+
+    /*! \brief Constructor
+     *
+     * No constructor can be auto-generated in the presence of any
+     * user-defined constructor, but we want the default constructor.
+     */
+    Allocator() = default;
+
+    /*! \brief Constructor to accept an AllocationPolicy.
+     *
+     * This is useful for AllocationPolicies with state.
+     */
+    Allocator(const AllocationPolicy& p) : AllocationPolicy(p) {}
+
+    /*! \brief Do the actual memory allocation
+     *
+     *  \param n    Number of elements of type T to allocate. n can be
+     *              0 bytes, which will return a non-null properly aligned
+     *              and padded pointer that should not be used.
+     *  \param hint Optional value returned from previous call to allocate.
+     *              For now this is not used.
+     *  \return Pointer to allocated memory
+     *
+     *  \throws std::bad_alloc if the allocation fails.
+     */
+    value_type* allocate(std::size_t n, typename std::allocator<void>::const_pointer gmx_unused hint = nullptr)
+    {
+        void* p = AllocationPolicy::malloc(n * sizeof(T));
+
+        if (p == nullptr)
+        {
+            throw std::bad_alloc();
+        }
+        else
+        {
+            return static_cast<value_type*>(p);
+        }
+    }
+
+    /*! \brief Release memory
+     *
+     * \param p  Pointer to previously allocated memory returned from allocate()
+     * \param n  number of objects previously passed to allocate()
+     */
+    void deallocate(value_type* p, std::size_t gmx_unused n) { AllocationPolicy::free(p); }
+
+    /*! \brief Return true if two allocators are identical
+     *
+     * This is a member function of the left-hand-side allocator.
+     * Always true for stateless polcies. Has to be defined in the policy for stateful policies.
+     * FUTURE: Can be removed with C++17 (is_always_equal)
+     *
+     * \todo Use std::is_empty_v when CUDA 11 is a requirement.
+     */
+    template<class T2, class A = AllocationPolicy, typename = std::enable_if_t<std::is_empty<A>::value>>
+    bool operator==(const Allocator<T2, AllocationPolicy>& /*unused*/) const
+    {
+        return true;
+    }
+
+    /*! \brief Return true if two allocators are different
+     *
+     * \param rhs Other allocator.
+     *
+     * This is a member function of the left-hand-side allocator.
+     */
+    template<class T2>
+    bool operator!=(const Allocator<T2, AllocationPolicy>& rhs) const
+    {
+        return !(*this == rhs);
+    }
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/any.h b/src/include/gromacs/utility/any.h
new file mode 100644 (file)
index 0000000..970703b
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::Any.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_ANY_H
+#define GMX_UTILITY_ANY_H
+
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <typeindex>
+#include <typeinfo>
+#include <utility>
+
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Represents a dynamically typed value of an arbitrary type - deprecated.
+ *
+ * New uses of this type should be avoided - prefer std::any or
+ * std::variant.
+ *
+ * To create an Any, either initialize it as empty, or with the create()
+ * method (or the equivalent constructor, if the type parameter can be deduced
+ * and is clear to the reader from the context).
+ *
+ * To query the type of the contents in the any, use isEmpty(), type(), and
+ * isType().
+ *
+ * To access the value, you need to know the type as a compile-time constant
+ * (e.g., through branching based on isType()), and then use cast() or
+ * tryCast().
+ *
+ * Methods in this class do not throw unless otherwise indicated.
+ *
+ * This provides essentially the same functionality as boost::any.
+ *
+ * It would be good to replace the current uses of this type with
+ * std::any or std::variant, but see
+ * https://gitlab.com/gromacs/gromacs/-/issues/3951 for discussion
+ * about the things that have blocked such attempts.
+ *
+ * \ingroup module_utility
+ */
+class Any
+{
+public:
+    /*! \brief
+     * Creates a any that holds the given value.
+     *
+     * \throws std::bad_alloc if out of memory.
+     *
+     * This method allows explicitly specifying the template argument,
+     * contrary to the templated constructor.
+     */
+    template<typename T>
+    static Any create(const T& value)
+    {
+        return Any(value);
+    }
+    /*! \brief
+     * Creates a any that holds the given value.
+     *
+     * \throws std::bad_alloc if out of memory.
+     *
+     * In addition to allowing specifying the template argument, this
+     * method avoids copying when move-construction is possible.
+     */
+    template<typename T>
+    static Any create(T&& value)
+    {
+        return Any(std::forward<T>(value));
+    }
+
+    //! Creates an empty any value.
+    Any() {}
+    /*! \brief
+     * Creates a any that holds the given value.
+     *
+     * \throws std::bad_alloc if out of memory.
+     */
+    template<typename T, typename = std::enable_if_t<!std::is_same<T, Any>::value>>
+    explicit Any(T&& value) :
+        content_(new Content<typename std::decay<T>::type>(std::forward<T>(value)))
+    {
+    }
+    /*! \brief
+     * Creates a deep copy of a any.
+     *
+     * \throws std::bad_alloc if out of memory.
+     */
+    Any(const Any& other) : content_(other.cloneContent()) {}
+    //! Move-constructs a any.
+    Any(Any&& other) noexcept : content_(std::move(other.content_)) {}
+    /*! \brief
+     * Assigns the any.
+     *
+     * \throws std::bad_alloc if out of memory.
+     */
+    Any& operator=(const Any& other)
+    {
+        content_ = other.cloneContent();
+        return *this;
+    }
+    //! Move-assigns the any.
+    Any& operator=(Any&& other) noexcept
+    {
+        content_ = std::move(other.content_);
+        return *this;
+    }
+
+    //! Whether any value is stored.
+    bool isEmpty() const { return content_ == nullptr; }
+    //! Returns the dynamic type of the value that is currently stored.
+    std::type_index type() const
+    {
+        const std::type_info& info = !isEmpty() ? content_->typeInfo() : typeid(void);
+        return std::type_index(info);
+    }
+    //! Returns whether the type stored matches the template parameter.
+    template<typename T>
+    bool isType() const
+    {
+        return !isEmpty() && content_->typeInfo() == typeid(T);
+    }
+
+    /*! \brief
+     * Tries to get the value as the given type.
+     *
+     * \tparam T  Type to get.
+     * \returns Pointer to the value, or nullptr if the type does not match
+     *     the stored value.
+     */
+    template<typename T>
+    const T* tryCast() const
+    {
+        return isType<T>() ? &static_cast<Content<T>*>(content_.get())->value_ : nullptr;
+    }
+    /*! \brief
+     * Gets the value when the type is known.
+     *
+     * \tparam T  Type to get (which must match what the any stores).
+     *
+     * Asserts if the any is empty or does not contain the requested type.
+     */
+    template<typename T>
+    const T& cast() const
+    {
+        const T* value = tryCast<T>();
+        GMX_RELEASE_ASSERT(value != nullptr, "Cast to incorrect type");
+        return *value;
+    }
+    /*! \brief
+     * Tries to get the value as the given type as a non-const pointer.
+     *
+     * \tparam T  Type to get.
+     * \returns Pointer to the value, or nullptr if the type does not match
+     *     the stored value.
+     *
+     * This method allows modifying the value in-place, which is useful
+     * with more complicated data structures.
+     */
+    template<typename T>
+    T* tryCastRef()
+    {
+        return isType<T>() ? &static_cast<Content<T>*>(content_.get())->value_ : nullptr;
+    }
+    /*! \brief
+     * Gets the value when the type is known as a modifiable reference.
+     *
+     * \tparam T  Type to get (which must match what the any stores).
+     *
+     * Asserts if the any is empty or does not contain the requested type.
+     */
+    template<typename T>
+    T& castRef()
+    {
+        T* value = tryCastRef<T>();
+        GMX_RELEASE_ASSERT(value != nullptr, "Cast to incorrect type");
+        return *value;
+    }
+
+private:
+    class IContent
+    {
+    public:
+        virtual ~IContent() {}
+        virtual const std::type_info&     typeInfo() const = 0;
+        virtual std::unique_ptr<IContent> clone() const    = 0;
+    };
+
+    template<typename T>
+    class Content : public IContent
+    {
+    public:
+        explicit Content(const T& value) : value_(value) {}
+        explicit Content(T&& value) : value_(std::move(value)) {}
+
+        const std::type_info&     typeInfo() const override { return typeid(T); }
+        std::unique_ptr<IContent> clone() const override
+        {
+            return std::make_unique<Content>(value_);
+        }
+
+        T value_;
+    };
+
+    //! Creates a deep copy of the content.
+    std::unique_ptr<IContent> cloneContent() const
+    {
+        return content_ != nullptr ? content_->clone() : nullptr;
+    }
+
+    std::unique_ptr<IContent> content_;
+};
+
+//! \cond libapi
+/*! \brief
+ * Converts a Any value to a string.
+ *
+ * As the name suggests, only some types of "simple" values (such as int) are
+ * supported.  Asserts for unsupported types.
+ *
+ * \ingroup module_utility
+ */
+std::string simpleValueToString(const Any& value);
+//! \endcond
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/arraysize.h b/src/include/gromacs/utility/arraysize.h
new file mode 100644 (file)
index 0000000..e7dcaa7
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Provides asize() function for calculating the static size of an array at compile time.
+ *
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_ARRAYSIZE_H
+#define GMX_UTILITY_ARRAYSIZE_H
+
+/*! \brief
+ * Calculates the number of elements in a static array at compile time.
+ *
+ * \ingroup module_utility
+ */
+template<typename T, int N>
+constexpr int asize(T (&/*unused*/)[N])
+{
+    static_assert(N >= 0, "Do negative size arrays exist?");
+    return N;
+}
+
+#endif
diff --git a/src/include/gromacs/utility/basenetwork.h b/src/include/gromacs/utility/basenetwork.h
new file mode 100644 (file)
index 0000000..b1fa383
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Utility functions for basic MPI and network functionality.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_BASENETWORK_H
+#define GMX_UTILITY_BASENETWORK_H
+
+/*! \brief
+ * Returns whether MPI has been initialized.
+ *
+ * The return value is `FALSE` if MPI_Init() has not been called, or if
+ * \Gromacs has been compiled without MPI support.
+ * For thread-MPI, returns `TRUE` when the threads have been started.
+ *
+ * Note that there is a lot of code in between MPI_Init() and the thread-MPI
+ * thread start where the return value is different depending on compilation
+ * options.
+ */
+bool gmx_mpi_initialized();
+
+/*! \brief
+ * Returns the number of nodes.
+ *
+ * For thread-MPI, returns one before the threads have been started.
+ * This allows code between the real MPI_Init() and the thread-MPI "init" to
+ * still use this function to check for serial/parallel status and work as
+ * expected: for thread-MPI, at that point they should behave as if the run was
+ * serial.
+ */
+int gmx_node_num();
+
+/*! \brief
+ * Returns the rank of the node.
+ *
+ * For thread-MPI, returns zero before the threads have been started.
+ * This allows code between the real MPI_Init() and the thread-MPI "init" to
+ * still use this function to check for master node work as expected:
+ * for thread-MPI, at that point the only thread of execution should behave as
+ * if it the master node.
+ */
+int gmx_node_rank();
+
+/*! \brief
+ * Return a non-negative hash that is, hopefully, unique for each physical
+ * node.
+ *
+ * This hash is useful for determining hardware locality.
+ */
+int gmx_physicalnode_id_hash();
+
+/*! \brief
+ * Broadcasts given data from rank zero to all other ranks.
+ */
+void gmx_broadcast_world(int size, void* buffer);
+
+/** Abort the parallel run */
+[[noreturn]] void gmx_abort(int errorno);
+
+#endif
diff --git a/src/include/gromacs/utility/baseversion-gen.cpp.cmakein b/src/include/gromacs/utility/baseversion-gen.cpp.cmakein
new file mode 100644 (file)
index 0000000..a78ba3c
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2012,2014,2015,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#include "gromacs/utility/baseversion_gen.h"
+
+const char gmx_ver_string[] = "@GMX_VERSION_STRING_FULL@";
+const char gmx_full_git_hash[] = "@GMX_VERSION_FULL_HASH@";
+const char gmx_central_base_hash[] = "@GMX_VERSION_CENTRAL_BASE_HASH@";
+const char gmxSourceDoiString[] = "@GMX_SOURCE_DOI@";
+const char gmxReleaseSourceFileChecksum[] = "@GMX_RELEASE_SOURCE_FILE_CHECKSUM@"; 
+const char gmxCurrentSourceFileChecksum[] = "@GMX_CURRENT_SOURCE_FILE_CHECKSUM@";
diff --git a/src/include/gromacs/utility/baseversion.h b/src/include/gromacs/utility/baseversion.h
new file mode 100644 (file)
index 0000000..aa0d284
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares functions to get basic version information.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_BASEVERSION_H
+#define GMX_UTILITY_BASEVERSION_H
+
+/*! \brief
+ * Version string, containing the version, date, and abbreviated hash.
+ *
+ * This can be a plain version if git version info was disabled during the
+ * build.
+ * The returned string used to start with a literal word `VERSION` before
+ * \Gromacs 2016, but no longer does.
+ *
+ * \ingroup module_utility
+ */
+const char* gmx_version();
+/*! \brief
+ * Full git hash of the latest commit.
+ *
+ * If git version info was disabled during the build, returns an empty string.
+ *
+ * \ingroup module_utility
+ */
+const char* gmx_version_git_full_hash();
+/*! \brief
+ * Full git hash of the latest commit in a central \Gromacs repository.
+ *
+ * If git version info was disabled during the build, returns an empty string.
+ * Also, if the latest commit was from a central repository, the return value
+ * is an empty string.
+ *
+ * \ingroup module_utility
+ */
+const char* gmx_version_git_central_base_hash();
+
+/*! \brief
+ * Defined if ``libgromacs`` has been compiled in double precision.
+ *
+ * Allows detecting the compiled precision of the library through checking the
+ * presence of the symbol, e.g., from autoconf or other types of build systems.
+ *
+ * \ingroup module_utility
+ */
+void gmx_is_double_precision();
+/*! \brief
+ * Defined if ``libgromacs`` has been compiled in single/mixed precision.
+ *
+ * Allows detecting the compiled precision of the library through checking the
+ * presence of the symbol, e.g., from autoconf or other types of build systems.
+ *
+ * \ingroup module_utility
+ */
+
+void gmx_is_single_precision();
+
+/*! \brief Return a string describing what kind of GPU suport was configured in the build.
+ *
+ * Currently returns correctly for CUDA, OpenCL and SYCL.
+ * Needs to be updated when adding new acceleration options.
+ */
+const char* getGpuImplementationString();
+
+/*! \brief
+ * DOI string, or empty when not a release build.
+ */
+const char* gmxDOI();
+
+/*! \brief
+ * Hash of the complete source released in the tarball.
+ *
+ * Empty when not a release tarball build.
+ */
+const char* gmxReleaseSourceChecksum();
+
+/*! \brief
+ * Hash of the complete source actually used when building.
+ *
+ * Always computed when building from tarball.
+ */
+const char* gmxCurrentSourceChecksum();
+
+#endif
diff --git a/src/include/gromacs/utility/baseversion_gen.h b/src/include/gromacs/utility/baseversion_gen.h
new file mode 100644 (file)
index 0000000..0249270
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares variables that hold generated version information.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_BASEVERSION_GEN_H
+#define GMX_UTILITY_BASEVERSION_GEN_H
+
+/*! \cond internal */
+//! \addtogroup module_utility
+//! \{
+
+//! Version string, containing the version, date, and abbreviated hash.
+extern const char gmx_ver_string[];
+//! Full git hash of the latest commit.
+extern const char gmx_full_git_hash[];
+//! Full git hash of the latest commit in a central \Gromacs repository.
+extern const char gmx_central_base_hash[];
+/*! \brief
+ *  DOI string for the \Gromacs source code populated by CMake.
+ *
+ *  The variable is populated with the generated DOI string
+ *  by CMake when the build of a release version is
+ *  requested by Jenkins. Allows identification and
+ *  referencing of different \Gromacs releases.
+ */
+extern const char gmxSourceDoiString[];
+//! Sha256 checksum of source and header files, populated for release builds.
+extern const char gmxReleaseSourceFileChecksum[];
+//! Sha256 checksum of source and header files, populated for builds from tarball.
+extern const char gmxCurrentSourceFileChecksum[];
+
+//! \}
+//! \endcond
+
+#endif
diff --git a/src/include/gromacs/utility/binaryinformation.h b/src/include/gromacs/utility/binaryinformation.h
new file mode 100644 (file)
index 0000000..6449ff7
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Helper functionality for information about the currently running binary
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_BINARYINFORMATION_H
+#define GMX_UTILITY_BINARYINFORMATION_H
+
+#include <cstdio>
+
+namespace gmx
+{
+
+class IProgramContext;
+class TextWriter;
+
+/*! \libinternal \brief
+ * Settings for printBinaryInformation().
+ *
+ * This class is used to specify what printBinaryInformation() prints.
+ *
+ * \ingroup module_utility
+ * \inlibraryapi
+ */
+class BinaryInformationSettings
+{
+public:
+    BinaryInformationSettings();
+
+    //! Set whether to print information about build settings.
+    BinaryInformationSettings& extendedInfo(bool bEnabled)
+    {
+        bExtendedInfo_ = bEnabled;
+        return *this;
+    }
+    //! Set whether to print copyright and license information.
+    BinaryInformationSettings& copyright(bool bEnabled)
+    {
+        bCopyright_ = bEnabled;
+        return *this;
+    }
+    //! Set whether to print the process ID.
+    BinaryInformationSettings& processId(bool bEnabled)
+    {
+        bProcessId_ = bEnabled;
+        return *this;
+    }
+    //! Set whether to print a header line with "Generated by" text (for output files).
+    BinaryInformationSettings& generatedByHeader(bool bEnabled)
+    {
+        bGeneratedByHeader_ = bEnabled;
+        return *this;
+    }
+    //! Prefix each line with this string.
+    BinaryInformationSettings& linePrefix(const char* prefix)
+    {
+        prefix_ = prefix;
+        return *this;
+    }
+    //! Suffix each line with this string.
+    BinaryInformationSettings& lineSuffix(const char* suffix)
+    {
+        suffix_ = suffix;
+        return *this;
+    }
+
+private:
+    bool        bExtendedInfo_;
+    bool        bCopyright_;
+    bool        bProcessId_;
+    bool        bGeneratedByHeader_;
+    const char* prefix_;
+    const char* suffix_;
+
+    //! Needed to read the members without otherwise unnecessary accessors.
+    friend void printBinaryInformation(TextWriter*                      writer,
+                                       const IProgramContext&           programContext,
+                                       const BinaryInformationSettings& settings);
+};
+
+/*! \brief
+ * Print basic information about the executable.
+ *
+ * \param     fp             Where to print the information to.
+ * \param[in] programContext Program information object to use.
+ */
+void printBinaryInformation(FILE* fp, const IProgramContext& programContext);
+/*! \brief
+ * Print basic information about the executable with custom settings.
+ *
+ * \param     fp             Where to print the information to.
+ * \param[in] programContext Program information object to use.
+ * \param[in] settings       Specifies what to print.
+ *
+ * \see BinaryInformationSettings
+ */
+void printBinaryInformation(FILE*                            fp,
+                            const IProgramContext&           programContext,
+                            const BinaryInformationSettings& settings);
+
+/*! \brief
+ * Print basic information about the executable with custom settings.
+ *
+ * \param[out] writer         Where to print the information.
+ * \param[in]  programContext Program information object to use.
+ * \param[in]  settings       Specifies what to print.
+ * \throws     std::bad_alloc if out of memory.
+ *
+ * \see BinaryInformationSettings
+ */
+void printBinaryInformation(TextWriter*                      writer,
+                            const IProgramContext&           programContext,
+                            const BinaryInformationSettings& settings);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/bitmask.h b/src/include/gromacs/utility/bitmask.h
new file mode 100644 (file)
index 0000000..6d4cb52
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx_bitmask_t and associated functions
+ *
+ * \author Roland Schulz <roland@utk.edu>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+
+#ifndef GMX_MDLIB_BITMASK_H
+#define GMX_MDLIB_BITMASK_H
+
+#include "config.h" /* for GMX_MAX_OPENMP_THREADS */
+
+#include <string.h>
+
+#include <algorithm>
+#include <array>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/stringutil.h"
+
+/*! \brief Size of bitmask. Has to be 32 or multiple of 64. */
+#ifndef BITMASK_SIZE
+#    define BITMASK_SIZE GMX_OPENMP_MAX_THREADS
+#endif
+
+#if BITMASK_SIZE != 32 && BITMASK_SIZE % 64 != 0
+#    error BITMASK_SIZE has to be 32 or a multiple of 64.
+#endif
+
+#if BITMASK_SIZE <= 64 || defined DOXYGEN
+#    if BITMASK_SIZE == 32
+typedef uint32_t gmx_bitmask_t;
+#    else
+typedef uint64_t gmx_bitmask_t; /**< bitmask type */
+#    endif
+
+/*! \brief Initialize all bits to 0 */
+inline static void bitmask_clear(gmx_bitmask_t* m)
+{
+    *m = 0;
+}
+
+/*! \brief Set bit at position b to 1. */
+inline static void bitmask_set_bit(gmx_bitmask_t* m, int b)
+{
+    *m |= gmx_bitmask_t(1) << b;
+}
+
+/*! \brief Initialize all bits: bit b to 1, others to 0 */
+inline static void bitmask_init_bit(gmx_bitmask_t* m, int b)
+{
+    *m = gmx_bitmask_t(1) << b;
+}
+
+/*! \brief Initialize all bits: all bits below b to 1, others to 0 */
+inline static void bitmask_init_low_bits(gmx_bitmask_t* m, int b)
+{
+    *m = (gmx_bitmask_t(1) << b) - 1;
+}
+
+/*! \brief Test if bit b is set */
+inline static bool bitmask_is_set(gmx_bitmask_t m, int b)
+{
+    return (m & (gmx_bitmask_t(1) << b)) != 0;
+}
+
+/*! \brief Test if both bitmasks have no common bits enabled */
+inline static bool bitmask_is_disjoint(gmx_bitmask_t a, gmx_bitmask_t b)
+{
+    return (a & b) == 0U;
+}
+
+/*! \brief Test if both bitmasks are equal */
+inline static bool bitmask_is_equal(gmx_bitmask_t a, gmx_bitmask_t b)
+{
+    return a == b;
+}
+
+/*! \brief Test if bitmask has no enabled bits */
+inline static bool bitmask_is_zero(gmx_bitmask_t m)
+{
+    return m == 0U;
+}
+
+/*! \brief Set all bits enabled in either mask and write into a */
+inline static void bitmask_union(gmx_bitmask_t* a, gmx_bitmask_t b)
+{
+    *a |= b;
+}
+#else
+#    define BITMASK_ALEN (BITMASK_SIZE / 64)
+using gmx_bitmask_t = std::array<uint64_t, BITMASK_ALEN>;
+
+inline static void bitmask_clear(gmx_bitmask_t* m)
+{
+    m->fill(0);
+}
+
+inline static void bitmask_set_bit(gmx_bitmask_t* m, int b)
+{
+    (*m)[b / 64] |= (static_cast<uint64_t>(1) << (b % 64));
+}
+
+inline static void bitmask_init_bit(gmx_bitmask_t* m, int b)
+{
+    bitmask_clear(m);
+    (*m)[b / 64] = (static_cast<uint64_t>(1) << (b % 64));
+}
+
+inline static void bitmask_init_low_bits(gmx_bitmask_t* m, int b)
+{
+    memset(m->data(), 255, b / 64 * 8);
+    (*m)[b / 64] = (static_cast<uint64_t>(1) << (b % 64)) - 1;
+    memset(m->data() + (b / 64 + 1), 0, (BITMASK_ALEN - b / 64 - 1) * 8);
+}
+
+inline static bool bitmask_is_set(gmx_bitmask_t m, int b)
+{
+    return (m[b / 64] & (static_cast<uint64_t>(1) << (b % 64))) != 0;
+}
+
+inline static bool bitmask_is_disjoint(gmx_bitmask_t a, gmx_bitmask_t b)
+{
+    bool r = true;
+    for (int i = 0; i < BITMASK_ALEN; i++)
+    {
+        r = r && ((a[i] & b[i]) == 0U);
+    }
+    return r;
+}
+
+inline static bool bitmask_is_equal(gmx_bitmask_t a, gmx_bitmask_t b)
+{
+    bool r = true;
+    for (int i = 0; i < BITMASK_ALEN; i++)
+    {
+        r = r && (a[i] == b[i]);
+    }
+    return r;
+}
+
+inline static bool bitmask_is_zero(gmx_bitmask_t m)
+{
+    bool r = true;
+    for (int i = 0; i < BITMASK_ALEN; i++)
+    {
+        r = r && (m[i] == 0U);
+    }
+    return r;
+}
+
+inline static void bitmask_union(gmx_bitmask_t* a, gmx_bitmask_t b)
+{
+    for (int i = 0; i < BITMASK_ALEN; i++)
+    {
+        (*a)[i] |= b[i];
+    }
+}
+#endif
+// In bitmask.h because only current use is for bitmask.
+
+//! Convert uint32_t to hex string
+inline static std::string to_hex_string(uint32_t m)
+{
+    return gmx::formatString("%08" PRIx32, m);
+}
+//! Convert uint64_t to hex string
+inline static std::string to_hex_string(uint64_t m)
+{
+    return gmx::formatString("%016" PRIx64, m);
+}
+//! Convert container of intergers to hex string
+template<typename C>
+inline static std::string to_hex_string(C m)
+{
+    std::string ret;
+    for (auto it = m.rbegin(); it < m.rend(); ++it)
+    {
+        ret += to_hex_string(*it);
+    }
+    return ret;
+}
+
+#endif
diff --git a/src/include/gromacs/utility/classhelpers.h b/src/include/gromacs/utility/classhelpers.h
new file mode 100644 (file)
index 0000000..ccee1e2
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares common utility classes and macros.
+ *
+ * This header contains helpers used to implement classes in the library.
+ * It is installed, because the helpers are used in installed headers, but
+ * typically users of the library should not need to be aware of these helpers.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_CLASSHELPERS_H
+#define GMX_UTILITY_CLASSHELPERS_H
+
+namespace gmx
+{
+
+#ifdef DOXYGEN
+/*! \brief
+ * Macro to declare a class non-copyable and non-assignable.
+ *
+ * For consistency, should appear last in the class declaration.
+ *
+ * \ingroup module_utility
+ */
+#    define GMX_DISALLOW_COPY_AND_ASSIGN(ClassName)
+#else
+#    define GMX_DISALLOW_COPY_AND_ASSIGN(ClassName)      \
+        ClassName& operator=(const ClassName&) = delete; \
+        ClassName(const ClassName&)            = delete
+#endif
+#ifdef DOXYGEN
+/*! \brief
+ * Macro to declare a class non-copyable, non-movable, non-copy-assignable and
+ * non-move-assignable.
+ *
+ * For consistency, should appear last in the class declaration.
+ *
+ * \ingroup module_utility
+ */
+#    define GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(ClassName)
+#else
+#    define GMX_DISALLOW_COPY_MOVE_AND_ASSIGN(ClassName)                                                            \
+        ClassName& operator=(const ClassName&) = delete;                                                            \
+        ClassName(const ClassName&)            = delete;                                                            \
+        ClassName& operator=(ClassName&&) = delete; /* NOLINT(misc-macro-parentheses,bugprone-macro-parentheses) */ \
+        ClassName(ClassName&&) = delete /* NOLINT(misc-macro-parentheses,bugprone-macro-parentheses) */
+#endif
+/*! \brief
+ * Macro to declare a class non-assignable.
+ *
+ * For consistency, should appear last in the class declaration.
+ *
+ * \ingroup module_utility
+ */
+#define GMX_DISALLOW_ASSIGN(ClassName) ClassName& operator=(const ClassName&) = delete
+
+// clang-format off
+#ifdef DOXYGEN
+/*! \brief
+ * Macro to declare default constructors
+ *
+ * Intended for copyable interfaces or bases classes which require to create custom
+ * destructor (e.g. protected or virtual) but need the default constructors.
+ *
+ * \ingroup module_utility
+ */
+#    define GMX_DEFAULT_CONSTRUCTORS(ClassName)
+#else
+#    define GMX_DEFAULT_CONSTRUCTORS(ClassName)                                                                           \
+        ClassName() = default;                                                                                            \
+        ClassName& operator=(const ClassName&) = default; /* NOLINT(misc-macro-parentheses,bugprone-macro-parentheses) */ \
+        ClassName(const ClassName&) = default;                                                                            \
+        ClassName& operator=(ClassName&&) = default; /* NOLINT(misc-macro-parentheses,bugprone-macro-parentheses) */      \
+        ClassName(ClassName&&) = default /* NOLINT(misc-macro-parentheses,bugprone-macro-parentheses) */
+#endif
+//clang-format on
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/compare.h b/src/include/gromacs/utility/compare.h
new file mode 100644 (file)
index 0000000..e8e3948
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Utilities for comparing data structures (for gmx check).
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_COMPARE_H
+#define GMX_UTILITY_COMPARE_H
+
+#include <cstdio>
+
+#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+//! Compares two real values for equality.
+gmx_bool equal_real(real i1, real i2, real ftol, real abstol);
+//! Compares two float values for equality.
+gmx_bool equal_float(float i1, float i2, float ftol, float abstol);
+//! Compares two double values for equality.
+gmx_bool equal_double(double i1, double i2, real ftol, real abstol);
+
+//! Compares two integers and prints differences.
+void cmp_int(FILE* fp, const char* s, int index, int i1, int i2);
+
+//! Compares two 64-bit integers and prints differences.
+void cmp_int64(FILE* fp, const char* s, int64_t i1, int64_t i2);
+
+//! Compares two unsigned short values and prints differences.
+void cmp_us(FILE* fp, const char* s, int index, unsigned short i1, unsigned short i2);
+
+//! Compares two unsigned char values and prints differences.
+void cmp_uc(FILE* fp, const char* s, int index, unsigned char i1, unsigned char i2);
+
+//! Compares two boolean values and prints differences, and returns whether both are true.
+gmx_bool cmp_bool(FILE* fp, const char* s, int index, gmx_bool b1, gmx_bool b2);
+
+//! Compares two strings and prints differences.
+void cmp_str(FILE* fp, const char* s, int index, const char* s1, const char* s2);
+
+//! Compares two reals and prints differences.
+void cmp_real(FILE* fp, const char* s, int index, real i1, real i2, real ftol, real abstol);
+
+//! Compares two floats and prints differences.
+void cmp_float(FILE* fp, const char* s, int index, float i1, float i2, float ftol, float abstol);
+
+//! Compares two doubles and prints differences.
+void cmp_double(FILE* fp, const char* s, int index, double i1, double i2, double ftol, double abstol);
+
+//! Compare two enums of generic type and print differences.
+template<typename EnumType>
+void cmpEnum(FILE* fp, const char* s, EnumType value1, EnumType value2)
+{
+    if (value1 != value2)
+    {
+        fprintf(fp, "%s (", s);
+        fprintf(fp, "%s", enumValueToString(value1));
+        fprintf(fp, " - ");
+        fprintf(fp, "%s", enumValueToString(value2));
+        fprintf(fp, ")\n");
+    }
+}
+
+#endif
diff --git a/src/include/gromacs/utility/coolstuff.h b/src/include/gromacs/utility/coolstuff.h
new file mode 100644 (file)
index 0000000..3a13b77
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Functionality for printing cool strings
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_COOLSTUFF_H
+#define GMX_UTILITY_COOLSTUFF_H
+
+#include <string>
+
+namespace gmx
+{
+
+//! Return a cool definition for the acronym GROMACS
+std::string bromacs();
+
+//! Return a string with a cool quote
+std::string getCoolQuote();
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/cstringutil.h b/src/include/gromacs/utility/cstringutil.h
new file mode 100644 (file)
index 0000000..e2b4550
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2015,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief Generic C string handling functions.
+ *
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_CSTRINGUTIL_H
+#define GMX_UTILITY_CSTRINGUTIL_H
+
+#include <stdio.h>
+
+#include "gromacs/utility/basedefinitions.h"
+
+/** Continuation character. */
+#define CONTINUE '\\'
+/** Standard size for char* string buffers. */
+#define STRLEN 4096
+
+/*! \brief
+ * Strip trailing spaces and if s ends with a ::CONTINUE remove that too.
+ *
+ * \returns TRUE if s ends with a CONTINUE, FALSE otherwise.
+ */
+int continuing(char* s);
+
+/*! \brief
+ * Reads a line from a stream.
+ *
+ * This routine reads a string from stream of max length n, including
+ * \0 and zero terminated, without newlines.  \p s should be long
+ * enough (>= \p n)
+ */
+char* fgets2(char* s, int n, FILE* stream);
+
+/** Remove portion of a line after a ';' comment sign.  */
+void strip_comment(char* line);
+
+/** Make a string uppercase. */
+void upstring(char* str);
+
+/** Remove leading whitespace from a string. */
+void ltrim(char* str);
+
+/** Remove trailing whitespace from a string. */
+void rtrim(char* str);
+
+/** Remove leading and trailing whitespace from a string. */
+void trim(char* str);
+
+/** Version of gmx_strcasecmp() that also ignores '-' and '_'. */
+int gmx_strcasecmp_min(const char* str1, const char* str2);
+/** Version of gmx_strncasecmp() that also ignores '-' and '_'. */
+int gmx_strncasecmp_min(const char* str1, const char* str2, int n);
+
+/** Case-insensitive strcmp(). */
+int gmx_strcasecmp(const char* str1, const char* str2);
+/** Case-insensitive strncmp(). */
+int gmx_strncasecmp(const char* str1, const char* str2, int n);
+
+/** Creates a duplicate of \p src. */
+char* gmx_strdup(const char* src);
+/** Duplicates first \p n characters of \p src. */
+char* gmx_strndup(const char* src, int n);
+
+/*! \brief
+ * Pattern matching with wildcards.
+ *
+ * \param[in] pattern  Pattern to match against.
+ * \param[in] str      String to match.
+ * \returns   0 on match, GMX_NO_WCMATCH if there is no match.
+ *
+ * Matches \p str against \p pattern, which may contain * and ? wildcards.
+ * All other characters are matched literally.
+ * Currently, it is not possible to match literal * or ?.
+ */
+int gmx_wcmatch(const char* pattern, const char* str);
+
+/** Return value for gmx_wcmatch() when there is no match. */
+#define GMX_NO_WCMATCH 1
+
+/** Magic hash initialization number from Dan J. Bernstein. */
+extern const unsigned int gmx_string_hash_init;
+
+/*! \brief
+ * Return a hash of the string according to Dan J. Bernsteins algorithm.
+ *
+ * \param[in] s          String to calculate hash for.
+ * \param[in] hash_init  Initial (or previous) hash value.
+ * \returns   Updated hash value (hash_init combined with string hash).
+ *
+ * On the first invocation for a new string, use the constant
+ * gmx_string_hash_init for the second argument. If you want to create a hash
+ * corresponding to several concatenated strings, provide the returned hash
+ * value as hash_init for the second string, etc.
+ */
+unsigned int gmx_string_fullhash_func(const char* s, unsigned int hash_init);
+
+/*! \brief
+ * Return a hash of the string according to Dan J. Bernsteins algorithm.
+ *
+ * \param[in] s          String to calculate hash for.
+ * \param[in] hash_init  Initial (or previous) hash value.
+ * \returns   Updated hash value (hash_init combined with string hash).
+ *
+ * Identical to gmx_string_fullhash_func, except that
+ * this routine only uses characters for which isalnum(c) is true,
+ * and all characters are converted to upper case.
+ */
+unsigned int gmx_string_hash_func(const char* s, unsigned int hash_init);
+
+/*! \brief
+ * Wraps lines, optionally indenting lines.
+ *
+ * Wraps lines at \p linewidth, indenting all following lines by \p indent
+ * spaces.  A temp buffer is allocated and returned, which can be disposed of
+ * if no longer needed.
+ * If \p bIndentFirst is FALSE, then the first line will not be indented, only
+ * the lines that are created due to wapping.
+ */
+char* wrap_lines(const char* buf, int line_width, int indent, gmx_bool bIndentFirst);
+
+/*! \brief
+ * Convert a string to int64_t.
+ *
+ * This method works as the standard library function strtol(), except that it
+ * does not support different bases.
+ */
+int64_t str_to_int64_t(const char* str, char** endptr);
+
+/** Minimum size of buffer to pass to gmx_step_str(). */
+#define STEPSTRSIZE 22
+
+/*! \brief
+ * Prints a int64_t value in buf and returns the pointer to buf.
+ *
+ * buf should be large enough to contain i: STEPSTRSIZE (22) chars.
+ * When multiple int64_t values are printed in the same printf call,
+ * be sure to call gmx_step_str with different buffers.
+ */
+char* gmx_step_str(int64_t i, char* buf);
+
+#endif
diff --git a/src/include/gromacs/utility/cuda_version_information.h b/src/include/gromacs/utility/cuda_version_information.h
new file mode 100644 (file)
index 0000000..79b95a8
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_UTILITY_CUDA_VERSION_INFORMATION_H
+#define GMX_UTILITY_CUDA_VERSION_INFORMATION_H
+
+#include <string>
+
+namespace gmx
+{
+
+//! Returns a string of the CUDA driver version.
+std::string getCudaDriverVersionString();
+
+//! Returns a string of the CUDA runtime version.
+std::string getCudaRuntimeVersionString();
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/datafilefinder.h b/src/include/gromacs/utility/datafilefinder.h
new file mode 100644 (file)
index 0000000..45e4c70
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::DataFileFinder and related classes.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_LIBFILEFINDER_H
+#define GMX_UTILITY_LIBFILEFINDER_H
+
+#include <cstdio>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/fileptr.h"
+
+namespace gmx
+{
+
+class DataFileFinder;
+
+/*! \brief
+ * Search parameters for DataFileFinder.
+ *
+ * This class implements a named parameter idiom for DataFileFinder::findFile()
+ * and DataFileFinder::openFile() to support an easily readable and
+ * customizable way of searching data files.
+ *
+ * By default, the search first considers the current directory, followed by
+ * specified and default data directories, and an exception is thrown if the
+ * file could not be found.
+ * To skip searching in the current directory, use includeCurrentDir().
+ *
+ * Methods in this class do not throw.
+ *
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+class DataFileOptions
+{
+public:
+    /*! \brief
+     * Constructs default options for searching for a file with the
+     * specified name.
+     *
+     * \param[in] filename  File name to search for.
+     *
+     * This constructor is not explicit to allow passing a simple string to
+     * DataFileFinder methods to search for the string with the default
+     * parameters.
+     */
+    DataFileOptions(const char* filename) : filename_(filename), bCurrentDir_(true), bThrow_(true)
+    {
+    }
+    //! \copydoc DataFileOptions(const char *)
+    DataFileOptions(const std::string& filename) :
+        filename_(filename.c_str()), bCurrentDir_(true), bThrow_(true)
+    {
+    }
+
+    //! Sets whether to search first in the current (working) directory.
+    DataFileOptions& includeCurrentDir(bool bInclude)
+    {
+        bCurrentDir_ = bInclude;
+        return *this;
+    }
+    //! Sets whether an exception is thrown if the file could not be found.
+    DataFileOptions& throwIfNotFound(bool bThrow)
+    {
+        bThrow_ = bThrow;
+        return *this;
+    }
+
+private:
+    const char* filename_;
+    bool        bCurrentDir_;
+    bool        bThrow_;
+
+    /*! \brief
+     * Needed to access the members without otherwise unnecessary accessors.
+     */
+    friend class DataFileFinder;
+};
+
+/*! \brief
+ * Information about a data file found by DataFileFinder::enumerateFiles().
+ *
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+struct DataFileInfo
+{
+    //! Initializes the structure with given values.
+    DataFileInfo(const std::string& dir, const std::string& name, bool bDefault) :
+        dir(dir), name(name), bFromDefaultDir(bDefault)
+    {
+    }
+
+    /*! \brief
+     * Directory from which the file was found.
+     *
+     * If the file was found from the current directory, this will be `"."`.
+     * In other cases, this will be a full path (except if the user-provided
+     * search path contains relative paths).
+     */
+    std::string dir;
+    /*! \brief
+     * Name of the file without any directory name.
+     */
+    std::string name;
+    /*! \brief
+     * Whether the file was found from the default directory.
+     *
+     * If `true`, the file was found from the default installation data
+     * directory, not from the current directory or any user-provided (through
+     * DataFileFinder::setSearchPathFromEnv()) location.
+     * \todo
+     * Consider replacing with an enum that identifies the source (current dir,
+     * GMXLIB, default).
+     */
+    bool bFromDefaultDir;
+};
+
+/*! \brief
+ * Searches data files from a set of paths.
+ *
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+class DataFileFinder
+{
+public:
+    /*! \brief
+     * Constructs a default data file finder.
+     *
+     * The constructed finder searches only in the directory specified by
+     * the global program context (see IProgramContext), and
+     * optionally in the current directory.
+     *
+     * Does not throw.
+     */
+    DataFileFinder();
+    ~DataFileFinder();
+
+    /*! \brief
+     * Adds search path from an environment variable.
+     *
+     * \param[in] envVarName  Name of the environment variable to use.
+     * \throws std::bad_alloc if out of memory.
+     *
+     * If the specified environment variable is set, it is interpreted like
+     * a `PATH` environment variable on the platform (split at appropriate
+     * separators), and each path found is added to the search path this
+     * finder searches.  The added paths take precedence over the default
+     * directory specified by the global program context, but the current
+     * directory is searched first.
+     */
+    void setSearchPathFromEnv(const char* envVarName);
+
+    /*! \brief
+     * Opens a data file (if found) in an RAII-style `FILE` handle.
+     *
+     * \param[in] options  Identifies the file to be searched for.
+     * \returns The opened file handle, or `NULL` if the file could not be
+     *     found and exceptions were turned off.
+     * \throws  std::bad_alloc if out of memory.
+     * \throws  FileIOError if
+     *   - no such file can be found, and \p options specifies that an
+     *     exception should be thrown, or
+     *   - there is an error opening the file (note that a file is skipped
+     *     during the search if the user does not have rights to open the
+     *     file at all).
+     *
+     * See findFile() for more details.
+     */
+    FilePtr openFile(const DataFileOptions& options) const;
+    /*! \brief
+     * Finds a full path to a data file if found.
+     *
+     * \param[in] options  Identifies the file to be searched for.
+     * \returns Full path to the data file, or an empty string if the file
+     *     could not be found and exceptions were turned off.
+     * \throws  std::bad_alloc if out of memory.
+     * \throws  FileIOError if no such file can be found, and \p options
+     *     specifies that an exception should be thrown.
+     *
+     * Searches for a data file in the search paths configured for the
+     * finder, as well as in the current directory if so required.
+     * Returns the full path to the first file found.
+     */
+    std::string findFile(const DataFileOptions& options) const;
+    /*! \brief
+     * Enumerates files in the data directories.
+     *
+     * \param[in] options  Idenfies files to be searched for.
+     * \returns Information about each found file.
+     * \throws  std::bad_alloc if out of memory.
+     * \throws  FileIOError if no such file can be found, and \p options
+     *     specifies that an exception should be thrown.
+     *
+     * Enumerates all files in the data directories that have the
+     * extension/suffix specified by the file name in \p options.
+     * Unlike findFile() and openFile(), this only works on files that are
+     * in the actual data directories, not for any entry within
+     * subdirectories of those.
+     * See DataFileInfo for details on what is returned for each found
+     * file.
+     * Files from the same directory will be returned as a continuous block
+     * in the returned vector.
+     */
+    std::vector<DataFileInfo> enumerateFiles(const DataFileOptions& options) const;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/defaultinitializationallocator.h b/src/include/gromacs/utility/defaultinitializationallocator.h
new file mode 100644 (file)
index 0000000..205b123
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Declares an allocator that can use default initialization instead
+ * of values initialization. This is useful for improving performance of
+ * resize() in standard vectors for buffers in performance critical code.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_DEFAULTINITIALIZATIONALLOCATOR_H
+#define GMX_UTILITY_DEFAULTINITIALIZATIONALLOCATOR_H
+
+#include <memory>
+
+namespace gmx
+{
+
+/*! \libinternal \brief Allocator adaptor that interposes construct() calls to
+ * convert value initialization into default initialization.
+ *
+ * This can be used to avoid initialization e.g. on resize() in std::vector.
+ */
+template<typename T, typename A = std::allocator<T>>
+class DefaultInitializationAllocator : public A
+{
+    typedef std::allocator_traits<A> a_t;
+
+public:
+    template<typename U>
+    struct rebind
+    {
+        using other = DefaultInitializationAllocator<U, typename a_t::template rebind_alloc<U>>;
+    };
+
+    using A::A;
+
+    /*! \brief Constructs an object and default initializes
+     *
+     * \todo Use std::is_nothrow_default_constructible_v when CUDA 11 is a requirement.
+     */
+    template<typename U>
+    void construct(U* ptr) noexcept(std::is_nothrow_default_constructible<U>::value)
+    {
+        ::new (static_cast<void*>(ptr)) U;
+    }
+
+    /*! \brief Constructs an object and value initializes */
+    template<typename U, typename... Args>
+    void construct(U* ptr, Args&&... args)
+    {
+        a_t::construct(static_cast<A&>(*this), ptr, std::forward<Args>(args)...);
+    }
+};
+
+} // namespace gmx
+
+#endif // GMX_UTILITY_DEFAULTINITIALIZATIONALLOCATOR_H
diff --git a/src/include/gromacs/utility/dir_separator.h b/src/include/gromacs/utility/dir_separator.h
new file mode 100644 (file)
index 0000000..dea093d
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2016,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Provides OS-specific directory-name separator
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_DIR_SEPARATOR_H
+#define GMX_UTILITY_DIR_SEPARATOR_H
+
+#include "config.h"
+
+/*! \def DIR_SEPARATOR
+ * \brief
+ * Directory separator on this OS.
+ *
+ * Native Windows uses backslash path separators (but accepts also slashes).
+ * Cygwin and most other systems use slash.
+ *
+ * \todo Get rid of this (Issue #950). It is not necessary for
+ * constructing paths on the systems that it currently supports, and
+ * is not reliable in parsing input paths either, since Windows needs
+ * to accept both instead of only DIR_SEPARATOR. At the very least, we
+ * should refactor the clients of this header so that they operate
+ * upon path objects rather than raw path strings.
+ */
+#if GMX_NATIVE_WINDOWS
+#    define DIR_SEPARATOR '\\'
+#else
+#    define DIR_SEPARATOR '/'
+#endif
+
+#endif
diff --git a/src/include/gromacs/utility/directoryenumerator.h b/src/include/gromacs/utility/directoryenumerator.h
new file mode 100644 (file)
index 0000000..36dec8b
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2016,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::DirectoryEnumerator.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_DIRECTORYENUMERATOR_H
+#define GMX_UTILITY_DIRECTORYENUMERATOR_H
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Lists files in a directory.
+ *
+ * If multiple threads share the same DirectoryEnumerator, they must
+ * take responsibility for their mutual synchronization, particularly
+ * with regard to calling nextFile().
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class DirectoryEnumerator
+{
+public:
+    /*! \brief
+     * Convenience function to list files with certain extension from a
+     * directory.
+     *
+     * \param[in]  dirname   Path to the directory to list.
+     * \param[in]  extension List files with the given extension
+     *     (or suffix in file name).
+     * \param[in]  bThrow    Whether failure to open the directory should throw.
+     * \returns    List of files with the given extension in \p dirname.
+     * \throws std::bad_alloc if out of memory.
+     * \throws FileIOError if opening the directory fails and `bThrow == true`.
+     * \throws FileIOError if some other I/O error occurs.
+     */
+    static std::vector<std::string> enumerateFilesWithExtension(const char* dirname,
+                                                                const char* extension,
+                                                                bool        bThrow);
+
+    /*! \brief
+     * Opens a directory for listing.
+     *
+     * \param[in] dirname Path to the directory to list.
+     * \param[in] bThrow  Whether failure to open the directory should throw.
+     * \throws std::bad_alloc if out of memory.
+     * \throws FileIOError if opening the directory fails and `bThrow == true`
+     */
+    explicit DirectoryEnumerator(const char* dirname, bool bThrow = true);
+    //! \copydoc DirectoryEnumerator(const char *, bool)
+    explicit DirectoryEnumerator(const std::string& dirname, bool bThrow = true);
+    ~DirectoryEnumerator();
+
+    /*! \brief
+     * Gets next file in a directory.
+     *
+     * \param[out] filename  Name of the next file.
+     * \returns `false` if there were no more files.
+     * \throws  std::bad_alloc if out of memory.
+     * \throws  FileIOError if listing the next file fails.
+     *
+     * If all files from the directory have been returned (or there are no
+     * files in the directory and this is the first call), the method
+     * returns `false` and \p filename is cleared.
+     * Otherwise, the return value is `true` and the first/next file name
+     * is returned in \p filename.
+     * \p filename will not contain any path information, only the name of
+     * the file.
+     *
+     * If `bThrow` passed to the constructor was `false` and the directory
+     * was not successfully opened, the first call to this function will
+     * return `false`.
+     *
+     * This method is not thread safe when called on the same
+     * object by multiple threads. Such use requires external
+     * synchronization.
+     */
+    bool nextFile(std::string* filename);
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/enumerationhelpers.h b/src/include/gromacs/utility/enumerationhelpers.h
new file mode 100644 (file)
index 0000000..b30c8fe
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief Defines helper types for class enumerations.
+ *
+ * These helper types facilitate iterating over class enums, and
+ * maintaining a type-safe and value-safe matching list of names. The
+ * code is closely based on the public-domain code by Guilherme
+ * R. Lampert, found in commit c94c18a of
+ * https://github.com/glampert/enum_helpers/blob/master/enum_helpers.hpp
+ * Thanks Guilherme!
+ *
+ * NOTE This functionality only works for enumerations of monotonically
+ * increasing values, starting with the value zero.
+ *
+ * Usage examples:
+ *
+ *  enum class Foo : int
+ *  {
+ *      Bar,
+ *      Baz,
+ *      Fooz,
+ *      Count
+ *  };
+ *
+ *  for (Foo c : EnumerationWrapper<Foo>{})
+ *  {
+ *      // 'c' is a constant from Foo
+ *  }
+ *
+ *
+ *  const EnumerationArray<Foo, std::string> fooStrings = { { "Bar", "Baz", "Fooz" } };
+ *  std::cout << fooStrings[Foo::Baz];
+ *  std::cout << fooStrings[Foo::Count]; // Triggers an assertion
+ *
+ *  for (Foo c : keysOf(fooStrings))
+ *  {
+ *      print(fooStrings[c]);
+ *  }
+ *
+ *  ArrayRef<const std::string> namesRef(fooStrings);
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_ENUMHELPERS_H
+#define GMX_UTILITY_ENUMHELPERS_H
+
+#include <cstddef>
+
+#include <iterator>
+#include <type_traits>
+
+#if __has_include(<boost/stl_interfaces/iterator_interface.hpp>)
+#    include <boost/stl_interfaces/iterator_interface.hpp>
+#else // fallback for installed headers
+#    include <gromacs/external/boost/stl_interfaces/iterator_interface.hpp>
+#endif
+
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief Allows iterating sequential enumerators.
+ *
+ * You can also provide an increment step > 1 if each constant is
+ * spaced by a larger value.  Terminating constant is assumed to be a
+ * 'Count' member, which is never iterated. A different name for the
+ * terminating constant can also be specified on declaration.
+ *
+ * NOTE This functionality only works for enumerations of monotonically
+ * increasing values, starting with the value zero.
+ *
+ * See file documentation for usage example.
+ *
+ * \tparam  EnumType   The enum (class) type.
+ * \tparam  Last       Last constant or number thereof (assumes a default 'Count' member).
+ * \tparam  Step       Step increment.
+ */
+template<typename EnumType, EnumType Last = EnumType::Count, std::ptrdiff_t Step = 1>
+class EnumerationIterator final :
+    public boost::stl_interfaces::iterator_interface<EnumerationIterator<EnumType, Last, Step>, std::random_access_iterator_tag, EnumType>
+{
+public:
+    // TODO: Use std::is_enum_v when CUDA 11 is a requirement.
+    static_assert(std::is_enum<EnumType>::value, "Enumeration iterator must be over an enum type.");
+    //! Convenience alias
+    using IntegerType = std::underlying_type_t<EnumType>;
+
+    constexpr EnumerationIterator() noexcept : m_current{ 0 } // Assumes 0 is the first constant
+    {
+    }
+    //! Conversion constructor
+    explicit constexpr EnumerationIterator(const EnumType index) noexcept :
+        m_current(static_cast<IntegerType>(index))
+    {
+    }
+    //! Addition-assignment operator
+    constexpr EnumerationIterator& operator+=(std::ptrdiff_t i) noexcept
+    {
+        m_current += Step * i;
+        return *this;
+    }
+    //! Dereference operator
+    constexpr EnumType operator*() const noexcept
+    {
+        GMX_ASSERT(m_current < static_cast<IntegerType>(Last), "dereferencing out of range");
+        return static_cast<EnumType>(m_current);
+    }
+    //! Difference operator
+    constexpr std::ptrdiff_t operator-(const EnumerationIterator other) const noexcept
+    {
+        return (static_cast<std::ptrdiff_t>(m_current) - static_cast<std::ptrdiff_t>(other.m_current)) / Step;
+    }
+
+private:
+    IntegerType m_current;
+};
+
+/*! \libinternal
+ * \brief Allows constructing iterators for looping over sequential enumerators.
+ *
+ * These are particularly useful for range-based for statements.
+ *
+ * You can also provide an increment step > 1 if each constant is
+ * spaced by a larger value.  Terminating constant is assumed to be a
+ * 'Count' member, which is never iterated. A different name for the
+ * terminating constant can also be specified on declaration.
+ *
+ * See file documentation for usage example.
+ *
+ * \tparam  EnumType   The enum (class) type.
+ * \tparam  Last       Last constant or number thereof (assumes a default 'Count' member).
+ * \tparam  Step       Step increment.
+ */
+template<typename EnumType, EnumType Last = EnumType::Count, unsigned int Step = 1>
+class EnumerationWrapper final
+{
+public:
+    //! Convenience alias.
+    using IteratorType = EnumerationIterator<EnumType, Last, Step>;
+
+    //! Functions required for range-based for statements to work.
+    /*!@{*/
+    IteratorType begin() const { return IteratorType{}; }
+    IteratorType end() const { return IteratorType{ Last }; }
+    /*!@}*/
+};
+
+/*! \libinternal
+ * \brief Wrapper for a C-style array with size and indexing defined
+ * by an enum. Useful for declaring arrays of enum names for debug
+ * or other printing. An ArrayRef<DataType> may be constructed from
+ * an object of this type.
+ *
+ * See file documentation for usage example.
+ *
+ * Note that if clang-tidy gives strange errors referring to the line
+ * number of the struct declaration, these likely refer to the
+ * compiler-generated constructors. Simplification of the calling code
+ * might eliminate that call and thus the clang-tidy error.
+ *
+ * \tparam  EnumType   The enum (class) type.
+ * \tparam  DataType   Type of the data stored in the array.
+ * \tparam  ArraySize  Size in entries of the array.
+ */
+template<typename EnumType, typename DataType, EnumType ArraySize = EnumType::Count>
+struct EnumerationArray final // NOLINT
+{
+    //! Convenience alias
+    using EnumerationWrapperType = EnumerationWrapper<EnumType, ArraySize>;
+
+    //! Convenience alias
+    using value_type = DataType;
+
+    /*! \brief Data for names.
+     *
+     * Data is kept public so we can use direct aggregate
+     * initialization just like in a plain C-style array. */
+    DataType m_elements[std::size_t(ArraySize)];
+
+    //! Returns an object that provides iterators over the keys.
+    static constexpr EnumerationWrapperType keys() { return EnumerationWrapperType{}; }
+    //! Returns the size of the enumeration.
+    constexpr std::size_t size() const { return std::size_t(ArraySize); }
+
+    /*!@{*/
+    //! Array access with asserts:
+    DataType& operator[](const std::size_t index)
+    {
+        GMX_ASSERT(index < size(), "index out of range");
+        return m_elements[index];
+    }
+    const DataType& operator[](const std::size_t index) const
+    {
+        GMX_ASSERT(index < size(), "index out of range");
+        return m_elements[index];
+    }
+
+    DataType& operator[](const EnumType index)
+    {
+        GMX_ASSERT(std::size_t(index) < size(), "index out of range");
+        return m_elements[std::size_t(index)];
+    }
+    const DataType& operator[](const EnumType index) const
+    {
+        GMX_ASSERT(std::size_t(index) < size(), "index out of range");
+        return m_elements[std::size_t(index)];
+    }
+    /*!@}*/
+
+    /*!@{*/
+    //! Range iterators (unchecked)
+    using iterator               = DataType*;
+    using const_iterator         = const DataType*;
+    using reverse_iterator       = std::reverse_iterator<iterator>;
+    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+    /*!@}*/
+
+    /*!@{*/
+    //! Getters for forward iterators for ranges
+    iterator       begin() { return &m_elements[0]; }
+    iterator       end() { return &m_elements[size()]; }
+    const_iterator begin() const { return &m_elements[0]; }
+    const_iterator end() const { return &m_elements[size()]; }
+    /*!@}*/
+
+    /*!@{*/
+    //! Getters for reverse iterators for ranges
+    reverse_iterator       rbegin() { return reverse_iterator{ end() }; }
+    reverse_iterator       rend() { return reverse_iterator{ begin() }; }
+    const_reverse_iterator rbegin() const { return const_reverse_iterator{ end() }; }
+    const_reverse_iterator rend() const { return const_reverse_iterator{ begin() }; }
+    /*!@}*/
+
+    /*!@{*/
+    //! Pointers (unchecked)
+    using pointer       = DataType*;
+    using const_pointer = const DataType*;
+    /*!@}*/
+
+    //! Returns a const raw pointer to the contents of the array.
+    const_pointer data() const { return &m_elements[0]; }
+    //! Returns a raw pointer to the contents of the array.
+    pointer data() { return &m_elements[0]; }
+};
+
+/*! \brief Returns an object that provides iterators over the keys
+ * associated with \c EnumerationArrayType.
+ *
+ * This helper function is useful in contexts where there is an object
+ * of an EnumerationArray, and we want to use a range-based for loop
+ * over the keys associated with it, and it would be inconvenient to
+ * use the very word EnumerationArray<...> type, nor introduce a using
+ * statement for this purpose. It is legal in C++ to call a static
+ * member function (such as keys()) via an object rather than the
+ * type, but clang-tidy warns about that. So instead we make available
+ * a free function that calls that static method. */
+template<typename EnumerationArrayType>
+typename EnumerationArrayType::EnumerationWrapperType keysOf(const EnumerationArrayType& /* arrayObject */)
+{
+    return EnumerationArrayType::keys();
+}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/errorcodes.h b/src/include/gromacs/utility/errorcodes.h
new file mode 100644 (file)
index 0000000..f1db79b
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares error codes and related functions for fatal error handling.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_ERRORCODES_H
+#define GMX_UTILITY_ERRORCODES_H
+
+namespace gmx
+{
+
+/*! \addtogroup module_utility
+ * \{
+ */
+
+/*! \brief
+ * Possible error return codes from Gromacs functions.
+ */
+enum ErrorCode
+{
+    //! Zero for successful return.
+    eeOK,
+
+    //! Not enough memory to complete operation.
+    eeOutOfMemory,
+    //! Provided file could not be opened.
+    eeFileNotFound,
+    //! System I/O error.
+    eeFileIO,
+    //! Invalid user input (could not be understood).
+    eeInvalidInput,
+    //! Invalid user input (conflicting or unsupported settings).
+    eeInconsistentInput,
+    //! Requested tolerance cannot be achieved.
+    eeTolerance,
+    //! Simulation instability detected.
+    eeInstability,
+
+    /*! \name Error codes for buggy code
+     *
+     * Error codes below are for internal error checking; if triggered, they
+     * should indicate a bug in the code.
+     * \{
+     */
+    //! Requested feature not yet implemented.
+    eeNotImplemented,
+    //! Input value violates API specification.
+    eeInvalidValue,
+    //! Invalid routine called or wrong calling sequence detected.
+    eeInvalidCall,
+    //! Internal consistency check failed.
+    eeInternalError,
+    //! API specification was violated.
+    eeAPIError,
+    //! Range consistency check failed.
+    eeRange,
+
+    //! Parallel consistency check failed.
+    eeParallelConsistency,
+
+    //! Error specific for modular simulator.
+    eeModularSimulator,
+    //!\}
+
+    //! Unknown error detected.
+    eeUnknownError,
+};
+
+/*! \brief
+ * Returns a short string description of an error code.
+ *
+ * \param[in] errorcode Error code to retrieve the string for.
+ * \returns   A constant string corresponding to \p errorcode.
+ *
+ * If \p errorcode is not one of those defined for ::gmx::ErrorCode,
+ * the string corresponding to ::eeUnknownError is returned.
+ *
+ * This function does not throw.
+ */
+const char* getErrorCodeString(int errorcode);
+
+/*!\}*/
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/errorformat.h b/src/include/gromacs/utility/errorformat.h
new file mode 100644 (file)
index 0000000..54a14e7
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Declares an internal helper function for formatting standard error messages.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_ERRORFORMAT_H
+#define GMX_UTILITY_ERRORFORMAT_H
+
+#include <cstdio>
+
+namespace gmx
+{
+
+/*! \cond internal */
+namespace internal
+{
+
+/*! \brief
+ * Formats a common header for fatal error messages.
+ *
+ * Does not throw.
+ *
+ * \ingroup module_utility
+ */
+void printFatalErrorHeader(FILE* fp, const char* title, const char* func, const char* file, int line);
+/*! \brief
+ * Formats a line of fatal error message text.
+ *
+ * Does not throw.
+ *
+ * \ingroup module_utility
+ */
+void printFatalErrorMessageLine(FILE* fp, const char* text, int indent);
+/*! \brief
+ * Formats a common footer for fatal error messages.
+ *
+ * Does not throw.
+ *
+ * \ingroup module_utility
+ */
+void printFatalErrorFooter(FILE* fp);
+
+} // namespace internal
+//! \endcond
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/fatalerror.h b/src/include/gromacs/utility/fatalerror.h
new file mode 100644 (file)
index 0000000..a43f9d0
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2014,2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares fatal error handling and debugging routines for C code.
+ *
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_FATALERROR_H
+#define GMX_UTILITY_FATALERROR_H
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/stringutil.h"
+
+/*! \brief
+ * Debug log file.
+ *
+ * Functions can write to this file for debug info.
+ * Before writing to it, it should be checked whether the file is not NULL:
+ * \code
+   if (debug)
+   {
+       fprintf(debug, "%s", "Debug text");
+   }
+   \endcode
+ */
+extern FILE* debug; //NOLINT(cppcoreguidelines-avoid-non-const-global-variables,-warnings-as-errors)
+/** Whether extra debugging is enabled. */
+extern gmx_bool gmx_debug_at; //NOLINT(cppcoreguidelines-avoid-non-const-global-variables,-warnings-as-errors)
+
+/*! \brief
+ * Initializes debugging variables.
+ *
+ * This function is not threadsafe.  It should be called as part of
+ * initializing \Gromacs, before any other thread accesses the library.
+ * For command line programs, gmx::CommandLineModuleManager takes care
+ * of this if the user requests debugging.
+ */
+void gmx_init_debug(int dbglevel, const char* dbgfile);
+
+/** Returns TRUE when the program was started in debug mode */
+gmx_bool bDebugMode();
+
+/** Sets the log file for printing error messages. */
+void gmx_fatal_set_log_file(FILE* fp);
+
+/** Function pointer type for fatal error handler callback. */
+typedef void (*gmx_error_handler_t)(const char* title, const std::string& msg, const char* file, int line);
+
+/*! \brief
+ * Sets an error handler for gmx_fatal() and other fatal error routines.
+ *
+ * The default handler prints the message.
+ * \Gromacs will terminate the program after the error handler returns.
+ * To make gmx_fatal_collective() work, the error handler should not terminate
+ * the program, as it cannot know what is the desired way of termination.
+ * The message passed to the handler may be a multi-line string.
+ *
+ * \see gmx_fatal()
+ */
+void gmx_set_error_handler(gmx_error_handler_t func);
+
+/** Identifies the state of the program on a fatal error. */
+enum ExitType
+{
+    /*! \brief
+     * Clean exit is possible.
+     *
+     * There should be no concurrently executing code that might be accessing
+     * global objects, and all MPI ranks should reach the same fatal error.
+     */
+    ExitType_CleanExit,
+    /*! \brief
+     * Program needs to be aborted.
+     *
+     * There are no preconditions for this state.
+     */
+    ExitType_Abort,
+    /*! \brief
+     * Program needs to be aborted, but some other rank is responsible of it.
+     *
+     * There should be some other MPI rank that reaches the same fatal error,
+     * but uses ExitType_Abort.  The other ranks can then use
+     * ExitType_NonMasterAbort to wait for that one rank to issue the abort.
+     */
+    ExitType_NonMasterAbort
+};
+
+/*! \brief
+ * Helper function to terminate the program on a fatal error.
+ *
+ * \param[in] exitType  Identifies the state of the program at the time of the
+ *    call, determining how the program can be terminated.
+ * \param[in] returnValue  Exit code for the program, for cases where it can be
+ *    used.
+ */
+[[noreturn]] void gmx_exit_on_fatal_error(enum ExitType exitType, int returnValue);
+
+/*! \brief
+ * Low-level fatal error reporting routine for collective MPI errors.
+ *
+ * This function works as gmx_fatal(), but provides additional control for
+ * cases where it is known that the same error occurs on multiple MPI ranks.
+ * The error handler is called only if \p bMaster is `TRUE`, and MPI_Finalize()
+ * is called instead of MPI_Abort() in MPI-enabled \Gromacs if \p bFinalize is
+ * `TRUE`.
+ *
+ * This is used to implement gmx_fatal_collective() (which cannot be declared
+ * here, since it would bring with it mdrun-specific dependencies).
+ *
+ * This function is deprecated and no new calls should be made to it.
+ */
+[[noreturn]] void gmx_fatal_mpi_va(int         fatal_errno,
+                                   const char* file,
+                                   int         line,
+                                   gmx_bool    bMaster,
+                                   gmx_bool    bFinalize,
+                                   const char* fmt,
+                                   va_list     ap);
+
+/*! \brief
+ * Fatal error reporting routine for \Gromacs.
+ *
+ * This function prints a fatal error message with a header that contains the
+ * source file and line number of the call, followed by the string specified by
+ * \p fmt and supplied parameters.
+ * If \p fatal_errno is 0, only the message and arguments are printed.
+ * If \p fatal_errno is a legal system errno or -1, a perror()-like message is
+ * printed after the first message; if fatal_errno is -1, the last system errno
+ * will be used.
+ * The format of \p fmt uses printf()-like formatting.
+ *
+ * In case all MPI processes want to stop with the same fatal error,
+ * use gmx_fatal_collective(), declared in network.h,
+ * to avoid having as many error messages as processes.
+ *
+ * This function is deprecated and no new calls should be made to it.
+ *
+ * The first three parameters can be provided through ::FARGS:
+ * \code
+   gmx_fatal(FARGS, fmt, ...);
+   \endcode
+ */
+[[noreturn]] void gmx_fatal(int fatal_errno, const char* file, int line, gmx_fmtstr const char* fmt, ...)
+        gmx_format(printf, 4, 5);
+/** Helper macro to pass first three parameters to gmx_fatal(). */
+#define FARGS 0, __FILE__, __LINE__
+
+/*! \brief Implementation for gmx_error().
+ *
+ * This function is deprecated and no new calls should be made to it. */
+[[noreturn]] void gmx_error_function(const char* key, const std::string& msg, const char* file, int line);
+/*! \brief
+ * Alternative fatal error routine with canned messages.
+ *
+ * This works as gmx_fatal(), except that a generic error message is added
+ * based on a string key, and printf-style formatting is not supported.
+ * Should not typically be called directly, but through gmx_call() etc.
+ *
+ * This macro is deprecated and no new calls should be made to it.
+ */
+#define gmx_error(key, msg) gmx_error_function(key, msg, __FILE__, __LINE__)
+
+/*! \name Fatal error routines for certain types of errors
+ *
+ * These wrap gmx_error() and provide the \p key parameter as one of the
+ * recognized strings.
+ *
+ * These macros are deprecated and no new calls should be made to them.
+ */
+/*! \{ */
+#define gmx_call(msg) gmx_error("call", msg)
+#define gmx_comm(msg) gmx_error("comm", msg)
+#define gmx_file(msg) gmx_error("file", msg)
+#define gmx_impl(msg) gmx_error("impl", msg)
+#define gmx_incons(msg) gmx_error("incons", msg)
+#define gmx_input(msg) gmx_error("input", msg)
+#define gmx_mem(msg) gmx_error("mem", msg)
+#define gmx_open(fn) gmx_error("open", fn)
+/*! \} */
+
+/*! \brief
+ * Implementation for range_check() and range_check_mesg().
+ *
+ * \p warn_str can be NULL.
+ */
+void range_check_function(int n, int n_min, int n_max, const char* warn_str, const char* var, const char* file, int line);
+
+/*! \brief
+ * Checks that a variable is within a range.
+ *
+ * If \p n is not in range [n_min, n_max), a fatal error is raised.
+ * \p n_min is inclusive, but \p n_max is not.
+ */
+#define range_check_mesg(n, n_min, n_max, str) \
+    range_check_function(n, n_min, n_max, str, #n, __FILE__, __LINE__)
+
+/*! \brief
+ * Checks that a variable is within a range.
+ *
+ * This works as range_check_mesg(), but with a default error message.
+ */
+#define range_check(n, n_min, n_max) \
+    range_check_function(n, n_min, n_max, NULL, #n, __FILE__, __LINE__)
+
+/*! \brief
+ * Prints a warning message to stderr.
+ *
+ * The format of \p fmt uses printf()-like formatting.
+ * The message string should NOT start with "WARNING"
+ * and should NOT end with a newline.
+ */
+void gmx_warning(gmx_fmtstr const char* fmt, ...) gmx_format(printf, 1, 2);
+
+#endif
diff --git a/src/include/gromacs/utility/fileptr.h b/src/include/gromacs/utility/fileptr.h
new file mode 100644 (file)
index 0000000..5a65ad0
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief Declares guard pointer for RAII-style handling of cstdio
+ * FILE pointers.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_FILEPTR_H
+#define GMX_UTILITY_FILEPTR_H
+
+#include <cstdio>
+
+#include "gromacs/utility/unique_cptr.h"
+
+namespace gmx
+{
+
+//! fclose wrapper to be used as unique_ptr deleter
+inline void fclose_wrapper(FILE* fp)
+{
+    fclose(fp); // NOLINT(cppcoreguidelines-owning-memory)
+}
+
+//! Simple guard pointer which calls fclose. See unique_cptr for details.
+using FilePtr = std::unique_ptr<FILE, functor_wrapper<FILE, fclose_wrapper>>;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/fileredirector.h b/src/include/gromacs/utility/fileredirector.h
new file mode 100644 (file)
index 0000000..3560011
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::IFileOutputRedirector.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_FILEREDIRECTOR_H
+#define GMX_UTILITY_FILEREDIRECTOR_H
+
+#include <string>
+
+#include "gromacs/utility/path.h"
+#include "gromacs/utility/textstream.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Allows overriding file existence checks from code that supports it.
+ *
+ * The calling code should take in this interface and use the methods in it
+ * all file system operations that need to support this redirection.
+ *
+ * This allows tests to override the file existence checks without actually
+ * using the file system.  See IFileOutputRedirector for notes on
+ * a typical usage pattern.
+ *
+ * With some further refactoring of the File class, this could also support
+ * redirecting input files from in-memory buffers as well, but for now the
+ * current capabilities are sufficient.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class IFileInputRedirector
+{
+public:
+    virtual ~IFileInputRedirector();
+
+    /*! \brief
+     * Checks whether the provided path exists (and is a file).
+     *
+     * The \p onNotFound can be used to influence the behavior on error
+     * conditions.  Functions to pass as this parameter are provided as
+     * members of gmx::File.
+     */
+    virtual bool fileExists(const char* filename, File::NotFoundHandler onNotFound) const = 0;
+
+    //! Convenience method to check file existence using an std::string path.
+    bool fileExists(const std::string& filename, File::NotFoundHandler onNotFound) const
+    {
+        return fileExists(filename.c_str(), onNotFound);
+    }
+};
+
+/*! \libinternal \brief
+ * Allows capturing `stdout` and file output from code that supports it.
+ *
+ * The calling code should take in this interface and use the stream objects
+ * it returns for all output that needs to support this redirection.
+ *
+ * Currently, the (nearly) only purpose for this interface is for unit tests to
+ * capture the file output without duplicating the knowledge of which files are
+ * actually produced.  The tests can also replace actual files with in-memory
+ * streams (e.g., a StringOutputStream), and test the output without actually
+ * accessing the file system and managing actual files.
+ *
+ * As the main user for non-default implementation of this interface is tests,
+ * code using this interface generally uses a pattern where the redirector is
+ * initialized to defaultFileOutputRedirector(), and a separate setter is
+ * provided for tests to change the default.  This allows code outside the
+ * tests (and outside the code actually calling the redirector) to be written
+ * as if this interface did not exist (i.e., they do not need to pass the
+ * default instance).
+ *
+ * Also, the interface only supports text files, but can be generalized if/when
+ * there is a need for binary streams (see also TextOutputStream).
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class IFileOutputRedirector
+{
+public:
+    virtual ~IFileOutputRedirector();
+
+    /*! \brief
+     * Returns a stream to use for `stdout` output.
+     */
+    virtual TextOutputStream& standardOutput() = 0;
+    /*! \brief
+     * Returns a stream to use for output to a file at a given path.
+     *
+     * \param[in] filename  Requested file name.
+     */
+    virtual TextOutputStreamPointer openTextOutputFile(const char* filename) = 0;
+
+    //! Convenience method to open a stream using an std::string path.
+    TextOutputStreamPointer openTextOutputFile(const std::string& filename)
+    {
+        return openTextOutputFile(filename.c_str());
+    }
+};
+
+//! \cond libapi
+/*! \brief
+ * Returns default implementation for IFileInputRedirector.
+ *
+ * The returned implementation does not redirect anything, but just uses the
+ * file system normally.
+ *
+ * Does not throw.
+ *
+ * \ingroup module_utility
+ */
+IFileInputRedirector& defaultFileInputRedirector();
+/*! \brief
+ * Returns default implementation for IFileOutputRedirector.
+ *
+ * The returned implementation does not redirect anything, but just opens the
+ * files at requested locations.
+ *
+ * Does not throw.
+ *
+ * \ingroup module_utility
+ */
+IFileOutputRedirector& defaultFileOutputRedirector();
+//! \endcond
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/filestream.h b/src/include/gromacs/utility/filestream.h
new file mode 100644 (file)
index 0000000..9e24a1c
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares implementations for textstream.h interfaces for file input/output.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_FILESTREAM_H
+#define GMX_UTILITY_FILESTREAM_H
+
+#include <cstdio>
+
+#include <memory>
+#include <string>
+
+#include "gromacs/utility/fileptr.h"
+#include "gromacs/utility/textstream.h"
+
+namespace gmx
+{
+
+namespace internal
+{
+class FileStreamImpl;
+}
+
+/*! \libinternal \brief
+ * Text input stream implementation for reading from `stdin`.
+ *
+ * Implementations for the TextInputStream methods throw FileIOError on any
+ * I/O error.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class StandardInputStream : public TextInputStream
+{
+public:
+    /*! \brief
+     * Returns whether `stdin` is an interactive terminal.
+     *
+     * Only works on Unix, otherwise always returns true.
+     *
+     * Does not throw.
+     */
+    static bool isInteractive();
+
+    // From TextInputStream
+    bool readLine(std::string* line) override;
+    void close() override {}
+};
+
+/*! \libinternal \brief
+ * Text input stream implementation for reading from a file.
+ *
+ * Implementations for the TextInputStream methods throw FileIOError on any
+ * I/O error.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextInputFile : public TextInputStream
+{
+public:
+    /*! \brief
+     * Opens a file and returns an RAII-style `FILE` handle.
+     *
+     * \param[in] filename  Path of the file to open.
+     * \throws    FileIOError on any I/O error.
+     *
+     * Instead of returning `NULL` on errors, throws an exception with
+     * additional details (including the file name and `errno`).
+     */
+    static FilePtr openRawHandle(const char* filename);
+    //! \copydoc openRawHandle(const char *)
+    static FilePtr openRawHandle(const std::string& filename);
+
+    /*! \brief
+     * Opens a text file as a stream.
+     *
+     * \param[in]  filename  Path to the file to open.
+     * \throws     std::bad_alloc if out of memory.
+     * \throws     FileIOError on any I/O error.
+     */
+    explicit TextInputFile(const std::string& filename);
+    /*! \brief
+     * Initializes file object from an existing file handle.
+     *
+     * \param[in]  fp     File handle to use.
+     * \throws     std::bad_alloc if out of memory.
+     *
+     * The caller is responsible of closing the file; close() does nothing
+     * for an object constructed this way.
+     */
+    explicit TextInputFile(FILE* fp);
+    ~TextInputFile() override;
+
+    /*! \brief
+     * Returns a raw handle to the input file.
+     *
+     * This is provided for interoperability with older C-like code.
+     */
+    FILE* handle();
+
+    // From TextInputStream
+    bool readLine(std::string* line) override;
+    void close() override;
+
+private:
+    std::unique_ptr<internal::FileStreamImpl> impl_;
+};
+
+/*! \libinternal \brief
+ * Text output stream implementation for writing to a file.
+ *
+ * Implementations for the TextOutputStream methods throw FileIOError on any
+ * I/O error.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextOutputFile : public TextOutputStream
+{
+public:
+    //! \copydoc TextInputFile::TextInputFile(const std::string &)
+    explicit TextOutputFile(const std::string& filename);
+    //! \copydoc TextInputFile::TextInputFile(FILE *)
+    explicit TextOutputFile(FILE* fp);
+    ~TextOutputFile() override;
+
+    // From TextOutputStream
+    void write(const char* text) override;
+    void close() override;
+
+    /*! \brief
+     * Returns a stream for accessing `stdout`.
+     *
+     * \throws    std::bad_alloc if out of memory (only on first call).
+     */
+    static TextOutputFile& standardOutput();
+    /*! \brief
+     * Returns a stream for accessing `stderr`.
+     *
+     * \throws    std::bad_alloc if out of memory (only on first call).
+     */
+    static TextOutputFile& standardError();
+
+private:
+    std::unique_ptr<internal::FileStreamImpl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/fixedcapacityvector.h b/src/include/gromacs/utility/fixedcapacityvector.h
new file mode 100644 (file)
index 0000000..36ea649
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::FixedCapacityVector
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_FIXEDCAPACITYVECTOR_H
+#define GMX_UTILITY_FIXEDCAPACITYVECTOR_H
+
+#include <array>
+#include <stdexcept>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+/*! \brief Vector that behaves likes std::vector but has fixed capacity.
+ *
+ * \tparam T         Value type of elements.
+ * \tparam capacity  The maximum number of elements that can be stored.
+ *
+ * This class provides a variable size container, but with constant
+ * memory usage and can be allocated on the stack and avoid the overhead
+ * of dynamic allocation. This is especially useful for small vectors
+ * which are set up frequently.
+ *
+ * The class supports all methods from \p std::array, but behaves more
+ * like \p std::vector since it has variable size. In addition to the methods
+ * from std::array, from \p std::vector the methods \p push_back(), \p pop_back(),
+ * emplace_back() and \p clear() are supported. In particular, methods that
+ * requires reordering, such as \p insert() and \p emplace() are not
+ * supported to keep the code simple.
+ *
+ * The size is 0 at construction and elements can only be added with
+ * \p push_back() and \p emplace_back().
+ *
+ * \note This class is very similar to the fixed_capacity_vector class
+ * proposed for the C++ standard in document P0843r see:
+ * http://open-std.org/JTC1/SC22/WG21/docs/papers/2018/p0843r1.html
+ *
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+template<typename T, size_t capacity>
+class FixedCapacityVector
+{
+public:
+    //! Type of values stored in the vector
+    using value_type = T;
+    //! Type for representing size of the vector
+    using size_type = size_t;
+    //! Type for representing difference between two indices
+    using difference_type = ptrdiff_t;
+    //! Const reference to an element
+    using const_reference = const T&;
+    //! Const pointer to an element
+    using const_pointer = const T*;
+    //! Const iterator type to an element
+    using const_iterator = const T*;
+    //! Reference to an element
+    using reference = T&;
+    //! Pointer to an element
+    using pointer = T*;
+    //! Iterator type to an element
+    using iterator = T*;
+    //! Standard reverse iterator
+    using reverse_iterator = std::reverse_iterator<iterator>;
+    //! Standard reverse iterator
+    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+    //! Returns a const iterator to the beginning
+    const_iterator begin() const noexcept { return data(); }
+    //! Returns an iterator to the beginning
+    iterator begin() noexcept { return data(); }
+    //! Returns a const iterator to the end
+    const_iterator end() const noexcept { return end_; }
+    //! Returns an iterator to the end
+    iterator end() noexcept { return end_; }
+    //! Returns a const iterator to the reverse beginning
+    const_reverse_iterator rbegin() const noexcept { return reverse_iterator(end_); }
+    //! Returns an iterator to the reverse beginning
+    reverse_iterator rbegin() noexcept { return reverse_iterator(end_); }
+    //! Returns a const iterator to the reverse end
+    const_reverse_iterator rend() const noexcept { return reverse_iterator(begin()); }
+    //! Returns an iterator to the reverse end
+    reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
+
+    /*! \brief Returns the size
+     *
+     * \note Use ssize for any expression involving arithmetic operations
+         (including loop indices).
+     */
+    size_type size() const noexcept { return end_ - data(); }
+    //! Returns the signed size
+    index ssize() const noexcept { return end_ - data(); }
+    //! Returns whether the vector is empty
+    bool empty() const noexcept { return data() == end_; }
+
+    //! Const access an element
+    const_reference operator[](size_type n) const noexcept
+    {
+        GMX_ASSERT(n < size(), "Index should be in range");
+        return data_[n];
+    }
+    //! Access an element
+    reference operator[](size_type n) noexcept
+    {
+        GMX_ASSERT(n < size(), "Index should be in range");
+        return data_[n];
+    }
+    //! Const access an element, throws an out_of_range exception when out of range
+    const_reference at(size_type n) const
+    {
+        if (n >= size())
+        {
+            throw std::out_of_range("Vector index out of range");
+        }
+        return data_[n];
+    }
+    //! Access an element, throws an out_of_range exception when out of range
+    reference at(size_type n)
+    {
+        if (n >= size())
+        {
+            throw std::out_of_range("Vector index out of range");
+        }
+        return data_[n];
+    }
+    //! Returns the first element
+    reference front() const noexcept { return data_.front(); }
+    //! Returns the last element
+    reference back() const noexcept { return *(end_ - 1); }
+
+    //! Returns a raw pointer to the contents of the array
+    const T* data() const noexcept { return data_.data(); }
+
+    //! Returns a raw pointer to the contents of the array
+    T* data() noexcept { return data_.data(); }
+
+    //! Adds element at the end
+    void push_back(const T& value) noexcept
+    {
+        GMX_ASSERT(size() < capacity, "Cannot add more elements than the capacity");
+        *end_ = value;
+        end_++;
+    }
+
+    //! Deletes last element
+    void pop_back() noexcept
+    {
+        GMX_ASSERT(!empty(), "Can only delete last element when present");
+        end_--;
+    }
+
+    //! Constructs an element at the end
+    template<class... Args>
+    reference emplace_back(Args&&... args)
+    {
+        GMX_ASSERT(size() < capacity, "Cannot add more elements than the capacity");
+        if (std::is_move_assignable<T>::value)
+        {
+            *end_ = std::move(T(args...));
+        }
+        else
+        {
+            *end_ = T(args...);
+        }
+        end_++;
+
+        return back();
+    }
+
+    //! Clears content
+    void clear() noexcept { end_ = data(); }
+
+private:
+    //! The elements, stored in a fixed size array
+    std::array<T, capacity> data_;
+    //! The size of the vector
+    pointer end_ = data();
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/flags.h b/src/include/gromacs/utility/flags.h
new file mode 100644 (file)
index 0000000..4be79c7
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2010,2011,2012,2013,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::FlagsTemplate.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_FLAGS_H
+#define GMX_UTILITY_FLAGS_H
+
+namespace gmx
+{
+
+/*! \brief
+ * Template class for typesafe handling of combination of flags.
+ *
+ * \tparam FlagType An enumerated type that holds the possible single flags. The enum
+ * must be of unsigned underlying type.
+ *
+ * This class is not used publicly, but is present in an installed header
+ * because it is used internally in public template classes.
+ *
+ * Currently, it is not completely transparent, since or'ing together two
+ * \p FlagType flags does not automatically create a FlagsTemplate object.
+ * Also, some operators and more complex operations (like testing for multiple
+ * flags at the same time) are missing, but can be added if the need arises.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+template<typename FlagType>
+class FlagsTemplate
+{
+public:
+    static_assert(std::is_enum_v<FlagType> && std::is_unsigned_v<std::underlying_type_t<FlagType>>,
+                  "Flags must be an unsigned enum type.");
+    //! Creates a flags object with no flags set.
+    FlagsTemplate() : flags_(0) {}
+    //! Creates a flags object from a single flag.
+    FlagsTemplate(FlagType flag) : flags_(flag) {}
+
+    /*! \brief
+     * Tests if the given flag is set.
+     *
+     * Note that if \p flag has more than a single bit set, then returns
+     * true if any of them is set.
+     */
+    bool test(FlagType flag) const { return (flags_ & flag) != 0; }
+    //! Clears all flags.
+    void clearAll() { flags_ = 0; }
+    //! Sets the given flag.
+    void set(FlagType flag) { flags_ |= flag; }
+    //! Clears the given flag.
+    void clear(FlagType flag) { flags_ &= ~flag; }
+    //! Sets or clears the given flag.
+    void set(FlagType flag, bool bSet)
+    {
+        if (bSet)
+        {
+            set(flag);
+        }
+        else
+        {
+            clear(flag);
+        }
+    }
+
+    //! Combines flags from two flags objects.
+    FlagsTemplate<FlagType> operator|(const FlagsTemplate<FlagType>& other) const
+    {
+        return FlagsTemplate<FlagType>(flags_ | other.flags_);
+    }
+    //! Combines flags from another flag object.
+    FlagsTemplate<FlagType>& operator|=(const FlagsTemplate<FlagType>& other)
+    {
+        flags_ |= other.flags_;
+        return *this;
+    }
+    //! Combined flags from two flags objects.
+    FlagsTemplate<FlagType> operator&(const FlagsTemplate<FlagType>& other) const
+    {
+        return FlagsTemplate<FlagType>(flags_ & other.flags_);
+    }
+    //! Returns an object with all flags flipped.
+    FlagsTemplate<FlagType> operator~() const { return FlagsTemplate<FlagType>(~flags_); }
+
+private:
+    //! Creates a flags object with the given flags.
+    explicit FlagsTemplate(unsigned long flags) : flags_(flags) {}
+
+    uint64_t flags_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/futil.h b/src/include/gromacs/utility/futil.h
new file mode 100644 (file)
index 0000000..ded880b
--- /dev/null
@@ -0,0 +1,265 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Low-level wrappers for OS-specific file handling with some \Gromacs
+ * customizations.
+ *
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_FUTIL_H
+#define GMX_UTILITY_FUTIL_H
+
+#include <climits>
+#include <cstdio>
+
+#include <string>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/fileptr.h"
+
+/*! \def GMX_PATH_MAX
+ * \brief
+ * Maximum path length, if the OS provides one, otherwise a fixed constant.
+ */
+#ifdef PATH_MAX
+#    define GMX_PATH_MAX PATH_MAX
+#elif defined MAX_PATH
+#    define GMX_PATH_MAX MAX_PATH
+#else
+#    define GMX_PATH_MAX 4096
+#endif
+
+/** \Gromacs definition to use instead of `off_t`. */
+typedef int64_t gmx_off_t;
+
+/*! \brief
+ * Turn off buffering for output files (which is default) for debugging
+ * purposes.
+ *
+ * This only has effect on files opened with gmx_ffopen().
+ */
+void gmx_disable_file_buffering();
+
+/*! \brief
+ * Enables backups with the specified number of maximum backups.
+ *
+ * If \p count == 0, disables backups.  If not called, this is the default.
+ * If \p count == -1, reads the count from an environment variable.
+ *
+ * This is not currently thread-safe, as it is only called during
+ * initialization code.
+ */
+void gmx_set_max_backup_count(int count);
+
+/*! \brief
+ * Check whether a path exists.
+ *
+ * \returns `TRUE` when \p fname exists.
+ *
+ * Note that this returns `TRUE` even if \p fname is a directory instead of a
+ * file.
+ */
+gmx_bool gmx_fexist(const std::string& fname);
+
+/*! \brief
+ * Makes a backup of file if the file exists.
+ */
+void make_backup(const std::string& file);
+
+/*! \brief
+ * Opens a file, with \Gromacs-specific additions.
+ *
+ * If the file is in compressed format, opens a pipe which uncompresses the
+ * file on the fly.  For this to work, gmx_ffclose() and frewind() should
+ * always be used for files opened with gmx_ffopen() instead of fclose() and
+ * rewind().  For compressed files, the \p file parameter should be passed
+ * without the compressed extension; if that file is not found, then a few
+ * compression extensions are tried.
+ * Creates a backup if a file opened for writing already exists before
+ * overwriting it.
+ * A fatal error results if the file cannot be opened, for whatever reason.
+ */
+FILE* gmx_ffopen(const std::string& file, const char* mode);
+
+/** Closes a file opened with gmx_ffopen(). */
+int gmx_ffclose(FILE* fp);
+
+/*! \brief
+ * Wraps rewind() for files opened with gmx_ffopen().
+ *
+ * A fatal error results if this function is called for a pipe (a compressed
+ * input file).
+ */
+void frewind(FILE* fp);
+
+/** OS-independent 64-bit fseek().
+ *
+ * \return 0 when successful, or -1 (and set errno) in case of error.
+ */
+int gmx_fseek(FILE* stream, gmx_off_t offset, int whence);
+
+/** OS-independent 64-bit ftell().
+ *
+ * \return The current offset when successful, or -1 (and set errno) in case of error.
+ */
+gmx_off_t gmx_ftell(FILE* stream);
+
+/** OS-independent truncate(). */
+int gmx_truncate(const std::string& filename, gmx_off_t length);
+
+namespace gmx
+{
+
+/*! \brief
+ * Finds full path for a library file.
+ *
+ * Searches in the configured library directories for \c filename. If
+ * \c bAddCWD is true, searches first in the current directory. Fatal
+ * error results if the file is not found in any location and \c
+ * bFatal is true.
+ */
+std::string findLibraryFile(const std::string& filename, bool bAddCWD = true, bool bFatal = true);
+//! \copydoc findLibraryFile(const std::string &, bool, bool)
+std::string findLibraryFile(const char* filename, bool bAddCWD = true, bool bFatal = true);
+
+/*! \brief
+ * Opens a library file for reading in an RAII-style `FILE` handle.
+ *
+ * Works as findLibraryFile(), except that it opens the file and
+ * returns a file handle.
+ */
+FilePtr openLibraryFile(const std::string& filename, bool bAddCWD = true, bool bFatal = true);
+//! \copydoc openLibraryFile(const std::string &, bool, bool)
+FilePtr openLibraryFile(const char* filename, bool bAddCWD = true, bool bFatal = true);
+
+} // namespace gmx
+
+/*! \brief
+ * Creates unique name for temp file (wrapper around mkstemp) and opens it.
+ *
+ * \p buf should be at least 7 bytes long
+ */
+FILE* gmx_fopen_temporary(char* buf);
+
+/*! \brief
+ * Creates unique name for temp file (wrapper around mkstemp).
+ *
+ * \p buf should be at least 7 bytes long
+ */
+void gmx_tmpnam(char* buf);
+
+/*! \brief
+ * OS-independent rename().
+ *
+ * Renames/moves a file atomically, if the OS makes that available.
+ */
+int gmx_file_rename(const char* oldname, const char* newname);
+
+/*! \brief
+ * Copies a file (data only) oldname to newname.
+ *
+ * If \p copy_if_empty is `FALSE`, the file won't be copied if it's empty.
+ */
+int gmx_file_copy(const char* oldname, const char* newname, gmx_bool copy_if_empty);
+
+/*! \brief
+ * OS-independent fsync().
+ *
+ * Only use this during checkpointing!
+ */
+int gmx_fsync(FILE* fp);
+
+/*! \brief
+ * OS-independent chdir().
+ *
+ * Exits with a fatal error if changing the directory fails.
+ */
+void gmx_chdir(const char* directory);
+/*! \brief
+ * OS-independent getcwd().
+ *
+ * Exits with a fatal error if the call fails.
+ */
+void gmx_getcwd(char* buffer, size_t size);
+
+namespace gmx
+{
+
+class DataFileFinder;
+
+/*! \brief
+ * Gets a finder for locating data files from share/top/.
+ *
+ * \returns Finder set with setLibraryFileFinder(), or a default finder.
+ *
+ * If setLibraryFileFinder() has not been called (or a `NULL` finder has been
+ * set), a default finder is returned.
+ * The default finder searches data files from the directory identified by the
+ * global program context; it does not respect GMXLIB environment variable.
+ * Calling initForCommandLine() sets a finder that respects GMXLIB.
+ *
+ * Does not throw.
+ *
+ * See setLibraryFileFinder() for thread safety.
+ *
+ * \ingroup module_utility
+ */
+const DataFileFinder& getLibraryFileFinder();
+/*! \brief
+ * Sets a finder for location data files from share/top/.
+ *
+ * \param[in] finder  finder to set
+ *     (can be NULL to restore the default finder).
+ *
+ * The library does not take ownership of \p finder.
+ * The provided object must remain valid until the global instance is changed
+ * by another call to setLibraryFileFinder().
+ *
+ * The global instance is used by findLibraryFile() and openLibraryFile().
+ *
+ * This method is not thread-safe.  See setProgramContext(); the same
+ * constraints apply here as well.
+ *
+ * Does not throw.
+ */
+void setLibraryFileFinder(const DataFileFinder* finder);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/gmxmpi.h b/src/include/gromacs/utility/gmxmpi.h
new file mode 100644 (file)
index 0000000..5c2a247
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2016,2017,2018 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Wraps mpi.h usage in Gromacs.
+ *
+ * This header wraps the MPI header <mpi.h>, and should be included instead of
+ * that one.  It abstracts away the case that depending on compilation
+ * settings, MPI routines may be provided by <mpi.h> or by thread-MPI.
+ * In the absence of MPI, this header still declares some types for
+ * convenience.  It also disables MPI C++ bindings that can cause compilation
+ * issues.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_GMXMPI_H
+#define GMX_UTILITY_GMXMPI_H
+
+#include "config.h"
+
+/*! \cond */
+#if GMX_LIB_MPI
+/* MPI C++ binding is deprecated and can cause name conflicts (e.g. stdio/mpi seek) */
+#    define MPICH_SKIP_MPICXX 1
+#    define OMPI_SKIP_MPICXX 1
+/* disable bindings for SGI MPT also */
+#    define MPI_NO_CPPBIND 1
+#    include <mpi.h>
+/* Starting with 2.2 MPI_INT64_T is required. Earlier version still might have it.
+   In theory MPI_Datatype doesn't have to be a #define, but current available MPI
+   implementations (OpenMPI + MPICH (+derivates)) use #define and future versions
+   should support 2.2. */
+#    if (MPI_VERSION == 1 || (MPI_VERSION == 2 && MPI_SUBVERSION < 2)) && !defined MPI_INT64_T
+#        include <limits.h>
+#        if LONG_MAX == 9223372036854775807L
+#            define MPI_INT64_T MPI_LONG
+#        elif LONG_LONG_MAX == 9223372036854775807L
+#            define MPI_INT64_T MPI_LONG_LONG
+#        else
+#            error No MPI_INT64_T and no 64 bit integer found.
+#        endif
+#    endif /*MPI_INT64_T*/
+#else
+#    if GMX_THREAD_MPI
+#        include "thread_mpi/mpi_bindings.h" /* IWYU pragma: export */
+#        include "thread_mpi/tmpi.h"         /* IWYU pragma: export */
+#    else
+typedef void* MPI_Comm;
+typedef void* MPI_Request;
+typedef void* MPI_Status;
+typedef void* MPI_Group;
+#        define MPI_COMM_NULL nullptr
+#        define MPI_GROUP_NULL nullptr
+#        define MPI_COMM_WORLD nullptr
+#    endif
+#endif
+//! \endcond
+
+#endif
diff --git a/src/include/gromacs/utility/gmxomp.h b/src/include/gromacs/utility/gmxomp.h
new file mode 100644 (file)
index 0000000..b019e3d
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares OpenMP wrappers to avoid conditional compilation.
+ *
+ * This module defines wrappers for OpenMP API functions and enables compiling
+ * code without conditional compilation even when OpenMP is turned off in the
+ * build system.
+ * Therefore, OpenMP API functions should always be used through these wrappers
+ * and omp.h should never be directly included.  Instead, this header should be
+ * used whenever OpenMP API functions are needed.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_OMP_H
+#define GMX_UTILITY_OMP_H
+
+/*! \addtogroup module_utility
+ * \{
+ */
+
+/*! \brief
+ * Returns an integer equal to or greater than the number of threads
+ * that would be available if a parallel region without num_threads were
+ * defined at that point in the code.
+ *
+ * Acts as a wrapper for omp_get_max_threads().
+ */
+int gmx_omp_get_max_threads();
+
+/*! \brief
+ * Returns the number of processors available when the function is called.
+ *
+ * Acts as a wrapper around omp_get_num_procs().
+ */
+int gmx_omp_get_num_procs();
+
+/*! \brief
+ * Returns the thread number of the thread executing within its thread team.
+ *
+ * Acts as a wrapper for omp_get_thread_num().
+ */
+int gmx_omp_get_thread_num();
+
+/*! \brief
+ * Sets the number of threads in subsequent parallel regions, unless overridden
+ * by a num_threads clause.
+ *
+ * Acts as a wrapper for omp_set_num_threads().
+ */
+void gmx_omp_set_num_threads(int num_threads);
+
+/*! \brief
+ * Check for externally set thread affinity to avoid conflicts with \Gromacs
+ * internal setting.
+ *
+ * \param[out] message  Receives the message to be shown to the user.
+ * \returns `true` if we can set thread affinity ourselves.
+ *
+ * The KMP_AFFINITY environment variable is used by Intel, GOMP_CPU_AFFINITY
+ * by the GNU compilers (Intel also honors it well).  If any of the variables
+ * is set, we should honor it and disable the internal pinning.
+ *
+ * If this function returns `false`, the caller is responsible to disable the
+ * pinning, show the message from \p *message to the user, and free the memory
+ * allocated for \p *message.
+ * If the return value is `true`, \p *message is NULL.
+ */
+bool gmx_omp_check_thread_affinity(char** message);
+
+/*! \} */
+
+#endif
diff --git a/src/include/gromacs/utility/ikeyvaluetreeerror.h b/src/include/gromacs/utility/ikeyvaluetreeerror.h
new file mode 100644 (file)
index 0000000..a697f15
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares an error handling interface for key-value tree operations.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_KEYVALUETREEERROR_H
+#define GMX_UTILITY_KEYVALUETREEERROR_H
+
+namespace gmx
+{
+
+class KeyValueTreePath;
+class UserInputError;
+
+class IKeyValueTreeErrorHandler
+{
+public:
+    virtual bool onError(UserInputError* ex, const KeyValueTreePath& context) = 0;
+
+protected:
+    virtual ~IKeyValueTreeErrorHandler();
+};
+
+//! \cond libapi
+/*! \brief
+ * Returns a default IKeyValueTreeErrorHandler that throws on first exception.
+ *
+ * \ingroup module_utility
+ */
+IKeyValueTreeErrorHandler* defaultKeyValueTreeErrorHandler();
+//! \endcond
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/init.h b/src/include/gromacs/utility/init.h
new file mode 100644 (file)
index 0000000..84b0c8d
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares functions for initializing the \Gromacs library.
+ *
+ * Currently, only MPI initialization/finalization management is
+ * required, and only if external MPI support is enabled.
+ *
+ * If MPI is already initialized, we should not call MPI_Init() or
+ * MPI_Finalize(). This management object permits \Gromacs test code to
+ * nest calls to functions that might normally implement a stand-alone
+ * MPI-using tool. It also permits \Gromacs code to be called from code
+ * that has already initialized MPI and needs that environment to work
+ * and persist after \Gromacs code returns (e.g. \Gromacs tests,
+ * external libraries that call \Gromacs code).
+ *
+ * It does so by maintaining a counter of the number of MPI
+ * initializations, and only calling MPI_Init() or MPI_Finalize when
+ * it is safe (ie. when the counter is at zero).
+ *
+ * Thread-MPI initialization and finalization for mdrun is all managed
+ * in runner.c.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_INIT_H
+#define GMX_UTILITY_INIT_H
+
+namespace gmx
+{
+
+/*! \brief
+ * Initializes the \Gromacs library.
+ *
+ * \param[in] argc  argc value passed to main().
+ * \param[in] argv  argv array passed to main().
+ *
+ * \p argc and \p argv are the command line arguments passed to main().
+ * They are allowed to be NULL if \Gromacs is not compiled with MPI, MPI_Init()
+ * has already been called, or if the MPI library \Gromacs is compiled against
+ * allows it.
+ *
+ * Does not throw.
+ *
+ * \ingroup module_utility
+ */
+void init(int* argc, char*** argv);
+/*! \brief
+ * Deinitializes the \Gromacs library.
+ *
+ * Decrements the initialization counter, and calls MPI_Finalize()
+ * if \Gromacs is compiled with MPI support and the counter has
+ * reached zero.  In that case, it is not possible to reinitialize
+ * \Gromacs after calling this function.  Instead, call gmx::init() at
+ * a higher level, and note that calls to init can be nested safely.
+ *
+ * \ingroup module_utility
+ */
+void finalize();
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/inmemoryserializer.h b/src/include/gromacs/utility/inmemoryserializer.h
new file mode 100644 (file)
index 0000000..c289c24
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::ISerializer implementation for in-memory serialization.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_INMEMORYSERIALIZER_H
+#define GMX_UTILITY_INMEMORYSERIALIZER_H
+
+#include <cstddef>
+
+#include <memory>
+#include <vector>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/iserializer.h"
+
+namespace gmx
+{
+
+//! Specify endian swapping behavoir.
+//
+// The host-dependent choices avoid the calling file having to
+// depend on config.h.
+//
+enum class EndianSwapBehavior : int
+{
+    DoNotSwap,                //!< Don't touch anything
+    Swap,                     //!< User-enforced swapping
+    SwapIfHostIsBigEndian,    //!< Only swap if machine we execute on is big-endian
+    SwapIfHostIsLittleEndian, //!< Only swap if machine we execute on is little-endian
+    Count                     //!< Number of possible behaviors
+};
+
+class InMemorySerializer : public ISerializer
+{
+public:
+    explicit InMemorySerializer(EndianSwapBehavior endianSwapBehavior = EndianSwapBehavior::DoNotSwap);
+    ~InMemorySerializer() override;
+
+    std::vector<char> finishAndGetBuffer();
+
+    // From ISerializer
+    bool reading() const override { return false; }
+    void doBool(bool* value) override;
+    void doUChar(unsigned char* value) override;
+    void doChar(char* value) override;
+    void doUShort(unsigned short* value) override;
+    void doInt(int* value) override;
+    void doInt32(int32_t* value) override;
+    void doInt64(int64_t* value) override;
+    void doFloat(float* value) override;
+    void doDouble(double* value) override;
+    void doReal(real* value) override;
+    void doIvec(ivec* value) override;
+    void doRvec(rvec* value) override;
+    void doString(std::string* value) override;
+    void doOpaque(char* data, std::size_t size) override;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+class InMemoryDeserializer : public ISerializer
+{
+public:
+    InMemoryDeserializer(ArrayRef<const char> buffer,
+                         bool                 sourceIsDouble,
+                         EndianSwapBehavior   endianSwapBehavior = EndianSwapBehavior::DoNotSwap);
+    ~InMemoryDeserializer() override;
+
+    //! Get if the source data was written in double precsion
+    bool sourceIsDouble() const;
+
+    // From ISerializer
+    bool reading() const override { return true; }
+    void doBool(bool* value) override;
+    void doUChar(unsigned char* value) override;
+    void doChar(char* value) override;
+    void doUShort(unsigned short* value) override;
+    void doInt(int* value) override;
+    void doInt32(int32_t* value) override;
+    void doInt64(int64_t* value) override;
+    void doFloat(float* value) override;
+    void doDouble(double* value) override;
+    void doReal(real* value) override;
+    void doIvec(ivec* value) override;
+    void doRvec(rvec* value) override;
+    void doString(std::string* value) override;
+    void doOpaque(char* data, std::size_t size) override;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/int64_to_int.h b/src/include/gromacs/utility/int64_to_int.h
new file mode 100644 (file)
index 0000000..17b6e5b
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal\file
+ * \brief
+ * Low-level utility for converting 64 bit int to int (the
+ * size of which is hardware dependent), printing
+ * a warning if an overflow will occur.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_INT64_TO_INT_H
+#define GMX_UTILITY_INT64_TO_INT_H
+
+#include "gromacs/utility/futil.h"
+
+/*! \brief Convert a int64_t value to int.
+ *
+ * \param[in] step The step number (or other int64)
+ * \param[in] warn If warn!=NULL a warning message will be written
+ *                 to stderr when step does not fit in an int,
+ *                 the first line is:
+ *                 "WARNING during %s:", where warn is printed in %s.
+ * \return the truncated step number.
+ */
+int int64_to_int(int64_t step, const char* warn);
+
+#endif
diff --git a/src/include/gromacs/utility/iserializer.h b/src/include/gromacs/utility/iserializer.h
new file mode 100644 (file)
index 0000000..5400475
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares a generic serialization interface that supports both directions.
+ *
+ * \todo Generalize and transfer serialization functionality used in
+ *       mrc density file header serialization to here.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_ISERIALIZER_H
+#define GMX_UTILITY_ISERIALIZER_H
+
+#include <cstddef>
+
+#include <string>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+#include "gromacs/utility/smalloc.h"
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief Interface for types that convert standard data types into a
+ * form suitable for storage or transfer.
+ *
+ * Different implementations could suit MPI, file I/O, or in-memory
+ * conversion. */
+class ISerializer
+{
+public:
+    virtual ~ISerializer() {}
+    /*! \brief Returns whether the serializer is reading or
+     * writing, because details like memory management vary
+     * accordingly. */
+    virtual bool reading() const = 0;
+    //! \brief Serialize values of different types.
+    ///@{
+    virtual void doBool(bool* value)                    = 0;
+    virtual void doUChar(unsigned char* value)          = 0;
+    virtual void doChar(char* value)                    = 0;
+    virtual void doUShort(unsigned short* value)        = 0;
+    virtual void doInt(int* value)                      = 0;
+    virtual void doInt32(int32_t* value)                = 0;
+    virtual void doInt64(int64_t* value)                = 0;
+    virtual void doFloat(float* value)                  = 0;
+    virtual void doDouble(double* value)                = 0;
+    virtual void doReal(real* value)                    = 0;
+    virtual void doIvec(ivec* value)                    = 0;
+    virtual void doRvec(rvec* value)                    = 0;
+    virtual void doString(std::string* value)           = 0;
+    virtual void doOpaque(char* data, std::size_t size) = 0;
+    ///@}
+
+    //! \brief Serialize arrays of values of different types.
+    ///@{
+    void doBoolArray(bool* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doBool(&(values[i]));
+        }
+    }
+    // Char, UChar and RVec have vector specializations that can be
+    // used instead of the default looping.
+    virtual void doCharArray(char* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doChar(&(values[i]));
+        }
+    }
+    virtual void doUCharArray(unsigned char* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doUChar(&(values[i]));
+        }
+    }
+    void doUShortArray(unsigned short* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doUShort(&(values[i]));
+        }
+    }
+    void doIntArray(int* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doInt(&(values[i]));
+        }
+    }
+    void doInt32Array(int32_t* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doInt32(&(values[i]));
+        }
+    }
+    void doInt64Array(int64_t* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doInt64(&(values[i]));
+        }
+    }
+    void doFloatArray(float* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doFloat(&(values[i]));
+        }
+    }
+    void doDoubleArray(double* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doDouble(&(values[i]));
+        }
+    }
+    void doRealArray(real* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doReal(&(values[i]));
+        }
+    }
+    void doIvecArray(ivec* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doIvec(&(values[i]));
+        }
+    }
+    virtual void doRvecArray(rvec* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doRvec(&(values[i]));
+        }
+    }
+    ///@}
+
+    //! Serialize enum value with underlying type int
+    template<typename EnumType>
+    void doEnumAsInt(EnumType* enumValue)
+    {
+        static_assert(std::is_same<std::underlying_type_t<EnumType>, int>::value,
+                      "Only enums with underlying type int are supported.");
+        auto castedValue = static_cast<int>(*enumValue);
+        doInt(&castedValue);
+        *enumValue = static_cast<EnumType>(castedValue);
+    }
+
+    //! Serialize array of enum values with underlying type.
+    template<typename EnumType>
+    void doEnumArrayAsInt(EnumType* values, int elements)
+    {
+        for (int i = 0; i < elements; i++)
+        {
+            doEnumAsInt<EnumType>(&(values[i]));
+        }
+    }
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/keyvaluetree.h b/src/include/gromacs/utility/keyvaluetree.h
new file mode 100644 (file)
index 0000000..8d1c85f
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares a data structure for JSON-like structured key-value mapping.
+ *
+ * A tree is composed of nodes that can have different types:
+ *  - _Value_ (gmx::KeyValueTreeValue) is a generic node that can
+ *    represent either a scalar value of arbitrary type, or an object or
+ *    an array.
+ *  - _Array_ (gmx::KeyValueTreeArray) is a collection of any number of values
+ *    (including zero).  The values can be of any type and different types
+ *    can be mixed in the same array.
+ *  - _Object_ (gmx::KeyValueTreeObject) is a collection of properties.
+ *    Each property must have a unique key.  Order of properties is preserved,
+ *    i.e., they can be iterated in the order they were added.
+ *  - _Property_ (gmx::KeyValueTreeProperty) is an arbitrary type of value
+ *    associated with a string key.
+ * The root object of a tree is typically an object, but could also be an
+ * array.  The data structure itself does not enforce any other constraints,
+ * but the context in which it is used can limit the allowed scalar types or,
+ * e.g., require arrays to have values of uniform type.  Also, several
+ * operations defined for the structure (string output, comparison,
+ * serialization, etc.) only work on a limited set of scalar types, or have
+ * limitations with the types of trees they work on (in particular, arrays are
+ * currently poorly supported).
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_KEYVALUETREE_H
+#define GMX_UTILITY_KEYVALUETREE_H
+
+#include <algorithm>
+#include <functional>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gromacs/utility/any.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+
+class KeyValueTreeArray;
+class KeyValueTreeObject;
+class TextWriter;
+
+/*! \libinternal \brief
+ * Identifies an entry in a key-value tree.
+ *
+ * This class is mainly an internal utility within the key-value tree
+ * implementation, but it is exposed on the API level where string-based
+ * specification of a location in the tree is necessary.  Constructors are not
+ * explicit to allow passing a simple string in contexts where a
+ * KeyValueTreePath is expected.
+ *
+ * The string specifying a location should start with a `/`, followed by the
+ * names of the properties separated by `/`.  For example, `/a/b/c` specifies
+ * property `c` in an object that is the value of `b` in an object that is the
+ * value of `a` at the root of the tree.
+ * Currently, there is no support for specifying paths to values within arrays
+ * (since none of the places where this is used implement array handling,
+ * either).
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class KeyValueTreePath
+{
+public:
+    //! Creates an empty path (corresponds to the root object).
+    KeyValueTreePath() = default;
+    //! Creates a path from given string representation.
+    KeyValueTreePath(const char* path);
+    //! Creates a path from given string representation.
+    KeyValueTreePath(const std::string& path);
+
+    //! Adds another element to the path, making it a child of the old path.
+    void append(const std::string& key) { path_.push_back(key); }
+    //! Adds elements from another path to the path.
+    void append(const KeyValueTreePath& other)
+    {
+        auto elements = other.elements();
+        path_.insert(path_.end(), elements.begin(), elements.end());
+    }
+    //! Removes the last element in the path, making it the parent path.
+    void pop_back() { return path_.pop_back(); }
+    //! Removes and returns the last element in the path.
+    std::string pop_last()
+    {
+        std::string result = std::move(path_.back());
+        path_.pop_back();
+        return result;
+    }
+
+    //! Whether the path is empty (pointing to the root object).
+    bool empty() const { return path_.empty(); }
+    //! Returns the number of elements (=nesting level) in the path.
+    size_t size() const { return path_.size(); }
+    //! Returns the i'th path element.
+    const std::string& operator[](int i) const { return path_[i]; }
+    //! Returns all the path elements.
+    const std::vector<std::string>& elements() const { return path_; }
+
+    //! Formats the path as a string for display.
+    std::string toString() const;
+
+private:
+    std::vector<std::string> path_;
+};
+
+//! \cond libapi
+
+//! Combines two paths as with KeyValueTreePath::append().
+inline KeyValueTreePath operator+(const KeyValueTreePath& a, const KeyValueTreePath& b)
+{
+    KeyValueTreePath result(a);
+    result.append(b);
+    return result;
+}
+
+//! Combines an element to a path as with KeyValueTreePath::append().
+inline KeyValueTreePath operator+(const KeyValueTreePath& a, const std::string& b)
+{
+    KeyValueTreePath result(a);
+    result.append(b);
+    return result;
+}
+//! \endcond
+
+class KeyValueTreeValue
+{
+public:
+    //! Returns whether the value is an array (KeyValueTreeArray).
+    bool isArray() const;
+    //! Returns whether the value is an object (KeyValueTreeObject).
+    bool isObject() const;
+    //! Returns whether the value is of a given type.
+    template<typename T>
+    bool isType() const
+    {
+        return value_.isType<T>();
+    }
+    //! Returns the type of the value.
+    std::type_index type() const { return value_.type(); }
+
+    KeyValueTreeArray&        asArray();
+    KeyValueTreeObject&       asObject();
+    const KeyValueTreeArray&  asArray() const;
+    const KeyValueTreeObject& asObject() const;
+    template<typename T>
+    const T& cast() const
+    {
+        return value_.cast<T>();
+    }
+
+    //! Returns the raw Any value (always possible).
+    const Any& asAny() const { return value_; }
+
+private:
+    explicit KeyValueTreeValue(Any&& value) : value_(std::move(value)) {}
+
+    Any value_;
+
+    friend class KeyValueTreeBuilder;
+    friend class KeyValueTreeObjectBuilder;
+    friend class KeyValueTreeValueBuilder;
+};
+
+class KeyValueTreeArray
+{
+public:
+    //! Whether all elements of the array are objects.
+    bool isObjectArray() const
+    {
+        return std::all_of(values_.begin(), values_.end(), std::mem_fn(&KeyValueTreeValue::isObject));
+    }
+
+    //! Returns the values in the array.
+    const std::vector<KeyValueTreeValue>& values() const { return values_; }
+
+private:
+    std::vector<KeyValueTreeValue> values_;
+
+    friend class KeyValueTreeArrayBuilderBase;
+};
+
+class KeyValueTreeProperty
+{
+public:
+    const std::string&       key() const { return value_->first; }
+    const KeyValueTreeValue& value() const { return value_->second; }
+
+private:
+    typedef std::map<std::string, KeyValueTreeValue>::const_iterator IteratorType;
+
+    explicit KeyValueTreeProperty(IteratorType value) : value_(value) {}
+
+    IteratorType value_;
+
+    friend class KeyValueTreeObject;
+    friend class KeyValueTreeObjectBuilder;
+};
+
+class KeyValueTreeObject
+{
+public:
+    KeyValueTreeObject() = default;
+    //! Creates a deep copy of an object.
+    KeyValueTreeObject(const KeyValueTreeObject& other)
+    {
+        for (const auto& value : other.values_)
+        {
+            auto iter = valueMap_.insert(std::make_pair(value.key(), value.value())).first;
+            values_.push_back(KeyValueTreeProperty(iter));
+        }
+    }
+    //! Assigns a deep copy of an object.
+    KeyValueTreeObject& operator=(const KeyValueTreeObject& other)
+    {
+        KeyValueTreeObject tmp(other);
+        std::swap(tmp.valueMap_, valueMap_);
+        std::swap(tmp.values_, values_);
+        return *this;
+    }
+    //! Default move constructor.
+    //NOLINTNEXTLINE(performance-noexcept-move-constructor) bug #38733
+    KeyValueTreeObject(KeyValueTreeObject&&) = default;
+    //! Default move assignment.
+    KeyValueTreeObject& operator=(KeyValueTreeObject&&) = default;
+
+    /*! \brief
+     * Returns all properties in the object.
+     *
+     * The properties are in the order they were added to the object.
+     */
+    const std::vector<KeyValueTreeProperty>& properties() const { return values_; }
+
+    //! Whether a property with given key exists.
+    bool keyExists(const std::string& key) const { return valueMap_.find(key) != valueMap_.end(); }
+    //! Returns value for a given key.
+    const KeyValueTreeValue& operator[](const std::string& key) const
+    {
+        GMX_ASSERT(keyExists(key), "Accessing non-existent value");
+        return valueMap_.at(key);
+    }
+
+    /*! \brief
+     * Returns whether the given object shares any keys with `this`.
+     */
+    bool hasDistinctProperties(const KeyValueTreeObject& obj) const;
+
+private:
+    //! Keeps the properties by key.
+    std::map<std::string, KeyValueTreeValue> valueMap_;
+    //! Keeps the insertion order of properties.
+    std::vector<KeyValueTreeProperty> values_;
+
+    friend class KeyValueTreeObjectBuilder;
+};
+
+/********************************************************************
+ * Inline functions that could not be declared within the classes
+ */
+
+inline bool KeyValueTreeValue::isArray() const
+{
+    return value_.isType<KeyValueTreeArray>();
+}
+inline bool KeyValueTreeValue::isObject() const
+{
+    return value_.isType<KeyValueTreeObject>();
+}
+inline const KeyValueTreeArray& KeyValueTreeValue::asArray() const
+{
+    return value_.cast<KeyValueTreeArray>();
+}
+inline const KeyValueTreeObject& KeyValueTreeValue::asObject() const
+{
+    return value_.cast<KeyValueTreeObject>();
+}
+inline KeyValueTreeArray& KeyValueTreeValue::asArray()
+{
+    return value_.castRef<KeyValueTreeArray>();
+}
+inline KeyValueTreeObject& KeyValueTreeValue::asObject()
+{
+    return value_.castRef<KeyValueTreeObject>();
+}
+
+//! \cond libapi
+/*! \brief
+ * Writes a human-readable representation of the tree with given writer.
+ *
+ * The output format is designed to be readable by humans; if some
+ * particular machine-readable format is needed, that should be
+ * implemented outside the generic key-value tree code.
+ *
+ * \ingroup module_utility
+ */
+void dumpKeyValueTree(TextWriter* writer, const KeyValueTreeObject& tree);
+
+/*! \brief
+ * Compares two KeyValueTrees and prints any differences.
+ *
+ * \ingroup module_utility
+ */
+void compareKeyValueTrees(TextWriter*               writer,
+                          const KeyValueTreeObject& tree1,
+                          const KeyValueTreeObject& tree2,
+                          real                      ftol,
+                          real                      abstol);
+
+//! Helper function to format a simple KeyValueTreeValue.
+static inline std::string simpleValueToString(const KeyValueTreeValue& value)
+{
+    return simpleValueToString(value.asAny());
+}
+
+//! \endcond
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/keyvaluetreebuilder.h b/src/include/gromacs/utility/keyvaluetreebuilder.h
new file mode 100644 (file)
index 0000000..6085f39
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares classes for building the data structures in keyvaluetree.h.
+ *
+ * These are separate from the data structures to enforce clear separation of
+ * the APIs, and to make the data structure immutable after construction.
+ *
+ * For the main use case described in \ref page_mdmodules, they are mainly
+ * used internally, but currently gmx::KeyValueTreeObjectBuilder (and
+ * everything it references) is exposed for more complex transforms through
+ * gmx::IKeyValueTreeTransformRules.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_KEYVALUETREEBUILDER_H
+#define GMX_UTILITY_KEYVALUETREEBUILDER_H
+
+#include <initializer_list>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "gromacs/utility/any.h"
+#include "gromacs/utility/gmxassert.h"
+#include "gromacs/utility/keyvaluetree.h"
+
+namespace gmx
+{
+
+class KeyValueTreeArrayBuilder;
+class KeyValueTreeObjectBuilder;
+
+/*! \libinternal \brief
+ * Root builder for creating trees that have an object at the root.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class KeyValueTreeBuilder
+{
+public:
+    //! Returns a builder for the root object.
+    KeyValueTreeObjectBuilder rootObject();
+
+    /*! \brief
+     * Builds the final object.
+     *
+     * The builder should not be accessed after this call.
+     */
+    KeyValueTreeObject build() { return std::move(root_); }
+
+private:
+    /*! \brief
+     * Helper function for other builders to create values of certain type.
+     */
+    template<typename T>
+    static KeyValueTreeValue createValue(const T& value)
+    {
+        return KeyValueTreeValue(Any::create<T>(value));
+    }
+    /*! \brief
+     * Helper function for other builders to create default-constructed
+     * values.
+     */
+    template<typename T>
+    static KeyValueTreeValue createValue()
+    {
+        return KeyValueTreeValue(Any::create<T>(T()));
+    }
+
+    KeyValueTreeObject root_;
+
+    //! For access to createValue() methods.
+    friend class KeyValueTreeObjectArrayBuilder;
+    //! For access to createValue() methods.
+    friend class KeyValueTreeObjectBuilder;
+    //! For access to createValue() methods.
+    template<typename T>
+    friend class KeyValueTreeUniformArrayBuilder;
+};
+
+/*! \libinternal \brief
+ * Builder for KeyValueTreeValue objects.
+ *
+ * This builder can be constructed directly and can create self-standing
+ * KeyValueTreeValue objects.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class KeyValueTreeValueBuilder
+{
+public:
+    //! Assigns a scalar value of certain type.
+    template<typename T>
+    void setValue(const T& value)
+    {
+        value_ = Any::create<T>(value);
+    }
+    //! Assigns a Any value to the built value.
+    void setAnyValue(Any&& value) { value_ = std::move(value); }
+    /*! \brief
+     * Returns an object builder for building an object into this value.
+     *
+     * Any method call in this value builder invalidates the returned
+     * builder.
+     */
+    KeyValueTreeObjectBuilder createObject();
+    /*! \brief
+     * Returns an array builder for building an array into this value.
+     *
+     * Any method call in this value builder invalidates the returned
+     * builder.
+     */
+    KeyValueTreeArrayBuilder createArray();
+
+    /*! \brief
+     * Builds the final value.
+     *
+     * The builder should not be accessed after this call.
+     */
+    KeyValueTreeValue build() { return KeyValueTreeValue(std::move(value_)); }
+
+private:
+    Any value_;
+};
+
+class KeyValueTreeArrayBuilderBase
+{
+protected:
+    //! Creates an array builder for populating given array object.
+    explicit KeyValueTreeArrayBuilderBase(KeyValueTreeArray* array) : array_(array) {}
+
+    //! Appends a raw Any value to the array.
+    KeyValueTreeValue& addRawValue(Any&& value)
+    {
+        KeyValueTreeValueBuilder builder;
+        builder.setAnyValue(std::move(value));
+        array_->values_.push_back(builder.build());
+        return array_->values_.back();
+    }
+    //! Appends a raw KeyValueTreeValue to the array.
+    KeyValueTreeValue& addRawValue(KeyValueTreeValue&& value)
+    {
+        array_->values_.push_back(std::move(value));
+        return array_->values_.back();
+    }
+
+private:
+    KeyValueTreeArray* array_;
+};
+
+class KeyValueTreeArrayBuilder : public KeyValueTreeArrayBuilderBase
+{
+public:
+    using KeyValueTreeArrayBuilderBase::addRawValue;
+
+private:
+    explicit KeyValueTreeArrayBuilder(KeyValueTreeArray* array) :
+        KeyValueTreeArrayBuilderBase(array)
+    {
+    }
+
+    friend class KeyValueTreeObjectBuilder;
+    friend class KeyValueTreeValueBuilder;
+};
+
+/*! \libinternal \brief
+ * Builder for KeyValueTreeArray objects where all elements are of type `T`.
+ *
+ * The builder does not own the array being constructed, but instead holds a
+ * reference to an object within a tree rooted in KeyValueTreeBuilder or
+ * KeyValueTreeValueBuilder.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+template<typename T>
+class KeyValueTreeUniformArrayBuilder : public KeyValueTreeArrayBuilderBase
+{
+public:
+    //! Appends a value to the array.
+    void addValue(const T& value) { addRawValue(KeyValueTreeBuilder::createValue<T>(value)); }
+
+private:
+    explicit KeyValueTreeUniformArrayBuilder(KeyValueTreeArray* array) :
+        KeyValueTreeArrayBuilderBase(array)
+    {
+    }
+
+    friend class KeyValueTreeObjectBuilder;
+};
+
+/*! \libinternal \brief
+ * Builder for KeyValueTreeArray objects where all elements are
+ * KeyValueTreeObject objects.
+ *
+ * The builder does not own the array being constructed, but instead holds a
+ * reference to an object within a tree rooted in KeyValueTreeBuilder or
+ * KeyValueTreeValueBuilder.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class KeyValueTreeObjectArrayBuilder : public KeyValueTreeArrayBuilderBase
+{
+public:
+    /*! \brief
+     * Appends an object to the array.
+     *
+     * The object is created empty and can be built using the returned
+     * builder.
+     */
+    KeyValueTreeObjectBuilder addObject();
+
+private:
+    explicit KeyValueTreeObjectArrayBuilder(KeyValueTreeArray* array) :
+        KeyValueTreeArrayBuilderBase(array)
+    {
+    }
+
+    friend class KeyValueTreeObjectBuilder;
+};
+
+/*! \libinternal \brief
+ * Builder for KeyValueTreeObject objects.
+ *
+ * The builder does not own the object being constructed, but instead holds a
+ * reference to an object within a tree rooted in KeyValueTreeBuilder or
+ * KeyValueTreeValueBuilder.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class KeyValueTreeObjectBuilder
+{
+public:
+    //! Adds a property with given key from a KeyValueTreeValue.
+    void addRawValue(const std::string& key, KeyValueTreeValue&& value)
+    {
+        addProperty(key, std::move(value));
+    }
+    //! Adds a property with given key from a Any value.
+    void addRawValue(const std::string& key, Any&& value)
+    {
+        addProperty(key, KeyValueTreeValue(std::move(value)));
+    }
+    //! Adds a scalar property with given key, type, and value.
+    template<typename T>
+    void addValue(const std::string& key, const T& value)
+    {
+        addRawValue(key, KeyValueTreeBuilder::createValue<T>(value));
+    }
+    /*! \brief
+     * Adds an object-valued property with given key.
+     *
+     * The object is created empty and can be built using the returned
+     * builder.
+     */
+    KeyValueTreeObjectBuilder addObject(const std::string& key)
+    {
+        auto iter = addProperty(key, KeyValueTreeBuilder::createValue<KeyValueTreeObject>());
+        return KeyValueTreeObjectBuilder(&iter->second);
+    }
+    /*! \brief
+     * Adds a generic array-valued property with given key.
+     *
+     * The array is created empty and can be built using the returned
+     * builder.
+     */
+    KeyValueTreeArrayBuilder addArray(const std::string& key)
+    {
+        auto iter = addProperty(key, KeyValueTreeBuilder::createValue<KeyValueTreeArray>());
+        return KeyValueTreeArrayBuilder(&iter->second.asArray());
+    }
+    /*! \brief
+     * Adds an array-valued property with uniform value types with given
+     * key.
+     *
+     * \tparam T  Type for all values in the array.
+     *
+     * The array is created empty and can be built using the returned
+     * builder.
+     */
+    template<typename T>
+    KeyValueTreeUniformArrayBuilder<T> addUniformArray(const std::string& key)
+    {
+        auto iter = addProperty(key, KeyValueTreeBuilder::createValue<KeyValueTreeArray>());
+        return KeyValueTreeUniformArrayBuilder<T>(&iter->second.asArray());
+    }
+    /*! \brief
+     * Adds an array-valued property with uniform value types with given
+     * key and values.
+     *
+     * \tparam T  Type for all values in the array.
+     *
+     * The array is created to contain the values from `values`.
+     */
+    template<typename T>
+    void addUniformArray(const std::string& key, std::initializer_list<T> values)
+    {
+        auto builder = addUniformArray<T>(key);
+        for (const auto& value : values)
+        {
+            builder.addValue(value);
+        }
+    }
+    /*! \brief
+     * Adds an array-valued property with objects in the array with given
+     * key.
+     *
+     * The array is created empty and can be built using the returned
+     * builder.
+     */
+    KeyValueTreeObjectArrayBuilder addObjectArray(const std::string& key)
+    {
+        auto iter = addProperty(key, KeyValueTreeBuilder::createValue<KeyValueTreeArray>());
+        return KeyValueTreeObjectArrayBuilder(&iter->second.asArray());
+    }
+
+    //! Whether a property with given key exists.
+    bool keyExists(const std::string& key) const { return object_->keyExists(key); }
+    //! Returns value for a given key.
+    const KeyValueTreeValue& operator[](const std::string& key) const { return (*object_)[key]; }
+    //! Returns an object builder for an existing object.
+    KeyValueTreeObjectBuilder getObjectBuilder(const std::string& key)
+    {
+        GMX_ASSERT(keyExists(key), "Requested non-existent value");
+        GMX_ASSERT((*this)[key].isObject(), "Accessing non-object value as object");
+        return KeyValueTreeObjectBuilder(&object_->valueMap_.at(key).asObject());
+    }
+
+    /*! \brief
+     * Returns whether the given object shares any keys with \p this.
+     */
+    bool objectHasDistinctProperties(const KeyValueTreeObject& obj) const
+    {
+        return object_->hasDistinctProperties(obj);
+    }
+    /*! \brief
+     * Merges properties from a given object to `this`.
+     *
+     * The objects should not share any keys, i.e.,
+     * objectHasDistinctProperties() should return `true`.
+     */
+    void mergeObject(KeyValueTreeObject&& obj)
+    {
+        GMX_ASSERT(objectHasDistinctProperties(obj), "Trying to merge overlapping object");
+        for (auto& prop : obj.valueMap_)
+        {
+            addRawValue(prop.first, std::move(prop.second));
+        }
+    }
+
+private:
+    explicit KeyValueTreeObjectBuilder(KeyValueTreeObject* object) : object_(object) {}
+    explicit KeyValueTreeObjectBuilder(KeyValueTreeValue* value) : object_(&value->asObject()) {}
+
+    std::map<std::string, KeyValueTreeValue>::iterator addProperty(const std::string&  key,
+                                                                   KeyValueTreeValue&& value)
+    {
+        GMX_RELEASE_ASSERT(!keyExists(key), "Duplicate key value");
+        object_->values_.reserve(object_->values_.size() + 1);
+        auto iter = object_->valueMap_.insert(std::make_pair(key, std::move(value))).first;
+        object_->values_.push_back(KeyValueTreeProperty(iter));
+        return iter;
+    }
+
+    KeyValueTreeObject* object_;
+
+    friend class KeyValueTreeBuilder;
+    friend class KeyValueTreeValueBuilder;
+    friend class KeyValueTreeObjectArrayBuilder;
+};
+
+/********************************************************************
+ * Inline functions that could not be declared within the classes
+ */
+
+inline KeyValueTreeObjectBuilder KeyValueTreeBuilder::rootObject()
+{
+    return KeyValueTreeObjectBuilder(&root_);
+}
+
+inline KeyValueTreeObjectBuilder KeyValueTreeValueBuilder::createObject()
+{
+    value_ = Any::create<KeyValueTreeObject>(KeyValueTreeObject());
+    return KeyValueTreeObjectBuilder(&value_.castRef<KeyValueTreeObject>());
+}
+
+inline KeyValueTreeArrayBuilder KeyValueTreeValueBuilder::createArray()
+{
+    value_ = Any::create<KeyValueTreeArray>(KeyValueTreeArray());
+    return KeyValueTreeArrayBuilder(&value_.castRef<KeyValueTreeArray>());
+}
+
+inline KeyValueTreeObjectBuilder KeyValueTreeObjectArrayBuilder::addObject()
+{
+    auto& value = addRawValue(KeyValueTreeBuilder::createValue<KeyValueTreeObject>());
+    return KeyValueTreeObjectBuilder(&value);
+}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/keyvaluetreemdpwriter.h b/src/include/gromacs/utility/keyvaluetreemdpwriter.h
new file mode 100644 (file)
index 0000000..60c8d64
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares a function to write a flat key-value tree to look like
+ * old-style mdp output.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_KEYVALUETREEMDPWRITER_H
+#define GMX_UTILITY_KEYVALUETREEMDPWRITER_H
+
+namespace gmx
+{
+
+class KeyValueTreeObject;
+class TextWriter;
+
+/*! \brief Write a flat key-value \c tree to \c writer in mdp style.
+ *
+ * Sub-objects will output nothing, so they can be used to
+ * contain a special key-value pair to create a comment, as
+ * well as the normal key and value. The comment pair will
+ * have a key of "comment", and the value will be used as a
+ * comment (if non-empty). */
+void writeKeyValueTreeAsMdp(TextWriter* writer, const KeyValueTreeObject& tree);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/keyvaluetreeserializer.h b/src/include/gromacs/utility/keyvaluetreeserializer.h
new file mode 100644 (file)
index 0000000..28a91d1
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares serialization routines for KeyValueTree objects.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_KEYVALUETREESERIALIZER_H
+#define GMX_UTILITY_KEYVALUETREESERIALIZER_H
+
+namespace gmx
+{
+
+class KeyValueTreeObject;
+class ISerializer;
+
+//! \cond libapi
+/*! \brief
+ * Serializes a KeyValueTreeObject with given serializer.
+ *
+ * \ingroup module_utility
+ */
+void serializeKeyValueTree(const KeyValueTreeObject& root, ISerializer* serializer);
+/*! \brief
+ * Deserializes a KeyValueTreeObject from a given serializer.
+ *
+ * \ingroup module_utility
+ */
+KeyValueTreeObject deserializeKeyValueTree(ISerializer* serializer);
+//! \endcond
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/keyvaluetreetransform.h b/src/include/gromacs/utility/keyvaluetreetransform.h
new file mode 100644 (file)
index 0000000..b693186
--- /dev/null
@@ -0,0 +1,387 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares utilities for transforming key-value trees.
+ *
+ * See \ref page_mdmodules for the main use case that these support.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_KEYVALUETREETRANSFORM_H
+#define GMX_UTILITY_KEYVALUETREETRANSFORM_H
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <typeindex>
+#include <vector>
+
+#include "gromacs/utility/any.h"
+#include "gromacs/utility/keyvaluetree.h"
+
+namespace gmx
+{
+
+class IKeyValueTreeErrorHandler;
+class KeyValueTreeObjectBuilder;
+
+enum class StringCompareType;
+
+class KeyValueTreeTransformResult;
+class KeyValueTreeTransformRuleBuilder;
+class KeyValueTreeTransformRulesScoped;
+
+namespace internal
+{
+class KeyValueTreeTransformerImpl;
+}
+
+/*! \libinternal \brief
+ * Interface to declare rules for transforming key-value trees.
+ *
+ * This interface is used to add transformation rules for key-value trees.
+ * A transformation is a set of rules that is used to map an input key-value
+ * tree to an output key-value tree, with possible conversion steps performed
+ * in the process.  Currently, each rule maps one item from the source tree to
+ * one item in the target tree (it is possible to expand a single value into an
+ * object with multiple properties).  See KeyValueTreeTransformRuleBuilder for
+ * the kinds of rules currently supported.
+ *
+ * The main use currently is in converting flat-format mdp files to a
+ * structured internal representation.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class IKeyValueTreeTransformRules
+{
+public:
+    /*! \brief
+     * Creates a new rule.
+     *
+     * Properties of the new rule must be specified using the returned
+     * builder.
+     */
+    virtual KeyValueTreeTransformRuleBuilder addRule() = 0;
+    /*! \brief
+     * Creates a scoped set of rules, where all rules use a target sub-tree.
+     *
+     * \param[in] scope Prefix defining the scope in the target tree
+     *
+     * Any rules added to the returned scope will have `scope` prefixed to
+     * their target paths, i.e., it is not possible to produce elements
+     * outside the specified subtree.
+     */
+    virtual KeyValueTreeTransformRulesScoped scopedTransform(const KeyValueTreePath& scope) = 0;
+
+protected:
+    virtual ~IKeyValueTreeTransformRules();
+};
+
+/*! \libinternal \brief
+ * Helper object returned from IKeyValueTreeTransformRules::scopedTransform().
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class KeyValueTreeTransformRulesScoped
+{
+public:
+    //! Internal constructor for creating the scope.
+    KeyValueTreeTransformRulesScoped(internal::KeyValueTreeTransformerImpl* impl,
+                                     const KeyValueTreePath&                prefix);
+    //! Supports returning the object from IKeyValueTreeTransformRules::scopedTransform().
+    KeyValueTreeTransformRulesScoped(KeyValueTreeTransformRulesScoped&& other) noexcept;
+    //! Supports returning the object from IKeyValueTreeTransformRules::scopedTransform().
+    KeyValueTreeTransformRulesScoped& operator=(KeyValueTreeTransformRulesScoped&& other) noexcept;
+    ~KeyValueTreeTransformRulesScoped();
+
+    //! Returns the interface for adding rules to this scope.
+    IKeyValueTreeTransformRules* rules();
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \libinternal \brief
+ * Provides methods to specify one transformation rule.
+ *
+ * \if internal
+ * The builder is implemented as a set of nested objects, each of which is
+ * provides methods for setting a particular property of the rule.  Setting a
+ * property returns another object that has relevant methods for the context.
+ * This provides some structure to the methods, and catches at least some types
+ * of incorrect rules already at compile time.
+ * Additionally, if you use an IDE with completion facilities, it can nicely
+ * guide you through which values you need to specify.
+ * All values are stored within the main builder object, and the rule is
+ * created at the end of the statement.
+ * \endif
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class KeyValueTreeTransformRuleBuilder
+{
+public:
+    /*! \internal \brief
+     * Base class used for implementing parameter provider objects.
+     */
+    class Base
+    {
+    protected:
+        //! Creates a parameter provider object within given builder.
+        explicit Base(KeyValueTreeTransformRuleBuilder* builder) : builder_(builder) {}
+
+        //! The parent builder.
+        KeyValueTreeTransformRuleBuilder* builder_;
+    };
+
+    /*! \libinternal \brief
+     * Properties that can be specified after from().to().
+     *
+     * \tparam FromType Type specified for from() to map from.
+     * \tparam ToType Type specified for to() to map to.
+     */
+    template<typename FromType, typename ToType>
+    class ToValue : public Base
+    {
+    public:
+        //! Creates a parameter provider object within given builder.
+        explicit ToValue(KeyValueTreeTransformRuleBuilder* builder) : Base(builder) {}
+
+        /*! \brief
+         * Specifies the transformation function to convert the value
+         * from FromType to ToType.
+         */
+        void transformWith(std::function<ToType(const FromType&)> transform)
+        {
+            builder_->addTransformToAny([transform](const Any& value) {
+                return Any::create<ToType>(transform(value.cast<FromType>()));
+            });
+        }
+    };
+
+    /*! \libinternal \brief
+     * Properties that can be specified after from().toObject().
+     *
+     * \tparam FromType Type specified for from() to map from.
+     */
+    template<typename FromType>
+    class ToObject : public Base
+    {
+    public:
+        //! Creates a parameter provider object within given builder.
+        explicit ToObject(KeyValueTreeTransformRuleBuilder* builder) : Base(builder) {}
+
+        /*! \brief
+         * Specifies the transformation function to build the output
+         * object.
+         *
+         * The transform should build the output object with the
+         * provided builder.
+         */
+        void transformWith(std::function<void(KeyValueTreeObjectBuilder*, const FromType&)> transform)
+        {
+            builder_->addTransformToObject([transform](KeyValueTreeObjectBuilder* builder, const Any& value) {
+                transform(builder, value.cast<FromType>());
+            });
+        }
+    };
+
+    /*! \libinternal \brief
+     * Properties that can be specified after from().
+     *
+     * \tparam FromType Type specified for from() to map from.
+     */
+    template<typename FromType>
+    class AfterFrom : public Base
+    {
+    public:
+        //! Creates a parameter provider object within given builder.
+        explicit AfterFrom(KeyValueTreeTransformRuleBuilder* builder) : Base(builder) {}
+
+        /*! \brief
+         * Specifies a rule that maps to a value at given path.
+         *
+         * \tparam ToType  Type to map to.
+         * \param[in] path Path to map to.
+         *
+         * It is an error if multiple rules map to the same path, or to
+         * a parent path of the target of an existing rule.
+         * Note that it is possible to have a to() rule map to a child
+         * of a toObject() rule, provided that the path is not created
+         * by the object rule.
+         */
+        template<typename ToType>
+        ToValue<FromType, ToType> to(const KeyValueTreePath& path)
+        {
+            builder_->setToPath(path);
+            return ToValue<FromType, ToType>(builder_);
+        }
+
+        /*! \brief
+         * Specifies a rule that maps to an object (collection of named
+         * values) at given path.
+         *
+         * \param[in] path Path to map to.
+         *
+         * It is an error if multiple rules map to the same path, or to
+         * a parent path of the target of an existing rule.
+         * However, it is allowed to have two toObject() rules map to
+         * the same path, provided that the properties they produce are
+         * distinct.
+         */
+        ToObject<FromType> toObject(const KeyValueTreePath& path)
+        {
+            builder_->setToPath(path);
+            return ToObject<FromType>(builder_);
+        }
+    };
+
+    //! Internal constructor for creating a builder.
+    KeyValueTreeTransformRuleBuilder(internal::KeyValueTreeTransformerImpl* impl,
+                                     const KeyValueTreePath&                prefix);
+    //! Supports returning the builder from IKeyValueTreeTransformRules::addRule().
+    KeyValueTreeTransformRuleBuilder(KeyValueTreeTransformRuleBuilder&&) = default;
+    //! Supports returning the builder from IKeyValueTreeTransformRules::addRule().
+    KeyValueTreeTransformRuleBuilder& operator=(KeyValueTreeTransformRuleBuilder&&) = default;
+    ~KeyValueTreeTransformRuleBuilder(); // NOLINT(bugprone-exception-escape)
+
+    /*! \brief
+     * Specifies a rule that maps a value at given path.
+     *
+     * \tparam FromType Type of value expected at `path`.
+     * \param[in] path Path to map in this rule.
+     *
+     * If the input tree has `path`, but it is not of type `FromType`,
+     * the transform will produce an error.
+     *
+     * It is an error to use the same path in two from() rules.  Similarly,
+     * it is an error to use a child path of a path used in a different
+     * from() rule.
+     */
+    template<typename FromType>
+    AfterFrom<FromType> from(const KeyValueTreePath& path)
+    {
+        setFromPath(path);
+        setExpectedType(typeid(FromType));
+        return AfterFrom<FromType>(this);
+    }
+    /*! \brief
+     * Specifies how strings are matched when matching rules against a path.
+     *
+     * For properties of the object at `path`, `keyMatchType` is used for
+     * string comparison.
+     *
+     * This rule must be specified first for a path, before any other
+     * from() rule specifies the path or a subpath.
+     * The rule only applies to immediate properties at the given path, not
+     * recursively.
+     * It is an error to specify the match type multiple times for a path.
+     */
+    void keyMatchType(const KeyValueTreePath& path, StringCompareType keyMatchType)
+    {
+        setFromPath(path);
+        setKeyMatchType(keyMatchType);
+    }
+
+private:
+    void setFromPath(const KeyValueTreePath& path);
+    void setExpectedType(const std::type_index& type);
+    void setToPath(const KeyValueTreePath& path);
+    void setKeyMatchType(StringCompareType keyMatchType);
+    void addTransformToAny(const std::function<Any(const Any&)>& transform);
+    void addTransformToObject(const std::function<void(KeyValueTreeObjectBuilder*, const Any&)>& transform);
+
+    class Data;
+
+    internal::KeyValueTreeTransformerImpl* impl_;
+    std::unique_ptr<Data>                  data_;
+};
+
+class KeyValueTreeTransformer
+{
+public:
+    KeyValueTreeTransformer();
+    ~KeyValueTreeTransformer();
+
+    IKeyValueTreeTransformRules* rules();
+
+    std::vector<KeyValueTreePath> mappedPaths() const;
+
+    KeyValueTreeTransformResult transform(const KeyValueTreeObject&  tree,
+                                          IKeyValueTreeErrorHandler* errorHandler) const;
+
+private:
+    std::unique_ptr<internal::KeyValueTreeTransformerImpl> impl_;
+};
+
+class IKeyValueTreeBackMapping
+{
+public:
+    virtual ~IKeyValueTreeBackMapping();
+
+    virtual KeyValueTreePath originalPath(const KeyValueTreePath& path) const = 0;
+};
+
+class KeyValueTreeTransformResult
+{
+public:
+    KeyValueTreeObject              object() { return std::move(object_); }
+    const IKeyValueTreeBackMapping& backMapping() const { return *mapping_; }
+
+private:
+    typedef std::unique_ptr<IKeyValueTreeBackMapping> MappingPointer;
+
+    KeyValueTreeTransformResult(KeyValueTreeObject&& object, MappingPointer&& mapping) :
+        object_(std::move(object)), mapping_(std::move(mapping))
+    {
+    }
+
+    KeyValueTreeObject object_;
+    MappingPointer     mapping_;
+
+    friend class internal::KeyValueTreeTransformerImpl;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/logger.h b/src/include/gromacs/utility/logger.h
new file mode 100644 (file)
index 0000000..4c9f773
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares functionality for logging.
+ *
+ * See \ref page_logging for an overview of the functionality.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_LOGGER_H
+#define GMX_UTILITY_LOGGER_H
+
+#include <string>
+
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+
+struct LogEntry
+{
+    LogEntry() : asParagraph(false) {}
+
+    std::string text;
+    bool        asParagraph;
+};
+
+/*! \libinternal \brief
+ * Target where log output can be written.
+ *
+ * \ingroup module_utility
+ */
+class ILogTarget
+{
+public:
+    virtual ~ILogTarget();
+
+    //! Writes a log entry to this target.
+    virtual void writeEntry(const LogEntry& entry) = 0;
+};
+
+/*! \libinternal \brief
+ * Helper class for creating log entries with ::GMX_LOG.
+ *
+ * \ingroup module_utility
+ */
+class LogEntryWriter
+{
+public:
+    //! Appends given text as a line in the log entry.
+    LogEntryWriter& appendText(const char* text)
+    {
+        entry_.text.append(text);
+        return *this;
+    }
+    //! Appends given text as a line in the log entry.
+    LogEntryWriter& appendText(const std::string& text)
+    {
+        entry_.text.append(text);
+        return *this;
+    }
+    //! Appends given text as a line in the log entry, with printf-style formatting.
+    LogEntryWriter& appendTextFormatted(gmx_fmtstr const char* fmt, ...) gmx_format(printf, 2, 3);
+    //! Writes the log entry with empty lines before and after.
+    LogEntryWriter& asParagraph()
+    {
+        entry_.asParagraph = true;
+        return *this;
+    }
+
+private:
+    LogEntry entry_;
+
+    friend class LogWriteHelper;
+};
+
+/*! \internal \brief
+ * Helper class for implementing ::GMX_LOG.
+ *
+ * \ingroup module_utility
+ */
+class LogWriteHelper
+{
+public:
+    //! Initializes a helper for writing to the given target.
+    explicit LogWriteHelper(ILogTarget* target) : target_(target) {}
+
+    // Should be explicit, once that works in CUDA.
+    /*! \brief
+     * Returns whether anything needs to be written.
+     *
+     * Note that the return value is unintuitively `false` when the target
+     * is active, to allow implementing ::GMX_LOG like it is now.
+     */
+    operator bool() const { return target_ == nullptr; }
+
+    /*! \brief
+     * Writes the entry from the given writer to the log target.
+     *
+     * This is implemented as an assignment operator to get proper
+     * precedence for operations for the ::GMX_LOG macro; this is a common
+     * technique for implementing macros that allow streming information to
+     * them (see, e.g., Google Test).
+     */
+    LogWriteHelper& operator=(const LogEntryWriter& entryWriter)
+    {
+        target_->writeEntry(entryWriter.entry_);
+        return *this;
+    }
+
+private:
+    ILogTarget* target_;
+};
+
+/*! \libinternal \brief
+ * Represents a single logging level.
+ *
+ * Typically this type is not used directly, but instances in MDLogger are
+ * simply accessed through ::GMX_LOG in code that writes to the log.
+ *
+ * \ingroup module_utility
+ */
+class LogLevelHelper
+{
+public:
+    //! Initializes a helper for writing to the given target.
+    explicit LogLevelHelper(ILogTarget* target) : target_(target) {}
+
+    // Both of the below should be explicit, once that works in CUDA.
+    //! Returns whether the output for this log level goes anywhere.
+    operator bool() const { return target_ != nullptr; }
+
+    //! Creates a helper for ::GMX_LOG.
+    operator LogWriteHelper() const { return LogWriteHelper(target_); }
+
+private:
+    ILogTarget* target_;
+};
+
+/*! \libinternal \brief
+ * Declares a logging interface.
+ *
+ * Typically, this object is not created directly, but instead through
+ * LoggerBuilder.
+ *
+ * For now, this is named MDLogger, since it is used only there, and it is not
+ * clear whether the logging levels can be the same throughout the code.  It
+ * should be relatively straightforward to split this into multiple classes
+ * with different supported logging levels without changing calling code, or to
+ * rename it to Logger if we do not need any specialization.
+ *
+ * \ingroup module_utility
+ */
+class MDLogger
+{
+public:
+    //! Supported logging levels.
+    enum class LogLevel
+    {
+        Error,
+        Warning,
+        Info,
+        Debug,
+        VerboseDebug,
+        Count
+    };
+    //! Number of logging levels.
+    static const int LogLevelCount = static_cast<int>(LogLevel::Count);
+
+    MDLogger();
+    //! Creates a logger with the given targets.
+    explicit MDLogger(ILogTarget* targets[LogLevelCount]);
+
+    //! For writing at LogLevel::Warning level.
+    LogLevelHelper warning;
+    //! For writing at LogLevel::Error level.
+    LogLevelHelper error;
+    //! For writing at LogLevel::Debug level.
+    LogLevelHelper debug;
+    //! For writing at LogLevel::VerboseDebug level.
+    LogLevelHelper verboseDebug;
+    //! For writing at LogLevel::Info level.
+    LogLevelHelper info;
+};
+
+/*! \brief
+ * Helper to log information using gmx::MDLogger.
+ *
+ * \param  logger  LogLevelHelper instance to use for logging.
+ *
+ * Used as
+ * \code
+   GMX_LOG(logger.warning).appendText(...);
+   \endcode
+ * and ensures that the code to format the output is only executed when the
+ * output goes somewhere.
+ *
+ * See LogEntryWriter for functions that can be used with the macro (such as
+ * the appendText() in the example).
+ *
+ * \ingroup module_utility
+ */
+#define GMX_LOG(logger)                                                  \
+    if (::gmx::LogWriteHelper helper = ::gmx::LogWriteHelper(logger)) {} \
+    else                                                                 \
+        helper = ::gmx::LogEntryWriter()
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/loggerbuilder.h b/src/include/gromacs/utility/loggerbuilder.h
new file mode 100644 (file)
index 0000000..499f587
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares functionality for initializing logging.
+ *
+ * See \ref page_logging for an overview of the functionality.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_LOGGERBUILDER_H
+#define GMX_UTILITY_LOGGERBUILDER_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/utility/logger.h"
+
+namespace gmx
+{
+
+class TextOutputStream;
+
+class LoggerFormatterBuilder;
+class LoggerOwner;
+
+/*! \libinternal \brief
+ * Initializes loggers.
+ *
+ * This class provides methods for specifying logging targets for a logger and
+ * building the logger after all targets have been specified.  Having this
+ * separate from the logger allows using different internal data structures
+ * during initialization and operation, and simplifies the responsibilities of
+ * the involved classes.
+ *
+ * \ingroup module_utility
+ */
+class LoggerBuilder
+{
+public:
+    LoggerBuilder();
+    ~LoggerBuilder();
+
+    /*! \brief
+     * Adds a stream to which log output is written.
+     *
+     * All output at level \p level or above it is written to \p stream.
+     * The caller is responsible of closing and freeing \p stream once the
+     * logger is discarded.
+     */
+    void addTargetStream(MDLogger::LogLevel level, TextOutputStream* stream);
+    /*! \brief
+     * Adds a file to which log output is written.
+     *
+     * All output at level \p level or above it is written to \p fp.
+     * The caller is responsible of closing \p fp once the logger is
+     * discarded.
+     */
+    void addTargetFile(MDLogger::LogLevel level, FILE* fp);
+
+    /*! \brief
+     * Builds the logger with the targets set for this builder.
+     *
+     * After this function has been called, the builder can (and should) be
+     * discarded.
+     */
+    LoggerOwner build();
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \libinternal \brief
+ * Manages memory for a logger built with LoggerBuilder.
+ *
+ * This class is responsible of managing all memory allocated by LoggerBuilder
+ * that is needed for operation of the actual logger.  Also the actual logger
+ * instance is owned by this class.  This allows keeping the actual logger simple
+ * and streamlined.
+ *
+ * This class supports move construction and assignment, which allows
+ * initializing it on the stack and assigning a new instance if the targets
+ * need to be changed.
+ *
+ * \ingroup module_utility
+ */
+class LoggerOwner
+{
+public:
+    //! Move-constructs the owner.
+    LoggerOwner(LoggerOwner&& other) noexcept;
+    ~LoggerOwner();
+
+    //! Move-assings the owner.
+    LoggerOwner& operator=(LoggerOwner&& other) noexcept;
+
+    //! Returns the logger for writing the logs.
+    const MDLogger& logger() const { return *logger_; }
+
+private:
+    class Impl;
+
+    LoggerOwner(std::unique_ptr<Impl> impl);
+
+    std::unique_ptr<Impl> impl_;
+    const MDLogger*       logger_;
+
+    friend class LoggerBuilder;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/mdmodulesnotifier.h b/src/include/gromacs/utility/mdmodulesnotifier.h
new file mode 100644 (file)
index 0000000..4f080af
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::MDModulesNotifier and builder.
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+
+#ifndef GMX_UTILITY_MDMODULESNOTIFIER_H
+#define GMX_UTILITY_MDMODULESNOTIFIER_H
+
+#include <functional>
+#include <vector>
+
+namespace gmx
+{
+
+/*! \libinternal
+ * \brief Organizes notifications about an event of interest to modules.
+ *
+ * An object of this type permits modules to subscribe to the
+ * corresponding event. The template types of this type encode what
+ * information is available when the event occurs. Modules \c
+ * subscribe() by providing a callback function that accepts a single
+ * parameter of such an event type. The code that handles that event
+ * has the responsibilty to call \c notify() afterwards. The
+ * subscribed modules then receive the callback with the requested
+ * event type as an argument.
+ *
+ * See gmx::MDModulesNotifiers for sequence diagrams for an example.
+ *
+ * This suits scenarios where several objects are built (or re-built)
+ * and one or more modules need to know when one or more of such
+ * objects are available (or updated), so they can adapt their
+ * internal state accordingly. Examples include responding to loading
+ * input data, or to changes related to a recurring process like
+ * checkpointing or partitioning. The coupling between these modules
+ * is now expressed indirectly. This improves the modularity and
+ * testability of those modules.
+ *
+ * The implementation provides the necessary flexibility to be
+ * parameterized with multiple event types and provide \c callback()
+ * and \b notify() methods corresponding to each related event. This
+ * is done by inheriting from a series of base classes, each of which
+ * handles a single type of event. BuildMDModulesNotifier implements
+ * the details. To create a class of this type that provides two
+ * events with callbacks that receive respectively types TypeA and
+ * TypeB, use BuildMDModulesNotifier<TypeA, TypeB>::type.
+ *
+ * \tparam CallParameter of the function to be notified
+ * \tparam MDModulesNotifierBase class to be extended with a notification
+ *                                  with CallParameter
+ *
+ * \note All added subscribers are required to out-live the MDModulesNotifier
+ *
+ */
+template<class CallParameter, class MDModulesNotifierBase>
+class MDModulesNotifier : public MDModulesNotifierBase
+{
+public:
+    //! Make base class notification trigger available to this class
+    using MDModulesNotifierBase::notify;
+    //! Make base class subscription available to this class
+    using MDModulesNotifierBase::subscribe;
+
+    /*! \brief Notifies subscribers of the event described by \c
+     * callbackParameter.
+     *
+     * \param[in] callParameter of the function to be called back
+     */
+    void notify(CallParameter callParameter) const
+    {
+        for (auto& callBack : callBackFunctions_)
+        {
+            callBack(callParameter);
+        }
+    }
+
+    /*! \brief
+     * Add callback function to be called when \c notify() is called
+     *
+     * \param[in] callBackFunction to be called
+     */
+    void subscribe(std::function<void(CallParameter)> callBackFunction)
+    {
+        callBackFunctions_.emplace_back(callBackFunction);
+    }
+
+private:
+    std::vector<std::function<void(CallParameter)>> callBackFunctions_;
+};
+
+/*! \internal
+ * \brief Aide to avoid nested MDModulesNotifier definition.
+ *
+ * Instead of
+ * MDModulesNotifier<CallParameterA, MDModulesNotifier<CallParameterB, etc ... >>
+ * this allows to write
+ * BuildMDModulesNotifier<CallParameterA, CallParameterB, ...>::type
+ *
+ * \tparam CallParameter all the callback types to be registered
+ */
+template<class... CallParameter>
+struct BuildMDModulesNotifier;
+
+/*! \internal \brief Template specialization to end parameter unpacking recursion.
+ */
+template<>
+struct BuildMDModulesNotifier<>
+{
+    /*! \internal
+     * \brief Do nothing but be base class of MDModulesNotifier.
+     *
+     * Required so that using MDModulesNotifierBase::notify and
+     * MDModulesNotifierBase::subscribe are valid in derived class.
+     */
+    class NoCallParameter
+    {
+    public:
+        //! Do nothing but provide MDModulesNotifier::notify to derived class
+        void notify() {}
+        //! Do nothing but provide MDModulesNotifier::subscribe to derived class
+        void subscribe() {}
+    };
+    /*! \brief Defines a type if no notifications are managed.
+     *
+     * This ensures that code works with MDModuleCallParameterManagement that
+     * does not manage any notifications.
+     */
+    using type = NoCallParameter;
+};
+
+/*! \libinternal
+ * \brief Template specialization to assemble MDModulesNotifier.
+ *
+ * Assembly of MDModulesNotifier is performed by recursively taking off the
+ * front of the CallParameter parameter pack and constructing the nested type
+ * definition of MDModulesNotifier base classes.
+ *
+ * \tparam CurrentCallParameter front of the template parameter pack
+ * \tparam CallParameter rest of the callback types
+ */
+template<class CurrentCallParameter, class... CallParameter>
+struct BuildMDModulesNotifier<CurrentCallParameter, CallParameter...>
+{
+    // private:
+    //! The next type with rest of the arguments with the front parameter removed.
+    using next_type = typename BuildMDModulesNotifier<CallParameter...>::type;
+    //! The type of the MDModulesNotifier
+    using type = MDModulesNotifier<CurrentCallParameter, next_type>;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/mdmodulesnotifiers.h b/src/include/gromacs/utility/mdmodulesnotifiers.h
new file mode 100644 (file)
index 0000000..0cbb565
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::MDModulesNotifiers.
+ *
+ * \author Christian Blau <blau@kth.se>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+
+#ifndef GMX_UTILITY_MDMODULESNOTIFIERS_H
+#define GMX_UTILITY_MDMODULESNOTIFIERS_H
+
+#include <string>
+#include <vector>
+
+#include "gromacs/math/arrayrefwithpadding.h"
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/mdmodulesnotifier.h"
+
+struct t_commrec;
+struct gmx_mtop_t;
+struct warninp;
+enum class PbcType : int;
+
+namespace gmx
+{
+
+class KeyValueTreeObject;
+class KeyValueTreeObjectBuilder;
+class LocalAtomSetManager;
+class MDLogger;
+class IndexGroupsAndNames;
+class SeparatePmeRanksPermitted;
+struct MDModulesCheckpointReadingDataOnMaster;
+struct MDModulesCheckpointReadingBroadcast;
+struct MDModulesWriteCheckpointData;
+
+/*! \libinternal \brief Check if module outputs energy to a specific field.
+ *
+ * Ensures that energy is output for this module.
+ */
+struct MDModulesEnergyOutputToDensityFittingRequestChecker
+{
+    //! Trigger output to density fitting energy field
+    bool energyOutputToDensityFitting_ = false;
+};
+
+/*! \libinternal \brief Check if QMMM module outputs energy to a specific field.
+ *
+ * Ensures that energy is output for QMMM module.
+ */
+struct MDModulesEnergyOutputToQMMMRequestChecker
+{
+    //! Trigger output to density fitting energy field
+    bool energyOutputToQMMM_ = false;
+};
+
+/*! \libinternal
+ * \brief Collect errors for the energy calculation frequency.
+ *
+ * Collect errors regarding energy calculation frequencies as strings that then
+ * may be used to issue errors.
+ *
+ * \note The mdp option "nstcalcenergy" is altered after reading the .mdp input
+ *       and only used in certain integrators, thus this class is to be used
+ *       only after all these operations are done.
+ */
+class EnergyCalculationFrequencyErrors
+{
+public:
+    //! Construct by setting the energy calculation frequency
+    EnergyCalculationFrequencyErrors(int64_t energyCalculationIntervalInSteps) :
+        energyCalculationIntervalInSteps_(energyCalculationIntervalInSteps)
+    {
+    }
+    //! Return the number of steps of an energy calculation interval
+    std::int64_t energyCalculationIntervalInSteps() const
+    {
+        return energyCalculationIntervalInSteps_;
+    }
+    //! Collect error messages
+    void addError(const std::string& errorMessage) { errorMessages_.push_back(errorMessage); }
+    //! Return error messages
+    const std::vector<std::string>& errorMessages() const { return errorMessages_; }
+
+private:
+    //! The frequency of energy calculations
+    const std::int64_t energyCalculationIntervalInSteps_;
+    //! The error messages
+    std::vector<std::string> errorMessages_;
+};
+
+/*! \libinternal \brief Provides the simulation time step in ps.
+ */
+struct SimulationTimeStep
+{
+    //! Time step (ps)
+    const double delta_t;
+};
+
+/*! \libinternal \brief Provides coordinates and simulation box.
+ */
+struct CoordinatesAndBoxPreprocessed
+{
+    ArrayRefWithPadding<RVec> coordinates_;
+    matrix                    box_;
+    PbcType                   pbc_;
+};
+
+/*! \libinternal \brief Mdrun input filename.
+ */
+struct MdRunInputFilename
+{
+    //! The name of the run input file (.tpr) as output by grompp
+    std::string mdRunFilename_;
+};
+
+/*! \libinternal \brief Notification for QM program input filename
+ *  provided by user as command-line argument for grompp
+ */
+struct QMInputFileName
+{
+    //! Flag if QM Input File has been provided by user
+    bool hasQMInputFileName_ = false;
+    //! The name of the QM Input file (.inp)
+    std::string qmInputFileName_;
+};
+
+/*! \libinternal
+ * \brief Group of notifers to organize that MDModules
+ * can receive callbacks they subscribe to.
+ *
+ * MDModules use members of this struct to subscribe to notifications
+ * of particular events. When the event occurs, the callback provided
+ * by a particular MDModule will be passed a parameter of the
+ * particular type they are interested in.
+ *
+ * Typically, during the setup phase, modules subscribe to notifiers
+ * that interest them by passing callbacks that expect a single parameter
+ * that describes the event. These are stored for later use. See the
+ * sequence diagram that follows:
+   \msc
+wordwraparcs=true,
+hscale="2";
+
+modules [label = "mdModules:\nMDModules"],
+notifiers [label="notifiers\nMDModulesNotifiers"],
+notifier [label="exampleNotifier:\nBuildMDModulesNotifier\n<EventX, EventY>::type"],
+moduleA [label="moduleA"],
+moduleB [label="moduleB"],
+moduleC [label="moduleC"];
+
+modules box moduleC [label = "mdModules creates and owns moduleA, moduleB, and moduleC"];
+modules =>> notifiers [label="creates"];
+notifiers =>> notifier [label="creates"];
+notifier =>> notifiers [label="returns"];
+notifiers =>> modules [label="returns"];
+
+modules =>> moduleA [label="provides notifiers"];
+moduleA =>> moduleA [label="unpacks\nnotifiers.exampleNotifier"];
+moduleA =>> notifier [label="subscribes with\ncallback(EventX&)"];
+notifier =>> notifier [label="records subscription\nto EventX"];
+moduleA =>> notifier [label="subscribes with\ncallback(EventY&)"];
+notifier =>> notifier [label="records subscription\nto EventY"];
+moduleA =>> modules [label="returns"];
+
+modules =>> moduleB [label="provides notifiers"];
+moduleB =>> moduleB [label="unpacks\nnotifiers.exampleNotifier"];
+moduleA =>> notifier [label="subscribes with\ncallback(EventY&)"];
+notifier =>> notifier [label="records subscription\nto EventY"];
+moduleB =>> modules [label="returns"];
+
+modules =>> moduleC [label="provides notifiers"];
+moduleC =>> moduleC [label="unpacks and keeps\nnotifiers.exampleNotifier"];
+moduleC =>> modules [label="returns"];
+
+   \endmsc
+
+   * When the event occurs later on, the stored callbacks are used to
+   * allow the modules to react. See the following sequence diagram,
+   * which assumes that exampleNotifier was configured as in the
+   * previous sequence diagram.
+
+   \msc
+wordwraparcs=true,
+hscale="2";
+
+moduleC [label="moduleC"],
+notifier [label="exampleNotifier:\nBuildMDModulesNotifier\n<EventX, EventY>::type"],
+moduleA [label="moduleA"],
+moduleB [label="moduleB"];
+
+moduleC box moduleB [label = "Later, when ModuleC is doing work"];
+moduleC =>> moduleC [label="generates EventX"];
+moduleC =>> moduleC [label="generates EventY"];
+moduleC =>> notifier [label="calls notify(eventX)"];
+notifier =>> moduleA [label="calls callback(eventX)"];
+moduleA =>> moduleA [label="reacts to eventX"];
+moduleA =>> notifier [label="returns"];
+
+notifier =>> moduleC [label="returns"];
+moduleC =>> notifier [label="calls notify(eventY)"];
+notifier =>> moduleA [label="calls callback(eventY)"];
+moduleA =>> moduleA [label="reacts to eventY"];
+moduleA =>> notifier [label="returns"];
+notifier =>> moduleB [label="calls callback(eventY)"];
+moduleB =>> moduleB [label="reacts to eventY"];
+moduleB =>> notifier [label="returns"];
+notifier =>> moduleC [label="returns"];
+   \endmsc
+ *
+ * The template arguments to the members of this struct are the
+ * parameters passed to the callback functions, one type per
+ * callback. Arguments passed as pointers are always meant to be
+ * modified, but never meant to be stored (in line with the policy
+ * everywhere else).
+ *
+ */
+struct MDModulesNotifiers
+{
+    /*! \brief Pre-processing callback functions.
+     * CoordinatesAndBoxPreprocessed Allows modules to access coordinates,
+     *                                box and pbc during grompp
+     * MDLogger Allows MdModule to use standard logging class for messages output
+     * warninp* Allows modules to make grompp warnings, notes and errors
+     * EnergyCalculationFrequencyErrors* allows modules to check if they match
+     *                                   their required calculation frequency
+     *                                   and add their error message if needed
+     *                                   to the collected error messages
+     * gmx_mtop_t* Allows modules to modify the topology during pre-processing
+     * IndexGroupsAndNames provides modules with atom indices and their names
+     * KeyValueTreeObjectBuilder enables writing of module internal data to
+     *                           .tpr files.
+     * QMInputFileName Allows QMMM module to know if user provided external QM input file
+     */
+    BuildMDModulesNotifier<const CoordinatesAndBoxPreprocessed&,
+                           const MDLogger&,
+                           warninp*,
+                           EnergyCalculationFrequencyErrors*,
+                           gmx_mtop_t*,
+                           const IndexGroupsAndNames&,
+                           KeyValueTreeObjectBuilder,
+                           const QMInputFileName&>::type preProcessingNotifier_;
+
+    /*! \brief Handles subscribing and calling checkpointing callback functions.
+     *
+     * MDModulesCheckpointReadingDataOnMaster provides modules with their
+     *                                        checkpointed data on the master
+     *                                        node and checkpoint file version
+     * MDModulesCheckpointReadingBroadcast provides modules with a communicator
+     *                                     and the checkpoint file version to
+     *                                     distribute their data
+     * MDModulesWriteCheckpointData provides the modules with a key-value-tree
+     *                              builder to store their checkpoint data and
+     *                              the checkpoint file version
+     */
+    BuildMDModulesNotifier<MDModulesCheckpointReadingDataOnMaster, MDModulesCheckpointReadingBroadcast, MDModulesWriteCheckpointData>::type
+            checkpointingNotifier_;
+
+    /*! \brief Handles subscribing and calling callbacks during simulation setup.
+     *
+     * const KeyValueTreeObject& provides modules with the internal data they
+     *                           wrote to .tpr files
+     * LocalAtomSetManager* enables modules to add atom indices to local atom sets
+     *                      to be managed
+     * const MDLogger& Allows MdModule to use standard logging class for messages output
+     * const gmx_mtop_t& provides the topology of the system to the modules
+     * MDModulesEnergyOutputToDensityFittingRequestChecker* enables modules to
+     *                      report if they want to write their energy output
+     *                      to the density fitting field in the energy files
+     * MDModulesEnergyOutputToQMMMRequestChecker* enables QMMM module to
+     *                      report if it want to write their energy output
+     *                      to the "Quantum En." field in the energy files
+     * SeparatePmeRanksPermitted* enables modules to report if they want
+     *                      to disable dedicated PME ranks
+     * const PbcType& provides modules with the periodic boundary condition type
+     *                that is used during the simulation
+     * const SimulationTimeStep& provides modules with the simulation time-step
+     *                           that allows them to interconvert between step
+     *                           time information
+     * const t_commrec& provides a communicator to the modules during simulation
+     *                  setup
+     * const MdRunInputFilename& Allows modules to know .tpr filename during mdrun
+     */
+    BuildMDModulesNotifier<const KeyValueTreeObject&,
+                           LocalAtomSetManager*,
+                           const MDLogger&,
+                           const gmx_mtop_t&,
+                           MDModulesEnergyOutputToDensityFittingRequestChecker*,
+                           MDModulesEnergyOutputToQMMMRequestChecker*,
+                           SeparatePmeRanksPermitted*,
+                           const PbcType&,
+                           const SimulationTimeStep&,
+                           const t_commrec&,
+                           const MdRunInputFilename&>::type simulationSetupNotifier_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/message_string_collector.h b/src/include/gromacs/utility/message_string_collector.h
new file mode 100644 (file)
index 0000000..a75851d
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2011,2012,2013,2014,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares ::gmx::MessageStringCollector.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_MESSAGE_STRING_COLLECTOR_H
+#define GMX_UTILITY_MESSAGE_STRING_COLLECTOR_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/utility/classhelpers.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Helper class for collecting message strings, optionally with context.
+ *
+ * After strings have been collected, they can be formatted into one long
+ * string for, e.g., printing out or for including in an exception.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class MessageStringCollector
+{
+public:
+    MessageStringCollector();
+    ~MessageStringCollector();
+
+    /*! \brief
+     * Starts a context for messages.
+     *
+     * \param[in] name  Short description of the context.
+     *
+     * \see finishContext()
+     * \see MessageStringContext
+     */
+    void startContext(const char* name);
+    /*! \brief
+     * Convenience wrapper for startContext(const char *).
+     */
+    void startContext(const std::string& name) { startContext(name.c_str()); }
+    /*! \brief
+     * Adds a new message.
+     */
+    void append(const char* message) { append(std::string(message)); }
+    /*! \brief
+     * Adds a new message.
+     */
+    void append(const std::string& message);
+    /*! \brief
+     * Adds a new message if the condition is satisfied..
+     */
+    void appendIf(bool condition, const char* message);
+    /*! \brief
+     * Adds a new message if the condition is satisfied.
+     */
+    void appendIf(bool condition, const std::string& message);
+    /*! \brief
+     * Ends a context started with startContext().
+     *
+     * \see MessageStringContext
+     */
+    void finishContext();
+    /*! \brief
+     * Clears all collected messages.
+     */
+    void clear();
+
+    /*! \brief
+     * Returns false if any messages have been added.
+     *
+     * \returns false if append() has been called at least once.
+     *
+     * The return value is identical to `toString().empty()`.
+     * Calls to startContext() or finishContext() do not affect the
+     * return value of this function.
+     */
+    bool isEmpty() const;
+    /*! \brief
+     * Returns all collected messages as one string.
+     */
+    std::string toString() const;
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+/*! \libinternal \brief
+ * Convenience class for creating a message context.
+ *
+ * This class provides a RAII-style interface to the
+ * MessageStringCollector::startContext() and
+ * MessageStringCollector::finishContext() methods: finishContext() is called
+ * upon destruction of the object.  This avoids the need to call
+ * MessageStringCollector::finishContext() on every possible exit point.
+ *
+ * Example usage:
+ * \code
+   bool function(::gmx::MessageStringCollector *errors)
+   {
+       ::gmx::MessageStringContext errcontext(errors, "In function()");
+       bool bOk = function2(errors);
+       bOk = function3(errors) && bOk;
+       // <more processing>
+       return bOk;
+   }
+   \endcode
+ *
+ * \see MessageStringCollector
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class MessageStringContext
+{
+public:
+    /*! \brief
+     * Adds a context for the given object.
+     */
+    MessageStringContext(MessageStringCollector* collector, const char* name) :
+        collector_(*collector)
+    {
+        collector_.startContext(name);
+    }
+    /*! \brief
+     * Adds a context for the given object.
+     */
+    MessageStringContext(MessageStringCollector* collector, const std::string& name) :
+        collector_(*collector)
+    {
+        collector_.startContext(name);
+    }
+    /*! \brief
+     * Calls MessageStringCollector::finishContext() on the wrapped object.
+     */
+    ~MessageStringContext() { collector_.finishContext(); }
+
+private:
+    //! The wrapped object.
+    MessageStringCollector& collector_;
+
+    GMX_DISALLOW_COPY_AND_ASSIGN(MessageStringContext);
+};
+
+} // namespace gmx
+
+#endif // GMX_UTILITY_MESSAGE_STRING_COLLECTOR_H
diff --git a/src/include/gromacs/utility/mpiinfo.h b/src/include/gromacs/utility/mpiinfo.h
new file mode 100644 (file)
index 0000000..0c8b0e5
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_UTILITY_MPI_INFO_H
+#define GMX_UTILITY_MPI_INFO_H
+
+namespace gmx
+{
+/*! \brief Enum describing CUDA-aware support in underlying MPI library.
+ */
+enum class CudaAwareMpiStatus : int
+{
+    Supported,    //!< CUDA-aware support available.
+    NotSupported, //!< CUDA-aware support NOT available.
+    NotKnown      //!< CUDA-aware support status not known.
+};
+
+
+/*! \brief
+ * Wrapper on top of MPIX_Query_cuda_support()
+ * For MPI implementations which don't support this function, it returns NotKnown
+ * Even when an MPI implementation does support this function, MPI library might not be
+ * robust enough to detect CUDA-aware support at runtime correcly e.g. when UCX PML is used
+ * or CUDA is disabled at runtime
+ *
+ * \returns     CUDA-aware status in MPI implementation */
+CudaAwareMpiStatus checkMpiCudaAwareSupport();
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/niceheader.h b/src/include/gromacs/utility/niceheader.h
new file mode 100644 (file)
index 0000000..b133ecd
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares function for printing a nice header for text output files.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_NICEHEADER_H
+#define GMX_UTILITY_NICEHEADER_H
+
+namespace gmx
+{
+
+class TextWriter;
+
+/*! \brief
+ * Prints creation time stamp and user information into a string as comments, and returns it.
+ *
+ * \param[out] writer         Where to print the information.
+ * \param[in]  fn             Name of the file being written; if nullptr, described as "unknown".
+ * \param[in]  commentChar    Character to use as the starting delimiter for comments.
+ * \throws     std::bad_alloc if out of memory. */
+void niceHeader(TextWriter* writer, const char* fn, char commentChar);
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/nodelete.h b/src/include/gromacs/utility/nodelete.h
new file mode 100644 (file)
index 0000000..170b297
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares no_delete deleter for std::shared_ptr.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_NODELETE_H
+#define GMX_UTILITY_NODELETE_H
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Deleter for std::shared_ptr that does nothing.
+ *
+ * This is useful for cases where a class needs to keep a reference to another
+ * class, and optionally also manage the lifetime of that other class.
+ * The simplest construct (that does not force all callers to use heap
+ * allocation and std::shared_ptr for the referenced class) is to use a
+ * single std::shared_ptr to hold that reference, and use no_delete as the
+ * deleter if the lifetime is managed externally.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+template<class T>
+struct no_delete
+{
+    //! Deleter that does nothing.
+    void operator()(T* /*unused*/) {}
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/path.h b/src/include/gromacs/utility/path.h
new file mode 100644 (file)
index 0000000..9e1596c
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2011,2012,2013,2014,2015 by the GROMACS development team.
+ * Copyright (c) 2016,2018,2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares functions for OS-independent path handling.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_PATH_H
+#define GMX_UTILITY_PATH_H
+
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+namespace gmx
+{
+
+class Path
+{
+public:
+    static bool containsDirectory(const std::string& path);
+    static bool isAbsolute(const char* path);
+    static bool isAbsolute(const std::string& path);
+    static bool isEquivalent(const std::string& path1, const std::string& path2);
+
+    static std::string join(const std::string& path1, const std::string& path2);
+    static std::string join(const std::string& path1, const std::string& path2, const std::string& path3);
+    //! Return a path using directory separators that suit the execution OS.
+    static std::string normalize(const std::string& path);
+    /*! \brief Returns a copy of the parent path (ie. directory
+     * components) of \c input ie. up to but excluding the last
+     * directory separator (if one exists).
+     *
+     * \returns A copy of the parent path-components, or empty if
+     * no directory separator exists. */
+    static std::string getParentPath(const std::string& input);
+    /*! \brief Returns a copy of the filename in \c input
+     * ie. after the last directory separator (if one exists). */
+    static std::string getFilename(const std::string& input);
+    //! Returns whether an extension is present in \c input.
+    static bool hasExtension(const std::string& input);
+    /*! \brief Returns whether the extension present in \c input
+     * matches \c extension (which does not include the separator
+     * character). */
+    static bool extensionMatches(std::string_view input, std::string_view extension);
+    /*! \brief Returns a copy of the input without any trailing
+     * extension found in the filename component. */
+    static std::string stripExtension(const std::string& input);
+    /*! \brief Concatenate \c stringToAdd to a copy of \c input,
+     * before any file extension (if one exists), and return the
+     * result. */
+    static std::string concatenateBeforeExtension(const std::string& input, const std::string& stringToAdd);
+
+    static const char* stripSourcePrefix(const char* path);
+
+    static bool        exists(const char* path);
+    static bool        exists(const std::string& path);
+    static std::string getWorkingDirectory();
+
+    static void splitPathEnvironment(const std::string& pathEnv, std::vector<std::string>* result);
+    static std::vector<std::string> getExecutablePaths();
+
+    static std::string resolveSymlinks(const std::string& path);
+
+private:
+    // Disallow instantiation.
+    Path();
+};
+
+class File
+{
+public:
+    struct NotFoundInfo
+    {
+        NotFoundInfo(const char* filename, const char* message, const char* call, bool wasError, int err) :
+            filename(filename), message(message), call(call), wasError(wasError), err(err)
+        {
+        }
+
+        const char* filename;
+        const char* message;
+        const char* call;
+        bool        wasError;
+        int         err;
+    };
+
+    static void              returnFalseOnError(const NotFoundInfo& info);
+    static void              throwOnError(const NotFoundInfo& info);
+    [[noreturn]] static void throwOnNotFound(const NotFoundInfo& info);
+
+    typedef void (*NotFoundHandler)(const NotFoundInfo& info);
+
+    /*! \brief
+     * Checks whether a file exists and is a regular file.
+     *
+     * \param[in] filename    Path to the file to check.
+     * \param[in] onNotFound  Function to call when the file does not
+     *     exists or there is an error accessing it.
+     * \returns   `true` if \p filename exists and is accessible.
+     *
+     * Does not throw, unless onNotFound throws.
+     */
+    static bool exists(const char* filename, NotFoundHandler onNotFound);
+    //! \copydoc exists(const char *, NotFoundHandler)
+    static bool exists(const std::string& filename, NotFoundHandler onNotFound);
+
+private:
+    // Disallow instantiation.
+    File();
+};
+
+class Directory
+{
+public:
+    static int  create(const char* path);
+    static int  create(const std::string& path);
+    static bool exists(const char* path);
+    static bool exists(const std::string& path);
+
+private:
+    // Disallow instantiation.
+    Directory();
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/physicalnodecommunicator.h b/src/include/gromacs/utility/physicalnodecommunicator.h
new file mode 100644 (file)
index 0000000..7d57eef
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares functionality for communicators across physical nodes.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_MDTYPES_PHYSICALNODECOMMUNICATOR_H
+#define GMX_MDTYPES_PHYSICALNODECOMMUNICATOR_H
+
+#include "gromacs/utility/gmxmpi.h"
+#include "gromacs/utility/unique_cptr.h"
+
+namespace gmx
+{
+
+/*! \brief Wrapper function for RAII-style cleanup.
+ *
+ * This is needed to discard the return value so it can be used as a
+ * deleter by a smart pointer. */
+void MPI_Comm_free_wrapper(MPI_Comm* comm);
+
+//! Make a smart pointer for MPI communicators.
+using MPI_Comm_ptr = gmx::unique_cptr<MPI_Comm, MPI_Comm_free_wrapper>;
+
+/*! \libinternal \brief Holds a communicator for the physical node of this rank
+ *
+ * This communicator should only be used for appropriate tasks,
+ * e.g. during initialization and finalization. It can contain ranks
+ * from PP, PME and multiple simulations with multisim, so is not
+ * suited for general-purpose communication. */
+class PhysicalNodeCommunicator
+{
+public:
+    /*! \brief Constructor.
+     *
+     * Communicates within \c world to make intra-communicator \c
+     * comm_ between all ranks that share \c physicalNodeId. */
+    PhysicalNodeCommunicator(MPI_Comm world, int physicalNodeId);
+    //! Communicator for all ranks on this physical node
+    MPI_Comm comm_;
+    //! Number of ranks on this physical node, corresponds to MPI_Comm_size of comm.
+    int size_;
+    //! Rank ID within this physical node, corresponds to MPI_Comm_rank of comm.
+    int rank_;
+    //! RAII handler for cleaning up \c comm_ only when appropriate.
+    MPI_Comm_ptr commGuard_;
+    //! Creates a barrier for all ranks on this physical node.
+    void barrier() const;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/pleasecite.h b/src/include/gromacs/utility/pleasecite.h
new file mode 100644 (file)
index 0000000..02e6f58
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares please_cite() for printing out literature references.
+ * Declares writeSourceDoi for printing of source code DOI.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_PLEASECITE_H
+#define GMX_UTILITY_PLEASECITE_H
+
+#include <cstdio>
+
+//! Print a message asking to cite something
+void please_cite(FILE* fp, const char* key);
+
+//! Write to \c fplog to request citation of GROMACS papers and source DOI.
+void pleaseCiteGromacs(FILE* fplog);
+
+#endif
diff --git a/src/include/gromacs/utility/programcontext.h b/src/include/gromacs/utility/programcontext.h
new file mode 100644 (file)
index 0000000..d681c95
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2013,2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::IProgramContext and related methods.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_PROGRAMCONTEXT_H
+#define GMX_UTILITY_PROGRAMCONTEXT_H
+
+namespace gmx
+{
+
+//! \addtogroup module_utility
+//! \{
+
+/*! \brief
+ * Provides information about installation prefix (see
+ * IProgramContext::installationPrefix()).
+ *
+ * \inpublicapi
+ */
+struct InstallationPrefixInfo
+{
+    //! Initializes the structure with given values.
+    InstallationPrefixInfo(const char* path, bool bSource) : path(path), bSourceLayout(bSource) {}
+
+    /*! \brief
+     * Path to the installation prefix of the current \Gromacs instance.
+     *
+     * If this is `NULL` or empty, data files cannot be looked up from the
+     * install tree and \Gromacs functions that access such files may fail.
+     * This can also contain a path to the source tree (see \a bSourceLayout).
+     */
+    const char* const path;
+    /*! \brief
+     * Whether \a path points to a source tree -like layout.
+     *
+     * For testing, it is useful to read data files from the source tree.
+     * For such cases, the program context can return the source tree root path
+     * in \a path, and set this to `true` to indicate that the data files
+     * should be searched using the layout of the source tree instead of the
+     * installation.
+     */
+    const bool bSourceLayout;
+};
+
+
+/*! \brief
+ * Provides context information about the program that is calling the library.
+ *
+ * This class provides access to information about the program that is
+ * currently running.  This information is used to provide better information
+ * to the user, and to locate the \Gromacs data files.
+ *
+ * setProgramContext() should be called before any other \Gromacs calls in
+ * a program (except for gmx::init()).  This avoids thread safety issues, and
+ * allows nicely formatted error messages.
+ *
+ * For thread safety, the implementations of the interface should ensure that
+ * the returned strings remain valid until the end of the program (see
+ * getProgramContext() for discussion on deinitialization).  Callers of the
+ * interface within \Gromacs are prepared for exceptions, but generally
+ * terminate the program on any exception.  Implementations should avoid
+ * exception except for truly fatal conditions.
+ *
+ * The destructor is protected to signify that the context is never destroyed
+ * through the interface.
+ *
+ * \see getProgramContext()
+ * \see setProgramContext()
+ * \inpublicapi
+ */
+class IProgramContext
+{
+public:
+    /*! \brief
+     * Returns the name of the binary as it was invoked without any path.
+     *
+     * This is typically `argv[0]` with any leading directory stripped.
+     * Currently, this should be a valid file name.
+     */
+    virtual const char* programName() const = 0;
+    /*! \brief
+     * Returns a display name for the program.
+     *
+     * For simple programs, this can equal programName().  For the \Gromacs
+     * `gmx` wrapper binary, this includes the name of the module (e.g.,
+     * `gmx angle`).  This is used only for informational purposes, and
+     * there are no constraints on contents, except that it should not be
+     * `NULL`.
+     */
+    virtual const char* displayName() const = 0;
+    /*! \brief
+     * Returns the full path of the running binary.
+     *
+     * This is mainly used for informational purposes.  There are no
+     * constraints on contents, except that it should not be `NULL`.
+     * Currently, this is also used for sanity checks in checkpointing.
+     *
+     * The implementation can provide an empty string if the path to the
+     * binary is not available.  In such a case, the information is not
+     * shown.
+     */
+    virtual const char* fullBinaryPath() const = 0;
+    /*! \brief
+     * Returns the installation prefix for \Gromacs.
+     *
+     * This path is used to locate the data files that are in `share/top/`
+     * in the source directory.
+     * The implementation can provide an empty string if the path is not
+     * available; in such a case, functions that require data files may
+     * fail.
+     *
+     * The returned structure also contains a flag to indicate whether the
+     * prefix actually points to the source tree.  This is used for tests
+     * and to support running binaries directly from the build tree.
+     */
+    virtual InstallationPrefixInfo installationPrefix() const = 0;
+    /*! \brief
+     * Returns the full command line used to invoke the binary.
+     *
+     * The implementation can provide an empty string if no command line is
+     * available.
+     */
+    virtual const char* commandLine() const = 0;
+
+protected:
+    virtual ~IProgramContext() {}
+};
+
+/*! \brief
+ * Returns the global IProgramContext instance.
+ *
+ * \returns The context set with setProgramContext().
+ *
+ * If nothing has been set with setProgramContext(), returns a default
+ * implementation that returns `"GROMACS"` for the program and display names,
+ * and empty strings for other values.
+ * The default implementation never throws.
+ *
+ * Does not throw.
+ *
+ * See setProgramContext() for thread safety notes.  You should not call this
+ * method in global deinitialization methods (e.g., destructors of global
+ * variables), since it is very difficult to clean up the state correctly in
+ * the presence of such calls.  For example, initForCommandLine() assumes that
+ * such calls do not exist to be able to free the context before exiting.
+ *
+ * \see IProgramContext
+ */
+const IProgramContext& getProgramContext();
+/*! \brief
+ * Sets the global IProgramContext instance.
+ *
+ * \param[in] context  Program context to set
+ *     (can be NULL to restore the default context).
+ *
+ * The library does not take ownership of \p context.
+ * The provided object must remain valid until the global instance is changed
+ * by another call to setProgramContext().
+ *
+ * This method is not thread-safe.  It must be the first call to the library
+ * after gmx::init(), and multi-threaded access is only supported after the
+ * call completes.  If \Gromacs is getting called from multiple threads, or
+ * uses multiple threads simultaneously, changing the program context is not
+ * supported once it is set.
+ * If the context is cleared at the end of the program, the caller must ensure
+ * that all other threads have been terminated at this point.
+ * These constraints simplify the implementation significantly.
+ *
+ * Does not throw.
+ *
+ * \see IProgramContext
+ */
+void setProgramContext(const IProgramContext* context);
+
+//! \}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/range.h b/src/include/gromacs/utility/range.h
new file mode 100644 (file)
index 0000000..d8f344a
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::Range
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_RANGE_H
+#define GMX_UTILITY_RANGE_H
+
+#include <type_traits>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/gmxassert.h"
+
+namespace gmx
+{
+
+/*! \brief Defines a range of integer numbers and accompanying operations
+ *
+ * Defines a range of integer type with begin and end.
+ * Can be used in a range loop over all integers in the range as in:
+ *
+ * Range<int> range(2,5);
+ * for (int i : range) { printf(" %d", i); }
+ *
+ * This will print: 2 3 4
+ */
+template<typename T>
+class Range
+{
+    // TODO: Use std::is_integral_v when CUDA 11 is a requirement.
+    static_assert(std::is_integral<T>::value, "Range can only be used with integral types");
+
+    // Note: This class has as invariant: begin_ <= end_
+
+public:
+    //! An iterator that loops over a range of integers
+    struct iterator
+    {
+        //! Constructor
+        iterator(T value) : value_(value) {}
+        //! Value
+        operator T() const { return value_; }
+        //! Reference
+        operator T&() { return value_; }
+        //! Pointer
+        T operator*() const { return value_; }
+        //! Inequality comparison
+        bool operator!=(const iterator other) { return value_ != other.value_; }
+        //! Increment operator
+        iterator& operator++()
+        {
+            ++value_;
+            return *this;
+        }
+        //! Increment operator
+        iterator operator++(int gmx_unused dummy)
+        {
+            iterator tmp(*this);
+            ++value_;
+            return tmp;
+        }
+        //! The actual value
+        T value_;
+    };
+
+    //! Constructor, has to be called with \p begin <= \p end (is checked)
+    Range(const T begin, const T end) : begin_(begin), end_(end)
+    {
+        GMX_RELEASE_ASSERT(begin_ <= end_, "A range should have begin<=end");
+    }
+
+    //! Default constructor, produces an empty range
+    Range() = default;
+
+    //! Begin iterator/value
+    iterator begin() const { return begin_; }
+    //! End iterator/value
+    iterator end() const { return end_; }
+
+    //! Returns the length of the range
+    T size() const { return end_ - begin_; }
+
+    //! Returns whether the range is empty
+    bool empty() const { return size() == 0; }
+
+    //! Returns whether \p value is in range
+    bool isInRange(const T value) const { return (begin_ <= value && value < end_); }
+
+private:
+    //! The start of the range
+    T begin_ = 0;
+    //! The end of the range
+    T end_ = 0;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/smalloc.h b/src/include/gromacs/utility/smalloc.h
new file mode 100644 (file)
index 0000000..2c0abc5
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019,2020, by the GROMACS development team.
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * C-style memory allocation routines for \Gromacs.
+ *
+ * This header provides macros snew(), srenew(), smalloc(), and sfree() for
+ * C-style memory management.  Additionally, snew_aligned() and sfree_aligned() are
+ * provided for managing memory with a specified byte alignment.
+ *
+ * If an allocation fails, the program is halted by calling gmx_fatal(), which
+ * outputs source file and line number and the name of the variable involved.
+ * This frees calling code from the trouble of checking the result of the
+ * allocations everywhere.  It also provides a location for centrally logging
+ * memory allocations for diagnosing memory usage (currently can only enabled
+ * by changing the source code).  Additionally, sfree() works also with a
+ * `NULL` parameter, which standard free() does not.
+ *
+ * The macros forward the calls to functions save_malloc(), save_calloc(),
+ * save_realloc(), save_free(), save_calloc_aligned(), and save_free_aligned().
+ * There are a few low-level locations in \Gromacs that call these directly,
+ * but generally the macros should be used.
+ * save_malloc_aligned() exists for this purpose, although there is no macro to
+ * invoke it.
+ *
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_SMALLOC_H
+#define GMX_UTILITY_SMALLOC_H
+
+#include <stddef.h>
+
+/*! \brief
+ * \Gromacs wrapper for malloc().
+ *
+ * \param[in] name   Variable name identifying the allocation.
+ * \param[in] file   Source code file where the allocation originates from.
+ * \param[in] line   Source code line where the allocation originates from.
+ * \param[in] size   Number of bytes to allocate.
+ * \returns   Pointer to the allocated space.
+ *
+ * This should generally be called through smalloc(), not directly.
+ */
+void* save_malloc(const char* name, const char* file, int line, size_t size);
+/*! \brief
+ * \Gromacs wrapper for calloc().
+ *
+ * \param[in] name   Variable name identifying the allocation.
+ * \param[in] file   Source code file where the allocation originates from.
+ * \param[in] line   Source code line where the allocation originates from.
+ * \param[in] nelem  Number of elements to allocate.
+ * \param[in] elsize Number of bytes per element.
+ * \returns   Pointer to the allocated space.
+ *
+ * This should generally be called through snew(), not directly.
+ */
+void* save_calloc(const char* name, const char* file, int line, size_t nelem, size_t elsize);
+/*! \brief
+ * \Gromacs wrapper for realloc().
+ *
+ * \param[in] name   Variable name identifying the allocation.
+ * \param[in] file   Source code file where the allocation originates from.
+ * \param[in] line   Source code line where the allocation originates from.
+ * \param[in] ptr    Pointer to the previously allocated memory (can be NULL).
+ * \param[in] nelem  Number of elements to allocate.
+ * \param[in] elsize Number of bytes per element.
+ * \returns   Pointer to the allocated space.
+ *
+ * As with realloc(), if \p ptr is NULL, memory is allocated as if malloc() was
+ * called.
+ * This should generally be called through srenew(), not directly.
+ *
+ * Note that the allocated memory is not initialized to zero.
+ */
+void* save_realloc(const char* name, const char* file, int line, void* ptr, size_t nelem, size_t elsize);
+/*! \brief
+ * \Gromacs wrapper for free().
+ *
+ * \param[in] name   Variable name identifying the deallocation.
+ * \param[in] file   Source code file where the deallocation originates from.
+ * \param[in] line   Source code line where the deallocation originates from.
+ * \param[in] ptr    Pointer to the allocated memory (can be NULL).
+ *
+ * If \p ptr is NULL, does nothing.
+ * This should generally be called through sfree(), not directly.
+ * This never fails.
+ */
+void save_free(const char* name, const char* file, int line, void* ptr);
+
+/*! \brief
+ * \Gromacs wrapper for allocating aligned memory.
+ *
+ * \param[in] name   Variable name identifying the allocation.
+ * \param[in] file   Source code file where the allocation originates from.
+ * \param[in] line   Source code line where the allocation originates from.
+ * \param[in] nelem  Number of elements to allocate.
+ * \param[in] elsize Number of bytes per element.
+ * \param[in] alignment Requested alignment in bytes.
+ * \returns   Pointer to the allocated space, aligned at `alignment`-byte
+ *     boundary.
+ *
+ * There is no macro that invokes this function.
+ *
+ * The returned pointer should only be freed with a call to save_free_aligned().
+ */
+void* save_malloc_aligned(const char* name, const char* file, int line, size_t nelem, size_t elsize, size_t alignment);
+/*! \brief
+ * \Gromacs wrapper for allocating zero-initialized aligned memory.
+ *
+ * \param[in] name   Variable name identifying the allocation.
+ * \param[in] file   Source code file where the allocation originates from.
+ * \param[in] line   Source code line where the allocation originates from.
+ * \param[in] nelem  Number of elements to allocate.
+ * \param[in] elsize Number of bytes per element.
+ * \param[in] alignment Requested alignment in bytes.
+ * \returns   Pointer to the allocated space, aligned at `alignment`-byte
+ *     boundary.
+ *
+ * This should generally be called through snew_aligned(), not directly.
+ *
+ * The returned pointer should only be freed with a call to save_free_aligned().
+ */
+void* save_calloc_aligned(const char* name, const char* file, int line, size_t nelem, size_t elsize, size_t alignment);
+/*! \brief
+ * \Gromacs wrapper for freeing aligned memory.
+ *
+ * \param[in] name   Variable name identifying the deallocation.
+ * \param[in] file   Source code file where the deallocation originates from.
+ * \param[in] line   Source code line where the deallocation originates from.
+ * \param[in] ptr    Pointer to the allocated memory (can be NULL).
+ *
+ * If \p ptr is NULL, does nothing.
+ * \p ptr should have been allocated with save_malloc_aligned() or
+ * save_calloc_aligned().
+ * This should generally be called through sfree_aligned(), not directly.
+ * This never fails.
+ */
+void save_free_aligned(const char* name, const char* file, int line, void* ptr);
+
+#include <type_traits>
+
+/*! \cond internal */
+/*! \name Implementation templates for C++ memory allocation macros
+ *
+ * These templates are used to implement the snew() etc. macros for C++, where
+ * an explicit cast is needed from `void *` (the return value of the allocation
+ * wrapper functions) to the type of \p ptr.
+ *
+ * Having these as `static` avoid some obscure bugs if several files define
+ * distinct data structures with identical names and allocate memory for them
+ * using snew().  By the C++ standard, such declarations cause undefined
+ * behavior, but can be difficult to spot in the existing C code.
+ * Without the `static` (and if the compiler does not inline the calls), the
+ * linker cannot that data structures with identical names are actually
+ * different and links calls to these template functions incorrectly, which can
+ * result in allocation of an incorrect amount of memory if the element size is
+ * computed within the function.
+ *
+ * The size cannot be passed as a parameter to the function either, since that
+ * provokes warnings from cppcheck for some invocations, where a complex
+ * expression is passed as \p ptr.
+ */
+/*! \{ */
+/** C++ helper for snew(). */
+template<typename T>
+static inline void gmx_snew_impl(const char* name, const char* file, int line, T*& ptr, size_t nelem)
+{
+    // TODO: Use std::is_pod_v when CUDA 11 is a requirement.
+    static_assert(std::is_pod<T>::value, "snew() called on C++ type");
+    // NOLINTNEXTLINE bugprone-sizeof-expression
+    ptr = static_cast<T*>(save_calloc(name, file, line, nelem, sizeof(T)));
+}
+/** C++ helper for srenew(). */
+template<typename T>
+static inline void gmx_srenew_impl(const char* name, const char* file, int line, T*& ptr, size_t nelem)
+{
+    // TODO: Use std::is_pod_v when CUDA 11 is a requirement.
+    static_assert(std::is_pod<T>::value, "srenew() called on C++ type");
+    // NOLINTNEXTLINE bugprone-sizeof-expression
+    ptr = static_cast<T*>(save_realloc(name, file, line, ptr, nelem, sizeof(T)));
+}
+/** C++ helper for smalloc(). */
+template<typename T>
+static inline void gmx_smalloc_impl(const char* name, const char* file, int line, T*& ptr, size_t size)
+{
+    // TODO: Use std::is_pod_v when CUDA 11 is a requirement.
+    static_assert(std::is_pod<T>::value, "smalloc() called on C++ type");
+    ptr = static_cast<T*>(save_malloc(name, file, line, size));
+}
+/** C++ helper for snew_aligned(). */
+template<typename T>
+static inline void
+gmx_snew_aligned_impl(const char* name, const char* file, int line, T*& ptr, size_t nelem, size_t alignment)
+{
+    // TODO: Use std::is_pod_v when CUDA 11 is a requirement.
+    static_assert(std::is_pod<T>::value, "snew_aligned() called on C++ type");
+    ptr = static_cast<T*>(save_calloc_aligned(name, file, line, nelem, sizeof(T), alignment));
+}
+/** C++ helper for sfree(). */
+template<typename T>
+static inline void gmx_sfree_impl(const char* name, const char* file, int line, T* ptr)
+{
+    // TODO: Use std::is_pod_v and std::is_void_v when CUDA 11 is a requirement.
+    static_assert(std::is_pod<T>::value || std::is_void<T>::value, "sfree() called on C++ type");
+    save_free(name, file, line, ptr);
+}
+/** C++ helper for sfree_aligned(). */
+template<typename T>
+static inline void gmx_sfree_aligned_impl(const char* name, const char* file, int line, T* ptr)
+{
+    // TODO: Use std::is_pod_v and std::is_void_v when CUDA 11 is a requirement.
+    static_assert(std::is_pod<T>::value || std::is_void<T>::value,
+                  "sfree_aligned() called on C++ type");
+    save_free_aligned(name, file, line, ptr);
+}
+/*! \} */
+/*! \endcond */
+
+/*! \def snew
+ * \brief
+ * Allocates memory for a given number of elements.
+ *
+ * \param[out] ptr   Pointer to allocate.
+ * \param[in]  nelem Number of elements to allocate.
+ *
+ * Allocates memory for \p nelem elements of type \p *ptr and sets this to
+ * \p ptr.  The allocated memory is initialized to zeros.
+ *
+ * \hideinitializer
+ */
+/*! \def srenew
+ * \brief
+ * Reallocates memory for a given number of elements.
+ *
+ * \param[in,out] ptr   Pointer to allocate/reallocate.
+ * \param[in]     nelem Number of elements to allocate.
+ *
+ * (Re)allocates memory for \p ptr such that it can hold \p nelem elements of
+ * type \p *ptr, and sets the new pointer to \p ptr.
+ * If \p ptr is `NULL`, memory is allocated as if it was new.
+ * If \p nelem is zero, \p ptr is freed (if not `NULL`).
+ * Note that the allocated memory is not initialized, unlike with snew().
+ *
+ * \hideinitializer
+ */
+/*! \def smalloc
+ * \brief
+ * Allocates memory for a given number of bytes.
+ *
+ * \param[out] ptr  Pointer to allocate.
+ * \param[in]  size Number of bytes to allocate.
+ *
+ * Allocates memory for \p size bytes and sets this to \p ptr.
+ * The allocated memory is initialized to zero.
+ *
+ * \hideinitializer
+ */
+/*! \def snew_aligned
+ * \brief
+ * Allocates aligned memory for a given number of elements.
+ *
+ * \param[out] ptr       Pointer to allocate.
+ * \param[in]  nelem     Number of elements to allocate.
+ * \param[in]  alignment Requested alignment in bytes.
+ *
+ * Allocates memory for \p nelem elements of type \p *ptr and sets this to
+ * \p ptr.  The returned pointer is `alignment`-byte aligned.
+ * The allocated memory is initialized to zeros.
+ *
+ * The returned pointer should only be freed with sfree_aligned().
+ *
+ * \hideinitializer
+ */
+/*! \def sfree
+ * \brief
+ * Frees memory referenced by \p ptr.
+ *
+ * \p ptr is allowed to be NULL, in which case nothing is done.
+ *
+ * \hideinitializer
+ */
+/*! \def sfree_aligned
+ * \brief
+ * Frees aligned memory referenced by \p ptr.
+ *
+ * This must only be called with a pointer obtained through snew_aligned().
+ * \p ptr is allowed to be NULL, in which case nothing is done.
+ *
+ * \hideinitializer
+ */
+
+/* C++ implementation */
+#define snew(ptr, nelem) gmx_snew_impl(#ptr, __FILE__, __LINE__, (ptr), (nelem))
+#define srenew(ptr, nelem) gmx_srenew_impl(#ptr, __FILE__, __LINE__, (ptr), (nelem))
+#define smalloc(ptr, size) gmx_smalloc_impl(#ptr, __FILE__, __LINE__, (ptr), (size))
+#define snew_aligned(ptr, nelem, alignment) \
+    gmx_snew_aligned_impl(#ptr, __FILE__, __LINE__, (ptr), (nelem), alignment)
+#define sfree(ptr) gmx_sfree_impl(#ptr, __FILE__, __LINE__, (ptr))
+#define sfree_aligned(ptr) gmx_sfree_aligned_impl(#ptr, __FILE__, __LINE__, (ptr))
+
+/*! \brief
+ * Over allocation factor for memory allocations.
+ *
+ * Memory (re)allocation can be VERY slow, especially with some
+ * MPI libraries that replace the standard malloc and realloc calls.
+ * To avoid slow memory allocation we use over_alloc to set the memory
+ * allocation size for large data blocks. Since this scales the size
+ * with a factor, we use log(n) realloc calls instead of n.
+ * This can reduce allocation times from minutes to seconds.
+ *
+ * This factor leads to 4 realloc calls to double the array size.
+ */
+constexpr float OVER_ALLOC_FAC = 1.19F;
+
+/*! \brief
+ * Turns over allocation for variable size atoms/cg/top arrays on or off,
+ * default is off.
+ *
+ * \todo
+ * This is mdrun-specific, so it might be better to put this and
+ * over_alloc_dd() much higher up.
+ */
+void set_over_alloc_dd(bool set);
+
+/*! \brief
+ * Returns new allocation count for domain decomposition allocations.
+ *
+ * Returns n when domain decomposition over allocation is off.
+ * Returns OVER_ALLOC_FAC*n + 100 when over allocation in on.
+ * This is to avoid frequent reallocation during domain decomposition in mdrun.
+ */
+int over_alloc_dd(int n);
+
+/** Over allocation for small data types: int, real etc. */
+template<typename T>
+constexpr T over_alloc_small(T n)
+{
+    return OVER_ALLOC_FAC * n + 8000;
+}
+
+/** Over allocation for large data types: complex structs */
+template<typename T>
+constexpr T over_alloc_large(T n)
+{
+    return OVER_ALLOC_FAC * n + 1000;
+}
+
+#endif
diff --git a/src/include/gromacs/utility/snprintf.h b/src/include/gromacs/utility/snprintf.h
new file mode 100644 (file)
index 0000000..94900a9
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
+ * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Provide snprintf symbol on all OS (for internal Gromacs use)
+ *
+ * \todo When all callers of snprintf compile as C++, perhaps use
+ * gmx::formatString() everywhere instead of snprintf.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_SNPRINTF_H
+#define GMX_UTILITY_SNPRINTF_H
+
+#include "config.h"
+
+#include <stdio.h>
+
+#if GMX_NATIVE_WINDOWS
+#    define snprintf _snprintf
+#endif
+
+#endif
diff --git a/src/include/gromacs/utility/strconvert.h b/src/include/gromacs/utility/strconvert.h
new file mode 100644 (file)
index 0000000..2af389f
--- /dev/null
@@ -0,0 +1,323 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017,2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares common utility functions for conversions to and from strings.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_STRCONVERT_H
+#define GMX_UTILITY_STRCONVERT_H
+
+#include <algorithm>
+#include <array>
+#include <optional>
+#include <string>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/stringutil.h"
+
+namespace gmx
+{
+
+//! \cond libapi
+//! \addtogroup module_utility
+//! \{
+
+/*! \brief
+ * Parses a boolean from a string.
+ *
+ * \throws  InvalidInputError if `str` is not recognized as a boolean value.
+ */
+bool boolFromString(const char* str);
+/*! \brief
+ * Parses an integer from a string.
+ *
+ * \throws  InvalidInputError if `str` is not a valid integer.
+ *
+ * Also checks for overflow.
+ */
+int intFromString(const char* str);
+/*! \brief
+ * Parses a 64-bit integer from a string.
+ *
+ * \throws  InvalidInputError if `str` is not a valid integer.
+ *
+ * Also checks for overflow.
+ */
+int64_t int64FromString(const char* str);
+/*! \brief
+ * Parses a float value from a string.
+ *
+ * \throws  InvalidInputError if `str` is not a valid number.
+ *
+ * Also checks for overflow.
+ */
+float floatFromString(const char* str);
+/*! \brief
+ * Parses a double value from a string.
+ *
+ * \throws  InvalidInputError if `str` is not a valid number.
+ *
+ * Also checks for overflow.
+ */
+double doubleFromString(const char* str);
+
+/*! \brief
+ * Parses a value from a string to a given type.
+ *
+ * \tparam T Type of value to parse.
+ *
+ * `T` can only be one of the types that is explicity supported.
+ * The main use for this function is to write `fromString<real>(value)`,
+ * but it can also be used for other types for consistency.
+ */
+template<typename T>
+static inline T fromString(const char* str);
+//! \copydoc fromString(const char *)
+template<typename T>
+static inline T fromString(const std::string& str)
+{
+    return fromString<T>(str.c_str());
+}
+/*! \copydoc fromString(const char *)
+ *
+ * Provided for situations where overload resolution cannot easily resolve the
+ * desired std::string parameter.
+ */
+template<typename T>
+static inline T fromStdString(const std::string& str)
+{
+    return fromString<T>(str.c_str());
+}
+
+//! Implementation for boolean values.
+template<>
+inline bool fromString<bool>(const char* str)
+{
+    return boolFromString(str);
+}
+//! Implementation for integer values.
+template<>
+inline int fromString<int>(const char* str)
+{
+    return intFromString(str);
+}
+//! Implementation for 64-bit integer values.
+template<>
+inline int64_t fromString<int64_t>(const char* str)
+{
+    return int64FromString(str);
+}
+//! Implementation for float values.
+template<>
+inline float fromString<float>(const char* str)
+{
+    return floatFromString(str);
+}
+//! Implementation for double values.
+template<>
+inline double fromString<double>(const char* str)
+{
+    return doubleFromString(str);
+}
+
+/*! \brief
+ * Converts a boolean to a "true"/"false" string.
+ *
+ * Does not throw.
+ */
+static inline const char* boolToString(bool value)
+{
+    return value ? "true" : "false";
+}
+/*! \brief
+ * Returns a string containing the value of \c t.
+ *
+ * \throws std::bad_alloc if out of memory.
+ */
+static inline std::string intToString(int t)
+{
+    return formatString("%d", t);
+}
+//! \copydoc intToString(int)
+static inline std::string int64ToString(int64_t t)
+{
+    return formatString("%" PRId64, t);
+}
+//! \copydoc intToString(int)
+static inline std::string doubleToString(double t)
+{
+    return formatString("%g", t);
+}
+
+/*! \name
+ * Overloads for converting a value of a given type to a string.
+ *
+ * \throws std::bad_alloc if out of memory.
+ * \{
+ */
+static inline std::string toString(bool t)
+{
+    return boolToString(t);
+}
+static inline std::string toString(int t)
+{
+    return intToString(t);
+}
+static inline std::string toString(int64_t t)
+{
+    return int64ToString(t);
+}
+static inline std::string toString(float t)
+{
+    return doubleToString(t);
+}
+static inline std::string toString(double t)
+{
+    return doubleToString(t);
+}
+static inline std::string toString(std::string t)
+{
+    return t;
+}
+//! \}
+
+//! \}
+//! \endcond
+
+/*! \brief Convert a string into an array of values.
+ *
+ * \tparam ValueType array element type to convert into
+ * \tparam NumExpectedValues number of values of the array
+ *
+ * \returns an array containing the converted string, optionally null if
+ *          the white-space stripped string is empty
+ *
+ * \throws InvalidInputError if splitting the string at whitespaces does not
+ *                           result in NumExpectedValues or zero substrings
+ *
+ * \throws InvalidInputError if conversion of any of the NumExpectedValues
+ *                           substrings of the splitted input string fails
+ *
+ * Converts a string into an array of type ValueType with exactly NumExpectedValues.
+ *
+ * No result is returned if the string is empty or contains only whitespace .
+ *
+ */
+template<typename ValueType, int NumExpectedValues>
+static inline std::optional<std::array<ValueType, NumExpectedValues>>
+parsedArrayFromInputString(const std::string& str)
+{
+    // return nullopt right away if the string is just whitespace or empty
+    {
+        const std::string& strippedString = stripString(str);
+        if (strippedString.empty())
+        {
+            return std::nullopt;
+        }
+    }
+
+    const std::vector<std::string>& valuesAsStrings = splitString(str);
+
+    // throw right away if we don't have the expected number of string entries
+    if (valuesAsStrings.size() != NumExpectedValues)
+    {
+        const std::string errorMessage =
+                "Expected empty string or string with " + intToString(NumExpectedValues)
+                + " elements to convert, but received " + intToString(valuesAsStrings.size())
+                + " elements instead.";
+        GMX_THROW(InvalidInputError(errorMessage));
+    }
+
+    // will throw if any conversion from string to value fails
+    std::array<ValueType, NumExpectedValues> valuesAsArray;
+    std::transform(std::begin(valuesAsStrings),
+                   std::end(valuesAsStrings),
+                   std::begin(valuesAsArray),
+                   [](const std::string& split) { return fromString<ValueType>(split); });
+
+    return { valuesAsArray };
+}
+
+/*! \brief Returns the input string, throwing an excpetion if the demanded
+ *         conversion to an array will not succeed.
+ *
+ * \tparam ValueType array element type to convert into
+ * \tparam NumExpectedValues number of values of the array
+ *
+ * \param[in] toConvert the string to convert
+ * \param[in] errorContextMessage the message to add to the thrown exceptions if
+ *                                conversion of the string is bound to fail at
+ *                                some point
+ * \returns the input string
+ *
+ * \throws InvalidInputError if splitting the string at whitespaces does not
+ *                           result in NumExpectedValues or zero substrings
+ *
+ * \throws InvalidInputError if conversion of any of the NumExpectedValues
+ *                           substrings of the splitted input string fails
+ *
+ * A typical use of this function would be in .mdp string option parsing
+ * where information in the .mdp file is transformed into the data that is
+ * stored in the .tpr file.
+ */
+template<typename ValueType, int NumExpectedValues>
+static inline std::string stringIdentityTransformWithArrayCheck(const std::string& toConvert,
+                                                                const std::string& errorContextMessage)
+{
+    // Attempt the conversion to an array so that the string parsing routine
+    // will throw an InvalidInputError if the string is not fit for conversion.
+    try
+    {
+        // The converted array is discarded.
+        gmx_unused const auto& val = parsedArrayFromInputString<ValueType, NumExpectedValues>(toConvert);
+    }
+    catch (const InvalidInputError& e)
+    {
+        InvalidInputError toThrow(errorContextMessage + std::string(e.what()));
+        GMX_THROW(toThrow);
+    }
+
+    return toConvert;
+}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/strdb.h b/src/include/gromacs/utility/strdb.h
new file mode 100644 (file)
index 0000000..13165f4
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2010,2014,2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares C functions for reading files with a list of strings.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_STRDB_H
+#define GMX_UTILITY_STRDB_H
+
+#include <cstdio>
+
+#include "gromacs/utility/basedefinitions.h"
+
+/*! \brief
+ * Reads a line of at most n characters from *fp to line.
+ *
+ * Comment ';...' and leading spaces are removed, empty lines are skipped.
+ * Return FALSE when eof.
+ */
+gmx_bool get_a_line(FILE* fp, char line[], int n);
+
+/*! \brief
+ * Read a header between '[' and ']' from line to header.
+ *
+ * Returns FALSE if no header is found.
+ */
+gmx_bool get_header(char line[], char header[]);
+
+/*! \brief
+ * Opens file db, or if non-existant file $GMXLIB/db and read strings.
+ *
+ * First line in the file needs to specify the number of strings following.
+ * Returns the number of strings.
+ */
+int get_lines(const char* db, char*** strings);
+
+/*! \brief
+ * Searches an array of strings for key, return the index if found.
+ *
+ * Returns -1 if not found.
+ */
+int search_str(int nstr, char** str, char* key);
+
+#endif
diff --git a/src/include/gromacs/utility/stringcompare.h b/src/include/gromacs/utility/stringcompare.h
new file mode 100644 (file)
index 0000000..9aad208
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares utility functionst for string comparison.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_STRINGCOMPARE_H
+#define GMX_UTILITY_STRINGCOMPARE_H
+
+#include <string>
+
+#include "gromacs/utility/cstringutil.h"
+
+namespace gmx
+{
+
+//! \cond libapi
+/*! \brief
+ * Specifies how strings should be compared in various contexts.
+ *
+ * \ingroup module_utility
+ */
+enum class StringCompareType
+{
+    //! Only exact matches are accepted.
+    Exact,
+    //! Case-insensitive comparison.
+    CaseInsensitive,
+    //! Case-insensitive comparison that also ignores '-' and '_'.
+    CaseAndDashInsensitive
+};
+//! \endcond
+
+/*! \libinternal \brief
+ * Compare object for std::string STL containers and algorithms that supports
+ * run-time decision on how to compare.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class StringCompare
+{
+public:
+    /*! \brief
+     * Creates a comparer with the given type
+     *
+     * This is not explicit, which allows passing \ref StringCompareType
+     * directly to, e.g., `std::map` constructors.
+     */
+    StringCompare(StringCompareType type = StringCompareType::Exact) : type_(type) {}
+
+    //! The comparison operation.
+    bool operator()(const std::string& a, const std::string& b) const
+    {
+        switch (type_)
+        {
+            case StringCompareType::Exact: return a < b;
+            case StringCompareType::CaseInsensitive:
+                return gmx_strcasecmp(a.c_str(), b.c_str()) < 0;
+            case StringCompareType::CaseAndDashInsensitive:
+                return gmx_strcasecmp_min(a.c_str(), b.c_str()) < 0;
+        }
+        return a < b;
+    }
+
+private:
+    StringCompareType type_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/stringstream.h b/src/include/gromacs/utility/stringstream.h
new file mode 100644 (file)
index 0000000..9912b92
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares implementations for textstream.h interfaces for input/output to
+ * in-memory strings.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_STRINGSTREAM_H
+#define GMX_UTILITY_STRINGSTREAM_H
+
+#include <string>
+#include <vector>
+
+#include "gromacs/utility/textstream.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Text output stream implementation for writing to an in-memory string.
+ *
+ * Implementations for the TextOutputStream methods throw std::bad_alloc if
+ * reallocation of the string fails.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class StringOutputStream : public TextOutputStream
+{
+public:
+    //! Returns the text written to the stream so far.
+    const std::string& toString() const { return str_; }
+
+    // From TextOutputStream
+    void write(const char* text) override;
+    void close() override;
+
+private:
+    std::string str_;
+};
+
+template<typename T>
+class ArrayRef;
+
+/*! \libinternal \brief
+ * Helper class to convert static string data to a stream.
+ *
+ * Provides a text input stream that returns lines from a string
+ */
+class StringInputStream : public TextInputStream
+{
+public:
+    /*! \brief
+     * Constructor that stores input lines in a string.
+     *
+     * The string is internally but no processing is done.
+     *
+     * \param[in] input String to be served by the stream.
+     */
+    explicit StringInputStream(const std::string& input);
+    /*! \brief
+     * Constructor that stores input lines in a string.
+     *
+     * The vector of strings is stored as a string separated by newline.
+     *
+     * \param[in] input String to be served by the stream.
+     */
+    explicit StringInputStream(const std::vector<std::string>& input);
+    /*! \brief
+     * Constructor that stores input lines in a string.
+     *
+     * The array of char * is stored as a string separated by newline.
+     *
+     * \param[in] input Array of char * to be served by the stream.
+     */
+    explicit StringInputStream(ArrayRef<const char* const> const& input);
+
+    // From TextInputStream
+    bool readLine(std::string* line) override;
+    void close() override {}
+
+private:
+    std::string input_;
+    size_t      pos_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/stringtoenumvalueconverter.h b/src/include/gromacs/utility/stringtoenumvalueconverter.h
new file mode 100644 (file)
index 0000000..bc45039
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief Defines helper function object for class enumerations
+ *
+ * This helper type facilitates efficient lookup of values from
+ * an enumeration identified by a string, given a pre-declared mapping of
+ * values to such strings.
+ *
+ * It is separated from enumerationhelpers.h because it is not as
+ * widely used and brings in several extra dependencies not needed by
+ * that header.
+ *
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_STRINGTOENUMVALUECONVERTER_H
+#define GMX_UTILITY_STRINGTOENUMVALUECONVERTER_H
+
+#include <map>
+#include <optional>
+#include <string>
+
+#include "enumerationhelpers.h"
+#include "stringcompare.h"
+#include "stringutil.h"
+
+namespace gmx
+{
+
+/*! \brief Enum class for whether StringToEnumValueConverter will strip strings
+ * of leading and trailing whitespace before comparison. */
+enum class StripStrings : int
+{
+    //! Do not strip strings
+    No,
+    //! Strip strings
+    Yes
+};
+
+/*! \brief A class to convert a string to an enum value of type \c EnumType.
+ *
+ * It can be configured:
+ *  - to match different enumerations,
+ *  - to convert enumerations to strings in a custom way,
+ *  - either strip strings of leading and trailing whitespace before
+ *    comparison or not,
+ *  - with different handling of how the string characters are compared.
+ *
+ * Usage example:
+ *
+ *    enum class Foo : int {
+ *       Fizz, Buzz, Count, Default = Fizz
+ *    };
+ *    StringToEnumValueConverter<Foo, enumValueToString> converter;
+ *    Foo type = converter.valueFrom(theString);
+ *
+ * \tparam EnumType                    A class enum for which enumValueToString
+ *                                     is defined and maps all values
+ *                                     (except EnumType::Count) to a string.
+ * \tparam enumValueToStringFunction   Function to convert EnumValue to string, which
+ *                                     is typically enumValueToString, per convention
+ * \tparam stringCompareType           Indicates how the string should be compared
+ *                                     with respect to case, hyphens, underscores, etc.
+ * \tparam stripStrings                Indicates whether strings should have leading
+ *                                     and trailing whitespace removed before comparison
+ */
+template<typename EnumType,
+         const char*(enumValueToStringFunction)(EnumType),
+         StringCompareType stringCompareType = StringCompareType::Exact,
+         StripStrings      stripStrings      = StripStrings::No>
+class StringToEnumValueConverter
+{
+public:
+    StringToEnumValueConverter() : stringToEnumValue_(stringCompareType)
+    {
+        for (const auto type : EnumerationWrapper<EnumType>{})
+        {
+            GMX_RELEASE_ASSERT(type != EnumType::Count,
+                               "EnumerationWrapper<EnumType> should never return EnumType::Count");
+            std::string stringFromType = enumValueToStringFunction(type);
+            if (stripStrings == StripStrings::Yes)
+            {
+                // Ensure leading and trailing spaces are removed
+                stringFromType = stripString(stringFromType);
+            }
+            stringToEnumValue_[stringFromType] = type;
+        }
+    }
+
+    //! Return an optional enum value identified from the \c s (which is never EnumType::Count)
+    std::optional<EnumType> valueFrom(const std::string& s) const
+    {
+        typename decltype(stringToEnumValue_)::const_iterator typeIt;
+        if (stripStrings == StripStrings::Yes)
+        {
+            // Ensure leading and trailing spaces are removed
+            typeIt = stringToEnumValue_.find(stripString(s));
+        }
+        else
+        {
+            typeIt = stringToEnumValue_.find(s);
+        }
+        return (typeIt != stringToEnumValue_.end()) ? std::make_optional(typeIt->second) : std::nullopt;
+    }
+
+private:
+    /*! \brief Map of strings to enumeration values.
+     *
+     * By construction, those values never include
+     * EnumType::Count. */
+    std::map<std::string, EnumType, StringCompare> stringToEnumValue_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/stringutil.h b/src/include/gromacs/utility/stringutil.h
new file mode 100644 (file)
index 0000000..e3a2680
--- /dev/null
@@ -0,0 +1,756 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2011-2018, The GROMACS development team.
+ * Copyright (c) 2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares common string utility and formatting routines.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inpublicapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_STRINGUTIL_H
+#define GMX_UTILITY_STRINGUTIL_H
+
+#include <cstdarg>
+#include <cstring>
+
+#include <string>
+#include <vector>
+
+namespace gmx
+{
+
+//! \addtogroup module_utility
+//! \{
+
+/*! \brief
+ * Tests whether a string is null or empty.
+ *
+ * Does not throw.
+ */
+static inline bool isNullOrEmpty(const char* str)
+{
+    return str == nullptr || str[0] == '\0';
+}
+
+/*! \brief
+ * Tests whether a string starts with another string.
+ *
+ * \param[in] str    String to process.
+ * \param[in] prefix Prefix to find.
+ * \returns   true if \p str starts with \p prefix.
+ *
+ * Returns true if \p prefix is empty.
+ * Does not throw.
+ */
+static inline bool startsWith(const std::string& str, const std::string& prefix)
+{
+    return str.compare(0, prefix.length(), prefix) == 0;
+}
+//! \copydoc startsWith(const std::string &, const std::string &)
+static inline bool startsWith(const char* str, const char* prefix)
+{
+    return std::strncmp(str, prefix, std::strlen(prefix)) == 0;
+}
+
+/*! \brief
+ * Tests whether a string ends with another string.
+ *
+ * \param[in] str    String to process.
+ * \param[in] suffix Suffix to find.
+ * \returns   true if \p str ends with \p suffix.
+ *
+ * Returns true if \p suffix is NULL or empty.
+ * Does not throw.
+ */
+bool endsWith(const char* str, const char* suffix);
+//! \copydoc endsWith(const char *, const char *)
+static inline bool endsWith(const std::string& str, const char* suffix)
+{
+    return endsWith(str.c_str(), suffix);
+}
+
+/*! \brief
+ * Tests whether a string contains another as a substring.
+ *
+ * \param[in] str    String to process.
+ * \param[in] substr Substring to find.
+ * \returns   true if \p str contains \p substr.
+ *
+ * Does not throw.
+ */
+static inline bool contains(const std::string& str, const char* substr)
+{
+    return str.find(substr) != std::string::npos;
+}
+//! \copydoc contains(const std::string &str, const char *substr)
+static inline bool contains(const std::string& str, const std::string& substr)
+{
+    return str.find(substr) != std::string::npos;
+}
+
+/*!\brief Returns number of space-separated words in zero-terminated char ptr
+ *
+ * \param s Character pointer to zero-terminated, which will not be changed.
+ *
+ * \returns number of words in string.
+ *
+ * \note This routine is mainly meant to support legacy code in GROMACS. For
+ *       new source you should try hard to use C++ string objects instead.
+ */
+std::size_t countWords(const char* s);
+
+/*!\brief Returns the number of space-separated words in a string object
+ *
+ * \param str Reference to string object, which will not be changed.
+ *
+ * \returns number of words in string.
+ */
+std::size_t countWords(const std::string& str);
+
+//! \copydoc endsWith(const std::string &str, const char *suffix)
+static inline bool endsWith(const std::string& str, const std::string& suffix)
+{
+    return endsWith(str, suffix.c_str());
+}
+
+/*! \brief
+ * Removes a suffix from a string.
+ *
+ * \param[in] str    String to process.
+ * \param[in] suffix Suffix to remove.
+ * \returns   \p str with \p suffix removed, or \p str unmodified if it does
+ *      not end with \p suffix.
+ * \throws    std::bad_alloc if out of memory.
+ *
+ * Returns \p str if \p suffix is NULL or empty.
+ */
+std::string stripSuffixIfPresent(const std::string& str, const char* suffix);
+/*! \brief
+ * Removes leading and trailing whitespace from a string.
+ *
+ * \param[in] str  String to process.
+ * \returns   \p str with leading and trailing whitespaces removed.
+ * \throws    std::bad_alloc if out of memory.
+ */
+std::string stripString(const std::string& str);
+#ifdef __GNUC__
+#    define gmx_format(archetype, string_index, first_to_check) \
+        __attribute__((format(archetype, string_index, first_to_check)))
+#else
+/*! \brief GCC like function format attribute
+ *
+ * The format attribute specifies that a function takes printf, scanf, ...
+ * style arguments that should be type-checked against a format string.
+ * The attribute has to be placed after the function.
+ * This attribute is only valid for function declarations and not function
+ * definitions (GCC limitation). For member functions the implicit `this`
+ * pointer is included in the argument count.
+ */
+#    define gmx_format(archetype, string_index, first_to_check)
+#endif
+#ifdef _MSC_VER
+#    define gmx_fmtstr _In_ _Printf_format_string_
+#else
+/*! \brief MSVC like function format attribute
+ *
+ * Does type checking for printf like format strings in MSVC style.
+ * Attribute has to be placed before format string.
+ */
+#    define gmx_fmtstr
+#endif
+/*! \brief
+ * Formats a string (snprintf() wrapper).
+ *
+ * \throws  std::bad_alloc if out of memory.
+ *
+ * This function works like sprintf(), except that it returns an std::string
+ * instead of requiring a preallocated buffer.  Arbitrary length output is
+ * supported.
+ */
+std::string formatString(gmx_fmtstr const char* fmt, ...) gmx_format(printf, 1, 2);
+
+/*! \brief
+ * Formats a string (vsnprintf() wrapper).
+ *
+ * \throws  std::bad_alloc if out of memory.
+ *
+ * This function works like vsprintf(), except that it returns an std::string
+ * instead of requiring a preallocated buffer.  Arbitrary length output is
+ * supported.
+ */
+std::string formatStringV(const char* fmt, va_list ap);
+
+/*! \brief Function object that wraps a call to formatString() that
+ * expects a single conversion argument, for use with algorithms. */
+class StringFormatter
+{
+public:
+    /*! \brief Constructor
+     *
+     * \param[in] format The printf-style format string that will
+     *     be applied to convert values of type T to
+     *     string. Exactly one argument to the conversion
+     *     specification(s) in `format` is supported. */
+    explicit StringFormatter(const char* format) : format_(format) {}
+
+    //! Implements the formatting functionality
+    template<typename T>
+    std::string operator()(const T& value) const
+    {
+        return formatString(format_, value);
+    }
+
+private:
+    //! Format string to use
+    const char* format_;
+};
+
+/*! \brief Function object to implement the same interface as
+ * `StringFormatter` to use with strings that should not be formatted
+ * further. */
+class IdentityFormatter
+{
+public:
+    //! Implements the formatting non-functionality
+    std::string operator()(const std::string& value) const { return value; }
+};
+
+/*! \brief Formats all the range as strings, and then joins them with
+ * a separator in between.
+ *
+ * \param[in] begin      Iterator the beginning of the range to join.
+ * \param[in] end        Iterator the end of the range to join.
+ * \param[in] separator  String to put in between the joined strings.
+ * \param[in] formatter  Function object to format the objects in
+ *     `container` as strings
+ * \returns   All objects in the range from `begin` to `end` formatted
+ *     as strings and concatenated with `separator` between each pair.
+ * \throws    std::bad_alloc if out of memory.
+ */
+template<typename InputIterator, typename FormatterType>
+std::string formatAndJoin(InputIterator begin, InputIterator end, const char* separator, const FormatterType& formatter)
+{
+    std::string result;
+    const char* currentSeparator = "";
+    for (InputIterator i = begin; i != end; ++i)
+    {
+        result.append(currentSeparator);
+        result.append(formatter(*i));
+        currentSeparator = separator;
+    }
+    return result;
+}
+
+/*! \brief Formats all elements of the container as strings, and then
+ * joins them with a separator in between.
+ *
+ * \param[in] container  Objects to join.
+ * \param[in] separator  String to put in between the joined strings.
+ * \param[in] formatter  Function object to format the objects in
+ *     `container` as strings
+ * \returns   All objects from `container` formatted as strings and
+ *     concatenated with `separator` between each pair.
+ * \throws    std::bad_alloc if out of memory.
+ */
+template<typename ContainerType, typename FormatterType>
+std::string formatAndJoin(const ContainerType& container, const char* separator, const FormatterType& formatter)
+{
+    return formatAndJoin(container.begin(), container.end(), separator, formatter);
+}
+
+/*! \brief
+ * Joins strings from a range with a separator in between.
+ *
+ * \param[in] begin      Iterator the beginning of the range to join.
+ * \param[in] end        Iterator the end of the range to join.
+ * \param[in] separator  String to put in between the joined strings.
+ * \returns   All strings from (`begin`, `end`) concatenated with `separator`
+ *     between each pair.
+ * \throws    std::bad_alloc if out of memory.
+ */
+template<typename InputIterator>
+std::string joinStrings(InputIterator begin, InputIterator end, const char* separator)
+{
+    return formatAndJoin(begin, end, separator, IdentityFormatter());
+}
+
+/*! \brief
+ * Joins strings from a container with a separator in between.
+ *
+ * \param[in] container  Strings to join.
+ * \param[in] separator  String to put in between the joined strings.
+ * \returns   All strings from `container` concatenated with `separator`
+ *     between each pair.
+ * \throws    std::bad_alloc if out of memory.
+ */
+template<typename ContainerType>
+std::string joinStrings(const ContainerType& container, const char* separator)
+{
+    return joinStrings(container.begin(), container.end(), separator);
+}
+
+/*! \brief
+ * Joins strings from an array with a separator in between.
+ *
+ * \param[in] array      Array of strings to join.
+ * \param[in] separator  String to put in between the joined strings.
+ * \tparam    count      Deduced number of elements in \p array.
+ * \returns   All strings from `aray` concatenated with `separator`
+ *     between each pair.
+ * \throws    std::bad_alloc if out of memory.
+ */
+template<size_t count>
+std::string joinStrings(const char* const (&array)[count], const char* separator)
+{
+    return joinStrings(array, array + count, separator);
+}
+
+/*! \brief
+ * Splits a string to whitespace separated tokens.
+ *
+ * \param[in] str  String to process.
+ * \returns   \p str split into tokens at each whitespace sequence.
+ * \throws    std::bad_alloc if out of memory.
+ *
+ * This function works like `split` in Python, i.e., leading and trailing
+ * whitespace is ignored, and consecutive whitespaces are treated as a single
+ * separator.
+ */
+std::vector<std::string> splitString(const std::string& str);
+/*! \brief
+ * Splits a string to tokens separated by a given delimiter.
+ *
+ * \param[in] str   String to process.
+ * \param[in] delim Delimiter to use for splitting.
+ * \returns   \p str split into tokens at delimiter.
+ * \throws    std::bad_alloc if out of memory.
+ *
+ * Unlike splitString(), consecutive delimiters will generate empty tokens, as
+ * will leading or trailing delimiters.
+ * Empty input will return an empty vector.
+ */
+std::vector<std::string> splitDelimitedString(const std::string& str, char delim);
+/*! \brief
+ * Splits \c str to tokens separated by delimiter \c delim. Removes
+ * leading and trailing whitespace from those strings with std::isspace.
+ *
+ * \param[in] str   String to process.
+ * \param[in] delim Delimiter to use for splitting.
+ * \returns   \p str split into tokens at delimiter, with whitespace stripped.
+ * \throws    std::bad_alloc if out of memory.
+ *
+ * Unlike splitString(), consecutive delimiters will generate empty tokens, as
+ * will leading or trailing delimiters.
+ * Empty input will return an empty vector.
+ * Input with only whitespace will return a vector of size 1,
+ * that contains an empty token.
+ */
+std::vector<std::string> splitAndTrimDelimitedString(const std::string& str, char delim);
+
+/*! \brief
+ * Replace all occurrences of a string with another string.
+ *
+ * \param[in] input  Input string.
+ * \param[in] from   String to find.
+ * \param[in] to     String to use to replace \p from.
+ * \returns   Copy of \p input with all occurrences of \p from replaced with \p to.
+ * \throws    std::bad_alloc if out of memory.
+ *
+ * The replacement is greedy and not recursive: starting from the beginning of
+ * \p input, each match of \p from is replaced with \p to, and the search for
+ * the next match begins after the end of the previous match.
+ *
+ * Complexity is O(N), where N is length of output.
+ *
+ * \see replaceAllWords()
+ */
+std::string replaceAll(const std::string& input, const char* from, const char* to);
+//! \copydoc replaceAll(const std::string &, const char *, const char *)
+std::string replaceAll(const std::string& input, const std::string& from, const std::string& to);
+/*! \brief
+ * Replace whole words with others.
+ *
+ * \param[in] input  Input string.
+ * \param[in] from   String to find.
+ * \param[in] to     String to use to replace \p from.
+ * \returns   Copy of \p input with all \p from words replaced with \p to.
+ * \throws    std::bad_alloc if out of memory.
+ *
+ * Works as replaceAll(), but a match is only considered if it is delimited by
+ * non-alphanumeric characters.
+ *
+ * \see replaceAll()
+ */
+std::string replaceAllWords(const std::string& input, const char* from, const char* to);
+//! \copydoc replaceAllWords(const std::string &, const char *, const char *)
+std::string replaceAllWords(const std::string& input, const std::string& from, const std::string& to);
+
+/*! \brief Return whether two strings are equal, ignoring case.
+ *
+ * Checks if two strings have the same length and if all characters
+ * in them match when compared case insensitive.
+ * Characters are converted by using std::tolower.
+ *
+ * \param[in] source Search string to compare against \p target.
+ * \param[in] target String to be matched to \p source.
+ * \returns True if the strings match.
+ */
+bool equalCaseInsensitive(const std::string& source, const std::string& target);
+
+//! Function object for comparisons with \c equalCaseInsensitive
+class EqualCaseInsensitive
+{
+public:
+    bool operator()(const std::string& lhs, const std::string& rhs) const
+    {
+        return gmx::equalCaseInsensitive(lhs, rhs);
+    }
+};
+
+/*! \brief
+ * Checks if at most \p maxLengthOfComparison characters of two strings match case insensitive.
+ *
+ * The function tests two strings \p source and \p target to see if at most
+ * \p maxLengthOfComparison characters match between the two. If fewer characters are present
+ * in \p source, only the maximum number of characters in \p source will be compared instead.
+ * In this case both \p source and \p target also need to have the same length, or the strings will
+ * compare as false, even if \p target matches \p source over the length of \p source.
+ *
+ * If \p maxLengthOfComparison is 0, the function always returns true.
+ * Characters are converted by using std::tolower.
+ *
+ * \param[in] source Search string to compare against \p target.
+ * \param[in] target String to be matched to \p source.
+ * \param[in] maxLengthOfComparison The maximum string length to compare.
+ * \returns True if the strings match.
+ */
+bool equalCaseInsensitive(const std::string& source, const std::string& target, size_t maxLengthOfComparison);
+
+/*! \brief
+ * Makes the string uppercase.
+ *
+ * \param[in] text  Input text.
+ * \returns   \p text with all characters transformed to uppercase.
+ * \throws    std::bad_alloc if out of memory.
+ */
+std::string toUpperCase(const std::string& text);
+
+/*! \brief
+ * Makes the string lowercase.
+ *
+ * \param[in] text  Input text.
+ * \returns   \p text with all characters transformed to lowercase.
+ * \throws    std::bad_alloc if out of memory.
+ */
+std::string toLowerCase(const std::string& text);
+
+
+class TextLineWrapper;
+
+/*! \brief
+ * Stores settings for line wrapping.
+ *
+ * Methods in this class do not throw.
+ *
+ * \see TextLineWrapper
+ *
+ * \inpublicapi
+ */
+class TextLineWrapperSettings
+{
+public:
+    /*! \brief
+     * Initializes default wrapper settings.
+     *
+     * Default settings are:
+     *  - No maximum line width (only explicit line breaks).
+     *  - No indentation.
+     *  - No continuation characters.
+     *  - Do not keep final spaces in input strings.
+     */
+    TextLineWrapperSettings();
+
+    /*! \brief
+     * Sets the maximum length for output lines.
+     *
+     * \param[in] length  Maximum length for the lines after wrapping.
+     *
+     * If this method is not called, or is called with zero \p length, the
+     * wrapper has no maximum length (only wraps at explicit line breaks).
+     */
+    void setLineLength(int length) { maxLength_ = length; }
+    /*! \brief
+     * Sets the indentation for output lines.
+     *
+     * \param[in] indent  Number of spaces to add for indentation.
+     *
+     * If this method is not called, the wrapper does not add indentation.
+     */
+    void setIndent(int indent) { indent_ = indent; }
+    /*! \brief
+     * Sets the indentation for first output line after a line break.
+     *
+     * \param[in] indent  Number of spaces to add for indentation.
+     *
+     * If this method is not called, or called with \p indent equal to -1,
+     * the value set with setIndent() is used.
+     */
+    void setFirstLineIndent(int indent) { firstLineIndent_ = indent; }
+    /*! \brief
+     * Sets whether final spaces in input should be kept.
+     *
+     * \param[in] bKeep  Whether to keep spaces at the end of the input.
+     *
+     * This means that wrapping a string that ends in spaces also keeps
+     * those spaces in the output.  This allows using the wrapper for
+     * partial lines where the initial part of the line may end in a space.
+     * By default, all trailing whitespace is removed.  Note that this
+     * option does not affect spaces before an explicit newline: those are
+     * always removed.
+     */
+    void setKeepFinalSpaces(bool bKeep) { bKeepFinalSpaces_ = bKeep; }
+    /*! \brief
+     * Sets a continuation marker for wrapped lines.
+     *
+     * \param[in] continuationChar  Character to use to mark continuation
+     *      lines.
+     *
+     * If set to non-zero character code, this character is added at the
+     * end of each line where a line break is added by TextLineWrapper
+     * (but not after lines produced by explicit line breaks).
+     * The default (\c '\0') is to not add continuation markers.
+     *
+     * Note that currently, the continuation char may cause the output line
+     * length to exceed the value set with setLineLength() by at most two
+     * characters.
+     */
+    void setContinuationChar(char continuationChar) { continuationChar_ = continuationChar; }
+
+    //! Returns the maximum length set with setLineLength().
+    int lineLength() const { return maxLength_; }
+    //! Returns the indentation set with setIndent().
+    int indent() const { return indent_; }
+    /*! \brief
+     * Returns the indentation set with setFirstLineIndent().
+     *
+     * If setFirstLineIndent() has not been called or has been called with
+     * -1, indent() is returned.
+     */
+    int firstLineIndent() const { return (firstLineIndent_ >= 0 ? firstLineIndent_ : indent_); }
+
+private:
+    //! Maximum length of output lines, or <= 0 if no limit.
+    int maxLength_;
+    //! Number of spaces to indent each output line with.
+    int indent_;
+    /*! \brief
+     * Number of spaces to indent the first line after a newline.
+     *
+     * If -1, \a indent_ is used.
+     */
+    int firstLineIndent_;
+    //! Whether to keep spaces at end of input.
+    bool bKeepFinalSpaces_;
+    //! If not \c '\0', mark each wrapping point with this character.
+    char continuationChar_;
+
+    //! Needed to access the members.
+    friend class TextLineWrapper;
+};
+
+/*! \brief
+ * Wraps lines to a predefined length.
+ *
+ * This utility class wraps lines at word breaks to produce lines that are not
+ * longer than a predefined length.  Explicit newlines ('\\n') are preserved.
+ * Only space is considered a word separator.  If a single word exceeds the
+ * maximum line length, it is still printed on a single line.
+ * Extra whitespace is stripped from the end of produced lines.
+ * Other options on the wrapping, such as the line length or indentation,
+ * can be changed using a TextLineWrapperSettings object.
+ *
+ * Two interfaces to do the wrapping are provided:
+ *  -# High-level interface using either wrapToString() (produces a single
+ *     string with embedded newlines) or wrapToVector() (produces a vector of
+ *     strings with each line as one element).
+ *     These methods operate on std::string and wrap the entire input string.
+ *  -# Low-level interface using findNextLine() and formatLine().
+ *     findNextLine() operates either on a C string or an std::string, and does
+ *     not do any memory allocation (so it does not throw).  It finds the next
+ *     line to be wrapped, considering the wrapping settings.
+ *     formatLine() does whitespace operations on the line found by
+ *     findNextLine() and returns an std::string.
+ *     These methods allow custom wrapping implementation to either avoid
+ *     exceptions or to wrap only a part of the input string.
+ *
+ * Typical usage:
+ * \code
+   gmx::TextLineWrapper wrapper;
+   wrapper.settings().setLineLength(78);
+   printf("%s\n", wrapper.wrapToString(textToWrap).c_str());
+   \endcode
+ *
+ * \inpublicapi
+ */
+class TextLineWrapper
+{
+public:
+    /*! \brief
+     * Constructs a new line wrapper with default settings.
+     *
+     * Does not throw.
+     */
+    TextLineWrapper() {}
+    /*! \brief
+     * Constructs a new line wrapper with given settings.
+     *
+     * \param[in] settings  Wrapping settings.
+     *
+     * Does not throw.
+     */
+    explicit TextLineWrapper(const TextLineWrapperSettings& settings) : settings_(settings) {}
+
+    /*! \brief
+     * Provides access to settings of this wrapper.
+     *
+     * \returns  The settings object for this wrapper.
+     *
+     * The returned object can be used to modify settings for the wrapper.
+     * All subsequent calls to wrapToString() and wrapToVector() use the
+     * modified settings.
+     *
+     * Does not throw.
+     */
+    TextLineWrapperSettings& settings() { return settings_; }
+
+    //! Returns true if the wrapper would not modify the input string.
+    bool isTrivial() const;
+
+    /*! \brief
+     * Finds the next line to be wrapped.
+     *
+     * \param[in] input     String to wrap.
+     * \param[in] lineStart Index of first character of the line to find.
+     * \returns   Index of first character of the next line.
+     *
+     * If this is the last line, returns the length of \p input.
+     * In determining the length of the returned line, this function
+     * considers the maximum line length, leaving space for indentation,
+     * and also whitespace stripping behavior.
+     * Thus, the line returned may be longer than the maximum line length
+     * if it has leading and/or trailing space.
+     * When wrapping a line on a space (not on an explicit line break),
+     * the returned index is always on a non-whitespace character after the
+     * space.
+     *
+     * To iterate over lines in a string, use the following code:
+     * \code
+       gmx::TextLineWrapper wrapper;
+       // <set desired wrapping settings>
+       size_t lineStart = 0;
+       size_t length = input.length();
+       while (lineStart < length)
+       {
+           size_t nextLineStart = wrapper.findNextLine(input, lineStart);
+           std::string line = wrapper.formatLine(input, lineStart, nextLineStart));
+           // <do something with the line>
+           lineStart = nextLineStart;
+       }
+       return result;
+       \endcode
+     *
+     * Does not throw.
+     */
+    size_t findNextLine(const char* input, size_t lineStart) const;
+    //! \copydoc findNextLine(const char *, size_t)const
+    size_t findNextLine(const std::string& input, size_t lineStart) const;
+    /*! \brief
+     * Formats a single line for output according to wrapping settings.
+     *
+     * \param[in] input     Input string.
+     * \param[in] lineStart Index of first character of the line to format.
+     * \param[in] lineEnd   Index of first character of the next line.
+     * \returns   The line with leading and/or trailing whitespace removed
+     *      and indentation applied.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * Intended to be used on the lines found by findNextLine().
+     * When used with the lines returned from findNextLine(), the returned
+     * line conforms to the wrapper settings.
+     * Trailing whitespace is always stripped (including any newlines,
+     * i.e., the return value does not contain a newline).
+     */
+    std::string formatLine(const std::string& input, size_t lineStart, size_t lineEnd) const;
+
+    /*! \brief
+     * Formats a string, producing a single string with all the lines.
+     *
+     * \param[in] input  String to wrap.
+     * \returns   \p input with added newlines such that maximum line
+     *      length is not exceeded.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * Newlines in the input are preserved, including terminal newlines.
+     * Note that if the input does not contain a terminal newline, the
+     * output does not either.
+     */
+    std::string wrapToString(const std::string& input) const;
+    /*! \brief
+     * Formats a string, producing a vector with all the lines.
+     *
+     * \param[in] input  String to wrap.
+     * \returns   \p input split into lines such that maximum line length
+     *      is not exceeded.
+     * \throws    std::bad_alloc if out of memory.
+     *
+     * The strings in the returned vector do not contain newlines at the
+     * end.
+     * Note that a single terminal newline does not affect the output:
+     * "line\\n" and "line" both produce the same output (but "line\\n\\n"
+     * produces two lines, the second of which is empty).
+     */
+    std::vector<std::string> wrapToVector(const std::string& input) const;
+
+private:
+    TextLineWrapperSettings settings_;
+};
+
+//! \}
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/sycl_version_information.h b/src/include/gromacs/utility/sycl_version_information.h
new file mode 100644 (file)
index 0000000..0b075ca
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+#ifndef GMX_UTILITY_SYCL_VERSION_INFORMATION_H
+#define GMX_UTILITY_SYCL_VERSION_INFORMATION_H
+
+#include <string>
+
+namespace gmx
+{
+
+//! Returns an internal version strings of the Intel DPC++ and hipSYCL compiler.
+std::string getSyclCompilerVersion();
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/sysinfo.h b/src/include/gromacs/utility/sysinfo.h
new file mode 100644 (file)
index 0000000..c4d0b94
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares functions that wrap platform-specific calls for obtaining
+ * information about the operating environment and the current
+ * process.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_SYSINFO_H
+#define GMX_UTILITY_SYSINFO_H
+
+#include <stddef.h>
+#include <time.h>
+
+#include <string>
+
+/*! \addtogroup module_utility
+ * \{
+ */
+
+/*! \brief
+ * Gets the hostname as given by gethostname(), if available.
+ *
+ * \param[out] buf  Buffer to receive the hostname.
+ * \param[in]  len  Length of buffer \p buf (must be >= 8).
+ * \returns 0 on success, -1 on error.
+ *
+ * If the value is not available, "unknown" is returned.
+ * \p name should have at least size \p len.
+ *
+ * Does not throw.
+ */
+int gmx_gethostname(char* buf, size_t len);
+
+/*! \brief
+ * Returns the process ID of the current process.
+ *
+ * Does not throw.
+ */
+int gmx_getpid();
+/*! \brief
+ * Returns the current user ID, or -1 if not available.
+ *
+ * Does not throw.
+ */
+int gmx_getuid();
+/*! \brief
+ * Gets the current user name, if available.
+ *
+ * \param[out] buf  Buffer to receive the username.
+ * \param[in]  len  Length of buffer \p buf (must be >= 8).
+ * \returns 0 on success, -1 on error.
+ *
+ * Does not throw.
+ */
+int gmx_getusername(char* buf, size_t len);
+
+/*! \brief
+ * Portable version of ctime_r.
+ *
+ * \throws std::bad_alloc when out of memory.
+ */
+std::string gmx_ctime_r(const time_t* clock);
+/*! \brief
+ * Gets the current time as a string.
+ *
+ * \throws std::bad_alloc when out of memory.
+ */
+std::string gmx_format_current_time();
+
+/*! \brief
+ * Wrapper for nice().
+ *
+ * Does not throw.
+ */
+int gmx_set_nice(int level);
+
+/*! \} */
+
+#endif
diff --git a/src/include/gromacs/utility/template_mp.h b/src/include/gromacs/utility/template_mp.h
new file mode 100644 (file)
index 0000000..902e1de
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2020,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares utilities for template metaprogramming
+ *
+ * \author Roland Schulz <roland.schulz@intel.com>
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_TEMPLATE_MP_H
+#define GMX_UTILITY_TEMPLATE_MP_H
+
+#include <cassert>
+#include <cstddef>
+
+#include <utility>
+
+#include "gromacs/compat/mp11.h"
+
+namespace gmx
+{
+
+/*! \internal \brief
+ * Helper function to select appropriate template based on runtime values.
+ *
+ * Can use enums or booleans for template parameters.
+ * These enums must have a member \c Count indicating the total number of valid values.
+ *
+ * Example usage:
+ * \code
+    enum class Options {
+        Op1 = 0,
+        Op2 = 1,
+        Count = 2
+    };
+
+    template<bool p0, Options p1, Options p2>
+    bool foo(int i);
+
+    bool bar(bool p0, Options p1, Options p2, int i) {
+        return dispatchTemplatedFunction(
+            [=](auto p0, auto p1, auto p2) {
+                return foo<p0, p1, p2>(i);
+            },
+            p0, p1, p2);
+    }
+ * \endcode
+ *
+ * \tparam Function Type of \p f.
+ * \param f Function to call.
+ * \return The result of calling \c f().
+*/
+template<class Function>
+auto dispatchTemplatedFunction(Function&& f)
+{
+    return std::forward<Function>(f)();
+}
+
+// Recursive templates confuse Doxygen
+//! \cond
+template<class Function, class Enum, class... Enums>
+auto dispatchTemplatedFunction(Function&& f, Enum e, Enums... es)
+{
+    return dispatchTemplatedFunction(
+            [&](auto... es_) {
+                return compat::mp_with_index<size_t(Enum::Count)>(size_t(e), [&](auto e_) {
+                    return std::forward<Function>(f)(
+                            std::integral_constant<Enum, static_cast<Enum>(size_t(e_))>(), es_...);
+                });
+            },
+            es...);
+}
+
+template<class Function, class... Enums>
+auto dispatchTemplatedFunction(Function&& f, bool e, Enums... es)
+{
+    return dispatchTemplatedFunction(
+            [&](auto... es_) {
+                return compat::mp_with_index<2>(size_t(e), [&](auto e_) {
+                    return std::forward<Function>(f)(std::bool_constant<static_cast<bool>(e_)>(), es_...);
+                });
+            },
+            es...);
+}
+//! \endcond
+
+} // namespace gmx
+
+#endif // GMX_UTILITY_TEMPLATE_MP_H
diff --git a/src/include/gromacs/utility/tests/alignedallocator_impl.h b/src/include/gromacs/utility/tests/alignedallocator_impl.h
new file mode 100644 (file)
index 0000000..abf6bdb
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2017,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief Tests for allocators that offer a minimum alignment.
+ *
+ * This implementation header can be included in multiple modules
+ * tests, which is currently needed because gpu_utils is physically
+ * separate from the utility module.
+ *
+ * \author Erik Lindahl <erik.lindahl@gmail.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_TESTS_ALIGNEDALLOCATOR_IMPL_H
+#define GMX_UTILITY_TESTS_ALIGNEDALLOCATOR_IMPL_H
+
+#include <cstddef>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "gromacs/math/vectypes.h"
+#include "gromacs/utility/real.h"
+
+namespace gmx
+{
+namespace test
+{
+
+/*! \libinternal
+ * \brief Templated test fixture. */
+template<typename T>
+class AllocatorTest : public ::testing::Test
+{
+public:
+    /*! \brief Return a bitmask for testing the alignment.
+     *
+     * e.g. for 128-byte alignment the mask is 128-1 - all of
+     * these bits should be zero in pointers that have the
+     * intended alignment. */
+    std::size_t mask(const T& allocator) { return allocator.alignment() - 1; }
+};
+
+// NB need to use this->mask() because of GoogleTest quirks
+
+TYPED_TEST(AllocatorTest, AllocatorAlignAllocatesWithAlignment) //NOLINT(misc-definitions-in-headers)
+{
+    using pointer = typename TypeParam::value_type*;
+    TypeParam a;
+    pointer   p = a.allocate(1000);
+
+    EXPECT_EQ(0, reinterpret_cast<std::size_t>(p) & this->mask(a));
+    a.deallocate(p, 1000);
+}
+
+
+TYPED_TEST(AllocatorTest, VectorAllocatesAndResizesWithAlignment) //NOLINT(misc-definitions-in-headers)
+{
+    using value_type = typename TypeParam::value_type;
+    std::vector<value_type, TypeParam> v(10);
+    EXPECT_EQ(0, reinterpret_cast<std::size_t>(v.data()) & this->mask(v.get_allocator()));
+
+    // Reserve a few times to check things work ok, making sure we
+    // will trigger several reallocations on common vector
+    // implementations.
+    for (std::size_t i = 1000; i <= 10000; i += 1000)
+    {
+        v.resize(i);
+        EXPECT_EQ(0, reinterpret_cast<std::size_t>(v.data()) & this->mask(v.get_allocator()));
+    }
+}
+
+TYPED_TEST(AllocatorTest, VectorAllocatesAndReservesWithAlignment) //NOLINT(misc-definitions-in-headers)
+{
+    using value_type = typename TypeParam::value_type;
+    std::vector<value_type, TypeParam> v(10);
+    EXPECT_EQ(0, reinterpret_cast<std::size_t>(v.data()) & this->mask(v.get_allocator()));
+
+    // Reserve a few times to check things work ok, making sure we
+    // will trigger several reallocations on common vector
+    // implementations.
+    for (std::size_t i = 1000; i <= 10000; i += 1000)
+    {
+        v.reserve(i);
+        EXPECT_EQ(0, reinterpret_cast<std::size_t>(v.data()) & this->mask(v.get_allocator()));
+    }
+}
+
+TYPED_TEST(AllocatorTest, Move) //NOLINT(misc-definitions-in-headers)
+{
+    using value_type = typename TypeParam::value_type;
+    std::vector<value_type, TypeParam> v1(1);
+    value_type*                        data = v1.data();
+    EXPECT_NE(data, nullptr);
+    std::vector<value_type, TypeParam> v2(std::move(v1));
+    EXPECT_EQ(data, v2.data());
+}
+
+} // namespace test
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/tests/bitmask.h b/src/include/gromacs/utility/tests/bitmask.h
new file mode 100644 (file)
index 0000000..19cd95b
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2014,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests for bitmask functionality.
+ *
+ * These tests check the functionality of bitmask.h
+
+ * \author Roland Schulz <roland@rschulz.eu>
+ * \ingroup module_utility
+ */
+#include <gtest/gtest.h>
+
+#include "gromacs/utility/bitmask.h"
+
+//! Implemenation of BITMASK_CLASSNAME
+#define BITMASK_CLASSNAME_(S) BitmaskTest##S
+//! Returns name of Bitmask test fixture class
+#define BITMASK_CLASSNAME(S) BITMASK_CLASSNAME_(S)
+//! Implementation of BITMASK_TEST_P
+#define BITMASK_TEST_P_(C, T) TEST_P(C, T)
+//! Defines a parameterized bitmask test
+#define BITMASK_TEST_P(T) BITMASK_TEST_P_(BITMASK_CLASSNAME(BITMASK_SIZE), T)
+
+class BITMASK_CLASSNAME(BITMASK_SIZE) : public ::testing::TestWithParam<int>
+{
+};
+
+BITMASK_TEST_P(SetAndClear) //NOLINT(misc-definitions-in-headers)
+{
+    gmx_bitmask_t m; //NOLINT(cppcoreguidelines-init-variables)
+    int           i = GetParam();
+    bitmask_clear(&m);
+    EXPECT_TRUE(bitmask_is_zero(m));
+    EXPECT_FALSE(bitmask_is_set(m, i));
+    bitmask_set_bit(&m, i);
+    for (int j = 0; j < BITMASK_SIZE; j++)
+    {
+        EXPECT_EQ(bitmask_is_set(m, j), j == i);
+    }
+    bitmask_clear(&m);
+    EXPECT_TRUE(bitmask_is_zero(m));
+}
+
+BITMASK_TEST_P(InitBit) //NOLINT(misc-definitions-in-headers)
+{
+    gmx_bitmask_t m1, m2; //NOLINT(cppcoreguidelines-init-variables)
+    int           i = GetParam();
+    bitmask_init_bit(&m1, i);
+    bitmask_clear(&m2);
+    EXPECT_FALSE(bitmask_is_equal(m1, m2));
+    bitmask_set_bit(&m2, i);
+    EXPECT_TRUE(bitmask_is_equal(m1, m2));
+}
+
+BITMASK_TEST_P(InitLowBits) //NOLINT(misc-definitions-in-headers)
+{
+    gmx_bitmask_t m; //NOLINT(cppcoreguidelines-init-variables)
+    int           i = GetParam();
+    bitmask_init_low_bits(&m, i);
+    for (int j = 0; j < BITMASK_SIZE; j++)
+    {
+        EXPECT_EQ(bitmask_is_set(m, j), j < i);
+    }
+}
+
+BITMASK_TEST_P(Disjoint) //NOLINT(misc-definitions-in-headers)
+{
+    gmx_bitmask_t m1, m2; //NOLINT(cppcoreguidelines-init-variables)
+    int           i = GetParam();
+    bitmask_init_bit(&m1, i);
+    bitmask_init_bit(&m2, i);
+    EXPECT_FALSE(bitmask_is_disjoint(m1, m2));
+    bitmask_init_low_bits(&m2, i);
+    EXPECT_TRUE(bitmask_is_disjoint(m1, m2));
+}
+
+BITMASK_TEST_P(Union) //NOLINT(misc-definitions-in-headers)
+{
+    gmx_bitmask_t m1, m2; //NOLINT(cppcoreguidelines-init-variables)
+    int           i = GetParam();
+    int           j = (i + BITMASK_SIZE / 2) % BITMASK_SIZE;
+    bitmask_init_bit(&m1, i);
+    bitmask_init_bit(&m2, j);
+    bitmask_union(&m1, m2);
+    for (int k = 0; k < BITMASK_SIZE; k++)
+    {
+        EXPECT_EQ(bitmask_is_set(m1, k), k == i || k == j);
+    }
+
+    bitmask_init_bit(&m1, i);
+    bitmask_clear(&m2);
+    bitmask_union(&m1, m2);
+    bitmask_init_bit(&m2, i);
+    EXPECT_TRUE(bitmask_is_equal(m1, m2));
+
+    bitmask_clear(&m1);
+    bitmask_init_bit(&m2, i);
+    bitmask_union(&m1, m2);
+    EXPECT_TRUE(bitmask_is_equal(m1, m2));
+}
+BITMASK_TEST_P(ToHex) //NOLINT(misc-definitions-in-headers)
+{
+    gmx_bitmask_t m; //NOLINT(cppcoreguidelines-init-variables)
+    bitmask_clear(&m);
+    bitmask_set_bit(&m, BITMASK_SIZE - 1);
+    EXPECT_EQ(to_hex_string(m), "8" + std::string(BITMASK_SIZE / 4 - 1, '0'));
+}
diff --git a/src/include/gromacs/utility/textreader.h b/src/include/gromacs/utility/textreader.h
new file mode 100644 (file)
index 0000000..f6c3f58
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2017,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::TextReader.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_TEXTREADER_H
+#define GMX_UTILITY_TEXTREADER_H
+
+#include <memory>
+#include <string>
+
+#include "gromacs/utility/textstream.h"
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Reads text from a TextInputStream.
+ *
+ * This class provides more formatted reading capabilities than reading raw
+ * lines from the stream (and a natural place to implement more such
+ * capabilities).
+ *
+ * All methods that read from the stream can throw any exceptions that the
+ * underlying stream throws.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextReader
+{
+public:
+    /*! \brief
+     * Reads contents of a file to a std::string.
+     *
+     * \param[in] filename  Name of the file to read.
+     * \returns   The contents of \p filename.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    FileIOError on any I/O error.
+     */
+    static std::string readFileToString(const char* filename);
+    //! \copydoc readFileToString(const char *)
+    static std::string readFileToString(const std::string& filename);
+
+    /*! \brief
+     * Creates a reader that reads from specified file.
+     *
+     * \param[in]  filename  Path to the file to open.
+     * \throws     std::bad_alloc if out of memory.
+     * \throws     FileIOError on any I/O error.
+     *
+     * This constructor is provided for convenience for reading directly
+     * from a file, without the need to construct multiple objects.
+     */
+    explicit TextReader(const std::string& filename);
+    /*! \brief
+     * Creates a reader that reads from specified stream.
+     *
+     * \param[in]  stream  Stream to read from.
+     * \throws     std::bad_alloc if out of memory.
+     *
+     * The caller is responsible of the lifetime of the stream (should
+     * remain in existence as long as the reader exists).
+     *
+     * This constructor is provided for convenience for cases where the
+     * stream is not allocated with `new` and/or not managed by a
+     * std::shared_ptr (e.g., if the stream is an object on the stack).
+     */
+    explicit TextReader(TextInputStream* stream);
+    /*! \brief
+     * Creates a reader that reads from specified stream.
+     *
+     * \param[in]  stream  Stream to read from.
+     * \throws     std::bad_alloc if out of memory.
+     *
+     * The reader keeps a reference to the stream, so the caller can pass
+     * in a temporary if necessary.
+     */
+    explicit TextReader(const TextInputStreamPointer& stream);
+    ~TextReader();
+
+    /*! \brief
+     * Reads a single line (including newline) from the stream.
+     *
+     * \param[out] line    String to receive the line.
+     * \returns    `false` if nothing was read because the file ended.
+     *
+     * On error or when false is returned, \p line will be empty.
+     * Newlines will be returned as part of \p line if it was present in
+     * the stream.
+     * To loop over all lines in the stream, use:
+     * \code
+       std::string line;
+       while (reader.readLine(&line))
+       {
+           // ...
+       }
+       \endcode
+     *
+     * Behaviours such as trimming whitespace or comments can be
+     * configured by calling other methods before this one.
+     */
+    bool readLine(std::string* line);
+    /*! \brief Sets whether the reader should trim leading whitespace
+     * from a line before returning it.
+     *
+     * \param[in] doTrimming  Whether trimming should be active.
+     */
+    void setTrimLeadingWhiteSpace(bool doTrimming);
+    /*! \brief Sets whether the reader should trim trailing whitespace
+     * from a line before returning it.
+     *
+     * Note that comment trimming will precede whitespace trimming
+     * when both are active.
+     *
+     * \param[in] doTrimming  Whether trimming should be active.
+     */
+    void setTrimTrailingWhiteSpace(bool doTrimming);
+    /*! \brief Sets whether the reader should trim at trailing
+     * comment from a line before returning it.
+     *
+     * Note that comment trimming will precede whitespace trimming
+     * when both are active.
+     *
+     * \param[in]  commentChar  The character that begins a comment.
+     *
+     * \param[in] doTrimming  Whether trimming should be active.
+     */
+    void setTrimTrailingComment(bool doTrimming, char commentChar);
+    /*! \brief
+     * Reads all remaining lines from the stream as a single string.
+     *
+     * \returns   Full contents of the stream (from the current point to
+     *     the end).
+     */
+    std::string readAll();
+
+    /*! \brief
+     * Closes the underlying stream.
+     */
+    void close();
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/textstream.h b/src/include/gromacs/utility/textstream.h
new file mode 100644 (file)
index 0000000..7c84211
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares interfaces for simple input/output streams.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_TEXTSTREAM_H
+#define GMX_UTILITY_TEXTSTREAM_H
+
+#include <memory>
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Interface for reading text.
+ *
+ * Concrete implementations can read the text from, e.g., a file or an in-memory
+ * string.  The main use is to allow unit tests to inject in-memory buffers
+ * instead of writing files to be read by the code under test, but there are
+ * also use cases outside the tests where it is useful to abstract out whether
+ * the input is from a real file or something else.
+ *
+ * To use more advanced formatting than reading raw lines, use TextReader.
+ *
+ * Both methods in the interface can throw std::bad_alloc or other exceptions
+ * that indicate failures to read from the stream.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextInputStream
+{
+public:
+    virtual ~TextInputStream() {}
+
+    /*! \brief
+     * Reads a line (with newline included) from the stream.
+     *
+     * \param[out] line    String to receive the line.
+     * \returns    `false` if nothing was read because the stream ended.
+     *
+     * On error or when `false` is returned, \p line will be empty.
+     */
+    virtual bool readLine(std::string* line) = 0;
+    /*! \brief
+     * Closes the stream.
+     *
+     * It is not allowed to read from a stream after it has been closed.
+     * See TextOutputStream::close() for rationale for a close() method
+     * separate from the destructor.  For input, failures during close
+     * should be rare, but it is clearer to keep the interface symmetric.
+     */
+    virtual void close() = 0;
+};
+
+/*! \libinternal \brief
+ * Interface for writing text.
+ *
+ * Concrete implementations can write the text to, e.g., a file or an in-memory
+ * string.  The main use is to allow unit tests to inject in-memory buffers
+ * instead of reading in files produced by the code under test, but there are
+ * also use cases outside the tests where it is useful to abstract out whether
+ * the output is into a real file or something else.
+ *
+ * To use more advanced formatting than writing plain strings, use TextWriter.
+ *
+ * The current implementation assumes text-only output in several places, but
+ * this interface could possibly be generalized also for binary files.
+ * However, since all binary files currently written by \Gromacs are either
+ * XDR- or TNG-based, they may require a different approach.  Also, it is worth
+ * keeping the distinction between text and binary files clear, since Windows
+ * does transparent `LF`-`CRLF` newline translation for text files, so mixing
+ * modes when reading and/or writing the same file can cause subtle issues.
+ *
+ * Both methods in the interface can throw std::bad_alloc or other exceptions
+ * that indicate failures to write to the stream.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextOutputStream
+{
+public:
+    virtual ~TextOutputStream() {}
+
+    /*! \brief
+     * Writes a given string to the stream.
+     */
+    virtual void write(const char* text) = 0;
+    /*! \brief
+     * Closes the stream.
+     *
+     * It is not allowed to write to a stream after it has been closed.
+     * A method separate from the destructor is provided such that errors
+     * that occur while closing the stream (e.g., when closing the file)
+     * can be handled using exceptions.
+     * The destructor is not allowed to throw, so code that wants to
+     * observe such errors needs to call close() after it has finished
+     * writing to the stream.
+     */
+    virtual void close() = 0;
+};
+
+//! Shorthand for a smart pointer to a TextInputStream.
+typedef std::shared_ptr<TextInputStream> TextInputStreamPointer;
+//! Shorthand for a smart pointer to a TextOutputStream.
+typedef std::shared_ptr<TextOutputStream> TextOutputStreamPointer;
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/textwriter.h b/src/include/gromacs/utility/textwriter.h
new file mode 100644 (file)
index 0000000..e1ddd78
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2015,2018,2019,2021, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares gmx::TextWriter.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_TEXTWRITER_H
+#define GMX_UTILITY_TEXTWRITER_H
+
+#include <cstdio>
+
+#include <memory>
+#include <string>
+
+#include "gromacs/utility/textstream.h"
+
+namespace gmx
+{
+
+class TextLineWrapperSettings;
+
+/*! \libinternal \brief
+ * Writes text into a TextOutputStream.
+ *
+ * This class provides more formatting and line-oriented writing capabilities
+ * than writing raw strings into the stream.
+ *
+ * All methods that write to the stream can throw any exceptions that the
+ * underlying stream throws.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+class TextWriter
+{
+public:
+    /*! \brief
+     * Convenience method for writing a file from a string in a single call.
+     *
+     * \param[in] filename  Name of the file to read.
+     * \param[in] text      String to write to \p filename.
+     * \throws    std::bad_alloc if out of memory.
+     * \throws    FileIOError on any I/O error.
+     *
+     * If \p filename exists, it is overwritten.
+     */
+    static void writeFileFromString(const std::string& filename, const std::string& text);
+
+    /*! \brief
+     * Creates a writer that writes to specified file.
+     *
+     * \param[in]  filename  Path to the file to open.
+     * \throws     std::bad_alloc if out of memory.
+     * \throws     FileIOError on any I/O error.
+     *
+     * This constructor is provided for convenience for writing directly to
+     * a file, without the need to construct multiple objects.
+     */
+    explicit TextWriter(const std::string& filename);
+    /*! \brief
+     * Creates a writer that writes to specified file.
+     *
+     * \param[in]  fp  File handle to write to.
+     * \throws     std::bad_alloc if out of memory.
+     * \throws     FileIOError on any I/O error.
+     *
+     * This constructor is provided for interoperability with C-like code
+     * for writing directly to an already opened file, without the need to
+     * construct multiple objects.
+     *
+     * The caller is responsible of closing \p fp; it is not allowed to
+     * call close() on the writer.
+     */
+    explicit TextWriter(FILE* fp);
+    /*! \brief
+     * Creates a writer that writes to specified stream.
+     *
+     * \param[in]  stream  Stream to write to.
+     * \throws     std::bad_alloc if out of memory.
+     *
+     * The caller is responsible of the lifetime of the stream (should
+     * remain in existence as long as the writer exists).
+     *
+     * This constructor is provided for convenience for cases where the
+     * stream is not allocated with `new` and/or not managed by a
+     * std::shared_ptr (e.g., if the stream is an object on the stack).
+     */
+    explicit TextWriter(TextOutputStream* stream);
+    /*! \brief
+     * Creates a writer that writes to specified stream.
+     *
+     * \param[in]  stream  Stream to write to.
+     * \throws     std::bad_alloc if out of memory.
+     *
+     * The writer keeps a reference to the stream, so the caller can pass
+     * in a temporary if necessary.
+     */
+    explicit TextWriter(const TextOutputStreamPointer& stream);
+    ~TextWriter();
+
+    /*! \brief
+     * Allows adjusting wrapping settings for the writer.
+     *
+     * \todo
+     * Wrapping is not currently implemented for code that writes partial
+     * lines with writeString().
+     */
+    TextLineWrapperSettings& wrapperSettings();
+
+    /*! \brief
+     * Writes a string to the stream.
+     *
+     * \param[in]  str  String to write.
+     */
+    void writeString(const char* str);
+    //! \copydoc writeString(const char *)
+    void writeString(const std::string& str);
+    //! Writes a string to the stream, with printf-style formatting.
+    void writeStringFormatted(const char* fmt, ...);
+    /*! \brief
+     * Writes a line to the stream.
+     *
+     * \param[in]  line  Line to write.
+     *
+     * If \p line does not end in a newline, one newline is appended.
+     * Otherwise, works as writeString().
+     */
+    void writeLine(const char* line);
+    //! \copydoc writeLine(const char *)
+    void writeLine(const std::string& line);
+    //! Writes a line to the stream, with printf-style formatting.
+    void writeLineFormatted(const char* fmt, ...);
+    //! Writes a newline to the stream.
+    void writeLine();
+
+    /*! \brief
+     * Writes a newline if previous output did not end in one.
+     *
+     * If nothing has been written using the writer, this method does
+     * nothing.
+     */
+    void ensureLineBreak();
+    /*! \brief
+     * Ensures that the next string written starts after an empty line.
+     *
+     * Always terminates the current line (as with ensureLineBreak()), but
+     * the empty line is only written out when the next line is written,
+     * so that trailing newlines after final output can be avoided.
+     *
+     * If nothing has been written using the writer, this method does
+     * nothing.
+     */
+    void ensureEmptyLine();
+
+    /*! \brief
+     * Closes the underlying stream.
+     */
+    void close();
+
+private:
+    class Impl;
+
+    std::unique_ptr<Impl> impl_;
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/txtdump.h b/src/include/gromacs/utility/txtdump.h
new file mode 100644 (file)
index 0000000..7a0afba
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
+ * Copyright (c) 2001-2004, The GROMACS development team.
+ * Copyright (c) 2013,2014,2015,2018,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares helper functions for dumping basic data structures as text.
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_FILEIO_TXTDUMP_H
+#define GMX_FILEIO_TXTDUMP_H
+
+#include <cstdio>
+
+#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/utility/real.h"
+
+//! Line width for text dump output.
+#define LINE_WIDTH 80
+//! Right margin for text dump output.
+#define RMARGIN 10
+//! Actual line length for text dump output.
+#define USE_WIDTH ((LINE_WIDTH) - (RMARGIN))
+//! Default indentation for text dump output.
+#define INDENT 3
+
+//! Prints an initial indentation for a line.
+int pr_indent(FILE* fp, int n);
+//! Returns whether \p is available (not null), and prints a note if it is not.
+bool available(FILE* fp, const void* p, int indent, const char* title);
+//! Prints a title for a dumped section.
+int pr_title(FILE* fp, int indent, const char* title);
+//! Prints a title for a dumped section with a number suffixed.
+int pr_title_n(FILE* fp, int indent, const char* title, int n);
+//! Prints a title for a dumped section with two numbers suffixed (in NxM format).
+int pr_title_nxn(FILE* fp, int indent, const char* title, int n1, int n2);
+//! Prints an array of reals.
+void pr_reals(FILE* fp, int indent, const char* title, const real vec[], int n);
+//! Prints an array of doubles.
+void pr_doubles(FILE* fp, int indent, const char* title, const double* vec, int n);
+//! Prints an array of reals as a matrix with inner dimension dim.
+void pr_reals_of_dim(FILE* fp, int indent, const char* title, const real* vec, int n, int dim);
+//! Prints an integer value.
+void pr_int(FILE* fp, int indent, const char* title, int i);
+//! Prints a int64_t value.
+void pr_int64(FILE* fp, int indent, const char* title, int64_t i);
+//! Prints a floating-point value.
+void pr_real(FILE* fp, int indent, const char* title, real r);
+//! Prints a double-precision floating-point value.
+void pr_double(FILE* fp, int indent, const char* title, double d);
+//! Prints a string value.
+void pr_str(FILE* fp, int indent, const char* title, const char* s);
+//! Prints strings as a section; intended to be used for an array of names.
+void pr_strings(FILE* fp, int indent, const char* title, char*** nm, int n, gmx_bool bShowNumbers);
+
+#endif
diff --git a/src/include/gromacs/utility/typetraits.h b/src/include/gromacs/utility/typetraits.h
new file mode 100644 (file)
index 0000000..90c8200
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2017,2019, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \libinternal \file
+ * \brief
+ * Declares type traits
+ *
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+
+#ifndef GMX_UTILITY_TYPETRAITS_H
+#define GMX_UTILITY_TYPETRAITS_H
+
+#include <type_traits>
+
+namespace gmx
+{
+
+/*! \libinternal \brief
+ * Is true if type is a std::integral_constant
+ *
+ * If the optional integral type is given, than it is only true for a
+ * std::integral_constant of that integral type.
+ *
+ * \tparam T type to check
+ * \tparam Int optional integral type
+ */
+template<typename T, typename Int = void>
+struct isIntegralConstant : public std::false_type
+{
+};
+
+template<typename Int, Int N>
+struct isIntegralConstant<std::integral_constant<Int, N>, void> : public std::true_type
+{
+};
+
+template<typename Int, Int N>
+struct isIntegralConstant<std::integral_constant<Int, N>, Int> : public std::true_type
+{
+};
+
+} // namespace gmx
+
+#endif
diff --git a/src/include/gromacs/utility/unique_cptr.h b/src/include/gromacs/utility/unique_cptr.h
new file mode 100644 (file)
index 0000000..fcf6cd0
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2012,2014,2015,2016,2017 by the GROMACS development team.
+ * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \file
+ * \brief
+ * Declares gmx::unique_cptr and gmx::sfree_guard.
+ *
+ * \author Teemu Murtola <teemu.murtola@gmail.com>
+ * \inlibraryapi
+ * \ingroup module_utility
+ */
+#ifndef GMX_UTILITY_UNIQUE_PTR_SFREE_H
+#define GMX_UTILITY_UNIQUE_PTR_SFREE_H
+
+#include <cstdlib>
+
+#include <memory>
+
+#include "gromacs/utility/smalloc.h"
+
+namespace gmx
+{
+
+/*! \brief Wrapper of standard library free(), to be used as
+ * unique_cptr deleter for memory allocated by malloc, e.g. by an
+ * external library such as TNG. */
+template<class T>
+inline void free_wrapper(T* p)
+{
+    free(p);
+}
+
+//! sfree wrapper to be used as unique_cptr deleter
+template<class T>
+inline void sfree_wrapper(T* p)
+{
+    sfree(p);
+}
+
+//! \internal \brief wrap function into functor to be used as deleter
+template<class T, void D(T*)>
+struct functor_wrapper
+{
+    //! call wrapped function
+    void operator()(T* t) { D(t); }
+};
+
+//! unique_ptr which takes function pointer (has to return void) as template argument
+template<typename T, void D(T*) = sfree_wrapper>
+using unique_cptr = std::unique_ptr<T, functor_wrapper<T, D>>;
+
+//! Simple guard which calls sfree. See unique_cptr for details.
+typedef unique_cptr<void> sfree_guard;
+
+
+//! Create unique_ptr with any deleter function or lambda
+template<typename T, typename D>
+std::unique_ptr<T, D> create_unique_with_deleter(T* t, D d)
+{
+    return std::unique_ptr<T, D>(t, d);
+}
+
+} // namespace gmx
+
+#endif